diff --git a/README.md b/README.md
index d546e85140536803b657303fad068a9d9436dfdd..c89b714f0330336fac8c1bc6dbe88eb0587fef53 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,6 @@ Optional args:
 |--version|-V|Show the generated version information. Run `$ go generate cmd/version.go` if the information is out of date.|--version|
 |--sessionfile|-f|File path for storing the session. If not specified, the session will be stored in RAM and won't persist.|-f mySuperCoolSessionFile|
 |--noBlockingTransmission| |Disables transmission rate limiting (useful for dummy client)|--noBlockingTransmission|
-|--mint| |Creates some coins for this user for testing and demos|--mint|
 |--help|-h|Prints a help message with all of these flags|-h|
 |--gwcertpath|-c|Enables TLS by passing in path to the gateway certificate file|-c "~/Documents/gateway.cert"|
 |--registrationcertpath|-r|Enables TLS by passing in path to the registration server certificate file|-r "~/Documents/registration.cert"|
@@ -56,6 +55,13 @@ Optional args:
 |--dummyfrequency| |How often dummy messages should be sent per second. This flag is likely to be replaced when we implement better dummy message sending.|--dummyfrequency 0.5|
 |--end2end| |Send messages with E2E encryption to destination user|--end2end|
 |--keyParams| |Set E2E key generation parameters. Pass values in comma separated list, with the following order: MinKeys,MaxKeys,NumRekeys,TTLScalar,MinNumKeys|--keyParams 100,200,32,1.2,50|
+|--email|-E|Email to register for User Discovery (default "default@default.com")||
+|--nick| |Nickname to register for User Discovery (default "Default")||
+|--ndfPubKey|-p|Path to the public key for the network definition JSON file|
+|--ndf|-n|Path to the network definition JSON file|
+|--ndfVerifySignature| |Specifies if the NDF should be loaded without the signature. defaults to true|
+|--ndfRegistration| |Overwrite the Registration values for the NDF|
+|--ndfUDB| |Overwrite the UDB values for the NDF|
 
 ##Project Structure
 
@@ -84,9 +90,7 @@ the client's part of the cipher.
 without seriously considering the alternatives. Most important is the Log 
 variable:
 
-```go
 globals.Log.ERROR.Println("this is an error")
-```
 
 Using this global Log variable allows external users of jww logging, like the 
 console UI, to see and print log messages from the client library if they need
diff --git a/api/client.go b/api/client.go
index 40c45875eb79c7809ee9fa9621622dd4881704e5..0a770a2162f3beca0408765ebe297ecc4178041a 100644
--- a/api/client.go
+++ b/api/client.go
@@ -31,8 +31,10 @@ import (
 	"gitlab.com/elixxir/crypto/large"
 	"gitlab.com/elixxir/crypto/registration"
 	"gitlab.com/elixxir/crypto/signature"
-	"gitlab.com/elixxir/primitives/format"
+	"gitlab.com/elixxir/crypto/signature/rsa"
+	"gitlab.com/elixxir/primitives/circuit"
 	"gitlab.com/elixxir/primitives/id"
+	"gitlab.com/elixxir/primitives/ndf"
 	"gitlab.com/elixxir/primitives/switchboard"
 	"google.golang.org/grpc/credentials"
 	goio "io"
@@ -40,11 +42,11 @@ import (
 )
 
 type Client struct {
-	storage     globals.Storage
-	sess        user.Session
-	comm        io.Communications
-	gwAddresses []io.ConnAddr
-	regAddress  io.ConnAddr
+	storage  globals.Storage
+	session  user.Session
+	comm     io.Communications
+	ndf      *ndf.NetworkDefinition
+	topology *circuit.Circuit
 }
 
 // Populates a text message and returns its wire representation
@@ -55,15 +57,55 @@ func FormatTextMessage(message string) []byte {
 		Message: message,
 		Time:    time.Now().Unix(),
 	}
+
 	wireRepresentation, _ := proto.Marshal(&textMessage)
 	return wireRepresentation
 }
 
+// VerifyNDF verifies the signature of the network definition file (NDF) and
+// returns the structure. Panics when the NDF string cannot be decoded and when
+// the signature cannot be verified. If the NDF public key is empty, then the
+// signature verification is skipped and warning is printed.
+func VerifyNDF(ndfString, ndfPub string) *ndf.NetworkDefinition {
+	// Decode NDF string to a NetworkDefinition and its signature
+	ndfJSON, ndfSignature, err := ndf.DecodeNDF(ndfString)
+	if err != nil {
+		globals.Log.FATAL.Panicf("Could not decode NDF: %v", err)
+	}
+
+	// If there is no public key, then skip verification and print warning
+	if ndfPub == "" {
+		globals.Log.WARN.Printf("Running without signed network " +
+			"definition file")
+	} else {
+		// Get public key
+		pubKey, err := rsa.LoadPublicKeyFromPem([]byte(ndfPub))
+		if err != nil {
+			globals.Log.FATAL.Panicf("Could not load public key: %v", err)
+		}
+
+		// Hash NDF JSON
+		opts := rsa.NewDefaultOptions()
+		rsaHash := opts.Hash.New()
+		rsaHash.Write(ndfJSON.Serialize())
+
+		// Verify signature
+		err = rsa.Verify(
+			pubKey, opts.Hash, rsaHash.Sum(nil), ndfSignature, nil)
+
+		if err != nil {
+			globals.Log.FATAL.Panicf("Could not verify NDF: %v", err)
+		}
+	}
+
+	return ndfJSON
+}
+
 // Creates a new Client using the storage mechanism provided.
 // If none is provided, a default storage using OS file access
 // is created
 // returns a new Client object, and an error if it fails
-func NewClient(s globals.Storage, loc string) (*Client, error) {
+func NewClient(s globals.Storage, loc string, ndfJSON *ndf.NetworkDefinition) (*Client, error) {
 	var store globals.Storage
 	if s == nil {
 		globals.Log.INFO.Printf("No storage provided," +
@@ -81,72 +123,104 @@ func NewClient(s globals.Storage, loc string) (*Client, error) {
 		return nil, err
 	}
 
+	cmixGrp := cyclic.NewGroup(
+		large.NewIntFromString(ndfJSON.CMIX.Prime, 16),
+		large.NewIntFromString(ndfJSON.CMIX.Generator, 16),
+		large.NewIntFromString(ndfJSON.CMIX.SmallPrime, 16))
+
+	user.InitUserRegistry(cmixGrp)
+
 	cl := new(Client)
 	cl.storage = store
 	cl.comm = io.NewMessenger()
-	cl.gwAddresses = make([]io.ConnAddr, 0)
+	cl.ndf = ndfJSON
+
+	//build the topology
+	nodeIDs := make([]*id.Node, len(cl.ndf.Nodes))
+	for i, node := range cl.ndf.Nodes {
+		nodeIDs[i] = id.NewNodeFromBytes(node.ID)
+	}
+
+	cl.topology = circuit.New(nodeIDs)
+
 	return cl, nil
 }
 
 // Connects to gateways and registration server (if needed)
 // using TLS filepaths to create credential information
 // for connection establishment
-func (cl *Client) Connect(gwAddresses []string, gwCertPath,
-	regAddr, regCertPath string) error {
-	if len(gwAddresses) < 1 {
+func (cl *Client) Connect() error {
+	if len(cl.ndf.Gateways) < 1 {
 		globals.Log.ERROR.Printf("Connect: Invalid number of nodes")
 		return errors.New("could not connect due to invalid number of nodes")
 	}
 
-	var gwCreds credentials.TransportCredentials = nil
-	if gwCertPath != "" {
-		gwCreds = connect.NewCredentialsFromFile(gwCertPath, "")
-	}
-
-	for _, gw := range gwAddresses {
-		addr := io.ConnAddr(gw)
-		(cl.comm).(*io.Messaging).Comms.ConnectToGateway(addr, gw, gwCreds)
-		cl.gwAddresses = append(cl.gwAddresses, addr)
+	//connect to all gateways
+	for _, gateway := range cl.ndf.Gateways {
+		var gwCreds credentials.TransportCredentials
+		if gateway.TlsCertificate != "" {
+			gwCreds = connect.NewCredentialsFromPEM(gateway.TlsCertificate, "")
+		}
+		addr := io.ConnAddr(gateway.Address)
+		(cl.comm).(*io.Messaging).Comms.ConnectToGateway(addr, gateway.Address, gwCreds)
 	}
 
-	if regAddr != "" {
-		var regCreds credentials.TransportCredentials = nil
-		if regCertPath != "" {
-			regCreds = connect.NewCredentialsFromFile(regCertPath, "")
+	//connect to the registration server
+	if cl.ndf.Registration.Address != "" {
+		var regCreds credentials.TransportCredentials
+		if cl.ndf.Registration.TlsCertificate != "" {
+			regCreds = connect.NewCredentialsFromPEM(cl.ndf.Registration.TlsCertificate, "")
 		}
-		addr := io.ConnAddr(regAddr)
-		(cl.comm).(*io.Messaging).Comms.ConnectToRegistration(addr, regAddr, regCreds)
-		cl.regAddress = addr
+		addr := io.ConnAddr(cl.ndf.Registration.Address)
+		(cl.comm).(*io.Messaging).Comms.ConnectToRegistration(addr, cl.ndf.Registration.Address, regCreds)
+	} else {
+		globals.Log.WARN.Printf("No registration server found to connect to")
 	}
 	return nil
 }
 
 // Registers user and returns the User ID.
 // Returns an error if registration fails.
-func (cl *Client) Register(preCan bool, registrationCode, nick string,
-	mint bool, grp *cyclic.Group) (*id.User, error) {
+func (cl *Client) Register(preCan bool, registrationCode, nick, email string) (*id.User, error) {
 	var err error
 	var u *user.User
 	var UID *id.User
+
+	cmixGrp := cyclic.NewGroup(
+		large.NewIntFromString(cl.ndf.CMIX.Prime, 16),
+		large.NewIntFromString(cl.ndf.CMIX.Generator, 16),
+		large.NewIntFromString(cl.ndf.CMIX.SmallPrime, 16))
+
+	e2eGrp := cyclic.NewGroup(
+		large.NewIntFromString(cl.ndf.E2E.Prime, 16),
+		large.NewIntFromString(cl.ndf.E2E.Generator, 16),
+		large.NewIntFromString(cl.ndf.E2E.SmallPrime, 16))
+
 	// Make CMIX keys array
-	nk := make([]user.NodeKeys, len(cl.gwAddresses))
+	nk := make(map[id.Node]user.NodeKeys)
 
 	// Generate DSA keypair even for precanned users as it will probably
 	// be needed for the new UDB flow
-	params := signature.GetDefaultDSAParams()
+	params := signature.CustomDSAParams(
+		cmixGrp.GetP(),
+		cmixGrp.GetQ(),
+		cmixGrp.GetG())
 	privateKey := params.PrivateKeyGen(rand.Reader)
 	publicKey := privateKey.PublicKeyGen()
 
+	fmt.Println("gened private keys")
+
 	// Handle precanned registration
 	if preCan {
 		var successLook bool
 		globals.Log.DEBUG.Printf("Registering precanned user")
 		UID, successLook = user.Users.LookupUser(registrationCode)
 
+		fmt.Println("UID:", UID, "success:", successLook)
+
 		if !successLook {
 			globals.Log.ERROR.Printf("Register: HUID does not match")
-			err = errors.New("could not register due to invalid HUID")
-			return id.ZeroID, err
+			return id.ZeroID, errors.New("could not register due to invalid HUID")
 		}
 
 		var successGet bool
@@ -162,6 +236,10 @@ func (cl *Client) Register(preCan bool, registrationCode, nick string,
 			u.Nick = nick
 		}
 
+		if email != "" {
+			u.Email = email
+		}
+
 		nodekeys, successKeys := user.Users.LookupKeys(u.User)
 
 		if !successKeys {
@@ -170,8 +248,8 @@ func (cl *Client) Register(preCan bool, registrationCode, nick string,
 			return id.ZeroID, err
 		}
 
-		for i := 0; i < len(cl.gwAddresses); i++ {
-			nk[i] = *nodekeys
+		for i := 0; i < len(cl.ndf.Gateways); i++ {
+			nk[*cl.topology.GetNodeAtIndex(i)] = *nodekeys
 		}
 	} else {
 		// Generate salt for UserID
@@ -191,10 +269,10 @@ func (cl *Client) Register(preCan bool, registrationCode, nick string,
 
 		// If Registration Server is specified, contact it
 		// Only if registrationCode is set
-		if cl.regAddress != "" && registrationCode != "" {
+		if cl.ndf.Registration.Address != "" && registrationCode != "" {
 			// Send registration code and public key to RegistrationServer
 			response, err := (cl.comm).(*io.Messaging).Comms.
-				SendRegistrationMessage(cl.regAddress,
+				SendRegistrationMessage(io.ConnAddr(cl.ndf.Registration.Address),
 					&pb.UserRegistration{
 						RegistrationCode: registrationCode,
 						Client: &pb.DSAPublicKey{
@@ -217,12 +295,13 @@ func (cl *Client) Register(preCan bool, registrationCode, nick string,
 			regR = response.ClientSignedByServer.R
 			regS = response.ClientSignedByServer.S
 			// Disconnect from regServer here since it will not be needed
-			(cl.comm).(*io.Messaging).Comms.Disconnect(cl.regAddress.String())
-			cl.regAddress = ""
+			(cl.comm).(*io.Messaging).Comms.Disconnect(cl.ndf.Registration.Address)
 		}
-
+		fmt.Println("passed reg")
 		// Loop over all Servers
-		for _, gwAddr := range cl.gwAddresses {
+		for _, gateway := range cl.ndf.Gateways {
+
+			gwAddr := io.ConnAddr(gateway.Address)
 
 			// Send signed public key and salt for UserID to Server
 			nonceResponse, err := (cl.comm).(*io.Messaging).Comms.
@@ -300,18 +379,17 @@ func (cl *Client) Register(preCan bool, registrationCode, nick string,
 		// Loop through all the server public keys
 		for itr, publicKey := range serverPublicKeys {
 
-			// Generate the base keys
-			nk[itr].TransmissionKey = registration.GenerateBaseKey(
-				grp, publicKey, privateKey, transmissionHash,
-			)
-
-			transmissionHash.Reset()
+			nodeID := *cl.topology.GetNodeAtIndex(itr)
 
-			nk[itr].ReceptionKey = registration.GenerateBaseKey(
-				grp, publicKey, privateKey, receptionHash,
-			)
+			nk[nodeID] = user.NodeKeys{
+				TransmissionKey: registration.GenerateBaseKey(cmixGrp,
+					publicKey, privateKey, transmissionHash),
+				ReceptionKey: registration.GenerateBaseKey(cmixGrp, publicKey,
+					privateKey, receptionHash),
+			}
 
 			receptionHash.Reset()
+			transmissionHash.Reset()
 		}
 
 		var actualNick string
@@ -325,7 +403,7 @@ func (cl *Client) Register(preCan bool, registrationCode, nick string,
 	}
 
 	// Create the user session
-	nus := user.NewSession(cl.storage, u, nk, publicKey, privateKey, grp)
+	nus := user.NewSession(cl.storage, u, nk, publicKey, privateKey, cmixGrp, e2eGrp)
 
 	// Store the user session
 	errStore := nus.StoreSession()
@@ -340,7 +418,10 @@ func (cl *Client) Register(preCan bool, registrationCode, nick string,
 		return id.ZeroID, err
 	}
 
-	nus.Immolate()
+	err = nus.Immolate()
+	if err != nil {
+		globals.Log.ERROR.Printf("Error on immolate: %+v", err)
+	}
 	nus = nil
 
 	return UID, nil
@@ -348,16 +429,16 @@ func (cl *Client) Register(preCan bool, registrationCode, nick string,
 
 // Logs in user and sets session on client object
 // returns the nickname or error if login fails
-func (cl *Client) Login(UID *id.User, email string) (string, error) {
+func (cl *Client) Login(UID *id.User) (string, error) {
 	session, err := user.LoadSession(cl.storage, UID)
 
 	if session == nil {
 		return "", errors.New("Unable to load session: " + err.Error())
 	}
 
-	(cl.comm).(*io.Messaging).SendAddress = cl.gwAddresses[0]
+	(cl.comm).(*io.Messaging).SendAddress = io.ConnAddr(cl.ndf.Gateways[0].Address)
 	(cl.comm).(*io.Messaging).ReceiveAddress =
-		cl.gwAddresses[len(cl.gwAddresses)-1]
+		io.ConnAddr(cl.ndf.Gateways[len(cl.ndf.Gateways)-1].Address)
 
 	if err != nil {
 		err = errors.New(fmt.Sprintf("Login: Could not login: %s",
@@ -366,7 +447,7 @@ func (cl *Client) Login(UID *id.User, email string) (string, error) {
 		return "", err
 	}
 
-	cl.sess = session
+	cl.session = session
 
 	pollWaitTimeMillis := 1000 * time.Millisecond
 	// TODO Don't start the message receiver if it's already started.
@@ -374,9 +455,11 @@ func (cl *Client) Login(UID *id.User, email string) (string, error) {
 	go cl.comm.MessageReceiver(session, pollWaitTimeMillis)
 
 	// Initialize UDB and nickname "bot" stuff here
-	bots.InitBots(cl.sess, cl.comm)
+	bots.InitBots(cl.session, cl.comm, cl.topology)
 	// Initialize Rekey listeners
-	rekey.InitRekey(cl.sess, cl.comm)
+	rekey.InitRekey(cl.session, cl.comm, cl.topology)
+
+	email := session.GetCurrentUser().Email
 
 	if email != "" {
 		err = cl.registerForUserDiscovery(email)
@@ -396,7 +479,7 @@ func (cl *Client) Send(message parse.MessageInterface) error {
 	// FIXME: There should (at least) be a version of this that takes a byte array
 	recipientID := message.GetRecipient()
 	cryptoType := message.GetCryptoType()
-	return cl.comm.SendMessage(cl.sess, recipientID, cryptoType, message.Pack())
+	return cl.comm.SendMessage(cl.session, cl.topology, recipientID, cryptoType, message.Pack())
 }
 
 // DisableBlockingTransmission turns off blocking transmission, for
@@ -411,50 +494,49 @@ func (cl *Client) SetRateLimiting(limit uint32) {
 	(cl.comm).(*io.Messaging).TransmitDelay = time.Duration(limit) * time.Millisecond
 }
 
-func (cl *Client) Listen(user *id.User, outerType format.CryptoType,
-	messageType int32, newListener switchboard.Listener) string {
-	listenerId := cl.sess.GetSwitchboard().
-		Register(user, outerType, messageType, newListener)
+func (cl *Client) Listen(user *id.User, messageType int32, newListener switchboard.Listener) string {
+	listenerId := cl.session.GetSwitchboard().
+		Register(user, messageType, newListener)
 	globals.Log.INFO.Printf("Listening now: user %v, message type %v, id %v",
 		user, messageType, listenerId)
 	return listenerId
 }
 
 func (cl *Client) StopListening(listenerHandle string) {
-	cl.sess.GetSwitchboard().Unregister(listenerHandle)
+	cl.session.GetSwitchboard().Unregister(listenerHandle)
 }
 
 func (cl *Client) GetSwitchboard() *switchboard.Switchboard {
-	return cl.sess.GetSwitchboard()
+	return cl.session.GetSwitchboard()
 }
 
 func (cl *Client) GetCurrentUser() *id.User {
-	return cl.sess.GetCurrentUser().User
+	return cl.session.GetCurrentUser().User
 }
 
 func (cl *Client) GetKeyParams() *keyStore.KeyParams {
-	return cl.sess.GetKeyStore().GetKeyParams()
+	return cl.session.GetKeyStore().GetKeyParams()
 }
 
 // Logout closes the connection to the server at this time and does
 // nothing with the user id. In the future this will release resources
 // and safely release any sensitive memory.
 func (cl *Client) Logout() error {
-	if cl.sess == nil {
+	if cl.session == nil {
 		err := errors.New("Logout: Cannot Logout when you are not logged in")
 		globals.Log.ERROR.Printf(err.Error())
 		return err
 	}
 
 	// Stop reception runner goroutine
-	cl.sess.GetQuitChan() <- true
+	cl.session.GetQuitChan() <- true
 
 	// Disconnect from the gateways
-	for _, gw := range cl.gwAddresses {
-		(cl.comm).(*io.Messaging).Comms.Disconnect(gw.String())
+	for _, gateway := range cl.ndf.Gateways {
+		(cl.comm).(*io.Messaging).Comms.Disconnect(gateway.Address)
 	}
 
-	errStore := cl.sess.StoreSession()
+	errStore := cl.session.StoreSession()
 
 	if errStore != nil {
 		err := errors.New(fmt.Sprintf("Logout: Store Failed: %s" +
@@ -463,8 +545,8 @@ func (cl *Client) Logout() error {
 		return err
 	}
 
-	errImmolate := cl.sess.Immolate()
-	cl.sess = nil
+	errImmolate := cl.session.Immolate()
+	cl.session = nil
 
 	if errImmolate != nil {
 		err := errors.New(fmt.Sprintf("Logout: Immolation Failed: %s" +
@@ -488,7 +570,7 @@ func (cl *Client) registerForUserDiscovery(emailAddress string) error {
 		return err
 	}
 
-	publicKey := cl.sess.GetPublicKey()
+	publicKey := cl.session.GetPublicKey()
 	publicKeyBytes := publicKey.GetKey().LeftpadBytes(256)
 	return bots.Register(valueType, emailAddress, publicKeyBytes)
 }
@@ -531,12 +613,12 @@ func (cl *Client) LookupNick(user *id.User,
 func (cl *Client) registerUserE2E(partnerID *id.User,
 	partnerPubKey []byte) {
 	// Get needed variables from session
-	grp := cl.sess.GetGroup()
-	userID := cl.sess.GetCurrentUser().User
+	grp := cl.session.GetCmixGroup()
+	userID := cl.session.GetCurrentUser().User
 
 	// Create user private key and partner public key
 	// in the group
-	privKey := cl.sess.GetPrivateKey()
+	privKey := cl.session.GetPrivateKey()
 	privKeyCyclic := grp.NewIntFromLargeInt(privKey.GetKey())
 	partnerPubKeyCyclic := grp.NewIntFromBytes(partnerPubKey)
 
@@ -547,7 +629,7 @@ func (cl *Client) registerUserE2E(partnerID *id.User,
 		grp)
 
 	// Generate key TTL and number of keys
-	params := cl.sess.GetKeyStore().GetKeyParams()
+	params := cl.session.GetKeyStore().GetKeyParams()
 	keysTTL, numKeys := e2e.GenerateKeyTTL(baseKey.GetLargeInt(),
 		params.MinKeys, params.MaxKeys, params.TTLParams)
 
@@ -557,7 +639,7 @@ func (cl *Client) registerUserE2E(partnerID *id.User,
 		numKeys, keysTTL, params.NumRekeys)
 
 	// Generate Send Keys
-	km.GenerateKeys(grp, userID, cl.sess.GetKeyStore())
+	km.GenerateKeys(grp, userID, cl.session.GetKeyStore())
 
 	// Create Receive KeyManager
 	km = keyStore.NewManager(baseKey, privKeyCyclic,
@@ -565,10 +647,10 @@ func (cl *Client) registerUserE2E(partnerID *id.User,
 		numKeys, keysTTL, params.NumRekeys)
 
 	// Generate Receive Keys
-	km.GenerateKeys(grp, userID, cl.sess.GetKeyStore())
+	km.GenerateKeys(grp, userID, cl.session.GetKeyStore())
 
 	// Create RekeyKeys and add to RekeyManager
-	rkm := cl.sess.GetRekeyManager()
+	rkm := cl.session.GetRekeyManager()
 
 	keys := &keyStore.RekeyKeys{
 		CurrPrivKey: privKeyCyclic,
@@ -618,7 +700,7 @@ func ParseMessage(message []byte) (ParsedMessage, error) {
 }
 
 func (cl *Client) GetSessionData() ([]byte, error) {
-	return cl.sess.GetSessionData()
+	return cl.session.GetSessionData()
 }
 
 // Set the output of the
diff --git a/api/client_test.go b/api/client_test.go
index 0a3531d3d9efd0e604ff9e4bd2511460de6c4465..508da9cf8e98a9a18b00d10f4810c594a0d63546 100644
--- a/api/client_test.go
+++ b/api/client_test.go
@@ -10,6 +10,7 @@ import (
 	"bytes"
 	"crypto/sha256"
 	"encoding/gob"
+	"fmt"
 	"github.com/golang/protobuf/proto"
 	"gitlab.com/elixxir/client/cmixproto"
 	"gitlab.com/elixxir/client/globals"
@@ -17,10 +18,13 @@ import (
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
 	"gitlab.com/elixxir/crypto/csprng"
+	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/diffieHellman"
 	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/crypto/hash"
+	"gitlab.com/elixxir/crypto/large"
 	"gitlab.com/elixxir/crypto/signature"
+	"gitlab.com/elixxir/primitives/circuit"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
 	"reflect"
@@ -28,21 +32,20 @@ import (
 	"time"
 )
 
-var testClient *Client
-
 func TestRegistrationGob(t *testing.T) {
 	// Get a Client
-	var err error
-	testClient, err = NewClient(&globals.RamStorage{}, "")
+	testClient, err := NewClient(&globals.RamStorage{}, "", def)
 	if err != nil {
 		t.Error(err)
 	}
 
-	testClient.Connect(RegGWAddresses[:], "", "", "")
+	err = testClient.Connect()
+	if err != nil {
+		t.Error(err)
+	}
 
 	// populate a gob in the store
-	grp := getGroup()
-	_, err = testClient.Register(true, "UAV6IWD6", "", false, grp)
+	_, err = testClient.Register(true, "UAV6IWD6", "", "")
 	if err != nil {
 		t.Error(err)
 	}
@@ -52,41 +55,45 @@ func TestRegistrationGob(t *testing.T) {
 	var sessionBytes bytes.Buffer
 	sessionBytes.Write(sessionGob)
 	dec := gob.NewDecoder(&sessionBytes)
-	Session = user.SessionObj{}
+	Session := user.SessionObj{}
 	err = dec.Decode(&Session)
 	if err != nil {
 		t.Error(err)
 	}
 
-	VerifyRegisterGobKeys(t)
-	VerifyRegisterGobUser(t)
+	VerifyRegisterGobUser(&Session, t)
+	VerifyRegisterGobKeys(&Session, testClient.topology, t)
 }
 
-func VerifyRegisterGobUser(t *testing.T) {
-	if *Session.GetCurrentUser().User != *id.NewUserFromUint(5, t) {
-		t.Errorf("User's ID was %q, expected %v",
-			Session.GetCurrentUser().User, 5)
+func VerifyRegisterGobUser(session user.Session, t *testing.T) {
+
+	expectedUser := id.NewUserFromUint(5, t)
+
+	if reflect.DeepEqual(session.GetCurrentUser().User, &expectedUser) {
+		t.Errorf("Incorrect User ID; \n   expected %q \n   recieved: %q",
+			expectedUser, session.GetCurrentUser().User)
 	}
 }
 
-func VerifyRegisterGobKeys(t *testing.T) {
-	grp := getGroup()
+func VerifyRegisterGobKeys(session user.Session, topology *circuit.Circuit, t *testing.T) {
+	cmixGrp, _ := getGroups()
 	h := sha256.New()
 	h.Write([]byte(string(20005)))
-	expectedTransmissionBaseKey := grp.NewIntFromBytes(h.Sum(nil))
-	if Session.GetKeys()[0].TransmissionKey.Cmp(
+	expectedTransmissionBaseKey := cmixGrp.NewIntFromBytes(h.Sum(nil))
+
+	if session.GetKeys(topology)[0].TransmissionKey.Cmp(
 		expectedTransmissionBaseKey) != 0 {
 		t.Errorf("Transmission base key was %v, expected %v",
-			Session.GetKeys()[0].TransmissionKey.Text(16),
+			session.GetKeys(topology)[0].TransmissionKey.Text(16),
 			expectedTransmissionBaseKey.Text(16))
 	}
 	h = sha256.New()
 	h.Write([]byte(string(40005)))
-	expectedReceptionBaseKey := grp.NewIntFromBytes(h.Sum(nil))
-	if Session.GetKeys()[0].ReceptionKey.Cmp(
+	expectedReceptionBaseKey := cmixGrp.NewIntFromBytes(h.Sum(nil))
+	if session.GetKeys(topology)[0].ReceptionKey.Cmp(
 		expectedReceptionBaseKey) != 0 {
 		t.Errorf("Reception base key was %v, expected %v",
-			Session.GetKeys()[0].ReceptionKey.Text(16),
+			session.GetKeys(topology)[0].ReceptionKey.Text(16),
 			expectedReceptionBaseKey.Text(16))
 	}
 }
@@ -184,29 +191,34 @@ func TestParse(t *testing.T) {
 
 // Test that registerUserE2E correctly creates keys and adds them to maps
 func TestRegisterUserE2E(t *testing.T) {
-	grp := getGroup()
+	testClient, err := NewClient(&globals.RamStorage{}, "", def)
+	if err != nil {
+		t.Error(err)
+	}
+
+	cmixGrp, e2eGrp := getGroups()
 	userID := id.NewUserFromUint(18, t)
 	partner := id.NewUserFromUint(14, t)
 	params := signature.CustomDSAParams(
-		grp.GetP(),
-		grp.GetG(),
-		grp.GetQ())
+		cmixGrp.GetP(),
+		cmixGrp.GetQ(),
+		cmixGrp.GetG())
 	rng := csprng.NewSystemRNG()
 	myPrivKey := params.PrivateKeyGen(rng)
-	myPrivKeyCyclic := grp.NewIntFromLargeInt(myPrivKey.GetKey())
+	myPrivKeyCyclic := cmixGrp.NewIntFromLargeInt(myPrivKey.GetKey())
 	myPubKey := myPrivKey.PublicKeyGen()
 	partnerPrivKey := params.PrivateKeyGen(rng)
 	partnerPubKey := partnerPrivKey.PublicKeyGen()
-	partnerPubKeyCyclic := grp.NewIntFromLargeInt(partnerPubKey.GetKey())
+	partnerPubKeyCyclic := cmixGrp.NewIntFromLargeInt(partnerPubKey.GetKey())
 
 	myUser := &user.User{User: userID, Nick: "test"}
 	session := user.NewSession(testClient.storage,
-		myUser, []user.NodeKeys{}, myPubKey, myPrivKey, grp)
-
-	testClient.sess = session
+		myUser, make(map[id.Node]user.NodeKeys), myPubKey, myPrivKey, cmixGrp, e2eGrp)
 
+	testClient.session = session
+	fmt.Println("runn")
 	testClient.registerUserE2E(partner, partnerPubKeyCyclic.Bytes())
-
+	fmt.Println("dunn")
 	// Confirm we can get all types of keys
 	km := session.GetKeyStore().GetSendManager(partner)
 	if km == nil {
@@ -215,7 +227,7 @@ func TestRegisterUserE2E(t *testing.T) {
 	key, action := km.PopKey()
 	if key == nil {
 		t.Errorf("TransmissionKeys map returned nil")
-	} else if key.GetOuterType() != format.E2E {
+	} else if key.GetOuterType() != parse.E2E {
 		t.Errorf("Key type expected 'E2E', got %s",
 			key.GetOuterType())
 	} else if action != keyStore.None {
@@ -226,7 +238,7 @@ func TestRegisterUserE2E(t *testing.T) {
 	key, action = km.PopRekey()
 	if key == nil {
 		t.Errorf("TransmissionReKeys map returned nil")
-	} else if key.GetOuterType() != format.Rekey {
+	} else if key.GetOuterType() != parse.Rekey {
 		t.Errorf("Key type expected 'Rekey', got %s",
 			key.GetOuterType())
 	} else if action != keyStore.None {
@@ -236,9 +248,9 @@ func TestRegisterUserE2E(t *testing.T) {
 
 	// Generate one reception key of each type to test
 	// fingerprint map
-	baseKey, _ := diffieHellman.CreateDHSessionKey(partnerPubKeyCyclic, myPrivKeyCyclic, grp)
-	recvKeys := e2e.DeriveKeys(grp, baseKey, partner, uint(1))
-	recvReKeys := e2e.DeriveEmergencyKeys(grp, baseKey, partner, uint(1))
+	baseKey, _ := diffieHellman.CreateDHSessionKey(partnerPubKeyCyclic, myPrivKeyCyclic, cmixGrp)
+	recvKeys := e2e.DeriveKeys(cmixGrp, baseKey, partner, uint(1))
+	recvReKeys := e2e.DeriveEmergencyKeys(cmixGrp, baseKey, partner, uint(1))
 
 	h, _ := hash.NewCMixHash()
 	h.Write(recvKeys[0].Bytes())
@@ -248,7 +260,7 @@ func TestRegisterUserE2E(t *testing.T) {
 	key = session.GetKeyStore().GetRecvKey(fp)
 	if key == nil {
 		t.Errorf("ReceptionKeys map returned nil for Key")
-	} else if key.GetOuterType() != format.E2E {
+	} else if key.GetOuterType() != parse.E2E {
 		t.Errorf("Key type expected 'E2E', got %s",
 			key.GetOuterType())
 	}
@@ -260,7 +272,7 @@ func TestRegisterUserE2E(t *testing.T) {
 	key = session.GetKeyStore().GetRecvKey(fp)
 	if key == nil {
 		t.Errorf("ReceptionKeys map returned nil for ReKey")
-	} else if key.GetOuterType() != format.Rekey {
+	} else if key.GetOuterType() != parse.Rekey {
 		t.Errorf("Key type expected 'Rekey', got %s",
 			key.GetOuterType())
 	}
@@ -268,41 +280,46 @@ func TestRegisterUserE2E(t *testing.T) {
 
 // Test all keys created with registerUserE2E match what is expected
 func TestRegisterUserE2E_CheckAllKeys(t *testing.T) {
-	grp := getGroup()
+	testClient, err := NewClient(&globals.RamStorage{}, "", def)
+	if err != nil {
+		t.Error(err)
+	}
+
+	cmixGrp, e2eGrp := getGroups()
 	userID := id.NewUserFromUint(18, t)
 	partner := id.NewUserFromUint(14, t)
 	params := signature.CustomDSAParams(
-		grp.GetP(),
-		grp.GetG(),
-		grp.GetQ())
+		cmixGrp.GetP(),
+		cmixGrp.GetQ(),
+		cmixGrp.GetG())
 	rng := csprng.NewSystemRNG()
 	myPrivKey := params.PrivateKeyGen(rng)
-	myPrivKeyCyclic := grp.NewIntFromLargeInt(myPrivKey.GetKey())
+	myPrivKeyCyclic := cmixGrp.NewIntFromLargeInt(myPrivKey.GetKey())
 	myPubKey := myPrivKey.PublicKeyGen()
 	partnerPrivKey := params.PrivateKeyGen(rng)
 	partnerPubKey := partnerPrivKey.PublicKeyGen()
-	partnerPubKeyCyclic := grp.NewIntFromLargeInt(partnerPubKey.GetKey())
+	partnerPubKeyCyclic := cmixGrp.NewIntFromLargeInt(partnerPubKey.GetKey())
 
 	myUser := &user.User{User: userID, Nick: "test"}
 	session := user.NewSession(testClient.storage,
-		myUser, []user.NodeKeys{}, myPubKey,
-		myPrivKey, grp)
+		myUser, make(map[id.Node]user.NodeKeys), myPubKey,
+		myPrivKey, cmixGrp, e2eGrp)
 
-	testClient.sess = session
+	testClient.session = session
 
 	testClient.registerUserE2E(partner, partnerPubKeyCyclic.Bytes())
 
 	// Generate all keys and confirm they all match
 	keyParams := testClient.GetKeyParams()
-	baseKey, _ := diffieHellman.CreateDHSessionKey(partnerPubKeyCyclic, myPrivKeyCyclic, grp)
+	baseKey, _ := diffieHellman.CreateDHSessionKey(partnerPubKeyCyclic, myPrivKeyCyclic, cmixGrp)
 	keyTTL, numKeys := e2e.GenerateKeyTTL(baseKey.GetLargeInt(),
 		keyParams.MinKeys, keyParams.MaxKeys, keyParams.TTLParams)
 
-	sendKeys := e2e.DeriveKeys(grp, baseKey, userID, uint(numKeys))
-	sendReKeys := e2e.DeriveEmergencyKeys(grp, baseKey,
+	sendKeys := e2e.DeriveKeys(cmixGrp, baseKey, userID, uint(numKeys))
+	sendReKeys := e2e.DeriveEmergencyKeys(cmixGrp, baseKey,
 		userID, uint(keyParams.NumRekeys))
-	recvKeys := e2e.DeriveKeys(grp, baseKey, partner, uint(numKeys))
-	recvReKeys := e2e.DeriveEmergencyKeys(grp, baseKey,
+	recvKeys := e2e.DeriveKeys(cmixGrp, baseKey, partner, uint(numKeys))
+	recvReKeys := e2e.DeriveEmergencyKeys(cmixGrp, baseKey,
 		partner, uint(keyParams.NumRekeys))
 
 	// Confirm all keys
@@ -314,7 +331,7 @@ func TestRegisterUserE2E_CheckAllKeys(t *testing.T) {
 		key, action := km.PopKey()
 		if key == nil {
 			t.Errorf("TransmissionKeys map returned nil")
-		} else if key.GetOuterType() != format.E2E {
+		} else if key.GetOuterType() != parse.E2E {
 			t.Errorf("Key type expected 'E2E', got %s",
 				key.GetOuterType())
 		}
@@ -342,7 +359,7 @@ func TestRegisterUserE2E_CheckAllKeys(t *testing.T) {
 		key, action := km.PopRekey()
 		if key == nil {
 			t.Errorf("TransmissionReKeys map returned nil")
-		} else if key.GetOuterType() != format.Rekey {
+		} else if key.GetOuterType() != parse.Rekey {
 			t.Errorf("Key type expected 'Rekey', got %s",
 				key.GetOuterType())
 		}
@@ -376,7 +393,7 @@ func TestRegisterUserE2E_CheckAllKeys(t *testing.T) {
 		key := session.GetKeyStore().GetRecvKey(fp)
 		if key == nil {
 			t.Errorf("ReceptionKeys map returned nil for Key")
-		} else if key.GetOuterType() != format.E2E {
+		} else if key.GetOuterType() != parse.E2E {
 			t.Errorf("Key type expected 'E2E', got %s",
 				key.GetOuterType())
 		}
@@ -395,7 +412,7 @@ func TestRegisterUserE2E_CheckAllKeys(t *testing.T) {
 		key := session.GetKeyStore().GetRecvKey(fp)
 		if key == nil {
 			t.Errorf("ReceptionKeys map returned nil for Rekey")
-		} else if key.GetOuterType() != format.Rekey {
+		} else if key.GetOuterType() != parse.Rekey {
 			t.Errorf("Key type expected 'Rekey', got %s",
 				key.GetOuterType())
 		}
@@ -482,3 +499,45 @@ func TestRegisterUserE2E_CheckAllKeys(t *testing.T) {
 //		t.Errorf("Message wrong length: %d v. expected 81", len(lastmsg))
 //	}
 //}
+
+func getGroups() (*cyclic.Group, *cyclic.Group) {
+
+	cmixGrp := cyclic.NewGroup(
+		large.NewIntFromString("9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48"+
+			"C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F"+
+			"FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5"+
+			"B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2"+
+			"35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41"+
+			"F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE"+
+			"92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15"+
+			"3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B", 16),
+		large.NewIntFromString("5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613"+
+			"D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4"+
+			"6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472"+
+			"085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5"+
+			"AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA"+
+			"3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71"+
+			"BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0"+
+			"DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7", 16),
+		large.NewIntFromString("F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F", 16))
+
+	e2eGrp := cyclic.NewGroup(
+		large.NewIntFromString("E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B"+
+			"7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AE"+
+			"DF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F"+
+			"8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041"+
+			"023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF45"+
+			"3B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE9209"+
+			"6EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29"+
+			"A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E"+
+			"37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F2"+
+			"78DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696"+
+			"015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E"+
+			"6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873"+
+			"847AEF49F66E43873", 16),
+		large.NewIntFromString("2", 16),
+		large.NewIntFromString("2", 16))
+
+	return cmixGrp, e2eGrp
+
+}
diff --git a/api/mockserver.go b/api/mockserver.go
index 9ff19efa84bb6cc5cbe1c420485484c6e657ffb3..abeb89278e4ee7f74f9bac3dad43b70efd3c19a6 100644
--- a/api/mockserver.go
+++ b/api/mockserver.go
@@ -9,9 +9,9 @@ package api
 
 import (
 	"gitlab.com/elixxir/client/cmixproto"
+	"gitlab.com/elixxir/client/parse"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/crypto/large"
-	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
 	"sync"
 )
@@ -40,8 +40,8 @@ func (m APIMessage) GetMessageType() int32 {
 	return int32(cmixproto.Type_NO_TYPE)
 }
 
-func (m APIMessage) GetCryptoType() format.CryptoType {
-	return format.None
+func (m APIMessage) GetCryptoType() parse.CryptoType {
+	return parse.None
 }
 
 func (m APIMessage) Pack() []byte {
@@ -96,7 +96,7 @@ type MockRegistration struct {
 }
 
 func (s *MockRegistration) RegisterNode(ID []byte,
-	NodeTLSCert, GatewayTLSCert, RegistrationCode string) error {
+	NodeTLSCert, GatewayTLSCert, RegistrationCode, Addr string) error {
 	return nil
 }
 
diff --git a/api/mockserver_test.go b/api/mockserver_test.go
index f99d87d3bd593658d64095b20b8469ef3b60c02e..5d91c396368a321cfc87a5bb138f3999d672c32e 100644
--- a/api/mockserver_test.go
+++ b/api/mockserver_test.go
@@ -10,42 +10,37 @@ package api
 import (
 	"fmt"
 	jww "github.com/spf13/jwalterweatherman"
-	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/user"
 	"gitlab.com/elixxir/comms/gateway"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/comms/registration"
-	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/primitives/id"
+	"gitlab.com/elixxir/primitives/ndf"
+	"math/rand"
 	"os"
 	"testing"
+	"time"
 )
 
-const NumGWs = 3
+const NumNodes = 3
+const NumGWs = NumNodes
 const RegPort = 5000
-const RegGWsStartPort = 10000
-const SessionGWPort = 15000
+const GWsStartPort = 10000
 
-var RegAddress = fmtAddress(RegPort)
-var RegGWAddresses [NumGWs]string
-var SessionGWAddress = fmtAddress(SessionGWPort)
-var RegGWComms [NumGWs]*gateway.GatewayComms
+var RegHandler = MockRegistration{}
 var RegComms *registration.RegistrationComms
-var SessionGWComms *gateway.GatewayComms
 
 const ValidRegCode = "UAV6IWD6"
-const InvalidRegCode = "INVALID_REG_CODE"
+const InvalidRegCode = "INVALID_REG_CODE_"
 
-var RegGWHandlers = [NumGWs]*TestInterface{
+var RegGWHandlers [3]*TestInterface = [NumGWs]*TestInterface{
 	{LastReceivedMessage: pb.Slot{}},
 	{LastReceivedMessage: pb.Slot{}},
 	{LastReceivedMessage: pb.Slot{}},
 }
+var GWComms [NumGWs]*gateway.GatewayComms
 
-var RegHandler = MockRegistration{}
-
-var SessionGWHandler = TestInterface{LastReceivedMessage: pb.Slot{}}
-var Session user.SessionObj
+var def *ndf.NetworkDefinition
 
 // Setups general testing params and calls test wrapper
 func TestMain(m *testing.M) {
@@ -62,17 +57,21 @@ func TestRegister_ValidPrecannedRegCodeReturnsZeroID(t *testing.T) {
 
 	// Initialize client with dummy storage
 	storage := DummyStorage{Location: "Blah", LastSave: []byte{'a', 'b', 'c'}}
-	client, err := NewClient(&storage, "hello")
+	client, err := NewClient(&storage, "hello", def)
 	if err != nil {
 		t.Errorf("Failed to initialize dummy client: %s", err.Error())
 	}
 
 	// Connect to gateways and reg server
-	client.Connect(RegGWAddresses[:], "", "", "")
+	err = client.Connect()
+
+	if err != nil {
+		t.Errorf("Client failed of connect: %+v", err)
+	}
 
 	// Register precanned user with all gateways
 	regRes, err := client.Register(true, ValidRegCode,
-		"", false, getGroup())
+		"", "")
 
 	// Verify registration succeeds with valid precanned registration code
 	if err != nil {
@@ -89,23 +88,26 @@ func TestRegister_ValidRegParams___(t *testing.T) {
 
 	// Initialize client with dummy storage
 	storage := DummyStorage{Location: "Blah", LastSave: []byte{'a', 'b', 'c'}}
-	client, err := NewClient(&storage, "hello")
+	client, err := NewClient(&storage, "hello", def)
 	if err != nil {
 		t.Errorf("Failed to initialize dummy client: %s", err.Error())
 	}
 
 	// Connect to gateways and reg server
-	client.Connect(RegGWAddresses[:], "", RegAddress, "")
+	err = client.Connect()
+
+	if err != nil {
+		t.Errorf("Client failed of connect: %+v", err)
+	}
 
 	// Register precanned user with all gateways
-	regRes, err := client.Register(false, ValidRegCode,
-		"", false, getGroup())
+	regRes, err := client.Register(false, ValidRegCode, "", "")
 	if err != nil {
 		t.Errorf("Registration failed: %s", err.Error())
 	}
 
 	if *regRes == *id.ZeroID {
-		t.Errorf("Invalid registration number received: %v", *regRes)
+		t.Errorf("Invalid registration number received: %+v", *regRes)
 	}
 }
 
@@ -114,19 +116,22 @@ func TestRegister_InvalidPrecannedRegCodeReturnsError(t *testing.T) {
 
 	// Initialize client with dummy storage
 	storage := DummyStorage{Location: "Blah", LastSave: []byte{'a', 'b', 'c'}}
-	client, err := NewClient(&storage, "hello")
+	client, err := NewClient(&storage, "hello", def)
 	if err != nil {
 		t.Errorf("Failed to initialize dummy client: %s", err.Error())
 	}
 
 	// Connect to gateways and reg server
-	client.Connect(RegGWAddresses[:], "", "", "")
+	err = client.Connect()
+
+	if err != nil {
+		t.Errorf("Client failed of connect: %+v", err)
+	}
 
 	// Register with invalid reg code
-	_, err = client.Register(true, InvalidRegCode,
-		"", false, getGroup())
+	uid, err := client.Register(true, InvalidRegCode, "", "")
 	if err == nil {
-		t.Error("Registration worked with invalid registration code!")
+		t.Errorf("Registration worked with invalid registration code! UID: %v", uid)
 	}
 }
 
@@ -134,21 +139,24 @@ func TestRegister_DeletedUserReturnsErr(t *testing.T) {
 
 	// Initialize client with dummy storage
 	storage := DummyStorage{Location: "Blah", LastSave: []byte{'a', 'b', 'c'}}
-	client, err := NewClient(&storage, "hello")
+	client, err := NewClient(&storage, "hello", def)
 	if err != nil {
 		t.Errorf("Failed to initialize dummy client: %s", err.Error())
 	}
 
 	// Connect to gateways and reg server
-	client.Connect(RegGWAddresses[:], "", "", "")
+	err = client.Connect()
+
+	if err != nil {
+		t.Errorf("Client failed of connect: %+v", err)
+	}
 
 	// ...
 	tempUser, _ := user.Users.GetUser(id.NewUserFromUint(5, t))
 	user.Users.DeleteUser(id.NewUserFromUint(5, t))
 
 	// Register
-	_, err = client.Register(true, ValidRegCode,
-		"", false, getGroup())
+	_, err = client.Register(true, ValidRegCode, "", "")
 	if err == nil {
 		t.Errorf("Registration worked with a deleted user: %s", err.Error())
 	}
@@ -160,24 +168,27 @@ func TestRegister_DeletedUserReturnsErr(t *testing.T) {
 func TestSend(t *testing.T) {
 	// Initialize client with dummy storage
 	storage := DummyStorage{Location: "Blah", LastSave: []byte{'a', 'b', 'c'}}
-	client, err := NewClient(&storage, "hello")
+	client, err := NewClient(&storage, "hello", def)
 	if err != nil {
 		t.Errorf("Failed to initialize dummy client: %s", err.Error())
 	}
 
 	// Connect to gateways and reg server
-	client.Connect(RegGWAddresses[:], "", "", "")
+	err = client.Connect()
+
+	if err != nil {
+		t.Errorf("Client failed of connect: %+v", err)
+	}
 
 	// Register with a valid registration code
-	userID, err := client.Register(true, ValidRegCode,
-		"", false, getGroup())
+	userID, err := client.Register(true, ValidRegCode, "", "")
 
 	if err != nil {
 		t.Errorf("Register failed: %s", err.Error())
 	}
 
 	// Login to gateway
-	_, err = client.Login(userID, "")
+	_, err = client.Login(userID)
 
 	if err != nil {
 		t.Errorf("Login failed: %s", err.Error())
@@ -195,7 +206,7 @@ func TestSend(t *testing.T) {
 	if err != nil {
 		// TODO: would be nice to catch the sender but we
 		// don't have the interface/mocking for that.
-		t.Errorf("error on first message send: %v", err)
+		t.Errorf("error on first message send: %+v", err)
 	}
 
 	// Test send with valid inputs
@@ -217,13 +228,17 @@ func TestLogout(t *testing.T) {
 
 	// Initialize client with dummy storage
 	storage := DummyStorage{Location: "Blah", LastSave: []byte{'a', 'b', 'c'}}
-	client, err := NewClient(&storage, "hello")
+	client, err := NewClient(&storage, "hello", def)
 	if err != nil {
 		t.Errorf("Failed to initialize dummy client: %s", err.Error())
 	}
 
 	// Connect to gateways and reg server
-	client.Connect(RegGWAddresses[:], "", "", "")
+	err = client.Connect()
+
+	if err != nil {
+		t.Errorf("Client failed of connect: %+v", err)
+	}
 
 	// Logout before logging in should return an error
 	err = client.Logout()
@@ -234,15 +249,14 @@ func TestLogout(t *testing.T) {
 	}
 
 	// Register with a valid registration code
-	userID, err := client.Register(true, ValidRegCode,
-		"", false, getGroup())
+	userID, err := client.Register(true, ValidRegCode, "", "")
 
 	if err != nil {
 		t.Errorf("Register failed: %s", err.Error())
 	}
 
 	// Login to gateway
-	_, err = client.Login(userID, "")
+	_, err = client.Login(userID)
 
 	if err != nil {
 		t.Errorf("Login failed: %s", err.Error())
@@ -267,36 +281,91 @@ func TestLogout(t *testing.T) {
 // gateways used for registration and gateway used for session
 func testMainWrapper(m *testing.M) int {
 
+	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
+
+	rndPort := int(rng.Uint64() % 10000)
+
+	def = getNDF()
+
 	// Start mock gateways used by registration and defer their shutdown (may not be needed)
 	for i, handler := range RegGWHandlers {
-		RegGWAddresses[i] = fmtAddress(RegGWsStartPort + i)
-		gw := gateway.StartGateway(RegGWAddresses[i],
+
+		gw := ndf.Gateway{
+			Address: fmtAddress(GWsStartPort + i + rndPort),
+		}
+
+		def.Gateways = append(def.Gateways, gw)
+
+		GWComms[i] = gateway.StartGateway(gw.Address,
 			handler, "", "")
-		RegGWComms[i] = gw
 	}
 
 	// Start mock registration server and defer its shutdown
-	RegComms = registration.StartRegistrationServer(RegAddress,
+	def.Registration = ndf.Registration{
+		Address: fmtAddress(RegPort + rndPort),
+	}
+	RegComms = registration.StartRegistrationServer(def.Registration.Address,
 		&RegHandler, "", "")
 
-	// Start session gateway and defer its shutdown
-	SessionGWComms = gateway.StartGateway(SessionGWAddress,
-		&SessionGWHandler, "", "")
+	for i := 0; i < NumNodes; i++ {
+		nIdBytes := make([]byte, id.NodeIdLen)
+		nIdBytes[0] = byte(i)
+		n := ndf.Node{
+			ID: nIdBytes,
+		}
+		def.Nodes = append(def.Nodes, n)
+	}
 
 	defer testWrapperShutdown()
 	return m.Run()
 }
 
 func testWrapperShutdown() {
-	for _, gw := range RegGWComms {
+	for _, gw := range GWComms {
 		gw.Shutdown()
 	}
 	RegComms.Shutdown()
-	SessionGWComms.Shutdown()
-}
-
-func getGroup() *cyclic.Group {
-	return globals.InitCrypto()
 }
 
 func fmtAddress(port int) string { return fmt.Sprintf("localhost:%d", port) }
+
+func getNDF() *ndf.NetworkDefinition {
+	return &ndf.NetworkDefinition{
+		E2E: ndf.Group{
+			Prime: "E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B" +
+				"7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AE" +
+				"DF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F" +
+				"8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041" +
+				"023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF45" +
+				"3B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE9209" +
+				"6EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29" +
+				"A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E" +
+				"37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F2" +
+				"78DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696" +
+				"015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E" +
+				"6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873" +
+				"847AEF49F66E43873",
+			SmallPrime: "2",
+			Generator:  "2",
+		},
+		CMIX: ndf.Group{
+			Prime: "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48" +
+				"C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F" +
+				"FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5" +
+				"B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2" +
+				"35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41" +
+				"F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE" +
+				"92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15" +
+				"3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B",
+			SmallPrime: "F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F",
+			Generator: "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613" +
+				"D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4" +
+				"6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472" +
+				"085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5" +
+				"AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA" +
+				"3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71" +
+				"BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0" +
+				"DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7",
+		},
+	}
+}
diff --git a/api/ndf_test.go b/api/ndf_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b2185a149af7662b762bb6e5592525d2cfcc8740
--- /dev/null
+++ b/api/ndf_test.go
@@ -0,0 +1,160 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2019 Privategrity Corporation                                    /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package api
+
+import (
+	"crypto/rand"
+	"encoding/base64"
+	"fmt"
+	"gitlab.com/elixxir/crypto/signature/rsa"
+	"gitlab.com/elixxir/primitives/ndf"
+	"reflect"
+	"testing"
+)
+
+var ExampleJSON = `{"Timestamp": "2019-06-04T20:48:48-07:00", "gateways": [{"Address": "52.25.135.52", "Tls_certificate": "-----BEGIN CERTIFICATE-----\nMIIDgTCCAmmgAwIBAgIJAKLdZ8UigIAeMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjEaMBgGA1UEAwwRZ2F0ZXdheSou\nY21peC5yaXAwHhcNMTkwMzA1MTgzNTU0WhcNMjkwMzAyMTgzNTU0WjBvMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ2xhcmVtb250\nMRswGQYDVQQKDBJQcml2YXRlZ3JpdHkgQ29ycC4xGjAYBgNVBAMMEWdhdGV3YXkq\nLmNtaXgucmlwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9+AaxwDP\nxHbhLmn4HoZu0oUM48Qufc6T5XEZTrpMrqJAouXk+61Jc0EFH96/sbj7VyvnXPRo\ngIENbk2Y84BkB9SkRMIXya/gh9dOEDSgnvj/yg24l3bdKFqBMKiFg00PYB30fU+A\nbe3OI/le0I+v++RwH2AV0BMq+T6PcAGjCC1Q1ZB0wP9/VqNMWq5lbK9wD46IQiSi\n+SgIQeE7HoiAZXrGO0Y7l9P3+VRoXjRQbqfn3ETNL9ZvQuarwAYC9Ix5MxUrS5ag\nOmfjc8bfkpYDFAXRXmdKNISJmtCebX2kDrpP8Bdasx7Fzsx59cEUHCl2aJOWXc7R\n5m3juOVL1HUxjQIDAQABoyAwHjAcBgNVHREEFTATghFnYXRld2F5Ki5jbWl4LnJp\ncDANBgkqhkiG9w0BAQUFAAOCAQEAMu3xoc2LW2UExAAIYYWEETggLNrlGonxteSu\njuJjOR+ik5SVLn0lEu22+z+FCA7gSk9FkWu+v9qnfOfm2Am+WKYWv3dJ5RypW/hD\nNXkOYxVJNYFxeShnHohNqq4eDKpdqSxEcuErFXJdLbZP1uNs4WIOKnThgzhkpuy7\ntZRosvOF1X5uL1frVJzHN5jASEDAa7hJNmQ24kh+ds/Ge39fGD8pK31CWhnIXeDo\nvKD7wivi/gSOBtcRWWLvU8SizZkS3hgTw0lSOf5geuzvasCEYlqrKFssj6cTzbCB\nxy3ra3WazRTNTW4TmkHlCUC9I3oWTTxw5iQxF/I2kQQnwR7L3w==\n-----END CERTIFICATE-----"}, {"Address": "52.25.219.38", "Tls_certificate": "-----BEGIN CERTIFICATE-----\nMIIDgTCCAmmgAwIBAgIJAKLdZ8UigIAeMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjEaMBgGA1UEAwwRZ2F0ZXdheSou\nY21peC5yaXAwHhcNMTkwMzA1MTgzNTU0WhcNMjkwMzAyMTgzNTU0WjBvMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ2xhcmVtb250\nMRswGQYDVQQKDBJQcml2YXRlZ3JpdHkgQ29ycC4xGjAYBgNVBAMMEWdhdGV3YXkq\nLmNtaXgucmlwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9+AaxwDP\nxHbhLmn4HoZu0oUM48Qufc6T5XEZTrpMrqJAouXk+61Jc0EFH96/sbj7VyvnXPRo\ngIENbk2Y84BkB9SkRMIXya/gh9dOEDSgnvj/yg24l3bdKFqBMKiFg00PYB30fU+A\nbe3OI/le0I+v++RwH2AV0BMq+T6PcAGjCC1Q1ZB0wP9/VqNMWq5lbK9wD46IQiSi\n+SgIQeE7HoiAZXrGO0Y7l9P3+VRoXjRQbqfn3ETNL9ZvQuarwAYC9Ix5MxUrS5ag\nOmfjc8bfkpYDFAXRXmdKNISJmtCebX2kDrpP8Bdasx7Fzsx59cEUHCl2aJOWXc7R\n5m3juOVL1HUxjQIDAQABoyAwHjAcBgNVHREEFTATghFnYXRld2F5Ki5jbWl4LnJp\ncDANBgkqhkiG9w0BAQUFAAOCAQEAMu3xoc2LW2UExAAIYYWEETggLNrlGonxteSu\njuJjOR+ik5SVLn0lEu22+z+FCA7gSk9FkWu+v9qnfOfm2Am+WKYWv3dJ5RypW/hD\nNXkOYxVJNYFxeShnHohNqq4eDKpdqSxEcuErFXJdLbZP1uNs4WIOKnThgzhkpuy7\ntZRosvOF1X5uL1frVJzHN5jASEDAa7hJNmQ24kh+ds/Ge39fGD8pK31CWhnIXeDo\nvKD7wivi/gSOBtcRWWLvU8SizZkS3hgTw0lSOf5geuzvasCEYlqrKFssj6cTzbCB\nxy3ra3WazRTNTW4TmkHlCUC9I3oWTTxw5iQxF/I2kQQnwR7L3w==\n-----END CERTIFICATE-----"}, {"Address": "52.41.80.104", "Tls_certificate": "-----BEGIN CERTIFICATE-----\nMIIDgTCCAmmgAwIBAgIJAKLdZ8UigIAeMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjEaMBgGA1UEAwwRZ2F0ZXdheSou\nY21peC5yaXAwHhcNMTkwMzA1MTgzNTU0WhcNMjkwMzAyMTgzNTU0WjBvMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ2xhcmVtb250\nMRswGQYDVQQKDBJQcml2YXRlZ3JpdHkgQ29ycC4xGjAYBgNVBAMMEWdhdGV3YXkq\nLmNtaXgucmlwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9+AaxwDP\nxHbhLmn4HoZu0oUM48Qufc6T5XEZTrpMrqJAouXk+61Jc0EFH96/sbj7VyvnXPRo\ngIENbk2Y84BkB9SkRMIXya/gh9dOEDSgnvj/yg24l3bdKFqBMKiFg00PYB30fU+A\nbe3OI/le0I+v++RwH2AV0BMq+T6PcAGjCC1Q1ZB0wP9/VqNMWq5lbK9wD46IQiSi\n+SgIQeE7HoiAZXrGO0Y7l9P3+VRoXjRQbqfn3ETNL9ZvQuarwAYC9Ix5MxUrS5ag\nOmfjc8bfkpYDFAXRXmdKNISJmtCebX2kDrpP8Bdasx7Fzsx59cEUHCl2aJOWXc7R\n5m3juOVL1HUxjQIDAQABoyAwHjAcBgNVHREEFTATghFnYXRld2F5Ki5jbWl4LnJp\ncDANBgkqhkiG9w0BAQUFAAOCAQEAMu3xoc2LW2UExAAIYYWEETggLNrlGonxteSu\njuJjOR+ik5SVLn0lEu22+z+FCA7gSk9FkWu+v9qnfOfm2Am+WKYWv3dJ5RypW/hD\nNXkOYxVJNYFxeShnHohNqq4eDKpdqSxEcuErFXJdLbZP1uNs4WIOKnThgzhkpuy7\ntZRosvOF1X5uL1frVJzHN5jASEDAa7hJNmQ24kh+ds/Ge39fGD8pK31CWhnIXeDo\nvKD7wivi/gSOBtcRWWLvU8SizZkS3hgTw0lSOf5geuzvasCEYlqrKFssj6cTzbCB\nxy3ra3WazRTNTW4TmkHlCUC9I3oWTTxw5iQxF/I2kQQnwR7L3w==\n-----END CERTIFICATE-----"}], "nodes": [{"Id": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "Dsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIDNDCCAiwCggEBAJ22+1lRtmu2/h4UDx0s5VAjdBYf1lON8WSCGGQvC1xIyPek\nGq36GHMkuHZ0+hgisA8ez4E2lD18VXVyZOWhpE/+AS6ZNuAMHT6TELAcfReYBdMF\niyqfS7b5cWv+YRfGtbPMTZvjQRBK1KgK1slOAF9LmT4U8JHrUXQ78zBQw43iNVZ+\nGzTD1qXAzqoaDzaCE8PRmEPQtLCdy5/HLTnI3kHxvxTUu0Vjyig3FiHK0zJLai05\nIUW+v6x0iAUjb1yi/pK4cc2PnDbTKStVCcqMqneirfx7/XfdpvcRJadFb+oVPkMy\nVqImHGoG7TaTeX55lfrVqrvPvj7aJ0HjdUBK4lsCIQDywxGTdM52yTVpkLRlN0oX\n8j+e01CJvZafYcbd6ZmMHwKCAQBcf/awb48UP+gohDNJPkdpxNmIrOW+JaDiSAln\nBxbGE9ewzuaTL4+qfETSyyRSPaU/vk9uw1lYktGqWMQyigbEahVmLn6qcDod7Pi7\nstBdvi65VsFCozhmHRBGHA0TVHIIUFfzSUMJ/6c8YR94syrbtXQMNhyfNb6QmX2y\nAU4u9apheC9Sq+uL1kMsTdCXvFQjsoXa+2DcNk6BYfSio1rKOhCxxNIDzHakcKM6\n/cvdkpWYWavYtW4XJSUteOrGbnG6muPx3SSHGZh0OTzU2DIYaABlR2Dh40wJ5NFV\nF5+ewNxEc/mWvc5u7Ryr7YtvEW962c9QXfD5mONKsnUUsP/nAoIBAERwUmUlL9YP\nq6MSn+bUr6qNZPsVYoQAo8nTjZWiuSjJa2XWnh7sftnISWkwkiiRxo7qfq3sAiD5\nB8+tM6kONeICBXukldXJerxoVBspYa+RiPuDWy2pwGRDBpfty3QqJOpu5g2ThYFJ\nD5Xu0yCuX8ZJRj33nliI8dQgKdQQva6p2VuXzyRT8LwXMfRwLuSB6Schc9mF8C\nkWCb4m0ujlEKe1xKoKt2zG9b1o7XyaVhxguSUAuEznifMzsEUfuONJOy+XoQELex\nF0wvLzNzABcyxkM3lx52uG41mKgJiV6Z0ZyuBRvt+V3VL/38tPn9lsTaFi8N6/IH\nRyy0bWP5s44=\n-----END PUBLIC KEY-----\n", "Address": "18.237.147.105", "Tls_certificate": "-----BEGIN CERTIFICATE-----MIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDAeFwOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDhDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfsWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSEtJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uAm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9bJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEAAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEAneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIfU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2qvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4cyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1RtgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E56m52PyzMNV+2N21IPppKwA==-----END CERTIFICATE-----"}, {"Id": [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "Dsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIDNDCCAiwCggEBAJ22+1lRtmu2/h4UDx0s5VAjdBYf1lON8WSCGGQvC1xIyPek\nGq36GHMkuHZ0+hgisA8ez4E2lD18VXVyZOWhpE/+AS6ZNuAMHT6TELAcfReYBdMF\niyqfS7b5cWv+YRfGtbPMTZvjQRBK1KgK1slOAF9LmT4U8JHrUXQ78zBQw43iNVZ+\nGzTD1qXAzqoaDzaCE8PRmEPQtLCdy5/HLTnI3kHxvxTUu0Vjyig3FiHK0zJLai05\nIUW+v6x0iAUjb1yi/pK4cc2PnDbTKStVCcqMqneirfx7/XfdpvcRJadFb+oVPkMy\nVqImHGoG7TaTeX55lfrVqrvPvj7aJ0HjdUBK4lsCIQDywxGTdM52yTVpkLRlN0oX\n8j+e01CJvZafYcbd6ZmMHwKCAQBcf/awb48UP+gohDNJPkdpxNmIrOW+JaDiSAln\nBxbGE9ewzuaTL4+qfETSyyRSPaU/vk9uw1lYktGqWMQyigbEahVmLn6qcDod7Pi7\nstBdvi65VsFCozhmHRBGHA0TVHIIUFfzSUMJ/6c8YR94syrbtXQMNhyfNb6QmX2y\nAU4u9apheC9Sq+uL1kMsTdCXvFQjsoXa+2DcNk6BYfSio1rKOhCxxNIDzHakcKM6\n/cvdkpWYWavYtW4XJSUteOrGbnG6muPx3SSHGZh0OTzU2DIYaABlR2Dh40wJ5NFV\nF5+ewNxEc/mWvc5u7Ryr7YtvEW962c9QXfD5mONKsnUUsP/nAoIBAFbADcqA8KQh\nxzgylW6VS1dYYelO5DjPZVVSjfdcbj1twu4ZHDNZLOexpv4nGY8xS6vesELXcVOR\n/CHXgh/3byBZYm0zkrBi/FsJJ3nP2uZ1+QCRldI2KzqcLOWH/CAYj8koork9k1Dp\nFq7rMSDgw4pktqvFj9Eev8dSZuRnoCfZbt/6vxi1r30AYAjDYOwcysqcVyUa1tPa\nLEh3JksttXUCd5cvfqatWedTs5Vxo7ICW1toGBHABYvSJkwK0YFfi5RLw+Oda1sA\njJ+aLcIxQjrpoRC2alXCdwmZXVb+O6zluQctw6LJjt4J704ueSvR4VNNhr0uLYGW\nk7e+WoQCS98=\n-----END PUBLIC KEY-----\n", "Address": "52.11.136.238", "Tls_certificate": "-----BEGIN CERTIFICATE-----MIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDAeFwOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDhDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfsWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSEtJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uAm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9bJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEAAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEAneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIfU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2qvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4cyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1RtgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E56m52PyzMNV+2N21IPppKwA==-----END CERTIFICATE-----"}, {"Id": [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "Dsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIDNTCCAiwCggEBAJ22+1lRtmu2/h4UDx0s5VAjdBYf1lON8WSCGGQvC1xIyPek\nGq36GHMkuHZ0+hgisA8ez4E2lD18VXVyZOWhpE/+AS6ZNuAMHT6TELAcfReYBdMF\niyqfS7b5cWv+YRfGtbPMTZvjQRBK1KgK1slOAF9LmT4U8JHrUXQ78zBQw43iNVZ+\nGzTD1qXAzqoaDzaCE8PRmEPQtLCdy5/HLTnI3kHxvxTUu0Vjyig3FiHK0zJLai05\nIUW+v6x0iAUjb1yi/pK4cc2PnDbTKStVCcqMqneirfx7/XfdpvcRJadFb+oVPkMy\nVqImHGoG7TaTeX55lfrVqrvPvj7aJ0HjdUBK4lsCIQDywxGTdM52yTVpkLRlN0oX\n8j+e01CJvZafYcbd6ZmMHwKCAQBcf/awb48UP+gohDNJPkdpxNmIrOW+JaDiSAln\nBxbGE9ewzuaTL4+qfETSyyRSPaU/vk9uw1lYktGqWMQyigbEahVmLn6qcDod7Pi7\nstBdvi65VsFCozhmHRBGHA0TVHIIUFfzSUMJ/6c8YR94syrbtXQMNhyfNb6QmX2y\nAU4u9apheC9Sq+uL1kMsTdCXvFQjsoXa+2DcNk6BYfSio1rKOhCxxNIDzHakcKM6\n/cvdkpWYWavYtW4XJSUteOrGbnG6muPx3SSHGZh0OTzU2DIYaABlR2Dh40wJ5NFV\nF5+ewNxEc/mWvc5u7Ryr7YtvEW962c9QXfD5mONKsnUUsP/nAoIBAQCN19tTnkS3\nitBQXXR/h8OKl+rliFBLgO6h6GvZL4yQDZFtBAOmkrs3wLoDroJRGCeqz/IUb+JF\njslEr/mpm2kcmK77hr535dq7HsWz1fFl9YyGTaOH055FLSV9QEPAV9j3zWADdQ1v\nuSQll+QfWi6lIibWV4HNQ2ywRFoOY8OBLCJB90UXLeJpaPanpqiM8hjda2VGRDbi\nIixEE2lCOWITydiz2DmvXrLhVGF49+g5MDwbWO65dmasCe//Ff6Z4bJ6n049xv\nVtac8nX6FO3eBsV5d+rG6HZXSG3brCKRCSKYCTX1IkTSiutYxYqvwaluoCjOakh0\nKkqvQ8IeVZ+B\n-----END PUBLIC KEY-----\n", "Address": "34.213.79.31", "Tls_certificate": "-----BEGIN CERTIFICATE-----MIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDAeFwOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDhDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfsWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSEtJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uAm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9bJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEAAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEAneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIfU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2qvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4cyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1RtgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E56m52PyzMNV+2N21IPppKwA==-----END CERTIFICATE-----"}], "registration": {"Dsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIDNDCCAiwCggEBAJ22+1lRtmu2/h4UDx0s5VAjdBYf1lON8WSCGGQvC1xIyPek\nGq36GHMkuHZ0+hgisA8ez4E2lD18VXVyZOWhpE/+AS6ZNuAMHT6TELAcfReYBdMF\niyqfS7b5cWv+YRfGtbPMTZvjQRBK1KgK1slOAF9LmT4U8JHrUXQ78zBQw43iNVZ+\nGzTD1qXAzqoaDzaCE8PRmEPQtLCdy5/HLTnI3kHxvxTUu0Vjyig3FiHK0zJLai05\nIUW+v6x0iAUjb1yi/pK4cc2PnDbTKStVCcqMqneirfx7/XfdpvcRJadFb+oVPkMy\nVqImHGoG7TaTeX55lfrVqrvPvj7aJ0HjdUBK4lsCIQDywxGTdM52yTVpkLRlN0oX\n8j+e01CJvZafYcbd6ZmMHwKCAQBcf/awb48UP+gohDNJPkdpxNmIrOW+JaDiSAln\nBxbGE9ewzuaTL4+qfETSyyRSPaU/vk9uw1lYktGqWMQyigbEahVmLn6qcDod7Pi7\nstBdvi65VsFCozhmHRBGHA0TVHIIUFfzSUMJ/6c8YR94syrbtXQMNhyfNb6QmX2y\nAU4u9apheC9Sq+uL1kMsTdCXvFQjsoXa+2DcNk6BYfSio1rKOhCxxNIDzHakcKM6\n/cvdkpWYWavYtW4XJSUteOrGbnG6muPx3SSHGZh0OTzU2DIYaABlR2Dh40wJ5NFV\nF5+ewNxEc/mWvc5u7Ryr7YtvEW962c9QXfD5mONKsnUUsP/nAoIBAAlELnrXLG0s\n4yAAn7IsVWwY7swDnbBcsIF2cnef4tjm/nNwrFKp5AxYqgeXCiJM8VkyJrotWG50\nnXQwMCR6BsvYrlVt/RmQvR8BSrir62uSLK7hMKm7dXnFvtyFtjp91UwTRbIjxhUQ\nGYnhAzrkCDWo1m54ysqXEGlrVwvRXrCAXiLKPiTEIS+B4GFH9W26SwBxhFLNYSUk\nZZ7+4qwMf9aTu7kIpXTP3hNIyRCjtuZvo5SnymtbLARwTP943hW8MOj0+Ege+m1P\ntey6rkMUGQ86cgK9/7+7Jb+EwW5UxdQtFPUFeNKdQ6zDPS6qbliecUrsc12tdgeg\nhQyuMbyKUuo=\n-----END PUBLIC KEY-----\n", "Address": "registration.default.cmix.rip", "Tls_certificate": "-----BEGIN CERTIFICATE-----MIIDkDCCAnigAwIBAgIJAJnjosuSsP7gMA0GCSqGSIb3DQEBBQUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjEfMB0GA1UEAwwWcmVnaXN0cmF0aW9uKi5jbWl4LnJpcDAeFwOTAzMDUyMTQ5NTZaFw0yOTAzMDIyMTQ5NTZaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjEfMB0GA1UEAwwWcmVnaXN0cmF0aW9uKi5jbWl4LnJpcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOQKvqjdh35o+MECBhCwopJzPlQNmq2iPbewRNtI02bUNK3kLQUbFlYdzNGZS4GYXGc5O+jdi8Slx82r1kdjz5PPCNFBARIsOP/L8r3DGeW+yeJdgBZjm1s3ylkamt4Ajiq/bNjysS6L/WSOp+sVumDxtBEzO/UTU1O6QRnzUphLaiWENmErGvsH0CZVq38Ia58k/QjCAzpUcYi4j2l1fb07xqFcQD8H6SmUM297UyQosDrp8ukdIo31Koxr4XDnnNNsYStC26tzHMeKuJ2Wl+3YzsSyflfM2YEcKE31sqB9DS36UkJ8J84eLsHNImGg3WodFAviDB67+jXDbB30NkMCAwEAAaMlMCMwIQYDVR0RBBowGIIWcmVnaXN0cmF0aW9uKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEAF9mNzk+g+o626Rllt3f3/1qIyYQrYJ0BjSWCKYEFMCgZ4JibAJjAvIajhVYERtltffM+YKcdE2kTpdzJ0YJuUnRfuv6sVnXlVVugUUnd4IOigmjbCdM32k170CYMm0aiwGxl4FrNa8ei7AIax/s1n+sqWq3HeW5LXjnoVb+s3HeCWIuLfcgrurfye8FnNhy14HFzxVYYefIKmL+DPlcGGGm/PPYt3u4a2+rP3xaihc65dTa0u5tf/XPXtPxTDPFj2JeQDFxo7QRREbPD89CtYnwuP937CrkvCKrL0GkW1FViXKqZY9F5uhxrvLIpzhbNrs/EbtweY35XGLDCCMkg==-----END CERTIFICATE-----"}, "udb": {"Id": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3], "Dsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIDNDCCAiwCggEBAJ22+1lRtmu2/h4UDx0s5VAjdBYf1lON8WSCGGQvC1xIyPek\nGq36GHMkuHZ0+hgisA8ez4E2lD18VXVyZOWhpE/+AS6ZNuAMHT6TELAcfReYBdMF\niyqfS7b5cWv+YRfGtbPMTZvjQRBK1KgK1slOAF9LmT4U8JHrUXQ78zBQw43iNVZ+\nGzTD1qXAzqoaDzaCE8PRmEPQtLCdy5/HLTnI3kHxvxTUu0Vjyig3FiHK0zJLai05\nIUW+v6x0iAUjb1yi/pK4cc2PnDbTKStVCcqMqneirfx7/XfdpvcRJadFb+oVPkMy\nVqImHGoG7TaTeX55lfrVqrvPvj7aJ0HjdUBK4lsCIQDywxGTdM52yTVpkLRlN0oX\n8j+e01CJvZafYcbd6ZmMHwKCAQBcf/awb48UP+gohDNJPkdpxNmIrOW+JaDiSAln\nBxbGE9ewzuaTL4+qfETSyyRSPaU/vk9uw1lYktGqWMQyigbEahVmLn6qcDod7Pi7\nstBdvi65VsFCozhmHRBGHA0TVHIIUFfzSUMJ/6c8YR94syrbtXQMNhyfNb6QmX2y\nAU4u9apheC9Sq+uL1kMsTdCXvFQjsoXa+2DcNk6BYfSio1rKOhCxxNIDzHakcKM6\n/cvdkpWYWavYtW4XJSUteOrGbnG6muPx3SSHGZh0OTzU2DIYaABlR2Dh40wJ5NFV\nF5+ewNxEc/mWvc5u7Ryr7YtvEW962c9QXfD5mONKsnUUsP/nAoIBACvR2lUslz3D\nB/MUo0rHVIHVkhVJCxNjtgTOYgJ9ckArSXQbYzr/fcigcNGjUO2LbK5NFp9GK43C\nrLxMUnJ9nkyIVPaWvquJFZItjcDK3NiNGyD4XyM0eRj4dYeSxQM48hvFbmtbjlXn\n9SQTnGIlr1XnTI4RVHZSQOL6kFJIaLw6wYrQ4w08Ng+p45brp5ercAHnLiftNUWP\nqROhQkdSEpS9LEwfotUSY1jP2AhQfaIMxaeXsZuTU1IYvdhMFRL3DR0r5Ww2Upf8\ng0Ace0mtnsUQ2OG+7MTh2jYIEWRjvuoe3RCz603ujW6g7BfQ1H7f4YFwc5xOOJ3u\nr4dj49dCCjc=\n-----END PUBLIC KEY-----\n"}, "E2e": {"Prime": "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", "Small_prime": "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68948127044533E63A0105DF531D89CD9128A5043CC71A026EF7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9EE1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AFC1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36B3861AA7255E4C0278BA3604650C10BE19482F23171B671DF1CF3B960C074301CD93C1D17603D147DAE2AEF837A62964EF15E5FB4AAC0B8C1CCAA4BE754AB5728AE9130C4C7D02880AB9472D455655347FFFFFFFFFFFFFFF", "Generator": "02"}, "CMIX": {"Prime": "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", "Small_prime": "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68948127044533E63A0105DF531D89CD9128A5043CC71A026EF7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9EE1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AFC1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36B3861AA7255E4C0278BA3604650C10BE19482F23171B671DF1CF3B960C074301CD93C1D17603D147DAE2AEF837A62964EF15E5FB4AAC0B8C1CCAA4BE754AB5728AE9130C4C7D02880AB9472D455655347FFFFFFFFFFFFFFF", "Generator": "02"}}`
+
+// Tests that VerifyNDF() correctly verifies the NDF signature.
+func TestVerifyNDF(t *testing.T) {
+	// Generate RSA private and public keys
+	// Size of 768 is unsafe, but allows the test to run faster
+	privateKey, _ := rsa.GenerateKey(rand.Reader, 768)
+	publicKey := &rsa.PublicKey{PublicKey: privateKey.PublicKey}
+	publicKeyBytes := rsa.CreatePublicKeyPem(publicKey)
+
+	// Sign the NDF
+	ndfJSON, _, _ := ndf.DecodeNDF(ExampleJSON + "\n")
+	opts := rsa.NewDefaultOptions()
+	rsaHash := opts.Hash.New()
+	rsaHash.Write(ndfJSON.Serialize())
+	signature, _ := rsa.Sign(
+		rand.Reader, privateKey, opts.Hash, rsaHash.Sum(nil), nil)
+
+	// Print error on panic
+	defer func() {
+		if r := recover(); r != nil {
+			t.Errorf("VerifyNDF() panicked when it was not supposed to"+
+				"\n\treceived: %#v\n\texpected: %#v", r, nil)
+		}
+	}()
+
+	// Compose network definition string
+	ndfString := ExampleJSON + "\n" + base64.StdEncoding.EncodeToString(signature)
+
+	// Run VerifyNDF()
+	fmt.Println(string(publicKeyBytes))
+	fmt.Println(ndfString)
+	ndfJSONOutput := VerifyNDF(ndfString, string(publicKeyBytes))
+
+	// Check that the output is the expected NetworkDefinition structure
+	if !reflect.DeepEqual(ndfJSONOutput, ndfJSON) {
+		t.Errorf("VerifyNDF() did not output the correct "+
+			"NetworkDefinition structure"+
+			"\n\treceived: %#v\n\texpected: %#v",
+			ndfJSONOutput, ndfJSON)
+	}
+}
+
+// Tests that VerifyNDF() panics when given the incorrect RSA public key.
+func TestVerifyNDF_ErrPublicKey(t *testing.T) {
+	// Generate RSA private key and fake RSA public key
+	// Size of 768 is unsafe, but allows the test to run faster
+	privateKey, _ := rsa.GenerateKey(rand.Reader, 768)
+
+	privateKey2, _ := rsa.GenerateKey(rand.Reader, 768)
+	publicKey := &rsa.PublicKey{PublicKey: privateKey2.PublicKey}
+	publicKeyBytes := rsa.CreatePublicKeyPem(publicKey)
+
+	// Sign the NDF
+	ndfJSON, _, _ := ndf.DecodeNDF(ExampleJSON + "\n")
+	opts := rsa.NewDefaultOptions()
+	rsaHash := opts.Hash.New()
+	rsaHash.Write(ndfJSON.Serialize())
+	signature, _ := rsa.Sign(
+		rand.Reader, privateKey, opts.Hash, rsaHash.Sum(nil), nil)
+
+	// Print error on no panic
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("VerifyNDF() did not panic when expected when the "+
+				"public key is invalid"+
+				"\n\treceived: %#v\n\texpected: %#v",
+				r, "Could not verify NDF: crypto/rsa: verification error")
+		}
+	}()
+
+	// Compose network definition string
+	ndfString := ExampleJSON + "\n" + base64.StdEncoding.EncodeToString(signature)
+
+	// Run VerifyNDF()
+	VerifyNDF(ndfString, string(publicKeyBytes))
+}
+
+// Tests that VerifyNDF() panics when given an invalid NDF string.
+func TestVerifyNDF_ErrInvalidNDF(t *testing.T) {
+	// Generate RSA private key and fake RSA public key
+	// Size of 768 is unsafe, but allows the test to run faster
+	privateKey, _ := rsa.GenerateKey(rand.Reader, 768)
+
+	privateKey2, _ := rsa.GenerateKey(rand.Reader, 768)
+	publicKey := &rsa.PublicKey{PublicKey: privateKey2.PublicKey}
+	publicKeyBytes := rsa.CreatePublicKeyPem(publicKey)
+
+	// Sign the NDF
+	ndfJSON, _, _ := ndf.DecodeNDF(ExampleJSON + "\n")
+	opts := rsa.NewDefaultOptions()
+	rsaHash := opts.Hash.New()
+	rsaHash.Write(ndfJSON.Serialize())
+	signature, _ := rsa.Sign(
+		rand.Reader, privateKey, opts.Hash, rsaHash.Sum(nil), nil)
+
+	// Print error on no panic
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("VerifyNDF() did not panic when expected when given "+
+				"invalid NDF"+
+				"\n\treceived: %#v\n\texpected: %#v",
+				r, "Could not decode NDF: unexpected end of JSON input")
+		}
+	}()
+
+	// Compose network definition string
+	ndfString := "   \n" + base64.StdEncoding.EncodeToString(signature)
+
+	// Run VerifyNDF()
+	VerifyNDF(ndfString, string(publicKeyBytes))
+}
+
+// Tests that VerifyNDF() correctly outputs a NetworkDefinition structure and
+// skips verifying the signature when the public key is empty.
+func TestVerifyNDF_EmptyPublicKey(t *testing.T) {
+	// Generate RSA private and public keys
+	// Size of 768 is unsafe, but allows the test to run faster
+	privateKey, _ := rsa.GenerateKey(rand.Reader, 768)
+
+	// Sign the NDF
+	ndfJSON, _, _ := ndf.DecodeNDF(ExampleJSON + "\n")
+	opts := rsa.NewDefaultOptions()
+	rsaHash := opts.Hash.New()
+	rsaHash.Write(ndfJSON.Serialize())
+	signature, _ := rsa.Sign(
+		rand.Reader, privateKey, opts.Hash, rsaHash.Sum(nil), nil)
+
+	// Compose network definition string
+	ndfString := ExampleJSON + "\n" + base64.StdEncoding.EncodeToString(signature)
+
+	// Run VerifyNDF()
+	ndfJSONOutput := VerifyNDF(ndfString, "")
+
+	// Check that the output is the expected NetworkDefinition structure
+	if !reflect.DeepEqual(ndfJSONOutput, ndfJSON) {
+		t.Errorf("VerifyNDF() did not output the correct "+
+			"NetworkDefinition structure"+
+			"\n\treceived: %#v\n\texpected: %#v",
+			ndfJSONOutput, ndfJSON)
+	}
+}
diff --git a/bindings/client.go b/bindings/client.go
index d4437fdbb746237c5de4f18675a7e50c4376804b..4db478d73201f9e6e608063783d5fe72abedd8ee 100644
--- a/bindings/client.go
+++ b/bindings/client.go
@@ -8,15 +8,13 @@ package bindings
 
 import (
 	"errors"
+	"fmt"
 	"gitlab.com/elixxir/client/api"
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/parse"
-	"gitlab.com/elixxir/crypto/cyclic"
-	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/switchboard"
 	"io"
-	"strings"
 )
 
 type Client struct {
@@ -73,7 +71,7 @@ func (cl *Client) Listen(userId []byte, messageType int32, newListener Listener)
 
 	listener := &listenerProxy{proxy: newListener}
 
-	return cl.client.Listen(typedUserId, format.None, messageType, listener)
+	return cl.client.Listen(typedUserId, messageType, listener)
 }
 
 // Pass the listener handle that Listen() returned to delete the listener
@@ -98,13 +96,15 @@ func FormatTextMessage(message string) []byte {
 // loc is a string. If you're using DefaultStorage for your storage,
 // this would be the filename of the file that you're storing the user
 // session in.
-func NewClient(storage Storage, loc string) (*Client, error) {
+func NewClient(storage Storage, loc string, ndfStr, ndfPubKey string) (*Client, error) {
 	if storage == nil {
 		return nil, errors.New("could not init client: Storage was nil")
 	}
 
+	ndf := api.VerifyNDF(ndfStr, ndfPubKey)
+
 	proxy := &storageProxy{boundStorage: storage}
-	cl, err := api.NewClient(globals.Storage(proxy), loc)
+	cl, err := api.NewClient(globals.Storage(proxy), loc, ndf)
 
 	return &Client{client: cl}, err
 }
@@ -112,15 +112,8 @@ func NewClient(storage Storage, loc string) (*Client, error) {
 // Connects to gateways and registration server (if needed)
 // using TLS filepaths to create credential information
 // for connection establishment
-func (cl *Client) Connect(gwAddressesList, gwCertPath,
-	regAddr, regCertPath string) error {
-
-	if gwAddressesList == "" {
-		return errors.New("invalid number of nodes")
-	}
-
-	gwList := strings.Split(gwAddressesList, ",")
-	return cl.client.Connect(gwList, gwCertPath, regAddr, regCertPath)
+func (cl *Client) Connect() error {
+	return cl.client.Connect()
 }
 
 // Registers user and returns the User ID bytes.
@@ -130,17 +123,9 @@ func (cl *Client) Connect(gwAddressesList, gwCertPath,
 // registrationAddr is the address of the registration server
 // gwAddressesList is CSV of gateway addresses
 // grp is the CMIX group needed for keys generation in JSON string format
-func (cl *Client) Register(preCan bool, registrationCode, nick string,
-	mint bool, grpJSON string) ([]byte, error) {
-
-	// Unmarshal group JSON
-	var grp cyclic.Group
-	err := grp.UnmarshalJSON([]byte(grpJSON))
-	if err != nil {
-		return id.ZeroID[:], err
-	}
-
-	UID, err := cl.client.Register(preCan, registrationCode, nick, mint, &grp)
+func (cl *Client) Register(preCan bool, registrationCode, nick, email string) ([]byte, error) {
+	fmt.Println("calling client reg")
+	UID, err := cl.client.Register(preCan, registrationCode, nick, email)
 
 	if err != nil {
 		return id.ZeroID[:], err
@@ -158,9 +143,9 @@ func (cl *Client) Register(preCan bool, registrationCode, nick string,
 // certificate string to "default", the bindings will use that certificate.
 // If you leave it empty, the Client will try to connect to the GW without TLS
 // This should only ever be used for testing purposes
-func (cl *Client) Login(UID []byte, email string) (string, error) {
+func (cl *Client) Login(UID []byte) (string, error) {
 	userID := id.NewUserFromBytes(UID)
-	return cl.client.Login(userID, email)
+	return cl.client.Login(userID)
 }
 
 // Sends a message structured via the message interface
@@ -170,11 +155,11 @@ func (cl *Client) Send(m Message, encrypt bool) error {
 	sender := id.NewUserFromBytes(m.GetSender())
 	recipient := id.NewUserFromBytes(m.GetRecipient())
 
-	var cryptoType format.CryptoType
+	var cryptoType parse.CryptoType
 	if encrypt {
-		cryptoType = format.E2E
+		cryptoType = parse.E2E
 	} else {
-		cryptoType = format.Unencrypted
+		cryptoType = parse.Unencrypted
 	}
 
 	return cl.client.Send(&parse.Message{
@@ -182,9 +167,9 @@ func (cl *Client) Send(m Message, encrypt bool) error {
 			MessageType: m.GetMessageType(),
 			Body:        m.GetPayload(),
 		},
-		CryptoType: cryptoType,
-		Sender:     sender,
-		Receiver:   recipient,
+		InferredType: cryptoType,
+		Sender:       sender,
+		Receiver:     recipient,
 	})
 }
 
diff --git a/bindings/client_test.go b/bindings/client_test.go
index 3b57f8330c3bd59d88ab14d9349ed2be384c371c..13c90eb54ead7b4f84e65c7d0e1dcaa6b7d628b2 100644
--- a/bindings/client_test.go
+++ b/bindings/client_test.go
@@ -8,38 +8,52 @@ package bindings
 
 import (
 	"bytes"
+	crand "crypto/rand"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
 	"gitlab.com/elixxir/client/api"
 	"gitlab.com/elixxir/client/cmixproto"
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/comms/gateway"
-	"gitlab.com/elixxir/crypto/cyclic"
-	"gitlab.com/elixxir/crypto/large"
+	"gitlab.com/elixxir/crypto/signature/rsa"
 	"gitlab.com/elixxir/primitives/id"
+	"gitlab.com/elixxir/primitives/ndf"
+	"math/rand"
 	"os"
 	"reflect"
 	"testing"
 	"time"
 )
 
-const gwAddress = "localhost:5557"
+const NumNodes = 1
+const NumGWs = NumNodes
+const GWsStartPort = 10000
 
+const ValidRegCode = "UAV6IWD6"
+
+var GWComms [NumGWs]*gateway.GatewayComms
+
+var def *ndf.NetworkDefinition
+
+// Setups general testing params and calls test wrapper
 func TestMain(m *testing.M) {
-	gwShutDown := gateway.StartGateway(gwAddress,
-		gateway.NewImplementation(), "", "")
-	time.Sleep(100 * time.Millisecond)
-	defer gwShutDown.Shutdown()
-	os.Exit(m.Run())
+
+	os.Exit(testMainWrapper(m))
 }
 
 // Make sure NewClient returns an error when called incorrectly.
 func TestNewClientNil(t *testing.T) {
-	_, err := NewClient(nil, "")
+
+	ndfStr, pubKey := getNDFJSONStr(def, t)
+
+	_, err := NewClient(nil, "", ndfStr, pubKey)
 	if err == nil {
 		t.Errorf("NewClient returned nil on invalid (nil, nil) input!")
 	}
 
-	_, err = NewClient(nil, "hello")
+	_, err = NewClient(nil, "hello", "", "")
 	if err == nil {
 		t.Errorf("NewClient returned nil on invalid (nil, 'hello') input!")
 	}
@@ -47,7 +61,10 @@ func TestNewClientNil(t *testing.T) {
 
 func TestNewClient(t *testing.T) {
 	d := api.DummyStorage{Location: "Blah", LastSave: []byte{'a', 'b', 'c'}}
-	client, err := NewClient(&d, "hello")
+
+	ndfStr, pubKey := getNDFJSONStr(def, t)
+
+	client, err := NewClient(&d, "hello", ndfStr, pubKey)
 	if err != nil {
 		t.Errorf("NewClient returned error: %v", err)
 	} else if client == nil {
@@ -55,52 +72,26 @@ func TestNewClient(t *testing.T) {
 	}
 }
 
-// BytesReceiver receives the last message and puts the data it received into
-// byte slices
-type BytesReceiver struct {
-	receptionBuffer []byte
-	lastSID         []byte
-	lastRID         []byte
-}
+func TestRegister(t *testing.T) {
 
-// This is the method that globals.Receive calls when you set a BytesReceiver
-// as the global receiver
-func (br *BytesReceiver) Receive(message Message) {
-	br.receptionBuffer = append(br.receptionBuffer, message.GetPayload()...)
-	br.lastRID = message.GetRecipient()
-	br.lastSID = message.GetSender()
-}
+	ndfStr, pubKey := getNDFJSONStr(def, t)
 
-func TestRegister(t *testing.T) {
-	registrationCode := "UAV6IWD6"
 	d := api.DummyStorage{Location: "Blah", LastSave: []byte{'a', 'b', 'c'}}
-	client, err := NewClient(&d, "hello")
-	primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
-		"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
-		"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
-		"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
-		"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
-		"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
-		"83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
-		"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" +
-		"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" +
-		"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" +
-		"15728E5A8AACAA68FFFFFFFFFFFFFFFF"
-	p := large.NewInt(1)
-	p.SetString(primeString, 16)
-	g := large.NewInt(2)
-	q := large.NewInt(3)
-	grp := cyclic.NewGroup(p, g, q)
-	grpJSON, err := grp.MarshalJSON()
+	client, err := NewClient(&d, "hello", ndfStr, pubKey)
+
 	if err != nil {
 		t.Errorf("Failed to marshal group JSON: %s", err)
 	}
 
 	// Connect to gateway
-	client.Connect(gwAddress, "", "", "")
+	err = client.Connect()
+
+	if err != nil {
+		t.Errorf("Could not connect: %+v", err)
+	}
 
-	regRes, err := client.Register(true, registrationCode,
-		"", false, string(grpJSON))
+	regRes, err := client.Register(true, ValidRegCode,
+		"", "")
 	if err != nil {
 		t.Errorf("Registration failed: %s", err.Error())
 	}
@@ -110,11 +101,18 @@ func TestRegister(t *testing.T) {
 }
 
 func TestConnectBadNumNodes(t *testing.T) {
+
+	newDef := *def
+
+	newDef.Gateways = make([]ndf.Gateway, 0)
+
+	ndfStr, pubKey := getNDFJSONStr(&newDef, t)
+
 	d := api.DummyStorage{Location: "Blah", LastSave: []byte{'a', 'b', 'c'}}
-	client, err := NewClient(&d, "hello")
+	client, err := NewClient(&d, "hello", ndfStr, pubKey)
 
 	// Connect to empty gw
-	err = client.Connect("", "", "", "")
+	err = client.Connect()
 
 	if err == nil {
 		t.Errorf("Connect should have returned an error when no gateway is passed")
@@ -122,38 +120,23 @@ func TestConnectBadNumNodes(t *testing.T) {
 }
 
 func TestLoginLogout(t *testing.T) {
-	registrationCode := "UAV6IWD6"
+
+	ndfStr, pubKey := getNDFJSONStr(def, t)
+
 	d := api.DummyStorage{Location: "Blah", LastSave: []byte{'a', 'b', 'c'}}
-	client, err := NewClient(&d, "hello")
-	primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
-		"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
-		"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
-		"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
-		"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
-		"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
-		"83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
-		"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" +
-		"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" +
-		"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" +
-		"15728E5A8AACAA68FFFFFFFFFFFFFFFF"
-	p := large.NewInt(1)
-	p.SetString(primeString, 16)
-	g := large.NewInt(2)
-	q := large.NewInt(3)
-	grp := cyclic.NewGroup(p, g, q)
-	grpJSON, err := grp.MarshalJSON()
-	if err != nil {
-		t.Errorf("Failed to marshal group JSON: %s", err)
-	}
+	client, err := NewClient(&d, "hello", ndfStr, pubKey)
 
 	// Connect to gateway
-	client.Connect(gwAddress, "", "", "")
+	err = client.Connect()
+	if err != nil {
+		t.Errorf("Could not connect: %+v", err)
+	}
 
-	regRes, err := client.Register(true, registrationCode,
-		"", false, string(grpJSON))
-	loginRes, err2 := client.Login(regRes, "")
+	regRes, err := client.Register(true, ValidRegCode,
+		"", "")
+	loginRes, err2 := client.Login(regRes)
 	if err2 != nil {
-		t.Errorf("Login failed: %s", err.Error())
+		t.Errorf("Login failed: %s", err2.Error())
 	}
 	if len(loginRes) == 0 {
 		t.Errorf("Invalid login received: %v", loginRes)
@@ -161,7 +144,7 @@ func TestLoginLogout(t *testing.T) {
 	time.Sleep(2000 * time.Millisecond)
 	err3 := client.Logout()
 	if err3 != nil {
-		t.Errorf("Logoutfailed: %s", err.Error())
+		t.Errorf("Logoutfailed: %s", err3.Error())
 	}
 }
 
@@ -173,36 +156,27 @@ func (m *MockListener) Hear(msg Message, isHeardElsewhere bool) {
 
 // Proves that a message can be received by a listener added with the bindings
 func TestListen(t *testing.T) {
-	registrationCode := "UAV6IWD6"
+
+	ndfStr, pubKey := getNDFJSONStr(def, t)
+
 	d := api.DummyStorage{Location: "Blah", LastSave: []byte{'a', 'b', 'c'}}
-	client, err := NewClient(&d, "hello")
-	primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
-		"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
-		"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
-		"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
-		"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
-		"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
-		"83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
-		"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" +
-		"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" +
-		"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" +
-		"15728E5A8AACAA68FFFFFFFFFFFFFFFF"
-	p := large.NewInt(1)
-	p.SetString(primeString, 16)
-	g := large.NewInt(2)
-	q := large.NewInt(3)
-	grp := cyclic.NewGroup(p, g, q)
-	grpJSON, err := grp.MarshalJSON()
+	client, err := NewClient(&d, "hello", ndfStr, pubKey)
+
+	// Connect to gateway
+	err = client.Connect()
+
 	if err != nil {
-		t.Errorf("Failed to marshal group JSON: %s", err)
+		t.Errorf("Could not connect: %+v", err)
 	}
 
-	// Connect to gateway
-	client.Connect(gwAddress, "", "", "")
+	regRes, _ := client.Register(true, ValidRegCode,
+		"", "")
+	_, err = client.Login(regRes)
+
+	if err != nil {
+		t.Errorf("Could not log in: %+v", err)
+	}
 
-	regRes, _ := client.Register(true, registrationCode,
-		"", false, string(grpJSON))
-	client.Login(regRes, "")
 	listener := MockListener(false)
 	client.Listen(id.ZeroID[:], int32(cmixproto.Type_NO_TYPE), &listener)
 	client.client.GetSwitchboard().Speak(&parse.Message{
@@ -219,36 +193,28 @@ func TestListen(t *testing.T) {
 }
 
 func TestStopListening(t *testing.T) {
-	registrationCode := "UAV6IWD6"
+
+	ndfStr, pubKey := getNDFJSONStr(def, t)
+
 	d := api.DummyStorage{Location: "Blah", LastSave: []byte{'a', 'b', 'c'}}
-	client, err := NewClient(&d, "hello")
-	primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
-		"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
-		"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
-		"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
-		"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
-		"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
-		"83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
-		"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" +
-		"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" +
-		"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" +
-		"15728E5A8AACAA68FFFFFFFFFFFFFFFF"
-	p := large.NewInt(1)
-	p.SetString(primeString, 16)
-	g := large.NewInt(2)
-	q := large.NewInt(3)
-	grp := cyclic.NewGroup(p, g, q)
-	grpJSON, err := grp.MarshalJSON()
+	client, err := NewClient(&d, "hello", ndfStr, pubKey)
+
+	// Connect to gateway
+	err = client.Connect()
+
 	if err != nil {
-		t.Errorf("Failed to marshal group JSON: %s", err)
+		t.Errorf("Could not connect: %+v", err)
 	}
 
-	// Connect to gateway
-	client.Connect(gwAddress, "", "", "")
+	regRes, _ := client.Register(true, ValidRegCode,
+		"", "")
+
+	_, err = client.Login(regRes)
+
+	if err != nil {
+		t.Errorf("Could not log in: %+v", err)
+	}
 
-	regRes, _ := client.Register(true, registrationCode,
-		"", false, string(grpJSON))
-	client.Login(regRes, "")
 	listener := MockListener(false)
 	handle := client.Listen(id.ZeroID[:], int32(cmixproto.Type_NO_TYPE), &listener)
 	client.StopListening(handle)
@@ -308,3 +274,112 @@ func TestParse(t *testing.T) {
 	}
 
 }
+
+func getNDFJSONStr(netDef *ndf.NetworkDefinition, t *testing.T) (string, string) {
+	ndfBytes, err := json.Marshal(netDef)
+
+	if err != nil {
+		t.Errorf("Could not JSON the NDF: %+v", err)
+	}
+
+	privateKey, _ := rsa.GenerateKey(crand.Reader, 768)
+	publicKey := &rsa.PublicKey{PublicKey: privateKey.PublicKey}
+	publicKeyPem := string(rsa.CreatePublicKeyPem(publicKey))
+
+	// Sign the NDF
+	opts := rsa.NewDefaultOptions()
+	rsaHash := opts.Hash.New()
+	rsaHash.Write(netDef.Serialize())
+	signature, _ := rsa.Sign(
+		crand.Reader, privateKey, opts.Hash, rsaHash.Sum(nil), nil)
+
+	// Compose network definition string
+	ndfStr := string(ndfBytes) + "\n" + base64.StdEncoding.EncodeToString(signature)
+
+	return ndfStr, publicKeyPem
+}
+
+// Handles initialization of mock registration server,
+// gateways used for registration and gateway used for session
+func testMainWrapper(m *testing.M) int {
+
+	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
+
+	rndPort := int(rng.Uint64() % 10000)
+
+	def = getNDF()
+
+	// Start mock gateways used by registration and defer their shutdown (may not be needed)
+	for i := 0; i < NumGWs; i++ {
+
+		gw := ndf.Gateway{
+			Address: fmtAddress(GWsStartPort + i + rndPort),
+		}
+
+		def.Gateways = append(def.Gateways, gw)
+
+		GWComms[i] = gateway.StartGateway(gw.Address,
+			gateway.NewImplementation(), "", "")
+	}
+
+	for i := 0; i < NumNodes; i++ {
+		nIdBytes := make([]byte, id.NodeIdLen)
+		nIdBytes[0] = byte(i)
+		n := ndf.Node{
+			ID: nIdBytes,
+		}
+		def.Nodes = append(def.Nodes, n)
+	}
+
+	defer testWrapperShutdown()
+	return m.Run()
+}
+
+func testWrapperShutdown() {
+	for _, gw := range GWComms {
+		gw.Shutdown()
+	}
+}
+
+func fmtAddress(port int) string { return fmt.Sprintf("localhost:%d", port) }
+
+func getNDF() *ndf.NetworkDefinition {
+	return &ndf.NetworkDefinition{
+		E2E: ndf.Group{
+			Prime: "E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B" +
+				"7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AE" +
+				"DF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F" +
+				"8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041" +
+				"023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF45" +
+				"3B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE9209" +
+				"6EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29" +
+				"A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E" +
+				"37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F2" +
+				"78DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696" +
+				"015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E" +
+				"6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873" +
+				"847AEF49F66E43873",
+			SmallPrime: "2",
+			Generator:  "2",
+		},
+		CMIX: ndf.Group{
+			Prime: "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48" +
+				"C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F" +
+				"FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5" +
+				"B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2" +
+				"35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41" +
+				"F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE" +
+				"92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15" +
+				"3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B",
+			SmallPrime: "F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F",
+			Generator: "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613" +
+				"D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4" +
+				"6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472" +
+				"085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5" +
+				"AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA" +
+				"3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71" +
+				"BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0" +
+				"DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7",
+		},
+	}
+}
diff --git a/bots/bots.go b/bots/bots.go
index 2db7183ef951b41b1632279554587b7d1d80575c..e001f0125206fa6bd665e5d7493e5b18e6f840d4 100644
--- a/bots/bots.go
+++ b/bots/bots.go
@@ -6,12 +6,13 @@ import (
 	"gitlab.com/elixxir/client/io"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/primitives/format"
+	"gitlab.com/elixxir/primitives/circuit"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/switchboard"
 )
 
 var session user.Session
+var topology *circuit.Circuit
 var messaging io.Communications
 
 // UdbID is the ID of the user discovery bot, which is always 3
@@ -48,7 +49,7 @@ func (l *nickReqListener) Hear(msg switchboard.Item, isHeardElsewhere bool) {
 var nicknameRequestListener nickReqListener
 
 // InitBots is called internally by the Login API
-func InitBots(s user.Session, m io.Communications) {
+func InitBots(s user.Session, m io.Communications, top *circuit.Circuit) {
 	UdbID = id.NewUserFromUints(&[4]uint64{0, 0, 0, 3})
 
 	pushKeyResponseListener = make(channelResponseListener)
@@ -59,39 +60,33 @@ func InitBots(s user.Session, m io.Communications) {
 	nicknameResponseListener = make(channelResponseListener)
 
 	session = s
+	topology = top
 	messaging = m
 	l := session.GetSwitchboard()
 
-	l.Register(UdbID,
-		format.None, int32(cmixproto.Type_UDB_PUSH_KEY_RESPONSE),
+	l.Register(UdbID, int32(cmixproto.Type_UDB_PUSH_KEY_RESPONSE),
 		&pushKeyResponseListener)
-	l.Register(UdbID,
-		format.None, int32(cmixproto.Type_UDB_GET_KEY_RESPONSE),
+	l.Register(UdbID, int32(cmixproto.Type_UDB_GET_KEY_RESPONSE),
 		&getKeyResponseListener)
-	l.Register(UdbID,
-		format.None, int32(cmixproto.Type_UDB_REGISTER_RESPONSE),
+	l.Register(UdbID, int32(cmixproto.Type_UDB_REGISTER_RESPONSE),
 		&registerResponseListener)
-	l.Register(UdbID,
-		format.None, int32(cmixproto.Type_UDB_SEARCH_RESPONSE),
+	l.Register(UdbID, int32(cmixproto.Type_UDB_SEARCH_RESPONSE),
 		&searchResponseListener)
 	l.Register(id.ZeroID,
-		format.None, int32(cmixproto.Type_NICKNAME_REQUEST),
-		&nicknameRequestListener)
+		int32(cmixproto.Type_NICKNAME_REQUEST), &nicknameRequestListener)
 	l.Register(id.ZeroID,
-		format.None, int32(cmixproto.Type_NICKNAME_RESPONSE),
-		&nicknameResponseListener)
+		int32(cmixproto.Type_NICKNAME_RESPONSE), &nicknameResponseListener)
 }
 
 // sendCommand sends a command to the udb. This doesn't block.
 // Callers that need to wait on a response should implement waiting with a
 // listener.
 func sendCommand(botID *id.User, command []byte) error {
-	return messaging.SendMessage(session, botID,
-		format.Unencrypted, command)
+	return messaging.SendMessage(session, topology, botID,
+		parse.Unencrypted, command)
 }
 
 // Nickname Lookup function
-
 func LookupNick(user *id.User) (string, error) {
 	globals.Log.DEBUG.Printf("Sending nickname request to user %v", *user)
 	msg := parse.Pack(&parse.TypedBody{
diff --git a/bots/bots_test.go b/bots/bots_test.go
index 1cb0b512771352983b1a9a8ef0d650961b624c21..88a7bafc0ba67a78dcd002d0fd05cfb016f2c9ad 100644
--- a/bots/bots_test.go
+++ b/bots/bots_test.go
@@ -16,6 +16,9 @@ import (
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/elixxir/crypto/large"
+	"gitlab.com/elixxir/primitives/circuit"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
 	"os"
@@ -31,8 +34,9 @@ type dummyMessaging struct {
 
 // SendMessage to the server
 func (d *dummyMessaging) SendMessage(sess user.Session,
+	topology *circuit.Circuit,
 	recipientID *id.User,
-	cryptoType format.CryptoType,
+	cryptoType parse.CryptoType,
 	message []byte) error {
 	jww.INFO.Printf("Sending: %s", string(message))
 	return nil
@@ -40,8 +44,9 @@ func (d *dummyMessaging) SendMessage(sess user.Session,
 
 // SendMessage without partitions to the server
 func (d *dummyMessaging) SendMessageNoPartition(sess user.Session,
+	topology *circuit.Circuit,
 	recipientID *id.User,
-	cryptoType format.CryptoType,
+	cryptoType parse.CryptoType,
 	message []byte) error {
 	jww.INFO.Printf("Sending: %s", string(message))
 	return nil
@@ -61,12 +66,18 @@ func TestMain(m *testing.M) {
 		User: id.NewUserFromUints(&[4]uint64{0, 0, 0, 18}),
 		Nick: "Bernie",
 	}
+
+	cmixGrp, e2eGrp := getGroups()
+
 	fakeSession := user.NewSession(&globals.RamStorage{},
-		u, nil, nil, nil, nil)
+		u, nil, nil, nil, cmixGrp, e2eGrp)
 	fakeComm := &dummyMessaging{
 		listener: ListenCh,
 	}
-	InitBots(fakeSession, fakeComm)
+
+	topology := circuit.New([]*id.Node{id.NewNodeFromBytes(make([]byte, id.NodeIdLen))})
+
+	InitBots(fakeSession, fakeComm, topology)
 
 	// Make the reception channels buffered for this test
 	// which overwrites the channels registered in InitBots
@@ -127,8 +138,8 @@ func TestNicknameFunctions(t *testing.T) {
 			MessageType: int32(cmixproto.Type_NICKNAME_REQUEST),
 			Body:        []byte{},
 		},
-		CryptoType: format.Unencrypted,
-		Receiver:   session.GetCurrentUser().User,
+		InferredType: parse.Unencrypted,
+		Receiver:     session.GetCurrentUser().User,
 	}
 	session.GetSwitchboard().Speak(msg)
 
@@ -153,8 +164,8 @@ func TestNicknameFunctions(t *testing.T) {
 			MessageType: int32(cmixproto.Type_NICKNAME_RESPONSE),
 			Body:        []byte(session.GetCurrentUser().Nick),
 		},
-		CryptoType: format.Unencrypted,
-		Receiver:   session.GetCurrentUser().User,
+		InferredType: parse.Unencrypted,
+		Receiver:     session.GetCurrentUser().User,
 	}
 	session.GetSwitchboard().Speak(msg)
 }
@@ -163,16 +174,18 @@ type errorMessaging struct{}
 
 // SendMessage that just errors out
 func (e *errorMessaging) SendMessage(sess user.Session,
+	topology *circuit.Circuit,
 	recipientID *id.User,
-	cryptoType format.CryptoType,
+	cryptoType parse.CryptoType,
 	message []byte) error {
 	return errors.New("This is an error")
 }
 
 // SendMessage no partition that just errors out
 func (e *errorMessaging) SendMessageNoPartition(sess user.Session,
+	topology *circuit.Circuit,
 	recipientID *id.User,
-	cryptoType format.CryptoType,
+	cryptoType parse.CryptoType,
 	message []byte) error {
 	return errors.New("This is an error")
 }
@@ -191,3 +204,40 @@ func TestLookupNick_error(t *testing.T) {
 		t.Errorf("LookupNick should have returned an error")
 	}
 }
+
+func getGroups() (cmixGrp *cyclic.Group, e2eGrp *cyclic.Group) {
+
+	cmixPrime := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
+		"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
+		"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
+		"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
+		"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
+		"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
+		"83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
+		"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" +
+		"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" +
+		"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" +
+		"15728E5A8AACAA68FFFFFFFFFFFFFFFF"
+
+	e2ePrime := "E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B" +
+		"7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AE" +
+		"DF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F" +
+		"8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041" +
+		"023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF45" +
+		"3B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE9209" +
+		"6EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29" +
+		"A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E" +
+		"37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F2" +
+		"78DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696" +
+		"015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E" +
+		"6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873" +
+		"847AEF49F66E43873"
+
+	cmixGrp = cyclic.NewGroup(large.NewIntFromString(cmixPrime, 16),
+		large.NewIntFromUInt(2), large.NewIntFromUInt(2))
+
+	e2eGrp = cyclic.NewGroup(large.NewIntFromString(e2ePrime, 16),
+		large.NewIntFromUInt(2), large.NewIntFromUInt(2))
+
+	return
+}
diff --git a/cmd/root.go b/cmd/root.go
index fecac877ff26a91d884837296ca9a0c3aacc52f6..0e009b221cc504b0547cd03a20d8ba44d93a5e77 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -21,8 +21,8 @@ import (
 	"gitlab.com/elixxir/client/user"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/large"
-	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
+	"gitlab.com/elixxir/primitives/ndf"
 	"gitlab.com/elixxir/primitives/switchboard"
 	"io/ioutil"
 	"log"
@@ -41,7 +41,6 @@ var message string
 var sessionFile string
 var dummyFrequency float64
 var noBlockingTransmission bool
-var mint bool
 var rateLimiting uint32
 var showVer bool
 var gwCertPath string
@@ -49,9 +48,15 @@ var registrationCertPath string
 var registrationAddr string
 var registrationCode string
 var userEmail string
+var userNick string
 var end2end bool
 var keyParams []string
 var client *api.Client
+var ndfPath string
+var ndfVerifySignature bool
+var ndfRegistration []string
+var ndfUDB []string
+var ndfPubKey string
 
 // Execute adds all child commands to the root command and sets flags
 // appropriately.  This is called by main.main(). It only needs to
@@ -67,9 +72,29 @@ func sessionInitialization() *id.User {
 	var err error
 	register := false
 
+	// Read in the network definition file and save as string
+	ndfBytes, err := ioutil.ReadFile(ndfPath)
+	if err != nil {
+		globals.Log.FATAL.Panicf("Could not read network definition file: %v", err)
+	}
+
+	// Check if the NDF verify flag is set
+	if !ndfVerifySignature {
+		ndfPubKey = ""
+		globals.Log.WARN.Println("Skipping NDF verification")
+	} else if ndfPubKey == "" {
+		globals.Log.FATAL.Panicln("No public key for NDF found")
+	}
+
+	// Verify the signature
+	ndfJSON := api.VerifyNDF(string(ndfBytes), ndfPubKey)
+
+	// Overwrite the network definition with any specified flags
+	overwriteNDF(ndfJSON)
+
 	//If no session file is passed initialize with RAM Storage
 	if sessionFile == "" {
-		client, err = api.NewClient(&globals.RamStorage{}, "")
+		client, err = api.NewClient(&globals.RamStorage{}, "", ndfJSON)
 		if err != nil {
 			globals.Log.ERROR.Printf("Could Not Initialize Ram Storage: %s\n",
 				err.Error())
@@ -93,7 +118,7 @@ func sessionInitialization() *id.User {
 		}
 
 		//Initialize client with OS Storage
-		client, err = api.NewClient(nil, sessionFile)
+		client, err = api.NewClient(nil, sessionFile, ndfJSON)
 
 		if err != nil {
 			globals.Log.ERROR.Printf("Could Not Initialize OS Storage: %s\n", err.Error())
@@ -123,8 +148,11 @@ func sessionInitialization() *id.User {
 	}
 
 	// Connect to gateways and reg server
-	client.Connect(gateways, gwCertPath,
-		registrationAddr, registrationCertPath)
+	err = client.Connect()
+
+	if err != nil {
+		globals.Log.FATAL.Panicf("Could not call connect on client: %+v", err)
+	}
 
 	// Holds the User ID
 	var uid *id.User
@@ -148,7 +176,7 @@ func sessionInitialization() *id.User {
 
 		globals.Log.INFO.Printf("Attempting to register with code %s...", regCode)
 
-		uid, err = client.Register(userId != 0, regCode, "", mint, &grp)
+		uid, err = client.Register(userId != 0, regCode, userNick, userEmail)
 		if err != nil {
 			globals.Log.ERROR.Printf("Could Not Register User: %s\n", err.Error())
 			return id.ZeroID
@@ -166,7 +194,7 @@ func sessionInitialization() *id.User {
 
 	// Log the user in, for now using the first gateway specified
 	// This will also register the user email with UDB
-	_, err = client.Login(uid, userEmail)
+	_, err = client.Login(uid)
 	if err != nil {
 		globals.Log.ERROR.Printf("Could Not Log In: %s\n", err)
 		return id.ZeroID
@@ -272,7 +300,12 @@ func (l *ChannelListener) Hear(item switchboard.Item, isHeardElsewhere bool) {
 	message := item.(*parse.Message)
 	globals.Log.INFO.Println("Hearing a channel message")
 	result := cmixproto.ChannelMessage{}
-	proto.Unmarshal(message.Body, &result)
+	err := proto.Unmarshal(message.Body, &result)
+
+	if err != nil {
+		globals.Log.ERROR.Printf("Could not unmarhsal message, message "+
+			"not processed: %+v", err)
+	}
 
 	sender, ok := user.Users.GetUser(message.Sender)
 	var senderNick string
@@ -319,15 +352,15 @@ var rootCmd = &cobra.Command{
 		// the integration test
 		// Normal text messages
 		text := TextListener{}
-		client.Listen(id.ZeroID, format.None, int32(cmixproto.Type_TEXT_MESSAGE),
+		client.Listen(id.ZeroID, int32(cmixproto.Type_TEXT_MESSAGE),
 			&text)
 		// Channel messages
 		channel := ChannelListener{}
-		client.Listen(id.ZeroID, format.None,
+		client.Listen(id.ZeroID,
 			int32(cmixproto.Type_CHANNEL_MESSAGE), &channel)
 		// All other messages
 		fallback := FallbackListener{}
-		client.Listen(id.ZeroID, format.None, int32(cmixproto.Type_NO_TYPE),
+		client.Listen(id.ZeroID, int32(cmixproto.Type_NO_TYPE),
 			&fallback)
 
 		// Do calculation for dummy messages if the flag is set
@@ -336,9 +369,9 @@ var rootCmd = &cobra.Command{
 				(time.Duration(float64(1000000000) * (float64(1.0) / dummyFrequency)))
 		}
 
-		cryptoType := format.Unencrypted
+		cryptoType := parse.Unencrypted
 		if end2end {
-			cryptoType = format.E2E
+			cryptoType = parse.E2E
 		}
 
 		// Only send a message if we have a message to send (except dummy messages)
@@ -362,15 +395,18 @@ var rootCmd = &cobra.Command{
 					recipientNick, message)
 
 				// Send the message
-				client.Send(&parse.Message{
+				err := client.Send(&parse.Message{
 					Sender: userID,
 					TypedBody: parse.TypedBody{
 						MessageType: int32(cmixproto.Type_TEXT_MESSAGE),
 						Body:        wireOut,
 					},
-					CryptoType: cryptoType,
-					Receiver:   recipientId,
+					InferredType: cryptoType,
+					Receiver:     recipientId,
 				})
+				if err != nil {
+					globals.Log.ERROR.Printf("Error sending message: %+v", err)
+				}
 			}
 		}
 
@@ -397,9 +433,12 @@ var rootCmd = &cobra.Command{
 						MessageType: int32(cmixproto.Type_TEXT_MESSAGE),
 						Body:        api.FormatTextMessage(message),
 					},
-					CryptoType: cryptoType,
-					Receiver:   recipientId}
-				client.Send(message)
+					InferredType: cryptoType,
+					Receiver:     recipientId}
+				err := client.Send(message)
+				if err != nil {
+					globals.Log.ERROR.Printf("Error sending message: %+v", err)
+				}
 
 				timer = time.NewTimer(dummyPeriod)
 			}
@@ -439,9 +478,6 @@ func init() {
 	rootCmd.PersistentFlags().BoolVarP(&noBlockingTransmission, "noBlockingTransmission",
 		"", false, "Sets if transmitting messages blocks or not.  "+
 			"Defaults to true if unset.")
-	rootCmd.PersistentFlags().BoolVarP(&mint, "mint", "", false,
-		"Mint some coins for testing")
-
 	rootCmd.PersistentFlags().Uint32VarP(&rateLimiting, "rateLimiting", "",
 		1000, "Sets the amount of time, in ms, "+
 			"that the client waits between sending messages.  "+
@@ -472,15 +508,47 @@ func init() {
 
 	rootCmd.PersistentFlags().StringVarP(&userEmail,
 		"email", "E",
-		"",
+		"default@default.com",
 		"Email to register for User Discovery")
 
+	rootCmd.PersistentFlags().StringVar(&userNick,
+		"nick",
+		"Default",
+		"Nickname to register for User Discovery")
+
 	rootCmd.PersistentFlags().StringVarP(&sessionFile, "sessionfile", "f",
 		"", "Passes a file path for loading a session.  "+
 			"If the file doesn't exist the code will register the user and"+
 			" store it there.  If not passed the session will be stored"+
 			" to ram and lost when the cli finishes")
 
+	rootCmd.PersistentFlags().StringVarP(&ndfPubKey,
+		"ndfPubKey",
+		"p",
+		"",
+		"Path to the public key for the network definition JSON file")
+
+	rootCmd.PersistentFlags().StringVarP(&ndfPath,
+		"ndf",
+		"n",
+		"ndf.json",
+		"Path to the network definition JSON file")
+
+	rootCmd.PersistentFlags().BoolVar(&ndfVerifySignature,
+		"ndfVerifySignature",
+		true,
+		"Specifies if the NDF should be loaded without the signature")
+
+	rootCmd.PersistentFlags().StringSliceVar(&ndfRegistration,
+		"ndfRegistration",
+		nil,
+		"Overwrite the Registration values for the NDF")
+
+	rootCmd.PersistentFlags().StringSliceVar(&ndfUDB,
+		"ndfUDB",
+		nil,
+		"Overwrite the UDB values for the NDF")
+
 	// Cobra also supports local flags, which will only run
 	// when this action is called directly.
 	rootCmd.Flags().StringVarP(&message, "message", "m", "", "Message to send")
@@ -502,14 +570,7 @@ func init() {
 }
 
 // initConfig reads in config file and ENV variables if set.
-func initConfig() {
-	// Temporarily need to get group as JSON data into viper
-	json, err := globals.InitCrypto().MarshalJSON()
-	if err != nil {
-		// panic
-	}
-	viper.Set("group", string(json))
-}
+func initConfig() {}
 
 // initLog initializes logging thresholds and the log path.
 func initLog() {
@@ -535,3 +596,29 @@ func initLog() {
 		}
 	}
 }
+
+// overwriteNDF replaces fields in the NetworkDefinition structure with values
+// specified from the commandline.
+func overwriteNDF(n *ndf.NetworkDefinition) {
+	if len(ndfRegistration) == 3 {
+		n.Registration.DsaPublicKey = ndfRegistration[0]
+		n.Registration.Address = ndfRegistration[1]
+		n.Registration.TlsCertificate = ndfRegistration[2]
+
+		globals.Log.WARN.Println("Overwrote Registration values in the " +
+			"NetworkDefinition from the commandline")
+	}
+
+	if len(ndfUDB) == 2 {
+		udbIdString, err := base64.StdEncoding.DecodeString(ndfUDB[0])
+		if err != nil {
+			globals.Log.WARN.Printf("Could not decode USB ID: %v", err)
+		}
+
+		n.UDB.ID = udbIdString
+		n.UDB.DsaPublicKey = ndfUDB[1]
+
+		globals.Log.WARN.Println("Overwrote UDB values in the " +
+			"NetworkDefinition from the commandline")
+	}
+}
diff --git a/crypto/decrypt.go b/crypto/decrypt.go
index 9b5e62ca9ee48a14802ad31c08ce8324bececb93..515ca745163cb05442381c8489767c799a183395 100644
--- a/crypto/decrypt.go
+++ b/crypto/decrypt.go
@@ -21,12 +21,11 @@ import (
 func E2EDecrypt(grp *cyclic.Group, key *cyclic.Int,
 	msg *format.Message) error {
 	// First thing to do is check MAC
-	if !hash.VerifyHMAC(msg.SerializePayload(),
-		msg.GetMAC(), key.Bytes()) {
+	if !hash.VerifyHMAC(msg.Contents.Get(), msg.GetMAC(), key.Bytes()) {
 		return errors.New("HMAC verification failed for E2E message")
 	}
 	var iv [e2e.AESBlockSize]byte
-	fp := msg.GetKeyFingerprint()
+	fp := msg.GetKeyFP()
 	copy(iv[:], fp[:e2e.AESBlockSize])
 	// decrypt the timestamp in the associated data
 	decryptedTimestamp, err := e2e.DecryptAES256WithIV(
@@ -36,13 +35,15 @@ func E2EDecrypt(grp *cyclic.Group, key *cyclic.Int,
 	}
 	// TODO deserialize this somewhere along the line and provide methods
 	// to mobile developers on the bindings to interact with the timestamps
+	decryptedTimestamp = append(decryptedTimestamp, 0)
 	msg.SetTimestamp(decryptedTimestamp)
 	// Decrypt e2e
-	decryptedPayload, err := e2e.Decrypt(grp, key, msg.SerializePayload())
+	decryptedPayload, err := e2e.Decrypt(grp, key, msg.Contents.Get())
+
 	if err != nil {
 		return errors.New("Failed to decrypt E2E message: " + err.Error())
 	}
-	msg.SetSplitPayload(decryptedPayload)
+	msg.Contents.SetRightAligned(decryptedPayload)
 	return nil
 }
 
@@ -53,12 +54,11 @@ func E2EDecrypt(grp *cyclic.Group, key *cyclic.Int,
 func E2EDecryptUnsafe(grp *cyclic.Group, key *cyclic.Int,
 	msg *format.Message) error {
 	// First thing to do is check MAC
-	if !hash.VerifyHMAC(msg.SerializePayload(),
-		msg.GetMAC(), key.Bytes()) {
+	if !hash.VerifyHMAC(msg.Contents.Get(), msg.GetMAC(), key.Bytes()) {
 		return errors.New("HMAC verification failed for E2E message")
 	}
 	var iv [e2e.AESBlockSize]byte
-	fp := msg.GetKeyFingerprint()
+	fp := msg.GetKeyFP()
 	copy(iv[:], fp[:e2e.AESBlockSize])
 	// decrypt the timestamp in the associated data
 	decryptedTimestamp, err := e2e.DecryptAES256WithIV(
@@ -68,9 +68,10 @@ func E2EDecryptUnsafe(grp *cyclic.Group, key *cyclic.Int,
 	}
 	// TODO deserialize this somewhere along the line and provide methods
 	// to mobile developers on the bindings to interact with the timestamps
+	decryptedTimestamp = append(decryptedTimestamp, 0)
 	msg.SetTimestamp(decryptedTimestamp)
 	// Decrypt e2e
-	decryptedPayload := e2e.DecryptUnsafe(grp, key, msg.SerializePayload())
-	msg.SetSplitPayload(decryptedPayload)
+	decryptedPayload := e2e.DecryptUnsafe(grp, key, msg.Contents.Get())
+	msg.Contents.Set(decryptedPayload)
 	return nil
 }
diff --git a/crypto/encrypt.go b/crypto/encrypt.go
index 0324491a12ec0b13903b46df61f81f649643d93d..295209ce272a7acc1da3e21c344d1ee9730bba4d 100644
--- a/crypto/encrypt.go
+++ b/crypto/encrypt.go
@@ -13,35 +13,24 @@ import (
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/crypto/hash"
-	"gitlab.com/elixxir/crypto/verification"
+	"gitlab.com/elixxir/primitives/circuit"
 	"gitlab.com/elixxir/primitives/format"
 )
 
 // CMIX Encrypt performs the encryption
 // of the msg to a team of nodes
 // It returns a new msg
-func CMIXEncrypt(session user.Session,
-	salt []byte,
+func CMIXEncrypt(session user.Session, topology *circuit.Circuit, salt []byte,
 	msg *format.Message) *format.Message {
 	// Generate the encryption key
-	nodeKeys := session.GetKeys()
+	nodeKeys := session.GetKeys(topology)
 	baseKeys := make([]*cyclic.Int, len(nodeKeys))
 	for i, key := range nodeKeys {
 		baseKeys[i] = key.TransmissionKey
 		//TODO: Add KMAC generation here
 	}
 
-	fp := msg.GetKeyFingerprint()
-	// Calculate MIC
-	recipientMicList := [][]byte{
-		msg.GetRecipientID(),
-		fp[:],
-		msg.GetTimestamp(),
-		msg.GetMAC(),
-	}
-	mic := verification.GenerateMIC(recipientMicList, uint64(format.AD_RMIC_LEN))
-	msg.SetRecipientMIC(mic)
-	return cmix.ClientEncryptDecrypt(true, session.GetGroup(), msg, salt, baseKeys)
+	return cmix.ClientEncrypt(session.GetCmixGroup(), msg, salt, baseKeys)
 }
 
 // E2EEncrypt uses the E2E key to encrypt msg
@@ -51,24 +40,27 @@ func CMIXEncrypt(session user.Session,
 func E2EEncrypt(grp *cyclic.Group,
 	key *cyclic.Int, keyFP format.Fingerprint,
 	msg *format.Message) {
-	msg.SetKeyFingerprint(keyFP)
+	msg.SetKeyFP(keyFP)
 
 	// Encrypt the timestamp using key
 	// Timestamp bytes were previously stored
 	// and GO only uses 15 bytes, so use those
 	var iv [e2e.AESBlockSize]byte
 	copy(iv[:], keyFP[:e2e.AESBlockSize])
-	encryptedTimestamp, _ :=
+	encryptedTimestamp, err :=
 		e2e.EncryptAES256WithIV(key.Bytes(), iv,
 			msg.GetTimestamp()[:15])
+	if err != nil {
+		panic(err)
+	}
 	msg.SetTimestamp(encryptedTimestamp)
 
 	// E2E encrypt the msg
-	encPayload, err := e2e.Encrypt(grp, key, msg.GetPayload())
+	encPayload, err := e2e.Encrypt(grp, key, msg.Contents.GetRightAligned())
 	if err != nil {
 		globals.Log.ERROR.Panicf(err.Error())
 	}
-	msg.SetPayload(encPayload)
+	msg.Contents.Set(encPayload)
 
 	// MAC is HMAC(key, ciphertext)
 	// Currently, the MAC doesn't include any of the associated data
@@ -85,21 +77,24 @@ func E2EEncrypt(grp *cyclic.Group,
 func E2EEncryptUnsafe(grp *cyclic.Group,
 	key *cyclic.Int, keyFP format.Fingerprint,
 	msg *format.Message) {
-	msg.SetKeyFingerprint(keyFP)
+	msg.SetKeyFP(keyFP)
 
 	// Encrypt the timestamp using key
 	// Timestamp bytes were previously stored
 	// and GO only uses 15 bytes, so use those
 	var iv [e2e.AESBlockSize]byte
 	copy(iv[:], keyFP[:e2e.AESBlockSize])
-	encryptedTimestamp, _ :=
+	encryptedTimestamp, err :=
 		e2e.EncryptAES256WithIV(key.Bytes(), iv,
 			msg.GetTimestamp()[:15])
+	if err != nil {
+		panic(err)
+	}
 	msg.SetTimestamp(encryptedTimestamp)
 
 	// E2E encrypt the msg
-	encPayload := e2e.EncryptUnsafe(grp, key, msg.GetPayload())
-	msg.SetPayload(encPayload)
+	encPayload := e2e.EncryptUnsafe(grp, key, msg.Contents.Get())
+	msg.Contents.Set(encPayload)
 
 	// MAC is HMAC(key, ciphertext)
 	// Currently, the MAC doesn't include any of the associated data
diff --git a/crypto/encryptdecrypt_test.go b/crypto/encryptdecrypt_test.go
index 6b0bf119b37085bd916b66d893f7baf5750cb785..d0f4c511653ee92802a31eabf0a402fdd7cbab4a 100644
--- a/crypto/encryptdecrypt_test.go
+++ b/crypto/encryptdecrypt_test.go
@@ -14,69 +14,74 @@ import (
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/hash"
 	"gitlab.com/elixxir/crypto/large"
+	"gitlab.com/elixxir/primitives/circuit"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
+	"golang.org/x/crypto/blake2b"
 	"os"
 	"testing"
 	"time"
 )
 
+const numNodes = 5
+
 var salt = []byte(
 	"fdecfa52a8ad1688dbfa7d16df74ebf27e535903c469cefc007ebbe1ee895064")
 
 var session user.Session
-var serverTransmissionKey *cyclic.Int
-var serverReceptionKey *cyclic.Int
+var serverPayloadAKey *cyclic.Int
+var serverPayloadBKey *cyclic.Int
+
+var topology *circuit.Circuit
 
 func setup() {
-	base := 16
-
-	pString := "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48" +
-		"C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F" +
-		"FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5" +
-		"B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2" +
-		"35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41" +
-		"F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE" +
-		"92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15" +
-		"3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B"
-
-	gString := "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613" +
-		"D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4" +
-		"6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472" +
-		"085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5" +
-		"AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA" +
-		"3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71" +
-		"BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0" +
-		"DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7"
-
-	qString := "F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F"
-
-	p := large.NewIntFromString(pString, base)
-	g := large.NewIntFromString(gString, base)
-	q := large.NewIntFromString(qString, base)
-
-	grp := cyclic.NewGroup(p, g, q)
+
+	cmixGrp, e2eGrp := getGroups()
+
+	user.InitUserRegistry(cmixGrp)
 
 	UID := id.NewUserFromUints(&[4]uint64{0, 0, 0, 18})
 	u, _ := user.Users.GetUser(UID)
 
-	nk := make([]user.NodeKeys, 5)
+	var nodeSlice []*id.Node
+
+	//build topology
+	for i := 0; i < numNodes; i++ {
+		nodeBytes := make([]byte, id.NodeIdLen)
+		nodeBytes[0] = byte(i)
+		nodeId := id.NewNodeFromBytes(nodeBytes)
+		nodeSlice = append(nodeSlice, nodeId)
+	}
+
+	topology = circuit.New(nodeSlice)
+
+	nkMap := make(map[id.Node]user.NodeKeys)
+
+	tempKey := cmixGrp.NewInt(1)
+	serverPayloadAKey = cmixGrp.NewInt(1)
+	serverPayloadBKey = cmixGrp.NewInt(1)
+
+	h, _ := blake2b.New256(nil)
+
+	for i := 0; i < numNodes; i++ {
+
+		nk := user.NodeKeys{}
 
-	baseKey := grp.NewInt(1)
-	serverTransmissionKey = grp.NewInt(1)
-	serverReceptionKey = grp.NewInt(1)
+		h.Reset()
+		h.Write(salt)
 
-	for i := range nk {
+		nk.TransmissionKey = cmixGrp.NewInt(int64(2 + i))
+		cmix.NodeKeyGen(cmixGrp, salt, nk.TransmissionKey, tempKey)
+		cmixGrp.Mul(serverPayloadAKey, tempKey, serverPayloadAKey)
 
-		nk[i].TransmissionKey = grp.NewInt(int64(2 + i))
-		cmix.NodeKeyGen(grp, salt, nk[i].TransmissionKey, baseKey)
-		grp.Mul(serverTransmissionKey, baseKey, serverTransmissionKey)
-		nk[i].ReceptionKey = grp.NewInt(int64(1000 + i))
-		cmix.NodeKeyGen(grp, salt, nk[i].ReceptionKey, baseKey)
-		grp.Mul(serverReceptionKey, baseKey, serverReceptionKey)
+		cmix.NodeKeyGen(cmixGrp, h.Sum(nil), nk.TransmissionKey, tempKey)
+		cmixGrp.Mul(serverPayloadBKey, tempKey, serverPayloadBKey)
+
+		nkMap[*topology.GetNodeAtIndex(i)] = nk
 	}
-	session = user.NewSession(nil, u, nk,
-		nil, nil, grp)
+
+	session = user.NewSession(nil, u, nkMap,
+		nil, nil, cmixGrp, e2eGrp)
 }
 
 func TestMain(m *testing.M) {
@@ -85,130 +90,122 @@ func TestMain(m *testing.M) {
 }
 
 func TestFullEncryptDecrypt(t *testing.T) {
-	grp := session.GetGroup()
+	cmixGrp, e2eGrp := getGroups()
+
 	sender := id.NewUserFromUint(38, t)
 	recipient := id.NewUserFromUint(29, t)
 	msg := format.NewMessage()
-	msg.SetSender(sender)
 	msg.SetRecipient(recipient)
 	msgPayload := []byte("help me, i'm stuck in an" +
 		" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory")
-	msg.SetPayloadData(msgPayload)
+	// Normally, msgPayload would be the right length due to padding
+	//msgPayload = append(msgPayload, make([]byte,
+	//	format.ContentsLen-len(msgPayload)-format.PadMinLen)...)
+	msg.Contents.SetRightAligned(msgPayload)
 	now := time.Now()
 	nowBytes, _ := now.MarshalBinary()
+	// Normally, nowBytes would be the right length due to AES encryption
+	nowBytes = append(nowBytes, make([]byte, format.TimestampLen-len(nowBytes))...)
 	msg.SetTimestamp(nowBytes)
 
-	key := grp.NewInt(42)
+	key := e2eGrp.NewInt(42)
 	h, _ := hash.NewCMixHash()
 	h.Write(key.Bytes())
 	fp := format.Fingerprint{}
 	copy(fp[:], h.Sum(nil))
 
 	// E2E Encryption
-	E2EEncrypt(grp, key, fp, msg)
+	E2EEncrypt(e2eGrp, key, fp, msg)
 
 	// CMIX Encryption
-	encMsg := CMIXEncrypt(session, salt, msg)
+	encMsg := CMIXEncrypt(session, topology, salt, msg)
 
 	// Server will decrypt payload (which is OK because the payload is now e2e)
 	// This block imitates what the server does during the realtime
-	var encryptedNet *pb.Slot
-	{
-		payload := grp.NewIntFromBytes(encMsg.SerializePayload())
-		assocData := grp.NewIntFromBytes(encMsg.SerializeAssociatedData())
-		// Multiply payload and associated data by transmission key only
-		grp.Mul(payload, serverTransmissionKey, payload)
-		// Multiply associated data only by transmission key
-		grp.Mul(assocData, serverTransmissionKey, assocData)
-		encryptedNet = &pb.Slot{
-			SenderID:       sender.Bytes(),
-			Salt:           salt,
-			MessagePayload: payload.LeftpadBytes(uint64(format.TOTAL_LEN)),
-			AssociatedData: assocData.LeftpadBytes(uint64(format.TOTAL_LEN)),
-		}
-	}
+	payloadA := cmixGrp.NewIntFromBytes(encMsg.GetPayloadA())
+	payloadB := cmixGrp.NewIntFromBytes(encMsg.GetPayloadB())
+	// Multiply payloadA and associated data by serverPayloadBkey
+	cmixGrp.Mul(payloadA, serverPayloadAKey, payloadA)
+	// Multiply payloadB data only by serverPayloadAkey
+	cmixGrp.Mul(payloadB, serverPayloadBKey, payloadB)
 
 	decMsg := format.NewMessage()
-	decMsg.Payload = format.DeserializePayload(encryptedNet.MessagePayload)
-	decMsg.AssociatedData = format.DeserializeAssociatedData(encryptedNet.AssociatedData)
+	decMsg.SetPayloadA(payloadA.LeftpadBytes(uint64(format.PayloadLen)))
+	decMsg.SetDecryptedPayloadB(payloadB.LeftpadBytes(uint64(format.PayloadLen)))
 
 	// E2E Decryption
-	err := E2EDecrypt(grp, key, decMsg)
+	err := E2EDecrypt(e2eGrp, key, decMsg)
 
 	if err != nil {
 		t.Errorf("E2EDecrypt returned error: %v", err.Error())
 	}
 
-	if *decMsg.GetSender() != *sender {
-		t.Errorf("Sender differed from expected: Got %q, expected %q",
-			decMsg.GetRecipient(), sender)
-	}
 	if *decMsg.GetRecipient() != *recipient {
 		t.Errorf("Recipient differed from expected: Got %q, expected %q",
 			decMsg.GetRecipient(), sender)
 	}
-	if !bytes.Equal(decMsg.GetPayloadData(), msgPayload) {
+	if !bytes.Equal(decMsg.Contents.GetRightAligned(), msgPayload) {
 		t.Errorf("Decrypted payload differed from expected: Got %q, "+
-			"expected %q", decMsg.GetPayloadData(), msgPayload)
+			"expected %q", decMsg.Contents.Get(), msgPayload)
 	}
 }
 
 // E2E unsafe functions should only be used when the payload
 // to be sent occupies the whole payload structure, i.e. 256 bytes
 func TestFullEncryptDecrypt_Unsafe(t *testing.T) {
-	grp := session.GetGroup()
+	cmixGrp, e2eGrp := getGroups()
 	sender := id.NewUserFromUint(38, t)
 	recipient := id.NewUserFromUint(29, t)
 	msg := format.NewMessage()
 	msg.SetRecipient(recipient)
 	msgPayload := []byte(
 		" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" +
+			" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" +
+			" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" +
+			" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" +
 			" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" +
 			" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" +
 			" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory")
-	// Need to take up space of SenderID
-	msg.SetSenderID(msgPayload[:format.MP_SID_LEN])
-	msg.SetPayloadData(msgPayload[format.MP_SID_LEN:])
-	now := time.Now()
-	nowBytes, _ := now.MarshalBinary()
-	msg.SetTimestamp(nowBytes)
+	msg.Contents.Set(msgPayload[:format.ContentsLen])
+
+	msg.SetTimestamp(make([]byte, 16))
 
-	key := grp.NewInt(42)
+	key := e2eGrp.NewInt(42)
 	h, _ := hash.NewCMixHash()
 	h.Write(key.Bytes())
 	fp := format.Fingerprint{}
 	copy(fp[:], h.Sum(nil))
 
 	// E2E Encryption without padding
-	E2EEncryptUnsafe(grp, key, fp, msg)
+	E2EEncryptUnsafe(e2eGrp, key, fp, msg)
 
 	// CMIX Encryption
-	encMsg := CMIXEncrypt(session, salt, msg)
+	encMsg := CMIXEncrypt(session, topology, salt, msg)
 
 	// Server will decrypt payload (which is OK because the payload is now e2e)
 	// This block imitates what the server does during the realtime
 	var encryptedNet *pb.Slot
 	{
-		payload := grp.NewIntFromBytes(encMsg.SerializePayload())
-		assocData := grp.NewIntFromBytes(encMsg.SerializeAssociatedData())
+		payload := cmixGrp.NewIntFromBytes(encMsg.GetPayloadA())
+		assocData := cmixGrp.NewIntFromBytes(encMsg.GetPayloadB())
 		// Multiply payload and associated data by transmission key only
-		grp.Mul(payload, serverTransmissionKey, payload)
+		cmixGrp.Mul(payload, serverPayloadAKey, payload)
 		// Multiply associated data only by transmission key
-		grp.Mul(assocData, serverTransmissionKey, assocData)
+		cmixGrp.Mul(assocData, serverPayloadBKey, assocData)
 		encryptedNet = &pb.Slot{
 			SenderID:       sender.Bytes(),
 			Salt:           salt,
-			MessagePayload: payload.LeftpadBytes(uint64(format.TOTAL_LEN)),
-			AssociatedData: assocData.LeftpadBytes(uint64(format.TOTAL_LEN)),
+			MessagePayload: payload.LeftpadBytes(uint64(format.PayloadLen)),
+			AssociatedData: assocData.LeftpadBytes(uint64(format.PayloadLen)),
 		}
 	}
 
 	decMsg := format.NewMessage()
-	decMsg.AssociatedData = format.DeserializeAssociatedData(encryptedNet.AssociatedData)
-	decMsg.Payload = format.DeserializePayload(encryptedNet.MessagePayload)
+	decMsg.SetPayloadA(encryptedNet.MessagePayload)
+	decMsg.SetDecryptedPayloadB(encryptedNet.AssociatedData)
 
 	// E2E Decryption
-	err := E2EDecryptUnsafe(grp, key, decMsg)
+	err := E2EDecryptUnsafe(e2eGrp, key, decMsg)
 
 	if err != nil {
 		t.Errorf("E2EDecryptUnsafe returned error: %v", err.Error())
@@ -218,31 +215,30 @@ func TestFullEncryptDecrypt_Unsafe(t *testing.T) {
 		t.Errorf("Recipient differed from expected: Got %q, expected %q",
 			decMsg.GetRecipient(), sender)
 	}
-	if !bytes.Equal(decMsg.GetPayload(), msgPayload) {
+	if !bytes.Equal(decMsg.Contents.Get(), msgPayload[:format.ContentsLen]) {
 		t.Errorf("Decrypted payload differed from expected: Got %q, "+
-			"expected %q", decMsg.GetPayload(), msgPayload)
+			"expected %q", decMsg.Contents.Get(), msgPayload[:format.ContentsLen])
 	}
 }
 
 // Test that E2EEncrypt panics if the payload is too big (can't be padded)
 func TestE2EEncrypt_Panic(t *testing.T) {
-	grp := session.GetGroup()
-	sender := id.NewUserFromUint(38, t)
+	_, e2eGrp := getGroups()
 	recipient := id.NewUserFromUint(29, t)
 	msg := format.NewMessage()
-	msg.SetSender(sender)
 	msg.SetRecipient(recipient)
 	msgPayload := []byte("help me, i'm stuck in an" +
+		" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" +
+		" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" +
 		" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" +
 		" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" +
 		" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" +
 		" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory")
-	msg.SetPayloadData(msgPayload)
-	now := time.Now()
-	nowBytes, _ := now.MarshalBinary()
-	msg.SetTimestamp(nowBytes)
+	msgPayload = msgPayload[:format.ContentsLen]
+	msg.Contents.Set(msgPayload)
+	msg.SetTimestamp(make([]byte, 16))
 
-	key := grp.NewInt(42)
+	key := e2eGrp.NewInt(42)
 	h, _ := hash.NewCMixHash()
 	h.Write(key.Bytes())
 	fp := format.Fingerprint{}
@@ -255,43 +251,38 @@ func TestE2EEncrypt_Panic(t *testing.T) {
 	}()
 
 	// E2E Encryption Panics
-	E2EEncrypt(grp, key, fp, msg)
+	E2EEncrypt(e2eGrp, key, fp, msg)
 }
 
 // Test that E2EDecrypt and E2EDecryptUnsafe handle errors correctly
 func TestE2EDecrypt_Errors(t *testing.T) {
-	grp := session.GetGroup()
-	sender := id.NewUserFromUint(38, t)
+	_, e2eGrp := getGroups()
 	recipient := id.NewUserFromUint(29, t)
 	msg := format.NewMessage()
-	msg.SetSender(sender)
 	msg.SetRecipient(recipient)
-	msgPayload := []byte("help me, i'm stuck in an" +
-		" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory")
-	msg.SetPayloadData(msgPayload)
-	now := time.Now()
-	nowBytes, _ := now.MarshalBinary()
-	msg.SetTimestamp(nowBytes)
+	msgPayload := []byte("help me, i'm stuck in an EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory ")
+	msg.Contents.SetRightAligned(msgPayload)
+	msg.SetTimestamp(make([]byte, 16))
 
-	key := grp.NewInt(42)
+	key := e2eGrp.NewInt(42)
 	h, _ := hash.NewCMixHash()
 	h.Write(key.Bytes())
 	fp := format.Fingerprint{}
 	copy(fp[:], h.Sum(nil))
 
 	// E2E Encryption
-	E2EEncrypt(grp, key, fp, msg)
+	E2EEncrypt(e2eGrp, key, fp, msg)
 
 	// Copy message
 	badMsg := format.NewMessage()
-	badMsg.Payload = format.DeserializePayload(msg.SerializePayload())
-	badMsg.AssociatedData = format.DeserializeAssociatedData(msg.SerializeAssociatedData())
+	badMsg.SetPayloadA(msg.GetPayloadA())
+	badMsg.SetPayloadB(msg.GetPayloadB())
 
 	// Corrupt MAC to make decryption fail
-	badMsg.SetMAC([]byte("sakfaskfajskasfkkaskfanjjnaf"))
+	badMsg.SetMAC([]byte("sakfaskfajskasfkkaskfanjffffjnaf"))
 
 	// E2E Decryption returns error
-	err := E2EDecrypt(grp, key, badMsg)
+	err := E2EDecrypt(e2eGrp, key, badMsg)
 
 	if err == nil {
 		t.Errorf("E2EDecrypt should have returned error")
@@ -300,7 +291,7 @@ func TestE2EDecrypt_Errors(t *testing.T) {
 	}
 
 	// Unsafe E2E Decryption returns error
-	err = E2EDecryptUnsafe(grp, key, badMsg)
+	err = E2EDecryptUnsafe(e2eGrp, key, badMsg)
 
 	if err == nil {
 		t.Errorf("E2EDecryptUnsafe should have returned error")
@@ -315,7 +306,7 @@ func TestE2EDecrypt_Errors(t *testing.T) {
 	badMsg.SetTimestamp([]byte("ABCDEF1234567890"))
 
 	// E2E Decryption returns error
-	err = E2EDecrypt(grp, key, badMsg)
+	err = E2EDecrypt(e2eGrp, key, badMsg)
 
 	if err == nil {
 		t.Errorf("E2EDecrypt should have returned error")
@@ -324,7 +315,7 @@ func TestE2EDecrypt_Errors(t *testing.T) {
 	}
 
 	// Unsafe E2E Decryption returns error
-	err = E2EDecryptUnsafe(grp, key, badMsg)
+	err = E2EDecryptUnsafe(e2eGrp, key, badMsg)
 
 	if err == nil {
 		t.Errorf("E2EDecryptUnsafe should have returned error")
@@ -336,14 +327,15 @@ func TestE2EDecrypt_Errors(t *testing.T) {
 	badMsg.SetTimestamp(msg.GetTimestamp())
 
 	// Corrupt payload to make decryption fail
-	badMsg.SetPayload([]byte("sakomnsfjeiknheuijhgfyaistuajhfaiuojfkhufijsahufiaij"))
+	badMsg.Contents.SetRightAligned([]byte(
+		"sakomnsfjeiknheuijhgfyaistuajhfaiuojfkhufijsahufiaij"))
 
 	// Calculate new MAC to avoid failing on that verification again
-	newMAC := hash.CreateHMAC(badMsg.SerializePayload(), key.Bytes())
+	newMAC := hash.CreateHMAC(badMsg.Contents.Get(), key.Bytes())
 	badMsg.SetMAC(newMAC)
 
 	// E2E Decryption returns error
-	err = E2EDecrypt(grp, key, badMsg)
+	err = E2EDecrypt(e2eGrp, key, badMsg)
 
 	if err == nil {
 		t.Errorf("E2EDecrypt should have returned error")
@@ -351,3 +343,41 @@ func TestE2EDecrypt_Errors(t *testing.T) {
 		t.Logf("E2EDecrypt error: %v", err.Error())
 	}
 }
+
+func getGroups() (*cyclic.Group, *cyclic.Group) {
+
+	cmixGrp := cyclic.NewGroup(
+		large.NewIntFromString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"+
+			"29024E088A67CC74020BBEA63B139B22514A08798E3404DD"+
+			"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"+
+			"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"+
+			"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"+
+			"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"+
+			"83655D23DCA3AD961C62F356208552BB9ED529077096966D"+
+			"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"+
+			"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"+
+			"DE2BCBF6955817183995497CEA956AE515D2261898FA0510"+
+			"15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16),
+		large.NewIntFromString("2", 16),
+		large.NewIntFromString("2", 16))
+
+	e2eGrp := cyclic.NewGroup(
+		large.NewIntFromString("E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B"+
+			"7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AE"+
+			"DF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F"+
+			"8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041"+
+			"023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF45"+
+			"3B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE9209"+
+			"6EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29"+
+			"A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E"+
+			"37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F2"+
+			"78DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696"+
+			"015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E"+
+			"6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873"+
+			"847AEF49F66E43873", 16),
+		large.NewIntFromString("2", 16),
+		large.NewIntFromString("2", 16))
+
+	return cmixGrp, e2eGrp
+
+}
diff --git a/glide.yaml b/glide.yaml
index 049ea320ff65c1816adcb4eedd1110e692d8fd8e..7d86dcd0d2e663ace9bd0bd5f1e5ae2280a478b1 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -4,7 +4,7 @@ import:
   version: master
   vcs: git
 - package: gitlab.com/elixxir/crypto
-  version: master
+  version: updateMessageFormat
   repo: git@gitlab.com:elixxir/crypto
   vcs: git
 - package: gitlab.com/elixxir/comms
@@ -12,7 +12,7 @@ import:
   repo: git@gitlab.com:elixxir/comms
   vcs: git
 - package: gitlab.com/elixxir/primitives
-  version: master
+  version: newMessageFormat
   repo: git@gitlab.com:elixxir/primitives
   vcs: git
 - package: github.com/mitchellh/go-homedir
diff --git a/globals/group.go b/globals/group.go
deleted file mode 100644
index 490a421b3cb1adf559119192443805e5525596c5..0000000000000000000000000000000000000000
--- a/globals/group.go
+++ /dev/null
@@ -1,45 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-// Copyright © 2018 Privategrity Corporation                                   /
-//                                                                             /
-// All rights reserved.                                                        /
-////////////////////////////////////////////////////////////////////////////////
-package globals
-
-import (
-	"gitlab.com/elixxir/crypto/cyclic"
-	"gitlab.com/elixxir/crypto/large"
-)
-
-// InitCrypto sets up the cryptographic constants for cMix
-func InitCrypto() *cyclic.Group {
-
-	base := 16
-
-	pString := "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48" +
-		"C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F" +
-		"FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5" +
-		"B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2" +
-		"35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41" +
-		"F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE" +
-		"92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15" +
-		"3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B"
-
-	gString := "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613" +
-		"D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4" +
-		"6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472" +
-		"085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5" +
-		"AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA" +
-		"3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71" +
-		"BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0" +
-		"DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7"
-
-	qString := "F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F"
-
-	p := large.NewIntFromString(pString, base)
-	g := large.NewIntFromString(gString, base)
-	q := large.NewIntFromString(qString, base)
-
-	grp := cyclic.NewGroup(p, g, q)
-
-	return grp
-}
diff --git a/io/collate.go b/io/collate.go
index 99a538a23a80d85715da08527555e522aedbdd1c..86ae0a5536154a03aef58ce6d1f2a9a61e2d131c 100644
--- a/io/collate.go
+++ b/io/collate.go
@@ -12,6 +12,7 @@ import (
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/primitives/format"
+	"gitlab.com/elixxir/primitives/id"
 	"sync"
 	"time"
 )
@@ -48,8 +49,11 @@ func NewCollator() *Collator {
 func (mb *Collator) AddMessage(message *format.Message,
 	timeout time.Duration) *parse.Message {
 
-	payload := message.GetPayloadData()
-	sender := message.GetSender()
+	payload := message.Contents.GetRightAligned()
+	// There's currently no mechanism for knowing who sent an unencrypted
+	// message, I think?
+	// Let's just try ZeroID for now...
+	sender := id.ZeroID
 	recipient := message.GetRecipient()
 
 	partition, err := parse.ValidatePartition(payload)
@@ -66,10 +70,10 @@ func (mb *Collator) AddMessage(message *format.Message,
 			}
 
 			msg := parse.Message{
-				TypedBody:  *typedBody,
-				CryptoType: format.Unencrypted,
-				Sender:     sender,
-				Receiver:   recipient,
+				TypedBody:    *typedBody,
+				InferredType: parse.Unencrypted,
+				Sender:       sender,
+				Receiver:     recipient,
 			}
 
 			return &msg
@@ -131,10 +135,10 @@ func (mb *Collator) AddMessage(message *format.Message,
 				}
 
 				msg := parse.Message{
-					TypedBody:  *typedBody,
-					CryptoType: format.Unencrypted,
-					Sender:     sender,
-					Receiver:   recipient,
+					TypedBody:    *typedBody,
+					InferredType: parse.Unencrypted,
+					Sender:       sender,
+					Receiver:     recipient,
 				}
 
 				delete(mb.pendingMessages, key)
diff --git a/io/collate_test.go b/io/collate_test.go
index 34c424f07fdb7507f8767c2b2bd1adf01fef1f94..40790998f6b1587c9704ae46d75f648dbe33a264 100644
--- a/io/collate_test.go
+++ b/io/collate_test.go
@@ -22,7 +22,7 @@ func TestCollator_AddMessage(t *testing.T) {
 		pendingMessages: make(map[PendingMessageKey]*multiPartMessage),
 	}
 	var bodies [][]byte
-	for length := 5; length < 20*format.MP_PAYLOAD_LEN; length += 20 {
+	for length := 5; length < 20*format.TotalLen; length += 20 {
 		newBody := make([]byte, length)
 		_, err := rand.Read(newBody)
 		if err != nil {
@@ -40,9 +40,8 @@ func TestCollator_AddMessage(t *testing.T) {
 		for j := range partitions {
 
 			fm := format.NewMessage()
-			fm.SetSender(id.NewUserFromUint(5, t))
 			fm.SetRecipient(id.NewUserFromUint(6, t))
-			fm.SetPayloadData(partitions[j])
+			fm.Contents.SetRightAligned(partitions[j])
 
 			result = collator.AddMessage(fm, time.Minute)
 		}
@@ -54,7 +53,7 @@ func TestCollator_AddMessage(t *testing.T) {
 		// already, and strings should respect null terminators,
 		// so it's probably not actually that much of a problem.
 		if !bytes.Contains(result.Body, typedBody.Body) {
-			t.Errorf("Input didn't match output for %v. Got: %v, expected %v",
+			t.Errorf("Input didn't match output for %v. \n  Got: %v\n  Expected %v",
 				i, hex.EncodeToString(result.Body),
 				hex.EncodeToString(typedBody.Body))
 		}
@@ -66,7 +65,7 @@ func TestCollator_AddMessage_Timeout(t *testing.T) {
 		pendingMessages: make(map[PendingMessageKey]*multiPartMessage),
 	}
 	//enough for four partitions, probably
-	body := make([]byte, 3*format.MP_PAYLOAD_LEN)
+	body := make([]byte, 3*format.ContentsLen)
 	partitions, err := parse.Partition(body, []byte{88})
 	if err != nil {
 		t.Errorf("Error partitioning messages: %v", err.Error())
@@ -74,9 +73,8 @@ func TestCollator_AddMessage_Timeout(t *testing.T) {
 	var result *parse.Message
 	for i := range partitions {
 		fm := format.NewMessage()
-		fm.SetSender(id.NewUserFromUint(5, t))
 		fm.SetRecipient(id.NewUserFromUint(6, t))
-		fm.SetPayload(partitions[i])
+		fm.Contents.SetRightAligned(partitions[i])
 
 		result = collator.AddMessage(fm, 80*time.Millisecond)
 		if result != nil {
diff --git a/io/interface.go b/io/interface.go
index 043e3ceb07e15c813c75b6e41c3b99a771c73bdb..d8130f15fd2c216d4c760c6f715b9f16e465121b 100644
--- a/io/interface.go
+++ b/io/interface.go
@@ -8,8 +8,9 @@
 package io
 
 import (
+	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/primitives/format"
+	"gitlab.com/elixxir/primitives/circuit"
 	"gitlab.com/elixxir/primitives/id"
 	"time"
 )
@@ -17,12 +18,13 @@ import (
 // Communication interface implements send/receive functionality with the server
 type Communications interface {
 	// SendMessage to the server
-	SendMessage(session user.Session, recipientID *id.User,
-		cryptoType format.CryptoType, message []byte) error
+	// TODO(nen) Can we get rid of the crypto type param here?
+	SendMessage(session user.Session, topology *circuit.Circuit,
+		recipientID *id.User, cryptoType parse.CryptoType, message []byte) error
 	// SendMessage without partitions to the server
 	// This is used to send rekey messages
-	SendMessageNoPartition(session user.Session, recipientID *id.User,
-		cryptoType format.CryptoType, message []byte) error
+	SendMessageNoPartition(session user.Session, topology *circuit.Circuit,
+		recipientID *id.User, cryptoType parse.CryptoType, message []byte) error
 	// MessageReceiver thread to get new messages
 	MessageReceiver(session user.Session, delay time.Duration)
 }
diff --git a/io/messaging.go b/io/messaging.go
index 04a137678e6280472b47152157906da354730443..b68a0a6f50bbe53216080da91c9a07bef0714c6b 100644
--- a/io/messaging.go
+++ b/io/messaging.go
@@ -22,6 +22,7 @@ import (
 	"gitlab.com/elixxir/crypto/cmix"
 	"gitlab.com/elixxir/crypto/csprng"
 	"gitlab.com/elixxir/crypto/e2e"
+	"gitlab.com/elixxir/primitives/circuit"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/switchboard"
@@ -72,9 +73,9 @@ func NewMessenger() *Messaging {
 // the keys) here. I won't touch crypto at this time, though...
 // TODO This method would be cleaner if it took a parse.Message (particularly
 // w.r.t. generating message IDs for multi-part messages.)
-func (m *Messaging) SendMessage(session user.Session,
+func (m *Messaging) SendMessage(session user.Session, topology *circuit.Circuit,
 	recipientID *id.User,
-	cryptoType format.CryptoType,
+	cryptoType parse.CryptoType,
 	message []byte) error {
 	// FIXME: We should really bring the plaintext parts of the NewMessage logic
 	// into this module, then have an EncryptedMessage type that is sent to/from
@@ -85,7 +86,6 @@ func (m *Messaging) SendMessage(session user.Session,
 	// TBD: Is there a really good reason why we'd ever have more than one user
 	// in this library? why not pass a sender object instead?
 	globals.Log.DEBUG.Printf("Sending message to %q: %q", *recipientID, message)
-	userID := session.GetCurrentUser().User
 	parts, err := parse.Partition([]byte(message),
 		m.nextId())
 	if err != nil {
@@ -100,15 +100,15 @@ func (m *Messaging) SendMessage(session user.Session,
 	if err != nil {
 		return fmt.Errorf("SendMessage MarshalBinary() error: %v", err.Error())
 	}
+	extendedNowBytes := append(nowBytes, 0)
 	for i := range parts {
 		message := format.NewMessage()
-		message.SetSender(userID)
 		message.SetRecipient(recipientID)
 		// The timestamp will be encrypted later
 		// NOTE: This sets 15 bytes, not 16
-		message.SetTimestamp(nowBytes)
-		message.SetPayloadData(parts[i])
-		err = m.send(session, cryptoType, message, false)
+		message.SetTimestamp(extendedNowBytes)
+		message.Contents.SetRightAligned(parts[i])
+		err = m.send(session, topology, cryptoType, message, false)
 		if err != nil {
 			return fmt.Errorf("SendMessage send() error: %v", err.Error())
 		}
@@ -120,14 +120,12 @@ func (m *Messaging) SendMessage(session user.Session,
 // This function will be needed for example to send a Rekey
 // message, where a new public key will take up the whole message
 func (m *Messaging) SendMessageNoPartition(session user.Session,
-	recipientID *id.User,
-	cryptoType format.CryptoType,
+	topology *circuit.Circuit, recipientID *id.User, cryptoType parse.CryptoType,
 	message []byte) error {
 	size := len(message)
-	if size > format.TOTAL_LEN {
+	if size > format.TotalLen {
 		return fmt.Errorf("SendMessageNoPartition() error: message to be sent is too big")
 	}
-	userID := session.GetCurrentUser().User
 	now := time.Now()
 	// GO Timestamp binary serialization is 15 bytes, which
 	// allows the encrypted timestamp to fit in 16 bytes
@@ -141,17 +139,9 @@ func (m *Messaging) SendMessageNoPartition(session user.Session,
 	// The timestamp will be encrypted later
 	// NOTE: This sets 15 bytes, not 16
 	msg.SetTimestamp(nowBytes)
-	// If message is bigger than payload size
-	// use SenderID space to send it
-	if size > format.MP_PAYLOAD_LEN {
-		msg.SetSenderID(message[:format.MP_SID_END])
-		msg.SetPayloadData(message[format.MP_SID_END:])
-	} else {
-		msg.SetSender(userID)
-		msg.SetPayloadData(message)
-	}
+	msg.Contents.Set(message)
 	globals.Log.DEBUG.Printf("Sending message to %v: %x", *recipientID, message)
-	err = m.send(session, cryptoType, msg, true)
+	err = m.send(session, topology, cryptoType, msg, true)
 	if err != nil {
 		return fmt.Errorf("SendMessageNoPartition send() error: %v", err.Error())
 	}
@@ -159,8 +149,8 @@ func (m *Messaging) SendMessageNoPartition(session user.Session,
 }
 
 // send actually sends the message to the server
-func (m *Messaging) send(session user.Session,
-	cryptoType format.CryptoType,
+func (m *Messaging) send(session user.Session, topology *circuit.Circuit,
+	cryptoType parse.CryptoType,
 	message *format.Message,
 	rekey bool) error {
 	// Enable transmission blocking if enabled
@@ -173,25 +163,25 @@ func (m *Messaging) send(session user.Session,
 	}
 
 	// Check message type
-	if cryptoType == format.E2E {
+	if cryptoType == parse.E2E {
 		handleE2ESending(session, message, rekey)
 	} else {
-		padded, err := e2e.Pad(message.GetPayload(), format.TOTAL_LEN)
+		padded, err := e2e.Pad(message.Contents.GetRightAligned(), format.ContentsLen)
 		if err != nil {
 			return err
 		}
-		message.SetPayload(padded)
+		message.Contents.Set(padded)
 		e2e.SetUnencrypted(message)
 	}
 
 	// CMIX Encryption
 	salt := cmix.NewSalt(csprng.Source(&csprng.SystemRNG{}), 16)
-	encMsg := crypto.CMIXEncrypt(session, salt, message)
+	encMsg := crypto.CMIXEncrypt(session, topology, salt, message)
 
 	msgPacket := &pb.Slot{
 		SenderID:       session.GetCurrentUser().User.Bytes(),
-		MessagePayload: encMsg.SerializePayload(),
-		AssociatedData: encMsg.SerializeAssociatedData(),
+		MessagePayload: encMsg.GetPayloadA(),
+		AssociatedData: encMsg.GetPayloadB(),
 		Salt:           salt,
 		KMACs:          make([][]byte, 0),
 	}
@@ -242,20 +232,20 @@ func handleE2ESending(session user.Session,
 				MessageType: int32(cmixproto.Type_REKEY_TRIGGER),
 				Body:        []byte{},
 			},
-			CryptoType: format.None,
-			Receiver:   recipientID,
+			InferredType: parse.None,
+			Receiver:     recipientID,
 		}
 		go session.GetSwitchboard().Speak(rekeyMsg)
 	}
 
 	globals.Log.DEBUG.Printf("E2E encrypting message")
 	if rekey {
-		crypto.E2EEncryptUnsafe(session.GetGroup(),
+		crypto.E2EEncryptUnsafe(session.GetE2EGroup(),
 			key.GetKey(),
 			key.KeyFingerprint(),
 			message)
 	} else {
-		crypto.E2EEncrypt(session.GetGroup(),
+		crypto.E2EEncrypt(session.GetE2EGroup(),
 			key.GetKey(),
 			key.KeyFingerprint(),
 			message)
@@ -302,7 +292,7 @@ func (m *Messaging) MessageReceiver(session user.Session, delay time.Duration) {
 
 func handleE2EReceiving(session user.Session,
 	message *format.Message) (bool, error) {
-	keyFingerprint := message.GetKeyFingerprint()
+	keyFingerprint := message.GetKeyFP()
 
 	// Lookup reception key
 	recpKey := session.GetKeyStore().
@@ -312,7 +302,7 @@ func handleE2EReceiving(session user.Session,
 	if recpKey == nil {
 		// TODO Handle sending error message to SW
 		return rekey, fmt.Errorf("E2EKey for matching fingerprint not found, can't process message")
-	} else if recpKey.GetOuterType() == format.Rekey {
+	} else if recpKey.GetOuterType() == parse.Rekey {
 		// If key type is rekey, the message is a rekey from partner
 		rekey = true
 	}
@@ -320,9 +310,9 @@ func handleE2EReceiving(session user.Session,
 	globals.Log.DEBUG.Printf("E2E decrypting message")
 	var err error
 	if rekey {
-		err = crypto.E2EDecryptUnsafe(session.GetGroup(), recpKey.GetKey(), message)
+		err = crypto.E2EDecryptUnsafe(session.GetE2EGroup(), recpKey.GetKey(), message)
 	} else {
-		err = crypto.E2EDecrypt(session.GetGroup(), recpKey.GetKey(), message)
+		err = crypto.E2EDecrypt(session.GetE2EGroup(), recpKey.GetKey(), message)
 	}
 
 	if err != nil {
@@ -335,15 +325,15 @@ func handleE2EReceiving(session user.Session,
 	// Send rekey message to switchboard
 	if rekey {
 		partner := recpKey.GetManager().GetPartner()
-		partnerPubKey := message.SerializePayload()
+		partnerPubKey := message.Contents.Get()
 		rekeyMsg := &parse.Message{
 			Sender: partner,
 			TypedBody: parse.TypedBody{
 				MessageType: int32(cmixproto.Type_NO_TYPE),
 				Body:        partnerPubKey,
 			},
-			CryptoType: format.Rekey,
-			Receiver:   session.GetCurrentUser().User,
+			InferredType: parse.Rekey,
+			Receiver:     session.GetCurrentUser().User,
 		}
 		go session.GetSwitchboard().Speak(rekeyMsg)
 	}
@@ -392,10 +382,8 @@ func (m *Messaging) receiveMessagesFromGateway(session user.Session,
 					}
 
 					msg := format.NewMessage()
-					msg.AssociatedData = format.DeserializeAssociatedData(
-						newMessage.AssociatedData)
-					msg.Payload = format.DeserializePayload(newMessage.
-						MessagePayload)
+					msg.SetPayloadA(newMessage.MessagePayload)
+					msg.SetPayloadB(newMessage.AssociatedData)
 
 					var err error = nil
 					var rekey bool
@@ -405,9 +393,9 @@ func (m *Messaging) receiveMessagesFromGateway(session user.Session,
 						rekey, err = handleE2EReceiving(session, msg)
 					} else {
 						// If message is non E2E, need to unpad payload
-						unpadded, err = e2e.Unpad(msg.SerializePayload())
+						unpadded, err = e2e.Unpad(msg.Contents.Get())
 						if err == nil {
-							msg.SetSplitPayload(unpadded)
+							msg.Contents.Set(unpadded)
 						}
 					}
 
diff --git a/keyStore/e2eKey.go b/keyStore/e2eKey.go
index d45808f60b83187848180c84899fb2ec36cf84ce..3c6226477ab86bd3bc34d30786eea663abe00e29 100644
--- a/keyStore/e2eKey.go
+++ b/keyStore/e2eKey.go
@@ -1,6 +1,7 @@
 package keyStore
 
 import (
+	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/hash"
 	"gitlab.com/elixxir/primitives/format"
@@ -14,7 +15,7 @@ type E2EKey struct {
 	key *cyclic.Int
 
 	// Designation of crypto type
-	outer format.CryptoType
+	outer parse.CryptoType
 
 	// keyNum is needed by Key Manager
 	// to keep track of which receiving keys
@@ -33,7 +34,7 @@ func (e2ekey *E2EKey) GetKey() *cyclic.Int {
 }
 
 // Get key type, E2E or Rekey
-func (e2ekey *E2EKey) GetOuterType() format.CryptoType {
+func (e2ekey *E2EKey) GetOuterType() parse.CryptoType {
 	return e2ekey.outer
 }
 
diff --git a/keyStore/keyManager.go b/keyStore/keyManager.go
index e34eb4646a7230976012d623745cfa1b05124f6c..669e72d9f87304d91d759bacb052306d8eec37a9 100644
--- a/keyStore/keyManager.go
+++ b/keyStore/keyManager.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"encoding/binary"
 	"encoding/gob"
+	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/primitives/format"
@@ -88,11 +89,11 @@ func NewManager(baseKey *cyclic.Int,
 	km.ttl = ttl
 	km.numKeys = numKeys
 	km.numReKeys = numReKeys
-	for i, _ := range km.recvKeysState {
+	for i := range km.recvKeysState {
 		km.recvKeysState[i] = new(uint64)
 		*km.recvKeysState[i] = 0
 	}
-	for i, _ := range km.recvReKeysState {
+	for i := range km.recvReKeysState {
 		km.recvReKeysState[i] = new(uint64)
 		*km.recvReKeysState[i] = 0
 	}
@@ -283,7 +284,7 @@ func (km *KeyManager) GenerateKeys(grp *cyclic.Group, userID *id.User,
 			e2ekey := new(E2EKey)
 			e2ekey.key = key
 			e2ekey.manager = km
-			e2ekey.outer = format.E2E
+			e2ekey.outer = parse.E2E
 			km.sendKeys.Push(e2ekey)
 		}
 
@@ -295,7 +296,7 @@ func (km *KeyManager) GenerateKeys(grp *cyclic.Group, userID *id.User,
 			e2ekey := new(E2EKey)
 			e2ekey.key = key
 			e2ekey.manager = km
-			e2ekey.outer = format.Rekey
+			e2ekey.outer = parse.Rekey
 			km.sendReKeys.Push(e2ekey)
 		}
 		// Add KeyManager to KeyStore map
@@ -317,7 +318,7 @@ func (km *KeyManager) GenerateKeys(grp *cyclic.Group, userID *id.User,
 				e2ekey := new(E2EKey)
 				e2ekey.key = key
 				e2ekey.manager = km
-				e2ekey.outer = format.E2E
+				e2ekey.outer = parse.E2E
 				e2ekey.keyNum = uint32(i)
 				keyFP := e2ekey.KeyFingerprint()
 				km.recvKeysFingerprint = append(km.recvKeysFingerprint, keyFP)
@@ -333,7 +334,7 @@ func (km *KeyManager) GenerateKeys(grp *cyclic.Group, userID *id.User,
 				e2ekey := new(E2EKey)
 				e2ekey.key = key
 				e2ekey.manager = km
-				e2ekey.outer = format.Rekey
+				e2ekey.outer = parse.Rekey
 				e2ekey.keyNum = uint32(i)
 				keyFP := e2ekey.KeyFingerprint()
 				km.recvReKeysFingerprint = append(km.recvReKeysFingerprint, keyFP)
diff --git a/keyStore/keyStack_test.go b/keyStore/keyStack_test.go
index a61db9971da30dea9a37624a35c07d91eb77f670..8d4085f8749dd0804a0ddd3df1d21e635c84845d 100644
--- a/keyStore/keyStack_test.go
+++ b/keyStore/keyStack_test.go
@@ -1,9 +1,9 @@
 package keyStore
 
 import (
+	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/large"
-	"gitlab.com/elixxir/primitives/format"
 	"testing"
 	"time"
 )
@@ -32,7 +32,7 @@ func TestKeyStack(t *testing.T) {
 
 	for i := 0; i < 100; i++ {
 		key := new(E2EKey)
-		key.outer = format.E2E
+		key.outer = parse.E2E
 		key.key = grp.NewInt(int64(i + 2))
 		key.manager = nil
 		expectedKeys[99-i] = key
@@ -57,7 +57,7 @@ func TestKeyStack_Panic(t *testing.T) {
 
 	for i := 0; i < 10; i++ {
 		key := new(E2EKey)
-		key.outer = format.E2E
+		key.outer = parse.E2E
 		key.key = grp.NewInt(int64(i + 2))
 		key.manager = nil
 		expectedKeys[9-i] = key
@@ -88,7 +88,7 @@ func TestKeyStack_Delete(t *testing.T) {
 
 	for i := 0; i < 100; i++ {
 		key := new(E2EKey)
-		key.outer = format.E2E
+		key.outer = parse.E2E
 		key.key = grp.NewInt(int64(i + 2))
 		key.manager = nil
 		expectedKeys[99-i] = key
@@ -123,7 +123,7 @@ func TestKeyStack_Concurrent(t *testing.T) {
 
 	for i := 0; i < 100; i++ {
 		key := new(E2EKey)
-		key.outer = format.E2E
+		key.outer = parse.E2E
 		key.key = grp.NewInt(int64(i + 2))
 		key.manager = nil
 		expectedKeys[99-i] = key
diff --git a/keyStore/keyStore.go b/keyStore/keyStore.go
index 85392d0e3257ba15b4ca90ce15f85dfd1a26f392..8c92dccc6afb12c5226e7c30b8d43ba8fc3924fd 100644
--- a/keyStore/keyStore.go
+++ b/keyStore/keyStore.go
@@ -3,6 +3,7 @@ package keyStore
 import (
 	"bytes"
 	"encoding/gob"
+	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/primitives/format"
@@ -68,7 +69,7 @@ func (m *inKeyMap) Pop(fingerprint format.Fingerprint) *E2EKey {
 	m.Delete(fingerprint)
 	// Update Key Manager Receiving State
 	key.GetManager().updateRecvState(
-		key.GetOuterType() == format.Rekey,
+		key.GetOuterType() == parse.Rekey,
 		key.keyNum)
 	return key
 }
diff --git a/parse/message.go b/parse/message.go
index fd34ef7a1a3c4e950886be313c8b87b59817baa5..b19f121e0bff039b5afac1cf6b6136edf2df6ca9 100644
--- a/parse/message.go
+++ b/parse/message.go
@@ -8,7 +8,6 @@ package parse
 
 import (
 	"crypto/sha256"
-	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
 )
 
@@ -19,10 +18,26 @@ type MessageHash [MessageHashLen]byte
 
 type Message struct {
 	TypedBody
-	CryptoType format.CryptoType
-	Sender     *id.User
-	Receiver   *id.User
-	Nonce      []byte
+	// The crypto type is inferred from the message's contents
+	InferredType CryptoType
+	Sender       *id.User
+	Receiver     *id.User
+	Nonce        []byte
+}
+
+type CryptoType int32
+
+const (
+	None CryptoType = iota
+	Unencrypted
+	Rekey
+	E2E
+)
+
+var cryptoTypeStrArr = []string{"None", "Unencrypted", "Rekey", "E2E"}
+
+func (ct CryptoType) String() string {
+	return cryptoTypeStrArr[ct]
 }
 
 // Interface used to standardize message definitions
@@ -38,7 +53,7 @@ type MessageInterface interface {
 	// Return the message's inner type
 	GetMessageType() int32
 	// Returns the message's outer type
-	GetCryptoType() format.CryptoType
+	GetCryptoType() CryptoType
 	// Return the message fully serialized including the type prefix
 	// Does this really belong in the interface?
 	Pack() []byte
@@ -78,8 +93,8 @@ func (m *Message) GetMessageType() int32 {
 	return m.MessageType
 }
 
-func (m *Message) GetCryptoType() format.CryptoType {
-	return m.CryptoType
+func (m *Message) GetCryptoType() CryptoType {
+	return m.InferredType
 }
 
 func (m *Message) Pack() []byte {
diff --git a/parse/partition.go b/parse/partition.go
index 23afa2467c5cd34e2f2e8154c6572c63376fc7d1..2036f307c4f4758504ace962f27aa744f0699fe9 100644
--- a/parse/partition.go
+++ b/parse/partition.go
@@ -11,12 +11,15 @@ import (
 	"errors"
 	"fmt"
 	"gitlab.com/elixxir/client/globals"
-	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/primitives/format"
 	"math"
 	"sync"
 )
 
+func getMaxMessageLength() int {
+	return format.ContentsLen - format.PadMinLen
+}
+
 // TODO is there a better way to generate unique message IDs locally?
 func IDCounter() func() []byte {
 	// 32 bits to put a smaller upper bound on the varint size on the wire
@@ -52,7 +55,7 @@ func GetMaxIndex(body []byte, id []byte) int32 {
 	if bodyLen > 0 {
 		bodyLen--
 	}
-	maxIndex := bodyLen / (format.MP_PAYLOAD_LEN - e2e.MinPaddingLen - len(id) - IndexLength)
+	maxIndex := bodyLen / (getMaxMessageLength() - len(id) - IndexLength)
 	return int32(maxIndex)
 }
 
@@ -75,7 +78,7 @@ func Partition(body []byte, id []byte) ([][]byte, error) {
 	var lastPartitionLength int
 	partitionReadIdx := 0
 	for i := range partitions {
-		maxPartitionLength := format.MP_PAYLOAD_LEN - e2e.MinPaddingLen
+		maxPartitionLength := getMaxMessageLength()
 		partitions[i], lastPartitionLength = makePartition(maxPartitionLength,
 			body[partitionReadIdx:], id, byte(i), byte(maxIndex))
 		partitionReadIdx += lastPartitionLength
@@ -128,7 +131,8 @@ func makePartition(maxLength int, body []byte, id []byte, i byte,
 func Assemble(partitions [][]byte) ([]byte, error) {
 	// this will allocate a bit more capacity than needed but not so much that
 	// it breaks the bank
-	result := make([]byte, 0, int(format.MP_PAYLOAD_LEN)*len(partitions))
+	result := make([]byte, 0, int(format.ContentsLen-format.PadMinLen)*
+		len(partitions))
 
 	for i := range partitions {
 		result = append(result, partitions[i]...)
diff --git a/parse/partition_test.go b/parse/partition_test.go
index d6dec915fc02a1d2a5ad58292002cc6e6eb5f088..349ad3c75da2cae06d696afdce1d76a4f5f17163 100644
--- a/parse/partition_test.go
+++ b/parse/partition_test.go
@@ -8,8 +8,6 @@ package parse
 
 import (
 	"bytes"
-	"gitlab.com/elixxir/crypto/e2e"
-	"gitlab.com/elixxir/primitives/format"
 	"math/rand"
 	"testing"
 )
@@ -61,7 +59,9 @@ func TestPartitionShort(t *testing.T) {
 // in sum, contains the whole message.
 func TestPartitionLong(t *testing.T) {
 	id := []byte{0xa2, 0x54}
-	randomBytes := randomString(0, 300)
+	// This should be about the right length
+	// With the other length, the test panicked with out of bounds
+	randomBytes := randomString(0, getMaxMessageLength()*2-12)
 	actual, err := Partition(randomBytes, id)
 
 	if err != nil {
@@ -75,7 +75,7 @@ func TestPartitionLong(t *testing.T) {
 	expected[0] = append(expected[0], 0, 1)
 	// part of random string
 	expected[0] = append(expected[0],
-		randomBytes[:format.MP_PAYLOAD_LEN-4-e2e.MinPaddingLen]...)
+		randomBytes[:getMaxMessageLength()-4]...)
 
 	// id
 	expected[1] = append(expected[1], id...)
@@ -83,7 +83,7 @@ func TestPartitionLong(t *testing.T) {
 	expected[1] = append(expected[1], 1, 1)
 	// other part of random string
 	expected[1] = append(expected[1],
-		randomBytes[format.MP_PAYLOAD_LEN-4-e2e.MinPaddingLen:]...)
+		randomBytes[getMaxMessageLength()-4:]...)
 
 	for i := range actual {
 		if !bytes.Equal(actual[i], expected[i]) {
@@ -106,7 +106,7 @@ func TestPartitionLongest(t *testing.T) {
 		t.Fatalf(err.Error())
 	}
 
-	expectedNumberOfPartitions := 256
+	expectedNumberOfPartitions := 139
 
 	if len(actual) != expectedNumberOfPartitions {
 		t.Errorf("Expected a 52480-byte message to split into %v partitions, got %v instead",
@@ -116,7 +116,7 @@ func TestPartitionLongest(t *testing.T) {
 
 	// check the index and max index of the last partition
 	lastIndex := len(actual) - 1
-	expectedIdx := byte(255)
+	expectedIdx := byte(138)
 	idxLocation := len(id)
 	maxIdxLocation := len(id) + 1
 	actualIdx := actual[lastIndex][idxLocation]
@@ -135,7 +135,7 @@ func TestPartitionLongest(t *testing.T) {
 // message that's too long to partition
 func TestPartitionTooLong(t *testing.T) {
 	id := []byte{0x1f, 0x2f, 0x3f, 0x4f, 0x5f}
-	_, err := Partition(randomString(0, 57856), id)
+	_, err := Partition(randomString(0, 257856), id)
 
 	if err == nil {
 		t.Error("Partition() processed a message that was too long to be" +
@@ -155,8 +155,7 @@ func TestOnlyAssemble(t *testing.T) {
 
 	partitions := make([][]byte, len(messageChunks))
 	for i := range partitions {
-		e2e.Pad([]byte(messageChunks[i]), format.MP_PAYLOAD_LEN)
-		partitions[i] = append(partitions[i], messageChunks[i]...)
+		partitions[i] = append(partitions[i], []byte(messageChunks[i])...)
 	}
 
 	assembled, err := Assemble(partitions)
@@ -164,7 +163,7 @@ func TestOnlyAssemble(t *testing.T) {
 		t.Error(err.Error())
 	}
 	if completeMessage != string(assembled) {
-		t.Errorf("TestOnlyAssemble: got \"%v\"; expected \"%v\".",
+		t.Errorf("TestOnlyAssemble: got \"%v\";\n expected \"%v\".",
 			string(assembled), completeMessage)
 	}
 }
@@ -449,7 +448,8 @@ func TestValidatePartition(t *testing.T) {
 	for i := range validPayloads {
 		result, err := ValidatePartition(validPayloads[i])
 		if err != nil {
-			t.Errorf("Payload %v was incorrectly invalidated: %v", i, err.Error())
+			t.Fatalf("Payload %v was incorrectly invalidated: %v", i,
+				err.Error())
 		}
 		if !bytes.Equal(result.ID, expectedIDs[i]) {
 			t.Errorf("Payload %v's ID was parsed incorrectly. Got %v, "+
diff --git a/rekey/rekey.go b/rekey/rekey.go
index 7caffa0018849fae237b61999a609442eca3f624..c4e8c9636ec82b4e3209def90e602a26711ed59a 100644
--- a/rekey/rekey.go
+++ b/rekey/rekey.go
@@ -15,12 +15,14 @@ import (
 	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/crypto/hash"
 	"gitlab.com/elixxir/crypto/signature"
+	"gitlab.com/elixxir/primitives/circuit"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/switchboard"
 )
 
 var session user.Session
+var topology *circuit.Circuit
 var messaging io.Communications
 
 var rekeyTriggerList rekeyTriggerListener
@@ -52,6 +54,10 @@ func (l *rekeyListener) Hear(msg switchboard.Item, isHeardElsewhere bool) {
 	m := msg.(*parse.Message)
 	partner := m.GetSender()
 	partnerPubKey := m.GetPayload()
+	if m.GetCryptoType() != parse.Rekey {
+		globals.Log.WARN.Printf("Received message with NO_TYPE but not Rekey CryptoType, needs to be fixed!")
+		return
+	}
 	globals.Log.DEBUG.Printf("Received Rekey message from user %v", *partner)
 	err := rekeyProcess(Rekey, partner, partnerPubKey)
 	if err != nil {
@@ -81,24 +87,30 @@ func (l *rekeyConfirmListener) Hear(msg switchboard.Item, isHeardElsewhere bool)
 }
 
 // InitRekey is called internally by the Login API
-func InitRekey(s user.Session, m io.Communications) {
+func InitRekey(s user.Session, m io.Communications, t *circuit.Circuit) {
 
 	rekeyTriggerList = rekeyTriggerListener{}
 	rekeyList = rekeyListener{}
 	rekeyConfirmList = rekeyConfirmListener{}
 
 	session = s
+	topology = t
 	messaging = m
 	l := session.GetSwitchboard()
 
 	l.Register(s.GetCurrentUser().User,
-		format.None, int32(cmixproto.Type_REKEY_TRIGGER),
+		int32(cmixproto.Type_REKEY_TRIGGER),
 		&rekeyTriggerList)
+	// TODO(nen) Wouldn't it be possible to register these listeners based
+	//  solely on the inner type? maybe the switchboard can rebroadcast
+	//  messages that have a type that includes the outer type if that's not
+	//  possible
+	// in short, switchboard should be the package that includes outer
 	l.Register(id.ZeroID,
-		format.Rekey, int32(cmixproto.Type_NO_TYPE),
+		int32(cmixproto.Type_NO_TYPE),
 		&rekeyList)
 	l.Register(id.ZeroID,
-		format.None, int32(cmixproto.Type_REKEY_CONFIRM),
+		int32(cmixproto.Type_REKEY_CONFIRM),
 		&rekeyConfirmList)
 }
 
@@ -113,7 +125,7 @@ const (
 
 func rekeyProcess(rt rekeyType, partner *id.User, data []byte) error {
 	rkm := session.GetRekeyManager()
-	grp := session.GetGroup()
+	grp := session.GetCmixGroup()
 
 	// Error handling according to Rekey Message Type
 	var ctx *keyStore.RekeyContext
@@ -236,17 +248,18 @@ func rekeyProcess(rt rekeyType, partner *id.User, data []byte) error {
 		// Directly send raw publicKey bytes, without any message type
 		// This ensures that the publicKey fits in a single message, which
 		// is sent with E2E encryption using a send Rekey, and without padding
-		return messaging.SendMessageNoPartition(session, partner, format.E2E,
-			pubKey.GetKey().LeftpadBytes(uint64(format.TOTAL_LEN)))
+		return messaging.SendMessageNoPartition(session, topology, partner, parse.E2E,
+			pubKey.GetKey().LeftpadBytes(uint64(format.TotalLen)))
 	case Rekey:
 		// Send rekey confirm message with hash of the baseKey
 		h, _ := hash.NewCMixHash()
 		h.Write(ctx.BaseKey.Bytes())
+		baseKeyHash := h.Sum(nil)
 		msg := parse.Pack(&parse.TypedBody{
 			MessageType: int32(cmixproto.Type_REKEY_CONFIRM),
-			Body:        h.Sum(nil),
+			Body:        baseKeyHash,
 		})
-		return messaging.SendMessage(session, partner, format.None, msg)
+		return messaging.SendMessage(session, topology, partner, parse.None, msg)
 	}
 	return nil
 }
diff --git a/rekey/rekey_test.go b/rekey/rekey_test.go
index 6dacf9d2c37918fbf3c9836209cc6e298ed15044..3e770747176348930859f02bdbaff44f5921dd11 100644
--- a/rekey/rekey_test.go
+++ b/rekey/rekey_test.go
@@ -8,11 +8,13 @@ import (
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
 	"gitlab.com/elixxir/crypto/csprng"
+	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/diffieHellman"
 	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/crypto/hash"
+	"gitlab.com/elixxir/crypto/large"
 	"gitlab.com/elixxir/crypto/signature"
-	"gitlab.com/elixxir/primitives/format"
+	"gitlab.com/elixxir/primitives/circuit"
 	"gitlab.com/elixxir/primitives/id"
 	"os"
 	"testing"
@@ -27,8 +29,9 @@ type dummyMessaging struct {
 
 // SendMessage to the server
 func (d *dummyMessaging) SendMessage(sess user.Session,
+	topology *circuit.Circuit,
 	recipientID *id.User,
-	cryptoType format.CryptoType,
+	cryptoType parse.CryptoType,
 	message []byte) error {
 	d.listener <- message
 	return nil
@@ -36,8 +39,9 @@ func (d *dummyMessaging) SendMessage(sess user.Session,
 
 // SendMessage without partitions to the server
 func (d *dummyMessaging) SendMessageNoPartition(sess user.Session,
+	topology *circuit.Circuit,
 	recipientID *id.User,
-	cryptoType format.CryptoType,
+	cryptoType parse.CryptoType,
 	message []byte) error {
 	d.listener <- message
 	return nil
@@ -49,11 +53,13 @@ func (d *dummyMessaging) MessageReceiver(session user.Session,
 }
 
 func TestMain(m *testing.M) {
-	grp := globals.InitCrypto()
+
+	grp, e2eGrp := getGroups()
+	user.InitUserRegistry(grp)
 	params := signature.CustomDSAParams(
 		grp.GetP(),
-		grp.GetG(),
-		grp.GetQ())
+		grp.GetQ(),
+		grp.GetG())
 	rng := csprng.NewSystemRNG()
 	u := &user.User{
 		User: id.NewUserFromUints(&[4]uint64{0, 0, 0, 18}),
@@ -68,12 +74,12 @@ func TestMain(m *testing.M) {
 	partnerPubKeyCyclic := grp.NewIntFromLargeInt(partnerPubKey.GetKey())
 
 	session := user.NewSession(&globals.RamStorage{},
-		u, nil, myPubKey, myPrivKey, grp)
+		u, nil, myPubKey, myPrivKey, grp, e2eGrp)
 	ListenCh = make(chan []byte, 100)
 	fakeComm := &dummyMessaging{
 		listener: ListenCh,
 	}
-	InitRekey(session, fakeComm)
+	InitRekey(session, fakeComm, circuit.New([]*id.Node{id.NewNodeFromBytes(make([]byte, id.NodeIdLen))}))
 
 	// Create E2E relationship with partner
 	// Generate baseKey
@@ -125,8 +131,8 @@ func TestRekeyTrigger(t *testing.T) {
 			MessageType: int32(cmixproto.Type_REKEY_TRIGGER),
 			Body:        partnerPubKey.Bytes(),
 		},
-		CryptoType: format.None,
-		Receiver:   partnerID,
+		InferredType: parse.None,
+		Receiver:     partnerID,
 	}
 	session.GetSwitchboard().Speak(msg)
 
@@ -137,7 +143,7 @@ func TestRekeyTrigger(t *testing.T) {
 	// Get new PubKey from Rekey message and confirm value matches
 	// with PubKey created from privKey in Rekey Context
 	value := <-ListenCh
-	grp := session.GetGroup()
+	grp := session.GetCmixGroup()
 	actualPubKey := grp.NewIntFromBytes(value)
 	privKey := session.GetRekeyManager().GetCtx(partnerID).PrivKey
 	expectedPubKey := grp.NewInt(1)
@@ -156,8 +162,8 @@ func TestRekeyTrigger(t *testing.T) {
 			MessageType: int32(cmixproto.Type_REKEY_TRIGGER),
 			Body:        partnerPubKey.Bytes(),
 		},
-		CryptoType: format.None,
-		Receiver:   partnerID,
+		InferredType: parse.None,
+		Receiver:     partnerID,
 	}
 	session.GetSwitchboard().Speak(msg)
 
@@ -179,8 +185,8 @@ func TestRekeyConfirm(t *testing.T) {
 			MessageType: int32(cmixproto.Type_REKEY_CONFIRM),
 			Body:        baseKey.Bytes(),
 		},
-		CryptoType: format.None,
-		Receiver:   session.GetCurrentUser().User,
+		InferredType: parse.None,
+		Receiver:     session.GetCurrentUser().User,
 	}
 	session.GetSwitchboard().Speak(msg)
 
@@ -198,8 +204,8 @@ func TestRekeyConfirm(t *testing.T) {
 			MessageType: int32(cmixproto.Type_REKEY_CONFIRM),
 			Body:        h.Sum(nil),
 		},
-		CryptoType: format.None,
-		Receiver:   session.GetCurrentUser().User,
+		InferredType: parse.None,
+		Receiver:     session.GetCurrentUser().User,
 	}
 	session.GetSwitchboard().Speak(msg)
 
@@ -224,8 +230,8 @@ func TestRekeyConfirm(t *testing.T) {
 			MessageType: int32(cmixproto.Type_REKEY_CONFIRM),
 			Body:        h.Sum(nil),
 		},
-		CryptoType: format.None,
-		Receiver:   session.GetCurrentUser().User,
+		InferredType: parse.None,
+		Receiver:     session.GetCurrentUser().User,
 	}
 	session.GetSwitchboard().Speak(msg)
 
@@ -240,11 +246,11 @@ func TestRekey(t *testing.T) {
 	partnerID := id.NewUserFromUints(&[4]uint64{0, 0, 0, 12})
 	km := session.GetKeyStore().GetSendManager(partnerID)
 	// Generate new partner public key
-	grp := globals.InitCrypto()
+	grp, _ := getGroups()
 	params := signature.CustomDSAParams(
 		grp.GetP(),
-		grp.GetG(),
-		grp.GetQ())
+		grp.GetQ(),
+		grp.GetG())
 	rng := csprng.NewSystemRNG()
 	partnerPrivKey := params.PrivateKeyGen(rng)
 	partnerPubKey := partnerPrivKey.PublicKeyGen()
@@ -255,8 +261,8 @@ func TestRekey(t *testing.T) {
 			MessageType: int32(cmixproto.Type_NO_TYPE),
 			Body:        partnerPubKey.GetKey().Bytes(),
 		},
-		CryptoType: format.Rekey,
-		Receiver:   session.GetCurrentUser().User,
+		InferredType: parse.Rekey,
+		Receiver:     session.GetCurrentUser().User,
 	}
 	session.GetSwitchboard().Speak(msg)
 
@@ -309,8 +315,8 @@ func TestRekey_Errors(t *testing.T) {
 			MessageType: int32(cmixproto.Type_REKEY_TRIGGER),
 			Body:        partnerPubKey.Bytes(),
 		},
-		CryptoType: format.None,
-		Receiver:   partnerID,
+		InferredType: parse.None,
+		Receiver:     partnerID,
 	}
 	session.GetSwitchboard().Speak(msg)
 
@@ -326,8 +332,8 @@ func TestRekey_Errors(t *testing.T) {
 			MessageType: int32(cmixproto.Type_NO_TYPE),
 			Body:        []byte{},
 		},
-		CryptoType: format.Rekey,
-		Receiver:   session.GetCurrentUser().User,
+		InferredType: parse.Rekey,
+		Receiver:     session.GetCurrentUser().User,
 	}
 	session.GetSwitchboard().Speak(msg)
 
@@ -336,3 +342,45 @@ func TestRekey_Errors(t *testing.T) {
 		t.Errorf("Rekey should have returned error")
 	}
 }
+
+func getGroups() (*cyclic.Group, *cyclic.Group) {
+
+	cmixGrp := cyclic.NewGroup(
+		large.NewIntFromString("9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48"+
+			"C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F"+
+			"FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5"+
+			"B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2"+
+			"35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41"+
+			"F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE"+
+			"92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15"+
+			"3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B", 16),
+		large.NewIntFromString("5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613"+
+			"D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4"+
+			"6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472"+
+			"085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5"+
+			"AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA"+
+			"3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71"+
+			"BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0"+
+			"DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7", 16),
+		large.NewIntFromString("F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F", 16))
+
+	e2eGrp := cyclic.NewGroup(
+		large.NewIntFromString("E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B"+
+			"7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AE"+
+			"DF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F"+
+			"8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041"+
+			"023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF45"+
+			"3B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE9209"+
+			"6EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29"+
+			"A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E"+
+			"37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F2"+
+			"78DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696"+
+			"015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E"+
+			"6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873"+
+			"847AEF49F66E43873", 16),
+		large.NewIntFromString("2", 16),
+		large.NewIntFromString("2", 16))
+
+	return cmixGrp, e2eGrp
+
+}
diff --git a/user/regPrime.go b/user/regPrime.go
new file mode 100644
index 0000000000000000000000000000000000000000..30278c7e99857f64377c5c4036146a383987173a
--- /dev/null
+++ b/user/regPrime.go
@@ -0,0 +1,61 @@
+package user
+
+var pString = "F0032FB15A40F9397B63481527D1" +
+	"80BAF55F8562824EF02139016E6335" +
+	"ABC78784A6CB263B0CDB0FA4763485" +
+	"0E9A500EE529F68550D57F70C22BF4" +
+	"D6735D764376A535AB7DF31B06B372" +
+	"C193090478E64387AA712A11D2BE51" +
+	"3189AC7E2B2CA392D29087FE5922D8" +
+	"F196765DA84CCE827E47126865FA5F" +
+	"A37FC019598889C0FD93DB1B23549C" +
+	"EDA7E01F250827F405638DADFDCED2" +
+	"3C918DB4C2BA43CC7F9310181B7CCB" +
+	"865C0180437705F3B20DC20641B56D" +
+	"96C4EB66B312EFCCD38E1B8E25AD44" +
+	"21B1CC94C3E885FC2DEB238BB377CF" +
+	"041797D83166EBEA2445C7E71C7111" +
+	"246340B4A97036108A76FD666A6DC9" +
+	"7C834256D9C03505C6EC4F39D25F38" +
+	"5B69D564EC49AD6C7B26DDCA4ED919" +
+	"DC3B1DA56E6F092F72AD6E8C13F278" +
+	"71026D09651939A5824EB2AA7E7D1C" +
+	"DFA833FBE79E77CE5CBB131A95CD62" +
+	"6B68E7F885533E101BD5738FE6B2B1" +
+	"E14350E986CDDB70C74591618DEA31" +
+	"77151E6ACEC74F7FB44A4DE0453C3C" +
+	"F23C307C3D47E61DD3BDD6FDE041EB" +
+	"77D80C2F957CB5CA93BE246AC79E4F" +
+	"498F2420766452BA1256F5"
+
+var qString = "8ACA238243784F3571ED3FC1A259" +
+	"58981ADA4F187A61AA0543C2AC6230" +
+	"A56267"
+
+var gString = "BB3428DCAB4CF726195626EB6B04" +
+	"26FC7BB310C43A3C959AF4D30898B6" +
+	"F60A2649DC4DF17BA9EE1A503B2DD1" +
+	"373A644648946305EB924458E62140" +
+	"98A59A71AB43BE31F2E8B51B039EF6" +
+	"6B8B94BC8D60B788A738CC77220546" +
+	"B8C68FA345CAA946183E3D8D139C3C" +
+	"8F62CC2DE7B022B51022A2301780E8" +
+	"A869809F25EECB1FCB41583B68E5A9" +
+	"D019CCE5279708F65C9222FFAB88A3" +
+	"16A8BECDB80AB6C7EB44D8E9596BCA" +
+	"1DBCA9D5B57240C9EAD6C6BBBE09D1" +
+	"025D017B257F3B6B9DDB4155F2A93B" +
+	"52CCEA17158295F1AB8B3A2BEF18D6" +
+	"B73802C9F030054B4628CA1FF6A8BC" +
+	"66F5CD19772039B231FE0B4508569A" +
+	"ABF34367ED033E1B3CC523AB0DD0E2" +
+	"EAB189596DFD31EEF0A51AF7792321" +
+	"FDDAF0722B1970388F958069DF4646" +
+	"AA45C28CF2AB981E8FA8F6202A54E4" +
+	"21A4D2DF742EAC1479A558A09ED8E4" +
+	"6DB76B85132DA0E648548E86440A4F" +
+	"5C1DBB175B1003B1EE3119E443590B" +
+	"1FD3EE96E132D545CB08C9E7598BB6" +
+	"9A4F65B3CF985DBB1F97C294C6096C" +
+	"3AF318A3CE836222253C3AB72EC253" +
+	"5886C7F84E8828DCE79095"
diff --git a/user/session.go b/user/session.go
index 7815f30aff44108d986c9cfa73f6d8b26d0b5548..33affb1523c01482278c5205f3c932b1fe889084 100644
--- a/user/session.go
+++ b/user/session.go
@@ -16,6 +16,7 @@ import (
 	"gitlab.com/elixxir/client/keyStore"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/signature"
+	"gitlab.com/elixxir/primitives/circuit"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/switchboard"
 	"math/rand"
@@ -29,10 +30,11 @@ var ErrQuery = errors.New("element not in map")
 // Interface for User Session operations
 type Session interface {
 	GetCurrentUser() (currentUser *User)
-	GetKeys() []NodeKeys
+	GetKeys(topology *circuit.Circuit) []NodeKeys
 	GetPrivateKey() *signature.DSAPrivateKey
 	GetPublicKey() *signature.DSAPublicKey
-	GetGroup() *cyclic.Group
+	GetCmixGroup() *cyclic.Group
+	GetE2EGroup() *cyclic.Group
 	GetLastMessageID() string
 	SetLastMessageID(id string)
 	StoreSession() error
@@ -56,10 +58,10 @@ type NodeKeys struct {
 
 // Creates a new Session interface for registration
 func NewSession(store globals.Storage,
-	u *User, nk []NodeKeys,
+	u *User, nk map[id.Node]NodeKeys,
 	publicKey *signature.DSAPublicKey,
 	privateKey *signature.DSAPrivateKey,
-	grp *cyclic.Group) Session {
+	cmixGrp, e2eGrp *cyclic.Group) Session {
 
 	// With an underlying Session data structure
 	return Session(&SessionObj{
@@ -67,7 +69,8 @@ func NewSession(store globals.Storage,
 		Keys:                nk,
 		PrivateKey:          privateKey,
 		PublicKey:           publicKey,
-		Grp:                 grp,
+		CmixGrp:             cmixGrp,
+		E2EGrp:              e2eGrp,
 		InterfaceMap:        make(map[string]interface{}),
 		KeyMaps:             keyStore.NewStore(),
 		RekeyManager:        keyStore.NewRekeyManager(),
@@ -127,7 +130,7 @@ func LoadSession(store globals.Storage,
 	}
 
 	// Reconstruct Key maps
-	session.KeyMaps.ReconstructKeys(session.Grp, UID)
+	session.KeyMaps.ReconstructKeys(session.E2EGrp, UID)
 
 	// Create switchboard
 	session.listeners = switchboard.NewSwitchboard()
@@ -144,10 +147,11 @@ type SessionObj struct {
 	// Currently authenticated user
 	CurrentUser *User
 
-	Keys       []NodeKeys
+	Keys       map[id.Node]NodeKeys
 	PrivateKey *signature.DSAPrivateKey
 	PublicKey  *signature.DSAPublicKey
-	Grp        *cyclic.Group
+	CmixGrp    *cyclic.Group
+	E2EGrp     *cyclic.Group
 
 	// Last received message ID. Check messages after this on the gateway.
 	LastMessageID string
@@ -186,10 +190,17 @@ func (s *SessionObj) SetLastMessageID(id string) {
 	s.UnlockStorage()
 }
 
-func (s *SessionObj) GetKeys() []NodeKeys {
+func (s *SessionObj) GetKeys(topology *circuit.Circuit) []NodeKeys {
 	s.LockStorage()
 	defer s.UnlockStorage()
-	return s.Keys
+
+	keys := make([]NodeKeys, topology.Len())
+
+	for i := 0; i < topology.Len(); i++ {
+		keys[i] = s.Keys[*topology.GetNodeAtIndex(i)]
+	}
+
+	return keys
 }
 
 func (s *SessionObj) GetPrivateKey() *signature.DSAPrivateKey {
@@ -204,10 +215,16 @@ func (s *SessionObj) GetPublicKey() *signature.DSAPublicKey {
 	return s.PublicKey
 }
 
-func (s *SessionObj) GetGroup() *cyclic.Group {
+func (s *SessionObj) GetCmixGroup() *cyclic.Group {
+	s.LockStorage()
+	defer s.UnlockStorage()
+	return s.CmixGrp
+}
+
+func (s *SessionObj) GetE2EGroup() *cyclic.Group {
 	s.LockStorage()
 	defer s.UnlockStorage()
-	return s.Grp
+	return s.E2EGrp
 }
 
 // Return a copy of the current user
diff --git a/user/session_test.go b/user/session_test.go
index a79a198748cef7627cc5d4b9e7add42140ea683e..a54f6beb2e59caa674ed7b792a3fcd980966adce 100644
--- a/user/session_test.go
+++ b/user/session_test.go
@@ -12,6 +12,7 @@ import (
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/large"
 	"gitlab.com/elixxir/crypto/signature"
+	"gitlab.com/elixxir/primitives/circuit"
 	"gitlab.com/elixxir/primitives/id"
 	"math/rand"
 	"reflect"
@@ -35,12 +36,17 @@ func TestUserSession(t *testing.T) {
 
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2), large.NewInt(5))
 
-	keys := make([]NodeKeys, 1)
-	keys[0] = NodeKeys{
+	keys := make(map[id.Node]NodeKeys)
+
+	nodeID := id.NewNodeFromUInt(1, t)
+
+	keys[*nodeID] = NodeKeys{
 		TransmissionKey: grp.NewInt(2),
 		ReceptionKey:    grp.NewInt(2),
 	}
 
+	topology := circuit.New([]*id.Node{nodeID})
+
 	// Storage
 	storage := &globals.RamStorage{}
 
@@ -48,8 +54,11 @@ func TestUserSession(t *testing.T) {
 	params := signature.NewDSAParams(rng, signature.L1024N160)
 	privateKey := params.PrivateKeyGen(rng)
 	publicKey := privateKey.PublicKeyGen()
+
+	cmixGrp, e2eGrp := getGroups()
+
 	ses := NewSession(storage,
-		u, keys, publicKey, privateKey, grp)
+		u, keys, publicKey, privateKey, cmixGrp, e2eGrp)
 
 	ses.SetLastMessageID("totally unique ID")
 
@@ -101,22 +110,22 @@ func TestUserSession(t *testing.T) {
 		pass++
 	}
 
-	if ses.GetKeys() == nil {
+	if ses.GetKeys(topology) == nil {
 		t.Errorf("Error: Keys not set correctly!")
 	} else {
 
-		test += len(ses.GetKeys())
+		test += len(ses.GetKeys(topology))
 
-		for i := 0; i < len(ses.GetKeys()); i++ {
+		for i := 0; i < len(ses.GetKeys(topology)); i++ {
 
 			if !reflect.DeepEqual(*ses.GetPublicKey(), *publicKey) {
 				t.Errorf("Error: Public key not set correctly!")
 			} else if !reflect.DeepEqual(*ses.GetPrivateKey(), *privateKey) {
 				t.Errorf("Error: Private key not set correctly!")
-			} else if ses.GetKeys()[i].ReceptionKey.Cmp(grp.
+			} else if ses.GetKeys(topology)[i].ReceptionKey.Cmp(grp.
 				NewInt(2)) != 0 {
 				t.Errorf("Error: Reception key not set correctly!")
-			} else if ses.GetKeys()[i].TransmissionKey.Cmp(grp.
+			} else if ses.GetKeys(topology)[i].TransmissionKey.Cmp(grp.
 				NewInt(2)) != 0 {
 				t.Errorf("Error: Transmission key not set correctly!")
 			}
@@ -195,8 +204,9 @@ func TestGetPubKey(t *testing.T) {
 
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2), large.NewInt(5))
 
-	keys := make([]NodeKeys, 1)
-	keys[0] = NodeKeys{
+	keys := make(map[id.Node]NodeKeys)
+
+	keys[*id.NewNodeFromUInt(1, t)] = NodeKeys{
 		TransmissionKey: grp.NewInt(2),
 		ReceptionKey:    grp.NewInt(2),
 	}
@@ -205,7 +215,10 @@ func TestGetPubKey(t *testing.T) {
 	params := signature.NewDSAParams(rng, signature.L1024N160)
 	privateKey := params.PrivateKeyGen(rng)
 	publicKey := privateKey.PublicKeyGen()
-	ses := NewSession(nil, u, keys, publicKey, privateKey, grp)
+
+	cmixGrp, e2eGrp := getGroups()
+
+	ses := NewSession(nil, u, keys, publicKey, privateKey, cmixGrp, e2eGrp)
 
 	pubKey := ses.GetPublicKey()
 	if !reflect.DeepEqual(pubKey, publicKey) {
@@ -222,8 +235,9 @@ func TestGetPrivKey(t *testing.T) {
 
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2), large.NewInt(5))
 
-	keys := make([]NodeKeys, 1)
-	keys[0] = NodeKeys{
+	keys := make(map[id.Node]NodeKeys)
+
+	keys[*id.NewNodeFromUInt(1, t)] = NodeKeys{
 		TransmissionKey: grp.NewInt(2),
 		ReceptionKey:    grp.NewInt(2),
 	}
@@ -232,7 +246,10 @@ func TestGetPrivKey(t *testing.T) {
 	params := signature.NewDSAParams(rng, signature.L1024N160)
 	privateKey := params.PrivateKeyGen(rng)
 	publicKey := privateKey.PublicKeyGen()
-	ses := NewSession(nil, u, keys, publicKey, privateKey, grp)
+
+	cmixGrp, e2eGrp := getGroups()
+
+	ses := NewSession(nil, u, keys, publicKey, privateKey, cmixGrp, e2eGrp)
 
 	privKey := ses.GetPrivateKey()
 	if !reflect.DeepEqual(*privKey, *privateKey) {
@@ -259,4 +276,42 @@ func TestBruntString(t *testing.T) {
 	if preBurnPointer != postBurnPointer {
 		t.Errorf("Pointer values are not the same")
 	}
-}
\ No newline at end of file
+}
+
+func getGroups() (*cyclic.Group, *cyclic.Group) {
+
+	cmixGrp := cyclic.NewGroup(
+		large.NewIntFromString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"+
+			"29024E088A67CC74020BBEA63B139B22514A08798E3404DD"+
+			"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"+
+			"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"+
+			"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"+
+			"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"+
+			"83655D23DCA3AD961C62F356208552BB9ED529077096966D"+
+			"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"+
+			"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"+
+			"DE2BCBF6955817183995497CEA956AE515D2261898FA0510"+
+			"15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16),
+		large.NewIntFromString("2", 16),
+		large.NewIntFromString("2", 16))
+
+	e2eGrp := cyclic.NewGroup(
+		large.NewIntFromString("E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B"+
+			"7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AE"+
+			"DF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F"+
+			"8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041"+
+			"023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF45"+
+			"3B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE9209"+
+			"6EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29"+
+			"A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E"+
+			"37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F2"+
+			"78DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696"+
+			"015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E"+
+			"6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873"+
+			"847AEF49F66E43873", 16),
+		large.NewIntFromString("2", 16),
+		large.NewIntFromString("2", 16))
+
+	return cmixGrp, e2eGrp
+
+}
diff --git a/user/user.go b/user/user.go
index 9b4da05e6fb569a61b0d6fa4d15b7ab545cd58d7..c48b94f5097e9480c9656906eb2b5850c64a2cb8 100644
--- a/user/user.go
+++ b/user/user.go
@@ -9,13 +9,14 @@ package user
 import (
 	"crypto/sha256"
 	"gitlab.com/elixxir/client/globals"
+	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/primitives/id"
 )
 
 // Globally instantiated Registry
-var Users = newRegistry()
+var Users Registry
 
-const NUM_DEMO_USERS = 40
+const NumDemoUsers = 40
 
 var DemoUserNicks = []string{"David", "Payments", "UDB", "Jim", "Ben", "Steph",
 	"Rick", "Jake", "Spencer", "Stephanie", "Mario", "Jono", "Amanda",
@@ -24,6 +25,10 @@ var DemoUserNicks = []string{"David", "Payments", "UDB", "Jim", "Ben", "Steph",
 var DemoChannelNames = []string{"#General", "#Engineering", "#Lunch",
 	"#Random"}
 
+func InitUserRegistry(grp *cyclic.Group) {
+	Users = newRegistry(grp)
+}
+
 // Interface for User Registry operations
 type Registry interface {
 	NewUser(id *id.User, nickname string) *User
@@ -48,7 +53,7 @@ type UserMap struct {
 }
 
 // newRegistry creates a new Registry interface
-func newRegistry() Registry {
+func newRegistry(grp *cyclic.Group) Registry {
 	if len(DemoChannelNames) > 10 || len(DemoUserNicks) > 30 {
 		globals.Log.ERROR.Print("Not enough demo users have been hardcoded.")
 	}
@@ -56,12 +61,9 @@ func newRegistry() Registry {
 	ul := make(map[string]*id.User)
 	nk := make(map[id.User]*NodeKeys)
 
-	// Initialize group object
-	grp := globals.InitCrypto()
-
-	// Deterministically create NUM_DEMO_USERS users
+	// Deterministically create NumDemoUsers users
 	// TODO Replace this with real user registration/discovery
-	for i := uint64(1); i <= NUM_DEMO_USERS; i++ {
+	for i := uint64(1); i <= NumDemoUsers; i++ {
 		currentID := id.NewUserFromUints(&[4]uint64{0, 0, 0, i})
 		t := new(User)
 		k := new(NodeKeys)
@@ -102,15 +104,16 @@ func newRegistry() Registry {
 
 	// With an underlying UserMap data structure
 	return Registry(&UserMap{userCollection: uc,
-		idCounter:  uint64(NUM_DEMO_USERS),
+		idCounter:  uint64(NumDemoUsers),
 		userLookup: ul,
 		keysLookup: nk})
 }
 
 // Struct representing a User in the system
 type User struct {
-	User *id.User
-	Nick string
+	User  *id.User
+	Nick  string
+	Email string
 }
 
 // DeepCopy performs a deep copy of a user and returns a pointer to the new copy
diff --git a/user/user_test.go b/user/user_test.go
index 2bd573b94b30717656bdc31a4a7da105d64aad10..4de0d3cbf2049b3dcae955276554dd982d1027b1 100644
--- a/user/user_test.go
+++ b/user/user_test.go
@@ -14,45 +14,12 @@ import (
 	"testing"
 )
 
-// InitGroup sets up the cryptographic constants for cMix
-func InitGroup() *cyclic.Group {
-
-	base := 16
-
-	pString := "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48" +
-		"C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F" +
-		"FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5" +
-		"B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2" +
-		"35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41" +
-		"F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE" +
-		"92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15" +
-		"3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B"
-
-	gString := "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613" +
-		"D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4" +
-		"6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472" +
-		"085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5" +
-		"AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA" +
-		"3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71" +
-		"BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0" +
-		"DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7"
-
-	qString := "F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F"
-
-	p := large.NewIntFromString(pString, base)
-	g := large.NewIntFromString(gString, base)
-	q := large.NewIntFromString(qString, base)
-
-	grp := cyclic.NewGroup(p, g, q)
-
-	return grp
-}
-
 // TestUserRegistry tests the constructors/getters/setters
 // surrounding the User struct and the Registry interface
 func TestUserRegistry(t *testing.T) {
+	InitUserRegistry(InitGroup())
 	// Test if CountUsers correctly counts the hard-coded demo users
-	if Users.CountUsers() != NUM_DEMO_USERS {
+	if Users.CountUsers() != NumDemoUsers {
 		t.Errorf("CountUsers: Start size of userRegistry not zero!")
 	}
 	// Test the integration of the LookupUser, UserHash and GetUser functions
@@ -138,8 +105,42 @@ func TestUserRegistry(t *testing.T) {
 // Doesn't actually do any testing, but can print the registration codes for
 // the first several users
 func TestPrintRegCodes(t *testing.T) {
-	for i := 1; i <= NUM_DEMO_USERS; i++ {
+	for i := 1; i <= NumDemoUsers; i++ {
 		currentID := id.NewUserFromUint(uint64(i), t)
 		t.Logf("%v:\t%v", i, currentID.RegistrationCode())
 	}
 }
+
+// InitGroup sets up the cryptographic constants for cMix
+func InitGroup() *cyclic.Group {
+
+	base := 16
+
+	pString := "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48" +
+		"C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F" +
+		"FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5" +
+		"B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2" +
+		"35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41" +
+		"F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE" +
+		"92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15" +
+		"3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B"
+
+	gString := "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613" +
+		"D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4" +
+		"6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472" +
+		"085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5" +
+		"AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA" +
+		"3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71" +
+		"BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0" +
+		"DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7"
+
+	qString := "F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F"
+
+	p := large.NewIntFromString(pString, base)
+	g := large.NewIntFromString(gString, base)
+	q := large.NewIntFromString(qString, base)
+
+	grp := cyclic.NewGroup(p, g, q)
+
+	return grp
+}