/////////////////////////////////////////////////////////////////////////////// // Copyright © 2020 xx network SEZC // // // // Use of this source code is governed by a license that can be found in the // // LICENSE file // /////////////////////////////////////////////////////////////////////////////// package api import ( "regexp" "runtime" "strings" "sync" "gitlab.com/elixxir/crypto/diffieHellman" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/storage/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" "gitlab.com/xx_network/primitives/id" ) const ( // SaltSize size of user salts SaltSize = 32 ) // createNewUser generates an identity for cMix func createNewUser(rng *fastRNG.StreamGenerator) user.Info { // CMIX Keygen var transmissionRsaKey, receptionRsaKey *rsa.PrivateKey var transmissionSalt, receptionSalt []byte transmissionSalt, receptionSalt, transmissionRsaKey, receptionRsaKey = createKeys(rng) transmissionID, err := xx.NewID(transmissionRsaKey.GetPublic(), transmissionSalt, id.User) if err != nil { jww.FATAL.Panicf(err.Error()) } receptionID, err := xx.NewID(receptionRsaKey.GetPublic(), receptionSalt, id.User) if err != nil { jww.FATAL.Panicf(err.Error()) } return user.Info{ TransmissionID: transmissionID.DeepCopy(), TransmissionSalt: transmissionSalt, TransmissionRSA: transmissionRsaKey, ReceptionID: receptionID.DeepCopy(), ReceptionSalt: receptionSalt, ReceptionRSA: receptionRsaKey, Precanned: false, E2eDhPrivateKey: nil, E2eDhPublicKey: nil, } } func createKeys(rng *fastRNG.StreamGenerator) ( transmissionSalt, receptionSalt []byte, transmissionRsaKey, receptionRsaKey *rsa.PrivateKey) { wg := sync.WaitGroup{} wg.Add(2) // RSA Keygen (4096 bit defaults) go func() { defer wg.Done() var err error stream := rng.GetStream() transmissionRsaKey, err = rsa.GenerateKey(stream, rsa.DefaultRSABitLen) if err != nil { jww.FATAL.Panicf(err.Error()) } transmissionSalt = make([]byte, 32) _, err = stream.Read(transmissionSalt) 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) if err != nil { jww.FATAL.Panicf(err.Error()) } receptionSalt = make([]byte, 32) _, err = stream.Read(receptionSalt) stream.Close() if err != nil { jww.FATAL.Panicf(err.Error()) } }() wg.Wait() isZero := func(data []byte) bool { if len(data) == 0 { return true } for i := len(data) - 1; i != 0; i-- { if data[i] != 0 { return false } } return true } if isZero(receptionSalt) || isZero(transmissionSalt) { jww.FATAL.Panicf("empty salt generation detected") } return } // createNewVanityUser generates an identity for cMix // The identity's ReceptionID is not random but starts with the supplied prefix func createNewVanityUser(rng csprng.Source, cmix, e2e *cyclic.Group, prefix string) user.Info { // DH Keygen prime := e2e.GetPBytes() keyLen := len(prime) e2eKey := diffieHellman.GeneratePrivateKey(keyLen, e2e, rng) // RSA Keygen (4096 bit defaults) transmissionRsaKey, err := rsa.GenerateKey(rng, rsa.DefaultRSABitLen) 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) } transmissionID, err := xx.NewID(transmissionRsaKey.GetPublic(), transmissionSalt, id.User) if err != nil { jww.FATAL.Panicf(err.Error()) } receptionRsaKey, err := rsa.GenerateKey(rng, rsa.DefaultRSABitLen) if err != nil { jww.FATAL.Panicf(err.Error()) } // just in case more than one go routine tries to access // receptionSalt and receptionID var mu sync.Mutex done := make(chan struct{}) found := make(chan bool) wg := &sync.WaitGroup{} cores := runtime.NumCPU() var receptionSalt []byte var receptionID *id.ID pref := prefix ignoreCase := false // check if case-insensitivity is enabled if strings.HasPrefix(prefix, "(?i)") { pref = strings.ToLower(pref[4:]) ignoreCase = true } // Check if prefix contains valid Base64 characters match, _ := regexp.MatchString("^[A-Za-z0-9+/]+$", pref) if match == false { jww.FATAL.Panicf("Prefix contains non-Base64 characters") } jww.INFO.Printf("Vanity userID generation started. Prefix: %s "+ "Ignore-Case: %v NumCPU: %d", pref, ignoreCase, cores) for w := 0; w < cores; w++ { wg.Add(1) go func() { rSalt := make([]byte, SaltSize) for { select { case <-done: defer wg.Done() return default: n, err = csprng.NewSystemRNG().Read( rSalt) if err != nil { jww.FATAL.Panicf(err.Error()) } if n != SaltSize { jww.FATAL.Panicf( "receptionSalt size "+ "too small: %d", n) } rID, err := xx.NewID( receptionRsaKey.GetPublic(), rSalt, id.User) if err != nil { jww.FATAL.Panicf(err.Error()) } id := rID.String() if ignoreCase { id = strings.ToLower(id) } if strings.HasPrefix(id, pref) { mu.Lock() receptionID = rID receptionSalt = rSalt mu.Unlock() found <- true defer wg.Done() return } } } }() } // wait for a solution then close the done channel to signal // the workers to exit <-found close(done) wg.Wait() return user.Info{ TransmissionID: transmissionID.DeepCopy(), TransmissionSalt: transmissionSalt, TransmissionRSA: transmissionRsaKey, ReceptionID: receptionID.DeepCopy(), ReceptionSalt: receptionSalt, ReceptionRSA: receptionRsaKey, Precanned: false, E2eDhPrivateKey: e2eKey, E2eDhPublicKey: diffieHellman.GeneratePublicKey(e2eKey, e2e), } }