diff --git a/e2e/fpGenerator.go b/e2e/fpGenerator.go index e9dabae2bdbf1aae89bfd61e13aaa22558695316..2e42f9af79bd186885392d2333f7c69cd461368d 100644 --- a/e2e/fpGenerator.go +++ b/e2e/fpGenerator.go @@ -5,6 +5,9 @@ import ( "gitlab.com/elixxir/client/e2e/ratchet/partner/session" ) +// Wrapper which allows the network>manager's fingerprint interface to be +// passed into ratchet without exposing ratchet to buisness logic +// adheres to the CypherHandler interface in session type fpGenerator struct { *manager } diff --git a/e2e/ratchet/partner/manager.go b/e2e/ratchet/partner/manager.go index f0a0f918e39f31f328f765b529c6535c59e4bcc5..3ff4befee5f95b491bf07b3aa149308a6fa19cef 100644 --- a/e2e/ratchet/partner/manager.go +++ b/e2e/ratchet/partner/manager.go @@ -189,9 +189,8 @@ func (m *Manager) NewReceiveSession(partnerPubKey *cyclic.Int, // received from the partner and a new private key for the user. Passing in a // private key is optional. A private key will be generated if none is passed. func (m *Manager) NewSendSession(myPrivKey *cyclic.Int, - mySIDHPrivKey *sidh.PrivateKey, e2eParams session.Params) *session.Session { - // Find the latest public key from the other party - sourceSession := m.receive.getNewestRekeyableSession() + mySIDHPrivKey *sidh.PrivateKey, e2eParams session.Params, + sourceSession *session.Session) *session.Session { // Add the session to the Send session buffer and return return m.send.AddSession(myPrivKey, sourceSession.GetPartnerPubKey(), nil, diff --git a/e2e/ratchet/partner/session/params.go b/e2e/ratchet/partner/session/params.go index 756bff1fa0a1129fd9c1874e22a9759a0ffbc4bd..033616ac26a8a65dc44435a56c3431dfea51447d 100644 --- a/e2e/ratchet/partner/session/params.go +++ b/e2e/ratchet/partner/session/params.go @@ -14,6 +14,10 @@ type Params struct { // many keys are not allowed to be used for sending messages // in order to ensure there are extras for rekeying. NumRekeys uint16 + // Number from 0 to 1, denotes how often when in the unconfirmed state the + // system will automatically resend the rekey request on any message send + // from the partner the session is associated with + UnconfirmedRetryRatio float64 } // DEFAULT KEY GENERATION PARAMETERS @@ -24,14 +28,16 @@ const ( maxKeys uint16 = 2000 rekeyThrshold float64 = 0.05 numReKeys uint16 = 16 + rekeyRatio float64 = 1 / 10 ) func GetDefaultE2ESessionParams() Params { return Params{ - MinKeys: minKeys, - MaxKeys: maxKeys, - RekeyThreshold: rekeyThrshold, - NumRekeys: numReKeys, + MinKeys: minKeys, + MaxKeys: maxKeys, + RekeyThreshold: rekeyThrshold, + NumRekeys: numReKeys, + UnconfirmedRetryRatio: rekeyRatio, } } diff --git a/e2e/ratchet/partner/session/session.go b/e2e/ratchet/partner/session/session.go index 7d1b227cb038651c9560834b59acd285ee137c68..a86a5e0b07e58b66ea5e47b4f399d2b78b5517b7 100644 --- a/e2e/ratchet/partner/session/session.go +++ b/e2e/ratchet/partner/session/session.go @@ -309,12 +309,12 @@ func GetSessionIDFromBaseKeyForTesting(baseKey *cyclic.Int, i interface{}) Sessi return GetSessionIDFromBaseKey(baseKey) } -//Blake2B hash of base key used for storage +// GetID Blake2B hash of base key used for storage func (s *Session) GetID() SessionID { return s.sID } -// returns the ID of the partner for this session +// GetPartner returns the ID of the partner for this session func (s *Session) GetPartner() *id.ID { if s.partner != nil { return s.partner @@ -323,92 +323,9 @@ func (s *Session) GetPartner() *id.ID { } } -//ekv functions -func (s *Session) marshal() ([]byte, error) { - sd := SessionDisk{} - - sd.E2EParams = s.e2eParams - sd.Type = uint8(s.t) - sd.BaseKey = s.baseKey.Bytes() - sd.MyPrivKey = s.myPrivKey.Bytes() - sd.PartnerPubKey = s.partnerPubKey.Bytes() - sd.MySIDHPrivKey = make([]byte, s.mySIDHPrivKey.Size()) - sd.PartnerSIDHPubKey = make([]byte, s.partnerSIDHPubKey.Size()) - - s.mySIDHPrivKey.Export(sd.MySIDHPrivKey) - sd.MySIDHVariant = byte(s.mySIDHPrivKey.Variant()) - - s.partnerSIDHPubKey.Export(sd.PartnerSIDHPubKey) - sd.PartnerSIDHVariant = byte(s.partnerSIDHPubKey.Variant()) - - sd.Trigger = s.partnerSource[:] - sd.RelationshipFingerprint = s.relationshipFingerprint - sd.Partner = s.partner.Bytes() - - // assume in progress confirmations and session creations have failed on - // reset, therefore do not store their pending progress - if s.negotiationStatus == Sending { - sd.Confirmation = uint8(Unconfirmed) - } else if s.negotiationStatus == NewSessionTriggered { - sd.Confirmation = uint8(Confirmed) - } else { - sd.Confirmation = uint8(s.negotiationStatus) - } - - sd.RekeyThreshold = s.rekeyThreshold - - return json.Marshal(&sd) -} - -func (s *Session) unmarshal(b []byte) error { - - sd := SessionDisk{} - - err := json.Unmarshal(b, &sd) - - if err != nil { - return err - } - - grp := s.grp - - s.e2eParams = sd.E2EParams - s.t = RelationshipType(sd.Type) - s.baseKey = grp.NewIntFromBytes(sd.BaseKey) - s.myPrivKey = grp.NewIntFromBytes(sd.MyPrivKey) - s.partnerPubKey = grp.NewIntFromBytes(sd.PartnerPubKey) - - mySIDHVariant := sidh.KeyVariant(sd.MySIDHVariant) - s.mySIDHPrivKey = utility.NewSIDHPrivateKey(mySIDHVariant) - err = s.mySIDHPrivKey.Import(sd.MySIDHPrivKey) - if err != nil { - return err - } - - partnerSIDHVariant := sidh.KeyVariant(sd.PartnerSIDHVariant) - s.partnerSIDHPubKey = utility.NewSIDHPublicKey(partnerSIDHVariant) - err = s.partnerSIDHPubKey.Import(sd.PartnerSIDHPubKey) - if err != nil { - return err - } - - s.negotiationStatus = Negotiation(sd.Confirmation) - s.rekeyThreshold = sd.RekeyThreshold - s.relationshipFingerprint = sd.RelationshipFingerprint - s.partner, _ = id.Unmarshal(sd.Partner) - copy(s.partnerSource[:], sd.Trigger) - - s.keyState, err = utility.LoadStateVector(s.kv, "") - if err != nil { - return err - } - - return nil -} - //key usage -// Popkey Pops the first unused key, skipping any which are denoted as used. +// PopKey Pops the first unused key, skipping any which are denoted as used. // will return if the remaining keys are designated as rekeys func (s *Session) PopKey() (*Cypher, error) { if s.keyState.GetNumAvailable() <= uint32(s.e2eParams.NumRekeys) { @@ -423,6 +340,8 @@ func (s *Session) PopKey() (*Cypher, error) { return newKey(s, keyNum), nil } +// PopReKey Pops the first unused key, skipping any which are denoted as used, +// including keys designated for rekeys func (s *Session) PopReKey() (*Cypher, error) { keyNum, err := s.keyState.Next() if err != nil { @@ -473,12 +392,12 @@ func (s *Session) Status() Status { var legalStateChanges = [][]bool{ // Unconf Sending Sent Confi NewTrig NewCreat - {false, false, false, false, false, false}, // Unc - {true, false, true, true, false, false}, // Sending - {false, false, false, true, false, false}, // Sent - {false, false, false, false, true, false}, // Confi - {false, false, false, true, false, true}, // NewTrig - {false, false, true, false, false, false}, // NewCreat + {false, true, false, false, false, false}, // Unc + {true, false, true, true, false, false}, // Sending + {false, false, false, true, false, false}, // Sent + {false, false, false, false, true, false}, // Confi + {false, false, false, true, false, true}, // NewTrig + {false, false, true, false, false, false}, // NewCreat } // todo - doscstring @@ -561,7 +480,8 @@ func (s *Session) TriggerNegotiation() bool { s.mux.Unlock() return false } - } else if s.negotiationStatus == Unconfirmed && decideIfResendRekey(s.rng, 1/10) { + } else if s.negotiationStatus == Unconfirmed && decideIfResendRekey(s.rng, + s.e2eParams.UnconfirmedRetryRatio) { // retrigger this sessions negotiation s.mux.RUnlock() s.mux.Lock() @@ -692,7 +612,90 @@ func (s *Session) getUnusedKeys() []*Cypher { return keys } -//builds the +//ekv functions +func (s *Session) marshal() ([]byte, error) { + sd := SessionDisk{} + + sd.E2EParams = s.e2eParams + sd.Type = uint8(s.t) + sd.BaseKey = s.baseKey.Bytes() + sd.MyPrivKey = s.myPrivKey.Bytes() + sd.PartnerPubKey = s.partnerPubKey.Bytes() + sd.MySIDHPrivKey = make([]byte, s.mySIDHPrivKey.Size()) + sd.PartnerSIDHPubKey = make([]byte, s.partnerSIDHPubKey.Size()) + + s.mySIDHPrivKey.Export(sd.MySIDHPrivKey) + sd.MySIDHVariant = byte(s.mySIDHPrivKey.Variant()) + + s.partnerSIDHPubKey.Export(sd.PartnerSIDHPubKey) + sd.PartnerSIDHVariant = byte(s.partnerSIDHPubKey.Variant()) + + sd.Trigger = s.partnerSource[:] + sd.RelationshipFingerprint = s.relationshipFingerprint + sd.Partner = s.partner.Bytes() + + // assume in progress confirmations and session creations have failed on + // reset, therefore do not store their pending progress + if s.negotiationStatus == Sending { + sd.Confirmation = uint8(Unconfirmed) + } else if s.negotiationStatus == NewSessionTriggered { + sd.Confirmation = uint8(Confirmed) + } else { + sd.Confirmation = uint8(s.negotiationStatus) + } + + sd.RekeyThreshold = s.rekeyThreshold + + return json.Marshal(&sd) +} + +func (s *Session) unmarshal(b []byte) error { + + sd := SessionDisk{} + + err := json.Unmarshal(b, &sd) + + if err != nil { + return err + } + + grp := s.grp + + s.e2eParams = sd.E2EParams + s.t = RelationshipType(sd.Type) + s.baseKey = grp.NewIntFromBytes(sd.BaseKey) + s.myPrivKey = grp.NewIntFromBytes(sd.MyPrivKey) + s.partnerPubKey = grp.NewIntFromBytes(sd.PartnerPubKey) + + mySIDHVariant := sidh.KeyVariant(sd.MySIDHVariant) + s.mySIDHPrivKey = utility.NewSIDHPrivateKey(mySIDHVariant) + err = s.mySIDHPrivKey.Import(sd.MySIDHPrivKey) + if err != nil { + return err + } + + partnerSIDHVariant := sidh.KeyVariant(sd.PartnerSIDHVariant) + s.partnerSIDHPubKey = utility.NewSIDHPublicKey(partnerSIDHVariant) + err = s.partnerSIDHPubKey.Import(sd.PartnerSIDHPubKey) + if err != nil { + return err + } + + s.negotiationStatus = Negotiation(sd.Confirmation) + s.rekeyThreshold = sd.RekeyThreshold + s.relationshipFingerprint = sd.RelationshipFingerprint + s.partner, _ = id.Unmarshal(sd.Partner) + copy(s.partnerSource[:], sd.Trigger) + + s.keyState, err = utility.LoadStateVector(s.kv, "") + if err != nil { + return err + } + + return nil +} + +// MakeSessionPrefix builds the prefix func MakeSessionPrefix(sid SessionID) string { return fmt.Sprintf(sessionPrefix, sid) } diff --git a/e2e/rekey/rekey.go b/e2e/rekey/rekey.go index bdac760b9d61f82fc7d29a6bef0c9f4ef0e3d4d9..9892d0047b126bc908dcfbec91f68031080f9b7d 100644 --- a/e2e/rekey/rekey.go +++ b/e2e/rekey/rekey.go @@ -29,7 +29,11 @@ import ( func CheckKeyExchanges(instance *commsNetwork.Instance, grp *cyclic.Group, sendE2E E2eSender, events event.Manager, manager *partner.Manager, sendTimeout time.Duration) { + + //get all sessions that may need a key exchange sessions := manager.TriggerNegotiations() + + //start an exchange for every session that needs one for _, sess := range sessions { go trigger(instance, grp, sendE2E, events, manager, sess, sendTimeout) @@ -41,38 +45,50 @@ func CheckKeyExchanges(instance *commsNetwork.Instance, grp *cyclic.Group, // session. They run the same negotiation, the former does it on a newly created // session while the latter on an extant session func trigger(instance *commsNetwork.Instance, grp *cyclic.Group, sendE2E E2eSender, - events event.Manager, manager *partner.Manager, sess *session.Session, + events event.Manager, manager *partner.Manager, inputSession *session.Session, sendTimeout time.Duration) { var negotiatingSession *session.Session jww.INFO.Printf("[REKEY] Negotiation triggered for session %s with "+ - "status: %s", sess, sess.NegotiationStatus()) - switch sess.NegotiationStatus() { + "status: %s", inputSession, inputSession.NegotiationStatus()) + + switch inputSession.NegotiationStatus() { // If the passed session is triggering a negotiation on a new session to // replace itself, then create the session case session.NewSessionTriggered: + //todo: check if any sessions have inputSession as a parent. If so, + //skip creation, set its status to newSession created, and bail + //this state could only happen if a crash occurred in a previous run + //between NewSendSession creation and the setting of the negotiation + //status on the input session + //create the session, pass a nil private key to generate a new one negotiatingSession = manager.NewSendSession(nil, nil, - session.GetDefaultE2ESessionParams()) + session.GetDefaultE2ESessionParams(), inputSession) + //move the state of the triggering session forward - sess.SetNegotiationStatus(session.NewSessionCreated) + inputSession.SetNegotiationStatus(session.NewSessionCreated) // If the session is set to send a negotiation case session.Sending: - negotiatingSession = sess + negotiatingSession = inputSession + + // should be unreachable, manager.TriggerNegotiations above should limit + // states for this switch default: jww.FATAL.Panicf("[REKEY] Session %s provided invalid e2e "+ - "negotiating status: %s", sess, sess.NegotiationStatus()) + "negotiating status: %s", inputSession, inputSession.NegotiationStatus()) } // send the rekey notification to the partner err := negotiate(instance, grp, sendE2E, negotiatingSession, sendTimeout) + // if sending the negotiation fails, revert the state of the session to // unconfirmed so it will be triggered in the future if err != nil { jww.ERROR.Printf("[REKEY] Failed to do Key Negotiation with "+ - "session %s: %s", sess, err) + "session %s: %s", inputSession, err) events.Report(1, "Rekey", "NegotiationFailed", err.Error()) } } @@ -136,7 +152,7 @@ func negotiate(instance *commsNetwork.Instance, grp *cyclic.Group, sendE2E E2eSe // transmit, the partner cannot read the result. Log the error and set // the session as unconfirmed so it will re-trigger the negotiation if !success { - sess.SetNegotiationStatus(session.Unconfirmed) + _ = sess.TrySetNegotiationStatus(session.Unconfirmed) return errors.Errorf("[REKEY] Key Negotiation rekey for %s failed to "+ "transmit %v/%v paritions: %v round failures, %v timeouts, msgID: %s", sess, numRoundFail+numTimeOut, len(rounds), numRoundFail,