diff --git a/go.mod b/go.mod
index 371b92b8588164f9862350b80182a5d592ca42ac..3ea0fa1712c239c1cc08581025adb4afd9f2859f 100644
--- a/go.mod
+++ b/go.mod
@@ -18,13 +18,14 @@ require (
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/spf13/viper v1.7.1
 	gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228
-	gitlab.com/elixxir/comms v0.0.4-0.20201120005227-ec9177071abb
-	gitlab.com/elixxir/crypto v0.0.5-0.20201118204646-9b23991834c6
+	gitlab.com/elixxir/comms v0.0.4-0.20201124200043-8031463a4843
+	gitlab.com/elixxir/crypto v0.0.5-0.20201124195819-54be458a0dac
 	gitlab.com/elixxir/ekv v0.1.3
 	gitlab.com/elixxir/primitives v0.0.3-0.20201116174806-97f190989704
 	gitlab.com/xx_network/comms v0.0.4-0.20201119231004-a67d08045535
-	gitlab.com/xx_network/crypto v0.0.4
+	gitlab.com/xx_network/crypto v0.0.5-0.20201124194022-366c10b1bce0
 	gitlab.com/xx_network/primitives v0.0.3-0.20201116234927-44e42fc91e7c
+	gitlab.com/xx_network/ring v0.0.3-0.20201120004140-b0e268db06d1 // indirect
 	golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
 	google.golang.org/protobuf v1.25.0
 	gopkg.in/ini.v1 v1.61.0 // indirect
diff --git a/go.sum b/go.sum
index 7bd0cfbf89543ee8e2f1be9b13a49923f5293859..f0cf37ef1223c2adeb4e55a26524b6cdd75b1f4a 100644
--- a/go.sum
+++ b/go.sum
@@ -254,6 +254,8 @@ gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 h1:Gi6rj4mAlK0
 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k=
 gitlab.com/elixxir/comms v0.0.4-0.20201120005227-ec9177071abb h1:nqceIUadPdJGLQZHGHOEA4lbb4qADYLm/f0nfeQPZ/o=
 gitlab.com/elixxir/comms v0.0.4-0.20201120005227-ec9177071abb/go.mod h1:Xdo7hhfg+wGShftpN4S/2tgO+6A8K+5OijlVO+wrl7s=
+gitlab.com/elixxir/comms v0.0.4-0.20201124200043-8031463a4843 h1:KYcfQnnmc1oBAodVi3gMCXNWS6zoSCeyLhrrRzQP40Y=
+gitlab.com/elixxir/comms v0.0.4-0.20201124200043-8031463a4843/go.mod h1:dRTzI+YApY9QZ2+eP9+0n5+KzxziZY24SyW31O4QWgM=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4 h1:28ftZDeYEko7xptCZzeFWS1Iam95dj46TWFVVlKmw6A=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
 gitlab.com/elixxir/crypto v0.0.3 h1:znCt/x2bL4y8czTPaaFkwzdgSgW3BJc/1+dxyf1jqVw=
@@ -262,6 +264,8 @@ gitlab.com/elixxir/crypto v0.0.5-0.20201110193609-6b5e881867b4 h1:1a1zZDuqZ56qU1
 gitlab.com/elixxir/crypto v0.0.5-0.20201110193609-6b5e881867b4/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA=
 gitlab.com/elixxir/crypto v0.0.5-0.20201118204646-9b23991834c6 h1:HEJHC6gyVMdCZ1PSJkFDScHnsrWAMF+PFxyL2zpNrgU=
 gitlab.com/elixxir/crypto v0.0.5-0.20201118204646-9b23991834c6/go.mod h1:BqvmtLM4eW+3NNOVK7U3COnnxqhJZxdCv4yziCuYhlA=
+gitlab.com/elixxir/crypto v0.0.5-0.20201124195819-54be458a0dac h1:JXtuads5nAgrSku7klahF8n+CF+dmupmm0DHsL0CCEM=
+gitlab.com/elixxir/crypto v0.0.5-0.20201124195819-54be458a0dac/go.mod h1:V8lricBRpa8v1ySymXQ1/lsb+8/lSak5S7ZWRT6OACY=
 gitlab.com/elixxir/ekv v0.1.3 h1:OE+LBMIhjGUMwc6hHJzYvEPNJQV7t1vMnJyIgxUMUo8=
 gitlab.com/elixxir/ekv v0.1.3/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4=
 gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg=
@@ -278,6 +282,8 @@ gitlab.com/xx_network/comms v0.0.4-0.20201119231004-a67d08045535/go.mod h1:YViGb
 gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt3t17VRqRE=
 gitlab.com/xx_network/crypto v0.0.4 h1:lpKOL5mTJ2awWMfgBy30oD/UvJVrWZzUimSHlOdZZxo=
 gitlab.com/xx_network/crypto v0.0.4/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk=
+gitlab.com/xx_network/crypto v0.0.5-0.20201124194022-366c10b1bce0 h1:v2xX/bl9lyZJZXXFZ6glDs7F/GNGIeKAH6hzafUNi5w=
+gitlab.com/xx_network/crypto v0.0.5-0.20201124194022-366c10b1bce0/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk=
 gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA=
 gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da h1:CCVslUwNC7Ul7NG5nu3ThGTSVUt1TxNRX+47f5TUwnk=
 gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug=
@@ -285,6 +291,7 @@ gitlab.com/xx_network/primitives v0.0.2 h1:r45yKenJ9e7PylI1ZXJ1Es09oYNaYXjxVy9+u
 gitlab.com/xx_network/primitives v0.0.2/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc=
 gitlab.com/xx_network/primitives v0.0.3-0.20201116234927-44e42fc91e7c h1:mYId667WIN97E6KhPw4HDYyCjWzsG7gCM/HLTNTCXZQ=
 gitlab.com/xx_network/primitives v0.0.3-0.20201116234927-44e42fc91e7c/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc=
+gitlab.com/xx_network/ring v0.0.2/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM=
 gitlab.com/xx_network/ring v0.0.3-0.20201120004140-b0e268db06d1 h1:yqXC/naXN6Hw1fmfI6qOOMmBbmZGsYXsESVSwf6IEdM=
 gitlab.com/xx_network/ring v0.0.3-0.20201120004140-b0e268db06d1/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
diff --git a/ud/register.go b/ud/register.go
index 84aec1a9277078b2a50ae7ba6d7f75a794a25d98..505c08d885ca23dd3d338ab66c1e39fafb5e832f 100644
--- a/ud/register.go
+++ b/ud/register.go
@@ -1,25 +1,75 @@
 package ud
 
 import (
+	"github.com/pkg/errors"
 	pb "gitlab.com/elixxir/comms/mixmessages"
-	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/elixxir/crypto/factID"
+	"gitlab.com/elixxir/crypto/hash"
+	"gitlab.com/elixxir/primitives/fact"
+	"gitlab.com/xx_network/comms/connect"
+	"gitlab.com/xx_network/comms/messages"
+	"gitlab.com/xx_network/crypto/signature/rsa"
 )
 
-func (m *Manager) Register(myID *id.ID, username string) error {
+type registerUserComms interface {
+	SendRegisterUser(*connect.Host, *pb.UDBUserRegistration) (*messages.Ack, error)
+}
+
+// Register registers a user with user discovery.
+func (m *Manager) Register(username string) error {
+	return m.register(username, m.comms)
+}
 
+// register registers a user with user discovery with a specified comm for
+// easier testing.
+func (m *Manager) register(username string, comm registerUserComms) error {
+	var err error
+	user := m.storage.User()
+	cryptoUser := m.storage.User().GetCryptographicIdentity()
+	rng := m.rng.GetStream()
+
+	// Construct the user registration message
 	msg := &pb.UDBUserRegistration{
-		PermissioningSignature: nil,
-		RSAPublicPem:           "",
-		IdentityRegistration:   nil,
-		IdentitySignature:      nil,
-		Frs:                    nil,
-		UID:                    myID.Bytes(),
-		XXX_NoUnkeyedLiteral:   struct{}{},
-		XXX_unrecognized:       nil,
-		XXX_sizecache:          0,
+		PermissioningSignature: user.GetRegistrationValidationSignature(),
+		RSAPublicPem:           string(rsa.CreatePublicKeyPem(cryptoUser.GetRSA().GetPublic())),
+		IdentityRegistration: &pb.Identity{
+			Username: username,
+			DhPubKey: m.storage.E2e().GetDHPublicKey().Bytes(),
+			Salt:     cryptoUser.GetSalt(),
+		},
+		UID: cryptoUser.GetUserID().Marshal(),
+	}
+
+	// Sign the identity data and add to user registration message
+	identityDigest := msg.IdentityRegistration.Digest()
+	msg.IdentitySignature, err = rsa.Sign(rng, cryptoUser.GetRSA(),
+		hash.CMixHash, identityDigest, nil)
+	if err != nil {
+		return errors.Errorf("Failed to sign user's IdentityRegistration: %+v", err)
+	}
+
+	// Create new username fact
+	usernameFact, err := fact.NewFact(fact.Username, username)
+	if err != nil {
+		return errors.Errorf("Failed to create new username fact: %+v", err)
+	}
+
+	// Hash and sign fact
+	hashedFact := factID.Fingerprint(usernameFact)
+	signedFact, err := rsa.Sign(rng, cryptoUser.GetRSA(), hash.CMixHash, hashedFact, nil)
+
+	// Add username fact register request to the user registration message
+	msg.Frs = &pb.FactRegisterRequest{
+		UID: cryptoUser.GetUserID().Marshal(),
+		Fact: &pb.Fact{
+			Fact:     username,
+			FactType: 0,
+		},
+		FactSig: signedFact,
 	}
 
-	_, _ = m.comms.SendRegisterUser(m.host, msg)
+	// Register user with user discovery
+	_, err = comm.SendRegisterUser(m.host, msg)
 
-	return nil
+	return err
 }
diff --git a/ud/register_test.go b/ud/register_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..274eebe9ca63865aa20831d4dfc0a28d6d52f4f2
--- /dev/null
+++ b/ud/register_test.go
@@ -0,0 +1,111 @@
+package ud
+
+import (
+	"bytes"
+	"gitlab.com/elixxir/client/storage"
+	pb "gitlab.com/elixxir/comms/mixmessages"
+	"gitlab.com/elixxir/crypto/factID"
+	"gitlab.com/elixxir/crypto/fastRNG"
+	"gitlab.com/elixxir/crypto/hash"
+	"gitlab.com/elixxir/primitives/fact"
+	"gitlab.com/xx_network/comms/connect"
+	"gitlab.com/xx_network/comms/messages"
+	"gitlab.com/xx_network/crypto/csprng"
+	"gitlab.com/xx_network/crypto/signature/rsa"
+	"gitlab.com/xx_network/primitives/id"
+	"reflect"
+	"testing"
+)
+
+type testRegisterComm struct {
+	msg *pb.UDBUserRegistration
+}
+
+func (t *testRegisterComm) SendRegisterUser(_ *connect.Host, msg *pb.UDBUserRegistration) (*messages.Ack, error) {
+	t.msg = msg
+	return &messages.Ack{}, nil
+}
+
+// Happy path.
+func TestManager_register(t *testing.T) {
+	// Create new host
+	host, err := connect.NewHost(&id.UDB, "0.0.0.0", nil, connect.GetDefaultHostParams())
+	if err != nil {
+		t.Fatalf("Could not create a new host: %+v", err)
+	}
+
+	// Set up manager
+	m := &Manager{
+		host:    host,
+		rng:     fastRNG.NewStreamGenerator(12, 3, csprng.NewSystemRNG),
+		storage: storage.InitTestingSession(t),
+	}
+
+	c := &testRegisterComm{}
+
+	err = m.register("testUser", c)
+	if err != nil {
+		t.Errorf("register() returned an error: %+v", err)
+	}
+
+	// Check if the UDBUserRegistration contents are correct
+	m.isCorrect("testUser", c.msg, t)
+
+	// Verify the signed identity data
+	pubKey := m.storage.User().GetCryptographicIdentity().GetRSA().GetPublic()
+	err = rsa.Verify(pubKey, hash.CMixHash, c.msg.IdentityRegistration.Digest(),
+		c.msg.IdentitySignature, nil)
+	if err != nil {
+		t.Errorf("Failed to verify signed identity data: %+v", err)
+	}
+
+	// Verify the signed fact
+	usernameFact, _ := fact.NewFact(fact.Username, "testUser")
+	err = rsa.Verify(pubKey, hash.CMixHash, factID.Fingerprint(usernameFact),
+		c.msg.Frs.FactSig, nil)
+	if err != nil {
+		t.Errorf("Failed to verify signed fact data: %+v", err)
+	}
+}
+
+// isCorrect checks if the UDBUserRegistration has all the expected fields minus
+// any signatures.
+func (m *Manager) isCorrect(username string, msg *pb.UDBUserRegistration, t *testing.T) {
+	user := m.storage.User()
+	cryptoUser := m.storage.User().GetCryptographicIdentity()
+
+	if !bytes.Equal(user.GetRegistrationValidationSignature(), msg.PermissioningSignature) {
+		t.Errorf("PermissioningSignature incorrect.\n\texpected: %v\n\treceived: %v",
+			user.GetRegistrationValidationSignature(), msg.PermissioningSignature)
+	}
+
+	if string(rsa.CreatePublicKeyPem(cryptoUser.GetRSA().GetPublic())) != msg.RSAPublicPem {
+		t.Errorf("RSAPublicPem incorrect.\n\texpected: %v\n\treceived: %v",
+			string(rsa.CreatePublicKeyPem(cryptoUser.GetRSA().GetPublic())), msg.RSAPublicPem)
+	}
+
+	if username != msg.IdentityRegistration.Username {
+		t.Errorf("IdentityRegistration Username incorrect.\n\texpected: %#v\n\treceived: %#v",
+			username, msg.IdentityRegistration.Username)
+	}
+
+	if !bytes.Equal(m.storage.E2e().GetDHPublicKey().Bytes(), msg.IdentityRegistration.DhPubKey) {
+		t.Errorf("IdentityRegistration DhPubKey incorrect.\n\texpected: %#v\n\treceived: %#v",
+			m.storage.E2e().GetDHPublicKey().Bytes(), msg.IdentityRegistration.DhPubKey)
+	}
+
+	if !bytes.Equal(cryptoUser.GetSalt(), msg.IdentityRegistration.Salt) {
+		t.Errorf("IdentityRegistration Salt incorrect.\n\texpected: %#v\n\treceived: %#v",
+			cryptoUser.GetSalt(), msg.IdentityRegistration.Salt)
+	}
+
+	if !bytes.Equal(cryptoUser.GetUserID().Marshal(), msg.Frs.UID) {
+		t.Errorf("Frs UID incorrect.\n\texpected: %v\n\treceived: %v",
+			cryptoUser.GetUserID().Marshal(), msg.Frs.UID)
+	}
+
+	if !reflect.DeepEqual(&pb.Fact{Fact: username}, msg.Frs.Fact) {
+		t.Errorf("Frs Fact incorrect.\n\texpected: %v\n\treceived: %v",
+			&pb.Fact{Fact: username}, msg.Frs.Fact)
+	}
+}