From 1a892678067a8056749aa77fdbd4e0eea6f03c20 Mon Sep 17 00:00:00 2001 From: Jono Wenger <jono@elixxir.io> Date: Wed, 13 Apr 2022 18:38:48 +0000 Subject: [PATCH] Fix backup package --- auth/confirm.go | 2 +- auth/receivedConfirm.go | 4 +- auth/state.go | 28 +-- backup/backup.go | 176 +++++++++--------- backup/backup_test.go | 187 ++++++++++---------- interfaces/backup.go => backup/container.go | 10 +- backup/utils_test.go | 156 ++++++++++++++++ 7 files changed, 368 insertions(+), 195 deletions(-) rename interfaces/backup.go => backup/container.go (83%) create mode 100644 backup/utils_test.go diff --git a/auth/confirm.go b/auth/confirm.go index e523715c7..ee89b8f18 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 1990fd5bd..07e12df9b 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 7ced7eb8d..dfb6cb03c 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 ea9667c03..291348fa6 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 434c76e78..076fc2d8c 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 559b4b0f8..fd456ea96 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 000000000..c16969ff8 --- /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-----` -- GitLab