From 387defa9fd0ae9f0035edd11760a61b93cc32b98 Mon Sep 17 00:00:00 2001
From: josh <josh@elixxir.io>
Date: Wed, 29 Sep 2021 10:25:32 -0700
Subject: [PATCH] Implement new registration method

---
 api/mnemonic.go          |   1 -
 go.mod                   |   4 +-
 go.sum                   |   4 +
 network/node/register.go | 206 ++++++++++++++++++++++++++++++++++-----
 4 files changed, 189 insertions(+), 26 deletions(-)

diff --git a/api/mnemonic.go b/api/mnemonic.go
index 0ab1f2303..b885e912c 100644
--- a/api/mnemonic.go
+++ b/api/mnemonic.go
@@ -94,4 +94,3 @@ func LoadSecretWithMnemonic(mnemonic, path string) (secret []byte, err error) {
 
 	return secret, nil
 }
-
diff --git a/go.mod b/go.mod
index f1c1cf6bb..5b4e6e09c 100644
--- a/go.mod
+++ b/go.mod
@@ -17,8 +17,8 @@ require (
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.1
 	gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228
-	gitlab.com/elixxir/comms v0.0.4-0.20210927221600-65a291f4e6a6
-	gitlab.com/elixxir/crypto v0.0.7-0.20210920180151-6c9b84bae372
+	gitlab.com/elixxir/comms v0.0.4-0.20210928204243-bcc6ba732877
+	gitlab.com/elixxir/crypto v0.0.7-0.20210928232606-4f7feb90bee7
 	gitlab.com/elixxir/ekv v0.1.5
 	gitlab.com/elixxir/primitives v0.0.3-0.20210920180121-b85bca5212f4
 	gitlab.com/xx_network/comms v0.0.4-0.20210921011654-3b73a40ed3d6
diff --git a/go.sum b/go.sum
index 39a2dd563..367f13987 100644
--- a/go.sum
+++ b/go.sum
@@ -261,10 +261,14 @@ gitlab.com/elixxir/comms v0.0.4-0.20210924220856-4864c21fe316 h1:PLGmuuaG5R1suI2
 gitlab.com/elixxir/comms v0.0.4-0.20210924220856-4864c21fe316/go.mod h1:h41+FHc9zlQGveEao3aw8VSfzyOPecEhhUIadUsW1C8=
 gitlab.com/elixxir/comms v0.0.4-0.20210927221600-65a291f4e6a6 h1:draTda/SDnop2oCRfyWvu6hqC8G4i7BrVzfwZ7tDZls=
 gitlab.com/elixxir/comms v0.0.4-0.20210927221600-65a291f4e6a6/go.mod h1:h41+FHc9zlQGveEao3aw8VSfzyOPecEhhUIadUsW1C8=
+gitlab.com/elixxir/comms v0.0.4-0.20210928204243-bcc6ba732877 h1:MpDU05tua291M8NH9FbxCzqmuyRsO2raOHtDQtJviao=
+gitlab.com/elixxir/comms v0.0.4-0.20210928204243-bcc6ba732877/go.mod h1:h41+FHc9zlQGveEao3aw8VSfzyOPecEhhUIadUsW1C8=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
 gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA=
 gitlab.com/elixxir/crypto v0.0.7-0.20210920180151-6c9b84bae372 h1:W5Ax+cwqOOcsVegaMLvsFJ/Cs24a4Wyhp5UHFwvMQxo=
 gitlab.com/elixxir/crypto v0.0.7-0.20210920180151-6c9b84bae372/go.mod h1:9ipko6RwRCaF2H01PtCzGCjzWVex8/ZH2/vOK1qjjw4=
+gitlab.com/elixxir/crypto v0.0.7-0.20210928232606-4f7feb90bee7 h1:03GKd/SLKnxeVSXdgX/QAzPtvdYcY5C/Fvt/lhTOlWk=
+gitlab.com/elixxir/crypto v0.0.7-0.20210928232606-4f7feb90bee7/go.mod h1:9ipko6RwRCaF2H01PtCzGCjzWVex8/ZH2/vOK1qjjw4=
 gitlab.com/elixxir/ekv v0.1.5 h1:R8M1PA5zRU1HVnTyrtwybdABh7gUJSCvt1JZwUSeTzk=
 gitlab.com/elixxir/ekv v0.1.5/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4=
 gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg=
diff --git a/network/node/register.go b/network/node/register.go
index e295c967e..7fcd941b4 100644
--- a/network/node/register.go
+++ b/network/node/register.go
@@ -27,8 +27,10 @@ import (
 	"gitlab.com/elixxir/crypto/registration"
 	"gitlab.com/xx_network/comms/connect"
 	"gitlab.com/xx_network/comms/messages"
+	"gitlab.com/xx_network/crypto/chacha"
 	"gitlab.com/xx_network/crypto/csprng"
 	"gitlab.com/xx_network/crypto/signature/rsa"
+	"gitlab.com/xx_network/crypto/tls"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/netTime"
 	"strconv"
@@ -38,8 +40,14 @@ import (
 type RegisterNodeCommsInterface interface {
 	SendRequestClientKeyMessage(host *connect.Host,
 		message *pb.SignedClientKeyRequest) (*pb.SignedKeyResponse, error)
+
+	// ---------------------- Start of deprecated fields ----------- //
+	// TODO: Remove once RequestClientKey is properly tested
+	SendRequestNonceMessage(host *connect.Host, message *pb.NonceRequest) (*pb.Nonce, error)
 	SendConfirmNonceMessage(host *connect.Host,
 		message *pb.RequestRegistrationConfirmation) (*pb.RegistrationConfirmation, error)
+	// ---------------------- End of deprecated fields ----------- //
+
 }
 
 func StartRegistration(sender *gateway.Sender, session *storage.Session, rngGen *fastRNG.StreamGenerator, comms RegisterNodeCommsInterface,
@@ -124,32 +132,73 @@ func registerWithNode(sender *gateway.Sender, comms RegisterNodeCommsInterface,
 		transmissionKey = store.GetGroup().NewIntFromBytes(h.Sum(nil))
 		jww.INFO.Printf("transmissionKey: %v", transmissionKey.Bytes())
 	} else {
-		// Initialise blake2b hash for transmission keys and reception
-		// keys
-		transmissionHash, _ := hash.NewCMixHash()
-
-		nonce, dhPub, err := requestKey(sender, comms, gatewayID, regSig,
+		// Request key from server
+		signedKeyResponse, err := requestKey(sender, comms, gatewayID, regSig,
 			registrationTimestampNano, uci, store, rng, stop)
 
 		if err != nil {
+			// TODO: remove old codepath when new registration path is properly tested
+			jww.WARN.Printf("Databaseless registration failed, attempting soon "+
+				"to be deprecated code path. Error: %v", err)
+			transmissionKey, err = registerDepreciated(sender, comms, regSig,
+				registrationTimestampNano, uci, store, rng, stop, gatewayID, nodeID)
 			return errors.Errorf("Failed to request nonce: %+v", err)
 		}
 
-		// Load server DH pubkey
-		serverPubDH := store.GetGroup().NewIntFromBytes(dhPub)
+		// Hash the response
+		opts := rsa.NewDefaultOptions()
+		h := opts.Hash.New()
+		h.Write(signedKeyResponse.KeyResponse)
+		hashedResponse := h.Sum(nil)
+
+		// Load node certificate
+		nodeCert, err := tls.LoadCertificate(ngw.Node.TlsCertificate)
+		if err != nil {
+			return errors.WithMessagef(err, "Unable to load node's certificate")
+		}
+
+		// Extract public key
+		nodePubKey, err := tls.ExtractPublicKey(nodeCert)
+		if err != nil {
+			return errors.WithMessagef(err, "Unable to load node's public key")
+		}
 
-		// Confirm received nonce
-		// fixme: need? I think this can be removed. I which case remove from comms as well
-		jww.INFO.Printf("Register: Confirming received nonce from node %s", nodeID.String())
-		err = confirmNonce(sender, comms, uci.GetTransmissionID().Bytes(),
-			nonce, uci.GetTransmissionRSA(), gatewayID, stop)
+		// Verify the response signature
+		err = rsa.Verify(nodePubKey, opts.Hash, hashedResponse,
+			signedKeyResponse.KeyResponseSignedByNode.Signature, opts)
+		if err != nil {
+			return errors.WithMessagef(err, "Could not verify node's signature")
+		}
+
+		// Unmarshal the response
+		keyResponse := &pb.ClientKeyResponse{}
+		err = proto.Unmarshal(signedKeyResponse.KeyResponse, keyResponse)
+		if err != nil {
+			return errors.WithMessagef(err, "Failed to unmarshal client key response")
+		}
+
+		// Construct the session key
+		clientDhPub := store.GetDHPublicKey().Bytes()
+		h.Reset()
+		h.Write(keyResponse.NodeDHPubKey)
+		h.Write(clientDhPub)
+		sessionKey := h.Sum(nil)
+
+		// Verify the HMAC
+		h.Reset()
+		if !registration.VerifyClientHMAC(sessionKey, keyResponse.EncryptedClientKey,
+			h, keyResponse.EncryptedClientKeyHMAC) {
+			return errors.WithMessagef(err, "Failed to verify client HMAC")
+		}
 
+		// Decrypt the client key
+		clientKey, err := chacha.Decrypt(sessionKey, keyResponse.EncryptedClientKey)
 		if err != nil {
-			errMsg := fmt.Sprintf("Register: Unable to confirm nonce: %v", err)
-			return errors.New(errMsg)
+			return errors.WithMessagef(err, "Failed to decrypt client key")
 		}
-		transmissionKey = registration.GenerateBaseKey(store.GetGroup(),
-			serverPubDH, store.GetDHPrivateKey(), transmissionHash)
+
+		// Construct the transmission key from the client key
+		transmissionKey = store.GetGroup().NewIntFromBytes(clientKey)
 	}
 
 	store.Add(nodeID, transmissionKey)
@@ -161,7 +210,7 @@ func registerWithNode(sender *gateway.Sender, comms RegisterNodeCommsInterface,
 
 func requestKey(sender *gateway.Sender, comms RegisterNodeCommsInterface, gwId *id.ID,
 	regSig []byte, registrationTimestampNano int64, uci *user.CryptographicIdentity,
-	store *cmix.Store, rng csprng.Source, stop *stoppable.Single) (keyResponse, clientGatewayKey []byte, err error) {
+	store *cmix.Store, rng csprng.Source, stop *stoppable.Single) (keyResponse *pb.SignedKeyResponse, err error) {
 
 	dhPub := store.GetDHPublicKey().Bytes()
 
@@ -170,14 +219,14 @@ func requestKey(sender *gateway.Sender, comms RegisterNodeCommsInterface, gwId *
 		ClientTransmissionConfirmation: &pb.SignedRegistrationConfirmation{
 			RegistrarSignature: &messages.RSASignature{Signature: regSig},
 		},
-		ClientDHPubKey:   dhPub,
+		ClientDHPubKey:        dhPub,
 		RegistrationTimestamp: registrationTimestampNano,
-		RequestTimestamp: netTime.Now().UnixNano(),
+		RequestTimestamp:      netTime.Now().UnixNano(),
 	}
 
 	serializedMessage, err := proto.Marshal(keyRequest)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
 	opts := rsa.NewDefaultOptions()
@@ -190,7 +239,7 @@ func requestKey(sender *gateway.Sender, comms RegisterNodeCommsInterface, gwId *
 	clientSig, err := rsa.Sign(rng, uci.GetTransmissionRSA(), opts.Hash,
 		data, opts)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
 	// Request nonce message from gateway
@@ -212,22 +261,131 @@ func requestKey(sender *gateway.Sender, comms RegisterNodeCommsInterface, gwId *
 		return nonceResponse, nil
 	}, stop)
 
+	if err != nil {
+		return nil, err
+	}
+
+	response := result.(*pb.SignedKeyResponse)
+	if response.Error != "" {
+		return nil, errors.New(response.Error)
+	}
+
+	// Use Client keypair to sign Server nonce
+	return response, nil
+}
+
+// ---------------------- Start of deprecated fields ----------- //
+
+// registerDepreciated is a DEPRECATED codepath that registers a user via
+// the request/confirmNonce codepath. This is left for backward compatibility
+// and will be removed.
+// TODO: Remove this once RequestClientKey is properly tested
+func registerDepreciated(sender *gateway.Sender, comms RegisterNodeCommsInterface,
+	regSig []byte, registrationTimestampNano int64, uci *user.CryptographicIdentity,
+	store *cmix.Store, rng csprng.Source, stop *stoppable.Single,
+	gatewayID, nodeID *id.ID) (*cyclic.Int, error) {
+
+	jww.WARN.Printf("DEPRECATED: Registering using soon to be deprecated code path")
+
+	transmissionHash, _ := hash.NewCMixHash()
+
+	// Register nonce
+	nonce, dhPub, err := requestNonce(sender, comms, gatewayID, regSig,
+		registrationTimestampNano, uci, store, rng, stop)
+	if err != nil {
+		return nil, err
+	}
+
+	// Load server DH pubkey
+	serverPubDH := store.GetGroup().NewIntFromBytes(dhPub)
+
+	// Confirm received nonce
+	jww.INFO.Printf("Register: Confirming received nonce from node %s", nodeID.String())
+	err = confirmNonce(sender, comms, uci.GetTransmissionID().Bytes(),
+		nonce, uci.GetTransmissionRSA(), gatewayID, stop)
+
+	if err != nil {
+		errMsg := fmt.Sprintf("Register: Unable to confirm nonce: %v", err)
+		return nil, errors.New(errMsg)
+	}
+	transmissionKey := registration.GenerateBaseKey(store.GetGroup(),
+		serverPubDH, store.GetDHPrivateKey(), transmissionHash)
+
+	return transmissionKey, err
+}
+
+// WARNING DEPRECATED: requestNonce will soon be deprecated and removed. This will only
+// be used for testing with backwards compatibility.
+// TODO: Remove this once RequestClientKey is properly tested
+func requestNonce(sender *gateway.Sender, comms RegisterNodeCommsInterface, gwId *id.ID,
+	regSig []byte, registrationTimestampNano int64, uci *user.CryptographicIdentity,
+	store *cmix.Store, rng csprng.Source, stop *stoppable.Single) ([]byte, []byte, error) {
+
+	jww.WARN.Printf("DEPRECATED: Registering with a soon to be deprecated function")
+
+	dhPub := store.GetDHPublicKey().Bytes()
+	opts := rsa.NewDefaultOptions()
+	opts.Hash = hash.CMixHash
+	h, _ := hash.NewCMixHash()
+	h.Write(dhPub)
+	data := h.Sum(nil)
+
+	// Sign DH pubkey
+	clientSig, err := rsa.Sign(rng, uci.GetTransmissionRSA(), opts.Hash,
+		data, opts)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// Request nonce message from gateway
+	jww.INFO.Printf("Register: Requesting nonce from gateway %v", gwId.String())
+
+	result, err := sender.SendToAny(func(host *connect.Host) (interface{}, error) {
+		nonceResponse, err := comms.SendRequestNonceMessage(host,
+			&pb.NonceRequest{
+				Salt:            uci.GetTransmissionSalt(),
+				ClientRSAPubKey: string(rsa.CreatePublicKeyPem(uci.GetTransmissionRSA().GetPublic())),
+				ClientSignedByServer: &messages.RSASignature{
+					Signature: regSig,
+				},
+				ClientDHPubKey: dhPub,
+				RequestSignature: &messages.RSASignature{
+					Signature: clientSig,
+				},
+				Target: gwId.Marshal(),
+				// Timestamp in which user has registered with registration
+				TimeStamp: registrationTimestampNano,
+			})
+		if err != nil {
+			return nil, errors.WithMessage(err, "Register: Failed requesting nonce from gateway")
+		}
+		if nonceResponse.Error != "" {
+			return nil, errors.WithMessage(err, "requestNonce: nonceResponse error")
+		}
+		return nonceResponse, nil
+	}, stop)
+
 	if err != nil {
 		return nil, nil, err
 	}
 
-	nonceResponse := result.(*pb.SignedKeyResponse)
+	nonceResponse := result.(*pb.Nonce)
 
 	// Use Client keypair to sign Server nonce
-	return nonceResponse.KeyResponse, nonceResponse.ClientGatewayKey, nil
+	return nonceResponse.Nonce, nonceResponse.DHPubKey, nil
 }
 
+// WARNING DEPRECATED: confirmNonce will soon be deprecated and removed. This will only
+// be used for testing with backwards compatibility.
 // confirmNonce is a helper for the Register function
 // It signs a nonce and sends it for confirmation
 // Returns nil if successful, error otherwise
+// TODO: Remove this once RequestClientKey is properly tested
 func confirmNonce(sender *gateway.Sender, comms RegisterNodeCommsInterface, UID,
 	nonce []byte, privateKeyRSA *rsa.PrivateKey, gwID *id.ID,
 	stop *stoppable.Single) error {
+	jww.WARN.Printf("DEPRECATED: ConfirmNonce is a soon to be deprecated function")
+
 	opts := rsa.NewDefaultOptions()
 	opts.Hash = hash.CMixHash
 	h, _ := hash.NewCMixHash()
@@ -270,3 +428,5 @@ func confirmNonce(sender *gateway.Sender, comms RegisterNodeCommsInterface, UID,
 
 	return err
 }
+
+// ---------------------- End of deprecated fields ----------- //
-- 
GitLab