diff --git a/auth/confirm.go b/auth/confirm.go index e523715c74a421773354216c9e335a5d9157b2fa..ee89b8f1827546c740f8aa23634940e30d26deea 100644 --- a/auth/confirm.go +++ b/auth/confirm.go @@ -126,7 +126,7 @@ func (s *state) confirm(partner contact.Contact, serviceTag string) ( s.event.Report(10, "Auth", "SendConfirmError", em) } - //todo: s.backupTrigger("confirmed authenticated channel") + s.backupTrigger("confirmed authenticated channel") jww.INFO.Printf("Confirming Auth from %s to %s, msgDigest: %s", partner.ID, s.e2e.GetReceptionID(), diff --git a/auth/receivedConfirm.go b/auth/receivedConfirm.go index 1990fd5bdca5eeb826573b6c06c9151ce96ce25c..07e12df9beae5cb55889d9eadd29a3361b68b2b3 100644 --- a/auth/receivedConfirm.go +++ b/auth/receivedConfirm.go @@ -5,9 +5,9 @@ import ( "fmt" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/auth/store" - "gitlab.com/elixxir/client/cmix/rounds" "gitlab.com/elixxir/client/cmix/identity/receptionID" "gitlab.com/elixxir/client/cmix/message" + "gitlab.com/elixxir/client/cmix/rounds" "gitlab.com/elixxir/client/e2e/ratchet/partner/session" "gitlab.com/elixxir/crypto/contact" cAuth "gitlab.com/elixxir/crypto/e2e/auth" @@ -95,7 +95,7 @@ func (rcs *receivedConfirmService) Process(msg format.Message, "%s : %+v", rcs.GetPartner(), receptionID.Source, err) } - //todo: trigger backup + rcs.s.backupTrigger("received confirmation from request") // remove the service used for notifications of the confirm state.net.DeleteService(receptionID.Source, rcs.notificationsService, nil) diff --git a/auth/state.go b/auth/state.go index 7ced7eb8d69ca96ef321fd5b3ac5faa625c6dab7..dfb6cb03ce7a7c8060800e7c233e5a7bcdf6a094 100644 --- a/auth/state.go +++ b/auth/state.go @@ -33,9 +33,7 @@ import ( type state struct { callbacks Callbacks - // net cmix.Client net cmixClient - // e2e e2e.Handler e2e e2eHandler rng *fastRNG.StreamGenerator @@ -43,6 +41,8 @@ type state struct { event event.Manager params Param + + backupTrigger func(reason string) } type cmixClient interface { @@ -101,9 +101,10 @@ type Callbacks interface { // NewState and use GetDefaultTemporaryParams() for the parameters func NewState(kv *versioned.KV, net cmix.Client, e2e e2e.Handler, rng *fastRNG.StreamGenerator, event event.Manager, params Param, - callbacks Callbacks) (State, error) { + callbacks Callbacks, backupTrigger func(reason string)) (State, error) { kv = kv.Prefix(makeStorePrefix(e2e.GetReceptionID())) - return NewStateLegacy(kv, net, e2e, rng, event, params, callbacks) + return NewStateLegacy( + kv, net, e2e, rng, event, params, callbacks, backupTrigger) } // NewStateLegacy loads the auth state or creates new auth state if one cannot be @@ -113,17 +114,16 @@ func NewState(kv *versioned.KV, net cmix.Client, e2e e2e.Handler, // Otherwise, acts the same as NewState func NewStateLegacy(kv *versioned.KV, net cmix.Client, e2e e2e.Handler, rng *fastRNG.StreamGenerator, event event.Manager, params Param, - callbacks Callbacks) (State, error) { + callbacks Callbacks, backupTrigger func(reason string)) (State, error) { s := &state{ - callbacks: callbacks, - - net: net, - e2e: e2e, - rng: rng, - - params: params, - event: event, + callbacks: callbacks, + net: net, + e2e: e2e, + rng: rng, + event: event, + params: params, + backupTrigger: backupTrigger, } // create the store @@ -131,7 +131,7 @@ func NewStateLegacy(kv *versioned.KV, net cmix.Client, e2e e2e.Handler, s.store, err = store.NewOrLoadStore(kv, e2e.GetGroup(), &sentRequestHandler{s: s}) - //register services + // register services net.AddService(e2e.GetReceptionID(), message.Service{ Identifier: e2e.GetReceptionID()[:], Tag: params.RequestTag, diff --git a/backup/backup.go b/backup/backup.go index ea9667c0391b495b6c1c814cf5cf27f19341002e..291348fa6aa3bbbee8b49e6937013c4ea77b51f6 100644 --- a/backup/backup.go +++ b/backup/backup.go @@ -5,34 +5,30 @@ // LICENSE file // //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 xx network SEZC // -// // -// Use of this source code is governed by a license that can be found in the // -// LICENSE file // -//////////////////////////////////////////////////////////////////////////////// - package backup import ( + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/primitives/fact" + "gitlab.com/xx_network/primitives/id" "sync" + "time" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/client/api" - "gitlab.com/elixxir/client/interfaces" - "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/crypto/backup" "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/xx_network/crypto/signature/rsa" ) // Error messages. const ( - // initializeBackup + // InitializeBackup errSavePassword = "failed to save password: %+v" errSaveKeySaltParams = "failed to save key, salt, and params: %+v" - // resumeBackup + // ResumeBackup errLoadPassword = "backup not initialized: load user password failed: %+v" // Backup.StopBackup @@ -46,15 +42,43 @@ type Backup struct { // Callback that is called with the encrypted backup when triggered updateBackupCb UpdateBackupFn - mux sync.RWMutex + container *Container + + jsonParams string // Client structures - client *api.Client - store *storage.Session - backupContainer *interfaces.BackupContainer - rng *fastRNG.StreamGenerator + e2e E2e + session Session + ud UserDiscovery + kv *versioned.KV + rng *fastRNG.StreamGenerator - jsonParams string + mux sync.RWMutex +} + +// E2e is a subset of functions from the interface e2e.Handler. +type E2e interface { + GetAllPartnerIDs() []*id.ID + GetHistoricalDHPubkey() *cyclic.Int + GetHistoricalDHPrivkey() *cyclic.Int +} + +// Session is a subset of functions from the interface storage.Session. +type Session interface { + GetRegCode() (string, error) + GetTransmissionID() *id.ID + GetTransmissionSalt() []byte + GetReceptionID() *id.ID + GetReceptionSalt() []byte + GetReceptionRSA() *rsa.PrivateKey + GetTransmissionRSA() *rsa.PrivateKey + GetTransmissionRegistrationValidationSignature() []byte + GetReceptionRegistrationValidationSignature() []byte + GetRegistrationTimestamp() time.Time +} + +type UserDiscovery interface { + GetFacts() fact.FactList } // UpdateBackupFn is the callback that encrypted backup data is returned on @@ -68,27 +92,20 @@ type UpdateBackupFn func(encryptedBackup []byte) // Call this to turn on backups for the first time or to replace the user's // password. func InitializeBackup(password string, updateBackupCb UpdateBackupFn, - c *api.Client) (*Backup, error) { - return initializeBackup( - password, updateBackupCb, c, c.GetStorage(), c.GetBackup(), c.GetRng()) -} - -// initializeBackup is a helper function that takes in all the fields for Backup -// as parameters for easier testing. -func initializeBackup(password string, updateBackupCb UpdateBackupFn, - c *api.Client, store *storage.Session, - backupContainer *interfaces.BackupContainer, rng *fastRNG.StreamGenerator) ( - *Backup, error) { + container *Container, e2e E2e, session Session, ud UserDiscovery, + kv *versioned.KV, rng *fastRNG.StreamGenerator) (*Backup, error) { b := &Backup{ - updateBackupCb: updateBackupCb, - client: c, - store: store, - backupContainer: backupContainer, - rng: rng, + updateBackupCb: updateBackupCb, + container: container, + e2e: e2e, + session: session, + ud: ud, + kv: kv, + rng: rng, } // Save password to storage - err := savePassword(password, b.store.GetKV()) + err := savePassword(password, b.kv) if err != nil { return nil, errors.Errorf(errSavePassword, err) } @@ -105,15 +122,15 @@ func initializeBackup(password string, updateBackupCb UpdateBackupFn, key := backup.DeriveKey(password, salt, params) // Save key, salt, and parameters to storage - err = saveBackup(key, salt, params, b.store.GetKV()) + err = saveBackup(key, salt, params, b.kv) if err != nil { return nil, errors.Errorf(errSaveKeySaltParams, err) } // Setting backup trigger in client - b.backupContainer.SetBackup(b.TriggerBackup) + b.container.SetBackup(b.TriggerBackup) - b.TriggerBackup("initializeBackup") + b.TriggerBackup("InitializeBackup") jww.INFO.Print("Initialized backup with new user key.") return b, nil @@ -122,32 +139,27 @@ func initializeBackup(password string, updateBackupCb UpdateBackupFn, // ResumeBackup resumes a backup by restoring the Backup object and registering // a new callback. Call this to resume backups that have already been // initialized. Returns an error if backups have not already been initialized. -func ResumeBackup(updateBackupCb UpdateBackupFn, c *api.Client) (*Backup, error) { - return resumeBackup( - updateBackupCb, c, c.GetStorage(), c.GetBackup(), c.GetRng()) -} - -// resumeBackup is a helper function that takes in all the fields for Backup as -// parameters for easier testing. -func resumeBackup(updateBackupCb UpdateBackupFn, c *api.Client, - store *storage.Session, backupContainer *interfaces.BackupContainer, +func ResumeBackup(updateBackupCb UpdateBackupFn, container *Container, + e2e E2e, session Session, ud UserDiscovery, kv *versioned.KV, rng *fastRNG.StreamGenerator) (*Backup, error) { - _, err := loadPassword(store.GetKV()) + _, err := loadPassword(kv) if err != nil { return nil, errors.Errorf(errLoadPassword, err) } b := &Backup{ - updateBackupCb: updateBackupCb, - client: c, - store: store, - backupContainer: backupContainer, - rng: rng, - jsonParams: loadJson(store.GetKV()), + updateBackupCb: updateBackupCb, + container: container, + jsonParams: loadJson(kv), + e2e: e2e, + session: session, + ud: ud, + kv: kv, + rng: rng, } // Setting backup trigger in client - b.backupContainer.SetBackup(b.TriggerBackup) + b.container.SetBackup(b.TriggerBackup) jww.INFO.Print("Resumed backup with password loaded from storage.") @@ -181,7 +193,7 @@ func (b *Backup) TriggerBackup(reason string) { b.mux.RLock() defer b.mux.RUnlock() - key, salt, params, err := loadBackup(b.store.GetKV()) + key, salt, params, err := loadBackup(b.kv) if err != nil { jww.ERROR.Printf("Backup Failed: could not load key, salt, and "+ "parameters for encrypting backup from storage: %+v", err) @@ -217,7 +229,7 @@ func (b *Backup) AddJson(newJson string) { if newJson != b.jsonParams { b.jsonParams = newJson - if err := storeJson(newJson, b.store.GetKV()); err != nil { + if err := storeJson(newJson, b.kv); err != nil { jww.FATAL.Panicf("Failed to store json: %+v", err) } go b.TriggerBackup("New Json") @@ -231,12 +243,12 @@ func (b *Backup) StopBackup() error { defer b.mux.Unlock() b.updateBackupCb = nil - err := deletePassword(b.store.GetKV()) + err := deletePassword(b.kv) if err != nil { return errors.Errorf(errDeletePassword, err) } - err = deleteBackup(b.store.GetKV()) + err = deleteBackup(b.kv) if err != nil { return errors.Errorf(errDeleteCrypto, err) } @@ -268,42 +280,38 @@ func (b *Backup) assembleBackup() backup.Backup { Contacts: backup.Contacts{}, } - // get user and storage user - u := b.store.GetUser() - su := b.store.User() - - // get registration timestamp - bu.RegistrationTimestamp = u.RegistrationTimestamp + // Get registration timestamp + bu.RegistrationTimestamp = b.session.GetRegistrationTimestamp().UnixNano() - // get registration code; ignore the error because if there is no + // Get registration code; ignore the error because if there is no // registration, then an empty string is returned - bu.RegistrationCode, _ = b.store.GetRegCode() + bu.RegistrationCode, _ = b.session.GetRegCode() - // get transmission identity + // Get transmission identity bu.TransmissionIdentity = backup.TransmissionIdentity{ - RSASigningPrivateKey: u.TransmissionRSA, - RegistrarSignature: su.GetTransmissionRegistrationValidationSignature(), - Salt: u.TransmissionSalt, - ComputedID: u.TransmissionID, + RSASigningPrivateKey: b.session.GetTransmissionRSA(), + RegistrarSignature: b.session.GetTransmissionRegistrationValidationSignature(), + Salt: b.session.GetTransmissionSalt(), + ComputedID: b.session.GetTransmissionID(), } - // get reception identity + // Get reception identity bu.ReceptionIdentity = backup.ReceptionIdentity{ - RSASigningPrivateKey: u.ReceptionRSA, - RegistrarSignature: su.GetReceptionRegistrationValidationSignature(), - Salt: u.ReceptionSalt, - ComputedID: u.ReceptionID, - DHPrivateKey: u.E2eDhPrivateKey, - DHPublicKey: u.E2eDhPublicKey, + RSASigningPrivateKey: b.session.GetReceptionRSA(), + RegistrarSignature: b.session.GetReceptionRegistrationValidationSignature(), + Salt: b.session.GetReceptionSalt(), + ComputedID: b.session.GetReceptionID(), + DHPrivateKey: b.e2e.GetHistoricalDHPrivkey(), + DHPublicKey: b.e2e.GetHistoricalDHPubkey(), } - // get facts - bu.UserDiscoveryRegistration.FactList = b.store.GetUd().GetFacts() + // Get facts + bu.UserDiscoveryRegistration.FactList = b.ud.GetFacts() - // get contacts - bu.Contacts.Identities = b.store.E2e().GetPartners() + // Get contacts + bu.Contacts.Identities = b.e2e.GetAllPartnerIDs() - //add the memoized json params + // Add the memoized json params bu.JSONParams = b.jsonParams return bu diff --git a/backup/backup_test.go b/backup/backup_test.go index 434c76e78665692524849952b599652be428600a..076fc2d8c7451dbe039d60fc1c8cd9edc85204e4 100644 --- a/backup/backup_test.go +++ b/backup/backup_test.go @@ -9,29 +9,30 @@ package backup import ( "bytes" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/ekv" "reflect" "strings" "testing" "time" - "gitlab.com/elixxir/client/interfaces" - "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/crypto/backup" "gitlab.com/elixxir/crypto/fastRNG" "gitlab.com/xx_network/crypto/csprng" ) -// Tests that Backup.initializeBackup returns a new Backup with a copy of the +// Tests that Backup.InitializeBackup returns a new Backup with a copy of the // key and the callback. -func Test_initializeBackup(t *testing.T) { +func Test_InitializeBackup(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + rngGen := fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG) cbChan := make(chan []byte, 2) cb := func(encryptedBackup []byte) { cbChan <- encryptedBackup } expectedPassword := "MySuperSecurePassword" - b, err := initializeBackup(expectedPassword, cb, nil, - storage.InitTestingSession(t), &interfaces.BackupContainer{}, - fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG)) + b, err := InitializeBackup(expectedPassword, cb, &Container{}, newMockE2e(t), + newMockSession(t), newMockUserDiscovery(), kv, rngGen) if err != nil { - t.Errorf("initializeBackup returned an error: %+v", err) + t.Errorf("InitializeBackup returned an error: %+v", err) } select { @@ -41,7 +42,7 @@ func Test_initializeBackup(t *testing.T) { } // Check that the correct password is in storage - loadedPassword, err := loadPassword(b.store.GetKV()) + loadedPassword, err := loadPassword(b.kv) if err != nil { t.Errorf("Failed to load password: %+v", err) } @@ -51,7 +52,7 @@ func Test_initializeBackup(t *testing.T) { } // Check that the key, salt, and params were saved to storage - key, salt, p, err := loadBackup(b.store.GetKV()) + key, salt, p, err := loadBackup(b.kv) if err != nil { t.Errorf("Failed to load key, salt, and params: %+v", err) } @@ -80,17 +81,17 @@ func Test_initializeBackup(t *testing.T) { } } -// Initialises a new backup and then tests that Backup.resumeBackup overwrites -// the callback but keeps the password. -func Test_resumeBackup(t *testing.T) { +// Initialises a new backup and then tests that ResumeBackup overwrites the +// callback but keeps the password. +func Test_ResumeBackup(t *testing.T) { // Start the first backup + kv := versioned.NewKV(make(ekv.Memstore)) + rngGen := fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG) cbChan1 := make(chan []byte) cb1 := func(encryptedBackup []byte) { cbChan1 <- encryptedBackup } - s := storage.InitTestingSession(t) expectedPassword := "MySuperSecurePassword" - b, err := initializeBackup(expectedPassword, cb1, nil, s, - &interfaces.BackupContainer{}, - fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG)) + b, err := InitializeBackup(expectedPassword, cb1, &Container{}, + newMockE2e(t), newMockSession(t), newMockUserDiscovery(), kv, rngGen) if err != nil { t.Errorf("Failed to initialize new Backup: %+v", err) } @@ -102,7 +103,7 @@ func Test_resumeBackup(t *testing.T) { } // get key and salt to compare to later - key1, salt1, _, err := loadBackup(b.store.GetKV()) + key1, salt1, _, err := loadBackup(b.kv) if err != nil { t.Errorf("Failed to load key, salt, and params from newly "+ "initialized backup: %+v", err) @@ -111,14 +112,14 @@ func Test_resumeBackup(t *testing.T) { // Resume the backup with a new callback cbChan2 := make(chan []byte) cb2 := func(encryptedBackup []byte) { cbChan2 <- encryptedBackup } - b2, err := resumeBackup(cb2, nil, s, &interfaces.BackupContainer{}, - fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG)) + b2, err := ResumeBackup(cb2, &Container{}, newMockE2e(t), newMockSession(t), + newMockUserDiscovery(), kv, rngGen) if err != nil { - t.Errorf("resumeBackup returned an error: %+v", err) + t.Errorf("ResumeBackup returned an error: %+v", err) } // Check that the correct password is in storage - loadedPassword, err := loadPassword(b.store.GetKV()) + loadedPassword, err := loadPassword(b.kv) if err != nil { t.Errorf("Failed to load password: %+v", err) } @@ -128,7 +129,7 @@ func Test_resumeBackup(t *testing.T) { } // get key, salt, and parameters of resumed backup - key2, salt2, _, err := loadBackup(b.store.GetKV()) + key2, salt2, _, err := loadBackup(b.kv) if err != nil { t.Errorf("Failed to load key, salt, and params from resumed "+ "backup: %+v", err) @@ -158,14 +159,16 @@ func Test_resumeBackup(t *testing.T) { } } -// Error path: Tests that Backup.resumeBackup returns an error if no password is +// Error path: Tests that ResumeBackup returns an error if no password is // present in storage. -func Test_resumeBackup_NoKeyError(t *testing.T) { +func Test_ResumeBackup_NoKeyError(t *testing.T) { expectedErr := strings.Split(errLoadPassword, "%")[0] - s := storage.InitTestingSession(t) - _, err := resumeBackup(nil, nil, s, &interfaces.BackupContainer{}, nil) + kv := versioned.NewKV(make(ekv.Memstore)) + rngGen := fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG) + _, err := ResumeBackup(nil, &Container{}, newMockE2e(t), newMockSession(t), + newMockUserDiscovery(), kv, rngGen) if err == nil || !strings.Contains(err.Error(), expectedErr) { - t.Errorf("resumeBackup did not return the expected error when no "+ + t.Errorf("ResumeBackup did not return the expected error when no "+ "password is present.\nexpected: %s\nreceived: %+v", expectedErr, err) } } @@ -178,7 +181,7 @@ func TestBackup_TriggerBackup(t *testing.T) { b := newTestBackup("MySuperSecurePassword", cb, t) // get password - password, err := loadPassword(b.store.GetKV()) + password, err := loadPassword(b.kv) if err != nil { t.Errorf("Failed to load password from storage: %+v", err) } @@ -215,7 +218,7 @@ func TestBackup_TriggerBackup_NoKey(t *testing.T) { t.Errorf("backup not called") } - err := deleteBackup(b.store.GetKV()) + err := deleteBackup(b.kv) if err != nil { t.Errorf("Failed to delete key, salt, and params: %+v", err) } @@ -260,13 +263,13 @@ func TestBackup_StopBackup(t *testing.T) { } // Make sure password is deleted - password, err := loadPassword(b.store.GetKV()) + password, err := loadPassword(b.kv) if err == nil || len(password) != 0 { t.Errorf("Loaded password that should be deleted: %q", password) } // Make sure key, salt, and params are deleted - key, salt, p, err := loadBackup(b.store.GetKV()) + key, salt, p, err := loadBackup(b.kv) if err == nil || len(key) != 0 || len(salt) != 0 || p != (backup.Params{}) { t.Errorf("Loaded key, salt, and params that should be deleted.") } @@ -296,77 +299,79 @@ func TestBackup_IsBackupRunning(t *testing.T) { func TestBackup_AddJson(t *testing.T) { b := newTestBackup("MySuperSecurePassword", nil, t) - s := b.store + s := b.session.(*mockSession) + e2e := b.e2e.(*mockE2e) json := "{'data': {'one': 1}}" - expectedCollatedBackup := backup.Backup{ - RegistrationTimestamp: s.GetUser().RegistrationTimestamp, + expected := backup.Backup{ + RegistrationCode: s.regCode, + RegistrationTimestamp: s.registrationTimestamp.UnixNano(), TransmissionIdentity: backup.TransmissionIdentity{ - RSASigningPrivateKey: s.GetUser().TransmissionRSA, - RegistrarSignature: s.User().GetTransmissionRegistrationValidationSignature(), - Salt: s.GetUser().TransmissionSalt, - ComputedID: s.GetUser().TransmissionID, + RSASigningPrivateKey: s.transmissionRSA, + RegistrarSignature: s.transmissionRegistrationValidationSignature, + Salt: s.transmissionSalt, + ComputedID: s.transmissionID, }, ReceptionIdentity: backup.ReceptionIdentity{ - RSASigningPrivateKey: s.GetUser().ReceptionRSA, - RegistrarSignature: s.User().GetReceptionRegistrationValidationSignature(), - Salt: s.GetUser().ReceptionSalt, - ComputedID: s.GetUser().ReceptionID, - DHPrivateKey: s.GetUser().E2eDhPrivateKey, - DHPublicKey: s.GetUser().E2eDhPublicKey, + RSASigningPrivateKey: s.receptionRSA, + RegistrarSignature: s.receptionRegistrationValidationSignature, + Salt: s.receptionSalt, + ComputedID: s.receptionID, + DHPrivateKey: e2e.historicalDHPrivkey, + DHPublicKey: e2e.historicalDHPubkey, }, UserDiscoveryRegistration: backup.UserDiscoveryRegistration{ - FactList: s.GetUd().GetFacts(), + FactList: b.ud.(*mockUserDiscovery).facts, }, - Contacts: backup.Contacts{Identities: s.E2e().GetPartners()}, + Contacts: backup.Contacts{Identities: e2e.partnerIDs}, JSONParams: json, } b.AddJson(json) collatedBackup := b.assembleBackup() - if !reflect.DeepEqual(expectedCollatedBackup, collatedBackup) { + if !reflect.DeepEqual(expected, collatedBackup) { t.Errorf("Collated backup does not match expected."+ - "\nexpected: %+v\nreceived: %+v", - expectedCollatedBackup, collatedBackup) + "\nexpected: %+v\nreceived: %+v", expected, collatedBackup) } } func TestBackup_AddJson_badJson(t *testing.T) { b := newTestBackup("MySuperSecurePassword", nil, t) - s := b.store + s := b.session.(*mockSession) + e2e := b.e2e.(*mockE2e) json := "abc{'i'm a bad json: 'one': 1'''}}" - expectedCollatedBackup := backup.Backup{ - RegistrationTimestamp: s.GetUser().RegistrationTimestamp, + expected := backup.Backup{ + RegistrationCode: s.regCode, + RegistrationTimestamp: s.registrationTimestamp.UnixNano(), TransmissionIdentity: backup.TransmissionIdentity{ - RSASigningPrivateKey: s.GetUser().TransmissionRSA, - RegistrarSignature: s.User().GetTransmissionRegistrationValidationSignature(), - Salt: s.GetUser().TransmissionSalt, - ComputedID: s.GetUser().TransmissionID, + RSASigningPrivateKey: s.transmissionRSA, + RegistrarSignature: s.transmissionRegistrationValidationSignature, + Salt: s.transmissionSalt, + ComputedID: s.transmissionID, }, ReceptionIdentity: backup.ReceptionIdentity{ - RSASigningPrivateKey: s.GetUser().ReceptionRSA, - RegistrarSignature: s.User().GetReceptionRegistrationValidationSignature(), - Salt: s.GetUser().ReceptionSalt, - ComputedID: s.GetUser().ReceptionID, - DHPrivateKey: s.GetUser().E2eDhPrivateKey, - DHPublicKey: s.GetUser().E2eDhPublicKey, + RSASigningPrivateKey: s.receptionRSA, + RegistrarSignature: s.receptionRegistrationValidationSignature, + Salt: s.receptionSalt, + ComputedID: s.receptionID, + DHPrivateKey: e2e.historicalDHPrivkey, + DHPublicKey: e2e.historicalDHPubkey, }, UserDiscoveryRegistration: backup.UserDiscoveryRegistration{ - FactList: s.GetUd().GetFacts(), + FactList: b.ud.(*mockUserDiscovery).facts, }, - Contacts: backup.Contacts{Identities: s.E2e().GetPartners()}, + Contacts: backup.Contacts{Identities: e2e.partnerIDs}, JSONParams: json, } b.AddJson(json) collatedBackup := b.assembleBackup() - if !reflect.DeepEqual(expectedCollatedBackup, collatedBackup) { + if !reflect.DeepEqual(expected, collatedBackup) { t.Errorf("Collated backup does not match expected."+ - "\nexpected: %+v\nreceived: %+v", - expectedCollatedBackup, collatedBackup) + "\nexpected: %+v\nreceived: %+v", expected, collatedBackup) } } @@ -374,47 +379,51 @@ func TestBackup_AddJson_badJson(t *testing.T) { // results. func TestBackup_assembleBackup(t *testing.T) { b := newTestBackup("MySuperSecurePassword", nil, t) - s := b.store + s := b.session.(*mockSession) + e2e := b.e2e.(*mockE2e) - expectedCollatedBackup := backup.Backup{ - RegistrationTimestamp: s.GetUser().RegistrationTimestamp, + expected := backup.Backup{ + RegistrationCode: s.regCode, + RegistrationTimestamp: s.registrationTimestamp.UnixNano(), TransmissionIdentity: backup.TransmissionIdentity{ - RSASigningPrivateKey: s.GetUser().TransmissionRSA, - RegistrarSignature: s.User().GetTransmissionRegistrationValidationSignature(), - Salt: s.GetUser().TransmissionSalt, - ComputedID: s.GetUser().TransmissionID, + RSASigningPrivateKey: s.transmissionRSA, + RegistrarSignature: s.transmissionRegistrationValidationSignature, + Salt: s.transmissionSalt, + ComputedID: s.transmissionID, }, ReceptionIdentity: backup.ReceptionIdentity{ - RSASigningPrivateKey: s.GetUser().ReceptionRSA, - RegistrarSignature: s.User().GetReceptionRegistrationValidationSignature(), - Salt: s.GetUser().ReceptionSalt, - ComputedID: s.GetUser().ReceptionID, - DHPrivateKey: s.GetUser().E2eDhPrivateKey, - DHPublicKey: s.GetUser().E2eDhPublicKey, + RSASigningPrivateKey: s.receptionRSA, + RegistrarSignature: s.receptionRegistrationValidationSignature, + Salt: s.receptionSalt, + ComputedID: s.receptionID, + DHPrivateKey: e2e.historicalDHPrivkey, + DHPublicKey: e2e.historicalDHPubkey, }, UserDiscoveryRegistration: backup.UserDiscoveryRegistration{ - FactList: s.GetUd().GetFacts(), + FactList: b.ud.(*mockUserDiscovery).facts, }, - Contacts: backup.Contacts{Identities: s.E2e().GetPartners()}, + Contacts: backup.Contacts{Identities: e2e.partnerIDs}, } collatedBackup := b.assembleBackup() - if !reflect.DeepEqual(expectedCollatedBackup, collatedBackup) { + if !reflect.DeepEqual(expected, collatedBackup) { t.Errorf("Collated backup does not match expected."+ "\nexpected: %+v\nreceived: %+v", - expectedCollatedBackup, collatedBackup) + expected, collatedBackup) } } // newTestBackup creates a new Backup for testing. func newTestBackup(password string, cb UpdateBackupFn, t *testing.T) *Backup { - b, err := initializeBackup( + b, err := InitializeBackup( password, cb, - nil, - storage.InitTestingSession(t), - &interfaces.BackupContainer{}, + &Container{}, + newMockE2e(t), + newMockSession(t), + newMockUserDiscovery(), + versioned.NewKV(make(ekv.Memstore)), fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG), ) if err != nil { diff --git a/interfaces/backup.go b/backup/container.go similarity index 83% rename from interfaces/backup.go rename to backup/container.go index 559b4b0f8756ee772aba9064757601d2ade417d6..fd456ea96c11337c4709991c97f6b84458adfe11 100644 --- a/interfaces/backup.go +++ b/backup/container.go @@ -5,14 +5,14 @@ // LICENSE file // //////////////////////////////////////////////////////////////////////////////// -package interfaces +package backup import "sync" type TriggerBackup func(reason string) -// BackupContainer contains the trigger to call to initiate a backup. -type BackupContainer struct { +// Container contains the trigger to call to initiate a backup. +type Container struct { triggerBackup TriggerBackup mux sync.RWMutex } @@ -22,7 +22,7 @@ type BackupContainer struct { // should be in the paste tense. For example, if a contact is deleted, the // reason can be "contact deleted" and the log will show: // Triggering backup: contact deleted -func (bc *BackupContainer) TriggerBackup(reason string) { +func (bc *Container) TriggerBackup(reason string) { bc.mux.RLock() defer bc.mux.RUnlock() if bc.triggerBackup != nil { @@ -32,7 +32,7 @@ func (bc *BackupContainer) TriggerBackup(reason string) { // SetBackup sets the backup trigger function which will cause a backup to start // on the next event that triggers is. -func (bc *BackupContainer) SetBackup(triggerBackup TriggerBackup) { +func (bc *Container) SetBackup(triggerBackup TriggerBackup) { bc.mux.Lock() defer bc.mux.Unlock() diff --git a/backup/utils_test.go b/backup/utils_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c16969ff89da1f08b29574dba9f20e9da2e4fb44 --- /dev/null +++ b/backup/utils_test.go @@ -0,0 +1,156 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +//////////////////////////////////////////////////////////////////////////////// + +package backup + +import ( + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/primitives/fact" + "gitlab.com/xx_network/crypto/large" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" + "testing" + "time" +) + +// Adheres to the E2e interface. +type mockE2e struct { + partnerIDs []*id.ID + historicalDHPubkey *cyclic.Int + historicalDHPrivkey *cyclic.Int +} + +func newMockE2e(t *testing.T) *mockE2e { + grp := cyclic.NewGroup(large.NewInt(173), large.NewInt(0)) + return &mockE2e{ + partnerIDs: []*id.ID{ + id.NewIdFromString("partner1", id.User, t), + id.NewIdFromString("partner2", id.User, t), + id.NewIdFromString("partner3", id.User, t), + }, + historicalDHPubkey: grp.NewInt(45), + historicalDHPrivkey: grp.NewInt(46), + } +} +func (m *mockE2e) GetAllPartnerIDs() []*id.ID { return m.partnerIDs } +func (m *mockE2e) GetHistoricalDHPubkey() *cyclic.Int { return m.historicalDHPubkey } +func (m *mockE2e) GetHistoricalDHPrivkey() *cyclic.Int { return m.historicalDHPrivkey } + +// Adheres to the Session interface. +type mockSession struct { + regCode string + transmissionID *id.ID + transmissionSalt []byte + receptionID *id.ID + receptionSalt []byte + receptionRSA *rsa.PrivateKey + transmissionRSA *rsa.PrivateKey + transmissionRegistrationValidationSignature []byte + receptionRegistrationValidationSignature []byte + registrationTimestamp time.Time +} + +func newMockSession(t *testing.T) *mockSession { + receptionRSA, _ := rsa.LoadPrivateKeyFromPem([]byte(privKey)) + transmissionRSA, _ := rsa.LoadPrivateKeyFromPem([]byte(privKey)) + + return &mockSession{ + regCode: "regCode", + transmissionID: id.NewIdFromString("transmission", id.User, t), + transmissionSalt: []byte("transmissionSalt"), + receptionID: id.NewIdFromString("reception", id.User, t), + receptionSalt: []byte("receptionSalt"), + receptionRSA: receptionRSA, + transmissionRSA: transmissionRSA, + transmissionRegistrationValidationSignature: []byte("transmissionSig"), + receptionRegistrationValidationSignature: []byte("receptionSig"), + registrationTimestamp: time.Date(2012, 12, 21, 22, 8, 41, 0, time.UTC), + } + +} +func (m mockSession) GetRegCode() (string, error) { return m.regCode, nil } +func (m mockSession) GetTransmissionID() *id.ID { return m.transmissionID } +func (m mockSession) GetTransmissionSalt() []byte { return m.transmissionSalt } +func (m mockSession) GetReceptionID() *id.ID { return m.receptionID } +func (m mockSession) GetReceptionSalt() []byte { return m.receptionSalt } +func (m mockSession) GetReceptionRSA() *rsa.PrivateKey { return m.receptionRSA } +func (m mockSession) GetTransmissionRSA() *rsa.PrivateKey { return m.transmissionRSA } +func (m mockSession) GetTransmissionRegistrationValidationSignature() []byte { + return m.transmissionRegistrationValidationSignature +} +func (m mockSession) GetReceptionRegistrationValidationSignature() []byte { + return m.receptionRegistrationValidationSignature +} +func (m mockSession) GetRegistrationTimestamp() time.Time { return m.registrationTimestamp } + +// Adheres to the UserDiscovery interface. +type mockUserDiscovery struct { + facts fact.FactList +} + +func newMockUserDiscovery() *mockUserDiscovery { + return &mockUserDiscovery{facts: fact.FactList{ + {"myUserName", fact.Username}, + {"hello@example.com", fact.Email}, + {"6175555212", fact.Phone}, + {"name", fact.Nickname}, + }} +} +func (m mockUserDiscovery) GetFacts() fact.FactList { return m.facts } + +const privKey = `-----BEGIN PRIVATE KEY----- +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 +OaRA5ZbdE7g7vxKRV36jT3wvD7W+ +-----END PRIVATE KEY-----` diff --git a/e2e/ratchet/partner/utils.go b/e2e/ratchet/partner/utils.go new file mode 100644 index 0000000000000000000000000000000000000000..55a25ab9b2f9da82f655eed4dcf85b2331fffa9a --- /dev/null +++ b/e2e/ratchet/partner/utils.go @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +package partner + +import ( + "github.com/cloudflare/circl/dh/sidh" + "gitlab.com/elixxir/client/cmix/message" + "gitlab.com/elixxir/client/e2e/ratchet/partner/session" + "gitlab.com/elixxir/crypto/contact" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/xx_network/primitives/id" + "testing" +) + +// Test implementation of the Manager interface +type testManager struct { + partnerId *id.ID + grp *cyclic.Group + partnerPubKey, myPrivKey *cyclic.Int +} + +// NewTestManager allows creation of a Manager interface object for testing purposes +// Backwards compatibility must be maintained if you make changes here +// Currently used for: Group chat testing +func NewTestManager(partnerId *id.ID, partnerPubKey, myPrivKey *cyclic.Int, t *testing.T) Manager { + return &testManager{partnerId: partnerId, partnerPubKey: partnerPubKey, myPrivKey: myPrivKey} +} + +func (p *testManager) GetPartnerID() *id.ID { + return p.partnerId +} + +func (p *testManager) GetMyID() *id.ID { + panic("implement me") +} + +func (p *testManager) GetMyOriginPrivateKey() *cyclic.Int { + return p.myPrivKey +} + +func (p *testManager) GetPartnerOriginPublicKey() *cyclic.Int { + return p.partnerPubKey +} + +func (p *testManager) GetSendRelationshipFingerprint() []byte { + panic("implement me") +} + +func (p *testManager) GetReceiveRelationshipFingerprint() []byte { + panic("implement me") +} + +func (p *testManager) GetConnectionFingerprintBytes() []byte { + panic("implement me") +} + +func (p *testManager) GetConnectionFingerprint() string { + panic("implement me") +} + +func (p *testManager) GetContact() contact.Contact { + panic("implement me") +} + +func (p *testManager) PopSendCypher() (*session.Cypher, error) { + panic("implement me") +} + +func (p *testManager) PopRekeyCypher() (*session.Cypher, error) { + panic("implement me") +} + +func (p *testManager) NewReceiveSession(partnerPubKey *cyclic.Int, partnerSIDHPubKey *sidh.PublicKey, e2eParams session.Params, source *session.Session) (*session.Session, bool) { + panic("implement me") +} + +func (p *testManager) NewSendSession(myDHPrivKey *cyclic.Int, mySIDHPrivateKey *sidh.PrivateKey, e2eParams session.Params, source *session.Session) *session.Session { + panic("implement me") +} + +func (p *testManager) GetSendSession(sid session.SessionID) *session.Session { + panic("implement me") +} + +func (p *testManager) GetReceiveSession(sid session.SessionID) *session.Session { + panic("implement me") +} + +func (p *testManager) Confirm(sid session.SessionID) error { + panic("implement me") +} + +func (p *testManager) TriggerNegotiations() []*session.Session { + panic("implement me") +} + +func (p *testManager) MakeService(tag string) message.Service { + panic("implement me") +} + +func (p *testManager) Delete() error { + panic("implement me") +} diff --git a/groupChat/makeGroup.go b/groupChat/makeGroup.go index d8f6dd8c32d3f01c97724d497c6b3beafcc44ee0..cc0d22da3119d16a05b7f5dade00cc928ff1cf58 100644 --- a/groupChat/makeGroup.go +++ b/groupChat/makeGroup.go @@ -29,7 +29,6 @@ const ( makeMembershipErr = "failed to assemble group chat membership: %+v" newIdPreimageErr = "failed to create group ID preimage: %+v" newKeyPreimageErr = "failed to create group key preimage: %+v" - addGroupErr = "failed to save new group: %+v" ) // MaxInitMessageSize is the maximum allowable length of the initial message @@ -81,16 +80,12 @@ func (m Manager) MakeGroup(membership []*id.ID, name, msg []byte) (gs.Group, // Create new group and add to manager g := gs.NewGroup( name, groupID, groupKey, idPreimage, keyPreimage, msg, created, mem, dkl) - if err = m.gs.Add(g); err != nil { - return gs.Group{}, nil, NotSent, errors.Errorf(addGroupErr, err) - } jww.DEBUG.Printf("Created new group %q with ID %s and %d members %s", g.Name, g.ID, len(g.Members), g.Members) // Send all group requests roundIDs, status, err := m.sendRequests(g) - if err == nil { err = m.JoinGroup(g) } diff --git a/groupChat/makeGroup_test.go b/groupChat/makeGroup_test.go index eb0676859a739945e6dd2821a97ac4d31da64586..a9541b84471d9d35db12787731916d761f5294e7 100644 --- a/groupChat/makeGroup_test.go +++ b/groupChat/makeGroup_test.go @@ -121,7 +121,7 @@ func TestManager_MakeGroup_AddGroupError(t *testing.T) { prng := rand.New(rand.NewSource(42)) m, _ := newTestManagerWithStore(prng, gs.MaxGroupChats, 0, nil, nil, t) memberIDs, _, _ := addPartners(m, t) - expectedErr := strings.SplitN(addGroupErr, "%", 2)[0] + expectedErr := strings.SplitN(joinGroupErr, "%", 2)[0] _, _, _, err := m.MakeGroup(memberIDs, []byte{}, []byte{}) if err == nil || !strings.Contains(err.Error(), expectedErr) { diff --git a/groupChat/manager.go b/groupChat/manager.go index c67aae9bcb9c677d05709634ae8aca95e000d95e..19c4de8bbca37c62fb7284e9c52701b865f7abba 100644 --- a/groupChat/manager.go +++ b/groupChat/manager.go @@ -44,6 +44,7 @@ type GroupCmix interface { response message.Processor) DeleteService(clientID *id.ID, toDelete message.Service, processor message.Processor) + GetMaxMessageLength() int } // GroupE2e is a subset of the e2e.Handler interface containing only the methods needed by GroupChat diff --git a/groupChat/manager_test.go b/groupChat/manager_test.go index 9cf51c8bcded7fd73381fff79d05a2cf57027604..5a9c30cdc4db6e31d486bdd54a7a5243b846a30d 100644 --- a/groupChat/manager_test.go +++ b/groupChat/manager_test.go @@ -31,7 +31,7 @@ func Test_newManager(t *testing.T) { requestFunc := func(g gs.Group) { requestChan <- g } receiveChan := make(chan MessageReceive) receiveFunc := func(msg MessageReceive) { receiveChan <- msg } - m, err := NewManager(nil, nil, user.ID, nil, nil, kv, requestFunc, receiveFunc) + m, err := NewManager(nil, newTestE2eManager(user.DhKey), user.ID, nil, nil, kv, requestFunc, receiveFunc) if err != nil { t.Errorf("newManager() returned an error: %+v", err) } @@ -84,7 +84,7 @@ func Test_newManager_LoadStorage(t *testing.T) { } } - m, err := NewManager(nil, nil, user.ID, nil, nil, kv, nil, nil) + m, err := NewManager(newTestNetworkManager(0, t), newTestE2eManager(user.DhKey), user.ID, nil, nil, kv, nil, nil) if err != nil { t.Errorf("newManager() returned an error: %+v", err) } @@ -118,7 +118,7 @@ func Test_newManager_LoadError(t *testing.T) { expectedErr := strings.SplitN(newGroupStoreErr, "%", 2)[0] - _, err = NewManager(nil, nil, user.ID, nil, nil, kv, nil, nil) + _, err = NewManager(nil, newTestE2eManager(user.DhKey), user.ID, nil, nil, kv, nil, nil) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("newManager() did not return the expected error."+ "\nexpected: %s\nreceived: %+v", expectedErr, err) diff --git a/groupChat/receive.go b/groupChat/receive.go index b559a0b0d4776cd70425f86c0744b3b6eac2b5f4..503a52fa79434510348680b442596c1a6b3dff5c 100644 --- a/groupChat/receive.go +++ b/groupChat/receive.go @@ -38,7 +38,6 @@ type receptionProcessor struct { // Process incoming group chat messages func (p *receptionProcessor) Process(message format.Message, receptionID receptionID.EphemeralIdentity, round rounds.Round) { jww.TRACE.Print("Group message reception received cMix message.") - // Attempt to read the message roundTimestamp := round.Timestamps[states.QUEUED] @@ -117,7 +116,6 @@ func getCryptKey(key group.Key, salt [group.SaltLen]byte, mac, payload []byte, // Compute the current epoch epoch := group.ComputeEpoch(roundTimestamp) - for _, dhKey := range dhKeys { // Create a key with the correct epoch diff --git a/groupChat/receiveRequest.go b/groupChat/receiveRequest.go index 127cd9a37ad2297d931974936dc74e279fcf9a53..3a8741d6b0a7e494c137ac53c6f1f7de9834a389 100644 --- a/groupChat/receiveRequest.go +++ b/groupChat/receiveRequest.go @@ -89,7 +89,7 @@ func (m *Manager) readRequest(msg receive.Message) (gs.Group, error) { // Generate the DH keys with each group member privKey := partner.GetMyOriginPrivateKey() - dkl := gs.GenerateDhKeyList(m.gs.GetUser().ID, privKey, membership, m.grp) + dkl := gs.GenerateDhKeyList(m.receptionId, privKey, membership, m.grp) // Restore the original public key for the leader so that the membership // digest generated later is correct diff --git a/groupChat/receiveRequest_test.go b/groupChat/receiveRequest_test.go index 265309f6e92fe1057e495143029550b4f9360325..398de2f495a348b2f34323cadee1d060074834e0 100644 --- a/groupChat/receiveRequest_test.go +++ b/groupChat/receiveRequest_test.go @@ -66,7 +66,7 @@ func TestRequestListener_Hear(t *testing.T) { _, _ = m.e2e.AddPartner( g.Members[0].ID, g.Members[0].DhKey, - m.grp.NewInt(2), + m.e2e.GetHistoricalDHPrivkey(), theirSIDHPubKey, mySIDHPrivKey, session.GetDefaultParams(), session.GetDefaultParams(), @@ -165,7 +165,7 @@ func TestManager_readRequest(t *testing.T) { _, _ = m.e2e.AddPartner( g.Members[0].ID, g.Members[0].DhKey, - m.grp.NewInt(2), + m.e2e.GetHistoricalDHPrivkey(), theirSIDHPubKey, mySIDHPrivKey, session.GetDefaultParams(), session.GetDefaultParams(), diff --git a/groupChat/send.go b/groupChat/send.go index dfcc522ef11627cd3ccb32648e8b7549f1a45858..64796dd35111424ff438b5ce2b6d7f721cb2083d 100644 --- a/groupChat/send.go +++ b/groupChat/send.go @@ -27,7 +27,6 @@ import ( const ( newCmixMsgErr = "failed to generate cMix messages for group chat: %+v" sendManyCmixErr = "failed to send group chat message from member %s to group %s: %+v" - newCmixErr = "failed to generate cMix message for member %d with ID %s in group %s: %+v" messageLenErr = "message length %d is greater than maximum message space %d" newNoGroupErr = "failed to create message for group %s that cannot be found" newKeyErr = "failed to generate key for encrypting group payload" @@ -94,7 +93,7 @@ func (m *Manager) newMessages(g gs.Group, msg []byte, timestamp time.Time) ( } // Add cMix message to list - cMixMsg, err := newCmixMsg(g, msg, timestamp, member, rng, m.receptionId, m.grp) + cMixMsg, err := newCmixMsg(g, msg, timestamp, member, rng, m.receptionId, m.services.GetMaxMessageLength()) if err != nil { return nil, err } @@ -106,7 +105,7 @@ func (m *Manager) newMessages(g gs.Group, msg []byte, timestamp time.Time) ( // newCmixMsg generates a new cMix message to be sent to a group member. func newCmixMsg(g gs.Group, msg []byte, timestamp time.Time, - mem group.Member, rng io.Reader, senderId *id.ID, grp *cyclic.Group) (cmix.TargetedCmixMessage, error) { + mem group.Member, rng io.Reader, senderId *id.ID, maxCmixMessageSize int) (cmix.TargetedCmixMessage, error) { // Initialize targeted message cmixMsg := cmix.TargetedCmixMessage{ @@ -119,7 +118,7 @@ func newCmixMsg(g gs.Group, msg []byte, timestamp time.Time, } // Create three message layers - pubMsg, intlMsg, err := newMessageParts(grp.GetP().ByteLen()) + pubMsg, intlMsg, err := newMessageParts(maxCmixMessageSize) if err != nil { return cmixMsg, err } diff --git a/groupChat/sendRequests.go b/groupChat/sendRequests.go index f87b53bb9b3d3c761145897d2c70c2dcd01f02e0..3bc201d41ecd77df369a85ba44a47056c456e22d 100644 --- a/groupChat/sendRequests.go +++ b/groupChat/sendRequests.go @@ -118,7 +118,7 @@ func (m Manager) sendRequest(memberID *id.ID, request []byte) ([]id.Round, error p.LastServiceTag = catalog.GroupRq p.CMIX.DebugTag = "group.Request" - rounds, _, _, err := m.e2e.SendE2E(catalog.GroupCreationRequest, m.receptionId, request, p) + rounds, _, _, err := m.e2e.SendE2E(catalog.GroupCreationRequest, memberID, request, p) if err != nil { return nil, errors.Errorf(sendE2eErr, memberID, err) } diff --git a/groupChat/send_test.go b/groupChat/send_test.go index ec257fd4e11dd2bbadc9f614fcb5b1afc12c4972..72d98232909c7d10f401e7805b0be4ef52393765 100644 --- a/groupChat/send_test.go +++ b/groupChat/send_test.go @@ -10,11 +10,12 @@ package groupChat import ( "bytes" "encoding/base64" - "gitlab.com/elixxir/client/cmix/rounds" "gitlab.com/elixxir/client/cmix/identity/receptionID" + "gitlab.com/elixxir/client/cmix/rounds" gs "gitlab.com/elixxir/client/groupChat/groupStore" "gitlab.com/elixxir/crypto/group" "gitlab.com/elixxir/primitives/format" + "gitlab.com/elixxir/primitives/states" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/netTime" "math/rand" @@ -50,8 +51,10 @@ func TestManager_Send(t *testing.T) { t.Error("No group cMix messages received.") } + timestamps := make(map[states.Round]time.Time) + timestamps[states.QUEUED] = netTime.Now().Round(0) for _, msg := range messages { - reception.Process(msg, receptionID.EphemeralIdentity{}, rounds.Round{ID: roundId}) + reception.Process(msg, receptionID.EphemeralIdentity{}, rounds.Round{ID: roundId, Timestamps: timestamps}) select { case result := <-receiveChan: if !result.SenderID.Cmp(m.receptionId) { @@ -70,26 +73,13 @@ func TestManager_Send(t *testing.T) { } } -// Error path: an error is returned when Manager.neCmixMsg returns an error. -func TestGroup_newMessages_NewCmixMsgError(t *testing.T) { - expectedErr := strings.SplitN(newCmixErr, "%", 2)[0] - prng := rand.New(rand.NewSource(42)) - m, g := newTestManager(prng, t) - - _, err := m.newMessages(g, make([]byte, 1000), netTime.Now()) - if err == nil || !strings.Contains(err.Error(), expectedErr) { - t.Errorf("newMessages() failed to return the expected error."+ - "\nexpected: %s\nreceived: %+v", expectedErr, err) - } -} - // Error path: reader returns an error. func TestGroup_newCmixMsg_SaltReaderError(t *testing.T) { expectedErr := strings.SplitN(saltReadErr, "%", 2)[0] - m := &Manager{} + m, _ := newTestManager(rand.New(rand.NewSource(42)), t) - _, err := newCmixMsg(gs.Group{}, - []byte{}, time.Time{}, group.Member{}, strings.NewReader(""), m.receptionId, m.grp) + _, err := newCmixMsg(gs.Group{ID: id.NewIdFromString("test", id.User, t)}, + []byte{}, time.Time{}, group.Member{}, strings.NewReader(""), m.receptionId, m.services.GetMaxMessageLength()) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("newCmixMsg() failed to return the expected error"+ "\nexpected: %s\nreceived: %+v", expectedErr, err) @@ -102,15 +92,15 @@ func TestGroup_newCmixMsg_InternalMsgSizeError(t *testing.T) { // Create new test Manager and Group prng := rand.New(rand.NewSource(42)) - m, g := newTestManager(prng, t) + m, g := newTestManagerWithStore(prng, 10, 0, nil, nil, t) // Create test parameters - testMsg := make([]byte, 341) + testMsg := make([]byte, 1500) mem := group.Member{ID: id.NewIdFromString("memberID", id.User, t)} // Create cMix message prng = rand.New(rand.NewSource(42)) - _, err := newCmixMsg(g, testMsg, netTime.Now(), mem, prng, m.receptionId, m.grp) + _, err := newCmixMsg(g, testMsg, netTime.Now(), mem, prng, m.receptionId, m.services.GetMaxMessageLength()) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("newCmixMsg() failed to return the expected error"+ "\nexpected: %s\nreceived: %+v", expectedErr, err) diff --git a/groupChat/utils_test.go b/groupChat/utils_test.go index 4854e56500b3964a01628475f8e9a17d69a86264..afbb0ec0604330cb27b35227dcacb93ff0a7f2aa 100644 --- a/groupChat/utils_test.go +++ b/groupChat/utils_test.go @@ -47,19 +47,15 @@ func newTestManager(rng *rand.Rand, t *testing.T) (*Manager, gs.Group) { rng: fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG), grp: getGroup(), services: newTestNetworkManager(0, t), - e2e: &testE2eManager{ - e2eMessages: []testE2eMessage{}, - errSkip: 0, - sendErr: 0, - }, + e2e: newTestE2eManager(randCycInt(rng)), } user := group.Member{ ID: m.receptionId, - DhKey: randCycInt(rng), + DhKey: m.e2e.GetHistoricalDHPubkey(), } g := newTestGroupWithUser(m.grp, user.ID, user.DhKey, - randCycInt(rng), rng, t) + m.e2e.GetHistoricalDHPrivkey(), rng, t) gStore, err := gs.NewStore(versioned.NewKV(make(ekv.Memstore)), user) if err != nil { t.Fatalf("Failed to create new group store: %+v", err) @@ -84,14 +80,15 @@ func newTestManagerWithStore(rng *rand.Rand, numGroups int, sendErr int, services: newTestNetworkManager(sendErr, t), e2e: &testE2eManager{ e2eMessages: []testE2eMessage{}, - errSkip: 0, sendErr: sendErr, + grp: getGroup(), + dhPubKey: randCycInt(rng), + partners: make(map[id.ID]partner.Manager), }, } - user := group.Member{ ID: m.receptionId, - DhKey: randCycInt(rng), + DhKey: m.e2e.GetHistoricalDHPubkey(), } gStore, err := gs.NewStore(versioned.NewKV(make(ekv.Memstore)), user) @@ -111,6 +108,16 @@ func newTestManagerWithStore(rng *rand.Rand, numGroups int, sendErr int, return m, g } +func newTestE2eManager(dhPubKey *cyclic.Int) *testE2eManager { + return &testE2eManager{ + e2eMessages: []testE2eMessage{}, + errSkip: 0, + grp: getGroup(), + dhPubKey: dhPubKey, + partners: make(map[id.ID]partner.Manager), + } +} + // getMembership returns a Membership with random members for testing. func getMembership(size int, uid *id.ID, pubKey *cyclic.Int, grp *cyclic.Group, prng *rand.Rand, t *testing.T) group.Membership { @@ -219,6 +226,7 @@ func newTestNetworkManager(sendErr int, t *testing.T) GroupCmix { return &testNetworkManager{ receptionMessages: [][]format.Message{}, sendMessages: [][]cmix.TargetedCmixMessage{}, + grp: getGroup(), sendErr: sendErr, } } @@ -226,8 +234,11 @@ func newTestNetworkManager(sendErr int, t *testing.T) GroupCmix { // testE2eManager is a test implementation of NetworkManager interface. type testE2eManager struct { e2eMessages []testE2eMessage + partners map[id.ID]partner.Manager errSkip int sendErr int + dhPubKey *cyclic.Int + grp *cyclic.Group sync.RWMutex } @@ -237,20 +248,27 @@ type testE2eMessage struct { } func (tnm *testE2eManager) AddPartner(partnerID *id.ID, partnerPubKey, myPrivKey *cyclic.Int, - partnerSIDHPubKey *sidh.PublicKey, mySIDHPrivKey *sidh.PrivateKey, sendParams, receiveParams session.Params) (partner.Manager, error) { - return nil, nil + partnerSIDHPubKey *sidh.PublicKey, mySIDHPrivKey *sidh.PrivateKey, + sendParams, receiveParams session.Params) (partner.Manager, error) { + + testPartner := partner.NewTestManager(partnerID, partnerPubKey, myPrivKey, &testing.T{}) + tnm.partners[*partnerID] = testPartner + return testPartner, nil } func (tnm *testE2eManager) GetPartner(partnerID *id.ID) (partner.Manager, error) { - return nil, nil + if partner, ok := tnm.partners[*partnerID]; ok { + return partner, nil + } + return nil, errors.New("Unable to find partner") } func (tnm *testE2eManager) GetHistoricalDHPubkey() *cyclic.Int { - panic("implement me") + return tnm.dhPubKey } func (tnm *testE2eManager) GetHistoricalDHPrivkey() *cyclic.Int { - panic("implement me") + return tnm.dhPubKey } func (tnm *testE2eManager) SendE2E(mt catalog.MessageType, recipient *id.ID, payload []byte, params clientE2E.Params) ([]id.Round, e2e.MessageID, time.Time, error) { @@ -273,11 +291,11 @@ func (tnm *testE2eManager) SendE2E(mt catalog.MessageType, recipient *id.ID, pay } func (*testE2eManager) RegisterListener(user *id.ID, messageType catalog.MessageType, newListener receive.Listener) receive.ListenerID { - panic("implement me") + return receive.ListenerID{} } func (*testE2eManager) AddService(tag string, processor message.Processor) error { - panic("implement me") + return nil } func (*testE2eManager) GetDefaultHistoricalDHPubkey() *cyclic.Int { @@ -300,9 +318,14 @@ type testNetworkManager struct { sendMessages [][]cmix.TargetedCmixMessage errSkip int sendErr int + grp *cyclic.Group sync.RWMutex } +func (tnm *testNetworkManager) GetMaxMessageLength() int { + return format.NewMessage(tnm.grp.GetP().ByteLen()).ContentsSize() +} + func (tnm *testNetworkManager) SendMany(messages []cmix.TargetedCmixMessage, p cmix.CMIXParams) (id.Round, []ephemeral.Id, error) { if tnm.sendErr == 1 { return 0, nil, errors.New("SendManyCMIX error") @@ -315,7 +338,7 @@ func (tnm *testNetworkManager) SendMany(messages []cmix.TargetedCmixMessage, p c receiveMessages := []format.Message{} for _, msg := range messages { - receiveMsg := format.Message{} + receiveMsg := format.NewMessage(tnm.grp.GetP().ByteLen()) receiveMsg.SetMac(msg.Mac) receiveMsg.SetContents(msg.Payload) receiveMsg.SetKeyFP(msg.Fingerprint) @@ -326,11 +349,11 @@ func (tnm *testNetworkManager) SendMany(messages []cmix.TargetedCmixMessage, p c } func (*testNetworkManager) AddService(clientID *id.ID, newService message.Service, response message.Processor) { - panic("implement me") + return } func (*testNetworkManager) DeleteService(clientID *id.ID, toDelete message.Service, processor message.Processor) { - panic("implement me") + return } type dummyEventMgr struct{}