diff --git a/e2e/fpGenerator.go b/e2e/fpGenerator.go index 2e42f9af79bd186885392d2333f7c69cd461368d..19d7958925aa60cb198e21f5d65d945e3f2a593f 100644 --- a/e2e/fpGenerator.go +++ b/e2e/fpGenerator.go @@ -13,7 +13,7 @@ type fpGenerator struct { } func (fp *fpGenerator) AddKey(k *session.Cypher) { - err := fp.net.AddFingerprint(fp.myID, k.Fingerprint(), &processor{ + err := fp.net.AddFingerprint(fp.myDefaultID, k.Fingerprint(), &processor{ cy: k, m: fp.manager, }) @@ -24,5 +24,5 @@ func (fp *fpGenerator) AddKey(k *session.Cypher) { } func (fp *fpGenerator) DeleteKey(k *session.Cypher) { - fp.net.DeleteFingerprint(fp.myID, k.Fingerprint()) + fp.net.DeleteFingerprint(fp.myDefaultID, k.Fingerprint()) } diff --git a/e2e/interface.go b/e2e/interface.go index d9990b4d472201c11bdad2a07deac38f303ca5fd..84e10dcfd510227cf67adbf9a56de4930012596d 100644 --- a/e2e/interface.go +++ b/e2e/interface.go @@ -94,19 +94,28 @@ type Handler interface { // AddPartner adds a partner. Automatically creates both send and receive // sessions using the passed cryptographic data and per the parameters sent - AddPartner(partnerID *id.ID, partnerPubKey, myPrivKey *cyclic.Int, - partnerSIDHPubKey *sidh.PublicKey, mySIDHPrivKey *sidh.PrivateKey, - sendParams, receiveParams session.Params, myID *id.ID, myPrivateKey *cyclic.Int, temporary bool) (*partner.Manager, error) + // If an alternate ID public key are to be used for this relationship, + // then pass them in, otherwise, leave myID and myPrivateKey nil + // If temporary is true, an alternate ram kv will be used for storage and + // the relationship will not survive a reset + AddPartner(myID *id.ID, myPrivateKey *cyclic.Int, partnerID *id.ID, + partnerPubKey, myPrivKey *cyclic.Int, partnerSIDHPubKey *sidh.PublicKey, + mySIDHPrivKey *sidh.PrivateKey, sendParams, + receiveParams session.Params, temporary bool) (*partner.Manager, error) // GetPartner returns the partner per its ID, if it exists - GetPartner(partnerID *id.ID) (*partner.Manager, error) + // myID is your ID in the relationship, if left blank, it will + // assume to be your defaultID + GetPartner(partnerID *id.ID, myID *id.ID) (*partner.Manager, error) // DeletePartner removes the associated contact from the E2E store - DeletePartner(partnerId *id.ID) error + // myID is your ID in the relationship, if left blank, it will + // assume to be your defaultID + DeletePartner(partnerId *id.ID, myID *id.ID) error // GetAllPartnerIDs returns a list of all partner IDs that the user has // an E2E relationship with. - GetAllPartnerIDs() []*id.ID + GetAllPartnerIDs(myID *id.ID) []*id.ID /* === Services ========================================================= */ diff --git a/e2e/manager.go b/e2e/manager.go index c47a18644f0fb135177fdfbcfed9278744c4941c..1a35f15746b91088579b90936eec88c65cbc6d14 100644 --- a/e2e/manager.go +++ b/e2e/manager.go @@ -23,20 +23,24 @@ type manager struct { *receive.Switchboard partitioner parse.Partitioner net network.Manager - myID *id.ID + myDefaultID *id.ID rng *fastRNG.StreamGenerator events event.Manager grp *cyclic.Group crit *critical } -//Init Creates stores. After calling, use load -func Init(kv *versioned.KV, myID *id.ID, privKey *cyclic.Int, grp *cyclic.Group) error { - return ratchet.New(kv, myID, privKey, grp) +// Init Creates stores. After calling, use load +// Passes a default ID and public key which is used for relationship with +// partners when no default ID is selected +func Init(kv *versioned.KV, myDefaultID *id.ID, privKey *cyclic.Int, grp *cyclic.Group) error { + return ratchet.New(kv, myDefaultID, privKey, grp) } // Load returns an e2e manager from storage -func Load(kv *versioned.KV, net network.Manager, myID *id.ID, +// Passes a default ID which is used for relationship with +// partners when no default ID is selected +func Load(kv *versioned.KV, net network.Manager, myDefaultID *id.ID, grp *cyclic.Group, rng *fastRNG.StreamGenerator, events event.Manager) (Handler, error) { //build the manager @@ -44,14 +48,14 @@ func Load(kv *versioned.KV, net network.Manager, myID *id.ID, Switchboard: receive.New(), partitioner: parse.NewPartitioner(kv, net.GetMaxMessageLength()), net: net, - myID: myID, + myDefaultID: myDefaultID, events: events, grp: grp, } var err error //load the ratchets - m.Ratchet, err = ratchet.Load(kv, myID, grp, + m.Ratchet, err = ratchet.Load(kv, myDefaultID, grp, &fpGenerator{m}, net, rng) if err != nil { return nil, err @@ -92,15 +96,15 @@ func (m *manager) StartProcesses() (stoppable.Stoppable, error) { // EnableUnsafeReception enables the reception of unsafe message by registering // bespoke services for reception. For debugging only! func (m *manager) EnableUnsafeReception() { - m.net.AddService(m.myID, message.Service{ - Identifier: m.myID[:], + m.net.AddService(m.myDefaultID, message.Service{ + Identifier: m.myDefaultID[:], Tag: ratchet.Silent, }, &UnsafeProcessor{ m: m, tag: ratchet.Silent, }) - m.net.AddService(m.myID, message.Service{ - Identifier: m.myID[:], + m.net.AddService(m.myDefaultID, message.Service{ + Identifier: m.myDefaultID[:], Tag: ratchet.E2e, }, &UnsafeProcessor{ m: m, diff --git a/e2e/ratchet/partner/manager.go b/e2e/ratchet/partner/manager.go index 3ff4befee5f95b491bf07b3aa149308a6fa19cef..7b04009ee06c005af6b77184c8d326c8d88fc675 100644 --- a/e2e/ratchet/partner/manager.go +++ b/e2e/ratchet/partner/manager.go @@ -216,6 +216,11 @@ func (m *Manager) GetPartnerID() *id.ID { return m.partner.DeepCopy() } +// GetMyID returns a copy of the ID used as self. +func (m *Manager) GetMyID() *id.ID { + return m.myID.DeepCopy() +} + // GetSendSession gets the Send session of the passed ID. Returns nil if no // session is found. func (m *Manager) GetSendSession(sid session.SessionID) *session.Session { @@ -245,6 +250,11 @@ func (m *Manager) TriggerNegotiations() []*session.Session { return m.send.TriggerNegotiation() } +// IsTemporary returns if this manager stores to disk or only in ram +func (m *Manager) IsTemporary() bool { + return m.kv.IsMemStore() +} + func (m *Manager) GetMyOriginPrivateKey() *cyclic.Int { return m.originMyPrivKey.DeepCopy() } diff --git a/e2e/ratchet/store.go b/e2e/ratchet/ratchet.go similarity index 50% rename from e2e/ratchet/store.go rename to e2e/ratchet/ratchet.go index f25e699794ed6d68af847133160c30275cc0d420..a84b1e5d5f993db01f987560acb51c0d2c386de1 100644 --- a/e2e/ratchet/store.go +++ b/e2e/ratchet/ratchet.go @@ -8,7 +8,6 @@ package ratchet import ( - "encoding/json" "github.com/cloudflare/circl/dh/sidh" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" @@ -21,27 +20,24 @@ import ( "gitlab.com/elixxir/crypto/diffieHellman" "gitlab.com/elixxir/crypto/fastRNG" "gitlab.com/xx_network/primitives/id" - "gitlab.com/xx_network/primitives/netTime" "sync" ) const ( - currentStoreVersion = 0 - packagePrefix = "e2eSession" - storeKey = "Ratchet" - pubKeyKey = "DhPubKey" - privKeyKey = "DhPrivKey" + packagePrefix = "e2eSession" + pubKeyKey = "DhPubKey" + privKeyKey = "DhPrivKey" ) var NoPartnerErrorStr = "No relationship with partner found" type Ratchet struct { - managers map[id.ID]*partner.Manager + managers map[relationshipIdentity]*partner.Manager mux sync.RWMutex - myID *id.ID - dhPrivateKey *cyclic.Int - dhPublicKey *cyclic.Int + defaultID *id.ID + defaultDHPrivateKey *cyclic.Int + defaultDHPublicKey *cyclic.Int grp *cyclic.Group cyHandler session.CypherHandler @@ -52,7 +48,8 @@ type Ratchet struct { sInteface Services servicesmux sync.RWMutex - kv *versioned.KV + kv *versioned.KV + memKv *versioned.KV } // New creates a new store for the passed user id and private key. @@ -69,12 +66,12 @@ func New(kv *versioned.KV, myID *id.ID, privKey *cyclic.Int, kv = kv.Prefix(packagePrefix) r := &Ratchet{ - managers: make(map[id.ID]*partner.Manager), + managers: make(map[relationshipIdentity]*partner.Manager), services: make(map[string]message.Processor), - myID: myID, - dhPrivateKey: privKey, - dhPublicKey: pubKey, + defaultID: myID, + defaultDHPrivateKey: privKey, + defaultDHPublicKey: pubKey, kv: kv, @@ -96,90 +93,46 @@ func New(kv *versioned.KV, myID *id.ID, privKey *cyclic.Int, return r.save() } -// Load loads an extant ratchet from disk -func Load(kv *versioned.KV, myID *id.ID, grp *cyclic.Group, - cyHandler session.CypherHandler, services Services, rng *fastRNG.StreamGenerator) ( - *Ratchet, error) { - kv = kv.Prefix(packagePrefix) - - r := &Ratchet{ - managers: make(map[id.ID]*partner.Manager), - services: make(map[string]message.Processor), - - myID: myID, - - kv: kv, - - cyHandler: cyHandler, - grp: grp, - rng: rng, - sInteface: services, - } - - obj, err := kv.Get(storeKey, currentStoreVersion) - if err != nil { - return nil, err - } - - err = r.unmarshal(obj.Data) - if err != nil { - return nil, err - } - - // add standard services - if err = r.AddService(Silent, nil); err != nil { - jww.FATAL.Panicf("Could not add standard %r "+ - "service: %+v", Silent, err) - } - if err = r.AddService(E2e, nil); err != nil { - jww.FATAL.Panicf("Could not add standard %r "+ - "service: %+v", E2e, err) - } - - return r, nil -} - -func (r *Ratchet) save() error { - now := netTime.Now() - - data, err := r.marshal() - if err != nil { - return err - } - - obj := versioned.Object{ - Version: currentStoreVersion, - Timestamp: now, - Data: data, - } - - return r.kv.Set(storeKey, currentStoreVersion, &obj) -} - // AddPartner adds a partner. Automatically creates both send and receive // sessions using the passed cryptographic data and per the parameters sent -func (r *Ratchet) AddPartner(partnerID *id.ID, partnerPubKey, - myPrivKey *cyclic.Int, partnerSIDHPubKey *sidh.PublicKey, +func (r *Ratchet) AddPartner(myID *id.ID, myPrivateKey *cyclic.Int, partnerID *id.ID, + partnerPubKey, myPrivKey *cyclic.Int, partnerSIDHPubKey *sidh.PublicKey, mySIDHPrivKey *sidh.PrivateKey, sendParams, - receiveParams session.Params) (*partner.Manager, error) { + receiveParams session.Params, temporary bool) (*partner.Manager, error) { r.mux.Lock() defer r.mux.Unlock() + if myID == nil { + myID = r.defaultID + } + + if myPrivateKey == nil { + myPrivateKey = r.defaultDHPrivateKey + } + jww.INFO.Printf("Adding Partner %r:\n\tMy Private Key: %r"+ - "\n\tPartner Public Key: %r", + "\n\tPartner Public Key: %r to %s", partnerID, myPrivKey.TextVerbose(16, 0), - partnerPubKey.TextVerbose(16, 0)) + partnerPubKey.TextVerbose(16, 0), myID) - if _, ok := r.managers[*partnerID]; ok { + rship := makeRelationshipIdentity(partnerID, myID) + + if _, ok := r.managers[rship]; ok { return nil, errors.New("Cannot overwrite existing partner") } - m := partner.NewManager(r.kv, r.myID, partnerID, myPrivKey, partnerPubKey, + //pass a memory kv if it is supposed to be temporary + kv := r.kv + if temporary { + kv = r.memKv + } + + m := partner.NewManager(kv, r.defaultID, partnerID, myPrivKey, partnerPubKey, mySIDHPrivKey, partnerSIDHPubKey, sendParams, receiveParams, r.cyHandler, r.grp, r.rng) - r.managers[*partnerID] = m + r.managers[rship] = m if err := r.save(); err != nil { jww.FATAL.Printf("Failed to add Partner %r: Save of store failed: %r", partnerID, err) @@ -192,11 +145,15 @@ func (r *Ratchet) AddPartner(partnerID *id.ID, partnerPubKey, } // GetPartner returns the partner per its ID, if it exists -func (r *Ratchet) GetPartner(partnerID *id.ID) (*partner.Manager, error) { +func (r *Ratchet) GetPartner(partnerID *id.ID, myID *id.ID) (*partner.Manager, error) { r.mux.RLock() defer r.mux.RUnlock() - m, ok := r.managers[*partnerID] + if myID == nil { + myID = r.defaultID + } + + m, ok := r.managers[makeRelationshipIdentity(partnerID, myID)] if !ok { return nil, errors.New(NoPartnerErrorStr) @@ -206,8 +163,13 @@ func (r *Ratchet) GetPartner(partnerID *id.ID) (*partner.Manager, error) { } // DeletePartner removes the associated contact from the E2E store -func (r *Ratchet) DeletePartner(partnerId *id.ID) error { - m, ok := r.managers[*partnerId] +func (r *Ratchet) DeletePartner(partnerId *id.ID, myID *id.ID) error { + if myID == nil { + myID = r.defaultID + } + + rShip := makeRelationshipIdentity(partnerId, myID) + m, ok := r.managers[rShip] if !ok { return errors.New(NoPartnerErrorStr) } @@ -219,21 +181,24 @@ func (r *Ratchet) DeletePartner(partnerId *id.ID) error { //delete services r.delete(m) - delete(r.managers, *partnerId) + delete(r.managers, rShip) return r.save() } // GetAllPartnerIDs returns a list of all partner IDs that the user has // an E2E relationship with. -func (r *Ratchet) GetAllPartnerIDs() []*id.ID { +func (r *Ratchet) GetAllPartnerIDs(myID *id.ID) []*id.ID { r.mux.RLock() defer r.mux.RUnlock() partnerIds := make([]*id.ID, 0, len(r.managers)) - for partnerId := range r.managers { - partnerIds = append(partnerIds, &partnerId) + for _, m := range r.managers { + if m.GetMyID().Cmp(myID) { + partnerIds = append(partnerIds, m.GetPartnerID()) + } + } return partnerIds @@ -241,72 +206,10 @@ func (r *Ratchet) GetAllPartnerIDs() []*id.ID { // GetDHPrivateKey returns the diffie hellman private key. func (r *Ratchet) GetDHPrivateKey() *cyclic.Int { - return r.dhPrivateKey + return r.defaultDHPrivateKey } // GetDHPublicKey returns the diffie hellman public key. func (r *Ratchet) GetDHPublicKey() *cyclic.Int { - return r.dhPublicKey -} - -// ekv functions -func (r *Ratchet) marshal() ([]byte, error) { - contacts := make([]id.ID, len(r.managers)) - - index := 0 - for partnerID := range r.managers { - contacts[index] = partnerID - index++ - } - - return json.Marshal(&contacts) -} - -func (r *Ratchet) unmarshal(b []byte) error { - - var contacts []id.ID - - err := json.Unmarshal(b, &contacts) - - if err != nil { - return err - } - - for i := range contacts { - //load the contact separately to ensure pointers do not get swapped - partnerID := (&contacts[i]).DeepCopy() - // Load the relationship. The relationship handles adding the fingerprints via the - // context object - manager, err := partner.LoadManager(r.kv, r.myID, partnerID, - r.cyHandler, r.grp, r.rng) - if err != nil { - jww.FATAL.Panicf("Failed to load relationship for partner %r: %r", - partnerID, err.Error()) - } - - if !manager.GetPartnerID().Cmp(partnerID) { - jww.FATAL.Panicf("Loaded a manager with the wrong partner "+ - "ID: \n\t loaded: %r \n\t present: %r", - partnerID, manager.GetPartnerID()) - } - - //add services for the manager - r.add(manager) - - r.managers[*partnerID] = manager - } - - r.dhPrivateKey, err = util.LoadCyclicKey(r.kv, privKeyKey) - if err != nil { - return errors.WithMessage(err, - "Failed to load e2e DH private key") - } - - r.dhPublicKey, err = util.LoadCyclicKey(r.kv, pubKeyKey) - if err != nil { - return errors.WithMessage(err, - "Failed to load e2e DH public key") - } - - return nil + return r.defaultDHPublicKey } diff --git a/e2e/ratchet/store_test.go b/e2e/ratchet/ratchet_test.go similarity index 90% rename from e2e/ratchet/store_test.go rename to e2e/ratchet/ratchet_test.go index 53cd33181ff12e240d09d0c84ba6e2cc7354b6ee..409f5ef85abcdcb927733aeac383f5c15f335bab 100644 --- a/e2e/ratchet/store_test.go +++ b/e2e/ratchet/ratchet_test.go @@ -40,12 +40,12 @@ func TestNewStore(t *testing.T) { rng := fastRNG.NewStreamGenerator(12, 3, csprng.NewSystemRNG) e2eP := params.GetDefaultE2ESessionParams() expectedStore := &Ratchet{ - managers: make(map[id.ID]*partner.Manager), - dhPrivateKey: privKey, - dhPublicKey: diffieHellman.GeneratePublicKey(privKey, grp), - grp: grp, - kv: kv.Prefix(packagePrefix), - fingerprints: &fingerprints, + managers: make(map[id.ID]*partner.Manager), + defaultDHPrivateKey: privKey, + defaultDHPublicKey: diffieHellman.GeneratePublicKey(privKey, grp), + grp: grp, + kv: kv.Prefix(packagePrefix), + fingerprints: &fingerprints, context: &context{ fa: &fingerprints, grp: grp, @@ -103,7 +103,7 @@ func TestStore_AddPartner(t *testing.T) { rng := csprng.NewSystemRNG() s, _, _ := makeTestStore() partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t) - pubKey := diffieHellman.GeneratePublicKey(s.dhPrivateKey, s.grp) + pubKey := diffieHellman.GeneratePublicKey(s.defaultDHPrivateKey, s.grp) p := params.GetDefaultE2ESessionParams() // NOTE: e2e store doesn't contain a private SIDH key, that's // because they're completely address as part of the @@ -111,11 +111,11 @@ func TestStore_AddPartner(t *testing.T) { _, pubSIDHKey := genSidhKeys(rng, sidh.KeyVariantSidhA) privSIDHKey, _ := genSidhKeys(rng, sidh.KeyVariantSidhB) expectedManager := partner.newManager(s.context, s.kv, partnerID, - s.dhPrivateKey, pubKey, + s.defaultDHPrivateKey, pubKey, privSIDHKey, pubSIDHKey, p, p) - err := s.AddPartner(partnerID, pubKey, s.dhPrivateKey, pubSIDHKey, + err := s.AddPartner(partnerID, pubKey, s.defaultDHPrivateKey, pubSIDHKey, privSIDHKey, p, p) if err != nil { t.Fatalf("AddPartner returned an error: %v", err) @@ -138,7 +138,7 @@ func TestStore_DeletePartner(t *testing.T) { rng := csprng.NewSystemRNG() s, _, _ := makeTestStore() partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t) - pubKey := diffieHellman.GeneratePublicKey(s.dhPrivateKey, s.grp) + pubKey := diffieHellman.GeneratePublicKey(s.defaultDHPrivateKey, s.grp) p := params.GetDefaultE2ESessionParams() // NOTE: e2e store doesn't contain a private SIDH key, that's // because they're completely address as part of the @@ -146,7 +146,7 @@ func TestStore_DeletePartner(t *testing.T) { _, pubSIDHKey := genSidhKeys(rng, sidh.KeyVariantSidhA) privSIDHKey, _ := genSidhKeys(rng, sidh.KeyVariantSidhB) - err := s.AddPartner(partnerID, pubKey, s.dhPrivateKey, pubSIDHKey, + err := s.AddPartner(partnerID, pubKey, s.defaultDHPrivateKey, pubSIDHKey, privSIDHKey, p, p) if err != nil { t.Fatalf("Could not add partner in set up: %v", err) @@ -169,13 +169,13 @@ func TestStore_GetPartner(t *testing.T) { rng := csprng.NewSystemRNG() s, _, _ := makeTestStore() partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t) - pubKey := diffieHellman.GeneratePublicKey(s.dhPrivateKey, s.grp) + pubKey := diffieHellman.GeneratePublicKey(s.defaultDHPrivateKey, s.grp) p := params.GetDefaultE2ESessionParams() _, pubSIDHKey := genSidhKeys(rng, sidh.KeyVariantSidhA) privSIDHKey, _ := genSidhKeys(rng, sidh.KeyVariantSidhB) expectedManager := partner.newManager(s.context, s.kv, partnerID, - s.dhPrivateKey, pubKey, privSIDHKey, pubSIDHKey, p, p) - _ = s.AddPartner(partnerID, pubKey, s.dhPrivateKey, pubSIDHKey, + s.defaultDHPrivateKey, pubKey, privSIDHKey, pubSIDHKey, p, p) + _ = s.AddPartner(partnerID, pubKey, s.defaultDHPrivateKey, pubSIDHKey, privSIDHKey, p, p) m, err := s.GetPartner(partnerID) @@ -209,7 +209,7 @@ func TestStore_GetPartner_Error(t *testing.T) { func TestStore_GetPartnerContact(t *testing.T) { s, _, _ := makeTestStore() partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t) - pubKey := diffieHellman.GeneratePublicKey(s.dhPrivateKey, s.grp) + pubKey := diffieHellman.GeneratePublicKey(s.defaultDHPrivateKey, s.grp) p := params.GetDefaultE2ESessionParams() expected := contact.Contact{ ID: partnerID, @@ -219,7 +219,7 @@ func TestStore_GetPartnerContact(t *testing.T) { _, pubSIDHKey := genSidhKeys(rng, sidh.KeyVariantSidhA) privSIDHKey, _ := genSidhKeys(rng, sidh.KeyVariantSidhB) - _ = s.AddPartner(partnerID, pubKey, s.dhPrivateKey, pubSIDHKey, + _ = s.AddPartner(partnerID, pubKey, s.defaultDHPrivateKey, pubSIDHKey, privSIDHKey, p, p) c, err := s.GetPartnerContact(partnerID) @@ -307,10 +307,10 @@ func TestStore_CheckKey(t *testing.T) { func TestStore_GetDHPrivateKey(t *testing.T) { s, _, _ := makeTestStore() - if s.dhPrivateKey != s.GetDHPrivateKey() { + if s.defaultDHPrivateKey != s.GetDHPrivateKey() { t.Errorf("GetDHPrivateKey() returned incorrect key."+ "\n\texpected: %v\n\treceived: %v", - s.dhPrivateKey, s.GetDHPrivateKey()) + s.defaultDHPrivateKey, s.GetDHPrivateKey()) } } @@ -318,10 +318,10 @@ func TestStore_GetDHPrivateKey(t *testing.T) { func TestStore_GetDHPublicKey(t *testing.T) { s, _, _ := makeTestStore() - if s.dhPublicKey != s.GetDHPublicKey() { + if s.defaultDHPublicKey != s.GetDHPublicKey() { t.Errorf("GetDHPublicKey() returned incorrect key."+ "\n\texpected: %v\n\treceived: %v", - s.dhPublicKey, s.GetDHPublicKey()) + s.defaultDHPublicKey, s.GetDHPublicKey()) } } diff --git a/e2e/ratchet/relationshipID.go b/e2e/ratchet/relationshipID.go new file mode 100644 index 0000000000000000000000000000000000000000..58cdfcbcedcf3acb5163ca592207b5e27c859f99 --- /dev/null +++ b/e2e/ratchet/relationshipID.go @@ -0,0 +1,26 @@ +package ratchet + +import ( + "gitlab.com/xx_network/primitives/id" +) + +type relationshipIdentity [2 * id.ArrIDLen]byte + +func makeRelationshipIdentity(partner, me *id.ID) relationshipIdentity { + ph := relationshipIdentity{} + copy(ph[:id.ArrIDLen], me[:]) + copy(ph[id.ArrIDLen:], partner[:]) + return ph +} + +func (ri relationshipIdentity) GetMe() *id.ID { + me := &id.ID{} + copy(me[:], ri[:id.ArrIDLen]) + return me +} + +func (ri relationshipIdentity) GetPartner() *id.ID { + partner := &id.ID{} + copy(partner[:], ri[id.ArrIDLen:]) + return partner +} diff --git a/e2e/ratchet/serviceList.go b/e2e/ratchet/serviceList.go index 1f6fb32e824e9ada47a5ea2ebf9ff33abd10a6bf..6184c8f1e0df1fe279732d9fc3bc7c343b9edd23 100644 --- a/e2e/ratchet/serviceList.go +++ b/e2e/ratchet/serviceList.go @@ -19,7 +19,7 @@ func (r *Ratchet) add(m *partner.Manager) { r.servicesmux.RLock() defer r.servicesmux.RUnlock() for tag, process := range r.services { - r.sInteface.AddService(r.myID, m.MakeService(tag), process) + r.sInteface.AddService(r.defaultID, m.MakeService(tag), process) } } @@ -27,7 +27,7 @@ func (r *Ratchet) delete(m *partner.Manager) { r.servicesmux.RLock() defer r.servicesmux.RUnlock() for tag, process := range r.services { - r.sInteface.DeleteService(r.myID, m.MakeService(tag), process) + r.sInteface.DeleteService(r.defaultID, m.MakeService(tag), process) } } @@ -42,7 +42,7 @@ func (r *Ratchet) AddService(tag string, processor message.Processor) error { //add a service for every manager for _, m := range r.managers { - r.sInteface.AddService(r.myID, m.MakeService(tag), processor) + r.sInteface.AddService(r.defaultID, m.MakeService(tag), processor) } return nil @@ -61,7 +61,7 @@ func (r *Ratchet) RemoveService(tag string) error { delete(r.services, tag) for _, m := range r.managers { - r.sInteface.DeleteService(r.myID, m.MakeService(tag), oldServiceProcess) + r.sInteface.DeleteService(r.defaultID, m.MakeService(tag), oldServiceProcess) } return nil diff --git a/e2e/ratchet/storage.go b/e2e/ratchet/storage.go new file mode 100644 index 0000000000000000000000000000000000000000..f170df1786f4e01544e035a091f5f51c06fef73e --- /dev/null +++ b/e2e/ratchet/storage.go @@ -0,0 +1,209 @@ +package ratchet + +import ( + "encoding/json" + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/e2e/ratchet/partner" + "gitlab.com/elixxir/client/e2e/ratchet/partner/session" + "gitlab.com/elixxir/client/network/message" + util "gitlab.com/elixxir/client/storage/utility" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/elixxir/ekv" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/netTime" +) + +const ( + currentStoreVersion = 0 + storeKey = "Store" +) + +// Load loads an extant ratchet from disk +func Load(kv *versioned.KV, myID *id.ID, grp *cyclic.Group, + cyHandler session.CypherHandler, services Services, rng *fastRNG.StreamGenerator) ( + *Ratchet, error) { + kv = kv.Prefix(packagePrefix) + + r := &Ratchet{ + managers: make(map[relationshipIdentity]*partner.Manager), + services: make(map[string]message.Processor), + + defaultID: myID, + + kv: kv, + memKv: versioned.NewKV(make(ekv.Memstore)), + + cyHandler: cyHandler, + grp: grp, + rng: rng, + sInteface: services, + } + + obj, err := kv.Get(storeKey, currentStoreVersion) + if err != nil { + //try to load an old one + obj, err = kv.Get(storeKey, 0) + if err != nil { + return nil, err + } else { + err = r.unmarshalOld(obj.Data) + if err != nil { + return nil, err + } + } + + } else { + err = r.unmarshal(obj.Data) + if err != nil { + return nil, err + } + } + + // add standard services + if err = r.AddService(Silent, nil); err != nil { + jww.FATAL.Panicf("Could not add standard %r "+ + "service: %+v", Silent, err) + } + if err = r.AddService(E2e, nil); err != nil { + jww.FATAL.Panicf("Could not add standard %r "+ + "service: %+v", E2e, err) + } + + return r, nil +} + +func (r *Ratchet) save() error { + now := netTime.Now() + + data, err := r.marshal() + if err != nil { + return err + } + + obj := versioned.Object{ + Version: currentStoreVersion, + Timestamp: now, + Data: data, + } + + return r.kv.Set(storeKey, currentStoreVersion, &obj) +} + +// ekv functions +func (r *Ratchet) marshal() ([]byte, error) { + contacts := make([]relationshipIdentity, len(r.managers)) + + index := 0 + for rid, m := range r.managers { + if !m.IsTemporary() { + contacts[index] = rid + index++ + } + } + + return json.Marshal(&contacts) +} + +// In the event an old structure was loaded, unmarshal it and upgrade it +func (r *Ratchet) unmarshalOld(b []byte) error { + + var contacts []id.ID + + err := json.Unmarshal(b, &contacts) + + if err != nil { + return err + } + + for i := range contacts { + //load the contact separately to ensure pointers do not get swapped + partnerID := (&contacts[i]).DeepCopy() + // Load the relationship. The relationship handles adding the fingerprints via the + // context object + manager, err := partner.LoadManager(r.kv, r.defaultID, partnerID, + r.cyHandler, r.grp, r.rng) + if err != nil { + jww.FATAL.Panicf("Failed to load relationship for partner %r: %r", + partnerID, err.Error()) + } + + if !manager.GetPartnerID().Cmp(partnerID) { + jww.FATAL.Panicf("Loaded a manager with the wrong partner "+ + "ID: \n\t loaded: %r \n\t present: %r", + partnerID, manager.GetPartnerID()) + } + + //add services for the manager + r.add(manager) + + //assume + r.managers[makeRelationshipIdentity(partnerID, r.defaultID)] = manager + } + + r.defaultDHPrivateKey, err = util.LoadCyclicKey(r.kv, privKeyKey) + if err != nil { + return errors.WithMessage(err, + "Failed to load e2e DH private key") + } + + r.defaultDHPublicKey, err = util.LoadCyclicKey(r.kv, pubKeyKey) + if err != nil { + return errors.WithMessage(err, + "Failed to load e2e DH public key") + } + + return nil +} + +func (r *Ratchet) unmarshal(b []byte) error { + + var contacts []relationshipIdentity + + err := json.Unmarshal(b, &contacts) + + if err != nil { + return err + } + + for i := range contacts { + //load the contact separately to ensure pointers do not get swapped + partnerID := contacts[i].GetPartner() + myID := contacts[i].GetMe() + // Load the relationship. The relationship handles adding the fingerprints via the + // context object + manager, err := partner.LoadManager(r.kv, myID, partnerID, + r.cyHandler, r.grp, r.rng) + if err != nil { + jww.FATAL.Panicf("Failed to load relationship for partner %r: %r", + partnerID, err.Error()) + } + + if !manager.GetPartnerID().Cmp(partnerID) { + jww.FATAL.Panicf("Loaded a manager with the wrong partner "+ + "ID: \n\t loaded: %r \n\t present: %r", + partnerID, manager.GetPartnerID()) + } + + //add services for the manager + r.add(manager) + + r.managers[contacts[i]] = manager + } + + r.defaultDHPrivateKey, err = util.LoadCyclicKey(r.kv, privKeyKey) + if err != nil { + return errors.WithMessage(err, + "Failed to load e2e DH private key") + } + + r.defaultDHPublicKey, err = util.LoadCyclicKey(r.kv, pubKeyKey) + if err != nil { + return errors.WithMessage(err, + "Failed to load e2e DH public key") + } + + return nil +} diff --git a/e2e/rekey/confirm.go b/e2e/rekey/confirm.go index a8d79136355be9077a8869c61ef7e05ab425e440..69c398e8ffce2c50ff360a502eba48a10f0350e2 100644 --- a/e2e/rekey/confirm.go +++ b/e2e/rekey/confirm.go @@ -36,12 +36,13 @@ func handleConfirm(ratchet *ratchet.Ratchet, confirmation receive.Message) { if !confirmation.Encrypted { jww.ERROR.Printf( "[REKEY] Received non-e2e encrypted Key Exchange "+ - "confirm from partner %s", confirmation.Sender) + "confirm from partner %s to %s", confirmation.Sender, + confirmation.RecipientID) return } //get the partner - partner, err := ratchet.GetPartner(confirmation.Sender) + partner, err := ratchet.GetPartner(confirmation.Sender, confirmation.RecipientID) if err != nil { jww.ERROR.Printf( "[REKEY] Received Key Exchange Confirmation with unknown "+ diff --git a/e2e/rekey/trigger.go b/e2e/rekey/trigger.go index b2ea371ac7f2982a61745cab776bb397856e7bc7..34ff601a8cc1e90d6b948934d369c384ee2945f2 100644 --- a/e2e/rekey/trigger.go +++ b/e2e/rekey/trigger.go @@ -61,7 +61,7 @@ func handleTrigger(ratchet *ratchet.Ratchet, sender E2eSender, } //get the partner - partner, err := ratchet.GetPartner(request.Sender) + partner, err := ratchet.GetPartner(request.Sender, request.RecipientID) if err != nil { errMsg := fmt.Sprintf(errUnknown, request.Sender) jww.ERROR.Printf(errMsg) diff --git a/e2e/sendUnsafe.go b/e2e/sendUnsafe.go index e602a14f96a508245c4d9b27e8908961832dfb6c..ac7582992500daa2bf1e9798d6ab7b9d010097ed 100644 --- a/e2e/sendUnsafe.go +++ b/e2e/sendUnsafe.go @@ -64,7 +64,7 @@ func (m *manager) sendUnsafe(mt catalog.MessageType, recipient *id.ID, wg.Add(1) go func(i int, payload []byte) { - unencryptedMAC, fp := e2e.SetUnencrypted(payload, m.myID) + unencryptedMAC, fp := e2e.SetUnencrypted(payload, m.myDefaultID) var err error roundIds[i], _, err = m.net.SendCMIX(recipient, fp, diff --git a/storage/utility/id.go b/storage/utility/id.go new file mode 100644 index 0000000000000000000000000000000000000000..08472b53e36da3d4912886ca3b19931d0120f78d --- /dev/null +++ b/storage/utility/id.go @@ -0,0 +1,42 @@ +package utility + +import ( + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/netTime" +) + +const currentIDVersion = 0 + +func StoreID(kv *versioned.KV, sid *id.ID, key string) error { + now := netTime.Now() + + data, err := sid.MarshalJSON() + if err != nil { + return err + } + + obj := versioned.Object{ + Version: currentIDVersion, + Timestamp: now, + Data: data, + } + + return kv.Set(key, currentIDVersion, &obj) +} + +func LoadID(kv *versioned.KV, key string) (*id.ID, error) { + vo, err := kv.Get(key, currentIDVersion) + if err != nil { + return nil, err + } + + sid := &id.ID{} + + return sid, sid.UnmarshalJSON(vo.Data) +} + +// DeleteCID deletes a given cyclic key from storage +func DeleteCID(kv *versioned.KV, key string) error { + return kv.Delete(key, currentIDVersion) +} diff --git a/storage/versioned/kv.go b/storage/versioned/kv.go index 3e05b1b052d8a2713b9c9227e209f990c9c3f41d..fe7e8cd6a9669f75878aa87fa97846f84eb83627 100644 --- a/storage/versioned/kv.go +++ b/storage/versioned/kv.go @@ -151,6 +151,11 @@ func (v *KV) Prefix(prefix string) *KV { return &kvPrefix } +func (v *KV) IsMemStore() bool { + _, success := v.r.data.(ekv.Memstore) + return success +} + //Returns the key with all prefixes appended func (v *KV) GetFullKey(key string, version uint64) string { return v.makeKey(key, version)