diff --git a/Makefile b/Makefile index 9c16bd45cb615fabfce834ad0a8e2cdf5518218d..a11b087948ddc0b6a076a585e7eaa61f2e19f16d 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ build: update_release: GOFLAGS="" go get -u gitlab.com/elixxir/primitives@release - GOFLAGS="" go get -u gitlab.com/elixxir/crypto@release + GOFLAGS="" go get -u gitlab.com/elixxir/crypto@ben/newE2E GOFLAGS="" go get -u gitlab.com/elixxir/comms@release GOFLAGS="" go get -u gitlab.com/xx_network/comms@release GOFLAGS="" go get -u gitlab.com/xx_network/primitives@release diff --git a/go.mod b/go.mod index dd4027a2473bb8aef8db64504fa537c4d9fc5c4b..ecae5105ff729de19564b50d63594052c7318bbe 100644 --- a/go.mod +++ b/go.mod @@ -15,12 +15,13 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.6.2 - gitlab.com/elixxir/comms v0.0.0-20200805174832-240bba97beaa - gitlab.com/elixxir/crypto v0.0.0-20200805174804-bdf909f2a16d + gitlab.com/elixxir/comms v0.0.0-20200813225502-e879259ca741 + gitlab.com/elixxir/crypto v0.0.0-20200819000020-020937ea25db gitlab.com/elixxir/ekv v0.1.1 - gitlab.com/elixxir/primitives v0.0.0-20200805174810-86b366d1dd2d - gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023 - gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da + gitlab.com/elixxir/primitives v0.0.0-20200812191102-31c01f08b4dc + gitlab.com/xx_network/comms v0.0.0-20200818182121-732dd75b1947 + gitlab.com/xx_network/primitives v0.0.0-20200812183720-516a65a4a9b2 + gitlab.com/xx_network/ring v0.0.2 golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de golang.org/x/sys v0.0.0-20200806125547-5acd03effb82 // indirect gopkg.in/ini.v1 v1.52.0 // indirect diff --git a/go.sum b/go.sum index 6daefd75f3b24c3ed997c6c6a0ac1ae451d6a170..3e57ab4c7033015540645942bd55b105a71aa0ef 100644 --- a/go.sum +++ b/go.sum @@ -163,12 +163,19 @@ github.com/zeebo/blake3 v0.0.4/go.mod h1:YOZo8A49yNqM0X/Y+JmDUZshJWLt1laHsNSn5ny github.com/zeebo/pcg v0.0.0-20181207190024-3cdc6b625a05/go.mod h1:Gr+78ptB0MwXxm//LBaEvBiaXY7hXJ6KGe2V32X2F6E= gitlab.com/elixxir/comms v0.0.0-20200805174832-240bba97beaa h1:yn5FW/zPPKb0DYbN1HvhudYkCrXhpBK4CrZGeUKCGu4= gitlab.com/elixxir/comms v0.0.0-20200805174832-240bba97beaa/go.mod h1:Wc6fZyP/M4sBjnzb9pRScLeqwMOCv6DRXoTOd07bO3g= +gitlab.com/elixxir/comms v0.0.0-20200813225502-e879259ca741 h1:yIjgre8xSDpnhJkDzTr1lgR7NC1bPWCk2Sgn8udiS2A= +gitlab.com/elixxir/comms v0.0.0-20200813225502-e879259ca741/go.mod h1:hEi6dhcR1v6TGcp3tBy+QFuE25zux206xymuB+PpUqs= gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4 h1:28ftZDeYEko7xptCZzeFWS1Iam95dj46TWFVVlKmw6A= gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c= gitlab.com/elixxir/crypto v0.0.0-20200805174804-bdf909f2a16d h1:3+o6r8a0o9/HIpBzlGCCiwuPN8OdEX3cHzdnCNqKDAw= gitlab.com/elixxir/crypto v0.0.0-20200805174804-bdf909f2a16d/go.mod h1:cu6uNoANVLV0J6HyTL6KqVtVyh9SHU1RjJhytYlsbVQ= +gitlab.com/elixxir/crypto v0.0.0-20200806211835-b8ce4472f399/go.mod h1:LthCESQ1AfV1H26URYL9kr+XgXXCE7JfEEPpomFPxIo= +gitlab.com/elixxir/crypto v0.0.0-20200811195343-de268a55c7c4/go.mod h1:LthCESQ1AfV1H26URYL9kr+XgXXCE7JfEEPpomFPxIo= +gitlab.com/elixxir/crypto v0.0.0-20200819000020-020937ea25db h1:1TWIk/c0FOUc2uVxEodkkKlig4ZRHMODBLMoW/r2kxU= +gitlab.com/elixxir/crypto v0.0.0-20200819000020-020937ea25db/go.mod h1:SpcmFZ4baOMRxH7qo2myvYFNr8/bz9ItYzVo5Q2Ld1k= gitlab.com/elixxir/ekv v0.1.1 h1:Em3rF8sv+tNbQGXbcpYzAS2blWRAP708JGhYlkN74Kg= gitlab.com/elixxir/ekv v0.1.1/go.mod h1:bXY0kgbV5BHYda4YY5/hiG5bjimGK+R3PYub5yM9C/s= +gitlab.com/elixxir/primitives v0.0.0-20200608222556-1d2c04e59346/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d h1:OKWTmYN5q8XVHo8JXThIH0TCuvl/fLXR7MGVacpqfRg= gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= gitlab.com/elixxir/primitives v0.0.0-20200804170709-a1896d262cd9 h1:o0P00afLOlI3/98DR3G5IfGSTAO1ab/uzhPYzxE/Kcg= @@ -178,13 +185,23 @@ gitlab.com/elixxir/primitives v0.0.0-20200804182913-788f47bded40/go.mod h1:tzdFF gitlab.com/elixxir/primitives v0.0.0-20200804231232-ad79a9e8f113/go.mod h1:tzdFFvb1ESmuTCOl1z6+yf6oAICDxH2NPUemVgoNLxc= gitlab.com/elixxir/primitives v0.0.0-20200805174810-86b366d1dd2d h1:ky5oz0D2EmOzk2n/A6Ugwj7S1B6rftxMJwc19sjGkz8= gitlab.com/elixxir/primitives v0.0.0-20200805174810-86b366d1dd2d/go.mod h1:tzdFFvb1ESmuTCOl1z6+yf6oAICDxH2NPUemVgoNLxc= +gitlab.com/elixxir/primitives v0.0.0-20200812191102-31c01f08b4dc h1:43innow2sbJLflB73gwS8gg1meInFXNA1LGYeeDQ6lw= +gitlab.com/elixxir/primitives v0.0.0-20200812191102-31c01f08b4dc/go.mod h1:pJx2DZk9s8vVMnLN7x0hIPngDjbNSdOP6kk3RLlRxHg= gitlab.com/xx_network/comms v0.0.0-20200804225654-09a9af23d699 h1:e9rzUjMxt/4iQ5AVXVgwANvbgxxXgWEbvApgd6P72jU= gitlab.com/xx_network/comms v0.0.0-20200804225654-09a9af23d699/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw= gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023 h1:fQPaxyuXyH3vl8qFlFDBEx8rlEzBnXBNy74K8ItFRM4= gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw= +gitlab.com/xx_network/comms v0.0.0-20200812204124-8dc2a2a1b9ca/go.mod h1:idLzPGYig57XE7xuU93OlIF9s6NgSJj7OArQvsd5DjY= +gitlab.com/xx_network/comms v0.0.0-20200818182121-732dd75b1947 h1:g0k4nP0o/6qkh09F9d/Fy7Ys93fkyZU+kK71JviLdMg= +gitlab.com/xx_network/comms v0.0.0-20200818182121-732dd75b1947/go.mod h1:idLzPGYig57XE7xuU93OlIF9s6NgSJj7OArQvsd5DjY= +gitlab.com/xx_network/crypto v0.0.0-20200806202113-978fa1984bbf/go.mod h1:i0df/q6dDCBiscgD51fMoS2U2TBrm6LcyN822JmB5Tw= +gitlab.com/xx_network/crypto v0.0.0-20200806235322-ede3c15881ce h1:gypNBUl2guESEv4MDgH+miwYqR4jPoWM8dLt2Zs5gIs= +gitlab.com/xx_network/crypto v0.0.0-20200806235322-ede3c15881ce/go.mod h1:i0df/q6dDCBiscgD51fMoS2U2TBrm6LcyN822JmB5Tw= gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA= gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da h1:CCVslUwNC7Ul7NG5nu3ThGTSVUt1TxNRX+47f5TUwnk= gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug= +gitlab.com/xx_network/primitives v0.0.0-20200812183720-516a65a4a9b2 h1:Jvv2fLk+2ULDCXiVTPI8Jlg2fBrmq2NSA+dlsN5t2Pk= +gitlab.com/xx_network/primitives v0.0.0-20200812183720-516a65a4a9b2/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug= gitlab.com/xx_network/ring v0.0.2 h1:TlPjlbFdhtJrwvRgIg4ScdngMTaynx/ByHBRZiXCoL0= gitlab.com/xx_network/ring v0.0.2/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= diff --git a/key/context.go b/key/context.go new file mode 100644 index 0000000000000000000000000000000000000000..1d7ac802a439f08a502fa4a7db4efa173b729a67 --- /dev/null +++ b/key/context.go @@ -0,0 +1,14 @@ +package key + +import ( + "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/crypto/cyclic" +) + +type context struct { + fa fingerprintAccess + + grp *cyclic.Group + + kv *storage.Session +} diff --git a/key/fingerprintAccess.go b/key/fingerprintAccess.go index b519a915000610a3846b5e17ae85e3c53bb7347b..5868760902621ae41a353e2360e83ebaa35f99cd 100644 --- a/key/fingerprintAccess.go +++ b/key/fingerprintAccess.go @@ -1,7 +1,8 @@ package key -type FingerprintAccess interface { - AddFingerprints([]*Key) error - //recieves a list of fingerprints - RemoveFingerprints([]*Key) error +type fingerprintAccess interface { + // Receives a list of fingerprints to add. Overrides on collision. + add([]*Key) + // Receives a list of fingerprints to delete. Ignores any not available Keys + remove([]*Key) } diff --git a/key/key.go b/key/key.go index 283f1912322a616a67b65b0dd74de44e8bf278ee..66395e39ac88c7209805b2223905f160c07d6d72 100644 --- a/key/key.go +++ b/key/key.go @@ -1,18 +1,19 @@ package key import ( + "github.com/pkg/errors" "gitlab.com/elixxir/client/parse" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/primitives/format" + jww "github.com/spf13/jwalterweatherman" ) type Key struct { // Links session *Session - // Key to be used - key *cyclic.Int + fp *format.Fingerprint // Designation of crypto type outer parse.CryptoType @@ -22,7 +23,14 @@ type Key struct { keyNum uint32 } -func Operate(func(s *Session, )) error +func newKey(session *Session, key *cyclic.Int, outer parse.CryptoType, keynum uint32) *Key { + return &Key{ + session: session, + key: key, + outer: outer, + keyNum: keynum, + } +} // return pointers to higher level management structures func (k *Key) GetSession() *Session { return k.session } @@ -36,10 +44,30 @@ func (k *Key) GetOuterType() parse.CryptoType { return k.outer } // Generate key fingerprint // NOTE: This function is not a getter, // it returns a new byte array on each call -func (k *Key) KeyFingerprint() format.Fingerprint { +func (k *Key) Fingerprint() format.Fingerprint { h, _ := hash.NewCMixHash() h.Write(k.key.Bytes()) fp := format.Fingerprint{} copy(fp[:], h.Sum(nil)) return fp } + +// Sets the key as used +func (k *Key) denoteUse() error { + switch k.outer { + case parse.E2E: + err := k.session.useKey(k.keyNum) + if err != nil { + return errors.WithMessage(err, "Could not use e2e key") + } + + case parse.Rekey: + err := k.session.useReKey(k.keyNum) + if err != nil { + return errors.WithMessage(err, "Could not use e2e rekey") + } + default: + jww.FATAL.Panicf("Key has invalid cryptotype: %s", k.outer) + } + return nil +} \ No newline at end of file diff --git a/key/manager.go b/key/manager.go index 2620f4d7d3b8de8cface1d590bb159f243579374..af241d19f5c74803166cbc643f05445df4318aeb 100644 --- a/key/manager.go +++ b/key/manager.go @@ -1,44 +1,142 @@ package key import ( + "github.com/pkg/errors" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/primitives/id" ) type Manager struct { - params Params + ctx *context - partner id.ID + partner *id.ID - receive *SessionBuff - send *SessionBuff + receive *sessionBuff + send *sessionBuff } -// generator -func NewManager(params Params, partnerID *id.ID, myPrivKey *cyclic.Int, partnerPubKey *cyclic.Int) (*Manager, error) { - return nil, nil +// create the manager and its first send and receive sessions +func newManager(ctx *context, partnerID *id.ID, myPrivKey *cyclic.Int, + partnerPubKey *cyclic.Int, sendParams, receiveParams SessionParams) (*Manager, error) { + m := &Manager{ + ctx: ctx, + partner: partnerID, + } + + m.send = NewSessionBuff(m, "send") + m.receive = NewSessionBuff(m, "receive") + + sendSession, err := newSession(m, myPrivKey, partnerPubKey, sendParams, true) + if err != nil { + return nil, errors.WithMessage(err, + "Failed to create the send session") + } + + err = m.send.AddSession(sendSession) + if err != nil { + return nil, errors.WithMessage(err, + "Failed to add the send session to buffer") + } + + receiveSession, err := newSession(m, myPrivKey, partnerPubKey, receiveParams, true) + if err != nil { + return nil, errors.WithMessage(err, + "Failed to create the receive session") + } + + err = m.receive.AddSession(receiveSession) + if err != nil { + return nil, errors.WithMessage(err, + "Failed to add the receive session to buffer") + } + + return m, nil } -// ekv functions -func (m *Manager) Marshal() ([]byte, error) { return nil, nil } -func (m *Manager) Unmarshal([]byte) error { return nil } +//loads a manager and all buffers and sessions from disk +func loadManager(ctx *context, partnerID *id.ID) (*Manager, error) { + m := &Manager{ + ctx: ctx, + partner: partnerID, + } + + var err error + + m.send, err = LoadSessionBuff(m, "send", partnerID) + if err != nil { + return nil, errors.WithMessage(err, + "Failed to load partner key manager due to failure to "+ + "load the send session buffer") + } + + m.receive, err = LoadSessionBuff(m, "receive", partnerID) + if err != nil { + return nil, errors.WithMessage(err, + "Failed to load partner key manager due to failure to "+ + "load the receive session buffer") + } + + return m, nil +} //gets a copy of the ID of the partner -func (m *Manager) GetPartner() *id.ID { +func (m *Manager) GetPartnerID() *id.ID { p := m.partner - return &p + return p } // creates a new receive session using the latest private key this user has sent -// and the new public key received from the partner -func (m *Manager) NewReceiveSession(partnerPubKey *cyclic.Int) {} +// and the new public key received from the partner. +func (m *Manager) NewReceiveSession(partnerPubKey *cyclic.Int, params SessionParams) error { + //find your last confirmed private key + myPrivKey := m.send.GetNewestConfirmed().GetMyPrivKey() + + //create the session + session, err := newSession(m, myPrivKey, partnerPubKey, params, true) + + if err != nil { + return err + } + + //add the session to the buffer + err = m.receive.AddSession(session) + if err != nil { + //delete the session if it failed to add to the buffer + err = session.Delete() + } + + return err +} // creates a new receive session using the latest public key received from the // partner and a mew private key for the user -func (m *Manager) NewSendSession(myPrivKey *cyclic.Int) {} +// passing in a private key is optional. a private key will be generated if +// none is passed +func (m *Manager) NewSendSession(myPrivKey *cyclic.Int, params SessionParams) error { + //find the latest public key from the other party + partnerPubKey := m.receive.GetNewestConfirmed().partnerPubKey -// gets the session buffer for message reception -func (m *Manager) GetReceiveSessionBuff() *SessionBuff { return nil } + session, err := newSession(m, myPrivKey, partnerPubKey, params, false) + if err != nil { + return err + } + + //add the session to the buffer + err = m.send.AddSession(session) + if err != nil { + //delete the session if it failed to add to the buffer + err = session.Delete() + } + + return err +} // gets the session buffer for message reception -func (m *Manager) LatestReceiveSession() *Session { return nil } +func (m *Manager) GetSendingSession() *Session { + return m.send.GetSessionForSending() +} + +// Confirms a send session is known about by the partner +func (m *Manager) Confirm(sid SessionID) error { + return m.send.Confirm(sid) +} diff --git a/key/params.go b/key/params.go index fed96b5da812ff4e8567b7d87cf2e2dc26588791..d8c62be1241dbc1a77f0f1e186983202cccd5a81 100644 --- a/key/params.go +++ b/key/params.go @@ -20,15 +20,15 @@ const ( numReKeys uint16 = 64 ) -type Params struct { +type SessionParams struct { MinKeys uint16 MaxKeys uint16 NumRekeys uint16 e2e.TTLParams } -func GetDefaultParams() Params { - return Params{ +func GetDefaultSessionParams() SessionParams { + return SessionParams{ MinKeys: minKeys, MaxKeys: maxKeys, NumRekeys: numReKeys, @@ -37,4 +37,4 @@ func GetDefaultParams() Params { MinNumKeys: threshold, }, } -} +} \ No newline at end of file diff --git a/key/session.go b/key/session.go index b8f70249715b21db678e7224b35f6d0f093d5cc1..154e9332773ccfb7f6e62a81bbd5cfeb9dbf47aa 100644 --- a/key/session.go +++ b/key/session.go @@ -1,22 +1,25 @@ package key import ( - "encoding/base64" - "gitlab.com/elixxir/client/keyStore" + "encoding/json" + "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/diffieHellman" "gitlab.com/elixxir/crypto/hash" "sync" + "time" ) -type SessionID [32]byte - -func (sid SessionID) String() string { - return base64.StdEncoding.EncodeToString(sid[:]) -} +const currentSessionVersion = 0 type Session struct { //pointer to manager manager *Manager + //params + params SessionParams + + //type + t SessionType // Underlying key baseKey *cyclic.Int @@ -29,76 +32,129 @@ type Session struct { confirmed bool // Value of the counter at which a rekey is triggered - ttl uint16 - - // Total number of Keys - numKeys uint32 - // Total number of Rekey keys - numReKeys uint16 + ttl uint32 // Received Keys dirty bits // Each bit represents a single Key - KeyState []*uint64 + keyState *stateVector // Received ReKeys dirty bits // Each bit represents a single ReKey - ReKeyState []*uint64 - - // Keys - keys []*Key - lastKey uint32 - - // ReKeys - reKeys []*Key - lastReKey uint32 + reKeyState *stateVector //mutex mux sync.RWMutex } type SessionDisk struct { + params SessionParams + // Underlying key - baseKey *cyclic.Int + BaseKey []byte // Own Private Key - myPrivKey *cyclic.Int + MyPrivKey []byte // Partner Public Key - partnerPubKey *cyclic.Int - - // Received Keys dirty bits - // Each bit represents a single Key - KeyState []*uint64 - // Received ReKeys dirty bits - // Each bit represents a single ReKey - ReKeyState []*uint64 + PartnerPubKey []byte //denotes if the other party has confirmed this key - confirmed bool - - //position of the earliest unused key - lastKey uint32 + Confirmed bool +} - //position of the earliest unused key - lastReKey uint32 +/*CONSTRUCTORS*/ +//Generator which creates all keys and structures +func newSession(manager *Manager, myPrivKey *cyclic.Int, partnerPubKey *cyclic.Int, params SessionParams, t SessionType) (*Session, error) { + session := &Session{ + params: params, + manager: manager, + t: t, + myPrivKey: myPrivKey, + partnerPubKey: partnerPubKey, + confirmed: t == Receive, + } + + err := session.generateKeys() + if err != nil { + return nil, err + } + + err = session.save() + if err != nil { + return nil, err + } + + return session, nil } //Generator which creates all keys and structures -func (s *Session) NewSession(myPrivKey *cyclic.Int, partnerPubKey *cyclic.Int, manager *Manager) (*Session, error) { - return nil, nil +func loadSession(manager *Manager, key string) (*Session, error) { + + session := Session{ + manager: manager, + } + + obj, err := manager.ctx.kv.Get(key) + if err != nil { + return nil, err + } + + err = session.unmarshal(obj.Data) + if err != nil { + return nil, err + } + + return &session, nil } -//Gets the manager used to +func (s *Session) save() error { + key := makeSessionKey(s.GetID()) + + now, err := time.Now().MarshalText() + if err != nil { + return err + } + + data, err := s.marshal() + if err != nil { + return err + } + + obj := storage.VersionedObject{ + Version: currentSessionVersion, + Timestamp: now, + Data: data, + } + + return s.manager.ctx.kv.Set(key, &obj) +} + +/*METHODS*/ +func (s *Session) Delete() error { + s.mux.Lock() + defer s.mux.Unlock() + + s.manager.ctx.fa.remove(s.keys) + s.manager.ctx.fa.remove(s.reKeys) + + return s.manager.ctx.kv.Delete(makeSessionKey(s.GetID())) +} //Gets the base key. func (s *Session) GetBaseKey() *cyclic.Int { + s.mux.RLock() + defer s.mux.RUnlock() // no lock is needed because this cannot be edited return s.baseKey.DeepCopy() } func (s *Session) GetMyPrivKey() *cyclic.Int { + s.mux.RLock() + defer s.mux.RUnlock() // no lock is needed because this cannot be edited return s.myPrivKey.DeepCopy() } func (s *Session) GetPartnerPubKey() *cyclic.Int { + s.mux.RLock() + defer s.mux.RUnlock() // no lock is needed because this cannot be edited return s.partnerPubKey.DeepCopy() } @@ -113,41 +169,138 @@ func (s *Session) GetID() SessionID { } //ekv functions -func (s *Session) Marshal() ([]byte, error) { return nil, nil } -func (s *Session) Unmarshal([]byte) error { return nil } +func (s *Session) marshal() ([]byte, error) { + sd := SessionDisk{} + + sd.params = s.params + sd.BaseKey = s.baseKey.Bytes() + sd.MyPrivKey = s.myPrivKey.Bytes() + sd.PartnerPubKey = s.partnerPubKey.Bytes() + sd.Confirmed = s.confirmed + + 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.manager.ctx.grp + + s.params = sd.params + s.baseKey = grp.NewIntFromBytes(sd.BaseKey) + s.myPrivKey = grp.NewIntFromBytes(sd.MyPrivKey) + s.partnerPubKey = grp.NewIntFromBytes(sd.PartnerPubKey) + s.confirmed = sd.Confirmed + + sid := s.GetID() + + s.keyState, err = loadStateVector(s.manager.ctx, makeStateVectorKey("keyStates", sid)) + if err != nil { + return err + } + + s.reKeyState, err = loadStateVector(s.manager.ctx, makeStateVectorKey("reKeyStates", sid)) + if err != nil { + return err + } + + return s.generateKeys() +} //key usage -// Pops the first unused key, skipping any which are denoted as used -func (s *Session) PopKey() (*keyStore.E2EKey, error) { return nil, nil } +// Pops the first unused key, skipping any which are denoted as used. The status +// is returned to check if a rekey is nessessary +func (s *Session) PopKey() (*Key, error) { + keynum, err := s.keyState.Next() + if err != nil { + return nil, err + } + + return s.keys[keynum], nil +} // Pops the first unused rekey, skipping any which are denoted as used -func (s *Session) PopReKey() (*keyStore.E2EKey, error) { return nil, nil } - -// denotes the passed key as used -func (s *Session) UseKey(*keyStore.E2EKey) error { return nil } +func (s *Session) PopReKey() (*Key, error) { + keynum, err := s.reKeyState.Next() + if err != nil { + return nil, err + } + return s.reKeys[keynum], nil +} -// denotes the passed rekey as used -func (s *Session) UseRekey(*keyStore.E2EKey) error { return nil } +// returns the state of the session, which denotes if the Session is active, +// functional but in need of a rekey, empty of send key, or empty of rekeys +func (s *Session) Status() Status { + s.mux.RLock() + defer s.mux.RUnlock() + if s.reKeyState.GetNumKeys() == 0 { + return RekeyEmpty + } else if s.keyState.GetNumKeys() == 0 { + return Empty + } else if s.keyState.GetNumKeys() >= s.ttl { + return RekeyNeeded + } else { + return Active + } +} -// returns the state of the keyblob, which denotes if the Session is active, +// returns the state of the session, which denotes if the Session is active, // functional but in need of a rekey, empty of send key, or empty of rekeys -func (s *Session) Status() Status { return 0 } +func (s *Session) IsReKeyNeeded() bool { + s.mux.RLock() + defer s.mux.RUnlock() + return s.keyState.GetNumAvailable() == s.ttl +} + +// checks if the session has been confirmed +func (s *Session) IsConfirmed() bool { + s.mux.RLock() + defer s.mux.RUnlock() + return s.confirmed +} + +/*PRIVATE*/ // Sets the confirm bool. this is set when the partner is certain to share the // session. It should be called immediately for receive keys and only on rekey // confirmation for send keys. Confirmation can only be made by the sessionBuffer // because it is used to keep track of active sessions for rekey as well -func (s *Session) confirm() { +func (s *Session) confirm() error { s.mux.Lock() defer s.mux.Unlock() s.confirmed = true + return s.save() } -// checks if the session has been confirmed -func (s *Session) IsConfirmed() bool { - s.mux.RLock() - defer s.mux.RUnlock() - return s.confirmed +func (s *Session) useKey(keynum uint32) error { + return s.keyState.Use(keynum) } -/*PRIVATE*/ +func (s *Session) useReKey(keynum uint32) error { + return s.reKeyState.Use(keynum) +} + +// generates keys from the base data stored in the session object. +// required fields: partnerPubKey, confirmed, manager, grp +// myPrivKey, baseKey, keyState, and ReKeyState will be +// created/calculated if not present +// if keyState is not present lastKey will be ignored and set to zero +// if ReKeyState is not present lastReKey will be ignored and set to zero +func (s *Session) generateKeys() error { + s.mux.Lock() + defer s.mux.Unlock() + + //if not private key is present, generate one + if s.myPrivKey == nil { + s.myPrivKey, diffieHellman + } + + return nil +} \ No newline at end of file diff --git a/key/sessionBuff.go b/key/sessionBuff.go index 6ba97f8a0b1b400771fe87ef57b8b60d34f4e380..ea6b4bc2096b69e82ea10764dc34e88e30273ffb 100644 --- a/key/sessionBuff.go +++ b/key/sessionBuff.go @@ -1,37 +1,139 @@ package key import ( + "encoding/base64" + "encoding/json" "github.com/pkg/errors" + "gitlab.com/elixxir/client/storage" + "gitlab.com/xx_network/primitives/id" "sync" + jww "github.com/spf13/jwalterweatherman" + "time" ) -type SessionBuff struct { +const maxUnconfirmed uint = 3 +const currentSessionBuffVersion = 0 + +type sessionBuff struct { + manager *Manager + sessions []*Session sessionByID map[SessionID]*Session + keyPrefix string + mux sync.RWMutex } -type SessionBuffDisk struct { - sessions []SessionID +func NewSessionBuff(manager *Manager, keyPrefix string) *sessionBuff { + return &sessionBuff{ + manager: manager, + sessions: make([]*Session, 0), + sessionByID: make(map[SessionID]*Session), + mux: sync.RWMutex{}, + keyPrefix: keyPrefix, + } +} + +func LoadSessionBuff(manager *Manager, keyPrefix string, partnerID *id.ID) (*sessionBuff, error) { + sb := &sessionBuff{ + manager: manager, + sessionByID: make(map[SessionID]*Session), + mux: sync.RWMutex{}, + } + + key := makeSessionBuffKey(keyPrefix, partnerID) + + obj, err := manager.ctx.kv.Get(key) + if err != nil { + return nil, err + } + + err = sb.unmarshal(obj.Data) + + if err != nil { + return nil, err + } + + return sb, nil } -func NewSessionBuff(n int, deletion func(session *Session)) *SessionBuff { return &SessionBuff{} } +func (sb *sessionBuff) save() error { + key := makeSessionBuffKey(sb.keyPrefix, sb.manager.partner) + + now, err := time.Now().MarshalText() + if err != nil { + return err + } + + data, err := sb.marshal() + if err != nil { + return err + } + + obj := storage.VersionedObject{ + Version: currentSessionBuffVersion, + Timestamp: now, + Data: data, + } + + return sb.manager.ctx.kv.Set(key, &obj) +} //ekv functions -func (sb *SessionBuff) Marshal() ([]byte, error) { return nil, nil } -func (sb *SessionBuff) Unmarshal([]byte) error { return nil } +func (sb *sessionBuff) marshal() ([]byte, error) { + sessions := make([]SessionID, len(sb.sessions)) + + index := 0 + for sid := range sb.sessionByID { + sessions[index] = sid + index++ + } + + return json.Marshal(&sessions) +} + +func (sb *sessionBuff) unmarshal(b []byte) error { + var sessions []SessionID + + err := json.Unmarshal(b, &sessions) -func (sb *SessionBuff) AddSession(s *Session) { + if err != nil { + return err + } + + sb.sessions = make([]*Session, len(sessions)) + + //load all the sessions + for _, sid := range sessions { + key := makeSessionKey(sid) + session, err := loadSession(sb.manager, key) + if err != nil { + jww.FATAL.Panicf("Failed to load session %s for %s: %s", + key, sb.manager.partner, err.Error()) + } + sb.addSession(session) + } + + return nil +} + +func (sb *sessionBuff) AddSession(s *Session) error { sb.mux.Lock() defer sb.mux.Unlock() + sb.addSession(s) + return sb.save() +} + +func (sb *sessionBuff) addSession(s *Session) { + sb.sessions = append([]*Session{s}, sb.sessions...) sb.sessionByID[s.GetID()] = s return } -func (sb *SessionBuff) GetNewest() *Session { +func (sb *sessionBuff) GetNewest() *Session { sb.mux.RLock() defer sb.mux.RUnlock() if len(sb.sessions) == 0 { @@ -41,7 +143,7 @@ func (sb *SessionBuff) GetNewest() *Session { } // returns the session which is most likely to be successful for sending -func (sb *SessionBuff) GetSessionForSending() *Session { +func (sb *sessionBuff) GetSessionForSending() *Session { sb.mux.RLock() defer sb.mux.RUnlock() if len(sb.sessions) == 0 { @@ -79,7 +181,7 @@ func (sb *SessionBuff) GetSessionForSending() *Session { return nil } -func (sb *SessionBuff) GetNewestConfirmed() *Session { +func (sb *sessionBuff) GetNewestConfirmed() *Session { sb.mux.RLock() defer sb.mux.RUnlock() if len(sb.sessions) == 0 { @@ -97,7 +199,7 @@ func (sb *SessionBuff) GetNewestConfirmed() *Session { return nil } -func (sb *SessionBuff) GetByID(id SessionID) *Session { +func (sb *sessionBuff) GetByID(id SessionID) *Session { sb.mux.RLock() defer sb.mux.RUnlock() return sb.sessionByID[id] @@ -106,7 +208,7 @@ func (sb *SessionBuff) GetByID(id SessionID) *Session { // sets the passed session ID as confirmed. Call "GetSessionRotation" after // to get any sessions that are to be deleted and then "DeleteSession" to // remove them -func (sb *SessionBuff) Confirm(id SessionID) error { +func (sb *sessionBuff) Confirm(id SessionID) error { sb.mux.Lock() defer sb.mux.Unlock() s, ok := sb.sessionByID[id] @@ -114,16 +216,44 @@ func (sb *SessionBuff) Confirm(id SessionID) error { return errors.Errorf("Could not confirm session %s, does not exist", s.GetID()) } - s.confirm() - return nil -} + err := s.confirm() + if err != nil { + jww.FATAL.Panicf("Failed to confirm session "+ + "%s for %s: %s", s.GetID(), sb.manager.partner, err.Error()) + } -func (sb *SessionBuff) Clean() error { -} + return sb.clean() } -//find the sessions position in the session buffer -loc := -1 -for i, sBuf := range sb.sessions{ -if sBuf==s{ -loc = i + +func (sb *sessionBuff) clean() error { + + numConfirmed := uint(0) + + var newSessions []*Session + + for _, s := range sb.sessions { + if s.IsConfirmed() { + numConfirmed++ + //if the number of newer confirmed is sufficient, delete the confirmed + if numConfirmed > maxUnconfirmed { + delete(sb.sessionByID, s.GetID()) + err := s.Delete() + if err != nil { + jww.FATAL.Panicf("Failed to delete session store "+ + "%s for %s: %s", s.GetID(), sb.manager.partner, err.Error()) + } + + break + } + } + newSessions = append(newSessions, s) + } + + sb.sessions = newSessions + + return sb.save() } + +func makeSessionBuffKey(keyPrefix string, partnerID *id.ID) string { + return keyPrefix + "sessionBuffer" + base64.StdEncoding.EncodeToString(partnerID.Marshal()) +} \ No newline at end of file diff --git a/key/sessionID.go b/key/sessionID.go new file mode 100644 index 0000000000000000000000000000000000000000..4ac8edfb78bfd1a80247571a8a7d76c9a49f9176 --- /dev/null +++ b/key/sessionID.go @@ -0,0 +1,18 @@ +package key + +import "encoding/base64" + +type SessionID [32]byte + +func (sid SessionID) Bytes() []byte { + return sid[:] +} + +func (sid SessionID) String() string { + return base64.StdEncoding.EncodeToString(sid[:]) +} + +//builds the +func makeSessionKey(sid SessionID) string { + return sid.String() +} diff --git a/key/sessionType.go b/key/sessionType.go new file mode 100644 index 0000000000000000000000000000000000000000..163509b30297e80e558bf1c6b229581558d99ed0 --- /dev/null +++ b/key/sessionType.go @@ -0,0 +1,19 @@ +package key + +type SessionType uint8 + +const ( + Send SessionType = iota + Receive +) + +func (st SessionType) String() string { + switch st { + case Send: + return "Send" + case Receive: + return "Receive" + default: + return "Unknown" + } +} diff --git a/key/sessionType_test.go b/key/sessionType_test.go new file mode 100644 index 0000000000000000000000000000000000000000..109376ac2983b1dd6886b52e4273ce40977bb5eb --- /dev/null +++ b/key/sessionType_test.go @@ -0,0 +1,28 @@ +package key + +import ( + "math" + "testing" +) + +//tests the stringers for all possible sessions types are correct +func TestSessionType_String(t *testing.T) { + for i := 0; i <= math.MaxUint8; i++ { + st := SessionType(i) + if st.String() != correctString(i) { + t.Errorf("Session Name for %v incorrect. Expected: %s, "+ + "Received: %s", i, correctString(i), st.String()) + } + } +} + +func correctString(i int) string { + switch i { + case 0: + return "Send" + case 1: + return "Receive" + default: + return "Unknown" + } +} diff --git a/key/stateVector.go b/key/stateVector.go new file mode 100644 index 0000000000000000000000000000000000000000..2dbe610794989f18968a7b4acdb967985a771477 --- /dev/null +++ b/key/stateVector.go @@ -0,0 +1,188 @@ +package key + +import ( + "encoding/json" + "github.com/pkg/errors" + "gitlab.com/elixxir/client/storage" + "sync" + "time" +) + +type stateVector struct { + ctx *context + key string + + vect []uint64 + + firstAvailable uint32 + numkeys uint32 + numAvalible uint32 + + mux sync.RWMutex +} + +type stateVectorDisk struct { + vect []uint64 + firstAvailable uint32 + numkeys uint32 +} + +func newStateVector(ctx *context, key string, numkeys uint32) *stateVector { + numBlocks := (numkeys + 63) / 64 + + sv := &stateVector{ + ctx: ctx, + vect: make([]uint64, numBlocks), + key: key, + firstAvailable: 0, + numAvalible: numkeys, + numkeys: numkeys, + } + + return sv +} + +func loadStateVector(ctx *context, key string) (*stateVector, error) { + sv := &stateVector{ + ctx: ctx, + key: key, + } + + obj, err := ctx.kv.Get(key) + if err != nil { + return nil, err + } + + err = sv.unmarshal(obj.Data) + if err != nil { + return nil, err + } + + return sv, nil +} + +func (sv *stateVector) save() error { + now, err := time.Now().MarshalText() + if err != nil { + return err + } + + data, err := sv.marshal() + if err != nil { + return err + } + + obj := storage.VersionedObject{ + Version: currentSessionVersion, + Timestamp: now, + Data: data, + } + + return sv.ctx.kv.Set(sv.key, &obj) +} + +func (sv *stateVector) Use(keynum uint32) error { + sv.mux.Lock() + defer sv.mux.Unlock() + + block := keynum / 64 + pos := keynum % 64 + + sv.vect[block] |= 1 << pos + + if keynum == sv.firstAvailable { + sv.nextAvailable() + } + + sv.numAvalible-- + + return sv.save() +} + +func (sv *stateVector) GetNumAvailable() uint32 { + sv.mux.RLock() + defer sv.mux.RUnlock() + return sv.numAvalible +} + +func (sv *stateVector) Used(keynum uint32) bool { + sv.mux.RLock() + defer sv.mux.RUnlock() + + block := keynum / 64 + pos := keynum % 64 + + sv.vect[block] |= 1 << pos + + return (sv.vect[block]>>pos)&1 == 1 +} + +func (sv *stateVector) Next() (uint32, error) { + sv.mux.Lock() + defer sv.mux.Lock() + + if sv.firstAvailable >= sv.numkeys { + return sv.numkeys, errors.New("No keys remaining") + } + + next := sv.firstAvailable + + sv.nextAvailable() + sv.numAvalible-- + + return next, sv.save() + +} + +func (sv *stateVector) GetNumKeys() uint32 { + return sv.numkeys +} + +// finds the next used state and sets that as firstAvailable. This does not +// execute a store and a store must be executed after. +func (sv *stateVector) nextAvailable() { + + block := (sv.firstAvailable + 1) / 64 + pos := (sv.firstAvailable + 1) % 64 + + for ; block < uint32(len(sv.vect)) && sv.vect[block]>>pos&1 == 1; pos++ { + if pos == 64 { + pos = 0 + block++ + } + } + + sv.firstAvailable = pos +} + +//ekv functions +func (sv *stateVector) marshal() ([]byte, error) { + svd := stateVectorDisk{} + + svd.firstAvailable = sv.firstAvailable + svd.numkeys = sv.numkeys + svd.vect = sv.vect + + return json.Marshal(&svd) +} + +func (sv *stateVector) unmarshal(b []byte) error { + + svd := stateVectorDisk{} + + err := json.Unmarshal(b, &svd) + + if err != nil { + return err + } + + sv.firstAvailable = svd.firstAvailable + sv.numkeys = svd.numkeys + sv.vect = svd.vect + + return nil +} + +func makeStateVectorKey(prefix string, sid SessionID) string { + return sid.String() + prefix +} diff --git a/key/store.go b/key/store.go index 4aeb5ef450d1e0cd2d30a376ae9a9257aab7df46..fdef832dbfbfb76d372700bfb3c86e5b00a36000 100644 --- a/key/store.go +++ b/key/store.go @@ -1,22 +1,195 @@ package key import ( + "encoding/json" + "github.com/pkg/errors" + "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" + "sync" + jww "github.com/spf13/jwalterweatherman" + "time" ) +const storeKey = "keyStore" +const currentStoreVersion = 0 + type Store struct { - managers map[id.ID]*Manager - fingerprintToKey map[format.Fingerprint]*Key + managers map[id.ID]*Manager + mux sync.RWMutex + + fingerprints + + context +} + +func NewStore(grp *cyclic.Group, kv *storage.Session) *Store { + fingerprints := newFingerprints() + return &Store{ + managers: make(map[id.ID]*Manager), + fingerprints: fingerprints, + context: context{ + fa: &fingerprints, + grp: grp, + kv: kv, + }, + } + +} + +func LoadStore(grp *cyclic.Group, kv *storage.Session) (*Store, error) { + s := NewStore(grp, kv) + + obj, err := kv.Get(storeKey) + if err != nil { + return nil, err + } + + err = s.unmarshal(obj.Data) + + if err != nil { + return nil, err + } + + return s, nil +} + +func (s *Store) save() error { + now, err := time.Now().MarshalText() + if err != nil { + return err + } + + data, err := s.marshal() + if err != nil { + return err + } + + obj := storage.VersionedObject{ + Version: currentStoreVersion, + Timestamp: now, + Data: data, + } + + return s.kv.Set(storeKey, &obj) +} + +func (s *Store) AddPartner(partnerID *id.ID, myPrivKey *cyclic.Int, + partnerPubKey *cyclic.Int, sendParams, receiveParams SessionParams) error { + s.mux.Lock() + defer s.mux.Unlock() + + m, err := newManager(&s.context, partnerID, myPrivKey, partnerPubKey, sendParams, receiveParams) + + if err != nil { + return err + } + + s.managers[*partnerID] = m + + return s.save() +} + +func (s *Store) GetPartner(partnerID *id.ID) (*Manager, error) { + s.mux.RLock() + defer s.mux.RUnlock() + + m, ok := s.managers[*partnerID] + + if !ok { + return nil, errors.New("Cound not find manager for partner") + } + + return m, nil +} + +//ekv functions +func (s *Store) marshal() ([]byte, error) { + + contacts := make([]id.ID, len(s.managers)) + + index := 0 + for partnerID := range s.managers { + contacts[index] = partnerID + } + + return json.Marshal(&contacts) +} + +func (s *Store) unmarshal(b []byte) error { + + var contacts []id.ID + + err := json.Unmarshal(b, &contacts) + + if err != nil { + return err + } + + for _, partnerID := range contacts { + // load the manager. Manager handles adding the fingerprints via the + // context object + manager, err := loadManager(&s.context, &partnerID) + if err != nil { + jww.FATAL.Panicf("Failed to load manager for partner %s: %s", &partnerID, err.Error()) + } + + s.managers[partnerID] = manager + } + return nil } -type StoreDisk struct { - contacts []id.ID +type fingerprints struct { + toKey map[format.Fingerprint]*Key + mux sync.RWMutex } -func (s *Store) CleanManager(partner id.ID) error { - //lookup +func newFingerprints() fingerprints { + return fingerprints{ + toKey: make(map[format.Fingerprint]*Key), + } +} - //get sessions to be removed +//fingerprint adhere to the fingerprintAccess interface +func (f *fingerprints) add(keys []*Key) { + f.mux.Lock() + defer f.mux.Unlock() + for _, k := range keys { + f.toKey[k.Fingerprint()] = k + } } + +func (f *fingerprints) remove(keys []*Key) { + f.mux.Lock() + defer f.mux.Unlock() + + for _, k := range keys { + delete(f.toKey, k.Fingerprint()) + } +} + +func (f *fingerprints) Pop(fingerprint format.Fingerprint) (*Key, error) { + f.mux.Lock() + defer f.mux.Unlock() + + key, ok := f.toKey[fingerprint] + + if !ok { + return nil, errors.New("Key could not be found") + } + + delete(f.toKey, fingerprint) + + err := key.denoteUse() + + if err != nil { + return nil, err + } + + key.fp = &fingerprint + + return key, nil +} + diff --git a/storage/session.go b/storage/session.go index 95777d7c26cc2e896a7403fe45c91ce5e1dfbcfc..b521cdf85c6754509d480bfae69d0c8375dbbbfd 100644 --- a/storage/session.go +++ b/storage/session.go @@ -50,6 +50,11 @@ func (s *Session) Set(key string, object *VersionedObject) error { return s.kv.Set(key, object) } +// Deletes a value in the session +func (s *Session) Delete(key string) error { + return s.kv.Delete(key) +} + // Obtain the LastMessageID from the Session func (s *Session) GetLastMessageId() (string, error) { v, err := s.Get("LastMessageID")