From 9532f7197f454557b51d625d1543837d086624dc Mon Sep 17 00:00:00 2001
From: Benjamin Wenger <ben@elixxir.ioo>
Date: Wed, 30 Jun 2021 21:24:26 -0700
Subject: [PATCH] multithreaded user creation to reduce how long it takes

---
 api/client.go |   3 +-
 api/user.go   | 130 ++++++++++++++++++++++++++++++++++----------------
 2 files changed, 90 insertions(+), 43 deletions(-)

diff --git a/api/client.go b/api/client.go
index 0f53ede47..20545312e 100644
--- a/api/client.go
+++ b/api/client.go
@@ -82,7 +82,6 @@ func NewClient(ndfJSON, storageDir string, password []byte, registrationCode str
 	// Use fastRNG for RNG ops (AES fortuna based RNG using system RNG)
 	jww.INFO.Printf("RNG Creation")
 	rngStreamGen := fastRNG.NewStreamGenerator(12, 3, csprng.NewSystemRNG)
-	rngStream := rngStreamGen.GetStream()
 	jww.INFO.Printf("Parsing NDF")
 	// Parse the NDF
 	def, err := parseNDF(ndfJSON)
@@ -92,7 +91,7 @@ func NewClient(ndfJSON, storageDir string, password []byte, registrationCode str
 	jww.INFO.Printf("Decoding Groups")
 	cmixGrp, e2eGrp := decodeGroups(def)
 	jww.INFO.Printf("Creating New User")
-	protoUser := createNewUser(rngStream, cmixGrp, e2eGrp)
+	protoUser := createNewUser(rngStreamGen, cmixGrp, e2eGrp)
 	jww.INFO.Printf("Setting Up Storage")
 	err = checkVersionAndSetupStorage(def, storageDir, password, protoUser,
 		cmixGrp, e2eGrp, rngStreamGen, false, registrationCode)
diff --git a/api/user.go b/api/user.go
index 48d7f992d..695ed3ed9 100644
--- a/api/user.go
+++ b/api/user.go
@@ -12,6 +12,7 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/interfaces/user"
 	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/elixxir/crypto/fastRNG"
 	"gitlab.com/xx_network/crypto/csprng"
 	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/crypto/xx"
@@ -29,58 +30,105 @@ const (
 )
 
 // createNewUser generates an identity for cMix
-func createNewUser(rng csprng.Source, cmix, e2e *cyclic.Group) user.User {
+func createNewUser(rng *fastRNG.StreamGenerator, cmix, e2e *cyclic.Group) user.User {
 	// CMIX Keygen
-	// FIXME: Why 256 bits? -- this is spec but not explained, it has
-	// to do with optimizing operations on one side and still preserves
-	// decent security -- cite this.
-	cMixKeyBytes, err := csprng.GenerateInGroup(cmix.GetPBytes(), 256, rng)
-	if err != nil {
-		jww.FATAL.Panicf(err.Error())
-	}
+	var transmissionRsaKey, receptionRsaKey *rsa.PrivateKey
 
-	// DH Keygen
-	// FIXME: Why 256 bits? -- this is spec but not explained, it has
-	// to do with optimizing operations on one side and still preserves
-	// decent security -- cite this. Why valid for BOTH e2e and cmix?
-	e2eKeyBytes, err := csprng.GenerateInGroup(e2e.GetPBytes(), 256, rng)
-	if err != nil {
-		jww.FATAL.Panicf(err.Error())
-	}
+	var cMixKeyBytes, e2eKeyBytes, transmissionSalt, receptionSalt []byte
+
+	wg := sync.WaitGroup{}
+
+	wg.Add(6)
+
+	go func(){
+		defer wg.Done()
+		var err error
+		// FIXME: Why 256 bits? -- this is spec but not explained, it has
+		// to do with optimizing operations on one side and still preserves
+		// decent security -- cite this.
+		stream := rng.GetStream()
+		cMixKeyBytes, err = csprng.GenerateInGroup(cmix.GetPBytes(), 256, stream)
+		stream.Close()
+		if err != nil {
+			jww.FATAL.Panicf(err.Error())
+		}
+	}()
+
+	go func(){
+		defer wg.Done()
+		var err error
+		// DH Keygen
+		// FIXME: Why 256 bits? -- this is spec but not explained, it has
+		// to do with optimizing operations on one side and still preserves
+		// decent security -- cite this. Why valid for BOTH e2e and cmix?
+		stream := rng.GetStream()
+		e2eKeyBytes, err = csprng.GenerateInGroup(e2e.GetPBytes(), 256, stream)
+		stream.Close()
+		if err != nil {
+			jww.FATAL.Panicf(err.Error())
+		}
+	}()
 
 	// RSA Keygen (4096 bit defaults)
-	transmissionRsaKey, err := rsa.GenerateKey(rng, rsa.DefaultRSABitLen)
-	if err != nil {
-		jww.FATAL.Panicf(err.Error())
-	}
-	receptionRsaKey, err := rsa.GenerateKey(rng, rsa.DefaultRSABitLen)
-	if err != nil {
-		jww.FATAL.Panicf(err.Error())
-	}
+	go func() {
+		defer wg.Done()
+		var err error
+		stream := rng.GetStream()
+		transmissionRsaKey, err = rsa.GenerateKey(stream, rsa.DefaultRSABitLen)
+		stream.Close()
+		if err != nil {
+			jww.FATAL.Panicf(err.Error())
+		}
+	}()
+
+	go func() {
+		defer wg.Done()
+		var err error
+		stream := rng.GetStream()
+		receptionRsaKey, err = rsa.GenerateKey(stream, rsa.DefaultRSABitLen)
+		stream.Close()
+		if err != nil {
+			jww.FATAL.Panicf(err.Error())
+		}
+	}()
 
 	// Salt, UID, etc gen
-	transmissionSalt := make([]byte, SaltSize)
-	n, err := csprng.NewSystemRNG().Read(transmissionSalt)
-	if err != nil {
-		jww.FATAL.Panicf(err.Error())
-	}
-	if n != SaltSize {
-		jww.FATAL.Panicf("transmissionSalt size too small: %d", n)
-	}
+	go func(){
+		defer wg.Done()
+		transmissionSalt = make([]byte, SaltSize)
+		stream := rng.GetStream()
+		n, err := stream.Read(transmissionSalt)
+		stream.Close()
+		if err != nil {
+			jww.FATAL.Panicf(err.Error())
+		}
+		if n != SaltSize {
+			jww.FATAL.Panicf("transmissionSalt size too small: %d", n)
+		}
+	}()
+
+
+	go func(){
+		defer wg.Done()
+		receptionSalt = make([]byte, SaltSize)
+		stream := rng.GetStream()
+		n, err := stream.Read(receptionSalt)
+		stream.Close()
+		if err != nil {
+			jww.FATAL.Panicf(err.Error())
+		}
+		if n != SaltSize {
+			jww.FATAL.Panicf("transmissionSalt size too small: %d", n)
+		}
+	}()
+
+	wg.Wait()
+
 	transmissionID, err := xx.NewID(transmissionRsaKey.GetPublic(), transmissionSalt, id.User)
 	if err != nil {
 		jww.FATAL.Panicf(err.Error())
 	}
 
-	// Salt, UID, etc gen
-	receptionSalt := make([]byte, SaltSize)
-	n, err = csprng.NewSystemRNG().Read(receptionSalt)
-	if err != nil {
-		jww.FATAL.Panicf(err.Error())
-	}
-	if n != SaltSize {
-		jww.FATAL.Panicf("receptionSalt size too small: %d", n)
-	}
 	receptionID, err := xx.NewID(receptionRsaKey.GetPublic(), receptionSalt, id.User)
 	if err != nil {
 		jww.FATAL.Panicf(err.Error())
-- 
GitLab