diff --git a/auth/callback.go b/auth/callback.go deleted file mode 100644 index 73b0a6bbe668a9643a38f0b20eb03c150fd5daf6..0000000000000000000000000000000000000000 --- a/auth/callback.go +++ /dev/null @@ -1,537 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 xx network SEZC // -// // -// Use of this source code is governed by a license that can be found in the // -// LICENSE file // -/////////////////////////////////////////////////////////////////////////////// - -package auth - -import ( - "fmt" - auth2 "gitlab.com/elixxir/client/auth/store" - "gitlab.com/elixxir/client/catalog" - "strings" - - "github.com/cloudflare/circl/dh/sidh" - "github.com/pkg/errors" - jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/client/interfaces" - "gitlab.com/elixxir/client/interfaces/message" - "gitlab.com/elixxir/client/interfaces/preimage" - "gitlab.com/elixxir/client/stoppable" - "gitlab.com/elixxir/client/storage/edge" - "gitlab.com/elixxir/crypto/contact" - "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/crypto/diffieHellman" - cAuth "gitlab.com/elixxir/crypto/e2e/auth" - "gitlab.com/elixxir/primitives/fact" - "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/primitives/id" -) - -func (s *State) StartProcesses() (stoppable.Stoppable, error) { - stop := stoppable.NewSingle("Auth") - - go func() { - for { - select { - case <-stop.Quit(): - stop.ToStopped() - return - case msg := <-s.rawMessages: - s.processAuthMessage(msg) - } - } - }() - - return stop, nil -} - -func (s *State) processAuthMessage(msg message.Receive) { - authStore := s.storage.Auth() - //lookup the message, check if it is an auth request - cmixMsg, err := format.Unmarshal(msg.Payload) - if err != nil { - jww.WARN.Printf("Invalid message when unmarshalling: %s", - err.Error()) - // Ignore this message - return - } - fp := cmixMsg.GetKeyFP() - jww.INFO.Printf("RAW AUTH FP: %v", fp) - // this takes the request lock if it is a specific fp, all - // exits after this need to call fail or delete if it is - // specific - fpType, sr, myHistoricalPrivKey, err := authStore.GetFingerprint(fp) - if err != nil { - jww.TRACE.Printf("FINGERPRINT FAILURE: %s", err.Error()) - // if the lookup fails, ignore the message. It is - // likely garbled or for a different protocol - return - } - - //denote that the message is not garbled - s.storage.GetGarbledMessages().Remove(cmixMsg) - grp := s.storage.E2e().GetGroup() - - switch fpType { - case auth2.General: - // if it is general, that means a new request has - // been received - s.handleRequest(cmixMsg, myHistoricalPrivKey, grp) - 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", - sr.GetPartner(), cmixMsg.Digest()) - s.handleConfirm(cmixMsg, sr, grp) - } -} - -func (s *State) handleRequest(cmixMsg format.Message, - myHistoricalPrivKey *cyclic.Int, grp *cyclic.Group) { - //decode the outer format - baseFmt, partnerPubKey, err := handleBaseFormat( - cmixMsg, grp) - if err != nil { - jww.WARN.Printf("Failed to handle auth request: %s", err) - return - } - - myPubKey := diffieHellman.GeneratePublicKey(myHistoricalPrivKey, grp) - - jww.TRACE.Printf("handleRequest MYPUBKEY: %v", myPubKey.Bytes()) - jww.TRACE.Printf("handleRequest PARTNERPUBKEY: %v", partnerPubKey.Bytes()) - - //decrypt the message - jww.TRACE.Printf("handleRequest ECRPAYLOAD: %v", baseFmt.GetEcrPayload()) - jww.TRACE.Printf("handleRequest MAC: %v", cmixMsg.GetMac()) - - ecrPayload := baseFmt.GetEcrPayload() - success, payload := cAuth.Decrypt(myHistoricalPrivKey, - partnerPubKey, ecrPayload, - cmixMsg.GetMac(), grp) - - if !success { - jww.WARN.Printf("Attempting to decrypt old request packet...") - ecrPayload = append(ecrPayload, baseFmt.GetVersion()) - success, payload = cAuth.Decrypt(myHistoricalPrivKey, - partnerPubKey, ecrPayload, - cmixMsg.GetMac(), grp) - } - - if !success { - jww.WARN.Printf("Received auth request failed " + - "its mac check") - return - } - - //decode the ecr format - ecrFmt, err := unmarshalEcrFormat(payload) - if err != nil { - jww.WARN.Printf("Failed to unmarshal auth "+ - "request's encrypted payload: %s", err) - return - } - partnerSIDHPubKey, err := ecrFmt.GetSidhPubKey() - if err != nil { - jww.WARN.Printf("Could not unmarshal partner SIDH Pubkey: %s", - err) - } - - //decode the request format - requestFmt, err := newRequestFormat(ecrFmt) - if err != nil { - jww.WARN.Printf("Failed to unmarshal auth "+ - "request's internal payload: %s", err) - return - } - - partnerID, err := requestFmt.GetID() - if err != nil { - jww.WARN.Printf("Failed to unmarshal auth "+ - "request's sender ID: %s", err) - return - } - - events := s.net.GetEventManager() - em := fmt.Sprintf("Received AuthRequest from %s,"+ - " msgDigest: %s", partnerID, cmixMsg.Digest()) - jww.INFO.Print(em) - events.Report(1, "Auth", "RequestReceived", em) - - /*do state edge checks*/ - // Check if this is a reset, which are valid as of version 1 - // Resets happen when our fingerprint is new AND we are - // the latest fingerprint to be added to the list and we already have - // a negotiation or authenticated channel in progress - fp := cAuth.CreateNegotiationFingerprint(partnerPubKey, - partnerSIDHPubKey) - newFP, latest := s.storage.Auth().AddIfNew(partnerID, fp) - resetSession := false - autoConfirm := false - if baseFmt.GetVersion() >= 1 && newFP && latest { - // If we had an existing session and it's new, then yes, we - // want to reset - if _, err := s.storage.E2e().GetPartner(partnerID); err == nil { - jww.INFO.Printf("Resetting session for %s", partnerID) - resetSession = true - // Most likely, we got 2 reset sessions at once, so this - // is a non-fatal error but we will record a warning - // just in case. - err = s.storage.E2e().DeletePartner(partnerID) - if err != nil { - jww.WARN.Printf("Unable to delete channel: %+v", - err) - } - // Also delete any existing request, sent or received - s.storage.Auth().Delete(partnerID) - } - // If we had an existing negotiation open, then it depends - - // If we've only received, then user has not confirmed, treat as - // a non-duplicate request, so delete the old one (to cause new - // callback to be called) - rType, _, _, err := s.storage.Auth().GetRequest(partnerID) - if err != nil && rType == auth2.Receive { - s.storage.Auth().Delete(partnerID) - } - - // If we've already Sent and are now receiving, - // then we attempt auto-confirm as below - // This poses a potential problem if it is truly a session - // reset by the other user, because we may not actually - // autoconfirm based on our public key compared to theirs. - // This could result in a permanently broken association, as - // the other side has attempted to reset it's session and - // can no longer detect a sent request collision, so this side - // cannot ever successfully resend. - // We prevent this by stopping session resets if they - // are called when the other side is in the "Sent" state. - // If the other side is in the "received" state we also block, - // but we could autoconfirm. - // Note that you can still get into this state by one side - // deleting requests. In that case, both sides need to clear - // out all requests and retry negotiation from scratch. - // NOTE: This protocol part could use an overhaul/second look, - // there's got to be a way to do this with far less state - // but this is the spec so we're sticking with it for now. - - // If not an existing request, we do nothing. - } else { - jww.WARN.Printf("Version: %d, newFP: %v, latest: %v", baseFmt.GetVersion(), - newFP, latest) - } - - // check if a relationship already exists. - // if it does and the keys used are the same as we have, send a - // confirmation in case there are state issues. - // do not store - if _, err := s.storage.E2e().GetPartner(partnerID); err == nil { - em := fmt.Sprintf("Received Auth request for %s, "+ - "channel already exists. Ignoring", partnerID) - jww.WARN.Print(em) - events.Report(5, "Auth", "RequestIgnored", em) - //exit - return - } else { - //check if the relationship already exists, - rType, _, c, err := s.storage.Auth().GetRequest(partnerID) - 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", - partnerID, err) - jww.ERROR.Print(em) - events.Report(10, "Auth", "RequestError", em) - return - } else { - //handle the events where the relationship already exists - switch rType { - // if this is a duplicate, ignore the message - case auth2.Receive: - em := fmt.Sprintf("Received new Auth request for %s, "+ - "is a duplicate", partnerID) - jww.WARN.Print(em) - events.Report(5, "Auth", "DuplicateRequest", em) - // if the caller of the API wants requests replayed, - // replay the duplicate request - if s.replayRequests { - cbList := s.requestCallbacks.Get(c.ID) - for _, cb := range cbList { - rcb := cb.(interfaces.RequestCallback) - go rcb(c) - } - } - return - // if we sent a request, then automatically confirm - // then exit, nothing else needed - case auth2.Sent: - jww.INFO.Printf("Received AuthRequest from %s,"+ - " msgDigest: %s which has been requested, auto-confirming", - partnerID, cmixMsg.Digest()) - - // Verify this request is legit - ownership := ecrFmt.GetOwnership() - if !cAuth.VerifyOwnershipProof( - myHistoricalPrivKey, partnerPubKey, grp, - ownership) { - jww.WARN.Printf("Invalid ownership proof from %s received, discarding msdDigest: %s", - partnerID, cmixMsg.Digest()) - } - - // Check if I need to resend by comparing the - // IDs - myBytes := s.storage.GetUser().ReceptionID.Bytes() - theirBytes := partnerID.Bytes() - for i := 0; i < len(myBytes); i++ { - if myBytes[i] > theirBytes[i] { - // OK, this side is dropping - // the request - // Do we need to delete - // something here? - // No, because we will - // now wait to receive - // confirmation. - return - } else if myBytes[i] < theirBytes[i] { - break - } - } - - // If I do, delete my request on disk - s.storage.Auth().Delete(partnerID) - - // Do the normal, fall out of this if block and - // create the contact, note that we use the data - // sent in the request and not any data we had - // already - - autoConfirm = true - - } - } - } - - //process the inner payload - facts, _, err := fact.UnstringifyFactList( - string(requestFmt.msgPayload)) - if err != nil { - em := fmt.Sprintf("failed to parse facts and message "+ - "from Auth Request: %s", err) - jww.WARN.Print(em) - events.Report(10, "Auth", "RequestError", em) - return - } - - //create the contact, note that no facts are sent in the payload - c := contact.Contact{ - ID: partnerID.DeepCopy(), - DhPubKey: partnerPubKey.DeepCopy(), - OwnershipProof: copySlice(ecrFmt.ownership), - Facts: facts, - } - - // fixme: the client will never be notified of the channel creation if a - // crash occurs after the store but before the conclusion of the callback - //create the auth storage - if err = s.storage.Auth().AddReceived(c, partnerSIDHPubKey); err != nil { - em := fmt.Sprintf("failed to store contact Auth "+ - "Request: %s", err) - jww.WARN.Print(em) - events.Report(10, "Auth", "RequestError", em) - return - } - - // We autoconfirm anytime we had already sent a request OR we are - // resetting an existing session - var rndNum id.Round - if autoConfirm || resetSession { - // Call ConfirmRequestAuth to send confirmation - rndNum, err = s.ConfirmRequestAuth(c) - if err != nil { - jww.ERROR.Printf("Could not ConfirmRequestAuth: %+v", - err) - return - } - - if autoConfirm { - jww.INFO.Printf("ConfirmRequestAuth to %s on round %d", - partnerID, rndNum) - cbList := s.confirmCallbacks.Get(c.ID) - for _, cb := range cbList { - ccb := cb.(interfaces.ConfirmCallback) - go ccb(c) - } - } - if resetSession { - jww.INFO.Printf("Reset Auth %s on round %d", - partnerID, rndNum) - cbList := s.resetCallbacks.Get(c.ID) - for _, cb := range cbList { - ccb := cb.(interfaces.ResetNotificationCallback) - go ccb(c) - } - } - } else { - // fixme: if a crash occurs before or during the calls, the notification - // will never be sent. - cbList := s.requestCallbacks.Get(c.ID) - for _, cb := range cbList { - rcb := cb.(interfaces.RequestCallback) - go rcb(c) - } - } - return -} - -func (s *State) handleConfirm(cmixMsg format.Message, sr *auth2.SentRequest, - grp *cyclic.Group) { - events := s.net.GetEventManager() - - // check if relationship already exists - if mgr, err := s.storage.E2e().GetPartner(sr.GetPartner()); mgr != nil || err == nil { - em := fmt.Sprintf("Cannot confirm auth for %s, channel already "+ - "exists.", sr.GetPartner()) - jww.WARN.Print(em) - events.Report(10, "Auth", "ConfirmError", em) - s.storage.Auth().Done(sr.GetPartner()) - return - } - - // extract the message - baseFmt, partnerPubKey, err := handleBaseFormat( - cmixMsg, grp) - if err != nil { - em := fmt.Sprintf("Failed to handle auth confirm: %s", err) - jww.WARN.Print(em) - events.Report(10, "Auth", "ConfirmError", em) - s.storage.Auth().Done(sr.GetPartner()) - return - } - - jww.TRACE.Printf("handleConfirm PARTNERPUBKEY: %v", partnerPubKey.Bytes()) - jww.TRACE.Printf("handleConfirm SRMYPUBKEY: %v", sr.GetMyPubKey().Bytes()) - - // decrypt the payload - jww.TRACE.Printf("handleConfirm ECRPAYLOAD: %v", baseFmt.GetEcrPayload()) - jww.TRACE.Printf("handleConfirm MAC: %v", cmixMsg.GetMac()) - success, payload := cAuth.Decrypt(sr.GetMyPrivKey(), - partnerPubKey, baseFmt.GetEcrPayload(), - cmixMsg.GetMac(), grp) - - if !success { - em := fmt.Sprintf("Received auth confirmation failed its mac " + - "check") - jww.WARN.Print(em) - events.Report(10, "Auth", "ConfirmError", em) - s.storage.Auth().Done(sr.GetPartner()) - return - } - - ecrFmt, err := unmarshalEcrFormat(payload) - if err != nil { - em := fmt.Sprintf("Failed to unmarshal auth confirmation's "+ - "encrypted payload: %s", err) - jww.WARN.Print(em) - events.Report(10, "Auth", "ConfirmError", em) - s.storage.Auth().Done(sr.GetPartner()) - return - } - - partnerSIDHPubKey, err := ecrFmt.GetSidhPubKey() - if err != nil { - em := fmt.Sprintf("Could not get auth conf SIDH Pubkey: %s", - err) - jww.WARN.Print(em) - events.Report(10, "Auth", "ConfirmError", em) - s.storage.Auth().Done(sr.GetPartner()) - return - } - jww.TRACE.Printf("handleConfirm PARTNERSIDHPUBKEY: %v", - partnerSIDHPubKey) - - // finalize the confirmation - if err := s.doConfirm(sr, grp, partnerPubKey, sr.GetMyPrivKey(), - sr.GetPartnerHistoricalPubKey(), - ecrFmt.GetOwnership(), - partnerSIDHPubKey); err != nil { - em := fmt.Sprintf("Confirmation failed: %s", err) - jww.WARN.Print(em) - events.Report(10, "Auth", "ConfirmError", em) - s.storage.Auth().Done(sr.GetPartner()) - return - } -} - -func (s *State) 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 - if !cAuth.VerifyOwnershipProof(myPrivateKeyOwnershipProof, - partnerPubKeyOwnershipProof, grp, ownershipProof) { - return errors.Errorf("Failed authenticate identity for auth "+ - "confirmation of %s", sr.GetPartner()) - } - - // fixme: channel can get into a bricked state if the first save occurs and - // the second does not - p := s.storage.E2e().GetE2ESessionParams() - if err := s.storage.E2e().AddPartner(sr.GetPartner(), - partnerPubKey, sr.GetMyPrivKey(), partnerSIDHPubKey, - sr.GetMySIDHPrivKey(), p, p); err != nil { - return errors.Errorf("Failed to create channel with partner (%s) "+ - "after confirmation: %+v", - sr.GetPartner(), err) - } - - s.backupTrigger("received confirmation from request") - - //remove the confirm fingerprint - fp := sr.GetFingerprint() - if err := s.storage.GetEdge().Remove(edge.Preimage{ - Data: preimage.Generate(fp[:], catalog.Confirm), - Type: catalog.Confirm, - Source: sr.GetPartner()[:], - }, s.storage.GetUser().ReceptionID); err != nil { - jww.WARN.Printf("Failed delete the preimage for confirm from %s: %+v", - sr.GetPartner(), err) - } - - addPreimages(sr.GetPartner(), s.storage) - - // delete the in progress negotiation - // this undoes the request lock - if err := s.storage.Auth().Delete(sr.GetPartner()); err != nil { - return errors.Errorf("UNRECOVERABLE! Failed to delete in "+ - "progress negotiation with partner (%s) after confirmation: %+v", - sr.GetPartner(), err) - } - - //notify the end point - c := contact.Contact{ - ID: sr.GetPartner().DeepCopy(), - DhPubKey: partnerPubKey.DeepCopy(), - OwnershipProof: copySlice(ownershipProof), - Facts: make([]fact.Fact, 0), - } - - // fixme: if a crash occurs before or during the calls, the notification - // will never be sent. - cbList := s.confirmCallbacks.Get(c.ID) - for _, cb := range cbList { - ccb := cb.(interfaces.ConfirmCallback) - go ccb(c) - } - - s.net.CheckGarbledMessages() - - return nil -} - -func copySlice(s []byte) []byte { - c := make([]byte, len(s)) - copy(c, s) - return c -} diff --git a/auth/callbacks.go b/auth/callbacks.go deleted file mode 100644 index 376a4079d5807dea5b11400f61aaef1c2689711e..0000000000000000000000000000000000000000 --- a/auth/callbacks.go +++ /dev/null @@ -1,52 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 xx network SEZC // -// // -// Use of this source code is governed by a license that can be found in the // -// LICENSE file // -/////////////////////////////////////////////////////////////////////////////// - -package auth - -import ( - "gitlab.com/elixxir/client/cmix/historical" - "gitlab.com/elixxir/client/cmix/identity/receptionID" - "gitlab.com/elixxir/crypto/contact" - "gitlab.com/xx_network/primitives/id" - "sync" -) - -type Callback func(requestor contact.Contact, - receptionID receptionID.EphemeralIdentity, round historical.Round) - -type callbackMap struct { - callbacks map[id.ID]Callback - mux sync.RWMutex -} - -func newCallbackMap() *callbackMap { - return &callbackMap{ - callbacks: make(map[id.ID]Callback), - } -} - -//adds a general callback. This will be preempted by any specific callback -func (cm *callbackMap) AddCallback(recipeint *id.ID, cb Callback) { - cm.mux.Lock() - defer cm.mux.Unlock() - cm.callbacks[*recipeint] = cb -} - -// removes a callback for a specific user ID if it exists. -func (cm *callbackMap) RemoveCallback(id *id.ID) { - cm.mux.Lock() - defer cm.mux.Unlock() - delete(cm.callbacks, *id) -} - -//get all callback which fit with the passed id -func (cm *callbackMap) Get(id *id.ID) (Callback, bool) { - cm.mux.RLock() - defer cm.mux.RUnlock() - cb, exist := cm.callbacks[*id] - return cb, exist -} diff --git a/auth/confirm.go b/auth/confirm.go index a256238edb2d5af358716cd763a79324b2019092..92bdf65a5950a14c5d8257bdb2b7f188c0e29e79 100644 --- a/auth/confirm.go +++ b/auth/confirm.go @@ -12,7 +12,6 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/auth/store" - "gitlab.com/elixxir/client/catalog" "gitlab.com/elixxir/client/cmix" "gitlab.com/elixxir/client/cmix/message" "gitlab.com/elixxir/client/e2e/ratchet/partner/session" @@ -24,7 +23,7 @@ import ( "gitlab.com/xx_network/primitives/id" ) -func (s *State) ConfirmRequestAuth(partner contact.Contact, me *id.ID) ( +func (s *State) ConfirmRequestAuth(partner contact.Contact) ( id.Round, error) { // check that messages can be sent over the network @@ -33,11 +32,10 @@ func (s *State) ConfirmRequestAuth(partner contact.Contact, me *id.ID) ( "when the network is not healthy") } - return s.confirmRequestAuth(partner, me) - + return s.confirmRequestAuth(partner, s.params.ConfirmTag) } -func (s *State) confirmRequestAuth(partner contact.Contact, me *id.ID) ( +func (s *State) confirmRequestAuth(partner contact.Contact, serviceTag string) ( id.Round, error) { // check that messages can be sent over the network @@ -46,12 +44,10 @@ func (s *State) confirmRequestAuth(partner contact.Contact, me *id.ID) ( "when the network is not healthy") } - kp := s.registeredIDs[*me] - var sentRound id.Round //run the handler - err := s.store.HandleReceivedRequest(partner.ID, me, func(rr *store.ReceivedRequest) error { + err := s.store.HandleReceivedRequest(partner.ID, func(rr *store.ReceivedRequest) error { // verify the passed contact matches what is stored if rr.GetContact().DhPubKey.Cmp(partner.DhPubKey) != 0 { return errors.New("pending Auth Request has different " + @@ -61,8 +57,8 @@ func (s *State) confirmRequestAuth(partner contact.Contact, me *id.ID) ( /*cryptographic generation*/ // generate ownership proof - ownership := cAuth.MakeOwnershipProof(kp.privkey, partner.DhPubKey, - s.e2e.GetGroup()) + ownership := cAuth.MakeOwnershipProof(s.e2e.GetHistoricalDHPrivkey(), + partner.DhPubKey, s.e2e.GetGroup()) rng := s.rng.GetStream() @@ -110,7 +106,7 @@ func (s *State) confirmRequestAuth(partner contact.Contact, me *id.ID) ( // create local relationship p := session.GetDefaultParams() - _, err := s.e2e.AddPartner(me, partner.ID, partner.DhPubKey, dhPriv, + _, err := s.e2e.AddPartner(partner.ID, partner.DhPubKey, dhPriv, rr.GetTheirSidHPubKeyA(), sidhPriv, p, p) if err != nil { em := fmt.Sprintf("Failed to create channel with partner (%s) "+ @@ -123,33 +119,35 @@ func (s *State) confirmRequestAuth(partner contact.Contact, me *id.ID) ( //todo: s.backupTrigger("confirmed authenticated channel") jww.INFO.Printf("Confirming Auth from %s to %s, msgDigest: %s", - partner.ID, me, format.DigestContents(baseFmt.Marshal())) + partner.ID, s.e2e.GetReceptionID(), + format.DigestContents(baseFmt.Marshal())) //service used for noticiation only /*send message*/ - if err = s.store.StoreConfirmation(partner.ID, me, baseFmt.Marshal(), + if err = s.store.StoreConfirmation(partner.ID, baseFmt.Marshal(), mac, fp); err == nil { jww.WARN.Printf("Failed to store confirmation for replay "+ "for relationship between %s and %s, cannot be replayed: %+v", - partner.ID, me, err) + partner.ID, s.e2e.GetReceptionID(), err) } //send confirmation - sentRound, err = sendAuthConfirm(s.net, partner.ID, me, fp, - baseFmt.Marshal(), mac, s.event) + sentRound, err = sendAuthConfirm(s.net, partner.ID, fp, + baseFmt.Marshal(), mac, s.event, serviceTag) return nil }) return sentRound, err } -func sendAuthConfirm(net cmix.Client, partner, me *id.ID, - fp format.Fingerprint, payload, mac []byte, event event.Manager) ( +func sendAuthConfirm(net cmix.Client, partner *id.ID, + fp format.Fingerprint, payload, mac []byte, event event.Manager, + serviceTag string) ( id.Round, error) { svc := message.Service{ - Identifier: partner.Marshal(), - Tag: catalog.Default, + Identifier: fp[:], + Tag: serviceTag, Metadata: nil, } diff --git a/auth/params.go b/auth/params.go index abc251e648327a694cd54b779f014159560f6026..60ab3ac58fcf594b96fe131b599b9932a187468c 100644 --- a/auth/params.go +++ b/auth/params.go @@ -1,22 +1,18 @@ package auth -import "gitlab.com/elixxir/client/e2e/parse/partition" - type Param struct { ReplayRequests bool - RequestTag string - ResetTag string + RequestTag string + ConfirmTag string + ResetRequestTag string + ResetConfirmTag string } -type PartPacket map[trasferID][]part - -func (pp PartPacket) Add(transferid, part) { - list, exist := PartPacket[transferid] - if exist { - PartPacket[transferid] = append(list, part) +func (p Param) getConfirmTag(reset bool) string { + if reset { + return p.ResetConfirmTag } else { - PartPacket[transferid][] - part{part} + return p.ConfirmTag } } diff --git a/auth/receivedConfirm.go b/auth/receivedConfirm.go index cfc7336282ffdd1c40d4059b0e0559475229b52f..7a1447426156065ed4ebdd187ec60659f781cc4e 100644 --- a/auth/receivedConfirm.go +++ b/auth/receivedConfirm.go @@ -5,7 +5,6 @@ import ( "fmt" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/auth/store" - "gitlab.com/elixxir/client/catalog" "gitlab.com/elixxir/client/cmix/historical" "gitlab.com/elixxir/client/cmix/identity/receptionID" "gitlab.com/elixxir/client/cmix/message" @@ -19,6 +18,7 @@ import ( type receivedConfirmService struct { s *State *store.SentRequest + notificationsService message.Service } func (rcs *receivedConfirmService) Process(msg format.Message, @@ -26,15 +26,6 @@ func (rcs *receivedConfirmService) Process(msg format.Message, state := rcs.s - // lookup keypair - kp, exist := state.getRegisteredIDs(receptionID.Source) - - if !exist { - jww.ERROR.Printf("received a confirm for %s, " + - "but they are not registered with auth, cannot process") - return - } - //parse the confirm baseFmt, partnerPubKey, err := handleBaseFormat(msg, state.e2e.GetGroup()) if err != nil { @@ -46,7 +37,7 @@ func (rcs *receivedConfirmService) Process(msg format.Message, jww.TRACE.Printf("processing confirm: \n\t MYPUBKEY: %s "+ "\n\t PARTNERPUBKEY: %s \n\t ECRPAYLOAD: %s \n\t MAC: %s", - kp.pubkey.TextVerbose(16, 0), + state.e2e.GetHistoricalDHPubkey().TextVerbose(16, 0), partnerPubKey.TextVerbose(16, 0), base64.StdEncoding.EncodeToString(baseFmt.data), base64.StdEncoding.EncodeToString(msg.GetMac())) @@ -87,7 +78,8 @@ func (rcs *receivedConfirmService) Process(msg format.Message, // check the ownership proof, this verifies the respondent owns the // initial identity - if !cAuth.VerifyOwnershipProof(kp.privkey, rcs.GetPartnerHistoricalPubKey(), + if !cAuth.VerifyOwnershipProof(state.e2e.GetHistoricalDHPrivkey(), + rcs.GetPartnerHistoricalPubKey(), state.e2e.GetGroup(), ecrFmt.GetOwnership()) { jww.WARN.Printf("Failed authenticate identity for auth "+ "confirmation of %s", rcs.GetPartner()) @@ -96,7 +88,7 @@ func (rcs *receivedConfirmService) Process(msg format.Message, // add the partner p := session.GetDefaultParams() - _, err = state.e2e.AddPartner(receptionID.Source, rcs.GetPartner(), partnerPubKey, + _, err = state.e2e.AddPartner(rcs.GetPartner(), partnerPubKey, rcs.GetMyPrivKey(), partnerSIDHPubKey, rcs.GetMySIDHPrivKey(), p, p) if err != nil { jww.WARN.Printf("Failed to create channel with partner %s and "+ @@ -106,11 +98,7 @@ func (rcs *receivedConfirmService) Process(msg format.Message, //todo: trigger backup // remove the service used for notifications of the confirm - confirmFP := rcs.GetFingerprint() - state.net.DeleteService(receptionID.Source, message.Service{ - Identifier: confirmFP[:], - Tag: catalog.Confirm, - }, nil) + state.net.DeleteService(receptionID.Source, rcs.notificationsService, nil) // callbacks c := contact.Contact{ @@ -119,7 +107,5 @@ func (rcs *receivedConfirmService) Process(msg format.Message, OwnershipProof: ecrFmt.GetOwnership(), Facts: make([]fact.Fact, 0), } - if cb, exists := state.confirmCallbacks.Get(receptionID.Source); exists { - cb(c, receptionID, round) - } + state.callbacks.Confirm(c, receptionID, round) } diff --git a/auth/receivedRequest.go b/auth/receivedRequest.go index 93010fb8f853e95cf3a3902e31d1b1ac0eaeb453..a50a059966be2099f626350bbcfbe79c34869306 100644 --- a/auth/receivedRequest.go +++ b/auth/receivedRequest.go @@ -21,7 +21,8 @@ import ( const dummyerr = "dummy error so we dont delete the request" type receivedRequestService struct { - s *State + s *State + reset bool } func (rrs *receivedRequestService) Process(message format.Message, @@ -29,15 +30,6 @@ func (rrs *receivedRequestService) Process(message format.Message, state := rrs.s - //lookup the keypair - kp, exist := state.getRegisteredIDs(receptionID.Source) - - if !exist { - jww.ERROR.Printf("received a confirm for %s, " + - "but they are not registered with auth, cannot process") - return - } - // check if the timestamp is before the id was created and therefore // should be ignored tid, err := state.net.GetIdentity(receptionID.Source) @@ -63,14 +55,15 @@ func (rrs *receivedRequestService) Process(message format.Message, jww.TRACE.Printf("processing requests: \n\t MYPUBKEY: %s "+ "\n\t PARTNERPUBKEY: %s \n\t ECRPAYLOAD: %s \n\t MAC: %s", - kp.pubkey.TextVerbose(16, 0), + state.e2e.GetHistoricalDHPubkey(), partnerPubKey.TextVerbose(16, 0), base64.StdEncoding.EncodeToString(baseFmt.data), base64.StdEncoding.EncodeToString(message.GetMac())) //Attempt to decrypt the payload - success, payload := cAuth.Decrypt(kp.privkey, partnerPubKey, - baseFmt.GetEcrPayload(), message.GetMac(), state.e2e.GetGroup()) + success, payload := cAuth.Decrypt(state.e2e.GetHistoricalDHPrivkey(), + partnerPubKey, baseFmt.GetEcrPayload(), message.GetMac(), + state.e2e.GetGroup()) if !success { jww.WARN.Printf("Received auth request of %s failed its mac "+ @@ -105,8 +98,7 @@ func (rrs *receivedRequestService) Process(message format.Message, // check the uniqueness of the request. Requests can be duplicated, so we // must verify this is is not a duplicate, and drop if it is - newFP, position := state.store.CheckIfNegotiationIsNew(partnerID, - receptionID.Source, fp) + newFP, position := state.store.CheckIfNegotiationIsNew(partnerID, fp) if !newFP { // if its the newest, resend the confirm @@ -118,10 +110,10 @@ func (rrs *receivedRequestService) Process(message format.Message, // check if we already accepted, if we did, resend the confirm if // we can load it - if _, err = state.e2e.GetPartner(partnerID, receptionID.Source); err != nil { + if _, err = state.e2e.GetPartner(partnerID); err != nil { //attempt to load the confirm, if we can, resend it confirmPayload, mac, keyfp, err := - state.store.LoadConfirmation(partnerID, receptionID.Source) + state.store.LoadConfirmation(partnerID) if err != nil { jww.ERROR.Printf("Could not reconfirm a duplicate "+ "request of an accepted confirm from %s to %s because "+ @@ -130,12 +122,14 @@ func (rrs *receivedRequestService) Process(message format.Message, } // resend the confirm. It sends as a critical message, so errors // do not need to be handled - _, _ = sendAuthConfirm(state.net, partnerID, receptionID.Source, - keyfp, confirmPayload, mac, state.event) + _, _ = sendAuthConfirm(state.net, partnerID, keyfp, + confirmPayload, mac, state.event, state.params.ResetConfirmTag) } else if state.params.ReplayRequests { //if we did not already accept, auto replay the request - if cb, exist := state.requestCallbacks.Get(receptionID.Source); exist { - cb(c, receptionID, round) + if rrs.reset { + state.callbacks.Reset(c, receptionID, round) + } else { + state.callbacks.Request(c, receptionID, round) } } //if not confirm, and params.replay requests is true, we need to replay @@ -148,28 +142,31 @@ func (rrs *receivedRequestService) Process(message format.Message, return } + // if we are a reset, check if we have a relationship. If we do not, + // this is an invalid reset and we need to treat it like a normal + // new request reset := false - // check if we have a relationship, given this is a new request, if we have - // a relationship, this must be a reset, which which case we delete all - // state and then continue like a new request - // delete only deletes if the partner is present, so we can just call delete - // instead of checking if it exists and then calling delete, and check the - // error to see if it did or didnt exist - // Note: due to the newFP handling above, this can ONLY run in the event of - // a reset or when the partner doesnt exist, so it is safe - if err = state.e2e.DeletePartner(partnerID, receptionID.Source); err != nil { - if !strings.Contains(err.Error(), ratchet.NoPartnerErrorStr) { - jww.FATAL.Panicf("Failed to do actual partner deletion: %+v", err) + if rrs.reset { + // delete only deletes if the partner is present, so we can just call delete + // instead of checking if it exists and then calling delete, and check the + // error to see if it did or didnt exist + // Note: due to the newFP handling above, this can ONLY run in the event of + // a reset or when the partner doesnt exist, so it is safe + if err = state.e2e.DeletePartner(partnerID); err != nil { + if !strings.Contains(err.Error(), ratchet.NoPartnerErrorStr) { + jww.FATAL.Panicf("Failed to do actual partner deletion: %+v", err) + } + } else { + reset = true + _ = state.store.DeleteConfirmation(partnerID) + _ = state.store.DeleteSentRequest(partnerID) } - } else { - reset = true - _ = state.store.DeleteConfirmation(partnerID, receptionID.Source) - _ = state.store.DeleteSentRequest(partnerID, receptionID.Source) } // if a new, unique request is received when one already exists, delete the // old one and process the new one - if err = state.store.DeleteReceivedRequest(partnerID, receptionID.Source); err != nil { + // this works because message pickup is generally time-sequential. + if err = state.store.DeleteReceivedRequest(partnerID); err != nil { if !strings.Contains(err.Error(), store.NoRequestFound) { jww.FATAL.Panicf("Failed to delete old received request: %+v", err) @@ -181,7 +178,7 @@ func (rrs *receivedRequestService) Process(message format.Message, // (SIDH keys have polarity, so both sent keys cannot be used together) autoConfirm := false bail := false - err = state.store.HandleSentRequest(partnerID, receptionID.Source, + err = state.store.HandleSentRequest(partnerID, func(request *store.SentRequest) error { //if this code is running, then we know we sent a request and can @@ -189,8 +186,8 @@ func (rrs *receivedRequestService) Process(message format.Message, //This runner will auto delete the sent request if successful //verify ownership proof - if !cAuth.VerifyOwnershipProof(kp.pubkey, partnerPubKey, - state.e2e.GetGroup(), ownershipProof) { + if !cAuth.VerifyOwnershipProof(state.e2e.GetHistoricalDHPrivkey(), + partnerPubKey, state.e2e.GetGroup(), ownershipProof) { jww.WARN.Printf("Invalid ownership proof from %s to %s "+ "received, discarding msdDigest: %s, fp: %s", partnerID, receptionID.Source, @@ -227,7 +224,7 @@ func (rrs *receivedRequestService) Process(message format.Message, // warning: the client will never be notified of the channel creation if a // crash occurs after the store but before the conclusion of the callback //create the auth storage - if err = state.store.AddReceived(receptionID.Source, c, partnerSIDHPubKey); err != nil { + if err = state.store.AddReceived(c, partnerSIDHPubKey, round); err != nil { em := fmt.Sprintf("failed to store contact Auth "+ "Request: %s", err) jww.WARN.Print(em) @@ -237,22 +234,15 @@ func (rrs *receivedRequestService) Process(message format.Message, //autoconfirm if we should if autoConfirm || reset { - _, _ = state.confirmRequestAuth(c, receptionID.Source) + _, _ = state.confirmRequestAuth(c, state.params.getConfirmTag(reset)) //handle callbacks if autoConfirm { - if cb, exist := state.confirmCallbacks.Get(receptionID.Source); exist { - cb(c, receptionID, round) - } + state.callbacks.Confirm(c, receptionID, round) } else if reset { - if cb, exist := state.requestCallbacks.Get(receptionID.Source); exist { - cb(c, receptionID, round) - } + state.callbacks.Reset(c, receptionID, round) } } else { - //otherwise call callbacks - if cb, exist := state.requestCallbacks.Get(receptionID.Source); exist { - cb(c, receptionID, round) - } + state.callbacks.Request(c, receptionID, round) } } @@ -302,3 +292,9 @@ func iShouldResend(partner, me *id.ID) bool { } return myBytes[i] < theirBytes[i] } + +func copySlice(s []byte) []byte { + c := make([]byte, len(s)) + copy(c, s) + return c +} diff --git a/auth/registeredIDs.go b/auth/registeredIDs.go deleted file mode 100644 index cc1d1adb49645f47a6903b55a15169cfa1255e14..0000000000000000000000000000000000000000 --- a/auth/registeredIDs.go +++ /dev/null @@ -1,42 +0,0 @@ -package auth - -import ( - "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/xx_network/primitives/id" - "sync" -) - -type registeredIDs struct { - r map[id.ID]keypair - mux sync.RWMutex -} - -type keypair struct { - privkey *cyclic.Int - //generated from pubkey on instantiation - pubkey *cyclic.Int -} - -func newRegisteredIDs() *registeredIDs { - return ®isteredIDs{ - r: make(map[id.ID]keypair), - mux: sync.RWMutex{}, - } -} - -func (rids *registeredIDs) addRegisteredIDs(id *id.ID, privkey, pubkey *cyclic.Int) { - rids.mux.Lock() - defer rids.mux.Unlock() - rids.r[*id] = keypair{ - privkey: privkey, - pubkey: pubkey, - } -} - -func (rids *registeredIDs) getRegisteredIDs(id *id.ID) (keypair, bool) { - rids.mux.Lock() - defer rids.mux.Unlock() - - kp, exists := rids.r[*id] - return kp, exists -} diff --git a/auth/request.go b/auth/request.go index 6fe4210cf7106dc8f3128b5ea267c1cc960ccdac..4cdef0e97e1da92a36004338ad450e49fa3d198c 100644 --- a/auth/request.go +++ b/auth/request.go @@ -21,6 +21,7 @@ import ( "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" cAuth "gitlab.com/elixxir/crypto/e2e/auth" + "gitlab.com/elixxir/primitives/fact" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" "io" @@ -29,40 +30,40 @@ import ( const terminator = ";" -func (s *State) RequestAuth(partner, me contact.Contact, - originDHPrivKey *cyclic.Int) (id.Round, error) { +func (s *State) RequestAuth(partner contact.Contact, myfacts fact.FactList) (id.Round, error) { // check that an authenticated channel does not already exist - if _, err := s.e2e.GetPartner(partner.ID, me.ID); err == nil || + if _, err := s.e2e.GetPartner(partner.ID); err == nil || !strings.Contains(err.Error(), ratchet.NoPartnerErrorStr) { return 0, errors.Errorf("Authenticated channel already " + "established with partner") } - return s.requestAuth(partner, me, originDHPrivKey) + return s.requestAuth(partner, myfacts, false) } // requestAuth internal helper -func (s *State) requestAuth(partner, me contact.Contact, - originDHPrivKey *cyclic.Int) (id.Round, error) { +func (s *State) requestAuth(partner contact.Contact, myfacts fact.FactList, reset bool) (id.Round, error) { //do key generation rng := s.rng.GetStream() defer rng.Close() + me := s.e2e.GetReceptionID() + dhPriv, dhPub := genDHKeys(s.e2e.GetGroup(), rng) sidhPriv, sidhPub := util.GenerateSIDHKeyPair( sidh.KeyVariantSidhA, rng) - ownership := cAuth.MakeOwnershipProof(originDHPrivKey, partner.DhPubKey, - s.e2e.GetGroup()) + ownership := cAuth.MakeOwnershipProof(s.e2e.GetHistoricalDHPrivkey(), + partner.DhPubKey, s.e2e.GetGroup()) confirmFp := cAuth.MakeOwnershipProofFP(ownership) // Add the sent request and use the return to build the send. This will // replace the send with an old one if one was in process, wasting the key // generation above. This is considered a reasonable loss due to the increase // in code simplicity of this approach - sr, err := s.store.AddSent(partner.ID, me.ID, partner.DhPubKey, dhPriv, dhPub, - sidhPriv, sidhPub, confirmFp) + sr, err := s.store.AddSent(partner.ID, partner.DhPubKey, dhPriv, dhPub, + sidhPriv, sidhPub, confirmFp, reset) if err != nil { if sr == nil { return 0, err @@ -78,7 +79,7 @@ func (s *State) requestAuth(partner, me contact.Contact, requestfp := cAuth.MakeRequestFingerprint(partner.DhPubKey) // My fact data so we can display in the interface. - msgPayload := []byte(me.Facts.Stringify() + terminator) + msgPayload := []byte(myfacts.Stringify() + terminator) // Create the request packet. request, mac, err := createRequestAuth(partner.ID, msgPayload, ownership, @@ -90,7 +91,7 @@ func (s *State) requestAuth(partner, me contact.Contact, contents := request.Marshal() //register the confirm fingerprint to pick up confirm - err = s.net.AddFingerprint(me.ID, confirmFp, &receivedConfirmService{ + err = s.net.AddFingerprint(me, confirmFp, &receivedConfirmService{ s: s, SentRequest: sr, }) @@ -100,7 +101,7 @@ func (s *State) requestAuth(partner, me contact.Contact, } //register service for notification on confirmation - s.net.AddService(me.ID, message.Service{ + s.net.AddService(me, message.Service{ Identifier: confirmFp[:], Tag: catalog.Confirm, Metadata: partner.ID[:], diff --git a/auth/reset.go b/auth/reset.go index c257a274b7e3bc1343452b0cb4a7d7da73195a44..0f415129ca36f6d5acf67b240159eb9c34844e45 100644 --- a/auth/reset.go +++ b/auth/reset.go @@ -3,22 +3,23 @@ package auth import ( jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/crypto/contact" + "gitlab.com/elixxir/primitives/fact" "gitlab.com/xx_network/primitives/id" ) -func (s *State) ResetSession(partner contact.Contact, me *id.ID) (id.Round, error) { +func (s *State) ResetSession(partner contact.Contact) (id.Round, error) { // Delete authenticated channel if it exists. - if err := s.e2e.DeletePartner(partner.ID, me); err != nil { + if err := s.e2e.DeletePartner(partner.ID); err != nil { jww.WARN.Printf("Unable to delete partner when "+ "resetting session: %+v", err) } //clean any data which is present - _ = s.store.DeleteConfirmation(partner.ID, me) - _ = s.store.DeleteSentRequest(partner.ID, me) - _ = s.store.DeleteReceivedRequest(partner.ID, me) + _ = s.store.DeleteConfirmation(partner.ID) + _ = s.store.DeleteSentRequest(partner.ID) + _ = s.store.DeleteReceivedRequest(partner.ID) // Try to initiate a clean session request - return s.confirmRequestAuth(partner, me) + return s.requestAuth(partner, fact.FactList{}, true) } diff --git a/auth/sentRequestHandler.go b/auth/sentRequestHandler.go new file mode 100644 index 0000000000000000000000000000000000000000..cb9b0b0b21591f998b9ba0a95e910e9264050410 --- /dev/null +++ b/auth/sentRequestHandler.go @@ -0,0 +1,58 @@ +package auth + +import ( + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/auth/store" + "gitlab.com/elixxir/client/cmix/message" +) + +// sentRequestHandler interface which allows the lower level to register +// sent requests with cmix while blackboxing it +type sentRequestHandler struct { + s *State +} + +// Add Adds the service and fingerprints to cmix for the given sent request +func (srh *sentRequestHandler) Add(sr *store.SentRequest) { + fp := sr.GetFingerprint() + rc := &receivedConfirmService{ + s: srh.s, + SentRequest: sr, + notificationsService: message.Service{ + Identifier: fp[:], + Tag: srh.s.params.getConfirmTag(sr.IsReset()), + Metadata: nil, + }, + } + + //add the notifications service + srh.s.net.AddService(srh.s.e2e.GetReceptionID(), rc.notificationsService, nil) + + //add the fingerprint + if err := srh.s.net.AddFingerprint(srh.s.e2e.GetReceptionID(), + sr.GetFingerprint(), rc); err != nil { + jww.FATAL.Panicf("failed to add a fingerprint for a auth confirm, " + + "this should never happen under the birthday paradox assumption of " + + "255 bits (the size fo the fingerprint).") + } + +} + +// Delete removes the service and fingerprints to cmix for the given sent +// request +func (srh *sentRequestHandler) Delete(sr *store.SentRequest) { + fp := sr.GetFingerprint() + + notificationsService := message.Service{ + Identifier: fp[:], + Tag: srh.s.params.getConfirmTag(sr.IsReset()), + Metadata: nil, + } + + //delete the notifications service + srh.s.net.DeleteService(srh.s.e2e.GetReceptionID(), notificationsService, nil) + + // delete the fingerprint, this will do nothing if the delete was caused by + // usage, but it will delete if this is a deletion of the request + srh.s.net.DeleteFingerprint(srh.s.e2e.GetReceptionID(), sr.GetFingerprint()) +} diff --git a/auth/state.go b/auth/state.go index 93a34792bab5b7f6aeabbac11af9b8ae6b527c2f..f09e174a28f9343b2d142709e583e10d79573507 100644 --- a/auth/state.go +++ b/auth/state.go @@ -8,24 +8,23 @@ package auth import ( + "encoding/base64" + "github.com/pkg/errors" "gitlab.com/elixxir/client/auth/store" "gitlab.com/elixxir/client/cmix" + "gitlab.com/elixxir/client/cmix/historical" + "gitlab.com/elixxir/client/cmix/identity/receptionID" + "gitlab.com/elixxir/client/cmix/message" "gitlab.com/elixxir/client/e2e" "gitlab.com/elixxir/client/event" - "gitlab.com/elixxir/client/interfaces" - "gitlab.com/elixxir/client/interfaces/message" - "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/client/storage/versioned" - "gitlab.com/elixxir/client/switchboard" - "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/crypto/fastRNG" "gitlab.com/xx_network/primitives/id" ) type State struct { - requestCallbacks *callbackMap - confirmCallbacks *callbackMap - resetCallbacks *callbackMap + callbacks Callbacks net cmix.Client e2e e2e.Handler @@ -34,24 +33,39 @@ type State struct { store *store.Store event event.Manager - registeredIDs - params Param } -type identity struct { - identity *id.ID - pubkey, privkey *cyclic.Int - request, confirm, reset Callback +type Callbacks interface { + Request(requestor contact.Contact, receptionID receptionID.EphemeralIdentity, + round historical.Round) + Confirm(requestor contact.Contact, receptionID receptionID.EphemeralIdentity, + round historical.Round) + Reset(requestor contact.Contact, receptionID receptionID.EphemeralIdentity, + round historical.Round) +} + +// NewState loads the auth state or creates new auth state if one cannot be +// found. +// Bases its reception identity and keys off of what is found in e2e. +// Uses this ID to modify the kv prefix for a unique storage path +func NewState(kv *versioned.KV, net cmix.Client, e2e e2e.Handler, + rng *fastRNG.StreamGenerator, event event.Manager, params Param, + callbacks Callbacks) (*State, error) { + kv = kv.Prefix(makeStorePrefix(e2e.GetReceptionID())) + return NewStateLegacy(kv, net, e2e, rng, event, params, callbacks) } -func NewManager(kv *versioned.KV, net cmix.Client, e2e e2e.Handler, +// NewStateLegacy loads the auth state or creates new auth state if one cannot be +// found. +// Bases its reception identity and keys off of what is found in e2e. +// Does not modify the kv prefix for backwards compatibility +func NewStateLegacy(kv *versioned.KV, net cmix.Client, e2e e2e.Handler, rng *fastRNG.StreamGenerator, event event.Manager, params Param, - defaultID []identity) *State { - m := &State{ - requestCallbacks: newCallbackMap(), - confirmCallbacks: newCallbackMap(), - resetCallbacks: newCallbackMap(), + callbacks Callbacks) (*State, error) { + + s := &State{ + callbacks: callbacks, net: net, e2e: e2e, @@ -59,46 +73,44 @@ func NewManager(kv *versioned.KV, net cmix.Client, e2e e2e.Handler, params: params, event: event, + } - //created lazily in add identity, see add identity for more details - store: nil, + // create the store + var err error + s.store, err = store.NewOrLoadStore(kv, e2e.GetGroup(), + &sentRequestHandler{s: s}) + + //register services + net.AddService(e2e.GetReceptionID(), message.Service{ + Identifier: e2e.GetReceptionID()[:], + Tag: params.RequestTag, + Metadata: nil, + }, &receivedRequestService{s: s, reset: false}) + + net.AddService(e2e.GetReceptionID(), message.Service{ + Identifier: e2e.GetReceptionID()[:], + Tag: params.ResetRequestTag, + Metadata: nil, + }, &receivedRequestService{s: s, reset: true}) + + if err != nil { + return nil, errors.Errorf("Failed to make Auth State manager") } - return m + return s, nil } // ReplayRequests will iterate through all pending contact requests and resend them // to the desired contact. func (s *State) ReplayRequests() { - cList := s.storage.Auth().GetAllReceived() - for i := range cList { - c := cList[i] - cbList := s.requestCallbacks.Get(c.ID) - for _, cb := range cbList { - rcb := cb.(interfaces.RequestCallback) - go rcb(c) - } + rrList := s.store.GetAllReceivedRequests() + for i := range rrList { + rr := rrList[i] + eph := receptionID.BuildIdentityFromRound(rr.GetContact().ID, rr.GetRound()) + s.callbacks.Request(rr.GetContact(), eph, rr.GetRound()) } } -// AddIdentity adds an identity and its callbacks to receive requests. -// This auto registers the appropriate services -// Note: the internal storage for auth is loaded on the first added identity, -// with that identity as the default identity. This is to allow v2.0 -// instantiations of this library (pre April 2022) to load, before requests were -// keyed on both parties IDs -func (s *State) AddIdentity(identity *id.ID, pubkey, privkey *cyclic.Int, - request, confirm, reset Callback) { - if s.store == nil { - //load store - } - -} - -func (s *State) AddDefaultIdentity(identity *id.ID, pubkey, privkey *cyclic.Int, - request, confirm, reset Callback) { - if s.store == nil { - //load store - } - +func makeStorePrefix(partner *id.ID) string { + return "authStore:" + base64.StdEncoding.EncodeToString(partner.Marshal()) } diff --git a/auth/store/authID.go b/auth/store/authID.go deleted file mode 100644 index cf9a4b628f44f3e4a7c61eb0d7bcf260f06a7511..0000000000000000000000000000000000000000 --- a/auth/store/authID.go +++ /dev/null @@ -1,39 +0,0 @@ -package store - -import ( - "encoding/base64" - "gitlab.com/xx_network/primitives/id" -) - -type authIdentity [2 * id.ArrIDLen]byte - -func makeAuthIdentity(partner, me *id.ID) authIdentity { - ph := authIdentity{} - copy(ph[:id.ArrIDLen], me[:]) - copy(ph[id.ArrIDLen:], partner[:]) - return ph -} - -func (ai authIdentity) GetMe() *id.ID { - me := &id.ID{} - copy(me[:], ai[:id.ArrIDLen]) - return me -} - -func (ai authIdentity) GetPartner() *id.ID { - partner := &id.ID{} - copy(partner[:], ai[id.ArrIDLen:]) - return partner -} - -func (ai authIdentity) String() string { - return base64.StdEncoding.EncodeToString(ai[:]) -} - -func makeReceiveRequestPrefix(aid authIdentity) string { - return "receiveRequest:" + base64.StdEncoding.EncodeToString(aid[:]) -} - -func makeSentRequestKey(aid authIdentity) string { - return "sentRequest:" + base64.StdEncoding.EncodeToString(aid[:]) -} diff --git a/auth/store/confirmation.go b/auth/store/confirmation.go index ba61ec4efedc746c79f185e1b1d55e37571d409a..c972fd8af974060824eaa735a9f6a371a56b12a8 100644 --- a/auth/store/confirmation.go +++ b/auth/store/confirmation.go @@ -29,7 +29,7 @@ type storedConfirm struct { // StoreConfirmation saves the confirmation to storage for the given partner and // fingerprint. -func (s *Store) StoreConfirmation(partner *id.ID, me *id.ID, +func (s *Store) StoreConfirmation(partner *id.ID, confirmationPayload, mac []byte, fp format.Fingerprint) error { confirm := storedConfirm{ Payload: confirmationPayload, @@ -48,16 +48,16 @@ func (s *Store) StoreConfirmation(partner *id.ID, me *id.ID, Data: confirmBytes, } - return s.kv.Set(makeConfirmationKey(partner, me), + return s.kv.Set(makeConfirmationKey(partner), currentConfirmationVersion, obj) } // LoadConfirmation loads the confirmation for the given partner and fingerprint // from storage. -func (s *Store) LoadConfirmation(partner, me *id.ID) ( +func (s *Store) LoadConfirmation(partner *id.ID) ( []byte, []byte, format.Fingerprint, error) { obj, err := s.kv.Get( - makeConfirmationKey(partner, me), currentConfirmationVersion) + makeConfirmationKey(partner), currentConfirmationVersion) if err != nil { return nil, nil, format.Fingerprint{}, err } @@ -75,15 +75,14 @@ func (s *Store) LoadConfirmation(partner, me *id.ID) ( // DeleteConfirmation deletes the confirmation for the given partner and // fingerprint from storage. -func (s *Store) DeleteConfirmation(partner, me *id.ID) error { +func (s *Store) DeleteConfirmation(partner *id.ID) error { return s.kv.Delete( - makeConfirmationKey(partner, me), currentConfirmationVersion) + makeConfirmationKey(partner), currentConfirmationVersion) } // makeConfirmationKey generates the key used to load and store confirmations // for the partner and fingerprint. -func makeConfirmationKey(partner, me *id.ID) string { +func makeConfirmationKey(partner *id.ID) string { return confirmationKeyPrefix + base64.StdEncoding.EncodeToString( - partner.Marshal()) + base64.StdEncoding.EncodeToString( - me.Marshal()) + partner.Marshal()) } diff --git a/auth/store/deletion.go b/auth/store/deletion.go index 1eff026fbbe940b7c2f90a5a95dc5059ae8c8bb9..677e1d02cacbe65d4c0e1b0dca22f70aefb449f2 100644 --- a/auth/store/deletion.go +++ b/auth/store/deletion.go @@ -35,11 +35,9 @@ func (s *Store) DeleteRequest(partner *id.ID) error { s.mux.Lock() defer s.mux.Unlock() - authId := makeAuthIdentity(partner, s.defaultID) - // Check if this is a relationship in either map - _, isReceivedRelationship := s.receivedByID[authId] - _, isSentRelationship := s.sentByID[authId] + _, isReceivedRelationship := s.receivedByID[*partner] + _, isSentRelationship := s.sentByID[*partner] // If it is not a relationship in either, return an error if !isSentRelationship && !isReceivedRelationship { @@ -49,8 +47,8 @@ func (s *Store) DeleteRequest(partner *id.ID) error { // Delete relationship. It should exist in at least one map, // for the other the delete operation is a no-op - delete(s.receivedByID, authId) - delete(s.sentByID, authId) + delete(s.receivedByID, *partner) + delete(s.sentByID, *partner) // Save to storage if err := s.save(); err != nil { @@ -78,11 +76,10 @@ func (s *Store) DeleteSentRequests() error { // DeleteReceivedRequest deletes the received request for the given partnerID // pair. -func (s *Store) DeleteReceivedRequest(partner, me *id.ID) error { +func (s *Store) DeleteReceivedRequest(partner *id.ID) error { - aid := makeAuthIdentity(partner, me) s.mux.Lock() - rr, exist := s.receivedByID[aid] + rr, exist := s.receivedByID[*partner] s.mux.Unlock() if !exist { @@ -91,8 +88,8 @@ func (s *Store) DeleteReceivedRequest(partner, me *id.ID) error { rr.mux.Lock() s.mux.Lock() - _, exist = s.receivedByID[aid] - delete(s.receivedByID, aid) + _, exist = s.receivedByID[*partner] + delete(s.receivedByID, *partner) rr.mux.Unlock() s.mux.Unlock() @@ -104,11 +101,10 @@ func (s *Store) DeleteReceivedRequest(partner, me *id.ID) error { } // DeleteSentRequest deletes the sent request for the given partnerID pair. -func (s *Store) DeleteSentRequest(partner, me *id.ID) error { +func (s *Store) DeleteSentRequest(partner *id.ID) error { - aid := makeAuthIdentity(partner, me) s.mux.Lock() - sr, exist := s.sentByID[aid] + sr, exist := s.sentByID[*partner] s.mux.Unlock() if !exist { @@ -117,8 +113,8 @@ func (s *Store) DeleteSentRequest(partner, me *id.ID) error { sr.mux.Lock() s.mux.Lock() - _, exist = s.sentByID[aid] - delete(s.receivedByID, aid) + _, exist = s.sentByID[*partner] + delete(s.receivedByID, *partner) s.mux.Unlock() sr.mux.Unlock() diff --git a/auth/store/previousNegotiations.go b/auth/store/previousNegotiations.go index 5eb1cb1ec1bc71245aef9271eec5101703c5428e..0b8e1c68ca004ae89269400657a7b2e632e1b6bd 100644 --- a/auth/store/previousNegotiations.go +++ b/auth/store/previousNegotiations.go @@ -17,12 +17,11 @@ import ( "gitlab.com/elixxir/crypto/e2e/auth" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/netTime" - "strings" ) const ( negotiationPartnersKey = "NegotiationPartners" - negotiationPartnersVersion = 1 + negotiationPartnersVersion = 0 negotiationFingerprintsKeyPrefix = "NegotiationFingerprints/" currentNegotiationFingerprintsVersion = 0 ) @@ -36,20 +35,19 @@ const ( // newFingerprint = false // in all cases it will return the position of the fingerprint, with the newest // always at position 0 -func (s *Store) CheckIfNegotiationIsNew(partner, myID *id.ID, negotiationFingerprint []byte) ( +func (s *Store) CheckIfNegotiationIsNew(partner *id.ID, negotiationFingerprint []byte) ( newFingerprint bool, position uint) { 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 - aid := makeAuthIdentity(partner, myID) - _, exists := s.previousNegotiations[aid] + _, exists := s.previousNegotiations[*partner] if !exists { - s.previousNegotiations[aid] = true + s.previousNegotiations[*partner] = true // Save fingerprint to storage - err := saveNegotiationFingerprints(partner, myID, s.kv, negotiationFingerprint) + err := saveNegotiationFingerprints(partner, s.kv, negotiationFingerprint) if err != nil { jww.FATAL.Panicf("Failed to save negotiation sentByFingerprints for "+ "partner %s: %+v", partner, err) @@ -68,7 +66,7 @@ func (s *Store) CheckIfNegotiationIsNew(partner, myID *id.ID, negotiationFingerp } // get the fingerprint list from storage - fingerprints, err := loadNegotiationFingerprints(partner, myID, s.kv, myID.Cmp(s.defaultID)) + fingerprints, err := loadNegotiationFingerprints(partner, s.kv) if err != nil { jww.FATAL.Panicf("Failed to load negotiation sentByFingerprints for "+ "partner %s: %+v", partner, err) @@ -91,7 +89,7 @@ func (s *Store) CheckIfNegotiationIsNew(partner, myID *id.ID, negotiationFingerp // 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 = saveNegotiationFingerprints(partner, myID, s.kv, fingerprints...) + err = saveNegotiationFingerprints(partner, s.kv, fingerprints...) if err != nil { jww.FATAL.Panicf("Failed to save negotiation sentByFingerprints for "+ "partner %s: %+v", partner, err) @@ -117,36 +115,22 @@ func (s *Store) savePreviousNegotiations() error { // newOrLoadPreviousNegotiations loads the list of previousNegotiations partners // from storage. -func (s *Store) newOrLoadPreviousNegotiations() (map[authIdentity]bool, error) { +func (s *Store) newOrLoadPreviousNegotiations() (map[id.ID]bool, 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") { - 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]bool), nil - } else { - return nil, err - } - } - return unmarshalOldPreviousNegotiations(obj.Data, s.defaultID), nil - } else { - return nil, err - } + return nil, err } return unmarshalPreviousNegotiations(obj.Data) } // marshalPreviousNegotiations marshals the list of partners into a byte slice. -func marshalPreviousNegotiations(partners map[authIdentity]bool) []byte { - toMarshal := make([]authIdentity, 0, len(partners)) +func marshalPreviousNegotiations(partners map[id.ID]bool) []byte { + toMarshal := make([]id.ID, 0, len(partners)) - for aid := range partners { - toMarshal = append(toMarshal, aid) + for partner := range partners { + toMarshal = append(toMarshal, partner) } b, err := json.Marshal(&toMarshal) @@ -159,15 +143,15 @@ func marshalPreviousNegotiations(partners map[authIdentity]bool) []byte { // unmarshalPreviousNegotiations unmarshalls the marshalled json into a //// list of partner IDs. -func unmarshalPreviousNegotiations(b []byte) (map[authIdentity]bool, +func unmarshalPreviousNegotiations(b []byte) (map[id.ID]bool, error) { - unmarshal := make([]authIdentity, 0) + unmarshal := make([]id.ID, 0) if err := json.Unmarshal(b, &unmarshal); err != nil { return nil, err } - partners := make(map[authIdentity]bool) + partners := make(map[id.ID]bool) for _, aid := range unmarshal { partners[aid] = true @@ -176,31 +160,10 @@ func unmarshalPreviousNegotiations(b []byte) (map[authIdentity]bool, return partners, nil } -// unmarshalOldPreviousNegotiations unmarshalls the marshalled json into a -// list of partner IDs. -func unmarshalOldPreviousNegotiations(buf []byte, defaultID *id.ID) map[authIdentity]bool { - buff := bytes.NewBuffer(buf) - - numberOfPartners := binary.LittleEndian.Uint64(buff.Next(8)) - partners := make(map[authIdentity]bool, numberOfPartners) - - for i := uint64(0); i < numberOfPartners; i++ { - partner, err := id.Unmarshal(buff.Next(id.ArrIDLen)) - if err != nil { - jww.FATAL.Panicf( - "Failed to unmarshal negotiation partner ID: %+v", err) - } - - partners[makeAuthIdentity(partner, defaultID)] = false - } - - return partners -} - // saveNegotiationFingerprints saves the list of sentByFingerprints for the given // partner to storage. func saveNegotiationFingerprints( - partner, myID *id.ID, kv *versioned.KV, fingerprints ...[]byte) error { + partner *id.ID, kv *versioned.KV, fingerprints ...[]byte) error { obj := &versioned.Object{ Version: currentNegotiationFingerprintsVersion, @@ -208,29 +171,17 @@ func saveNegotiationFingerprints( Data: marshalNegotiationFingerprints(fingerprints...), } - return kv.Set(makeNegotiationFingerprintsKey(partner, myID), + return kv.Set(makeNegotiationFingerprintsKey(partner), currentNegotiationFingerprintsVersion, obj) } // loadNegotiationFingerprints loads the list of sentByFingerprints for the given // partner from storage. -func loadNegotiationFingerprints(partner, myID *id.ID, kv *versioned.KV, possibleOld bool) ([][]byte, error) { - obj, err := kv.Get(makeNegotiationFingerprintsKey(partner, myID), +func loadNegotiationFingerprints(partner *id.ID, kv *versioned.KV) ([][]byte, error) { + obj, err := kv.Get(makeNegotiationFingerprintsKey(partner), currentNegotiationFingerprintsVersion) if err != nil { - if possibleOld { - obj, err = kv.Get(makeOldNegotiationFingerprintsKey(partner), - currentNegotiationFingerprintsVersion) - if err != nil { - return nil, err - } - if err = kv.Set(makeNegotiationFingerprintsKey(partner, myID), - currentNegotiationFingerprintsVersion, obj); err != nil { - return nil, err - } - } else { - return nil, err - } + return nil, err } return unmarshalNegotiationFingerprints(obj.Data), nil @@ -273,15 +224,7 @@ func unmarshalNegotiationFingerprints(buf []byte) [][]byte { // makeOldNegotiationFingerprintsKey generates the key used to load and store // negotiation sentByFingerprints for the partner. -func makeOldNegotiationFingerprintsKey(partner *id.ID) string { +func makeNegotiationFingerprintsKey(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, myID *id.ID) string { - return negotiationFingerprintsKeyPrefix + - string(base64.StdEncoding.EncodeToString(partner.Marshal())) + - string(base64.StdEncoding.EncodeToString(myID.Marshal())) -} diff --git a/auth/store/receivedRequest.go b/auth/store/receivedRequest.go index 701fad374c26a515ec0a17a78954e0072bbdc0f9..957af798edfe52c18cf52ba465520194a5a4224f 100644 --- a/auth/store/receivedRequest.go +++ b/auth/store/receivedRequest.go @@ -1,9 +1,11 @@ package store import ( + "encoding/base64" "github.com/cloudflare/circl/dh/sidh" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/cmix/historical" util "gitlab.com/elixxir/client/storage/utility" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/contact" @@ -14,97 +16,80 @@ import ( type ReceivedRequest struct { kv *versioned.KV - aid authIdentity - - // ID received on - myID *id.ID - // contact of partner partner contact.Contact //sidHPublic key of partner theirSidHPubKeyA *sidh.PublicKey + //round received on + round historical.Round + //lock to make sure only one operator at a time mux *sync.Mutex } -func newReceivedRequest(kv *versioned.KV, myID *id.ID, c contact.Contact, - key *sidh.PublicKey) *ReceivedRequest { - - aid := makeAuthIdentity(c.ID, myID) - kv = kv.Prefix(makeReceiveRequestPrefix(aid)) +func newReceivedRequest(kv *versioned.KV, c contact.Contact, + key *sidh.PublicKey, round historical.Round) *ReceivedRequest { if err := util.StoreContact(kv, c); err != nil { jww.FATAL.Panicf("Failed to save contact for partner %s", c.ID.String()) } - storeKey := util.MakeSIDHPublicKeyKey(c.ID) - if err := util.StoreSIDHPublicKey(kv, key, storeKey); err != nil { - jww.FATAL.Panicf("Failed to save contact pubKey for partner %s", - c.ID.String()) + sidhStoreKey := util.MakeSIDHPublicKeyKey(c.ID) + if err := util.StoreSIDHPublicKey(kv, key, sidhStoreKey); err != nil { + jww.FATAL.Panicf("Failed to save contact SIDH pubKey for "+ + "partner %s", c.ID.String()) + } + + roundStoreKey := makeRoundKey(c.ID) + if err := util.StoreRound(kv, round, roundStoreKey); err != nil { + jww.FATAL.Panicf("Failed to save round request was received on "+ + "for partner %s", c.ID.String()) } return &ReceivedRequest{ kv: kv, - aid: aid, - myID: myID, partner: c, theirSidHPubKeyA: key, + round: round, } } -func loadReceivedRequest(kv *versioned.KV, partner *id.ID, myID *id.ID) ( +func loadReceivedRequest(kv *versioned.KV, partner *id.ID) ( *ReceivedRequest, error) { - // try the load with both the new prefix and the old, which one is - // successful will determine which file structure the sent request will use - // a change was made when auth was upgraded to handle auths for multiple - // outgoing IDs and it became possible to have multiple auths for the same - // partner at a time, so it now needed to be keyed on the touple of - // partnerID,MyID. Old receivedByID always have the same myID so they can be left - // at their own paths - aid := makeAuthIdentity(partner, myID) - newKV := kv - oldKV := kv.Prefix(makeReceiveRequestPrefix(aid)) - - c, err := util.LoadContact(newKV, partner) + c, err := util.LoadContact(kv, partner) //loading with the new prefix path failed, try with the new if err != nil { - c, err = util.LoadContact(newKV, partner) - if err != nil { - return nil, errors.WithMessagef(err, "Failed to Load "+ - "Received Auth Request Contact with %s and to %s", - partner, myID) - } else { - kv = oldKV - } - } else { - kv = newKV + return nil, errors.WithMessagef(err, "Failed to Load "+ + "Received Auth Request Contact with %s", + partner) + } + + key, err := util.LoadSIDHPublicKey(kv, util.MakeSIDHPublicKeyKey(partner)) + if err != nil { + return nil, errors.WithMessagef(err, "Failed to Load "+ + "Received Auth Request Partner SIDHkey with %s", + partner) } - key, err := util.LoadSIDHPublicKey(kv, - util.MakeSIDHPublicKeyKey(c.ID)) + round, err := util.LoadRound(kv, makeRoundKey(partner)) if err != nil { return nil, errors.WithMessagef(err, "Failed to Load "+ - "Received Auth Request Partner SIDHkey with %s and to %s", - partner, myID) + "round request was received on with %s", + partner) } return &ReceivedRequest{ - aid: aid, kv: kv, - myID: myID, partner: c, theirSidHPubKeyA: key, + round: round, }, nil } -func (rr *ReceivedRequest) GetMyID() *id.ID { - return rr.myID -} - func (rr *ReceivedRequest) GetContact() contact.Contact { return rr.partner } @@ -113,15 +98,19 @@ func (rr *ReceivedRequest) GetTheirSidHPubKeyA() *sidh.PublicKey { return rr.theirSidHPubKeyA } +func (rr *ReceivedRequest) GetRound() historical.Round { + return rr.round +} + 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) + "contact for %s", rr.partner.ID) } 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) + "SIDH pubkey for %s", rr.partner.ID) } } @@ -132,3 +121,8 @@ func (rr *ReceivedRequest) getType() RequestType { func (rr *ReceivedRequest) isTemporary() bool { return rr.kv.IsMemStore() } + +func makeRoundKey(partner *id.ID) string { + return "receivedRequestRound:" + + base64.StdEncoding.EncodeToString(partner.Marshal()) +} diff --git a/auth/store/request.go b/auth/store/request.go index 634ec0c3771dba81bf32bcc951d9c79acb602f4e..0678d9c5c220c29f88c49aa286dfd592454a95be 100644 --- a/auth/store/request.go +++ b/auth/store/request.go @@ -15,7 +15,6 @@ const ( ) type requestDisk struct { - T uint - ID []byte - MyID []byte + T uint + ID []byte } diff --git a/auth/store/sentRequest.go b/auth/store/sentRequest.go index 674c3b0552af4fe3a102d26c33ae7a43ba456c96..3347837eb6b4d8eeee57995d75d4d0cb8055f5ea 100644 --- a/auth/store/sentRequest.go +++ b/auth/store/sentRequest.go @@ -8,6 +8,7 @@ package store import ( + "encoding/base64" "encoding/hex" "encoding/json" "github.com/cloudflare/circl/dh/sidh" @@ -27,9 +28,6 @@ const currentSentRequestVersion = 0 type SentRequest struct { kv *versioned.KV - aid authIdentity - - myID *id.ID partner *id.ID partnerHistoricalPubKey *cyclic.Int myPrivKey *cyclic.Int @@ -37,6 +35,7 @@ type SentRequest struct { mySidHPrivKeyA *sidh.PrivateKey mySidHPubKeyA *sidh.PublicKey fingerprint format.Fingerprint + reset bool mux sync.Mutex } @@ -48,17 +47,15 @@ type sentRequestDisk struct { MySidHPrivKeyA []byte MySidHPubKeyA []byte Fingerprint []byte + Reset bool } -func newSentRequest(kv *versioned.KV, partner, myID *id.ID, partnerHistoricalPubKey, myPrivKey, - myPubKey *cyclic.Int, sidHPrivA *sidh.PrivateKey, sidHPubA *sidh.PublicKey, - fp format.Fingerprint) (*SentRequest, error) { - - aid := makeAuthIdentity(partner, myID) +func newSentRequest(kv *versioned.KV, partner *id.ID, partnerHistoricalPubKey, + myPrivKey, myPubKey *cyclic.Int, sidHPrivA *sidh.PrivateKey, + sidHPubA *sidh.PublicKey, fp format.Fingerprint, reset bool) (*SentRequest, error) { sr := &SentRequest{ kv: kv, - aid: aid, partner: partner, partnerHistoricalPubKey: partnerHistoricalPubKey, myPrivKey: myPrivKey, @@ -66,39 +63,20 @@ func newSentRequest(kv *versioned.KV, partner, myID *id.ID, partnerHistoricalPub mySidHPubKeyA: sidHPubA, mySidHPrivKeyA: sidHPrivA, fingerprint: fp, + reset: reset, } return sr, sr.save() } -func loadSentRequest(kv *versioned.KV, partner *id.ID, myID *id.ID, grp *cyclic.Group) (*SentRequest, error) { - - // try the load with both the new prefix and the old, which one is - // successful will determine which file structure the sent request will use - // a change was made when auth was upgraded to handle auths for multiple - // outgoing IDs and it became possible to have multiple auths for the same - // partner at a time, so it now needed to be keyed on the touple of - // partnerID,MyID. Old receivedByID always have the same myID so they can be left - // at their own paths - aid := makeAuthIdentity(partner, myID) +func loadSentRequest(kv *versioned.KV, partner *id.ID, grp *cyclic.Group) (*SentRequest, error) { - obj, err := kv.Get(makeSentRequestKey(aid), + obj, err := kv.Get(makeSentRequestKey(partner), currentSentRequestVersion) - //loading with the new prefix path failed, try with the new if err != nil { - obj, err = kv.Get(versioned.MakePartnerPrefix(partner), - currentSentRequestVersion) - if err != nil { - return nil, errors.WithMessagef(err, "Failed to Load "+ - "SentRequest Auth with %s", partner) - } else { - err = kv.Set(makeSentRequestKey(aid), currentSentRequestVersion, obj) - if err != nil { - return nil, errors.WithMessagef(err, "Failed to update "+ - "from old store SentRequest Auth with %s", partner) - } - } + return nil, errors.WithMessagef(err, "Failed to Load "+ + "SentRequest Auth with %s", partner) } srd := &sentRequestDisk{} @@ -158,8 +136,6 @@ func loadSentRequest(kv *versioned.KV, partner *id.ID, myID *id.ID, grp *cyclic. return &SentRequest{ kv: kv, - aid: aid, - myID: myID, partner: partner, partnerHistoricalPubKey: historicalPubKey, myPrivKey: myPrivKey, @@ -167,6 +143,7 @@ func loadSentRequest(kv *versioned.KV, partner *id.ID, myID *id.ID, grp *cyclic. mySidHPrivKeyA: mySidHPrivKeyA, mySidHPubKeyA: mySidHPubKeyA, fingerprint: fp, + reset: srd.Reset, }, nil } @@ -209,6 +186,7 @@ func (sr *SentRequest) save() error { MySidHPrivKeyA: sidHPriv, MySidHPubKeyA: sidHPub, Fingerprint: sr.fingerprint[:], + Reset: sr.reset, } data, err := json.Marshal(&ipd) @@ -258,6 +236,10 @@ func (sr *SentRequest) GetMySIDHPubKey() *sidh.PublicKey { return sr.mySidHPubKeyA } +func (sr *SentRequest) IsReset() bool { + return sr.reset +} + // OverwriteSIDHKeys is used to temporarily overwrite sidh keys // to handle e.g., confirmation receivedByID. // FIXME: this is a code smell but was the cleanest solution at @@ -272,10 +254,6 @@ func (sr *SentRequest) GetFingerprint() format.Fingerprint { return sr.fingerprint } -func (sr *SentRequest) getAuthID() authIdentity { - return sr.aid -} - func (sr *SentRequest) getType() RequestType { return Sent } @@ -283,3 +261,7 @@ func (sr *SentRequest) getType() RequestType { func (sr *SentRequest) isTemporary() bool { return sr.kv.IsMemStore() } + +func makeSentRequestKey(partner *id.ID) string { + return "sentRequest:" + base64.StdEncoding.EncodeToString(partner.Marshal()) +} diff --git a/auth/store/store.go b/auth/store/store.go index 9fcf821dbb7ca04c64fb5f6ac4a71b7ce49e7c25..6a08e6b931871754209c98dda25173cd83a92e92 100644 --- a/auth/store/store.go +++ b/auth/store/store.go @@ -12,6 +12,7 @@ import ( "github.com/cloudflare/circl/dh/sidh" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/cmix/historical" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/crypto/cyclic" @@ -31,54 +32,28 @@ const requestMapVersion = 0 type Store struct { kv *versioned.KV grp *cyclic.Group - receivedByID map[authIdentity]*ReceivedRequest - sentByID map[authIdentity]*SentRequest + receivedByID map[id.ID]*ReceivedRequest + sentByID map[id.ID]*SentRequest - previousNegotiations map[authIdentity]bool - - defaultID *id.ID + previousNegotiations map[id.ID]bool srh SentRequestHandler - unloadedDefault []requestDisk - mux sync.RWMutex } -// NewStore creates a new store. All passed in private keys are added as -// sentByFingerprints so they can be used to trigger receivedByID. -func NewStore(kv *versioned.KV, grp *cyclic.Group, srh SentRequestHandler) error { - kv = kv.Prefix(storePrefix) - s := &Store{ - kv: kv, - grp: grp, - receivedByID: make(map[authIdentity]*ReceivedRequest), - sentByID: make(map[authIdentity]*SentRequest), - previousNegotiations: make(map[authIdentity]bool), - srh: srh, - } - - err := s.savePreviousNegotiations() - if err != nil { - return errors.Errorf( - "failed to load previousNegotiations partners: %+v", err) - } - - return s.save() -} - -// LoadStore loads an extant new store. All passed in private keys are added as +// NewOrLoadStore loads an extant new store. All passed in private keys are added as // sentByFingerprints so they can be used to trigger receivedByID. -func LoadStore(kv *versioned.KV, defaultID *id.ID, grp *cyclic.Group, srh SentRequestHandler) (*Store, error) { +// If no store can be found, it creates a new one +func NewOrLoadStore(kv *versioned.KV, grp *cyclic.Group, srh SentRequestHandler) (*Store, error) { kv = kv.Prefix(storePrefix) s := &Store{ kv: kv, grp: grp, - receivedByID: make(map[authIdentity]*ReceivedRequest), - sentByID: make(map[authIdentity]*SentRequest), - previousNegotiations: make(map[authIdentity]bool), - defaultID: defaultID, + receivedByID: make(map[id.ID]*ReceivedRequest), + sentByID: make(map[id.ID]*SentRequest), + previousNegotiations: make(map[id.ID]bool), srh: srh, } @@ -87,7 +62,13 @@ func LoadStore(kv *versioned.KV, defaultID *id.ID, grp *cyclic.Group, srh SentRe //load all receivedByID sentObj, err := kv.Get(requestMapKey, requestMapVersion) if err != nil { - return nil, errors.WithMessagef(err, "Failed to load requestMap") + //no store can be found, lets make a new one + jww.WARN.Printf("No auth store could be found, making a new one") + s, err := newStore(kv, grp, srh) + if err != nil { + return nil, errors.WithMessagef(err, "Failed to load requestMap") + } + return s, nil } if err := json.Unmarshal(sentObj.Data, &requestList); err != nil { @@ -106,35 +87,22 @@ func LoadStore(kv *versioned.KV, defaultID *id.ID, grp *cyclic.Group, srh SentRe jww.FATAL.Panicf("Failed to load stored id: %+v", err) } - var myID *id.ID - // load the self id used. If it is blank, that means this is an older - // version request, and replace it with the default ID - if len(rDisk.MyID) == 0 { - s.unloadedDefault = append(s.unloadedDefault, rDisk) - continue - } else { - myID, err = id.Unmarshal(rDisk.ID) - if err != nil { - jww.FATAL.Panicf("Failed to load stored self id: %+v", err) - } - } - switch requestType { case Sent: - sr, err := loadSentRequest(kv, partner, myID, grp) + sr, err := loadSentRequest(kv, partner, grp) if err != nil { jww.FATAL.Panicf("Failed to load stored sentRequest: %+v", err) } - s.sentByID[sr.getAuthID()] = sr + s.sentByID[*sr.GetPartner()] = sr s.srh.Add(sr) case Receive: - rr, err := loadReceivedRequest(kv, partner, myID) + rr, err := loadReceivedRequest(kv, partner) if err != nil { jww.FATAL.Panicf("Failed to load stored receivedRequest: %+v", err) } - s.receivedByID[rr.aid] = rr + s.receivedByID[*rr.GetContact().ID] = rr default: jww.FATAL.Panicf("Unknown request type: %d", requestType) @@ -156,9 +124,8 @@ func (s *Store) save() error { for _, rr := range s.receivedByID { if !rr.isTemporary() { rDisk := requestDisk{ - T: uint(rr.getType()), - ID: rr.partner.ID.Marshal(), - MyID: rr.myID.Marshal(), + T: uint(rr.getType()), + ID: rr.partner.ID.Marshal(), } requestIDList = append(requestIDList, rDisk) } @@ -167,9 +134,8 @@ func (s *Store) save() error { for _, sr := range s.sentByID { if !sr.isTemporary() { rDisk := requestDisk{ - T: uint(sr.getType()), - ID: sr.partner.Marshal(), - MyID: sr.myID.Marshal(), + T: uint(sr.getType()), + ID: sr.partner.Marshal(), } requestIDList = append(requestIDList, rDisk) } @@ -188,61 +154,81 @@ func (s *Store) save() error { return s.kv.Set(requestMapKey, requestMapVersion, &obj) } -func (s *Store) AddSent(partner, myID *id.ID, partnerHistoricalPubKey, myPrivKey, +// NewStore creates a new store. All passed in private keys are added as +// sentByFingerprints so they can be used to trigger receivedByID. +func newStore(kv *versioned.KV, grp *cyclic.Group, srh SentRequestHandler) ( + *Store, error) { + kv = kv.Prefix(storePrefix) + s := &Store{ + kv: kv, + grp: grp, + receivedByID: make(map[id.ID]*ReceivedRequest), + sentByID: make(map[id.ID]*SentRequest), + previousNegotiations: make(map[id.ID]bool), + srh: srh, + } + + err := s.savePreviousNegotiations() + if err != nil { + return nil, errors.Errorf( + "failed to save previousNegotiations partners: %+v", err) + } + + return s, s.save() +} + +func (s *Store) AddSent(partner *id.ID, partnerHistoricalPubKey, myPrivKey, myPubKey *cyclic.Int, sidHPrivA *sidh.PrivateKey, sidHPubA *sidh.PublicKey, - fp format.Fingerprint) (*SentRequest, error) { + fp format.Fingerprint, reset bool) (*SentRequest, error) { s.mux.Lock() defer s.mux.Unlock() - aid := makeAuthIdentity(partner, myID) - - if sentRq, ok := s.sentByID[aid]; ok { + if sentRq, ok := s.sentByID[*partner]; ok { return sentRq, errors.Errorf("Cannot make new sentRequest for partner "+ "%s, a sent request already exists", partner) } - if _, ok := s.receivedByID[aid]; ok { + if _, ok := s.receivedByID[*partner]; ok { return nil, errors.Errorf("Cannot make new sentRequest for partner "+ "%s, a received reqyest already exists", partner) } - sr, err := newSentRequest(s.kv, partner, myID, partnerHistoricalPubKey, myPrivKey, - myPubKey, sidHPrivA, sidHPubA, fp) + sr, err := newSentRequest(s.kv, partner, partnerHistoricalPubKey, myPrivKey, + myPubKey, sidHPrivA, sidHPubA, fp, reset) if err != nil { return nil, err } - s.sentByID[sr.getAuthID()] = sr + s.sentByID[*sr.GetPartner()] = sr s.srh.Add(sr) if err = s.save(); err != nil { jww.FATAL.Panicf("Failed to save Sent Request Map after adding "+ - "partner %s to %s", partner, myID) + "partner %s", partner) } return sr, nil } -func (s *Store) AddReceived(myID *id.ID, c contact.Contact, key *sidh.PublicKey) error { +func (s *Store) AddReceived(c contact.Contact, key *sidh.PublicKey, + round historical.Round) error { s.mux.Lock() defer s.mux.Unlock() - jww.DEBUG.Printf("AddReceived new contact: %s with %s", c.ID, myID) + jww.DEBUG.Printf("AddReceived new contact: %s", c.ID) - aih := makeAuthIdentity(c.ID, myID) - - if _, ok := s.receivedByID[aih]; ok { + if _, ok := s.receivedByID[*c.ID]; ok { return errors.Errorf("Cannot add contact for partner "+ "%s, one already exists", c.ID) } - if _, ok := s.sentByID[aih]; ok { + if _, ok := s.sentByID[*c.ID]; ok { return errors.Errorf("Cannot add contact for partner "+ "%s, one already exists", c.ID) } - r := newReceivedRequest(s.kv, myID, c, key) + r := newReceivedRequest(s.kv, c, key, round) - s.receivedByID[r.aid] = r + s.receivedByID[*r.GetContact().ID] = r if err := s.save(); err != nil { jww.FATAL.Panicf("Failed to save Sent Request Map after adding "+ - "partner %s to %s", c.ID, myID) + "partner %s", c.ID) } return nil @@ -251,11 +237,10 @@ func (s *Store) AddReceived(myID *id.ID, c contact.Contact, key *sidh.PublicKey) // 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) +func (s *Store) HandleReceivedRequest(partner *id.ID, handler func(*ReceivedRequest) error) error { s.mux.RLock() - rr, ok := s.receivedByID[aid] + rr, ok := s.receivedByID[*partner] s.mux.RUnlock() if !ok { @@ -270,7 +255,7 @@ func (s *Store) HandleReceivedRequest(partner, myID *id.ID, handler func(*Receiv // Check that the request still exists; it could have been deleted while the // lock was taken s.mux.RLock() - _, ok = s.receivedByID[aid] + _, ok = s.receivedByID[*partner] s.mux.RUnlock() if !ok { @@ -285,7 +270,7 @@ func (s *Store) HandleReceivedRequest(partner, myID *id.ID, handler func(*Receiv return errors.WithMessage(handleErr, "Received error from handler") } - delete(s.receivedByID, aid) + delete(s.receivedByID, *partner) rr.delete() return nil @@ -294,11 +279,10 @@ func (s *Store) HandleReceivedRequest(partner, myID *id.ID, handler func(*Receiv // 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) +func (s *Store) HandleSentRequest(partner *id.ID, handler func(request *SentRequest) error) error { s.mux.RLock() - sr, ok := s.sentByID[aid] + sr, ok := s.sentByID[*partner] s.mux.RUnlock() if !ok { @@ -313,7 +297,7 @@ func (s *Store) HandleSentRequest(partner, myID *id.ID, handler func(request *Se // Check that the request still exists; it could have been deleted while the // lock was taken s.mux.RLock() - _, ok = s.sentByID[aid] + _, ok = s.sentByID[*partner] s.mux.RUnlock() if !ok { @@ -328,7 +312,7 @@ func (s *Store) HandleSentRequest(partner, myID *id.ID, handler func(request *Se return errors.WithMessage(handleErr, "Received error from handler") } - delete(s.receivedByID, aid) + delete(s.receivedByID, *partner) sr.delete() return nil @@ -337,15 +321,9 @@ func (s *Store) HandleSentRequest(partner, myID *id.ID, handler func(request *Se // 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 - } - - aid := makeAuthIdentity(partner, myID) - +func (s *Store) GetReceivedRequest(partner *id.ID) (contact.Contact, error) { s.mux.RLock() - r, ok := s.receivedByID[aid] + r, ok := s.receivedByID[*partner] s.mux.RUnlock() if !ok { @@ -355,3 +333,17 @@ func (s *Store) GetReceivedRequest(partner, myID *id.ID) (contact.Contact, error return r.partner, nil } + +// GetAllReceivedRequests returns a slice of all recieved requests. +func (s *Store) GetAllReceivedRequests() []*ReceivedRequest { + + s.mux.RLock() + rr := make([]*ReceivedRequest, 0, len(s.receivedByID)) + + for _, r := range s.receivedByID { + rr = append(rr, r) + } + s.mux.RUnlock() + + return rr +} diff --git a/auth/verify.go b/auth/verify.go index 66a86df6f125bb0abe7c28be4ecbb408603b0a98..f89d92c8b45e7a92815bc123ec4a3be422cec142 100644 --- a/auth/verify.go +++ b/auth/verify.go @@ -8,13 +8,13 @@ package auth import ( - "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/e2e" "gitlab.com/elixxir/crypto/contact" cAuth "gitlab.com/elixxir/crypto/e2e/auth" ) -func VerifyOwnership(received, verified contact.Contact, storage *storage.Session) bool { - myHistoricalPrivKey := storage.E2e().GetDHPrivateKey() +func VerifyOwnership(received, verified contact.Contact, e2e e2e.Handler) bool { + myHistoricalPrivKey := e2e.GetHistoricalDHPrivkey() return cAuth.VerifyOwnershipProof(myHistoricalPrivKey, verified.DhPubKey, - storage.E2e().GetGroup(), received.OwnershipProof) + e2e.GetGroup(), received.OwnershipProof) } diff --git a/cmix/identity/receptionID/identity.go b/cmix/identity/receptionID/identity.go index ae421452a0b1a2386487de635c6e49b9b0490945..e0e75425ca6a0db288de908b9c6a8f8736eb40a1 100644 --- a/cmix/identity/receptionID/identity.go +++ b/cmix/identity/receptionID/identity.go @@ -3,7 +3,9 @@ package receptionID import ( "encoding/json" "github.com/pkg/errors" + "gitlab.com/elixxir/client/cmix/historical" "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/primitives/states" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/id/ephemeral" "gitlab.com/xx_network/primitives/netTime" @@ -114,3 +116,15 @@ func (i Identity) Equal(b Identity) bool { i.EndValid.Equal(b.EndValid) && i.Ephemeral == b.Ephemeral } + +// BuildIdentityFromRound returns an EphemeralIdentity that the source would +// use to receive messages from the given round +func BuildIdentityFromRound(source *id.ID, + round historical.Round) EphemeralIdentity { + ephID, _, _, _ := ephemeral.GetId(source, uint(round.AddressSpaceSize), + round.Timestamps[states.QUEUED].UnixNano()) + return EphemeralIdentity{ + EphId: ephID, + Source: source, + } +} diff --git a/storage/utility/round.go b/storage/utility/round.go new file mode 100644 index 0000000000000000000000000000000000000000..1227bc6ce2fc5c8fcdec24d12ca5836f31cc6fcf --- /dev/null +++ b/storage/utility/round.go @@ -0,0 +1,50 @@ +package utility + +import ( + "github.com/golang/protobuf/proto" + "gitlab.com/elixxir/client/cmix/historical" + "gitlab.com/elixxir/client/storage/versioned" + pb "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/xx_network/primitives/netTime" +) + +const currentRoundVersion = 0 + +// StoreRound stores the round using the key. +func StoreRound(kv *versioned.KV, round historical.Round, key string) error { + now := netTime.Now() + + marshaled, err := proto.Marshal(round.Raw) + + if err != nil { + return err + } + + obj := versioned.Object{ + Version: currentCyclicVersion, + Timestamp: now, + Data: marshaled, + } + + return kv.Set(key, currentRoundVersion, &obj) +} + +// LoadRound stores the round using the key. +func LoadRound(kv *versioned.KV, key string) (historical.Round, error) { + vo, err := kv.Get(key, currentRoundVersion) + if err != nil { + return historical.Round{}, err + } + + ri := &pb.RoundInfo{} + err = proto.Unmarshal(vo.Data, ri) + if err != nil { + return historical.Round{}, err + } + + return historical.MakeRound(ri), nil +} + +func DeleteRound(kv *versioned.KV, key string) error { + return kv.Delete(key, currentCyclicVersion) +}