diff --git a/e2e/fpGenerator.go b/e2e/fpGenerator.go index 3a9a6c00ee1fdd753e3a6aaa78ef9e17e56b91cc..5e89dd8016e3c231fd968c54bdb4824f9ceeac9c 100644 --- a/e2e/fpGenerator.go +++ b/e2e/fpGenerator.go @@ -5,25 +5,24 @@ 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 +// fpGenerator is a wrapper that allows the network manager's fingerprint +// interface to be passed into ratchet without exposing ratchet to the business +// logic. type fpGenerator struct { - *manager + m *manager } -func (fp *fpGenerator) AddKey(k *session.Cypher) { - err := fp.net.AddFingerprint(fp.myID, k.Fingerprint(), - &processor{ - cy: k, - m: fp.manager, - }) +// AddKey adds a fingerprint to be tracked for the given cypher. +func (fpg *fpGenerator) AddKey(cy session.Cypher) { + err := fpg.m.net.AddFingerprint( + fpg.m.myID, cy.Fingerprint(), &processor{cy, fpg.m}) if err != nil { - jww.ERROR.Printf("Could not add fingerprint %s: %+v", - k.Fingerprint(), err) + jww.ERROR.Printf( + "Could not add fingerprint %s: %+v", cy.Fingerprint(), err) } } -func (fp *fpGenerator) DeleteKey(k *session.Cypher) { - fp.net.DeleteFingerprint(fp.myID, k.Fingerprint()) +// DeleteKey deletes the fingerprint for the given cypher. +func (fpg *fpGenerator) DeleteKey(cy session.Cypher) { + fpg.m.net.DeleteFingerprint(fpg.m.myID, cy.Fingerprint()) } diff --git a/e2e/fpGenerator_test.go b/e2e/fpGenerator_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ada64460a28221276304055f21056b3be92ae3dd --- /dev/null +++ b/e2e/fpGenerator_test.go @@ -0,0 +1,182 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +//////////////////////////////////////////////////////////////////////////////// + +package e2e + +import ( + "gitlab.com/elixxir/client/cmix" + "gitlab.com/elixxir/client/cmix/gateway" + "gitlab.com/elixxir/client/cmix/identity" + "gitlab.com/elixxir/client/cmix/message" + "gitlab.com/elixxir/client/cmix/rounds" + "gitlab.com/elixxir/client/e2e/ratchet/partner/session" + "gitlab.com/elixxir/client/stoppable" + "gitlab.com/elixxir/comms/network" + "gitlab.com/elixxir/primitives/format" + "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" + "gitlab.com/xx_network/primitives/ndf" + "math/rand" + "sync" + "testing" + "time" +) + +// Adds a list of cyphers with different fingerprints with fpGenerator.AddKey +// and then checks that they were added to the mock cMix fingerprint tracker. +func Test_fpGenerator_AddKey(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + net := newMockFpgCmix() + fpg := &fpGenerator{&manager{ + net: net, + myID: id.NewIdFromString("myID", id.User, t), + }} + + fps := make([]format.Fingerprint, 20) + for i := range fps { + prng.Read(fps[i][:]) + fpg.AddKey(mockSessionCypher{fps[i]}) + } + + for i, fp := range fps { + if _, exists := net.processors[*fpg.m.myID][fp]; !exists { + t.Errorf("Fingerprint #%d does not exist.", i) + } else { + delete(net.processors[*fpg.m.myID], fp) + } + } + + if len(net.processors[*fpg.m.myID]) != 0 { + t.Errorf("%d extra fingerprints found: %+v", + len(net.processors[*fpg.m.myID]), net.processors[*fpg.m.myID]) + } +} + +// Adds a list of cyphers with different fingerprints and then deletes all of +// them with fpGenerator.DeleteKey and checks that all keys were deleted. +func Test_fpGenerator_DeleteKey(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + net := newMockFpgCmix() + fpg := &fpGenerator{&manager{ + net: net, + myID: id.NewIdFromString("myID", id.User, t), + }} + + fps := make([]format.Fingerprint, 20) + for i := range fps { + prng.Read(fps[i][:]) + fpg.AddKey(mockSessionCypher{fps[i]}) + } + + for _, fp := range fps { + fpg.DeleteKey(mockSessionCypher{fp}) + } + + if len(net.processors[*fpg.m.myID]) != 0 { + t.Errorf("%d extra fingerprints found: %+v", + len(net.processors[*fpg.m.myID]), net.processors[*fpg.m.myID]) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Mock Session Cypher // +//////////////////////////////////////////////////////////////////////////////// + +type mockSessionCypher struct { + fp format.Fingerprint +} + +func (m mockSessionCypher) GetSession() *session.Session { return nil } +func (m mockSessionCypher) Fingerprint() format.Fingerprint { return m.fp } +func (m mockSessionCypher) Encrypt([]byte) (ecrContents, mac []byte) { return nil, nil } +func (m mockSessionCypher) Decrypt(format.Message) ([]byte, error) { return nil, nil } +func (m mockSessionCypher) Use() {} + +//////////////////////////////////////////////////////////////////////////////// +// Mock cMix Client // +//////////////////////////////////////////////////////////////////////////////// + +type mockFpgCmix struct { + processors map[id.ID]map[format.Fingerprint]message.Processor + sync.Mutex +} + +func newMockFpgCmix() *mockFpgCmix { + return &mockFpgCmix{ + processors: make(map[id.ID]map[format.Fingerprint]message.Processor), + } +} + +func (m *mockFpgCmix) Connect(*ndf.NetworkDefinition) error { return nil } +func (m *mockFpgCmix) Follow(cmix.ClientErrorReport) (stoppable.Stoppable, error) { return nil, nil } +func (m *mockFpgCmix) GetMaxMessageLength() int { return 0 } +func (m *mockFpgCmix) Send(*id.ID, format.Fingerprint, message.Service, []byte, []byte, cmix.CMIXParams) (id.Round, ephemeral.Id, error) { + return 0, ephemeral.Id{}, nil +} +func (m *mockFpgCmix) SendMany([]cmix.TargetedCmixMessage, cmix.CMIXParams) (id.Round, []ephemeral.Id, error) { + return 0, nil, nil +} +func (m *mockFpgCmix) AddIdentity(*id.ID, time.Time, bool) {} +func (m *mockFpgCmix) RemoveIdentity(*id.ID) {} +func (m *mockFpgCmix) GetIdentity(*id.ID) (identity.TrackedID, error) { + return identity.TrackedID{}, nil +} + +func (m *mockFpgCmix) AddFingerprint(uid *id.ID, fp format.Fingerprint, mp message.Processor) error { + m.Lock() + defer m.Unlock() + + if _, exists := m.processors[*uid]; !exists { + m.processors[*uid] = + map[format.Fingerprint]message.Processor{fp: mp} + } else if _, exists = m.processors[*uid][fp]; !exists { + m.processors[*uid][fp] = mp + } + + return nil +} + +func (m *mockFpgCmix) DeleteFingerprint(uid *id.ID, fp format.Fingerprint) { + m.Lock() + defer m.Unlock() + + if _, exists := m.processors[*uid]; exists { + delete(m.processors[*uid], fp) + } +} + +func (m *mockFpgCmix) DeleteClientFingerprints(*id.ID) {} +func (m *mockFpgCmix) AddService(*id.ID, message.Service, message.Processor) {} +func (m *mockFpgCmix) DeleteService(*id.ID, message.Service, message.Processor) {} +func (m *mockFpgCmix) DeleteClientService(*id.ID) {} +func (m *mockFpgCmix) TrackServices(message.ServicesTracker) {} +func (m *mockFpgCmix) CheckInProgressMessages() {} +func (m *mockFpgCmix) IsHealthy() bool { return false } +func (m *mockFpgCmix) WasHealthy() bool { return false } +func (m *mockFpgCmix) AddHealthCallback(func(bool)) uint64 { return 0 } +func (m *mockFpgCmix) RemoveHealthCallback(uint64) {} +func (m *mockFpgCmix) HasNode(*id.ID) bool { return false } +func (m *mockFpgCmix) NumRegisteredNodes() int { return 0 } +func (m *mockFpgCmix) TriggerNodeRegistration(*id.ID) {} +func (m *mockFpgCmix) GetRoundResults(time.Duration, cmix.RoundEventCallback, ...id.Round) error { + return nil +} +func (m *mockFpgCmix) LookupHistoricalRound(id.Round, rounds.RoundResultCallback) error { return nil } +func (m *mockFpgCmix) SendToAny(func(host *connect.Host) (interface{}, error), *stoppable.Single) (interface{}, error) { + return nil, nil +} +func (m *mockFpgCmix) SendToPreferred([]*id.ID, gateway.SendToPreferredFunc, *stoppable.Single, time.Duration) (interface{}, error) { + return nil, nil +} +func (m *mockFpgCmix) SetGatewayFilter(gateway.Filter) {} +func (m *mockFpgCmix) GetHostParams() connect.HostParams { return connect.HostParams{} } +func (m *mockFpgCmix) GetAddressSpace() uint8 { return 0 } +func (m *mockFpgCmix) RegisterAddressSpaceNotification(string) (chan uint8, error) { return nil, nil } +func (m *mockFpgCmix) UnregisterAddressSpaceNotification(string) {} +func (m *mockFpgCmix) GetInstance() *network.Instance { return nil } +func (m *mockFpgCmix) GetVerboseRounds() string { return "" } diff --git a/e2e/manager.go b/e2e/manager.go index 73eeb111abdcc7db9b360e7e06065e5f7bce4a49..2bdb8c21f1afcfd20cf41a2ba06fdc06d08bd13e 100644 --- a/e2e/manager.go +++ b/e2e/manager.go @@ -118,8 +118,7 @@ func loadE2E(kv *versioned.KV, net cmix.Client, myDefaultID *id.ID, m := &manager{ Switchboard: receive.New(), - partitioner: parse.NewPartitioner(kv, - net.GetMaxMessageLength()), + partitioner: parse.NewPartitioner(kv, net.GetMaxMessageLength()), net: net, myID: myDefaultID, events: events, diff --git a/e2e/manager_test.go b/e2e/manager_test.go index 0c1b530dabc4961aef006721fc1fd0d8e38c4066..5f89bc5ecd2c9434557606001a40ed21803171fc 100644 --- a/e2e/manager_test.go +++ b/e2e/manager_test.go @@ -26,7 +26,7 @@ type legacyPartnerData struct { mySidhPrivKey *sidh.PrivateKey partnerSidhPubKey *sidh.PublicKey sendFP []byte - recieveFp []byte + receiveFp []byte } // TestRatchet_unmarshalOld tests the loading of legacy data @@ -87,7 +87,7 @@ func TestLoadLegacy(t *testing.T) { // the legacy loading tests sendFP: e2e.MakeRelationshipFingerprint(myPubKey, partnerPubKey, myId, partnerID), - recieveFp: e2e.MakeRelationshipFingerprint(myPubKey, partnerPubKey, + receiveFp: e2e.MakeRelationshipFingerprint(myPubKey, partnerPubKey, partnerID, myId), } @@ -96,12 +96,12 @@ func TestLoadLegacy(t *testing.T) { } // Construct kv with legacy data - //fs, err := ekv.NewFilestore("/home/josh/src/client/e2e/legacyEkv", "hello") - //if err != nil { + // fs, err := ekv.NewFilestore("/home/josh/src/client/e2e/legacyEkv", "hello") + // if err != nil { // t.Fatalf( // "Failed to create storage session: %+v", err) - //} - kv := versioned.NewKV(&ekv.MakeMemstore()) + // } + kv := versioned.NewKV(ekv.MakeMemstore()) err := ratchet.New(kv, myId, myPrivKey, grp) if err != nil { @@ -111,7 +111,7 @@ func TestLoadLegacy(t *testing.T) { rng := fastRNG.NewStreamGenerator(12, 3, csprng.NewSystemRNG) // Load legacy data - h, err := LoadLegacy(kv, &mockCmixNet{testingInterface: t}, myId, + h, err := LoadLegacy(kv, newMockCmix(nil, nil, t), myId, grp, rng, mockEventsManager{}, rekey.GetDefaultParams()) if err != nil { t.Fatalf("LoadLegacy error: %v", err) @@ -121,20 +121,19 @@ func TestLoadLegacy(t *testing.T) { for _, legacyPartner := range legacyData { partnerManager, err := h.GetPartner(legacyPartner.partnerId) if err != nil { - t.Errorf("Partner %d does not exist in handler.", legacyPartner.partnerId) + t.Errorf("Partner %s does not exist in handler.", legacyPartner.partnerId) + } else { + if !bytes.Equal(partnerManager.SendRelationshipFingerprint(), legacyPartner.sendFP) { + t.Fatalf("Send relationship fingerprint pulled from legacy does not match expected data."+ + "\nExpected: %v"+ + "\nReceived: %v", legacyPartner.sendFP, partnerManager.SendRelationshipFingerprint()) + } + + if !bytes.Equal(partnerManager.ReceiveRelationshipFingerprint(), legacyPartner.receiveFp) { + t.Fatalf("Receive relationship fingerprint pulled from legacy does not match expected data."+ + "\nExpected: %v"+ + "\nReceived: %v", legacyPartner.sendFP, partnerManager.SendRelationshipFingerprint()) + } } - - if !bytes.Equal(partnerManager.SendRelationshipFingerprint(), legacyPartner.sendFP) { - t.Fatalf("Send relationship fingerprint pulled from legacy does not match expected data."+ - "\nExpected: %v"+ - "\nReceived: %v", legacyPartner.sendFP, partnerManager.SendRelationshipFingerprint()) - } - - if !bytes.Equal(partnerManager.ReceiveRelationshipFingerprint(), legacyPartner.recieveFp) { - t.Fatalf("Receive relationship fingerprint pulled from legacy does not match expected data."+ - "\nExpected: %v"+ - "\nReceived: %v", legacyPartner.sendFP, partnerManager.SendRelationshipFingerprint()) - } - } } diff --git a/e2e/processor.go b/e2e/processor.go index ea7d4aef499eb4b6c8ca012e5fc3c07695568020..1b6620149005c12ad456fc3b3b34ead31890a240 100644 --- a/e2e/processor.go +++ b/e2e/processor.go @@ -2,16 +2,16 @@ package e2e import ( "fmt" + "gitlab.com/elixxir/client/e2e/ratchet/partner/session" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/cmix/identity/receptionID" "gitlab.com/elixxir/client/cmix/rounds" - "gitlab.com/elixxir/client/e2e/ratchet/partner/session" "gitlab.com/elixxir/primitives/format" ) type processor struct { - cy *session.Cypher + cy session.Cypher m *manager } diff --git a/e2e/ratchet/partner/interface.go b/e2e/ratchet/partner/interface.go index 02315b1ff7e4f63845f79a9feee3bd8668e79656..1074893e8ead14f8280133bd59a5276723b69339 100644 --- a/e2e/ratchet/partner/interface.go +++ b/e2e/ratchet/partner/interface.go @@ -29,9 +29,9 @@ type Manager interface { Contact() contact.Contact // PopSendCypher returns the key which is most likely to be successful for sending - PopSendCypher() (*session.Cypher, error) + PopSendCypher() (session.Cypher, error) // PopRekeyCypher returns a key which should be used for rekeying - PopRekeyCypher() (*session.Cypher, error) + PopRekeyCypher() (session.Cypher, error) // NewReceiveSession creates a new Receive session using the latest private key // this user has sent and the new public key received from the partner. If the diff --git a/e2e/ratchet/partner/manager.go b/e2e/ratchet/partner/manager.go index fbb33af91e1cb1b00508cb471422a41e925d46dd..ec7799363f2a107da4a49d42f8b2404d0dc0ac85 100644 --- a/e2e/ratchet/partner/manager.go +++ b/e2e/ratchet/partner/manager.go @@ -248,12 +248,12 @@ func (m *manager) NewSendSession(myPrivKey *cyclic.Int, } // PopSendCypher returns the key which is most likely to be successful for sending -func (m *manager) PopSendCypher() (*session.Cypher, error) { +func (m *manager) PopSendCypher() (session.Cypher, error) { return m.send.getKeyForSending() } // PopRekeyCypher returns a key which should be used for rekeying -func (m *manager) PopRekeyCypher() (*session.Cypher, error) { +func (m *manager) PopRekeyCypher() (session.Cypher, error) { return m.send.getKeyForRekey() } diff --git a/e2e/ratchet/partner/relationship.go b/e2e/ratchet/partner/relationship.go index 56b08a57137ac48f0fe27b953452dee0bbe2c01a..ff321d04becdce2e4ddeb9f18d520174b40f1b5c 100644 --- a/e2e/ratchet/partner/relationship.go +++ b/e2e/ratchet/partner/relationship.go @@ -249,7 +249,7 @@ func (r *relationship) GetNewest() *session.Session { } // returns the key which is most likely to be successful for sending -func (r *relationship) getKeyForSending() (*session.Cypher, error) { +func (r *relationship) getKeyForSending() (session.Cypher, error) { r.sendMux.Lock() defer r.sendMux.Unlock() s := r.getSessionForSending() @@ -325,7 +325,7 @@ func (r *relationship) TriggerNegotiation() []*session.Session { } // returns a key which should be used for rekeying -func (r *relationship) getKeyForRekey() (*session.Cypher, error) { +func (r *relationship) getKeyForRekey() (session.Cypher, error) { r.sendMux.Lock() defer r.sendMux.Unlock() s := r.getNewestRekeyableSession() diff --git a/e2e/ratchet/partner/session/cypher.go b/e2e/ratchet/partner/session/cypher.go index fe713eb08c2cf07f563e9ebcfee80b8f0e94c5e2..ef4094a40f0967f199dd4f89786fd6a2dcda5459 100644 --- a/e2e/ratchet/partner/session/cypher.go +++ b/e2e/ratchet/partner/session/cypher.go @@ -19,7 +19,7 @@ import ( ) // GenerateE2ESessionBaseKey returns the baseKey symmetric encryption key root. -// The baseKey is created by hashing the results of the diffie-helman (DH) key +// The baseKey is created by hashing the results of the Diffie-Hellman (DH) key // exchange with the post-quantum secure Supersingular Isogeny DH exchange // results. func GenerateE2ESessionBaseKey(myDHPrivKey, theirDHPubKey *cyclic.Int, @@ -50,61 +50,85 @@ func GenerateE2ESessionBaseKey(myDHPrivKey, theirDHPubKey *cyclic.Int, return baseKey } -type Cypher struct { - // Links +// Cypher manages the cryptographic material for E2E messages and provides +// methods to encrypt and decrypt them. +type Cypher interface { + + // GetSession return pointers to higher level management structures. + GetSession() *Session + + // Fingerprint returns the Cypher key fingerprint, if it has it. Otherwise, + // it generates and returns a new one. + Fingerprint() format.Fingerprint + + // Encrypt uses the E2E key to encrypt the message to its intended + // recipient. It also properly populates the associated data, including the + // MAC, fingerprint, and encrypted timestamp. + Encrypt(contents []byte) (ecrContents, mac []byte) + + // Decrypt uses the E2E key to decrypt the message. It returns an error in + // case of HMAC verification failure or in case of a decryption error + // (related to padding). + Decrypt(msg format.Message) ([]byte, error) + + // Use sets the key as used. It cannot be used again. + Use() +} + +// cypher adheres to the Cypher interface. +type cypher struct { session *Session fp *format.Fingerprint - // keyNum is the index of the key by order of creation - // it is used to identify the key in the key.Session + // keyNum is the index of the key by order of creation. It is used to + // identify the key in the cypher.Session. keyNum uint32 } -func newKey(session *Session, keynum uint32) *Cypher { - return &Cypher{ +func newCypher(session *Session, keyNum uint32) *cypher { + return &cypher{ session: session, - keyNum: keynum, + keyNum: keyNum, } } -// return pointers to higher level management structures -func (k *Cypher) GetSession() *Session { return k.session } +// GetSession return pointers to higher level management structures. +func (k *cypher) GetSession() *Session { return k.session } -// returns the key fingerprint if it has it, otherwise generates it -// this function does not memoize the fingerprint if it doesnt have it because -// in most cases it will not be used for a long time and as a result should not -// be stored in ram. -func (k *Cypher) Fingerprint() format.Fingerprint { +// Fingerprint returns the Cypher key fingerprint, if it has it. Otherwise, it +// generates and returns a new one. This function does not memoize the +// fingerprint if it does not have it because in most cases, it will not be used +// for a long time and as a result, should not be stored in memory. +func (k *cypher) Fingerprint() format.Fingerprint { if k.fp != nil { return *k.fp } - return e2eCrypto.DeriveKeyFingerprint(k.session.baseKey, k.keyNum, - k.session.relationshipFingerprint) + return e2eCrypto.DeriveKeyFingerprint( + k.session.baseKey, k.keyNum, k.session.relationshipFingerprint) } -// the E2E key to encrypt msg to its intended recipient -// It also properly populates the associated data, including the MAC, fingerprint, -// and encrypted timestamp -func (k *Cypher) Encrypt(contents []byte) (ecrContents, mac []byte) { +// Encrypt uses the E2E key to encrypt the message to its intended recipient. It +// also properly populates the associated data, including the MAC, fingerprint, +// and encrypted timestamp. +func (k *cypher) Encrypt(contents []byte) (ecrContents, mac []byte) { fp := k.Fingerprint() key := k.generateKey() // encrypt the payload ecrContents = e2eCrypto.Crypt(key, fp, contents) - // create the MAC - // MAC is HMAC(key, ciphertext) + // Create the MAC, which is HMAC(key, ciphertext) // Currently, the MAC doesn't include any of the associated data mac = hash.CreateHMAC(ecrContents, key[:]) return ecrContents, mac } -// Decrypt uses the E2E key to decrypt the message -// It returns an error in case of HMAC verification failure -// or in case of a decryption error (related to padding) -func (k *Cypher) Decrypt(msg format.Message) ([]byte, error) { +// Decrypt uses the E2E key to decrypt the message. It returns an error in case +// of HMAC verification failure or in case of a decryption error (related to +// padding). +func (k *cypher) Decrypt(msg format.Message) ([]byte, error) { fp := k.Fingerprint() key := k.generateKey() @@ -120,13 +144,13 @@ func (k *Cypher) Decrypt(msg format.Message) ([]byte, error) { } // Use sets the key as used. It cannot be used again. -func (k *Cypher) Use() { +func (k *cypher) Use() { k.session.useKey(k.keyNum) } -// generateKey derives the current e2e key from the baseKey and the index -// keyNum and returns it -func (k *Cypher) generateKey() e2eCrypto.Key { - return e2eCrypto.DeriveKey(k.session.baseKey, k.keyNum, - k.session.relationshipFingerprint) +// generateKey derives the current e2e key from the baseKey and the index keyNum +// and returns it. +func (k *cypher) generateKey() e2eCrypto.Key { + return e2eCrypto.DeriveKey( + k.session.baseKey, k.keyNum, k.session.relationshipFingerprint) } diff --git a/e2e/ratchet/partner/session/cypherHandler.go b/e2e/ratchet/partner/session/cypherHandler.go index 535d5ac63dd39bcfff24db572633874a87d2b02e..9bba56fec7c40ab1d0103cceb21aa0ab542d042a 100644 --- a/e2e/ratchet/partner/session/cypherHandler.go +++ b/e2e/ratchet/partner/session/cypherHandler.go @@ -1,6 +1,6 @@ package session type CypherHandler interface { - AddKey(k *Cypher) - DeleteKey(k *Cypher) + AddKey(cy Cypher) + DeleteKey(cy Cypher) } diff --git a/e2e/ratchet/partner/session/cypher_test.go b/e2e/ratchet/partner/session/cypher_test.go index f78cfb879baca57c2c9661b4bb390ff48e798df9..b6c92de98bc3dcaf1b6e18237df418ad246dbe5b 100644 --- a/e2e/ratchet/partner/session/cypher_test.go +++ b/e2e/ratchet/partner/session/cypher_test.go @@ -39,11 +39,11 @@ func TestGenerateE2ESessionBaseKey(t *testing.T) { // SIDH keys pubA := sidh.NewPublicKey(sidh.Fp434, sidh.KeyVariantSidhA) privA := sidh.NewPrivateKey(sidh.Fp434, sidh.KeyVariantSidhA) - privA.Generate(myRng) + _ = privA.Generate(myRng) privA.GeneratePublicKey(pubA) pubB := sidh.NewPublicKey(sidh.Fp434, sidh.KeyVariantSidhB) privB := sidh.NewPrivateKey(sidh.Fp434, sidh.KeyVariantSidhB) - privB.Generate(myRng) + _ = privB.Generate(myRng) privB.GeneratePublicKey(pubB) myRng.Close() @@ -60,47 +60,45 @@ func TestGenerateE2ESessionBaseKey(t *testing.T) { } -// Happy path of newKey(). -func Test_newKey(t *testing.T) { +// Happy path of newCypher. +func Test_newCypher(t *testing.T) { s, _ := makeTestSession() - expectedKey := &Cypher{ + expectedKey := &cypher{ session: s, keyNum: rand.Uint32(), } - testKey := newKey(expectedKey.session, expectedKey.keyNum) + testKey := newCypher(expectedKey.session, expectedKey.keyNum) if !reflect.DeepEqual(expectedKey, testKey) { - t.Errorf("newKey() did not produce the expected Key."+ - "\n\texpected: %v\n\treceived: %v", + t.Errorf("Unexpected new key.\nexpected: %+v\nreceived: %v", expectedKey, testKey) } } -// Happy path of Key.GetSession(). -func TestKey_GetSession(t *testing.T) { +// Happy path of cypher.GetSession. +func Test_cypher_GetSession(t *testing.T) { s, _ := makeTestSession() - k := newKey(s, rand.Uint32()) + cy := newCypher(s, rand.Uint32()) - testSession := k.GetSession() + testSession := cy.GetSession() - if !reflect.DeepEqual(k.session, testSession) { - - if !reflect.DeepEqual(k.session, testSession) { + if !reflect.DeepEqual(cy.session, testSession) { + if !reflect.DeepEqual(cy.session, testSession) { t.Errorf("GetSession() did not produce the expected Session."+ "\n\texpected: %v\n\treceived: %v", - k.session, testSession) + cy.session, testSession) } } } -// Happy path of Key.Fingerprint(). -func TestKey_Fingerprint(t *testing.T) { +// Happy path of cypher.Fingerprint. +func Test_cypher_Fingerprint(t *testing.T) { s, _ := makeTestSession() - k := newKey(s, rand.Uint32()) + cy := newCypher(s, rand.Uint32()) // Generate test and expected fingerprints testFingerprint := getFingerprint() @@ -109,13 +107,13 @@ func TestKey_Fingerprint(t *testing.T) { expectedFP format.Fingerprint }{ {testFingerprint, *testFingerprint}, - {nil, e2e.DeriveKeyFingerprint(k.session.baseKey, k.keyNum)}, + {nil, e2e.DeriveKeyFingerprint(cy.session.baseKey, cy.keyNum)}, } // Test cases for _, data := range testData { - k.fp = data.testFP - testFP := k.Fingerprint() + cy.fp = data.testFP + testFP := cy.Fingerprint() if !reflect.DeepEqual(data.expectedFP, testFP) { t.Errorf("Fingerprint() did not produce the expected Fingerprint."+ @@ -125,7 +123,7 @@ func TestKey_Fingerprint(t *testing.T) { } } -func TestKey_EncryptDecrypt(t *testing.T) { +func Test_cypher_EncryptDecrypt(t *testing.T) { const numTests = 100 @@ -134,7 +132,7 @@ func TestKey_EncryptDecrypt(t *testing.T) { prng := rand.New(rand.NewSource(42)) for i := 0; i < numTests; i++ { - // finalizeKeyNegotation the baseKey and session + // Finalize Key negotiation the baseKey and session privateKey := dh.GeneratePrivateKey(dh.DefaultPrivateKeyLength, grp, rng) publicKey := dh.GeneratePublicKey(privateKey, grp) baseKey := dh.GenerateSessionKey(privateKey, publicKey, grp) @@ -143,30 +141,30 @@ func TestKey_EncryptDecrypt(t *testing.T) { baseKey: baseKey, } - //create the keys - k := newKey(s, prng.Uint32()) + // Create the cypher + cy := newCypher(s, prng.Uint32()) - //make the message to be encrypted + // Make the message to be encrypted msg := format.NewMessage(grp.GetP().ByteLen()) - //set the contents + // Set the contents contents := make([]byte, msg.ContentsSize()) prng.Read(contents) msg.SetContents(contents) // Encrypt - contentsEnc, mac := k.Encrypt(msg.GetContents()) + contentsEnc, mac := cy.Encrypt(msg.GetContents()) - //make the encrypted message + // Make the encrypted message ecrMsg := format.NewMessage(grp.GetP().ByteLen()) - ecrMsg.SetKeyFP(k.Fingerprint()) + ecrMsg.SetKeyFP(cy.Fingerprint()) ecrMsg.SetContents(contentsEnc) ecrMsg.SetMac(mac) // Decrypt - contentsDecr, err := k.Decrypt(ecrMsg) + contentsDecr, err := cy.Decrypt(ecrMsg) if err != nil { - t.Fatalf("Decrypt error: %v", err) + t.Fatalf("Decrypt error: %+v", err) } if !bytes.Equal(contentsDecr, msg.GetContents()) { @@ -176,35 +174,34 @@ func TestKey_EncryptDecrypt(t *testing.T) { } } -// Happy path of Key.Use() -func TestKey_denoteUse(t *testing.T) { +// Happy path of cypher.Use. +func Test_cypher_Use(t *testing.T) { s, _ := makeTestSession() keyNum := uint32(rand.Int31n(31)) - k := newKey(s, keyNum) + k := newCypher(s, keyNum) k.Use() if !k.session.keyState.Used(keyNum) { - t.Errorf("Use() did not use the key") + t.Errorf("Use did not use the key") } } -// Happy path of generateKey(). -func TestKey_generateKey(t *testing.T) { +// Happy path of cypher.generateKey. +func Test_cypher_generateKey(t *testing.T) { s, _ := makeTestSession() - k := newKey(s, rand.Uint32()) + k := newCypher(s, rand.Uint32()) // Generate test CryptoType values and expected keys expectedKey := e2e.DeriveKey(k.session.baseKey, k.keyNum) testKey := k.generateKey() if !reflect.DeepEqual(expectedKey, testKey) { - t.Errorf("generateKey() did not produce the expected e2e key."+ - "\n\texpected: %v\n\treceived: %v", - expectedKey, testKey) + t.Errorf("generateKey did not produce the expected e2e key."+ + "\nexpected: %v\nreceived: %v", expectedKey, testKey) } } diff --git a/e2e/ratchet/partner/session/session.go b/e2e/ratchet/partner/session/session.go index 234c8b357a58f8bb5b328a49e631fc41effe44f6..58d1ca8ac4ca71d2adbc3f0eaf554e1507dfee0c 100644 --- a/e2e/ratchet/partner/session/session.go +++ b/e2e/ratchet/partner/session/session.go @@ -327,7 +327,7 @@ func (s *Session) GetPartner() *id.ID { // 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) { +func (s *Session) PopKey() (Cypher, error) { if s.keyState.GetNumAvailable() <= uint32(s.e2eParams.NumRekeys) { return nil, errors.New("no more keys left, remaining reserved " + "for rekey") @@ -337,18 +337,18 @@ func (s *Session) PopKey() (*Cypher, error) { return nil, err } - return newKey(s, keyNum), nil + return newCypher(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) { +func (s *Session) PopReKey() (Cypher, error) { keyNum, err := s.keyState.Next() if err != nil { return nil, err } - return newKey(s, keyNum), nil + return newCypher(s, keyNum), nil } // todo - doscstring @@ -600,12 +600,12 @@ func (s *Session) buildChildKeys() { } //returns key objects for all unused keys -func (s *Session) getUnusedKeys() []*Cypher { +func (s *Session) getUnusedKeys() []Cypher { keyNums := s.keyState.GetUnusedKeyNums() - keys := make([]*Cypher, len(keyNums)) + keys := make([]Cypher, len(keyNums)) for i, keyNum := range keyNums { - keys[i] = newKey(s, keyNum) + keys[i] = newCypher(s, keyNum) } return keys diff --git a/e2e/ratchet/partner/session/session_test.go b/e2e/ratchet/partner/session/session_test.go index c22fdc582fcebf1a29b021bdafc6ed8b5cebd127..cd72eee94c90ab559435e8dded5ecc0dbf346094 100644 --- a/e2e/ratchet/partner/session/session_test.go +++ b/e2e/ratchet/partner/session/session_test.go @@ -172,10 +172,12 @@ func TestSession_Serialization(t *testing.T) { // PopKey should return a new key from this session func TestSession_PopKey(t *testing.T) { s, _ := makeTestSession() - key, err := s.PopKey() + keyInterface, err := s.PopKey() if err != nil { t.Fatal(err) } + key := keyInterface.(*cypher) + if key == nil { t.Error("PopKey should have returned non-nil key") } @@ -232,10 +234,12 @@ func TestSession_PopKey_Error(t *testing.T) { // There's no boundary, except for the number of keyNums in the state vector func TestSession_PopReKey(t *testing.T) { s, _ := makeTestSession() - key, err := s.PopReKey() + keyInterface, err := s.PopReKey() if err != nil { t.Fatal("PopKey should have returned an error") } + key := keyInterface.(*cypher) + if key == nil { t.Error("Key should be non-nil") } diff --git a/e2e/ratchet/partner/session/testUtils.go b/e2e/ratchet/partner/session/testUtils.go index 2a5abb32725b180bd9300e98c0cb78946dddc90d..61d82ea1f651820f0a9d76bcec396f094c820d4e 100644 --- a/e2e/ratchet/partner/session/testUtils.go +++ b/e2e/ratchet/partner/session/testUtils.go @@ -97,7 +97,7 @@ func makeTestSession() (*Session, *versioned.KV) { rekeyThreshold: 5, partner: &id.ID{}, grp: grp, - cyHandler: mockCyHandler{}, + cyHandler: &mockCyHandler{}, rng: fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG), } var err error @@ -147,13 +147,7 @@ func cmpSerializedFields(a *Session, b *Session) error { return nil } -type mockCyHandler struct { -} - -func (m mockCyHandler) AddKey(k *Cypher) { - return -} +type mockCyHandler struct{} -func (m mockCyHandler) DeleteKey(k *Cypher) { - return -} +func (m *mockCyHandler) AddKey(Cypher) {} +func (m *mockCyHandler) DeleteKey(Cypher) {} diff --git a/e2e/ratchet/partner/utils.go b/e2e/ratchet/partner/utils.go index 0bfc02fbc28338f628d967f2f092a32c7deb951b..aa4c7d90e267848c50d53ce32ae87c63cb6febc9 100644 --- a/e2e/ratchet/partner/utils.go +++ b/e2e/ratchet/partner/utils.go @@ -73,11 +73,11 @@ func (p *testManager) Contact() contact.Contact { panic("implement me") } -func (p *testManager) PopSendCypher() (*session.Cypher, error) { +func (p *testManager) PopSendCypher() (session.Cypher, error) { panic("implement me") } -func (p *testManager) PopRekeyCypher() (*session.Cypher, error) { +func (p *testManager) PopRekeyCypher() (session.Cypher, error) { panic("implement me") } diff --git a/e2e/ratchet/partner/utils_test.go b/e2e/ratchet/partner/utils_test.go index d75ae5822658eb161c3887ca457b6eaa89f47257..17d28200b7c79868c7ca3f156b2d8f897b635e02 100644 --- a/e2e/ratchet/partner/utils_test.go +++ b/e2e/ratchet/partner/utils_test.go @@ -21,13 +21,9 @@ import ( type mockCyHandler struct { } -func (m mockCyHandler) AddKey(k *session.Cypher) { - return -} +func (m mockCyHandler) AddKey(session.Cypher) {} -func (m mockCyHandler) DeleteKey(k *session.Cypher) { - return -} +func (m mockCyHandler) DeleteKey(session.Cypher) {} func getGroup() *cyclic.Group { e2eGrp := cyclic.NewGroup( @@ -65,11 +61,17 @@ func newTestManager(t *testing.T) (manager, *versioned.KV) { partnerSIDHPrivKey := util.NewSIDHPrivateKey(sidh.KeyVariantSidhA) partnerSIDHPubKey := util.NewSIDHPublicKey(sidh.KeyVariantSidhA) - partnerSIDHPrivKey.Generate(rng.GetStream()) + err := partnerSIDHPrivKey.Generate(rng.GetStream()) + if err != nil { + t.Errorf("Failed to generate private key: %+v", err) + } partnerSIDHPrivKey.GeneratePublicKey(partnerSIDHPubKey) mySIDHPrivKey := util.NewSIDHPrivateKey(sidh.KeyVariantSidhB) mySIDHPubKey := util.NewSIDHPublicKey(sidh.KeyVariantSidhB) - mySIDHPrivKey.Generate(rng.GetStream()) + err = mySIDHPrivKey.Generate(rng.GetStream()) + if err != nil { + t.Errorf("Failed to generate private key: %+v", err) + } mySIDHPrivKey.GeneratePublicKey(mySIDHPubKey) kv := versioned.NewKV(ekv.MakeMemstore()) diff --git a/e2e/ratchet/ratchet.go b/e2e/ratchet/ratchet.go index 92ff4ba4250d23138c5559bd90cc4e5a584080ce..1a272b9a942c63d6fe88a492040eda68da19180c 100644 --- a/e2e/ratchet/ratchet.go +++ b/e2e/ratchet/ratchet.go @@ -44,18 +44,18 @@ type Ratchet struct { cyHandler session.CypherHandler rng *fastRNG.StreamGenerator - //services handler + // services handler services map[string]message.Processor - sInteface Services - servicesmux sync.RWMutex + sInterface Services + servicesMux sync.RWMutex kv *versioned.KV } -// New creates a new store for the passed user id and private key. +// New creates a new store for the passed user ID and private key. // The store can then be accessed by calling LoadStore. // Does not create at a unique prefix, if multiple Ratchets are needed, make -// sure to add a uint prefix to the KV before instantiation. +// sure to add an uint prefix to the KV before instantiation. func New(kv *versioned.KV, myID *id.ID, privKey *cyclic.Int, grp *cyclic.Group) error { @@ -125,7 +125,7 @@ func (r *Ratchet) AddPartner(partnerID *id.ID, partnerID, err) } - //add services for the manager + // Add services for the manager r.add(m) return m, nil @@ -158,7 +158,7 @@ func (r *Ratchet) DeletePartner(partnerID *id.ID) error { partnerID) } - //delete services + // Delete services r.delete(m) delete(r.managers, *partnerID) diff --git a/e2e/ratchet/ratchet_test.go b/e2e/ratchet/ratchet_test.go index f510f74a11173502a9d22e7e485c74da9603c6f8..a1cdbcb881b893dd5ebb4ce470078236e62b9f40 100644 --- a/e2e/ratchet/ratchet_test.go +++ b/e2e/ratchet/ratchet_test.go @@ -68,7 +68,7 @@ func TestLoadStore(t *testing.T) { } store, err := Load(kv, &id.ID{}, - expectedRatchet.grp, expectedRatchet.cyHandler, expectedRatchet.sInteface, + expectedRatchet.grp, expectedRatchet.cyHandler, expectedRatchet.sInterface, expectedRatchet.rng) if err != nil { t.Errorf("LoadStore() produced an error: %v", err) diff --git a/e2e/ratchet/serviceList.go b/e2e/ratchet/serviceList.go index 0bd23650b751e167b92a7eb608b92d2a4b0c6483..b382b5aced663ae8384b497052c29d57eb0ba71e 100644 --- a/e2e/ratchet/serviceList.go +++ b/e2e/ratchet/serviceList.go @@ -9,31 +9,31 @@ import ( // Services is a subsection of the cmix.Manager interface used for services type Services interface { - AddService(AddService *id.ID, newService message.Service, - response message.Processor) - DeleteService(clientID *id.ID, toDelete message.Service, - processor message.Processor) + AddService( + clientID *id.ID, newService message.Service, response message.Processor) + DeleteService( + clientID *id.ID, toDelete message.Service, processor message.Processor) } func (r *Ratchet) add(m partner.Manager) { - r.servicesmux.RLock() - defer r.servicesmux.RUnlock() + r.servicesMux.RLock() + defer r.servicesMux.RUnlock() for tag, process := range r.services { - r.sInteface.AddService(r.myID, m.MakeService(tag), process) + r.sInterface.AddService(r.myID, m.MakeService(tag), process) } } func (r *Ratchet) delete(m partner.Manager) { - r.servicesmux.RLock() - defer r.servicesmux.RUnlock() + r.servicesMux.RLock() + defer r.servicesMux.RUnlock() for tag, process := range r.services { - r.sInteface.DeleteService(r.myID, m.MakeService(tag), process) + r.sInterface.DeleteService(r.myID, m.MakeService(tag), process) } } func (r *Ratchet) AddService(tag string, processor message.Processor) error { - r.servicesmux.Lock() - defer r.servicesmux.Unlock() + r.servicesMux.Lock() + defer r.servicesMux.Unlock() //add the services to the list if _, exists := r.services[tag]; exists { return errors.Errorf("Cannot add more than one service '%s'", tag) @@ -42,15 +42,15 @@ 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.sInterface.AddService(r.myID, m.MakeService(tag), processor) } return nil } func (r *Ratchet) RemoveService(tag string) error { - r.servicesmux.Lock() - defer r.servicesmux.Unlock() + r.servicesMux.Lock() + defer r.servicesMux.Unlock() oldServiceProcess, exists := r.services[tag] if !exists { @@ -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.sInterface.DeleteService(r.myID, m.MakeService(tag), oldServiceProcess) } return nil diff --git a/e2e/ratchet/storage.go b/e2e/ratchet/storage.go index 146a51752419746c070eda7fd640e734899f3d79..999938dc7ec1784743163acfd34a550a24134544 100644 --- a/e2e/ratchet/storage.go +++ b/e2e/ratchet/storage.go @@ -49,10 +49,10 @@ func Load(kv *versioned.KV, myID *id.ID, grp *cyclic.Group, kv: kv, - cyHandler: cyHandler, - grp: grp, - rng: rng, - sInteface: services, + cyHandler: cyHandler, + grp: grp, + rng: rng, + sInterface: services, } obj, err := kv.Get(storeKey, currentStoreVersion) diff --git a/e2e/ratchet/utils_test.go b/e2e/ratchet/utils_test.go index 073dc315b6c5f42991f3d72d4cbe6f56581e7a33..0a0cc267f842f98cd43c568a139b63feb3808f8b 100644 --- a/e2e/ratchet/utils_test.go +++ b/e2e/ratchet/utils_test.go @@ -3,7 +3,6 @@ package ratchet import ( "io" "reflect" - "strings" "testing" "github.com/cloudflare/circl/dh/sidh" @@ -52,7 +51,7 @@ func managersEqual(expected, received partner.Manager, t *testing.T) bool { equal = false } - if !strings.EqualFold(expected.ConnectionFingerprint(), received.ConnectionFingerprint()) { + if !reflect.DeepEqual(expected.ConnectionFingerprint(), received.ConnectionFingerprint()) { t.Errorf("Did not Receive expected Manager.Receive."+ "\n\texpected: %+v\n\treceived: %+v", expected.ConnectionFingerprint(), received.ConnectionFingerprint()) @@ -96,11 +95,11 @@ func genSidhKeys(rng io.Reader, variant sidh.KeyVariant) (*sidh.PrivateKey, *sid type mockCyHandler struct{} -func (m mockCyHandler) AddKey(k *session.Cypher) { +func (m mockCyHandler) AddKey(k session.Cypher) { return } -func (m mockCyHandler) DeleteKey(k *session.Cypher) { +func (m mockCyHandler) DeleteKey(k session.Cypher) { return } diff --git a/e2e/rekey/utils_test.go b/e2e/rekey/utils_test.go index c7296ecd26fe92a207e39cf43140805c4af6710c..e30b22c255aa47c36dd3cb917c2c34a7a84219ef 100644 --- a/e2e/rekey/utils_test.go +++ b/e2e/rekey/utils_test.go @@ -199,16 +199,10 @@ func (mci *mockCommsInstance) GetRoundEvents() *ds.RoundEvents { return mci.RoundEvents } -type mockCyHandler struct { -} +type mockCyHandler struct{} -func (m mockCyHandler) AddKey(k *session2.Cypher) { - return -} - -func (m mockCyHandler) DeleteKey(k *session2.Cypher) { - return -} +func (m mockCyHandler) AddKey(session2.Cypher) {} +func (m mockCyHandler) DeleteKey(session2.Cypher) {} type mockServiceHandler struct { } @@ -224,6 +218,11 @@ func (m mockServiceHandler) DeleteService(clientID *id.ID, toDelete message.Serv type mockNetManager struct{} +func (m *mockNetManager) Connect(ndf *ndf.NetworkDefinition) error { + // TODO implement me + panic("implement me") +} + func (m *mockNetManager) GetIdentity(get *id.ID) (identity.TrackedID, error) { //TODO implement me panic("implement me") diff --git a/e2e/sendE2E.go b/e2e/sendE2E.go index e2f6ddeeb40fe4e1df555d69129674c0022f3865..2a50201de5e20d75eb41602f194d58e99a0d6af1 100644 --- a/e2e/sendE2E.go +++ b/e2e/sendE2E.go @@ -19,70 +19,66 @@ import ( ) func (m *manager) SendE2E(mt catalog.MessageType, recipient *id.ID, - payload []byte, params Params) ([]id.Round, e2e.MessageID, - time.Time, error) { + payload []byte, params Params) ([]id.Round, e2e.MessageID, time.Time, error) { if !m.net.IsHealthy() { - return nil, e2e.MessageID{}, time.Time{}, errors.New("cannot " + - "sendE2E when network is not healthy") + return nil, e2e.MessageID{}, time.Time{}, + errors.New("cannot sendE2E when network is not healthy") } handleCritical := params.Critical if handleCritical { m.crit.AddProcessing(mt, recipient, payload, params) - // set critical to false so the network layer doesnt - // make the messages critical as well + // Set critical to false so that the network layer does not make the + // messages critical as well params.Critical = false } - rnds, msgID, t, err := m.sendE2E(mt, recipient, payload, params) + rounds, msgID, t, err := m.sendE2E(mt, recipient, payload, params) if handleCritical { - m.crit.handle(mt, recipient, payload, rnds, err) + m.crit.handle(mt, recipient, payload, rounds, err) } - return rnds, msgID, t, err + return rounds, msgID, t, err } func (m *manager) sendE2E(mt catalog.MessageType, recipient *id.ID, - payload []byte, params Params) ([]id.Round, e2e.MessageID, - time.Time, error) { + payload []byte, params Params) ([]id.Round, e2e.MessageID, time.Time, error) { ts := netTime.Now() partitions, internalMsgId, err := m.partitioner.Partition(recipient, mt, ts, payload) if err != nil { - err = errors.WithMessage(err, "failed to send unsafe message") - return nil, e2e.MessageID{}, time.Time{}, err + return nil, e2e.MessageID{}, time.Time{}, + errors.WithMessage(err, "failed to send unsafe message") } - jww.INFO.Printf("E2E sending %d messages to %s", - len(partitions), recipient) + jww.INFO.Printf("E2E sending %d messages to %s", len(partitions), recipient) - // When sending E2E messages, we first partition into cMix packets and - // then send each partition over cMix. + // When sending E2E messages, we first partition into cMix packets and then + // send each partition over cMix roundIds := make([]id.Round, len(partitions)) errCh := make(chan error, len(partitions)) - // The Key manager for the partner (recipient) ensures single - // use of each key negotiated for the ratchet. + // The Key manager for the partner (recipient) ensures single use of each + // key negotiated for the ratchet partner, err := m.Ratchet.GetPartner(recipient) if err != nil { - err = errors.WithMessagef(err, "cannot send E2E message "+ - "no relationship found with %s", recipient) - return nil, e2e.MessageID{}, time.Time{}, err + return nil, e2e.MessageID{}, time.Time{}, errors.WithMessagef(err, + "cannot send E2E message no relationship found with %s", recipient) } - msgID := e2e.NewMessageID(partner.SendRelationshipFingerprint(), - internalMsgId) + msgID := e2e.NewMessageID( + partner.SendRelationshipFingerprint(), internalMsgId) wg := sync.WaitGroup{} for i, p := range partitions { if mt != catalog.KeyExchangeTrigger { - // check if any rekeys need to happen and trigger them - rekeySendFunc := func(mt catalog.MessageType, recipient *id.ID, payload []byte, - cmixParams cmix.CMIXParams) ( + // Check if any rekeys need to happen and trigger them + rekeySendFunc := func(mt catalog.MessageType, recipient *id.ID, + payload []byte, cmixParams cmix.CMIXParams) ( []id.Round, e2e.MessageID, time.Time, error) { par := GetDefaultParams() par.CMIXParams = cmixParams @@ -92,32 +88,30 @@ func (m *manager) sendE2E(mt catalog.MessageType, recipient *id.ID, m.events, partner, m.rekeyParams, 1*time.Minute) } - var keyGetter func() (*session.Cypher, error) + var keyGetter func() (session.Cypher, error) if params.Rekey { keyGetter = partner.PopRekeyCypher } else { keyGetter = partner.PopSendCypher } - // FIXME: remove this wait, it is weird. Why is it - // here? we cant remember. - key, err := waitForKey(keyGetter, params.KeyGetRetryCount, - params.KeyGeRetryDelay, params.Stop, recipient, - format.DigestContents(p), i) + // FIXME: remove this wait, it is weird. Why is it here? we cant remember. + key, err := waitForKey( + keyGetter, params.KeyGetRetryCount, params.KeyGeRetryDelay, + params.Stop, recipient, format.DigestContents(p), i) if err != nil { - err = errors.WithMessagef(err, - "Failed to get key for end to end encryption") - return nil, e2e.MessageID{}, time.Time{}, err + return nil, e2e.MessageID{}, time.Time{}, errors.WithMessagef(err, + "Failed to get key for end-to-end encryption") } - // This does not encrypt for cMix but - // instead end to end encrypts the cmix message + // This does not encrypt for cMix but instead end-to-end encrypts the + // cMix message contentsEnc, mac := key.Encrypt(p) - jww.INFO.Printf("E2E sending %d/%d to %s with key fp: "+ - "%s, msgID: %s (msgDigest %s)", - i+i, len(partitions), recipient, - key.Fingerprint(), msgID, format.DigestContents(p)) + jww.INFO.Printf( + "E2E sending %d/%d to %s with key fp: %s, msgID: %s (msgDigest %s)", + i+i, len(partitions), recipient, key.Fingerprint(), msgID, + format.DigestContents(p)) var s message.Service if i == len(partitions)-1 { @@ -126,14 +120,13 @@ func (m *manager) sendE2E(mt catalog.MessageType, recipient *id.ID, s = partner.MakeService(params.ServiceTag) } - // We send each partition in it's own thread here, some - // may send in round X, others in X+1 or X+2, and so on. + // We send each partition in its own thread here; some may send in round + // X, others in X+1 or X+2, and so on wg.Add(1) go func(i int) { var err error roundIds[i], _, err = m.net.Send(recipient, - key.Fingerprint(), s, contentsEnc, mac, - params.CMIXParams) + key.Fingerprint(), s, contentsEnc, mac, params.CMIXParams) if err != nil { errCh <- err } @@ -147,24 +140,25 @@ func (m *manager) sendE2E(mt catalog.MessageType, recipient *id.ID, if numFail > 0 { jww.INFO.Printf("Failed to E2E send %d/%d to %s", numFail, len(partitions), recipient) - err = errors.Errorf("Failed to E2E send %v/%v sub payloads:"+ - " %s", numFail, len(partitions), errRtn) - return nil, e2e.MessageID{}, time.Time{}, err + return nil, e2e.MessageID{}, time.Time{}, errors.Errorf( + "Failed to E2E send %v/%v sub payloads: %s", + numFail, len(partitions), errRtn) } else { jww.INFO.Printf("Successfully E2E sent %d/%d to %s", len(partitions)-numFail, len(partitions), recipient) } - jww.INFO.Printf("Successful E2E Send of %d messages to %s with msgID "+ - "%s", len(partitions), recipient, msgID) + jww.INFO.Printf("Successful E2E Send of %d messages to %s with msgID %s", + len(partitions), recipient, msgID) + return roundIds, msgID, ts, nil } // waitForKey waits the designated amount of time for a key to become available // with the partner. -func waitForKey(keyGetter func() (*session.Cypher, error), numAttempts uint, - wait time.Duration, stop *stoppable.Single, recipient *id.ID, - digest string, partition int) (*session.Cypher, error) { +func waitForKey(keyGetter func() (session.Cypher, error), numAttempts uint, + wait time.Duration, stop *stoppable.Single, recipient *id.ID, digest string, + partition int) (session.Cypher, error) { key, err := keyGetter() if err == nil { return key, nil @@ -173,13 +167,11 @@ func waitForKey(keyGetter func() (*session.Cypher, error), numAttempts uint, ticker := time.NewTicker(wait) defer ticker.Stop() - for keyTries := uint(1); err != nil && - keyTries < numAttempts; keyTries++ { - jww.WARN.Printf("Out of sending keys for %s "+ - "(digest: %s, partition: %d), this can "+ - "happen when sending messages faster than "+ - "the client can negotiate keys. Please "+ - "adjust your e2e key parameters", + for keyTries := uint(1); err != nil && keyTries < numAttempts; keyTries++ { + jww.WARN.Printf( + "Out of sending keys for %s (digest: %s, partition: %d), this can "+ + "happen when sending messages faster than the client can "+ + "negotiate keys. Please adjust your e2e key parameters.", recipient, digest, partition) select { @@ -194,19 +186,16 @@ func waitForKey(keyGetter func() (*session.Cypher, error), numAttempts uint, return key, err } -// getSendErrors returns any errors on the error channel -func getSendErrors(c chan error) (int, string) { - var errRtn string - numFail := 0 - done := false - for !done { +// getSendErrors returns a string of all error received on the error channel and +// a count of the number of errors. +func getSendErrors(c chan error) (numFail int, errRtn string) { + for { select { case err := <-c: errRtn += err.Error() numFail++ default: - done = true + return numFail, errRtn } } - return numFail, errRtn } diff --git a/e2e/sendE2E_test.go b/e2e/sendE2E_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d316b90c79a388ac32c405542e49d882089138d1 --- /dev/null +++ b/e2e/sendE2E_test.go @@ -0,0 +1,256 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +//////////////////////////////////////////////////////////////////////////////// + +package e2e + +import ( + "bytes" + "github.com/cloudflare/circl/dh/sidh" + "github.com/pkg/errors" + "gitlab.com/elixxir/client/catalog" + "gitlab.com/elixxir/client/e2e/parse" + "gitlab.com/elixxir/client/e2e/ratchet" + "gitlab.com/elixxir/client/e2e/ratchet/partner/session" + "gitlab.com/elixxir/client/e2e/receive" + "gitlab.com/elixxir/client/e2e/rekey" + "gitlab.com/elixxir/client/stoppable" + util "gitlab.com/elixxir/client/storage/utility" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/crypto/cyclic" + dh "gitlab.com/elixxir/crypto/diffieHellman" + "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/elixxir/ekv" + "gitlab.com/elixxir/primitives/format" + "gitlab.com/xx_network/crypto/csprng" + "gitlab.com/xx_network/primitives/id" + "io" + "testing" + "time" +) + +func Test_manager_SendE2E_Smoke(t *testing.T) { + streamGen := fastRNG.NewStreamGenerator(12, 1024, csprng.NewSystemRNG) + rng := streamGen.GetStream() + defer rng.Close() + netHandler := newMockCmixHandler() + + // Generate new E2E manager + myKv := versioned.NewKV(ekv.MakeMemstore()) + myID := id.NewIdFromString("myID", id.User, t) + myNet := newMockCmix(myID, netHandler, t) + m1 := &manager{ + Switchboard: receive.New(), + partitioner: parse.NewPartitioner(myKv, myNet.GetMaxMessageLength()), + net: myNet, + myID: myID, + events: mockEventsManager{}, + grp: myNet.GetInstance().GetE2EGroup(), + rekeyParams: rekey.GetDefaultParams(), + } + + myPrivKey := dh.GeneratePrivateKey( + dh.DefaultPrivateKeyLength, m1.grp, rng) + err := ratchet.New(myKv, myID, myPrivKey, m1.grp) + if err != nil { + t.Errorf("Failed to generate new ratchet: %+v", err) + } + + myFpGen := &fpGenerator{m1} + myServices := newMockServices() + + m1.Ratchet, err = ratchet.Load( + myKv, myID, m1.grp, myFpGen, myServices, streamGen) + + // Generate new E2E manager + partnerKv := versioned.NewKV(ekv.MakeMemstore()) + partnerID := id.NewIdFromString("partnerID", id.User, t) + partnerNet := newMockCmix(partnerID, netHandler, t) + m2 := &manager{ + Switchboard: receive.New(), + partitioner: parse.NewPartitioner(partnerKv, partnerNet.GetMaxMessageLength()), + net: partnerNet, + myID: partnerID, + events: mockEventsManager{}, + grp: partnerNet.GetInstance().GetE2EGroup(), + rekeyParams: rekey.GetDefaultParams(), + } + + receiveChan := make(chan receive.Message, 10) + m2.Switchboard.RegisterListener(partnerID, catalog.NoType, &mockListener{receiveChan}) + + partnerPrivKey := dh.GeneratePrivateKey( + dh.DefaultPrivateKeyLength, m2.grp, rng) + err = ratchet.New(partnerKv, partnerID, partnerPrivKey, m2.grp) + if err != nil { + t.Errorf("Failed to generate new ratchet: %+v", err) + } + + partnerFpGen := &fpGenerator{m2} + partnerServices := newMockServices() + + m1.Ratchet, err = ratchet.Load( + partnerKv, partnerID, m2.grp, partnerFpGen, partnerServices, streamGen) + + // Generate partner identity and add partner + partnerPubKey, partnerSidhPubKey, mySidhPrivKey, sessionParams := + genPartnerKeys(partnerPrivKey, m1.grp, rng, t) + _, err = m1.Ratchet.AddPartner(partnerID, partnerPubKey, myPrivKey, + partnerSidhPubKey, mySidhPrivKey, sessionParams, sessionParams) + if err != nil { + t.Errorf("Failed to add partner: %+v", err) + } + + payload := []byte("My Payload") + p := GetDefaultParams() + _, _, _, err = m1.SendE2E(catalog.NoType, partnerID, payload, p) + if err != nil { + t.Errorf("SendE2E failed: %+v", err) + } + + select { + case r := <-receiveChan: + if !bytes.Equal(payload, r.Payload) { + t.Errorf("Received payload does not match sent payload."+ + "\nexpected: %q\nreceived: %q", payload, r.Payload) + } + case <-time.After(305 * time.Millisecond): + t.Errorf("Timed out waiting for E2E message.") + } +} + +// genPartnerKeys generates the keys needed to add a partner. +func genPartnerKeys(partnerPrivKey *cyclic.Int, grp *cyclic.Group, + rng io.Reader, t testing.TB) ( + partnerPubKey *cyclic.Int, partnerSidhPubKey *sidh.PublicKey, + mySidhPrivKey *sidh.PrivateKey, params session.Params) { + + partnerPubKey = dh.GeneratePublicKey(partnerPrivKey, grp) + + partnerSidhPrivKey := util.NewSIDHPrivateKey(sidh.KeyVariantSidhA) + partnerSidhPubKey = util.NewSIDHPublicKey(sidh.KeyVariantSidhA) + err := partnerSidhPrivKey.Generate(rng) + if err != nil { + t.Fatalf("Failed to generate partner SIDH private key: %+v", err) + } + partnerSidhPrivKey.GeneratePublicKey(partnerSidhPubKey) + + mySidhPrivKey = util.NewSIDHPrivateKey(sidh.KeyVariantSidhB) + mySidhPubKey := util.NewSIDHPublicKey(sidh.KeyVariantSidhB) + err = mySidhPrivKey.Generate(rng) + if err != nil { + t.Fatalf("Failed to generate my SIDH private key: %+v", err) + } + mySidhPrivKey.GeneratePublicKey(mySidhPubKey) + + params = session.GetDefaultParams() + + return partnerPubKey, partnerSidhPubKey, mySidhPrivKey, params +} + +// Tests that waitForKey returns a key after it waits 5 times. +func Test_waitForKey(t *testing.T) { + wait := 15 * time.Millisecond + numAttempts := uint(10) + expectedCypher := &mockWaitForKeyCypher{5} + var attempt uint + keyGetter := func() (session.Cypher, error) { + if attempt >= (numAttempts / 2) { + return expectedCypher, nil + } + attempt++ + return nil, errors.New("Failed to get key.") + } + stop := stoppable.NewSingle("Test_waitForKey") + + c, err := waitForKey(keyGetter, numAttempts, wait, stop, &id.ID{}, "", 0) + if err != nil { + t.Errorf("waitForKey returned an error: %+v", err) + } + + if *c.(*mockWaitForKeyCypher) != *expectedCypher { + t.Errorf("Received unexpected cypher.\nexpected: %#v\nreceived: %#v", + *expectedCypher, *c.(*mockWaitForKeyCypher)) + } +} + +// Error path: tests that waitForKey returns an error after the key getter does +// not return any keys after all attempts +func Test_waitForKey_NoKeyError(t *testing.T) { + expectedErr := "Failed to get key." + keyGetter := func() (session.Cypher, error) { + return nil, errors.New(expectedErr) + } + stop := stoppable.NewSingle("Test_waitForKey") + + _, err := waitForKey(keyGetter, 10, 1, stop, &id.ID{}, "", 0) + if err == nil || err.Error() != expectedErr { + t.Errorf("waitForKey did not return the expected error when no key "+ + "is available.\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// Error path: tests that waitForKey returns an error after the stoppable is +// triggered. +func Test_waitForKey_StopError(t *testing.T) { + expectedErr := "Stopped by stopper" + keyGetter := func() (session.Cypher, error) { + return nil, errors.New("Failed to get key.") + } + stop := stoppable.NewSingle("Test_waitForKey") + + go func() { + _, err := waitForKey(keyGetter, 10, 1, stop, &id.ID{}, "", 0) + if err == nil || err.Error() != expectedErr { + t.Errorf("waitForKey did not return the expected error when the "+ + "stoppable was triggered.\nexpected: %s\nreceived: %+v", + expectedErr, err) + } + }() + + err := stop.Close() + if err != nil { + t.Errorf("Failed to stop stoppable: %+v", err) + } +} + +type mockWaitForKeyCypher struct { + cypherNum int +} + +func (m *mockWaitForKeyCypher) GetSession() *session.Session { return nil } +func (m *mockWaitForKeyCypher) Fingerprint() format.Fingerprint { return format.Fingerprint{} } +func (m *mockWaitForKeyCypher) Encrypt([]byte) ([]byte, []byte) { return nil, nil } +func (m *mockWaitForKeyCypher) Decrypt(format.Message) ([]byte, error) { return nil, nil } +func (m *mockWaitForKeyCypher) Use() {} + +// Tests that getSendErrors returns all the errors on the channel. +func Test_getSendErrors(t *testing.T) { + const n = 10 + var expectedErrors string + errorList := make([]error, n) + for i := range errorList { + errorList[i] = errors.Errorf("Error %d of %d", i, n) + expectedErrors += errorList[i].Error() + } + + c := make(chan error, n*2) + for _, e := range errorList { + c <- e + } + + numFail, errRtn := getSendErrors(c) + + if numFail != n { + t.Errorf("Incorrect number of failed.\nexpected: %d\nreceived: %d", + n, numFail) + } + + if errRtn != expectedErrors { + t.Errorf("Received incorrect errors.\nexpected: %q\nreceived: %q", + expectedErrors, errRtn) + } +} diff --git a/e2e/utils_test.go b/e2e/utils_test.go index 2dbd52c26f4cdbf222df26721defbff1604cc3bb..32a3d986b7a6c462d2636e79d73524356d574cab 100644 --- a/e2e/utils_test.go +++ b/e2e/utils_test.go @@ -2,11 +2,14 @@ package e2e import ( "bytes" + "github.com/pkg/errors" "gitlab.com/elixxir/client/cmix" "gitlab.com/elixxir/client/cmix/gateway" "gitlab.com/elixxir/client/cmix/identity" + "gitlab.com/elixxir/client/cmix/identity/receptionID" "gitlab.com/elixxir/client/cmix/message" "gitlab.com/elixxir/client/cmix/rounds" + "gitlab.com/elixxir/client/e2e/receive" "gitlab.com/elixxir/client/stoppable" "gitlab.com/elixxir/comms/network" "gitlab.com/elixxir/primitives/format" @@ -16,52 +19,11 @@ import ( "gitlab.com/xx_network/primitives/ndf" "gitlab.com/xx_network/primitives/netTime" "math/rand" + "sync" "testing" "time" ) -func getNDF() *ndf.NetworkDefinition { - return &ndf.NetworkDefinition{ - E2E: ndf.Group{ - Prime: "E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B7A" + - "8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3D" + - "D2AEDF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E78615" + - "75E745D31F8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC" + - "6ADC718DD2A3E041023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C" + - "4A530E8FFB1BC51DADDF453B0B2717C2BC6669ED76B4BDD5C9FF558E88F2" + - "6E5785302BEDBCA23EAC5ACE92096EE8A60642FB61E8F3D24990B8CB12EE" + - "448EEF78E184C7242DD161C7738F32BF29A841698978825B4111B4BC3E1E" + - "198455095958333D776D8B2BEEED3A1A1A221A6E37E664A64B83981C46FF" + - "DDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F278DE8014A47323" + - "631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696015CB79C" + - "3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E63" + - "19BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC3" + - "5873847AEF49F66E43873", - Generator: "2", - }, - CMIX: ndf.Group{ - Prime: "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642" + - "F0B5C48C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757" + - "264E5A1A44FFE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F" + - "9716BFE6117C6B5B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091E" + - "B51743BF33050C38DE235567E1B34C3D6A5C0CEAA1A0F368213C3D19843D" + - "0B4B09DCB9FC72D39C8DE41F1BF14D4BB4563CA28371621CAD3324B6A2D3" + - "92145BEBFAC748805236F5CA2FE92B871CD8F9C36D3292B5509CA8CAA77A" + - "2ADFC7BFD77DDA6F71125A7456FEA153E433256A2261C6A06ED3693797E7" + - "995FAD5AABBCFBE3EDA2741E375404AE25B", - Generator: "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E2480" + - "9670716C613D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D" + - "1AA58C4328A06C46A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A33" + - "8661D10461C0D135472085057F3494309FFA73C611F78B32ADBB5740C361" + - "C9F35BE90997DB2014E2EF5AA61782F52ABEB8BD6432C4DD097BC5423B28" + - "5DAFB60DC364E8161F4A2A35ACA3A10B1C4D203CC76A470A33AFDCBDD929" + - "59859ABD8B56E1725252D78EAC66E71BA9AE3F1DD2487199874393CD4D83" + - "2186800654760E1E34C09E4D155179F9EC0DC4473F996BDCE6EED1CABED8" + - "B6F116F7AD9CF505DF0F998E34AB27514B0FFE7", - }, - } -} - func e2eMessagesEqual(received, expected e2eMessage, t *testing.T) bool { equals := true if !bytes.Equal(received.Recipient, expected.Recipient) { @@ -101,193 +63,230 @@ func makeTestE2EMessages(n int, t *testing.T) []e2eMessage { return msgs } -type mockEventsManager struct{} - -func (m mockEventsManager) Report(priority int, category, evtType, details string) { +//////////////////////////////////////////////////////////////////////////////// +// Mock Listener // +//////////////////////////////////////////////////////////////////////////////// +type mockListener struct { + receiveChan chan receive.Message } -// todo: implement this for specific tests -type mockCmixNet struct { - testingInterface interface{} - instance *network.Instance -} +func (m *mockListener) Hear(item receive.Message) { m.receiveChan <- item } +func (m *mockListener) Name() string { return "" } -func (m mockCmixNet) Connect(ndf *ndf.NetworkDefinition) error { - // TODO implement me - panic("implement me") -} +//////////////////////////////////////////////////////////////////////////////// +// Mock Events Manager // +//////////////////////////////////////////////////////////////////////////////// -func (m mockCmixNet) Follow(report cmix.ClientErrorReport) (stoppable.Stoppable, error) { - //TODO implement me - return nil, nil -} +type mockEventsManager struct{} -func (m mockCmixNet) GetMaxMessageLength() int { - //TODO implement me - return 0 -} +func (m mockEventsManager) Report(int, string, string, string) {} -func (m mockCmixNet) Send(recipient *id.ID, fingerprint format.Fingerprint, service message.Service, payload, mac []byte, cmixParams cmix.CMIXParams) (id.Round, ephemeral.Id, error) { - //TODO implement me - return 0, ephemeral.Id{}, nil -} +//////////////////////////////////////////////////////////////////////////////// +// Mock Services // +//////////////////////////////////////////////////////////////////////////////// -func (m mockCmixNet) SendMany(messages []cmix.TargetedCmixMessage, p cmix.CMIXParams) (id.Round, []ephemeral.Id, error) { - //TODO implement me - return 0, nil, nil +type mockServices struct { + services map[id.ID]map[string]message.Processor + sync.Mutex } -func (m mockCmixNet) AddIdentity(id *id.ID, validUntil time.Time, persistent bool) { - //TODO implement me - return +func newMockServices() *mockServices { + return &mockServices{ + services: make(map[id.ID]map[string]message.Processor), + } } -func (m mockCmixNet) RemoveIdentity(id *id.ID) { - //TODO implement me - return -} +func (m *mockServices) AddService( + clientID *id.ID, ms message.Service, p message.Processor) { + m.Lock() + defer m.Unlock() -func (m mockCmixNet) GetIdentity(get *id.ID) (identity.TrackedID, error) { - //TODO implement me - return identity.TrackedID{}, nil + if m.services[*clientID] == nil { + m.services[*clientID] = map[string]message.Processor{ms.Tag: p} + } else { + m.services[*clientID][ms.Tag] = p + } + m.services[*clientID][ms.Tag] = p } -func (m mockCmixNet) AddFingerprint(identity *id.ID, fingerprint format.Fingerprint, mp message.Processor) error { - //TODO implement me - return nil -} +func (m *mockServices) DeleteService( + clientID *id.ID, ms message.Service, _ message.Processor) { + m.Lock() + defer m.Unlock() -func (m mockCmixNet) DeleteFingerprint(identity *id.ID, fingerprint format.Fingerprint) { - //TODO implement me - return + if m.services[*clientID] != nil { + delete(m.services[*clientID], ms.Tag) + } } -func (m mockCmixNet) DeleteClientFingerprints(identity *id.ID) { - //TODO implement me - return -} +//////////////////////////////////////////////////////////////////////////////// +// Mock cMix Client // +//////////////////////////////////////////////////////////////////////////////// -func (m mockCmixNet) AddService(clientID *id.ID, newService message.Service, response message.Processor) { - //TODO implement me - return +type mockCmixHandler struct { + processorMap map[format.Fingerprint]message.Processor + sync.Mutex } -func (m mockCmixNet) DeleteService(clientID *id.ID, toDelete message.Service, processor message.Processor) { - //TODO implement me - return +func newMockCmixHandler() *mockCmixHandler { + return &mockCmixHandler{ + processorMap: make(map[format.Fingerprint]message.Processor), + } } -func (m mockCmixNet) DeleteClientService(clientID *id.ID) { - //TODO implement me - return -} +// todo: implement this for specific tests +type mockCmix struct { + t testing.TB + myID *id.ID + numPrimeBytes int + health bool + handler *mockCmixHandler + instance *network.Instance +} + +func newMockCmix(myID *id.ID, handler *mockCmixHandler, t testing.TB) *mockCmix { + comms := &connect.ProtoComms{Manager: connect.NewManagerTesting(t)} + def := getNDF() + + instance, err := network.NewInstanceTesting(comms, def, def, nil, nil, t) + if err != nil { + panic(err) + } -func (m mockCmixNet) TrackServices(tracker message.ServicesTracker) { - //TODO implement me - return + return &mockCmix{ + t: t, + myID: myID, + numPrimeBytes: 4096, + health: true, + handler: handler, + instance: instance, + } } -func (m mockCmixNet) CheckInProgressMessages() { - //TODO implement me - return -} +func (m *mockCmix) Connect(*ndf.NetworkDefinition) error { return nil } +func (m *mockCmix) Follow(cmix.ClientErrorReport) (stoppable.Stoppable, error) { return nil, nil } -func (m mockCmixNet) IsHealthy() bool { - //TODO implement me - return true +func (m *mockCmix) GetMaxMessageLength() int { + msg := format.NewMessage(m.numPrimeBytes) + return msg.ContentsSize() } -func (m mockCmixNet) WasHealthy() bool { - //TODO implement me - return true -} +func (m *mockCmix) Send(_ *id.ID, fp format.Fingerprint, _ message.Service, + payload, mac []byte, _ cmix.CMIXParams) (id.Round, ephemeral.Id, error) { + m.handler.Lock() + defer m.handler.Unlock() -func (m mockCmixNet) AddHealthCallback(f func(bool)) uint64 { - //TODO implement me - return 0 -} + msg := format.NewMessage(m.numPrimeBytes) + msg.SetContents(payload) + msg.SetMac(mac) + msg.SetKeyFP(fp) -func (m mockCmixNet) RemoveHealthCallback(u uint64) { - //TODO implement me - return -} + if m.handler.processorMap[fp] == nil { + m.t.Errorf("No processor found for fingerprint %s", fp) + return 0, ephemeral.Id{}, + errors.Errorf("No processor found for fingerprint %s", fp) + } -func (m mockCmixNet) HasNode(nid *id.ID) bool { - //TODO implement me - return true -} + m.handler.processorMap[fp].Process( + msg, receptionID.EphemeralIdentity{}, rounds.Round{}) -func (m mockCmixNet) NumRegisteredNodes() int { - //TODO implement me - return 0 + return 0, ephemeral.Id{}, nil } -func (m mockCmixNet) TriggerNodeRegistration(nid *id.ID) { - //TODO implement me - return +func (m *mockCmix) SendMany([]cmix.TargetedCmixMessage, cmix.CMIXParams) (id.Round, []ephemeral.Id, error) { + return 0, nil, nil } +func (m *mockCmix) AddIdentity(*id.ID, time.Time, bool) {} +func (m *mockCmix) RemoveIdentity(*id.ID) {} +func (m *mockCmix) GetIdentity(*id.ID) (identity.TrackedID, error) { return identity.TrackedID{}, nil } -func (m mockCmixNet) GetRoundResults(timeout time.Duration, roundCallback cmix.RoundEventCallback, roundList ...id.Round) error { - //TODO implement me +func (m *mockCmix) AddFingerprint(_ *id.ID, fp format.Fingerprint, mp message.Processor) error { + m.handler.Lock() + defer m.handler.Unlock() + m.handler.processorMap[fp] = mp return nil } -func (m mockCmixNet) LookupHistoricalRound(rid id.Round, callback rounds.RoundResultCallback) error { - //TODO implement me +func (m *mockCmix) DeleteFingerprint(_ *id.ID, fp format.Fingerprint) { + m.handler.Lock() + delete(m.handler.processorMap, fp) + m.handler.Unlock() +} + +func (m *mockCmix) DeleteClientFingerprints(*id.ID) {} +func (m *mockCmix) AddService(*id.ID, message.Service, message.Processor) {} +func (m *mockCmix) DeleteService(*id.ID, message.Service, message.Processor) {} +func (m *mockCmix) DeleteClientService(*id.ID) {} +func (m *mockCmix) TrackServices(message.ServicesTracker) {} +func (m *mockCmix) CheckInProgressMessages() {} +func (m *mockCmix) IsHealthy() bool { return m.health } +func (m *mockCmix) WasHealthy() bool { return true } +func (m *mockCmix) AddHealthCallback(func(bool)) uint64 { return 0 } +func (m *mockCmix) RemoveHealthCallback(uint64) {} +func (m *mockCmix) HasNode(*id.ID) bool { return true } +func (m *mockCmix) NumRegisteredNodes() int { return 0 } +func (m *mockCmix) TriggerNodeRegistration(*id.ID) {} +func (m *mockCmix) GetRoundResults(time.Duration, cmix.RoundEventCallback, ...id.Round) error { return nil } - -func (m mockCmixNet) SendToAny(sendFunc func(host *connect.Host) (interface{}, error), stop *stoppable.Single) (interface{}, error) { - //TODO implement me +func (m *mockCmix) LookupHistoricalRound(id.Round, rounds.RoundResultCallback) error { return nil } +func (m *mockCmix) SendToAny(func(host *connect.Host) (interface{}, error), *stoppable.Single) (interface{}, error) { return nil, nil } - -func (m mockCmixNet) SendToPreferred(targets []*id.ID, sendFunc gateway.SendToPreferredFunc, stop *stoppable.Single, timeout time.Duration) (interface{}, error) { +func (m *mockCmix) SendToPreferred([]*id.ID, gateway.SendToPreferredFunc, *stoppable.Single, time.Duration) (interface{}, error) { return nil, nil } +func (m *mockCmix) SetGatewayFilter(gateway.Filter) {} +func (m *mockCmix) GetHostParams() connect.HostParams { return connect.HostParams{} } +func (m *mockCmix) GetAddressSpace() uint8 { return 0 } +func (m *mockCmix) RegisterAddressSpaceNotification(string) (chan uint8, error) { return nil, nil } +func (m *mockCmix) UnregisterAddressSpaceNotification(string) { return } +func (m *mockCmix) GetInstance() *network.Instance { return m.instance } +func (m *mockCmix) GetVerboseRounds() string { return "" } -func (m mockCmixNet) SetGatewayFilter(f gateway.Filter) { - return -} - -func (m mockCmixNet) GetHostParams() connect.HostParams { - return connect.HostParams{} -} - -func (m mockCmixNet) GetAddressSpace() uint8 { - return 0 -} +//////////////////////////////////////////////////////////////////////////////// +// NDF // +//////////////////////////////////////////////////////////////////////////////// -func (m mockCmixNet) RegisterAddressSpaceNotification(tag string) (chan uint8, error) { - return nil, nil -} - -func (m mockCmixNet) UnregisterAddressSpaceNotification(tag string) { - return -} - -func (m *mockCmixNet) GetInstance() *network.Instance { - if m.instance == nil { - commsManager := connect.NewManagerTesting(m.testingInterface) - - instanceComms := &connect.ProtoComms{ - Manager: commsManager, - } - - def := getNDF() - - thisInstance, err := network.NewInstanceTesting(instanceComms, def, def, nil, nil, m.testingInterface) - if err != nil { - panic(err) - } - - m.instance = thisInstance +func getNDF() *ndf.NetworkDefinition { + return &ndf.NetworkDefinition{ + E2E: ndf.Group{ + Prime: "E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B7A" + + "8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3D" + + "D2AEDF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E78615" + + "75E745D31F8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC" + + "6ADC718DD2A3E041023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C" + + "4A530E8FFB1BC51DADDF453B0B2717C2BC6669ED76B4BDD5C9FF558E88F2" + + "6E5785302BEDBCA23EAC5ACE92096EE8A60642FB61E8F3D24990B8CB12EE" + + "448EEF78E184C7242DD161C7738F32BF29A841698978825B4111B4BC3E1E" + + "198455095958333D776D8B2BEEED3A1A1A221A6E37E664A64B83981C46FF" + + "DDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F278DE8014A47323" + + "631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696015CB79C" + + "3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E63" + + "19BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC3" + + "5873847AEF49F66E43873", + Generator: "2", + }, + CMIX: ndf.Group{ + Prime: "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642" + + "F0B5C48C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757" + + "264E5A1A44FFE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F" + + "9716BFE6117C6B5B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091E" + + "B51743BF33050C38DE235567E1B34C3D6A5C0CEAA1A0F368213C3D19843D" + + "0B4B09DCB9FC72D39C8DE41F1BF14D4BB4563CA28371621CAD3324B6A2D3" + + "92145BEBFAC748805236F5CA2FE92B871CD8F9C36D3292B5509CA8CAA77A" + + "2ADFC7BFD77DDA6F71125A7456FEA153E433256A2261C6A06ED3693797E7" + + "995FAD5AABBCFBE3EDA2741E375404AE25B", + Generator: "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E2480" + + "9670716C613D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D" + + "1AA58C4328A06C46A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A33" + + "8661D10461C0D135472085057F3494309FFA73C611F78B32ADBB5740C361" + + "C9F35BE90997DB2014E2EF5AA61782F52ABEB8BD6432C4DD097BC5423B28" + + "5DAFB60DC364E8161F4A2A35ACA3A10B1C4D203CC76A470A33AFDCBDD929" + + "59859ABD8B56E1725252D78EAC66E71BA9AE3F1DD2487199874393CD4D83" + + "2186800654760E1E34C09E4D155179F9EC0DC4473F996BDCE6EED1CABED8" + + "B6F116F7AD9CF505DF0F998E34AB27514B0FFE7", + }, } - - return m.instance -} - -func (m mockCmixNet) GetVerboseRounds() string { - return "" }