diff --git a/auth/callback.go b/auth/callback.go index 70b9c2843406205198cdb439666741b193c52518..6da4fd2fbd3ad612a7942cb2f7aac11337d6ee74 100644 --- a/auth/callback.go +++ b/auth/callback.go @@ -348,7 +348,7 @@ func (m *Manager) handleRequest(cmixMsg format.Message, var rndNum id.Round if autoConfirm || resetSession { // Call ConfirmRequestAuth to send confirmation - rndNum, err = m.ConfirmRequestAuth(c) + rndNum, err = m.confirmRequestAuth(c, true) if err != nil { jww.ERROR.Printf("Could not ConfirmRequestAuth: %+v", err) diff --git a/auth/confirm.go b/auth/confirm.go index 28a5292aec2ad83f888c35e1f19feb32a55acea7..40ddc527dc1f43da6b5f5071d0670d1702369d16 100644 --- a/auth/confirm.go +++ b/auth/confirm.go @@ -32,6 +32,11 @@ func (m *Manager) ConfirmRequestAuth(partner contact.Contact) (id.Round, error) return 0, errors.New("Cannot confirm authenticated message " + "when the network is not healthy") } + return m.confirmRequestAuth(partner, false) +} + +func (m *Manager) confirmRequestAuth(partner contact.Contact, critical bool) (id.Round, + error) { // Cannot confirm already established channels if _, err := m.storage.E2e().GetPartner(partner.ID); err == nil { @@ -156,15 +161,28 @@ func (m *Manager) ConfirmRequestAuth(partner contact.Contact) (id.Round, error) param.IdentityPreimage = preimg param.DebugTag = "auth.Confirm" /*send message*/ + if critical { + m.storage.GetCriticalRawMessages().AddProcessing(cmixMsg, + partner.ID) + } round, _, err := m.net.SendCMIX(cmixMsg, partner.ID, param) if err != nil { // if the send fails just set it to failed, it will but automatically // retried jww.INFO.Printf("Auth Confirm with %s (msgDigest: %s) failed "+ "to transmit: %+v", partner.ID, cmixMsg.Digest(), err) + if critical { + m.storage.GetCriticalRawMessages().Failed(cmixMsg, + partner.ID) + } return 0, errors.WithMessage(err, "Auth Confirm Failed to transmit") } + if critical { + m.storage.GetCriticalRawMessages().Succeeded(cmixMsg, + partner.ID) + } + em := fmt.Sprintf("Confirm Request with %s (msgDigest: %s) sent on round %d", partner.ID, cmixMsg.Digest(), round) jww.INFO.Print(em) diff --git a/auth/request.go b/auth/request.go index cd32a5116b2f62d4da3cb90e5cfc3c8da94aba50..8f39fc9db835f30e41e51e6a6fe5db377aef666f 100644 --- a/auth/request.go +++ b/auth/request.go @@ -88,10 +88,12 @@ func requestAuth(partner, me contact.Contact, rng io.Reader, reset bool, } else if err == nil { switch rqType { case auth.Receive: - // TODO: We've already received a request, so send a - // confirmation instead? - return 0, errors.Errorf("Cannot send a request after " + - "receiving a request") + if reset { + storage.Auth().DeleteRequest(partner.ID) + } else { + return 0, errors.Errorf("Cannot send a " + + "request after receiving a request") + } case auth.Sent: resend = true default: diff --git a/backup/backup.go b/backup/backup.go index 71ea5a210b8915be03f1f78458384caa2b4d8ac6..5280b8dc9257a86543c97b02244066b795f1b069 100644 --- a/backup/backup.go +++ b/backup/backup.go @@ -24,6 +24,7 @@ import ( "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/crypto/backup" "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/xx_network/primitives/id" ) // Error messages. @@ -149,7 +150,7 @@ func resumeBackup(updateBackupCb UpdateBackupFn, c *api.Client, // Setting backup trigger in client b.backupContainer.SetBackup(b.TriggerBackup) - jww.INFO.Print("Resumed backup with password loaded from storage.") + jww.INFO.Print("resumed backup with password loaded from storage.") return b, nil } @@ -302,9 +303,33 @@ func (b *Backup) assembleBackup() backup.Backup { // Get contacts bu.Contacts.Identities = b.store.E2e().GetPartners() + // Get pending auth requests + // NOTE: Received requests don't matter here, as those are either + // not yet noticed by user OR explicitly rejected. + bu.Contacts.Identities = append(bu.Contacts.Identities, + b.store.Auth().GetAllSentIDs()...) + jww.INFO.Printf("backup saw %d contacts", len(bu.Contacts.Identities)) + jww.DEBUG.Printf("contacts in backup list: %+v", bu.Contacts.Identities) + //deduplicate list + bu.Contacts.Identities = deduplicate(bu.Contacts.Identities) + + jww.INFO.Printf("backup saved %d contacts after deduplication", + len(bu.Contacts.Identities)) // Add the memoized JSON params bu.JSONParams = b.jsonParams return bu } + +func deduplicate(list []*id.ID) []*id.ID { + entryMap := make(map[id.ID]bool) + newList := make([]*id.ID, 0) + for i, _ := range list { + if _, value := entryMap[*list[i]]; !value { + entryMap[*list[i]] = true + newList = append(newList, list[i]) + } + } + return newList +} diff --git a/bindings/restoreContacts.go b/bindings/restoreContacts.go index b970c77a2ed8cbf5664f76a6754904edb9b8d0f0..aeead16fd7a7c94e4e4148d1291f1b6404f0a690 100644 --- a/bindings/restoreContacts.go +++ b/bindings/restoreContacts.go @@ -8,7 +8,9 @@ package bindings import ( + jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/xxmutils" + "gitlab.com/elixxir/crypto/contact" "gitlab.com/xx_network/primitives/id" ) @@ -74,11 +76,30 @@ func (r *RestoreContactsReport) GetRestoreContactsError() string { // the mobile phone apps and are not intended to be part of the xxDK. It // should be treated as internal functions specific to the phone apps. func RestoreContactsFromBackup(backupPartnerIDs []byte, client *Client, - udManager *UserDiscovery, + udManager *UserDiscovery, lookupCB LookupCallback, updatesCb RestoreContactsUpdater) *RestoreContactsReport { + extLookupCB := func(c contact.Contact, myErr error) { + jww.INFO.Printf("extLookupCB triggered: %v, %v", c, myErr) + bindingsContact := &Contact{c: &c} + errStr := "" + if myErr != nil { + jww.WARN.Printf("restore err on lookup: %+v", + myErr) + errStr = myErr.Error() + } + if lookupCB != nil { + jww.INFO.Printf("Calling lookupCB(%+v, %+v)", + bindingsContact, errStr) + lookupCB.Callback(bindingsContact, errStr) + } else { + jww.WARN.Printf("nil external lookup callback") + } + } + restored, failed, errs, err := xxmutils.RestoreContactsFromBackup( - backupPartnerIDs, &client.api, udManager.ud, updatesCb) + backupPartnerIDs, &client.api, udManager.ud, extLookupCB, + updatesCb) return &RestoreContactsReport{ restored: restored, diff --git a/cmd/ud.go b/cmd/ud.go index dc940811b9ff1888ae4b221b8fa8ad5accb66e7a..a8f6205a4fe173d62af1d9ef10c1b85574ad63e9 100644 --- a/cmd/ud.go +++ b/cmd/ud.go @@ -169,7 +169,7 @@ var udCmd = &cobra.Command{ jww.FATAL.Panicf("BATCHADD: Couldn't read file: %+v", err) } restored, _, _, err := xxmutils.RestoreContactsFromBackup( - idListFile, client, userDiscoveryMgr, nil) + idListFile, client, userDiscoveryMgr, nil, nil) if err != nil { jww.FATAL.Panicf("%+v", err) } diff --git a/storage/auth/store.go b/storage/auth/store.go index f0a48c6d7dd1c80b19d6c11aac046bb4da9a209d..2d81f960cf767702f21b5401e26e780c0ff74603 100644 --- a/storage/auth/store.go +++ b/storage/auth/store.go @@ -9,6 +9,8 @@ package auth import ( "encoding/json" + "sync" + "github.com/cloudflare/circl/dh/sidh" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" @@ -20,7 +22,6 @@ import ( "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/netTime" - "sync" ) const NoRequest = "Request Not Found" @@ -291,6 +292,20 @@ func (s *Store) GetAllReceived() []contact.Contact { return cList } +// GetAllReceived returns all pending received contact requests from storage. +func (s *Store) GetAllSentIDs() []*id.ID { + s.mux.RLock() + defer s.mux.RUnlock() + cList := make([]*id.ID, 0, len(s.requests)) + for key := range s.requests { + r := s.requests[key] + if r.rt == Sent { + cList = append(cList, r.sent.partner) + } + } + return cList +} + // GetFingerprint can return either a private key or a sentRequest if the // fingerprint is found. If it returns a sentRequest, then it takes the lock to // ensure there is only one operator at a time. The user of the API must release diff --git a/xxmutils/restoreContacts.go b/xxmutils/restoreContacts.go index b64947418cb401ea797c99cc8c5935a60335bfb9..5074508c6d70f21057a0dffe3300035f66162b26 100644 --- a/xxmutils/restoreContacts.go +++ b/xxmutils/restoreContacts.go @@ -24,9 +24,12 @@ import ( "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/client/ud" "gitlab.com/elixxir/crypto/contact" + "gitlab.com/elixxir/primitives/fact" "gitlab.com/xx_network/primitives/id" ) +type LookupCallback func(c contact.Contact, myErr error) + // RestoreContactsFromBackup takes as input the jason output of the // `NewClientFromBackup` function, unmarshals it into IDs, looks up // each ID in user discovery, and initiates a session reset request. @@ -36,7 +39,7 @@ import ( // the mobile phone apps and are not intended to be part of the xxDK. It // should be treated as internal functions specific to the phone apps. func RestoreContactsFromBackup(backupPartnerIDs []byte, client *api.Client, - udManager *ud.Manager, + udManager *ud.Manager, lookupCB LookupCallback, updatesCb interfaces.RestoreContactsUpdater) ([]*id.ID, []*id.ID, []error, error) { @@ -66,6 +69,9 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *api.Client, } lookupIDs, resetContacts, restored := checkRestoreState(idList, store) + jww.INFO.Printf("restoring %d backup partner IDs", len(lookupIDs)) + jww.DEBUG.Printf("backup partner IDs to restore: %+v", lookupIDs) + // State variables, how many we have looked up successfully // and how many we have already reset. totalCnt := len(idList) @@ -92,7 +98,8 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *api.Client, rsWg := &sync.WaitGroup{} rsWg.Add(numRoutines) for i := 0; i < numRoutines; i++ { - go LookupContacts(lookupCh, foundCh, failCh, udManager, lcWg) + go LookupContacts(lookupCh, foundCh, failCh, udManager, lookupCB, + lcWg) go ResetSessions(resetContactCh, restoredCh, failCh, *client, rsWg) } @@ -172,13 +179,13 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *api.Client, // the mobile phone apps and are not intended to be part of the xxDK. It // should be treated as internal functions specific to the phone apps. func LookupContacts(in chan *id.ID, out chan *contact.Contact, - failCh chan failure, udManager *ud.Manager, + failCh chan failure, udManager *ud.Manager, extLookupCB LookupCallback, wg *sync.WaitGroup) { defer wg.Done() // Start looking up contacts with user discovery and feed this // contacts channel. for lookupID := range in { - c, err := LookupContact(lookupID, udManager) + c, err := LookupContact(lookupID, udManager, extLookupCB) if err == nil { out <- c continue @@ -220,8 +227,8 @@ func ResetSessions(in, out chan *contact.Contact, failCh chan failure, // xxDK users should not use this function. This function is used by // the mobile phone apps and are not intended to be part of the xxDK. It // should be treated as internal functions specific to the phone apps. -func LookupContact(userID *id.ID, udManager *ud.Manager) ( - *contact.Contact, error) { +func LookupContact(userID *id.ID, udManager *ud.Manager, + extLookupCB LookupCallback) (*contact.Contact, error) { // This is a little wonky, but wait until we get called then // set the result to the contact objects details if there is // no error @@ -229,11 +236,23 @@ func LookupContact(userID *id.ID, udManager *ud.Manager) ( var result *contact.Contact var err error lookupCB := func(c contact.Contact, myErr error) { - defer waiter.Unlock() - if myErr != nil { + if myErr == nil { + newOwnership := make([]byte, len(c.OwnershipProof)) + copy(newOwnership, c.OwnershipProof) + newFacts, _, _ := fact.UnstringifyFactList( + c.Facts.Stringify()) + result = &contact.Contact{ + ID: c.ID.DeepCopy(), + DhPubKey: c.DhPubKey.DeepCopy(), + OwnershipProof: newOwnership, + Facts: newFacts, + } + } else { err = myErr + result = nil } - result = &c + waiter.Unlock() + extLookupCB(c, myErr) } // Take lock once to make sure I will wait waiter.Lock()