Upgrade Tapir / Fix Token Acquisition
continuous-integration/drone/pr Build is failing Details

This commit is contained in:
Sarah Jamie Lewis 2022-10-25 13:59:05 -07:00 committed by Gitea
parent c66561d84f
commit f2b879a9c4
8 changed files with 64 additions and 41 deletions

View File

@ -145,11 +145,14 @@ func (app *application) DeletePeer(onion string, password string) {
defer app.appmutex.Unlock() defer app.appmutex.Unlock()
if app.peers[onion].CheckPassword(password) { if app.peers[onion].CheckPassword(password) {
app.shutdownPeer(onion) // soft-shutdown
app.peers[onion].Shutdown()
// delete the underlying storage
app.peers[onion].Delete() app.peers[onion].Delete()
// hard shutdown / remove from app
app.shutdownPeer(onion)
// Shutdown and Remove the Engine // Shutdown and Remove the Engine
log.Debugf("Delete peer for %v Done\n", onion) log.Debugf("Delete peer for %v Done\n", onion)
app.appBus.Publish(event.NewEventList(event.PeerDeleted, event.Identity, onion)) app.appBus.Publish(event.NewEventList(event.PeerDeleted, event.Identity, onion))
return return

2
go.mod
View File

@ -3,7 +3,7 @@ module cwtch.im/cwtch
go 1.17 go 1.17
require ( require (
git.openprivacy.ca/cwtch.im/tapir v0.5.5 git.openprivacy.ca/cwtch.im/tapir v0.6.0
git.openprivacy.ca/openprivacy/connectivity v1.8.6 git.openprivacy.ca/openprivacy/connectivity v1.8.6
git.openprivacy.ca/openprivacy/log v1.0.3 git.openprivacy.ca/openprivacy/log v1.0.3
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c

4
go.sum
View File

@ -1,8 +1,8 @@
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
git.openprivacy.ca/cwtch.im/tapir v0.5.5 h1:km6UDrLYH/GCEn2s+S299/TiRHhxKCIAipYr9GbG3Hk= git.openprivacy.ca/cwtch.im/tapir v0.6.0 h1:TtnKjxitkIDMM7Qn0n/u+mOHRLJzuQUYjYRu5n0/QFY=
git.openprivacy.ca/cwtch.im/tapir v0.5.5/go.mod h1:bWWHrDYBtHvxMri59RwIB/w7Eg1aC0BrQ/ycKlnbB5k= git.openprivacy.ca/cwtch.im/tapir v0.6.0/go.mod h1:iQIq4y7N+DuP3CxyG66WNEC/d6vzh+wXvvOmelB+KoY=
git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c= git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=
git.openprivacy.ca/openprivacy/bine v0.0.4/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU= git.openprivacy.ca/openprivacy/bine v0.0.4/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
git.openprivacy.ca/openprivacy/connectivity v1.8.6 h1:g74PyDGvpMZ3+K0dXy3mlTJh+e0rcwNk0XF8owzkmOA= git.openprivacy.ca/openprivacy/connectivity v1.8.6 h1:g74PyDGvpMZ3+K0dXy3mlTJh+e0rcwNk0XF8owzkmOA=

View File

@ -99,42 +99,54 @@ func (cp *cwtchPeer) Delete() {
cp.storage.Delete() cp.storage.Delete()
} }
// CheckPassword returns true if the given password can be used to derive the key that encrypts the underlying
// cwtch storage database. Returns false otherwise.
func (cp *cwtchPeer) CheckPassword(password string) bool { func (cp *cwtchPeer) CheckPassword(password string) bool {
// this lock is not really needed, but because we directly access cp.storage.ProfileDirectory
// we keep it here.
cp.mutex.Lock() cp.mutex.Lock()
defer cp.mutex.Unlock() defer cp.mutex.Unlock()
// open *our* database with the given password (set createIfNotExists to false)
db, err := openEncryptedDatabase(cp.storage.ProfileDirectory, password, false) db, err := openEncryptedDatabase(cp.storage.ProfileDirectory, password, false)
if db == nil || err != nil { if db == nil || err != nil {
// this will only fail in the rare cases that ProfileDirectory has been moved or deleted
// it is actually a critical error, but far beyond the scope of Cwtch to deal with.
return false return false
} }
db.Close() // check that the storage object is valid (this will fail if the DB key is incorrect)
cps, err := NewCwtchProfileStorage(db, cp.storage.ProfileDirectory)
if err != nil {
// this will error if any SQL queries fail, which will be the case if the profile is invalid.
return false
}
// we have a valid database, close the storage (but don't purge as we may be using those conversations...)
cps.Close(false)
// success!
return true return true
} }
func (cp *cwtchPeer) ChangePassword(password string, newpassword string, newpasswordAgain string) error { func (cp *cwtchPeer) ChangePassword(password string, newpassword string, newpasswordAgain string) error {
cp.mutex.Lock() if cp.CheckPassword(password) {
defer cp.mutex.Unlock() cp.mutex.Lock()
db, err := openEncryptedDatabase(cp.storage.ProfileDirectory, password, false) defer cp.mutex.Unlock()
if db == nil || err != nil {
return errors.New(constants.InvalidPasswordError)
}
cps, err := NewCwtchProfileStorage(db, cp.storage.ProfileDirectory)
if err != nil {
return errors.New(constants.InvalidPasswordError)
}
cps.Close()
salt, err := os.ReadFile(path.Join(cp.storage.ProfileDirectory, saltFile)) salt, err := os.ReadFile(path.Join(cp.storage.ProfileDirectory, saltFile))
if err != nil { if err != nil {
return err return err
} }
// probably redundant but we like api safety // probably redundant but we like api safety
if newpassword == newpasswordAgain { if newpassword == newpasswordAgain {
rekey := createKey(newpassword, salt) rekey := createKey(newpassword, salt)
log.Infof("rekeying database...") log.Infof("rekeying database...")
return cp.storage.Rekey(rekey) return cp.storage.Rekey(rekey)
}
return errors.New(constants.PasswordsDoNotMatchError)
} }
return errors.New(constants.PasswordsDoNotMatchError) return errors.New(constants.InvalidPasswordError)
} }
// GenerateProtocolEngine // GenerateProtocolEngine
@ -1027,7 +1039,7 @@ func (cp *cwtchPeer) Shutdown() {
cp.shutdown = true cp.shutdown = true
cp.queue.Shutdown() cp.queue.Shutdown()
if cp.storage != nil { if cp.storage != nil {
cp.storage.Close() cp.storage.Close(true)
} }
} }

View File

@ -771,12 +771,13 @@ func (cps *CwtchProfileStorage) PurgeNonSavedMessages() {
} }
// Close closes the underlying database and prepared statements // Close closes the underlying database and prepared statements
func (cps *CwtchProfileStorage) Close() { func (cps *CwtchProfileStorage) Close(purgeAllNonSavedMessages bool) {
cps.mutex.Lock() cps.mutex.Lock()
defer cps.mutex.Unlock() defer cps.mutex.Unlock()
if cps.db != nil { if cps.db != nil {
if purgeAllNonSavedMessages {
cps.PurgeNonSavedMessages() cps.PurgeNonSavedMessages()
}
cps.insertProfileKeyValueStmt.Close() cps.insertProfileKeyValueStmt.Close()
cps.selectProfileKeyValueStmt.Close() cps.selectProfileKeyValueStmt.Close()

View File

@ -490,14 +490,14 @@ func (e *engine) peerAuthed(onion string) {
func (e *engine) peerConnecting(onion string) { func (e *engine) peerConnecting(onion string) {
e.eventManager.Publish(event.NewEvent(event.PeerStateChange, map[event.Field]string{ e.eventManager.Publish(event.NewEvent(event.PeerStateChange, map[event.Field]string{
event.RemotePeer: string(onion), event.RemotePeer: onion,
event.ConnectionState: ConnectionStateName[CONNECTING], event.ConnectionState: ConnectionStateName[CONNECTING],
})) }))
} }
func (e *engine) serverConnecting(onion string) { func (e *engine) serverConnecting(onion string) {
e.eventManager.Publish(event.NewEvent(event.ServerStateChange, map[event.Field]string{ e.eventManager.Publish(event.NewEvent(event.ServerStateChange, map[event.Field]string{
event.GroupServer: string(onion), event.GroupServer: onion,
event.ConnectionState: ConnectionStateName[CONNECTING], event.ConnectionState: ConnectionStateName[CONNECTING],
})) }))
} }

View File

@ -4,18 +4,19 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"git.openprivacy.ca/cwtch.im/tapir/primitives/privacypass" "git.openprivacy.ca/cwtch.im/tapir/primitives/privacypass"
"git.openprivacy.ca/openprivacy/log"
"sync" "sync"
) )
// TokenManager maintains a list of tokens associated with a single TokenServer // TokenManager maintains a list of tokens associated with a single TokenServer
type TokenManager struct { type TokenManager struct {
lock sync.Mutex lock sync.Mutex
tokens map[string]bool tokens map[string]*privacypass.Token
} }
func NewTokenManager() *TokenManager { func NewTokenManager() *TokenManager {
tm := new(TokenManager) tm := new(TokenManager)
tm.tokens = make(map[string]bool) tm.tokens = make(map[string]*privacypass.Token)
return tm return tm
} }
@ -23,9 +24,10 @@ func NewTokenManager() *TokenManager {
func (tm *TokenManager) NewTokens(tokens []*privacypass.Token) { func (tm *TokenManager) NewTokens(tokens []*privacypass.Token) {
tm.lock.Lock() tm.lock.Lock()
defer tm.lock.Unlock() defer tm.lock.Unlock()
log.Debugf("acquired %v new tokens", tokens)
for _, token := range tokens { for _, token := range tokens {
serialized, _ := json.Marshal(token) serialized, _ := json.Marshal(token)
tm.tokens[string(serialized)] = true tm.tokens[string(serialized)] = token
} }
} }
@ -44,10 +46,8 @@ func (tm *TokenManager) FetchToken() (*privacypass.Token, int, error) {
if len(tm.tokens) == 0 { if len(tm.tokens) == 0 {
return nil, 0, errors.New("no more tokens") return nil, 0, errors.New("no more tokens")
} }
for serializedToken := range tm.tokens { for serializedToken, token := range tm.tokens {
delete(tm.tokens, serializedToken) delete(tm.tokens, serializedToken)
token := new(privacypass.Token)
json.Unmarshal([]byte(serializedToken), token)
return token, len(tm.tokens), nil return token, len(tm.tokens), nil
} }
return nil, 0, errors.New("no more tokens") return nil, 0, errors.New("no more tokens")

View File

@ -66,17 +66,22 @@ func (ta *TokenBoardClient) NewInstance() tapir.Application {
// Init initializes the cryptographic TokenBoardApp // Init initializes the cryptographic TokenBoardApp
func (ta *TokenBoardClient) Init(connection tapir.Connection) { func (ta *TokenBoardClient) Init(connection tapir.Connection) {
// connection.Hostname is always valid because we are ALWAYS the initiating party
log.Debugf("connecting to server: %v", connection.Hostname())
ta.AuthApp.Init(connection) ta.AuthApp.Init(connection)
log.Debugf("server protocol complete: %v", connection.Hostname())
if connection.HasCapability(applications.AuthCapability) { if connection.HasCapability(applications.AuthCapability) {
ta.connection = connection
ta.tokenBoardHandler.ServerAuthedHandler(ta.connection.Hostname())
log.Debugf("Successfully Initialized Connection to %v", connection.Hostname()) log.Debugf("Successfully Initialized Connection to %v", connection.Hostname())
ta.connection = connection
ta.tokenBoardHandler.ServerAuthedHandler(connection.Hostname())
go ta.Listen() go ta.Listen()
// Optimistically acquire many tokens for this server... // Optimistically acquire many tokens for this server...
go ta.PurchaseTokens() go ta.PurchaseTokens()
go ta.PurchaseTokens() go ta.PurchaseTokens()
ta.Replay() ta.Replay()
} else { } else {
log.Debugf("Error Connecting to %v", connection.Hostname())
ta.tokenBoardHandler.ServerClosedHandler(connection.Hostname())
connection.Close() connection.Close()
} }
} }
@ -117,10 +122,12 @@ func (ta *TokenBoardClient) Listen() {
ta.postQueue = ta.postQueue[1:] ta.postQueue = ta.postQueue[1:]
ta.postLock.Unlock() ta.postLock.Unlock()
if !message.PostResult.Success { if !message.PostResult.Success {
log.Debugf("post result message: %v", message.PostResult)
// Retry using another token // Retry using another token
posted, _ := ta.Post(egm.Group, egm.Ciphertext, egm.Signature) posted, _ := ta.Post(egm.Group, egm.Ciphertext, egm.Signature)
// if posting failed... // if posting failed...
if !posted { if !posted {
log.Errorf("error posting message")
ta.tokenBoardHandler.PostingFailed(egm.Group, egm.Signature) ta.tokenBoardHandler.PostingFailed(egm.Group, egm.Signature)
} }
} }