diff --git a/catalog/services.go b/catalog/services.go
index cd28b663e04c925eadd329a49f82aa84aec824de..24ab74a466f6499654267861760b72cbc2d6472e 100644
--- a/catalog/services.go
+++ b/catalog/services.go
@@ -23,4 +23,6 @@ const (
 	Group   = "group"
 	EndFT   = "endFT"
 	GroupRq = "groupRq"
+
+	RestLike = "restLike"
 )
diff --git a/cmd/single.go b/cmd/single.go
index 55f33b26b8328c9381a1c2a88fc37cd454c585e1..00670077a2cf169ca36ca9352238ae0d36f0b714 100644
--- a/cmd/single.go
+++ b/cmd/single.go
@@ -221,7 +221,7 @@ func replySingleUse(timeout time.Duration, receiver *Receiver) {
 		// Create new payload from repeated received payloads so that each
 		// message part contains the same payload
 		resPayload := makeResponsePayload(payload, results.request.GetMaxParts(),
-			results.request.GetMaxContentsSize())
+			results.request.GetMaxResponsePartSize())
 
 		fmt.Printf("Sending single-use response message: %s\n", payload)
 		jww.DEBUG.Printf("Sending single-use response to %s: %s",
diff --git a/e2e/e2eMessageBuffer_test.go b/e2e/e2eMessageBuffer_test.go
index b10433dac17a2f72b7b71bfb6903e5c6731f0a26..37826cfcff8dee48b705ad4f0efa7b2bd83bd6ac 100644
--- a/e2e/e2eMessageBuffer_test.go
+++ b/e2e/e2eMessageBuffer_test.go
@@ -23,7 +23,7 @@ import (
 func TestE2EMessageHandler_SaveMessage(t *testing.T) {
 	// Set up test values
 	emg := &e2eMessageHandler{}
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	testMsgs := makeTestE2EMessages(10, t)
 
 	for _, msg := range testMsgs {
@@ -60,7 +60,7 @@ func TestE2EMessageHandler_SaveMessage(t *testing.T) {
 func TestE2EMessageHandler_LoadMessage(t *testing.T) {
 	// Set up test values
 	cmh := &e2eMessageHandler{}
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	testMsgs := makeTestE2EMessages(10, t)
 
 	for _, msg := range testMsgs {
@@ -96,7 +96,7 @@ func TestE2EMessageHandler_LoadMessage(t *testing.T) {
 func TestE2EMessageHandler_Smoke(t *testing.T) {
 	// Set up test messages
 	testMsgs := makeTestE2EMessages(2, t)
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	// Create new buffer
 	cmb, err := NewOrLoadE2eMessageBuffer(kv, "testKey")
 	if err != nil {
@@ -178,7 +178,7 @@ func TestE2EParamMarshalUnmarshal(t *testing.T) {
 		Payload:     []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 		MessageType: 42,
 		Params: Params{
-			CMIX: cmix.CMIXParams{
+			CMIXParams: cmix.CMIXParams{
 				RoundTries:       6,
 				Timeout:          99,
 				RetryDelay:       -4,
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 af4736d0ddb1fee413be96e026dbb4ea05c75109..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.Memstore{})
+	// }
+	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/parse/conversation/partner_test.go b/e2e/parse/conversation/partner_test.go
index 9a52791f147f1ad9b69eace2bc6cc3378320f265..41dd638ca5a04fefc125c2a4977715c5c4493f5c 100644
--- a/e2e/parse/conversation/partner_test.go
+++ b/e2e/parse/conversation/partner_test.go
@@ -19,7 +19,7 @@ import (
 // Tests happy path of LoadOrMakeConversation when making a new Conversation.
 func TestLoadOrMakeConversation_New(t *testing.T) {
 	// Set up test values
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	partner := id.NewIdFromString("partner ID", id.User, t)
 	expectedConv := &Conversation{
 		lastReceivedID:         0,
@@ -42,7 +42,7 @@ func TestLoadOrMakeConversation_New(t *testing.T) {
 // Tests happy path of LoadOrMakeConversation when loading a Conversation.
 func TestLoadOrMakeConversation_Load(t *testing.T) {
 	// Set up test values
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	partner := id.NewIdFromString("partner ID", id.User, t)
 	expectedConv := LoadOrMakeConversation(kv, partner)
 
@@ -60,7 +60,7 @@ func TestLoadOrMakeConversation_Load(t *testing.T) {
 func TestConversation_ProcessReceivedMessageID_Case_1(t *testing.T) {
 	// Set up test values
 	mid := uint32(5)
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	partner := id.NewIdFromString("partner ID", id.User, t)
 	expectedConv := LoadOrMakeConversation(kv, partner)
 	expectedConv.lastReceivedID = mid
@@ -84,7 +84,7 @@ func TestConversation_ProcessReceivedMessageID_Case_1(t *testing.T) {
 func TestConversation_ProcessReceivedMessageID_Case_0(t *testing.T) {
 	// Set up test values
 	mid := uint32(5)
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	partner := id.NewIdFromString("partner ID", id.User, t)
 	expectedConv := LoadOrMakeConversation(kv, partner)
 	expectedConv.lastReceivedID = mid
@@ -106,7 +106,7 @@ func TestConversation_ProcessReceivedMessageID_Case_0(t *testing.T) {
 func TestConversation_ProcessReceivedMessageID_Case_Neg1(t *testing.T) {
 	// Set up test values
 	mid := uint32(topRegion + 5)
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	partner := id.NewIdFromString("partner ID", id.User, t)
 	expectedConv := LoadOrMakeConversation(kv, partner)
 	expectedConv.lastReceivedID = bottomRegion - 5
@@ -128,7 +128,7 @@ func TestConversation_ProcessReceivedMessageID_Case_Neg1(t *testing.T) {
 // Tests happy path of Conversation.GetNextSendID.
 func TestConversation_GetNextSendID(t *testing.T) {
 	// Set up test values
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	partner := id.NewIdFromString("partner ID", id.User, t)
 	conv := LoadOrMakeConversation(kv, partner)
 	conv.nextSentID = maxTruncatedID - 100
@@ -148,7 +148,7 @@ func TestConversation_GetNextSendID(t *testing.T) {
 
 // Tests the happy path of save and loadConversation.
 func TestConversation_save_load(t *testing.T) {
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	partner := id.NewIdFromString("partner ID", id.User, t)
 	expectedConv := makeRandomConv(kv, partner)
 	expectedErr := "loadConversation produced an error: Failed to Load " +
@@ -169,7 +169,7 @@ func TestConversation_save_load(t *testing.T) {
 			"\nexpected: %+v\nreceived: %+v", expectedConv, testConv)
 	}
 
-	_, err = loadConversation(versioned.NewKV(make(ekv.Memstore)), partner)
+	_, err = loadConversation(versioned.NewKV(ekv.MakeMemstore()), partner)
 	if err == nil {
 		t.Errorf("loadConversation failed to produce an error."+
 			"\nexpected: %s\nreceived: %v", expectedErr, nil)
@@ -178,7 +178,7 @@ func TestConversation_save_load(t *testing.T) {
 
 // Happy path.
 func TestConversation_Delete(t *testing.T) {
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	partner := id.NewIdFromString("partner ID", id.User, t)
 	conv := makeRandomConv(kv, partner)
 
@@ -201,7 +201,7 @@ func TestConversation_Delete(t *testing.T) {
 
 // Tests the happy path of marshal and unmarshal.
 func TestConversation_marshal_unmarshal(t *testing.T) {
-	expectedConv := makeRandomConv(versioned.NewKV(make(ekv.Memstore)),
+	expectedConv := makeRandomConv(versioned.NewKV(ekv.MakeMemstore()),
 		id.NewIdFromString("partner ID", id.User, t))
 	testConv := LoadOrMakeConversation(expectedConv.kv, expectedConv.partner)
 
diff --git a/e2e/parse/conversation/ring_test.go b/e2e/parse/conversation/ring_test.go
index e9174d366681c427b33bfb626c51a292001a1f30..1d1db1983fbe265d6841b22c1f969b21119806f7 100644
--- a/e2e/parse/conversation/ring_test.go
+++ b/e2e/parse/conversation/ring_test.go
@@ -19,7 +19,7 @@ import (
 // TestNewBuff tests the creation of a Buff object.
 func TestNewBuff(t *testing.T) {
 	// Initialize buffer
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	buffLen := 20
 	testBuff, err := NewBuff(kv, buffLen)
 	if err != nil {
@@ -43,7 +43,7 @@ func TestNewBuff(t *testing.T) {
 // the Buff.buff, buff.lookup, and proper index updates.
 func TestBuff_Add(t *testing.T) {
 	// Initialize buffer
-	testBuff, err := NewBuff(versioned.NewKV(make(ekv.Memstore)), 20)
+	testBuff, err := NewBuff(versioned.NewKV(ekv.MakeMemstore()), 20)
 	if err != nil {
 		t.Errorf("Failed to make new Buff: %+v", err)
 	}
@@ -98,7 +98,7 @@ func TestBuff_Add(t *testing.T) {
 // value is overwritten.
 func TestBuff_Add_Overflow(t *testing.T) {
 	buffLen := 20
-	testBuff, err := NewBuff(versioned.NewKV(make(ekv.Memstore)), buffLen)
+	testBuff, err := NewBuff(versioned.NewKV(ekv.MakeMemstore()), buffLen)
 	if err != nil {
 		t.Errorf("Failed to make new Buff: %+v", err)
 	}
@@ -142,7 +142,7 @@ func TestBuff_Add_Overflow(t *testing.T) {
 // Tests that Buff.Get returns the latest inserted Message.
 func TestBuff_Get(t *testing.T) {
 	// Initialize buffer
-	testBuff, err := NewBuff(versioned.NewKV(make(ekv.Memstore)), 20)
+	testBuff, err := NewBuff(versioned.NewKV(ekv.MakeMemstore()), 20)
 	if err != nil {
 		t.Errorf("Failed to make new Buff: %+v", err)
 	}
@@ -196,7 +196,7 @@ func TestBuff_Get(t *testing.T) {
 // MessageID.
 func TestBuff_GetByMessageID(t *testing.T) {
 	// Initialize buffer
-	testBuff, err := NewBuff(versioned.NewKV(make(ekv.Memstore)), 20)
+	testBuff, err := NewBuff(versioned.NewKV(ekv.MakeMemstore()), 20)
 	if err != nil {
 		t.Errorf("Failed to make new Buff: %+v", err)
 	}
@@ -234,7 +234,7 @@ func TestBuff_GetByMessageID(t *testing.T) {
 // that does not exist in Buff.
 func TestBuff_GetByMessageID_Error(t *testing.T) {
 	// Initialize buffer
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	buffLen := 20
 	testBuff, err := NewBuff(kv, buffLen)
 	if err != nil {
@@ -254,7 +254,7 @@ func TestBuff_GetByMessageID_Error(t *testing.T) {
 
 func TestBuff_GetNextMessage(t *testing.T) {
 	// Initialize buffer
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	buffLen := 20
 	testBuff, err := NewBuff(kv, buffLen)
 	if err != nil {
@@ -298,7 +298,7 @@ func TestBuff_GetNextMessage(t *testing.T) {
 
 func TestLoadBuff(t *testing.T) {
 	// Initialize buffer
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	buffLen := 20
 	testBuff, err := NewBuff(kv, buffLen)
 	if err != nil {
diff --git a/e2e/parse/conversation/store_test.go b/e2e/parse/conversation/store_test.go
index 16598c3cd268194c32f89eba021ccd11e4e7733b..176d251be7085dba1baa22e9038aeef10efb58fc 100644
--- a/e2e/parse/conversation/store_test.go
+++ b/e2e/parse/conversation/store_test.go
@@ -17,7 +17,7 @@ import (
 
 // Happy path.
 func TestStore_Delete(t *testing.T) {
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	store := NewStore(kv)
 	pIDs := make([]*id.ID, 10)
 
diff --git a/e2e/parse/partition/multiPartMessage_test.go b/e2e/parse/partition/multiPartMessage_test.go
index 8bd4846625bde3c50dcdb30a2c6ab19e3e423bdb..616b7dda8d3bcae9799b78ac595f4a6fe3f123ad 100644
--- a/e2e/parse/partition/multiPartMessage_test.go
+++ b/e2e/parse/partition/multiPartMessage_test.go
@@ -34,7 +34,7 @@ func Test_loadOrCreateMultiPartMessage_Create(t *testing.T) {
 		PresentParts:    0,
 		SenderTimestamp: time.Time{},
 		MessageType:     0,
-		kv:              versioned.NewKV(make(ekv.Memstore)),
+		kv:              versioned.NewKV(ekv.MakeMemstore()),
 	}
 	expectedData, err := json.Marshal(expectedMpm)
 	if err != nil {
@@ -70,7 +70,7 @@ func Test_loadOrCreateMultiPartMessage_Load(t *testing.T) {
 		PresentParts:    0,
 		SenderTimestamp: time.Time{},
 		MessageType:     0,
-		kv:              versioned.NewKV(make(ekv.Memstore)),
+		kv:              versioned.NewKV(ekv.MakeMemstore()),
 	}
 	err := expectedMpm.save()
 	if err != nil {
@@ -136,7 +136,7 @@ func TestMultiPartMessage_Add(t *testing.T) {
 	prng := rand.New(rand.NewSource(netTime.Now().UnixNano()))
 	mpm := loadOrCreateMultiPartMessage(
 		id.NewIdFromUInt(prng.Uint64(), id.User, t), prng.Uint64(),
-		versioned.NewKV(make(ekv.Memstore)))
+		versioned.NewKV(ekv.MakeMemstore()))
 	partNums, parts := generateParts(prng, 0)
 
 	for i := range partNums {
@@ -184,7 +184,7 @@ func TestMultiPartMessage_AddFirst(t *testing.T) {
 		SenderTimestamp: netTime.Now(),
 		MessageType:     catalog.NoType,
 		parts:           make([][]byte, 3),
-		kv:              versioned.NewKV(make(ekv.Memstore)),
+		kv:              versioned.NewKV(ekv.MakeMemstore()),
 	}
 	expectedMpm.parts[2] = []byte{5, 8, 78, 9}
 	npm := loadOrCreateMultiPartMessage(expectedMpm.Sender,
@@ -213,7 +213,7 @@ func TestMultiPartMessage_IsComplete(t *testing.T) {
 	mid := prng.Uint64()
 	mpm := loadOrCreateMultiPartMessage(
 		id.NewIdFromUInt(prng.Uint64(), id.User, t), mid,
-		versioned.NewKV(make(ekv.Memstore)))
+		versioned.NewKV(ekv.MakeMemstore()))
 	partNums, parts := generateParts(prng, 75)
 
 	// Check that IsComplete is false where there are no parts
@@ -258,7 +258,7 @@ func TestMultiPartMessage_IsComplete(t *testing.T) {
 // Tests happy path of multiPartMessage.delete.
 func TestMultiPartMessage_delete(t *testing.T) {
 	prng := rand.New(rand.NewSource(netTime.Now().UnixNano()))
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	mpm := loadOrCreateMultiPartMessage(
 		id.NewIdFromUInt(prng.Uint64(), id.User, t), prng.Uint64(), kv)
 
diff --git a/e2e/parse/partition/part_test.go b/e2e/parse/partition/part_test.go
index 936de7af865b4379ee4e72eb396bf926f1ff56bd..53366fdb6d211a74e531e21cf8b4f0a9665079c3 100644
--- a/e2e/parse/partition/part_test.go
+++ b/e2e/parse/partition/part_test.go
@@ -20,7 +20,7 @@ import (
 func Test_savePart(t *testing.T) {
 	// Set up test values
 	prng := rand.New(rand.NewSource(netTime.Now().UnixNano()))
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	partNum := uint8(prng.Uint32())
 	part := make([]byte, prng.Int31n(500))
 	prng.Read(part)
@@ -49,7 +49,7 @@ func Test_savePart(t *testing.T) {
 func Test_loadPart(t *testing.T) {
 	// Set up test values
 	prng := rand.New(rand.NewSource(netTime.Now().UnixNano()))
-	rootKv := versioned.NewKV(make(ekv.Memstore))
+	rootKv := versioned.NewKV(ekv.MakeMemstore())
 	partNum := uint8(prng.Uint32())
 	part := make([]byte, prng.Int31n(500))
 	prng.Read(part)
@@ -80,7 +80,7 @@ func Test_loadPart(t *testing.T) {
 func Test_loadPart_NotFoundError(t *testing.T) {
 	// Set up test values
 	prng := rand.New(rand.NewSource(netTime.Now().UnixNano()))
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	partNum := uint8(prng.Uint32())
 	part := make([]byte, prng.Int31n(500))
 	prng.Read(part)
@@ -102,7 +102,7 @@ func Test_loadPart_NotFoundError(t *testing.T) {
 func TestDeletePart(t *testing.T) {
 	// Set up test values
 	prng := rand.New(rand.NewSource(netTime.Now().UnixNano()))
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	partNum := uint8(prng.Uint32())
 	part := make([]byte, prng.Int31n(500))
 	prng.Read(part)
diff --git a/e2e/parse/partition/store_test.go b/e2e/parse/partition/store_test.go
index 9a86fb17b65997dc08be57aeb63800a3ba775f4c..909de9b829d54e7b07fc4936ed82bdd2337ccc5f 100644
--- a/e2e/parse/partition/store_test.go
+++ b/e2e/parse/partition/store_test.go
@@ -20,7 +20,7 @@ import (
 
 // Tests happy path of NewOrLoad.
 func TestNewOrLoad(t *testing.T) {
-	rootKv := versioned.NewKV(make(ekv.Memstore))
+	rootKv := versioned.NewKV(ekv.MakeMemstore())
 	expectedStore := &Store{
 		multiParts:  make(map[multiPartID]*multiPartMessage),
 		activeParts: make(map[*multiPartMessage]bool),
@@ -38,7 +38,7 @@ func TestNewOrLoad(t *testing.T) {
 // Tests happy path of Store.AddFirst.
 func TestStore_AddFirst(t *testing.T) {
 	part := []byte("Test message.")
-	s := NewOrLoad(versioned.NewKV(ekv.Memstore{}))
+	s := NewOrLoad(versioned.NewKV(ekv.MakeMemstore()))
 
 	msg, complete := s.AddFirst(id.NewIdFromString("User", id.User, t),
 		catalog.XxMessage, 5, 0, 1, netTime.Now(), netTime.Now(), part,
@@ -58,7 +58,7 @@ func TestStore_AddFirst(t *testing.T) {
 func TestStore_Add(t *testing.T) {
 	part1 := []byte("Test message.")
 	part2 := []byte("Second Sentence.")
-	s := NewOrLoad(versioned.NewKV(ekv.Memstore{}))
+	s := NewOrLoad(versioned.NewKV(ekv.MakeMemstore()))
 
 	msg, complete := s.AddFirst(id.NewIdFromString("User", id.User, t),
 		catalog.XxMessage, 5, 0, 2, netTime.Now(), netTime.Now(), part1,
@@ -87,7 +87,7 @@ func TestStore_prune(t *testing.T) {
 	// new message
 	part1 := []byte("Test message.")
 	part2 := []byte("Second Sentence.")
-	s := NewOrLoad(versioned.NewKV(ekv.Memstore{}))
+	s := NewOrLoad(versioned.NewKV(ekv.MakeMemstore()))
 
 	partner1 := id.NewIdFromString("User", id.User, t)
 	messageId1 := uint64(5)
diff --git a/e2e/parse/partition_test.go b/e2e/parse/partition_test.go
index 98970c881d67f9a69bc4dda4a0114f3644459055..e8440ef1a6f62b34ec4ef6a7378c56218157a88c 100644
--- a/e2e/parse/partition_test.go
+++ b/e2e/parse/partition_test.go
@@ -24,7 +24,7 @@ var ipsumTestStr = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cra
 
 // Test that NewPartitioner outputs a correctly made Partitioner
 func TestNewPartitioner(t *testing.T) {
-	p := NewPartitioner(versioned.NewKV(make(ekv.Memstore)), 4096)
+	p := NewPartitioner(versioned.NewKV(ekv.MakeMemstore()), 4096)
 
 	if p.baseMessageSize != 4096 {
 		t.Errorf("baseMessageSize content mismatch."+
@@ -57,7 +57,7 @@ func TestNewPartitioner(t *testing.T) {
 
 // Test that no error is returned running Partitioner.Partition.
 func TestPartitioner_Partition(t *testing.T) {
-	p := NewPartitioner(versioned.NewKV(make(ekv.Memstore)), len(ipsumTestStr))
+	p := NewPartitioner(versioned.NewKV(ekv.MakeMemstore()), len(ipsumTestStr))
 
 	_, _, err := p.Partition(
 		&id.DummyUser, catalog.XxMessage, netTime.Now(), []byte(ipsumTestStr))
@@ -68,7 +68,7 @@ func TestPartitioner_Partition(t *testing.T) {
 
 // Test that Partitioner.HandlePartition can handle a message part.
 func TestPartitioner_HandlePartition(t *testing.T) {
-	p := NewPartitioner(versioned.NewKV(make(ekv.Memstore)), len(ipsumTestStr))
+	p := NewPartitioner(versioned.NewKV(ekv.MakeMemstore()), len(ipsumTestStr))
 	m := newMessagePart(1107, 1, []byte(ipsumTestStr))
 
 	_, _ = p.HandlePartition(
@@ -80,7 +80,7 @@ func TestPartitioner_HandlePartition(t *testing.T) {
 
 // Test that HandlePartition can handle a first message part
 func TestPartitioner_HandleFirstPartition(t *testing.T) {
-	p := NewPartitioner(versioned.NewKV(make(ekv.Memstore)), len(ipsumTestStr))
+	p := NewPartitioner(versioned.NewKV(ekv.MakeMemstore()), len(ipsumTestStr))
 	m := newFirstMessagePart(
 		catalog.XxMessage, 1107, 1, netTime.Now(), []byte(ipsumTestStr))
 
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/relationship_test.go b/e2e/ratchet/partner/relationship_test.go
index 9591ecea3193a0696b7910a64c130e537550a18c..5160844daf0c6c3dcc6e4ea2844065f42f2b612b 100644
--- a/e2e/ratchet/partner/relationship_test.go
+++ b/e2e/ratchet/partner/relationship_test.go
@@ -875,7 +875,7 @@ func makeTestRelationshipManager(t *testing.T) (*manager, *versioned.KV) {
 	mySIDHPrivKey.Generate(rng)
 	mySIDHPrivKey.GeneratePublicKey(mySIDHPubKey)
 
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	frng := fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG)
 	return &manager{
 		kv:                      kv,
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 910384d7ce98fa557f936f13e5b94bfbbe7650a6..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,14 +61,20 @@ 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(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	partnerID := id.NewIdFromString("partner", id.User, t)
 
 	myId := id.NewIdFromString("me", id.User, t)
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 2fa7f095f06a294587b2a3c89a0bc25dd9919dc7..a1cdbcb881b893dd5ebb4ce470078236e62b9f40 100644
--- a/e2e/ratchet/ratchet_test.go
+++ b/e2e/ratchet/ratchet_test.go
@@ -30,7 +30,7 @@ import (
 func TestNewStore(t *testing.T) {
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2))
 	privKey := grp.NewInt(57)
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	expectedStore := &Ratchet{
 		managers:               make(map[id.ID]partner.Manager),
 		advertisedDHPrivateKey: privKey,
@@ -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 02c9ed79297487e013c72174aea994d2840ccb3e..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"
@@ -24,7 +23,7 @@ import (
 func makeTestRatchet() (*Ratchet, *versioned.KV, error) {
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2))
 	privKey := grp.NewInt(57)
-	kv := versioned.NewKV(make(ekv.Memstore))
+	kv := versioned.NewKV(ekv.MakeMemstore())
 	rng := fastRNG.NewStreamGenerator(12, 3, csprng.NewSystemRNG)
 	err := New(kv, &id.ID{}, privKey, grp)
 	if err != nil {
@@ -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/confirm_test.go b/e2e/rekey/confirm_test.go
index fe9115392b6376c87de4805c76a685dacbbe5881..24de5fda04353b367d1d222d1517f48c09fb7cde 100644
--- a/e2e/rekey/confirm_test.go
+++ b/e2e/rekey/confirm_test.go
@@ -33,7 +33,7 @@ func TestHandleConfirm(t *testing.T) {
 	rng := fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG)
 	myID := id.NewIdFromString("zezima", id.User, t)
 
-	kv := versioned.NewKV(ekv.Memstore{})
+	kv := versioned.NewKV(ekv.MakeMemstore())
 
 	// Maintain an ID for bob
 	bobID := id.NewIdFromBytes([]byte("test"), t)
diff --git a/e2e/rekey/exchange_test.go b/e2e/rekey/exchange_test.go
index 6e4f3036144fb3fcf33617dfc26fcb9c3021bb27..2967d81cbf2cea380ae1c0e64393fbe493fa1851 100644
--- a/e2e/rekey/exchange_test.go
+++ b/e2e/rekey/exchange_test.go
@@ -41,7 +41,7 @@ func TestFullExchange(t *testing.T) {
 	rng := fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG)
 	aliceID = id.NewIdFromString("zezima", id.User, t)
 
-	kv := versioned.NewKV(ekv.Memstore{})
+	kv := versioned.NewKV(ekv.MakeMemstore())
 
 	// Maintain an ID for bob
 	bobID = id.NewIdFromBytes([]byte("test"), t)
diff --git a/e2e/rekey/rekey_test.go b/e2e/rekey/rekey_test.go
index 0e35f8124299ec22fe11b02f9261c88348d7b524..ac5afe5fd8b9feb256e7b47c7dccc1efd85500be 100644
--- a/e2e/rekey/rekey_test.go
+++ b/e2e/rekey/rekey_test.go
@@ -39,7 +39,7 @@ func TestRekey(t *testing.T) {
 
 	bobID = id.NewIdFromUInt(rand.Uint64(), id.User, t)
 
-	kv := versioned.NewKV(ekv.Memstore{})
+	kv := versioned.NewKV(ekv.MakeMemstore())
 
 	err := ratchet.New(kv, aliceID, alicePrivKey, grp)
 	if err != nil {
diff --git a/e2e/rekey/trigger_test.go b/e2e/rekey/trigger_test.go
index 7d6586ac6dc2d63c6ccab8f7699ecbdf7bfcaa1d..8e9ab6658eb627babb1dd9acacd0150222f1a71c 100644
--- a/e2e/rekey/trigger_test.go
+++ b/e2e/rekey/trigger_test.go
@@ -70,7 +70,7 @@ func TestHandleTrigger(t *testing.T) {
 	// Maintain an ID for bob
 	bobID = id.NewIdFromBytes([]byte("test"), t)
 	aliceID = id.NewIdFromString("zezima", id.User, t)
-	kv := versioned.NewKV(ekv.Memstore{})
+	kv := versioned.NewKV(ekv.MakeMemstore())
 
 	err := ratchet.New(kv, aliceID, alicePrivKey, grp)
 	if err != nil {
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 7a71a84091e80cbaaed1e30b7a0f531a63c828a2..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,188 +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) Follow(report cmix.ClientErrorReport) (stoppable.Stoppable, error) {
-	//TODO implement me
-	return nil, nil
-}
+////////////////////////////////////////////////////////////////////////////////
+// Mock Events Manager                                                        //
+////////////////////////////////////////////////////////////////////////////////
 
-func (m mockCmixNet) GetMaxMessageLength() int {
-	//TODO implement me
-	return 0
-}
+type mockEventsManager struct{}
 
-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
-}
+func (m mockEventsManager) Report(int, string, string, string) {}
 
-func (m mockCmixNet) SendMany(messages []cmix.TargetedCmixMessage, p cmix.CMIXParams) (id.Round, []ephemeral.Id, error) {
-	//TODO implement me
-	return 0, nil, nil
-}
+////////////////////////////////////////////////////////////////////////////////
+// Mock Services                                                              //
+////////////////////////////////////////////////////////////////////////////////
 
-func (m mockCmixNet) AddIdentity(id *id.ID, validUntil time.Time, persistent bool) {
-	//TODO implement me
-	return
+type mockServices struct {
+	services map[id.ID]map[string]message.Processor
+	sync.Mutex
 }
 
-func (m mockCmixNet) RemoveIdentity(id *id.ID) {
-	//TODO implement me
-	return
+func newMockServices() *mockServices {
+	return &mockServices{
+		services: make(map[id.ID]map[string]message.Processor),
+	}
 }
 
-func (m mockCmixNet) GetIdentity(get *id.ID) (identity.TrackedID, error) {
-	//TODO implement me
-	return identity.TrackedID{}, nil
-}
+func (m *mockServices) AddService(
+	clientID *id.ID, ms message.Service, p message.Processor) {
+	m.Lock()
+	defer m.Unlock()
 
-func (m mockCmixNet) AddFingerprint(identity *id.ID, fingerprint format.Fingerprint, mp message.Processor) error {
-	//TODO implement me
-	return 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) DeleteFingerprint(identity *id.ID, fingerprint format.Fingerprint) {
-	//TODO implement me
-	return
-}
+func (m *mockServices) DeleteService(
+	clientID *id.ID, ms message.Service, _ message.Processor) {
+	m.Lock()
+	defer m.Unlock()
 
-func (m mockCmixNet) DeleteClientFingerprints(identity *id.ID) {
-	//TODO implement me
-	return
+	if m.services[*clientID] != nil {
+		delete(m.services[*clientID], ms.Tag)
+	}
 }
 
-func (m mockCmixNet) AddService(clientID *id.ID, newService message.Service, response message.Processor) {
-	//TODO implement me
-	return
-}
+////////////////////////////////////////////////////////////////////////////////
+// Mock cMix Client                                                           //
+////////////////////////////////////////////////////////////////////////////////
 
-func (m mockCmixNet) DeleteService(clientID *id.ID, toDelete message.Service, processor message.Processor) {
-	//TODO implement me
-	return
+type mockCmixHandler struct {
+	processorMap map[format.Fingerprint]message.Processor
+	sync.Mutex
 }
 
-func (m mockCmixNet) DeleteClientService(clientID *id.ID) {
-	//TODO implement me
-	return
+func newMockCmixHandler() *mockCmixHandler {
+	return &mockCmixHandler{
+		processorMap: make(map[format.Fingerprint]message.Processor),
+	}
 }
 
-func (m mockCmixNet) TrackServices(tracker message.ServicesTracker) {
-	//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) CheckInProgressMessages() {
-	//TODO implement me
-	return
+	return &mockCmix{
+		t:             t,
+		myID:          myID,
+		numPrimeBytes: 4096,
+		health:        true,
+		handler:       handler,
+		instance:      instance,
+	}
 }
 
-func (m mockCmixNet) IsHealthy() bool {
-	//TODO implement me
-	return true
-}
+func (m *mockCmix) Connect(*ndf.NetworkDefinition) error                       { return nil }
+func (m *mockCmix) Follow(cmix.ClientErrorReport) (stoppable.Stoppable, error) { return nil, nil }
 
-func (m mockCmixNet) WasHealthy() bool {
-	//TODO implement me
-	return true
+func (m *mockCmix) GetMaxMessageLength() int {
+	msg := format.NewMessage(m.numPrimeBytes)
+	return msg.ContentsSize()
 }
 
-func (m mockCmixNet) AddHealthCallback(f func(bool)) uint64 {
-	//TODO implement me
-	return 0
-}
+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) RemoveHealthCallback(u uint64) {
-	//TODO implement me
-	return
-}
+	msg := format.NewMessage(m.numPrimeBytes)
+	msg.SetContents(payload)
+	msg.SetMac(mac)
+	msg.SetKeyFP(fp)
 
-func (m mockCmixNet) HasNode(nid *id.ID) bool {
-	//TODO implement me
-	return true
-}
+	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) NumRegisteredNodes() int {
-	//TODO implement me
-	return 0
+	m.handler.processorMap[fp].Process(
+		msg, receptionID.EphemeralIdentity{}, rounds.Round{})
+
+	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 ""
 }
diff --git a/restlike/generateProto.sh b/restlike/generateProto.sh
new file mode 100755
index 0000000000000000000000000000000000000000..67b6d293f6f4e6a68eff4162a42acb242129cd18
--- /dev/null
+++ b/restlike/generateProto.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+protoc --go_out=paths=source_relative:. restlike/restLikeMessages.proto
diff --git a/restlike/restLikeMessages.pb.go b/restlike/restLikeMessages.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..c6666a5d08dd3c0a086a100a19ca0a74f630af79
--- /dev/null
+++ b/restlike/restLikeMessages.pb.go
@@ -0,0 +1,270 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.27.1
+// 	protoc        v3.19.1
+// source: restlike/restLikeMessages.proto
+
+package restlike
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// Message are used for sending to and receiving from a RestServer
+type Message struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Content []byte   `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"`
+	Headers *Headers `protobuf:"bytes,2,opt,name=headers,proto3" json:"headers,omitempty"`
+	Method  uint32   `protobuf:"varint,3,opt,name=method,proto3" json:"method,omitempty"`
+	Uri     string   `protobuf:"bytes,4,opt,name=uri,proto3" json:"uri,omitempty"`
+	Error   string   `protobuf:"bytes,5,opt,name=error,proto3" json:"error,omitempty"`
+}
+
+func (x *Message) Reset() {
+	*x = Message{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_restlike_restLikeMessages_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Message) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Message) ProtoMessage() {}
+
+func (x *Message) ProtoReflect() protoreflect.Message {
+	mi := &file_restlike_restLikeMessages_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Message.ProtoReflect.Descriptor instead.
+func (*Message) Descriptor() ([]byte, []int) {
+	return file_restlike_restLikeMessages_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Message) GetContent() []byte {
+	if x != nil {
+		return x.Content
+	}
+	return nil
+}
+
+func (x *Message) GetHeaders() *Headers {
+	if x != nil {
+		return x.Headers
+	}
+	return nil
+}
+
+func (x *Message) GetMethod() uint32 {
+	if x != nil {
+		return x.Method
+	}
+	return 0
+}
+
+func (x *Message) GetUri() string {
+	if x != nil {
+		return x.Uri
+	}
+	return ""
+}
+
+func (x *Message) GetError() string {
+	if x != nil {
+		return x.Error
+	}
+	return ""
+}
+
+// Headers allows different configurations for each Request
+// that will be specified in the Request header
+type Headers struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Headers allows for custom headers to be included with a Request
+	Headers []byte `protobuf:"bytes,1,opt,name=headers,proto3" json:"headers,omitempty"`
+	// Version allows for endpoints to be backwards-compatible
+	// and handle different formats of the same Request
+	Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"`
+}
+
+func (x *Headers) Reset() {
+	*x = Headers{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_restlike_restLikeMessages_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Headers) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Headers) ProtoMessage() {}
+
+func (x *Headers) ProtoReflect() protoreflect.Message {
+	mi := &file_restlike_restLikeMessages_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Headers.ProtoReflect.Descriptor instead.
+func (*Headers) Descriptor() ([]byte, []int) {
+	return file_restlike_restLikeMessages_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Headers) GetHeaders() []byte {
+	if x != nil {
+		return x.Headers
+	}
+	return nil
+}
+
+func (x *Headers) GetVersion() uint32 {
+	if x != nil {
+		return x.Version
+	}
+	return 0
+}
+
+var File_restlike_restLikeMessages_proto protoreflect.FileDescriptor
+
+var file_restlike_restLikeMessages_proto_rawDesc = []byte{
+	0x0a, 0x1f, 0x72, 0x65, 0x73, 0x74, 0x6c, 0x69, 0x6b, 0x65, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x4c,
+	0x69, 0x6b, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x12, 0x10, 0x72, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6b, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61,
+	0x67, 0x65, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
+	0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
+	0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x07, 0x68, 0x65, 0x61,
+	0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x65, 0x73,
+	0x74, 0x4c, 0x69, 0x6b, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x48, 0x65,
+	0x61, 0x64, 0x65, 0x72, 0x73, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x16,
+	0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06,
+	0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f,
+	0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x3d,
+	0x0a, 0x07, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x65, 0x61,
+	0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64,
+	0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x24, 0x5a,
+	0x22, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x6c, 0x69, 0x78,
+	0x78, 0x69, 0x72, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x6c,
+	0x69, 0x6b, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_restlike_restLikeMessages_proto_rawDescOnce sync.Once
+	file_restlike_restLikeMessages_proto_rawDescData = file_restlike_restLikeMessages_proto_rawDesc
+)
+
+func file_restlike_restLikeMessages_proto_rawDescGZIP() []byte {
+	file_restlike_restLikeMessages_proto_rawDescOnce.Do(func() {
+		file_restlike_restLikeMessages_proto_rawDescData = protoimpl.X.CompressGZIP(file_restlike_restLikeMessages_proto_rawDescData)
+	})
+	return file_restlike_restLikeMessages_proto_rawDescData
+}
+
+var file_restlike_restLikeMessages_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_restlike_restLikeMessages_proto_goTypes = []interface{}{
+	(*Message)(nil), // 0: restLikeMessages.Message
+	(*Headers)(nil), // 1: restLikeMessages.Headers
+}
+var file_restlike_restLikeMessages_proto_depIdxs = []int32{
+	1, // 0: restLikeMessages.Message.headers:type_name -> restLikeMessages.Headers
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_restlike_restLikeMessages_proto_init() }
+func file_restlike_restLikeMessages_proto_init() {
+	if File_restlike_restLikeMessages_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_restlike_restLikeMessages_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Message); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_restlike_restLikeMessages_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Headers); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_restlike_restLikeMessages_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_restlike_restLikeMessages_proto_goTypes,
+		DependencyIndexes: file_restlike_restLikeMessages_proto_depIdxs,
+		MessageInfos:      file_restlike_restLikeMessages_proto_msgTypes,
+	}.Build()
+	File_restlike_restLikeMessages_proto = out.File
+	file_restlike_restLikeMessages_proto_rawDesc = nil
+	file_restlike_restLikeMessages_proto_goTypes = nil
+	file_restlike_restLikeMessages_proto_depIdxs = nil
+}
diff --git a/restlike/restLikeMessages.proto b/restlike/restLikeMessages.proto
new file mode 100644
index 0000000000000000000000000000000000000000..00156d5443904383deb89f64c391e0009741dc78
--- /dev/null
+++ b/restlike/restLikeMessages.proto
@@ -0,0 +1,30 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+syntax = "proto3";
+package restLikeMessages;
+option go_package = "gitlab.com/elixxir/client/restlike";
+
+// Message are used for sending to and receiving from a RestServer
+message Message {
+  bytes content = 1;
+  Headers headers = 2;
+  uint32 method = 3;
+  string uri = 4;
+  string error = 5;
+}
+
+// Headers allows different configurations for each Request
+// that will be specified in the Request header
+message Headers {
+  // Headers allows for custom headers to be included with a Request
+  bytes headers = 1;
+
+  // Version allows for endpoints to be backwards-compatible
+  // and handle different formats of the same Request
+  uint32 version = 2;
+}
\ No newline at end of file
diff --git a/restlike/restServer.go b/restlike/restServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..dccb13ad3da83850bdd98be9b568dfab5c933102
--- /dev/null
+++ b/restlike/restServer.go
@@ -0,0 +1,67 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	"gitlab.com/elixxir/client/catalog"
+	"gitlab.com/elixxir/client/single"
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/xx_network/primitives/id"
+)
+
+// RestServer allows for clients to make REST-like requests this client
+type RestServer interface {
+	// RegisterEndpoint allows the association of a Callback with
+	// a specific URI and a variety of different REST Method
+	RegisterEndpoint(path URI, method Method, cb Callback) error
+
+	// UnregisterEndpoint removes the Callback associated with
+	// a specific URI and REST Method
+	UnregisterEndpoint(path URI, method Method) error
+
+	// Close the internal RestServer endpoints and external services
+	Close()
+}
+
+// singleServer implements the RestServer interface using single-use
+type singleServer struct {
+	receptionId *id.ID
+	listener    single.Listener
+	endpoints   *Endpoints
+}
+
+// NewSingleServer builds a RestServer with single-use and
+// the provided arguments, then registers necessary external services
+func NewSingleServer(receptionId *id.ID, privKey *cyclic.Int, net single.ListenCmix, e2eGrp *cyclic.Group) RestServer {
+	newServer := &singleServer{
+		receptionId: receptionId,
+		endpoints:   &Endpoints{endpoints: make(map[URI]map[Method]Callback)},
+	}
+	newServer.listener = single.Listen(catalog.RestLike, receptionId, privKey,
+		net, e2eGrp, &singleReceiver{newServer.endpoints})
+	return newServer
+}
+
+// RegisterEndpoint allows the association of a Callback with
+// a specific URI and a variety of different REST Method
+func (r *singleServer) RegisterEndpoint(path URI, method Method, cb Callback) error {
+	return r.endpoints.Add(path, method, cb)
+}
+
+// UnregisterEndpoint removes the Callback associated with
+// a specific URI and REST Method
+func (r *singleServer) UnregisterEndpoint(path URI, method Method) error {
+	return r.endpoints.Remove(path, method)
+}
+
+// Close the internal RestServer endpoints and external services
+func (r *singleServer) Close() {
+	// Clear all internal endpoints
+	r.endpoints = nil
+	// Destroy external services
+	r.listener.Stop()
+}
diff --git a/restlike/singleReceiver.go b/restlike/singleReceiver.go
new file mode 100644
index 0000000000000000000000000000000000000000..7a137631bea709c8b9d7dabbb0888fec60a60cdc
--- /dev/null
+++ b/restlike/singleReceiver.go
@@ -0,0 +1,62 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	"github.com/pkg/errors"
+	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/cmix"
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/elixxir/client/cmix/rounds"
+	"gitlab.com/elixxir/client/single"
+	"google.golang.org/protobuf/proto"
+	"time"
+)
+
+// processor is the reception handler for a RestServer
+type singleReceiver struct {
+	endpoints *Endpoints
+}
+
+// Callback is the handler for single-use message reception for a RestServer
+// Automatically responds to invalid endpoint requests
+func (s *singleReceiver) Callback(req *single.Request, receptionId receptionID.EphemeralIdentity, rounds []rounds.Round) {
+	// Unmarshal the request payload
+	newMessage := &Message{}
+	err := proto.Unmarshal(req.GetPayload(), newMessage)
+	if err != nil {
+		jww.ERROR.Printf("Unable to unmarshal restlike message: %+v", err)
+		return
+	}
+
+	var respondErr error
+	if cb, err := s.endpoints.Get(URI(newMessage.GetUri()), Method(newMessage.GetMethod())); err == nil {
+		// Send the payload to the proper Callback if it exists and respond with the result
+		respondErr = respond(cb(newMessage), req)
+	} else {
+		// If no callback, automatically send an error response
+		respondErr = respond(&Message{Error: err.Error()}, req)
+	}
+	if respondErr != nil {
+		jww.ERROR.Printf("Unable to respond to request: %+v", err)
+	}
+}
+
+// respond to a single.Request with the given Message
+func respond(response *Message, req *single.Request) error {
+	payload, err := proto.Marshal(response)
+	if err != nil {
+		return errors.Errorf("unable to marshal restlike response message: %+v", err)
+	}
+
+	// TODO: Parameterize params and timeout
+	_, err = req.Respond(payload, cmix.GetDefaultCMIXParams(), 30*time.Second)
+	if err != nil {
+		return errors.Errorf("unable to send restlike response message: %+v", err)
+	}
+	return nil
+}
diff --git a/restlike/singleReceiver_test.go b/restlike/singleReceiver_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..56a46586daaf6411d589b79c0e179ade2ce4e017
--- /dev/null
+++ b/restlike/singleReceiver_test.go
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/elixxir/client/single"
+	"testing"
+)
+
+// Test failure of proto unmarshal
+func TestSingleReceiver_Callback_FailUnmarshal(t *testing.T) {
+	ep := &Endpoints{endpoints: make(map[URI]map[Method]Callback)}
+	receiver := singleReceiver{endpoints: ep}
+
+	testReq := single.BuildTestRequest(make([]byte, 0), t)
+	receiver.Callback(testReq, receptionID.EphemeralIdentity{}, nil)
+}
+
+// Test happy path
+//func TestSingleReceiver_Callback(t *testing.T) {
+//	ep := &Endpoints{endpoints: make(map[URI]map[Method]Callback)}
+//	resultChan := make(chan interface{}, 1)
+//	cb := func(*Message) *Message {
+//		resultChan <- ""
+//		return nil
+//	}
+//	testPath := URI("test/path")
+//	testMethod := Get
+//	testMessage := &Message{
+//		Content: []byte("test"),
+//		Headers: nil,
+//		Method:  uint32(testMethod),
+//		Uri:     string(testPath),
+//		Error:   "",
+//	}
+//
+//	err := ep.Add(testPath, testMethod, cb)
+//	if err != nil {
+//		t.Errorf(err.Error())
+//	}
+//	receiver := singleReceiver{endpoints: ep}
+//
+//	testPayload, err := proto.Marshal(testMessage)
+//	if err != nil {
+//		t.Errorf(err.Error())
+//	}
+//	testReq := single.BuildTestRequest(testPayload, t)
+//	receiver.Callback(testReq, receptionID.EphemeralIdentity{}, nil)
+//
+//	select {
+//	case _ = <-resultChan:
+//	case <-time.After(3 * time.Second):
+//		t.Errorf("Test SingleReceiver timed out!")
+//	}
+//}
diff --git a/restlike/singleRequest.go b/restlike/singleRequest.go
new file mode 100644
index 0000000000000000000000000000000000000000..774f112c43745c0a36b9692ceb209bd4e8b783f1
--- /dev/null
+++ b/restlike/singleRequest.go
@@ -0,0 +1,84 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/catalog"
+	"gitlab.com/elixxir/client/single"
+	"gitlab.com/elixxir/crypto/contact"
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/xx_network/crypto/csprng"
+	"google.golang.org/protobuf/proto"
+)
+
+// SingleRequest allows for making REST-like requests to a RestServer using single-use messages
+// Can be used as stateful or declared inline without state
+type SingleRequest struct {
+	Net    single.Cmix
+	Rng    csprng.Source
+	E2eGrp *cyclic.Group
+}
+
+// Request provides several Method of sending Data to the given URI
+// and blocks until the Message is returned
+func (s *SingleRequest) Request(recipient contact.Contact, method Method, path URI,
+	content Data, headers *Headers, singleParams single.RequestParams) (*Message, error) {
+	// Build the Message
+	newMessage := &Message{
+		Content: content,
+		Headers: headers,
+		Method:  uint32(method),
+		Uri:     string(path),
+	}
+	msg, err := proto.Marshal(newMessage)
+	if err != nil {
+		return nil, err
+	}
+
+	// Build callback for the single-use response
+	signalChannel := make(chan *Message, 1)
+	cb := func(msg *Message) {
+		signalChannel <- msg
+	}
+
+	// Transmit the Message
+	_, _, err = single.TransmitRequest(recipient, catalog.RestLike, msg,
+		&singleResponse{responseCallback: cb}, singleParams, s.Net, s.Rng, s.E2eGrp)
+	if err != nil {
+		return nil, err
+	}
+
+	// Block waiting for single-use response
+	jww.DEBUG.Printf("Restlike waiting for single-use response from %s...", recipient.ID.String())
+	newResponse := <-signalChannel
+	jww.DEBUG.Printf("Restlike single-use response received from %s", recipient.ID.String())
+
+	return newResponse, nil
+}
+
+// AsyncRequest provides several Method of sending Data to the given URI
+// and will return the Message to the given Callback when received
+func (s *SingleRequest) AsyncRequest(recipient contact.Contact, method Method, path URI,
+	content Data, headers *Headers, cb RequestCallback, singleParams single.RequestParams) error {
+	// Build the Message
+	newMessage := &Message{
+		Content: content,
+		Headers: headers,
+		Method:  uint32(method),
+		Uri:     string(path),
+	}
+	msg, err := proto.Marshal(newMessage)
+	if err != nil {
+		return err
+	}
+
+	// Transmit the Message
+	_, _, err = single.TransmitRequest(recipient, catalog.RestLike, msg,
+		&singleResponse{responseCallback: cb}, singleParams, s.Net, s.Rng, s.E2eGrp)
+	return err
+}
diff --git a/restlike/singleResponse.go b/restlike/singleResponse.go
new file mode 100644
index 0000000000000000000000000000000000000000..41e21b6061c110eaaa06fc25a472bf0326ca1ff3
--- /dev/null
+++ b/restlike/singleResponse.go
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/elixxir/client/cmix/rounds"
+	"google.golang.org/protobuf/proto"
+)
+
+// processor is the response handler for a Request
+type singleResponse struct {
+	responseCallback RequestCallback
+}
+
+// Callback is the handler for single-use message responses for a Request
+func (s *singleResponse) Callback(payload []byte, receptionID receptionID.EphemeralIdentity, rounds []rounds.Round, err error) {
+	newMessage := &Message{}
+
+	// Handle response errors
+	if err != nil {
+		newMessage.Error = err.Error()
+		s.responseCallback(newMessage)
+		return
+	}
+
+	// Unmarshal the payload
+	err = proto.Unmarshal(payload, newMessage)
+	if err != nil {
+		newMessage.Error = err.Error()
+	}
+
+	// Send the response payload to the responseCallback
+	s.responseCallback(newMessage)
+}
diff --git a/restlike/singleResponse_test.go b/restlike/singleResponse_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0381b452cca26c9d892abfe2b8329569a1eef689
--- /dev/null
+++ b/restlike/singleResponse_test.go
@@ -0,0 +1,96 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	"bytes"
+	"github.com/pkg/errors"
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"google.golang.org/protobuf/proto"
+	"testing"
+	"time"
+)
+
+// Test happy path
+func TestSingleResponse_Callback(t *testing.T) {
+	resultChan := make(chan *Message, 1)
+	cb := func(input *Message) {
+		resultChan <- input
+	}
+	testPath := "test/path"
+	testMethod := Get
+	testMessage := &Message{
+		Content: []byte("test"),
+		Headers: nil,
+		Method:  uint32(testMethod),
+		Uri:     testPath,
+		Error:   "",
+	}
+
+	response := singleResponse{cb}
+
+	testPayload, err := proto.Marshal(testMessage)
+	if err != nil {
+		t.Errorf(err.Error())
+	}
+	response.Callback(testPayload, receptionID.EphemeralIdentity{}, nil, nil)
+
+	select {
+	case result := <-resultChan:
+		if result.Uri != testPath {
+			t.Errorf("Mismatched uri")
+		}
+		if result.Method != uint32(testMethod) {
+			t.Errorf("Mismatched method")
+		}
+		if !bytes.Equal(testMessage.Content, result.Content) {
+			t.Errorf("Mismatched content")
+		}
+	case <-time.After(3 * time.Second):
+		t.Errorf("Test SingleResponse timed out!")
+	}
+}
+
+// Test error input path
+func TestSingleResponse_Callback_Err(t *testing.T) {
+	resultChan := make(chan *Message, 1)
+	cb := func(input *Message) {
+		resultChan <- input
+	}
+	response := singleResponse{cb}
+
+	response.Callback(nil, receptionID.EphemeralIdentity{}, nil, errors.New("test"))
+
+	select {
+	case result := <-resultChan:
+		if len(result.Error) == 0 {
+			t.Errorf("Expected cb error!")
+		}
+	case <-time.After(3 * time.Second):
+		t.Errorf("Test SingleResponse input error timed out!")
+	}
+}
+
+// Test proto error path
+func TestSingleResponse_Callback_ProtoErr(t *testing.T) {
+	resultChan := make(chan *Message, 1)
+	cb := func(input *Message) {
+		resultChan <- input
+	}
+	response := singleResponse{cb}
+
+	response.Callback([]byte("test"), receptionID.EphemeralIdentity{}, nil, nil)
+
+	select {
+	case result := <-resultChan:
+		if len(result.Error) == 0 {
+			t.Errorf("Expected cb proto error!")
+		}
+	case <-time.After(3 * time.Second):
+		t.Errorf("Test SingleResponse proto error timed out!")
+	}
+}
diff --git a/restlike/types.go b/restlike/types.go
new file mode 100644
index 0000000000000000000000000000000000000000..247943844613a43c99b5d666f412a5c53f078471
--- /dev/null
+++ b/restlike/types.go
@@ -0,0 +1,116 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	"github.com/pkg/errors"
+	"sync"
+)
+
+// URI defines the destination endpoint of a Request
+type URI string
+
+// Data provides a generic structure for data sent with a Request or received in a Message
+// NOTE: The way this is encoded is up to the implementation. For example, protobuf or JSON
+type Data []byte
+
+// Method defines the possible Request types
+type Method uint32
+
+// RequestCallback provides the ability to make asynchronous Request
+// in order to get the Message later without blocking
+type RequestCallback func(*Message)
+
+// Callback serves as an Endpoint function to be called when a Request is received
+// Should return the desired response to be sent back to the sender
+type Callback func(*Message) *Message
+
+const (
+	// Undefined default value
+	Undefined Method = iota
+	// Get retrieve an existing resource.
+	Get
+	// Post creates a new resource.
+	Post
+	// Put updates an existing resource.
+	Put
+	// Patch partially updates an existing resource.
+	Patch
+	// Delete a resource.
+	Delete
+)
+
+// methodStrings is a map of Method values back to their constant names for printing
+var methodStrings = map[Method]string{
+	Undefined: "undefined",
+	Get:       "get",
+	Post:      "post",
+	Put:       "put",
+	Patch:     "patch",
+	Delete:    "delete",
+}
+
+// String returns the Method as a human-readable name.
+func (m Method) String() string {
+	if methodStr, ok := methodStrings[m]; ok {
+		return methodStr
+	}
+	return methodStrings[Undefined]
+}
+
+// Endpoints represents a map of internal endpoints for a RestServer
+type Endpoints struct {
+	endpoints map[URI]map[Method]Callback
+	sync.RWMutex
+}
+
+// Add a new Endpoint
+// Returns an error if Endpoint already exists
+func (e *Endpoints) Add(path URI, method Method, cb Callback) error {
+	e.Lock()
+	defer e.Unlock()
+
+	if _, ok := e.endpoints[path]; !ok {
+		e.endpoints[path] = make(map[Method]Callback)
+	}
+	if _, ok := e.endpoints[path][method]; ok {
+		return errors.Errorf("unable to RegisterEndpoint: %s/%s already exists", path, method)
+	}
+	e.endpoints[path][method] = cb
+	return nil
+}
+
+// Get an Endpoint
+// Returns an error if Endpoint does not exist
+func (e *Endpoints) Get(path URI, method Method) (Callback, error) {
+	e.RLock()
+	defer e.RUnlock()
+
+	if _, ok := e.endpoints[path]; !ok {
+		return nil, errors.Errorf("unable to locate endpoint: %s", path)
+	}
+	if _, innerOk := e.endpoints[path][method]; !innerOk {
+		return nil, errors.Errorf("unable to locate endpoint: %s/%s", path, method)
+	}
+	return e.endpoints[path][method], nil
+}
+
+// Remove an Endpoint
+// Returns an error if Endpoint does not exist
+func (e *Endpoints) Remove(path URI, method Method) error {
+	if _, err := e.Get(path, method); err != nil {
+		return errors.Errorf("unable to UnregisterEndpoint: %s", err.Error())
+	}
+
+	e.Lock()
+	defer e.Unlock()
+	delete(e.endpoints[path], method)
+	if len(e.endpoints[path]) == 0 {
+		delete(e.endpoints, path)
+	}
+	return nil
+}
diff --git a/restlike/types_test.go b/restlike/types_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7b5b1a9d5b1bdaebc556ee4521379e48478019cb
--- /dev/null
+++ b/restlike/types_test.go
@@ -0,0 +1,47 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import "testing"
+
+// Full test for all add/get/remove cases
+func TestEndpoints(t *testing.T) {
+	ep := &Endpoints{endpoints: make(map[URI]map[Method]Callback)}
+	cb := func(*Message) *Message {
+		return nil
+	}
+
+	testPath := URI("test/path")
+	testMethod := Get
+	err := ep.Add(testPath, testMethod, cb)
+	if _, ok := ep.endpoints[testPath][testMethod]; err != nil || !ok {
+		t.Errorf("Failed to add endpoint: %+v", err)
+	}
+	err = ep.Add(testPath, testMethod, cb)
+	if _, ok := ep.endpoints[testPath][testMethod]; err == nil || !ok {
+		t.Errorf("Expected failure to add endpoint")
+	}
+
+	resultCb, err := ep.Get(testPath, testMethod)
+	if resultCb == nil || err != nil {
+		t.Errorf("Expected to get endpoint: %+v", err)
+	}
+
+	err = ep.Remove(testPath, testMethod)
+	if _, ok := ep.endpoints[testPath][testMethod]; err != nil || ok {
+		t.Errorf("Failed to remove endpoint: %+v", err)
+	}
+	err = ep.Remove(testPath, testMethod)
+	if _, ok := ep.endpoints[testPath][testMethod]; err == nil || ok {
+		t.Errorf("Expected failure to remove endpoint")
+	}
+
+	resultCb, err = ep.Get(testPath, testMethod)
+	if resultCb != nil || err == nil {
+		t.Errorf("Expected failure to get endpoint: %+v", err)
+	}
+}
diff --git a/single/listener_test.go b/single/listener_test.go
index 3e2755a8f1d85404d2a0af6bdc7a0a8bf0c8be07..366907e527492c921d382bebead2499cf7d7a18e 100644
--- a/single/listener_test.go
+++ b/single/listener_test.go
@@ -220,6 +220,10 @@ func Test_listener_Stop(t *testing.T) {
 	}
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// Mock cMix Client                                                           //
+////////////////////////////////////////////////////////////////////////////////
+
 type mockListenCmixHandler struct {
 	fingerprintMap map[id.ID]map[format.Fingerprint][]cMixMsg.Processor
 	serviceMap     map[id.ID]map[string][]cMixMsg.Processor
diff --git a/single/message/requestPart_test.go b/single/message/requestPart_test.go
index 75090ffd6823c5c6844fbaa60bea64057f380fca..8f3bbdf8e7cafee33432674fdaad8786dbfad5da 100644
--- a/single/message/requestPart_test.go
+++ b/single/message/requestPart_test.go
@@ -159,7 +159,7 @@ func TestRequestPart_SetContents_GetContents_GetContentsSize_GetMaxContentsSize(
 	}
 
 	if externalPayloadSize-reqPartMinSize != rmp.GetMaxContentsSize() {
-		t.Errorf("GetMaxContentsSize failed to return the expected max "+
+		t.Errorf("GetMaxResponsePartSize failed to return the expected max "+
 			"contents size.\nexpected: %d\nrecieved: %d",
 			externalPayloadSize-reqPartMinSize, rmp.GetMaxContentsSize())
 	}
diff --git a/single/message/request_test.go b/single/message/request_test.go
index b2ee1dca55ecef72b534972b2303d43d63cf252a..8bdc81cf91acd357db9d471bffc2235fe34ef2d2 100644
--- a/single/message/request_test.go
+++ b/single/message/request_test.go
@@ -423,7 +423,7 @@ func TestRequestPayload_GetContents_GetContentsSize_GetMaxContentsSize(t *testin
 	}
 
 	if format.MinimumPrimeSize-requestMinSize != mp.GetMaxContentsSize() {
-		t.Errorf("GetMaxContentsSize did not return the expected size."+
+		t.Errorf("GetMaxResponsePartSize did not return the expected size."+
 			"\nexpected: %d\nreceived: %d",
 			format.MinimumPrimeSize-requestMinSize, mp.GetMaxContentsSize())
 	}
diff --git a/single/message/responsePart_test.go b/single/message/responsePart_test.go
index f8a315c13f66d0f7a261844f4593d73d98ee8cf3..45c06773f113fb64f3e67db2cbeaba4d3bef6bb7 100644
--- a/single/message/responsePart_test.go
+++ b/single/message/responsePart_test.go
@@ -170,7 +170,7 @@ func TestResponsePart_SetContents_GetContents_GetContentsSize_GetMaxContentsSize
 	}
 
 	if externalPayloadSize-resPartMinSize != rmp.GetMaxContentsSize() {
-		t.Errorf("GetMaxContentsSize failed to return the expected max "+
+		t.Errorf("GetMaxResponsePartSize failed to return the expected max "+
 			"contents size.\nexpected: %d\nrecieved: %d",
 			externalPayloadSize-resPartMinSize, rmp.GetMaxContentsSize())
 	}
diff --git a/single/receivedRequest.go b/single/receivedRequest.go
index 5026f353065f0405e497e18be194b3cde82477cc..cb445e8b0a02b15d6458dd824a3cdd478d1167f3 100644
--- a/single/receivedRequest.go
+++ b/single/receivedRequest.go
@@ -18,21 +18,25 @@ import (
 	"gitlab.com/xx_network/primitives/id/ephemeral"
 	"sync"
 	"sync/atomic"
+	"testing"
 	"time"
 )
 
-// Request contains the information to respond to a single-use contact.
+// Request contains the information contained in a single-use request message.
 type Request struct {
-	sender         *id.ID      // ID of the person to respond to
+	sender         *id.ID      // ID of the sender/ID to send response to
 	senderPubKey   *cyclic.Int // Public key of the sender
 	dhKey          *cyclic.Int // DH key
 	tag            string      // Identifies which callback to use
 	maxParts       uint8       // Max number of messages allowed in reply
-	used           *uint32     // Atomic variable
-	requestPayload []byte
-	net            RequestCmix
+	used           *uint32     // Set when response is sent
+	requestPayload []byte      // Request message payload
+
+	net RequestCmix
 }
 
+// RequestCmix interface matches a subset of the cmix.Client methods used by the
+// Request for easier testing.
 type RequestCmix interface {
 	GetMaxMessageLength() int
 	Send(recipient *id.ID, fingerprint format.Fingerprint,
@@ -41,55 +45,13 @@ type RequestCmix interface {
 	GetInstance() *network.Instance
 }
 
-// GetMaxParts returns the maximum number of message parts that can be sent in a
-// reply.
-func (r Request) GetMaxParts() uint8 {
-	return r.maxParts
-}
-
-// GetMaxResponseLength returns the maximum total payload size, which is the
-// maximum size of each individual part multiplied by the maximum number of parts
-func (r Request) GetMaxResponseLength() int {
-	return r.GetMaxContentsSize() * int(r.GetMaxParts())
-}
-
-// GetMaxContentsSize returns maximum payload size for an individual part
-func (r Request) GetMaxContentsSize() int {
-	responseMsg := message.NewResponsePart(r.net.GetMaxMessageLength())
-	return responseMsg.GetMaxContentsSize()
-}
-
-// GetPartner returns a copy of the sender ID.
-func (r Request) GetPartner() *id.ID {
-	return r.sender.DeepCopy()
-}
-
-// GetTag returns the tag for the request.
-func (r Request) GetTag() string {
-	return r.tag
-}
-
-// GetPayload returns the payload that came in the request
-func (r Request) GetPayload() []byte {
-	return r.requestPayload
-}
-
-// String returns a string of the Contact structure.
-func (r Request) String() string {
-	return fmt.Sprintf(
-		"{sender:%s senderPubKey:%s dhKey:%s tag:%q maxParts:%d used:%p(%d) "+
-			"requestPayload:%q net:%p}",
-		r.sender, r.senderPubKey.Text(10), r.dhKey.Text(10), r.tag, r.maxParts,
-		r.used, atomic.LoadUint32(r.used), r.requestPayload, r.net)
-}
-
 // Respond is used to respond to the request. It sends a payload up to
 // Request.GetMaxResponseLength. It will chunk the message into multiple cMix
 // messages if it is too long for a single message. It will fail if a single
 // cMix message cannot be sent.
-func (r Request) Respond(payload []byte, cMixParams cmix.CMIXParams,
+func (r *Request) Respond(payload []byte, cMixParams cmix.CMIXParams,
 	timeout time.Duration) ([]id.Round, error) {
-	// make sure this has only been run once
+	// Make sure this has only been run once
 	newRun := atomic.CompareAndSwapUint32(r.used, 0, 1)
 	if !newRun {
 		return nil, errors.Errorf("cannot respond to single-use response " +
@@ -185,13 +147,57 @@ func (r Request) Respond(payload []byte, cMixParams cmix.CMIXParams,
 	return rounds, nil
 }
 
+// GetMaxParts returns the maximum number of messages allowed to send in the
+// reply.
+func (r *Request) GetMaxParts() uint8 {
+	return r.maxParts
+}
+
+// GetMaxResponseLength returns the maximum size of the entire response message.
+func (r *Request) GetMaxResponseLength() int {
+	return r.GetMaxResponsePartSize() * int(r.GetMaxParts())
+}
+
+// GetMaxResponsePartSize returns maximum payload size for an individual part of
+// the response message.
+func (r *Request) GetMaxResponsePartSize() int {
+	responseMsg := message.NewResponsePart(r.net.GetMaxMessageLength())
+	return responseMsg.GetMaxContentsSize()
+}
+
+// GetPartner returns a copy of the sender ID.
+func (r *Request) GetPartner() *id.ID {
+	return r.sender.DeepCopy()
+}
+
+// GetTag returns the tag for the request.
+func (r *Request) GetTag() string {
+	return r.tag
+}
+
+// GetPayload returns the payload that came in the request
+func (r *Request) GetPayload() []byte {
+	return r.requestPayload
+}
+
+// GoString returns string showing the values of all the fields of Request.
+// Adheres to the fmt.GoStringer interface.
+func (r *Request) GoString() string {
+	return fmt.Sprintf(
+		"{sender:%s senderPubKey:%s dhKey:%s tag:%q maxParts:%d used:%p(%d) "+
+			"requestPayload:%q net:%p}",
+		r.sender, r.senderPubKey.Text(10), r.dhKey.Text(10), r.tag, r.maxParts,
+		r.used, atomic.LoadUint32(r.used), r.requestPayload, r.net)
+}
+
 // partitionResponse breaks a payload into its sub payloads for sending.
-func partitionResponse(payload []byte, cmixMessageLength int, maxParts uint8) []message.ResponsePart {
+func partitionResponse(payload []byte, cmixMessageLength int,
+	maxParts uint8) []message.ResponsePart {
 	responseMsg := message.NewResponsePart(cmixMessageLength)
 
 	// Split payloads
-	payloadParts := splitPayload(payload, responseMsg.GetMaxContentsSize(),
-		int(maxParts))
+	payloadParts := splitPayload(
+		payload, responseMsg.GetMaxContentsSize(), int(maxParts))
 
 	// Create messages
 	parts := make([]message.ResponsePart, len(payloadParts))
@@ -218,3 +224,17 @@ func splitPayload(payload []byte, maxSize, maxParts int) [][]byte {
 	}
 	return parts
 }
+
+// BuildTestRequest can be used for mocking a Request
+func BuildTestRequest(payload []byte, t *testing.T) *Request {
+	return &Request{
+		sender:         nil,
+		senderPubKey:   nil,
+		dhKey:          nil,
+		tag:            "",
+		maxParts:       0,
+		used:           nil,
+		requestPayload: payload,
+		net:            nil,
+	}
+}
diff --git a/single/receivedRequest_test.go b/single/receivedRequest_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d5d8b15fb500270160f657ad82db712094a61e88
--- /dev/null
+++ b/single/receivedRequest_test.go
@@ -0,0 +1,243 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+package single
+
+import (
+	"bytes"
+	"gitlab.com/elixxir/client/cmix"
+	cmixMsg "gitlab.com/elixxir/client/cmix/message"
+	"gitlab.com/elixxir/client/single/message"
+	"gitlab.com/elixxir/comms/network"
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/elixxir/crypto/e2e/singleUse"
+	"gitlab.com/elixxir/primitives/format"
+	"gitlab.com/xx_network/comms/connect"
+	"gitlab.com/xx_network/crypto/large"
+	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/id/ephemeral"
+	"gitlab.com/xx_network/primitives/ndf"
+	"reflect"
+	"testing"
+	"time"
+)
+
+// Tests that RequestCmix adheres to the cmix.Client interface.
+var _ RequestCmix = (cmix.Client)(nil)
+
+func TestRequest_Respond(t *testing.T) {
+	grp := cyclic.NewGroup(large.NewInt(173), large.NewInt(2))
+	privKey := grp.NewInt(42)
+	used := uint32(0)
+	payloadChan := make(chan format.Message, 10)
+	net := newMockRequestCmix(payloadChan, t)
+	r := &Request{
+		sender:         id.NewIdFromString("singleUseRequest", id.User, t),
+		senderPubKey:   grp.NewInt(42),
+		dhKey:          grp.ExpG(privKey, grp.NewInt(1)),
+		tag:            "requestTag",
+		maxParts:       10,
+		used:           &used,
+		requestPayload: []byte("test"),
+		net:            net,
+	}
+
+	payload := []byte("My Response.")
+	cMixParams := cmix.GetDefaultCMIXParams()
+
+	_, err := r.Respond(payload, cMixParams, 0)
+	if err != nil {
+		t.Errorf("Respond returned an error: %+v", err)
+	}
+
+	select {
+	case ecrMsg := <-payloadChan:
+		c := &cypher{
+			dhKey:  r.dhKey,
+			num:    0,
+			newKey: singleUse.NewResponseKey,
+			newFp:  singleUse.NewResponseFingerprint,
+		}
+
+		decrypted, err := c.decrypt(ecrMsg.GetContents(), ecrMsg.GetMac())
+		if err != nil {
+			t.Errorf("Failed to decrypt single-use response payload: %+v", err)
+			return
+		}
+
+		// Unmarshal the cMix message contents to a request message
+		responsePart, err := message.UnmarshalResponsePart(decrypted)
+		if err != nil {
+			t.Errorf("could not unmarshal ResponsePart: %+v", err)
+		}
+
+		if !bytes.Equal(payload, responsePart.GetContents()) {
+			t.Errorf("Did not receive expected payload."+
+				"\nexpected: %q\nreceived: %q",
+				payload, responsePart.GetContents())
+		}
+
+	case <-time.After(15 * time.Millisecond):
+		t.Errorf("Timed out waiting for response.")
+	}
+}
+
+// Tests that partitionResponse creates a list of message.ResponsePart each with
+// the expected contents and part number.
+func Test_partitionResponse(t *testing.T) {
+	cmixMessageLength := 10
+
+	maxSize := message.NewResponsePart(cmixMessageLength).GetMaxContentsSize()
+	maxParts := uint8(10)
+	payload := []byte("012345678901234567890123456789012345678901234567890123" +
+		"45678901234567890123456789012345678901234567890123456789")
+	expectedParts := [][]byte{
+		payload[:maxSize],
+		payload[maxSize : 2*maxSize],
+		payload[2*maxSize : 3*maxSize],
+		payload[3*maxSize : 4*maxSize],
+		payload[4*maxSize : 5*maxSize],
+		payload[5*maxSize : 6*maxSize],
+		payload[6*maxSize : 7*maxSize],
+		payload[7*maxSize : 8*maxSize],
+		payload[8*maxSize : 9*maxSize],
+		payload[9*maxSize : 10*maxSize],
+	}
+
+	parts := partitionResponse(payload, cmixMessageLength, maxParts)
+
+	for i, part := range parts {
+		if part.GetNumParts() != maxParts {
+			t.Errorf("Part #%d has wrong numParts.\nexpected: %d\nreceived: %d",
+				i, maxParts, part.GetNumParts())
+		}
+		if int(part.GetPartNum()) != i {
+			t.Errorf("Part #%d has wrong part num.\nexpected: %d\nreceived: %d",
+				i, i, part.GetPartNum())
+		}
+		if !bytes.Equal(part.GetContents(), expectedParts[i]) {
+			t.Errorf("Part #%d has wrong contents.\nexpected: %q\nreceived: %q",
+				i, expectedParts[i], part.GetContents())
+		}
+	}
+}
+
+// Tests that splitPayload splits the payload to match the expected.
+func Test_splitPayload(t *testing.T) {
+	maxSize := 5
+	maxParts := 10
+	payload := []byte("012345678901234567890123456789012345678901234567890123" +
+		"45678901234567890123456789012345678901234567890123456789")
+	expectedParts := [][]byte{
+		payload[:maxSize],
+		payload[maxSize : 2*maxSize],
+		payload[2*maxSize : 3*maxSize],
+		payload[3*maxSize : 4*maxSize],
+		payload[4*maxSize : 5*maxSize],
+		payload[5*maxSize : 6*maxSize],
+		payload[6*maxSize : 7*maxSize],
+		payload[7*maxSize : 8*maxSize],
+		payload[8*maxSize : 9*maxSize],
+		payload[9*maxSize : 10*maxSize],
+	}
+
+	testParts := splitPayload(payload, maxSize, maxParts)
+
+	if !reflect.DeepEqual(expectedParts, testParts) {
+		t.Errorf("splitPayload() failed to correctly split the payload."+
+			"\nexpected: %s\nreceived: %s", expectedParts, testParts)
+	}
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Mock cMix Client                                                           //
+////////////////////////////////////////////////////////////////////////////////
+
+type mockRequestCmix struct {
+	sendPayload   chan format.Message
+	numPrimeBytes int
+	instance      *network.Instance
+}
+
+func newMockRequestCmix(sendPayload chan format.Message, t *testing.T) *mockRequestCmix {
+	instanceComms := &connect.ProtoComms{Manager: connect.NewManagerTesting(t)}
+	grp := cyclic.NewGroup(large.NewInt(173), large.NewInt(2))
+	thisInstance, err := network.NewInstanceTesting(
+		instanceComms, getNDF(), getNDF(), grp, grp, t)
+	if err != nil {
+		t.Errorf("Failed to create new test instance: %v", err)
+	}
+
+	return &mockRequestCmix{
+		sendPayload:   sendPayload,
+		numPrimeBytes: 97,
+		instance:      thisInstance,
+	}
+}
+
+func (m *mockRequestCmix) GetMaxMessageLength() int {
+	msg := format.NewMessage(m.numPrimeBytes)
+	return msg.ContentsSize()
+}
+
+func (m *mockRequestCmix) Send(_ *id.ID, fp format.Fingerprint,
+	_ cmixMsg.Service, payload, mac []byte, _ cmix.CMIXParams) (
+	id.Round, ephemeral.Id, error) {
+	msg := format.NewMessage(m.numPrimeBytes)
+	msg.SetMac(mac)
+	msg.SetKeyFP(fp)
+	msg.SetContents(payload)
+	m.sendPayload <- msg
+
+	return 0, ephemeral.Id{}, nil
+}
+
+func (m *mockRequestCmix) GetInstance() *network.Instance {
+	return m.instance
+}
+
+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",
+		},
+	}
+}