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 ""
 }