diff --git a/auth/callback.go b/auth/callback.go index 8b56de709682876fcf051e760a3c52baa7c9d53a..c82f0c85086ce903c1317056d26e620c1a0930fa 100644 --- a/auth/callback.go +++ b/auth/callback.go @@ -9,6 +9,7 @@ package auth import ( "fmt" + auth2 "gitlab.com/elixxir/client/auth/store" "gitlab.com/elixxir/client/catalog" "strings" @@ -19,7 +20,6 @@ import ( "gitlab.com/elixxir/client/interfaces/message" "gitlab.com/elixxir/client/interfaces/preimage" "gitlab.com/elixxir/client/stoppable" - "gitlab.com/elixxir/client/storage/auth" "gitlab.com/elixxir/client/storage/edge" "gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/crypto/cyclic" @@ -76,11 +76,11 @@ func (m *Manager) processAuthMessage(msg message.Receive) { grp := m.storage.E2e().GetGroup() switch fpType { - case auth.General: + case auth2.General: // if it is general, that means a new request has // been received m.handleRequest(cmixMsg, myHistoricalPrivKey, grp) - case auth.Specific: + case auth2.Specific: // if it is specific, that means the original request was sent // by this users and a confirmation has been received jww.INFO.Printf("Received AuthConfirm from %s, msgDigest: %s", @@ -194,7 +194,7 @@ func (m *Manager) handleRequest(cmixMsg format.Message, // a non-duplicate request, so delete the old one (to cause new // callback to be called) rType, _, _, err := m.storage.Auth().GetRequest(partnerID) - if err != nil && rType == auth.Receive { + if err != nil && rType == auth2.Receive { m.storage.Auth().Delete(partnerID) } @@ -238,7 +238,7 @@ func (m *Manager) handleRequest(cmixMsg format.Message, } else { //check if the relationship already exists, rType, _, c, err := m.storage.Auth().GetRequest(partnerID) - if err != nil && !strings.Contains(err.Error(), auth.NoRequest) { + if err != nil && !strings.Contains(err.Error(), auth2.NoRequest) { // if another error is received, print it and exit em := fmt.Sprintf("Received new Auth request for %s, "+ "internal lookup produced bad result: %+v", @@ -250,7 +250,7 @@ func (m *Manager) handleRequest(cmixMsg format.Message, //handle the events where the relationship already exists switch rType { // if this is a duplicate, ignore the message - case auth.Receive: + case auth2.Receive: em := fmt.Sprintf("Received new Auth request for %s, "+ "is a duplicate", partnerID) jww.WARN.Print(em) @@ -267,7 +267,7 @@ func (m *Manager) handleRequest(cmixMsg format.Message, return // if we sent a request, then automatically confirm // then exit, nothing else needed - case auth.Sent: + case auth2.Sent: jww.INFO.Printf("Received AuthRequest from %s,"+ " msgDigest: %s which has been requested, auto-confirming", partnerID, cmixMsg.Digest()) @@ -386,7 +386,7 @@ func (m *Manager) handleRequest(cmixMsg format.Message, return } -func (m *Manager) handleConfirm(cmixMsg format.Message, sr *auth.SentRequest, +func (m *Manager) handleConfirm(cmixMsg format.Message, sr *auth2.SentRequest, grp *cyclic.Group) { events := m.net.GetEventManager() @@ -465,7 +465,7 @@ func (m *Manager) handleConfirm(cmixMsg format.Message, sr *auth.SentRequest, } } -func (m *Manager) doConfirm(sr *auth.SentRequest, grp *cyclic.Group, +func (m *Manager) doConfirm(sr *auth2.SentRequest, grp *cyclic.Group, partnerPubKey, myPrivateKeyOwnershipProof, partnerPubKeyOwnershipProof *cyclic.Int, ownershipProof []byte, partnerSIDHPubKey *sidh.PublicKey) error { // verify the message came from the intended recipient diff --git a/auth/manager_test.go b/auth/manager_test.go index 7332a2db6d45ba7e2c0d9ddbfb18bc02d06ab286..2e384f83ffa6829624b07ab35ac2ef7b259b0e34 100644 --- a/auth/manager_test.go +++ b/auth/manager_test.go @@ -9,9 +9,9 @@ package auth import ( "github.com/cloudflare/circl/dh/sidh" + auth2 "gitlab.com/elixxir/client/auth/store" "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/storage" - "gitlab.com/elixxir/client/storage/auth" util "gitlab.com/elixxir/client/storage/utility" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/contact" @@ -76,7 +76,7 @@ loop: } } -func makeTestStore(t *testing.T) (*auth.Store, *versioned.KV, []*cyclic.Int) { +func makeTestStore(t *testing.T) (*auth2.Store, *versioned.KV, []*cyclic.Int) { kv := versioned.NewKV(make(ekv.Memstore)) grp := cyclic.NewGroup(large.NewInt(173), large.NewInt(0)) privKeys := make([]*cyclic.Int, 10) @@ -84,7 +84,7 @@ func makeTestStore(t *testing.T) (*auth.Store, *versioned.KV, []*cyclic.Int) { privKeys[i] = grp.NewInt(rand.Int63n(170) + 1) } - store, err := auth.NewStore(kv, grp, privKeys) + store, err := auth2.NewStore(kv, grp, privKeys) if err != nil { t.Fatalf("Failed to create new Store: %+v", err) } diff --git a/auth/request.go b/auth/request.go index 650d4f4ac314a9fe5cc31e0160f22becfad00c81..e3d987e577698225806423ccb1f26a8897711966 100644 --- a/auth/request.go +++ b/auth/request.go @@ -8,6 +8,7 @@ package auth import ( + auth2 "gitlab.com/elixxir/client/auth/store" "gitlab.com/elixxir/client/catalog" e2e2 "gitlab.com/elixxir/client/e2e/ratchet" "io" @@ -20,7 +21,6 @@ import ( "gitlab.com/elixxir/client/interfaces/params" "gitlab.com/elixxir/client/interfaces/preimage" "gitlab.com/elixxir/client/storage" - "gitlab.com/elixxir/client/storage/auth" "gitlab.com/elixxir/client/storage/edge" util "gitlab.com/elixxir/client/storage/utility" "gitlab.com/elixxir/crypto/contact" @@ -57,7 +57,7 @@ func ResetSession(partner, me contact.Contact, rng io.Reader, } rqType, _, _, err := storage.Auth().GetRequest(partner.ID) - if err == nil && rqType == auth.Sent { + if err == nil && rqType == auth2.Sent { return 0, errors.New("Cannot reset a session after " + "sending request, caller must resend request instead") } @@ -82,18 +82,18 @@ func requestAuth(partner, me contact.Contact, rng io.Reader, reset bool, //lookup if an ongoing request is occurring rqType, sr, _, err := storage.Auth().GetRequest(partner.ID) - if err != nil && !strings.Contains(err.Error(), auth.NoRequest) { + if err != nil && !strings.Contains(err.Error(), auth2.NoRequest) { return 0, errors.WithMessage(err, "Cannot send a request after receiving unknown error "+ "on requesting contact status") } else if err == nil { switch rqType { - case auth.Receive: + case auth2.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") - case auth.Sent: + case auth2.Sent: resend = true default: return 0, errors.Errorf("Cannot send a request after "+ diff --git a/storage/auth/authID.go b/auth/store/authID.go similarity index 98% rename from storage/auth/authID.go rename to auth/store/authID.go index 360bc3e6beb9824612f38c39bed7a28ea4946b86..cf9a4b628f44f3e4a7c61eb0d7bcf260f06a7511 100644 --- a/storage/auth/authID.go +++ b/auth/store/authID.go @@ -1,4 +1,4 @@ -package auth +package store import ( "encoding/base64" diff --git a/storage/auth/confirmation.go b/auth/store/confirmation.go similarity index 99% rename from storage/auth/confirmation.go rename to auth/store/confirmation.go index b55c79433db9d7caf51d303caee06e00665df011..6bd37b62dada4e1e5d3b6ebed7e1aa85700d283b 100644 --- a/storage/auth/confirmation.go +++ b/auth/store/confirmation.go @@ -5,7 +5,7 @@ // LICENSE file // //////////////////////////////////////////////////////////////////////////////// -package auth +package store import ( "encoding/base64" diff --git a/storage/auth/confirmation_test.go b/auth/store/confirmation_test.go similarity index 99% rename from storage/auth/confirmation_test.go rename to auth/store/confirmation_test.go index 2de100e510a38051aec12d28701a44badcf246ce..6e53a62958bad90184be184d9ab10ee543fe82fe 100644 --- a/storage/auth/confirmation_test.go +++ b/auth/store/confirmation_test.go @@ -5,7 +5,7 @@ // LICENSE file // //////////////////////////////////////////////////////////////////////////////// -package auth +package store import ( "github.com/cloudflare/circl/dh/sidh" diff --git a/auth/store/deletion.go b/auth/store/deletion.go new file mode 100644 index 0000000000000000000000000000000000000000..034ec59b9b242f9a459e7904384fa720a12598d1 --- /dev/null +++ b/auth/store/deletion.go @@ -0,0 +1,126 @@ +package store + +import ( + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + util "gitlab.com/elixxir/client/storage/utility" + "gitlab.com/xx_network/primitives/id" +) + +// DeleteAllRequests clears the request map and all associated storage objects +// containing request data. +func (s *Store) DeleteAllRequests() error { + s.mux.Lock() + defer s.mux.Unlock() + + for partnerId, req := range s.receivedByID { + switch req.rt { + case Sent: + s.deleteSentRequest(req) + delete(s.receivedByID, partnerId) + case Receive: + s.deleteReceiveRequest(req) + delete(s.receivedByID, partnerId) + } + + } + + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to store updated request map after "+ + "deleting all receivedByID: %+v", err) + } + + return nil +} + +// DeleteRequest deletes a request from Store given a partner ID. +// If the partner ID exists as a request, then the request will be deleted +// and the state stored. If the partner does not exist, then an error will +// be returned. +func (s *Store) DeleteRequest(partnerId *id.ID) error { + s.mux.Lock() + defer s.mux.Unlock() + + req, ok := s.receivedByID[*partnerId] + if !ok { + return errors.Errorf("Request for %s does not exist", partnerId) + } + + switch req.rt { + case Sent: + s.deleteSentRequest(req) + case Receive: + s.deleteReceiveRequest(req) + } + + delete(s.receivedByID, *partnerId) + + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to store updated request map after "+ + "deleting partner request for partner %s: %+v", partnerId, err) + } + + return nil +} + +// DeleteSentRequests deletes all Sent receivedByID from Store. +func (s *Store) DeleteSentRequests() error { + s.mux.Lock() + defer s.mux.Unlock() + + for partnerId, req := range s.receivedByID { + switch req.rt { + case Sent: + s.deleteSentRequest(req) + delete(s.receivedByID, partnerId) + case Receive: + continue + } + } + + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to store updated request map after "+ + "deleting all sent receivedByID: %+v", err) + } + + return nil +} + +// DeleteReceiveRequests deletes all Receive receivedByID from Store. +func (s *Store) DeleteReceiveRequests() error { + s.mux.Lock() + defer s.mux.Unlock() + + for partnerId, req := range s.receivedByID { + switch req.rt { + case Sent: + continue + case Receive: + s.deleteReceiveRequest(req) + delete(s.receivedByID, partnerId) + } + } + + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to store updated request map after "+ + "deleting all partner receivedByID: %+v", err) + } + + return nil +} + +// deleteSentRequest is a helper function which deletes a Sent request from storage. +func (s *Store) deleteSentRequest(r *ReceivedRequest) { + delete(s.sentByFingerprints, r.sent.fingerprint) + if err := r.sent.delete(); err != nil { + jww.FATAL.Panicf("Failed to delete sent request: %+v", err) + } +} + +// deleteReceiveRequest is a helper function which deletes a Receive request from storage. +func (s *Store) deleteReceiveRequest(r *ReceivedRequest) { + if err := util.DeleteContact(s.kv, r.partner.ID); err != nil { + jww.FATAL.Panicf("Failed to delete recieved request "+ + "contact: %+v", err) + } +} diff --git a/storage/auth/fingerprint.go b/auth/store/fingerprint.go similarity index 98% rename from storage/auth/fingerprint.go rename to auth/store/fingerprint.go index b4aa9941490d79ad7cf37319f16b82f77a43cca1..3a99453a985f02f86e0e54636cd55b1249dfebbc 100644 --- a/storage/auth/fingerprint.go +++ b/auth/store/fingerprint.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package auth +package store import "gitlab.com/elixxir/crypto/cyclic" diff --git a/storage/auth/previousNegotiations.go b/auth/store/previousNegotiations.go similarity index 66% rename from storage/auth/previousNegotiations.go rename to auth/store/previousNegotiations.go index f00a5cdf9df348c4cb088fd4ac0223555e23e507..db49a7b25c7e8223299eff7b93d943d6a9dd92fe 100644 --- a/storage/auth/previousNegotiations.go +++ b/auth/store/previousNegotiations.go @@ -12,11 +12,13 @@ // LICENSE file // //////////////////////////////////////////////////////////////////////////////// -package auth +package store import ( "bytes" + "encoding/base64" "encoding/binary" + "encoding/json" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/e2e/auth" @@ -27,7 +29,7 @@ import ( const ( negotiationPartnersKey = "NegotiationPartners" - negotiationPartnersVersion = 0 + negotiationPartnersVersion = 1 negotiationFingerprintsKeyPrefix = "NegotiationFingerprints/" currentNegotiationFingerprintsVersion = 0 ) @@ -40,19 +42,20 @@ const ( // If the partner exists and the fingerprint exists, return // newFingerprint = false, latest = false or latest = true if it is the last one // in the list. -func (s *Store) AddIfNew(partner *id.ID, negotiationFingerprint []byte) ( +func (s *Store) AddIfNew(partner, myID *id.ID, negotiationFingerprint []byte) ( newFingerprint, latest bool) { s.mux.Lock() defer s.mux.Unlock() // If the partner does not exist, add it to the list and store a new // fingerprint to storage - _, exists := s.previousNegotiations[*partner] + aid := makeAuthIdentity(partner, myID) + _, exists := s.previousNegotiations[aid] if !exists { - s.previousNegotiations[*partner] = struct{}{} + s.previousNegotiations[aid] = struct{}{} // Save fingerprint to storage - err := s.saveNegotiationFingerprints(partner, negotiationFingerprint) + err := s.saveNegotiationFingerprints(partner, myID, negotiationFingerprint) if err != nil { jww.FATAL.Panicf("Failed to save negotiation sentByFingerprints for "+ "partner %s: %+v", partner, err) @@ -72,7 +75,7 @@ func (s *Store) AddIfNew(partner *id.ID, negotiationFingerprint []byte) ( } // get the fingerprint list from storage - fingerprints, err := s.loadNegotiationFingerprints(partner) + fingerprints, err := s.loadNegotiationFingerprints(partner, myID) if err != nil { jww.FATAL.Panicf("Failed to load negotiation sentByFingerprints for "+ "partner %s: %+v", partner, err) @@ -94,7 +97,7 @@ func (s *Store) AddIfNew(partner *id.ID, negotiationFingerprint []byte) ( // If the partner does exist and the fingerprint does not exist, then add // the fingerprint to the list as latest fingerprints = append(fingerprints, negotiationFingerprint) - err = s.saveNegotiationFingerprints(partner, fingerprints...) + err = s.saveNegotiationFingerprints(partner, myID, fingerprints...) if err != nil { jww.FATAL.Panicf("Failed to save negotiation sentByFingerprints for "+ "partner %s: %+v", partner, err) @@ -108,15 +111,17 @@ func (s *Store) AddIfNew(partner *id.ID, negotiationFingerprint []byte) ( // deletePreviousNegotiationPartner removes the partner, its sentByFingerprints, and // its confirmations from memory and storage. -func (s *Store) deletePreviousNegotiationPartner(partner *id.ID) error { +func (s *Store) deletePreviousNegotiationPartner(partner, myID *id.ID) error { + + aid := makeAuthIdentity(partner, myID) // Do nothing if the partner does not exist - if _, exists := s.previousNegotiations[*partner]; !exists { + if _, exists := s.previousNegotiations[aid]; !exists { return nil } // Delete partner from memory - delete(s.previousNegotiations, *partner) + delete(s.previousNegotiations, aid) // Delete partner from storage and return an error err := s.savePreviousNegotiations() @@ -125,14 +130,14 @@ func (s *Store) deletePreviousNegotiationPartner(partner *id.ID) error { } // Check if sentByFingerprints exist - fingerprints, err := s.loadNegotiationFingerprints(partner) + fingerprints, err := s.loadNegotiationFingerprints(partner, myID) // If sentByFingerprints exist for this partner, delete them from storage and any // accompanying confirmations if err == nil { // Delete the fingerprint list from storage but do not return the error // until after attempting to delete the confirmations - err = s.kv.Delete(makeNegotiationFingerprintsKey(partner), + err = s.kv.Delete(makeNegotiationFingerprintsKey(partner, myID), currentNegotiationFingerprintsVersion) // Delete all confirmations from storage @@ -160,44 +165,72 @@ func (s *Store) savePreviousNegotiations() error { // newOrLoadPreviousNegotiations loads the list of previousNegotiations partners // from storage. -func (s *Store) newOrLoadPreviousNegotiations() (map[id.ID]struct{}, error) { +func (s *Store) newOrLoadPreviousNegotiations() (map[authIdentity]struct{}, error) { + obj, err := s.kv.Get(negotiationPartnersKey, negotiationPartnersVersion) if err != nil { if strings.Contains(err.Error(), "object not found") || strings.Contains(err.Error(), "no such file or directory") { - return make(map[id.ID]struct{}), nil + obj, err = s.kv.Get(negotiationPartnersKey, 0) + if err != nil { + if strings.Contains(err.Error(), "object not found") || + strings.Contains(err.Error(), "no such file or directory") { + return make(map[authIdentity]struct{}), nil + } else { + return nil, err + } + } + return unmarshalOldPreviousNegotiations(obj.Data, s.defaultID), nil + } else { + return nil, err } - return nil, err } - return unmarshalPreviousNegotiations(obj.Data), nil + return unmarshalPreviousNegotiations(obj.Data) } // marshalPreviousNegotiations marshals the list of partners into a byte slice. -func marshalPreviousNegotiations(partners map[id.ID]struct{}) []byte { - buff := bytes.NewBuffer(nil) - buff.Grow(8 + (len(partners) * id.ArrIDLen)) +func marshalPreviousNegotiations(partners map[authIdentity]struct{}) []byte { + toMarshal := make([]authIdentity, 0, len(partners)) - // Write number of partners to buffer - b := make([]byte, 8) - binary.LittleEndian.PutUint64(b, uint64(len(partners))) - buff.Write(b) + for aid := range partners { + toMarshal = append(toMarshal, aid) + } - // Write each partner ID to buffer - for partner := range partners { - buff.Write(partner.Marshal()) + b, err := json.Marshal(&toMarshal) + if err != nil { + jww.FATAL.Panicf("Failed to unmarshal previous negotations", err) } - return buff.Bytes() + return b } -// unmarshalPreviousNegotiations unmarshalls the marshalled byte slice into a +// unmarshalPreviousNegotiations unmarshalls the marshalled json into a +//// list of partner IDs. +func unmarshalPreviousNegotiations(b []byte) (map[authIdentity]struct{}, + error) { + unmarshal := make([]authIdentity, 0) + + if err := json.Unmarshal(b, &unmarshal); err != nil { + return nil, err + } + + partners := make(map[authIdentity]struct{}) + + for _, aid := range unmarshal { + partners[aid] = struct{}{} + } + + return partners, nil +} + +// unmarshalOldPreviousNegotiations unmarshalls the marshalled json into a // list of partner IDs. -func unmarshalPreviousNegotiations(buf []byte) map[id.ID]struct{} { +func unmarshalOldPreviousNegotiations(buf []byte, defaultID *id.ID) map[authIdentity]struct{} { buff := bytes.NewBuffer(buf) numberOfPartners := binary.LittleEndian.Uint64(buff.Next(8)) - partners := make(map[id.ID]struct{}, numberOfPartners) + partners := make(map[authIdentity]struct{}, numberOfPartners) for i := uint64(0); i < numberOfPartners; i++ { partner, err := id.Unmarshal(buff.Next(id.ArrIDLen)) @@ -206,7 +239,7 @@ func unmarshalPreviousNegotiations(buf []byte) map[id.ID]struct{} { "Failed to unmarshal negotiation partner ID: %+v", err) } - partners[*partner] = struct{}{} + partners[makeAuthIdentity(partner, defaultID)] = struct{}{} } return partners @@ -215,7 +248,7 @@ func unmarshalPreviousNegotiations(buf []byte) map[id.ID]struct{} { // saveNegotiationFingerprints saves the list of sentByFingerprints for the given // partner to storage. func (s *Store) saveNegotiationFingerprints( - partner *id.ID, fingerprints ...[]byte) error { + partner, myID *id.ID, fingerprints ...[]byte) error { obj := &versioned.Object{ Version: currentNegotiationFingerprintsVersion, @@ -223,17 +256,29 @@ func (s *Store) saveNegotiationFingerprints( Data: marshalNegotiationFingerprints(fingerprints...), } - return s.kv.Set(makeNegotiationFingerprintsKey(partner), + return s.kv.Set(makeNegotiationFingerprintsKey(partner, myID), currentNegotiationFingerprintsVersion, obj) } // loadNegotiationFingerprints loads the list of sentByFingerprints for the given // partner from storage. -func (s *Store) loadNegotiationFingerprints(partner *id.ID) ([][]byte, error) { - obj, err := s.kv.Get(makeNegotiationFingerprintsKey(partner), +func (s *Store) loadNegotiationFingerprints(partner, myID *id.ID) ([][]byte, error) { + obj, err := s.kv.Get(makeNegotiationFingerprintsKey(partner, myID), currentNegotiationFingerprintsVersion) if err != nil { - return nil, err + if myID.Cmp(s.defaultID) { + obj, err = s.kv.Get(makeOldNegotiationFingerprintsKey(partner), + currentNegotiationFingerprintsVersion) + if err != nil { + return nil, err + } + if err = s.kv.Set(makeNegotiationFingerprintsKey(partner, myID), + currentNegotiationFingerprintsVersion, obj); err != nil { + return nil, err + } + } else { + return nil, err + } } return unmarshalNegotiationFingerprints(obj.Data), nil @@ -274,8 +319,17 @@ func unmarshalNegotiationFingerprints(buf []byte) [][]byte { return fingerprints } +// makeOldNegotiationFingerprintsKey generates the key used to load and store +// negotiation sentByFingerprints for the partner. +func makeOldNegotiationFingerprintsKey(partner *id.ID) string { + return negotiationFingerprintsKeyPrefix + + string(base64.StdEncoding.EncodeToString(partner.Marshal())) +} + // makeNegotiationFingerprintsKey generates the key used to load and store // negotiation sentByFingerprints for the partner. -func makeNegotiationFingerprintsKey(partner *id.ID) string { - return negotiationFingerprintsKeyPrefix + partner.String() +func makeNegotiationFingerprintsKey(partner, myID *id.ID) string { + return negotiationFingerprintsKeyPrefix + + string(base64.StdEncoding.EncodeToString(partner.Marshal())) + + string(base64.StdEncoding.EncodeToString(myID.Marshal())) } diff --git a/storage/auth/previousNegotiations_test.go b/auth/store/previousNegotiations_test.go similarity index 99% rename from storage/auth/previousNegotiations_test.go rename to auth/store/previousNegotiations_test.go index 7f840e633342848e88cd34a6db11b35989e540fb..b227fb5c501ac1cf8825e4ce2328d5211421ec16 100644 --- a/storage/auth/previousNegotiations_test.go +++ b/auth/store/previousNegotiations_test.go @@ -5,7 +5,7 @@ // LICENSE file // //////////////////////////////////////////////////////////////////////////////// -package auth +package store import ( "github.com/cloudflare/circl/dh/sidh" @@ -414,7 +414,7 @@ func Test_marshalNegotiationFingerprints_unmarshalNegotiationFingerprints(t *tes } } -// Consistency test of makeNegotiationFingerprintsKey. +// Consistency test of makeOldNegotiationFingerprintsKey. func Test_makeNegotiationFingerprintsKey_Consistency(t *testing.T) { prng := rand.New(rand.NewSource(42)) expectedKeys := []string{ @@ -439,7 +439,7 @@ func Test_makeNegotiationFingerprintsKey_Consistency(t *testing.T) { for i, expected := range expectedKeys { partner, _ := id.NewRandomID(prng, id.User) - key := makeNegotiationFingerprintsKey(partner) + key := makeOldNegotiationFingerprintsKey(partner) if expected != key { t.Errorf("Negotiation sentByFingerprints key does not match expected "+ "for partner %s (%d).\nexpected: %q\nreceived: %q", partner, i, diff --git a/storage/auth/receivedRequest.go b/auth/store/receivedRequest.go similarity index 87% rename from storage/auth/receivedRequest.go rename to auth/store/receivedRequest.go index d83f7556a175665902ff8291aa544df9eb905320..2dd7153d930bd8251f082ad297df9689b7fa938c 100644 --- a/storage/auth/receivedRequest.go +++ b/auth/store/receivedRequest.go @@ -1,4 +1,4 @@ -package auth +package store import ( "github.com/cloudflare/circl/dh/sidh" @@ -101,10 +101,6 @@ func loadReceivedRequest(kv *versioned.KV, partner *id.ID, myID *id.ID) ( }, nil } -func (rr *ReceivedRequest) getType() RequestType { - return Receive -} - func (rr *ReceivedRequest) GetMyID() *id.ID { return rr.myID } @@ -116,3 +112,19 @@ func (rr *ReceivedRequest) GetContact() contact.Contact { func (rr *ReceivedRequest) GetTheirSidHPubKeyA() *sidh.PublicKey { return rr.theirSidHPubKeyA } + +func (rr *ReceivedRequest) delete() { + if err := util.DeleteContact(rr.kv, rr.partner.ID); err != nil { + jww.FATAL.Panicf("Failed to delete received request "+ + "contact for %s to %s", rr.partner.ID, rr.myID) + } + if err := util.DeleteSIDHPublicKey(rr.kv, + util.MakeSIDHPublicKeyKey(rr.partner.ID)); err != nil { + jww.FATAL.Panicf("Failed to delete received request "+ + "SIDH pubkey for %s to %s", rr.partner.ID, rr.myID) + } +} + +func (rr *ReceivedRequest) getType() RequestType { + return Receive +} diff --git a/storage/auth/request.go b/auth/store/request.go similarity index 97% rename from storage/auth/request.go rename to auth/store/request.go index e3da8be3c0332cb9ee28d2251f587e9589433cc5..634ec0c3771dba81bf32bcc951d9c79acb602f4e 100644 --- a/storage/auth/request.go +++ b/auth/store/request.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package auth +package store type RequestType uint diff --git a/storage/auth/sentRequest.go b/auth/store/sentRequest.go similarity index 96% rename from storage/auth/sentRequest.go rename to auth/store/sentRequest.go index ce177ee0d7f3e1b17be96d3c08155cfd0818ebe2..02721bdd251ca1503f71041e420c5c6447ddfa47 100644 --- a/storage/auth/sentRequest.go +++ b/auth/store/sentRequest.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package auth +package store import ( "encoding/hex" @@ -19,6 +19,7 @@ import ( "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/netTime" + "sync" ) const currentSentRequestVersion = 0 @@ -36,6 +37,8 @@ type SentRequest struct { mySidHPrivKeyA *sidh.PrivateKey mySidHPubKeyA *sidh.PublicKey fingerprint format.Fingerprint + + mux sync.Mutex } type sentRequestDisk struct { @@ -223,9 +226,12 @@ func (sr *SentRequest) save() error { currentSentRequestVersion, &obj) } -func (sr *SentRequest) delete() error { - return sr.kv.Delete(versioned.MakePartnerPrefix(sr.partner), - currentSentRequestVersion) +func (sr *SentRequest) delete() { + if err := sr.kv.Delete(versioned.MakePartnerPrefix(sr.partner), + currentSentRequestVersion); err != nil { + jww.FATAL.Panicf("Failed to delete sent request from %s to %s: "+ + "%+v", sr.partner, sr.partner, err) + } } func (sr *SentRequest) GetPartner() *id.ID { diff --git a/storage/auth/sentRequestHandler.go b/auth/store/sentRequestHandler.go similarity index 86% rename from storage/auth/sentRequestHandler.go rename to auth/store/sentRequestHandler.go index 792d6d7d2e4c777aff344e9ac35dbfacffd841b9..d6d4b77a2df617804ee3ddbf99b2a186025d1e63 100644 --- a/storage/auth/sentRequestHandler.go +++ b/auth/store/sentRequestHandler.go @@ -1,4 +1,4 @@ -package auth +package store type SentRequestHandler interface { Add(sr *SentRequest) diff --git a/storage/auth/store.go b/auth/store/store.go similarity index 58% rename from storage/auth/store.go rename to auth/store/store.go index 8582c7571bf2886b0770f63f99a3b105a8304044..46b70ed606243a3767fd51af6a2630179857f24f 100644 --- a/storage/auth/store.go +++ b/auth/store/store.go @@ -5,14 +5,13 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package auth +package store import ( "encoding/json" "github.com/cloudflare/circl/dh/sidh" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - util "gitlab.com/elixxir/client/storage/utility" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/crypto/cyclic" @@ -35,7 +34,7 @@ type Store struct { receivedByID map[authIdentity]*ReceivedRequest sentByID map[authIdentity]*SentRequest - previousNegotiations map[id.ID]struct{} + previousNegotiations map[authIdentity]struct{} defaultID *id.ID @@ -53,7 +52,7 @@ func NewStore(kv *versioned.KV, grp *cyclic.Group, srh SentRequestHandler) error grp: grp, receivedByID: make(map[authIdentity]*ReceivedRequest), sentByID: make(map[authIdentity]*SentRequest), - previousNegotiations: make(map[id.ID]struct{}), + previousNegotiations: make(map[authIdentity]struct{}), srh: srh, } @@ -76,7 +75,7 @@ func LoadStore(kv *versioned.KV, defaultID *id.ID, grp *cyclic.Group, srh SentRe grp: grp, receivedByID: make(map[authIdentity]*ReceivedRequest), sentByID: make(map[authIdentity]*SentRequest), - previousNegotiations: make(map[id.ID]struct{}), + previousNegotiations: make(map[authIdentity]struct{}), defaultID: defaultID, srh: srh, } @@ -195,6 +194,10 @@ func (s *Store) AddSent(partner, myID *id.ID, partnerHistoricalPubKey, myPrivKey return nil, errors.Errorf("Cannot make new sentRequest for partner "+ "%s, one already exists", partner) } + if _, ok := s.receivedByID[aid]; ok { + return nil, errors.Errorf("Cannot make new sentRequest for partner "+ + "%s, one already exists", partner) + } sr, err := newSentRequest(s.kv, partner, myID, partnerHistoricalPubKey, myPrivKey, myPubKey, sidHPrivA, sidHPubA, fp) @@ -220,6 +223,10 @@ func (s *Store) AddReceived(myID *id.ID, c contact.Contact, key *sidh.PublicKey) return errors.Errorf("Cannot add contact for partner "+ "%s, one already exists", c.ID) } + if _, ok := s.sentByID[aih]; ok { + return errors.Errorf("Cannot add contact for partner "+ + "%s, one already exists", c.ID) + } r := newReceivedRequest(s.kv, myID, c, key) @@ -232,24 +239,24 @@ func (s *Store) AddReceived(myID *id.ID, c contact.Contact, key *sidh.PublicKey) return nil } -// GetReceivedRequest returns the contact representing the partner request, if -// it exists. If it returns, then it takes the lock to ensure that there is only -// one operator at a time. The user of the API must release the lock by calling -// store.delete() or store.Failed() with the partner ID. -func (s *Store) GetReceivedRequest(partner, myID *id.ID) (*ReceivedRequest, error) { +// HandleReceivedRequest handles the request singly, only a single operator +// operates on the same request at a time. It will delete the request if no +// error is returned from the handler +func (s *Store) HandleReceivedRequest(partner, myID *id.ID, handler func(*ReceivedRequest) error) error { aid := makeAuthIdentity(partner, myID) s.mux.RLock() - r, ok := s.receivedByID[aid] + rr, ok := s.receivedByID[aid] s.mux.RUnlock() if !ok { - return nil, errors.Errorf("Received request not "+ + return errors.Errorf("Received request not "+ "found: %s", partner) } // Take the lock to ensure there is only one operator at a time - r.mux.Lock() + rr.mux.Lock() + defer rr.mux.Unlock() // Check that the request still exists; it could have been deleted while the // lock was taken @@ -258,202 +265,84 @@ func (s *Store) GetReceivedRequest(partner, myID *id.ID) (*ReceivedRequest, erro s.mux.RUnlock() if !ok { - r.mux.Unlock() - return nil, errors.Errorf("Received request not "+ + return errors.Errorf("Received request not "+ "found: %s", partner) } - return r, nil -} + //run the handler + handleErr := handler(rr) -// GetReceivedRequestData returns the contact representing the partner request -// if it exists. It does not take the lock. It is only meant to return the -// contact to an external API user. -func (s *Store) GetReceivedRequestData(partner, myID *id.ID) (contact.Contact, error) { - if myID == nil { - myID = s.defaultID + if handleErr != nil { + return errors.WithMessage(handleErr, "Received error from handler") } + delete(s.receivedByID, aid) + rr.delete() + + return nil +} + +// HandleSentRequest handles the request singly, only a single operator +// operates on the same request at a time. It will delete the request if no +// error is returned from the handler +func (s *Store) HandleSentRequest(partner, myID *id.ID, handler func(request *SentRequest) error) error { aid := makeAuthIdentity(partner, myID) s.mux.RLock() - r, ok := s.receivedByID[aid] + sr, ok := s.sentByID[aid] s.mux.RUnlock() if !ok { - return contact.Contact{}, errors.Errorf("Received request not "+ + return errors.Errorf("Received request not "+ "found: %s", partner) } - return r.partner, nil -} + // Take the lock to ensure there is only one operator at a time + sr.mux.Lock() + defer sr.mux.Unlock() -// Done is one of two calls after using a request. This one is to be used when -// the use is unsuccessful. It will allow any thread waiting on access to -// continue using the structure. -// It does not return an error because an error is not handleable. -func (s *Store) Done(rr *ReceivedRequest) { + // Check that the request still exists; it could have been deleted while the + // lock was taken s.mux.RLock() - r, ok := s.receivedByID[rr.aid] + _, ok = s.sentByID[aid] s.mux.RUnlock() - r.mux.Unlock() - if !ok { - jww.ERROR.Panicf("Request cannot be finished, not "+ - "found: %s, %s", rr.partner, rr.myID) - return - } -} - -// Delete is one of two calls after using a request. This one is to be used when -// the use is unsuccessful. It deletes all references to the request associated -// with the passed partner, if it exists. It will allow any thread waiting on -// access to continue. They should fail due to the deletion of the structure. -func (s *Store) Delete(partner *id.ID) error { - s.mux.Lock() - defer s.mux.Unlock() - r, ok := s.receivedByID[*partner] - - if !ok { - return errors.Errorf("Request not found: %s", partner) + return errors.Errorf("Received request not "+ + "found: %s", partner) } - switch r.rt { - case Sent: - s.deleteSentRequest(r) - case Receive: - s.deleteReceiveRequest(r) - } + //run the handler + handleErr := handler(sr) - delete(s.receivedByID, *partner) - if err := s.save(); err != nil { - jww.FATAL.Panicf("Failed to store updated request map after "+ - "deletion: %+v", err) + if handleErr != nil { + return errors.WithMessage(handleErr, "Received error from handler") } - err := s.deletePreviousNegotiationPartner(partner) - if err != nil { - jww.FATAL.Panicf("Failed to delete partner negotiations: %+v", err) - } + delete(s.receivedByID, aid) + sr.delete() return nil } -// DeleteAllRequests clears the request map and all associated storage objects -// containing request data. -func (s *Store) DeleteAllRequests() error { - s.mux.Lock() - defer s.mux.Unlock() - - for partnerId, req := range s.receivedByID { - switch req.rt { - case Sent: - s.deleteSentRequest(req) - delete(s.receivedByID, partnerId) - case Receive: - s.deleteReceiveRequest(req) - delete(s.receivedByID, partnerId) - } - - } - - if err := s.save(); err != nil { - jww.FATAL.Panicf("Failed to store updated request map after "+ - "deleting all receivedByID: %+v", err) +// GetReceivedRequest returns the contact representing the partner request +// if it exists. It does not take the lock. It is only meant to return the +// contact to an external API user. +func (s *Store) GetReceivedRequest(partner, myID *id.ID) (contact.Contact, error) { + if myID == nil { + myID = s.defaultID } - return nil -} + aid := makeAuthIdentity(partner, myID) -// DeleteRequest deletes a request from Store given a partner ID. -// If the partner ID exists as a request, then the request will be deleted -// and the state stored. If the partner does not exist, then an error will -// be returned. -func (s *Store) DeleteRequest(partnerId *id.ID) error { - s.mux.Lock() - defer s.mux.Unlock() + s.mux.RLock() + r, ok := s.receivedByID[aid] + s.mux.RUnlock() - req, ok := s.receivedByID[*partnerId] if !ok { - return errors.Errorf("Request for %s does not exist", partnerId) - } - - switch req.rt { - case Sent: - s.deleteSentRequest(req) - case Receive: - s.deleteReceiveRequest(req) - } - - delete(s.receivedByID, *partnerId) - - if err := s.save(); err != nil { - jww.FATAL.Panicf("Failed to store updated request map after "+ - "deleting partner request for partner %s: %+v", partnerId, err) - } - - return nil -} - -// DeleteSentRequests deletes all Sent receivedByID from Store. -func (s *Store) DeleteSentRequests() error { - s.mux.Lock() - defer s.mux.Unlock() - - for partnerId, req := range s.receivedByID { - switch req.rt { - case Sent: - s.deleteSentRequest(req) - delete(s.receivedByID, partnerId) - case Receive: - continue - } - } - - if err := s.save(); err != nil { - jww.FATAL.Panicf("Failed to store updated request map after "+ - "deleting all sent receivedByID: %+v", err) - } - - return nil -} - -// DeleteReceiveRequests deletes all Receive receivedByID from Store. -func (s *Store) DeleteReceiveRequests() error { - s.mux.Lock() - defer s.mux.Unlock() - - for partnerId, req := range s.receivedByID { - switch req.rt { - case Sent: - continue - case Receive: - s.deleteReceiveRequest(req) - delete(s.receivedByID, partnerId) - } - } - - if err := s.save(); err != nil { - jww.FATAL.Panicf("Failed to store updated request map after "+ - "deleting all partner receivedByID: %+v", err) - } - - return nil -} - -// deleteSentRequest is a helper function which deletes a Sent request from storage. -func (s *Store) deleteSentRequest(r *ReceivedRequest) { - delete(s.sentByFingerprints, r.sent.fingerprint) - if err := r.sent.delete(); err != nil { - jww.FATAL.Panicf("Failed to delete sent request: %+v", err) + return contact.Contact{}, errors.Errorf("Received request not "+ + "found: %s", partner) } -} -// deleteReceiveRequest is a helper function which deletes a Receive request from storage. -func (s *Store) deleteReceiveRequest(r *ReceivedRequest) { - if err := util.DeleteContact(s.kv, r.partner.ID); err != nil { - jww.FATAL.Panicf("Failed to delete recieved request "+ - "contact: %+v", err) - } + return r.partner, nil } diff --git a/storage/auth/store_test.go b/auth/store/store_test.go similarity index 99% rename from storage/auth/store_test.go rename to auth/store/store_test.go index 964b3b88e807bde5e4606ffbbcf25aa47ab9fa32..20f09f27928df8eb620a4832b76cff2a054582b0 100644 --- a/storage/auth/store_test.go +++ b/auth/store/store_test.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package auth +package store import ( "bytes" diff --git a/e2e/ratchet/partner/relationship_test.go b/e2e/ratchet/partner/relationship_test.go index 9100087a8bf5d98ef7b5c75a464959d6d7725a07..2dc11017a9f4160586b82a0017daffb19fe9caf7 100644 --- a/e2e/ratchet/partner/relationship_test.go +++ b/e2e/ratchet/partner/relationship_test.go @@ -803,6 +803,7 @@ func TestSessionBuff_TriggerNegotiation(t *testing.T) { // Unconfirmed sessions should also be included in the list // as the client should attempt to confirm them p := session.GetDefaultParams() + //set the retry ratio so the unconfirmed session is always retried p.UnconfirmedRetryRatio = 1 session3 := sb.AddSession(myPrivKey, partnerPubKey, nil, mySIDHPrivKey, partnerSIDHPubKey,