Skip to content
Snippets Groups Projects
Select Git revision
  • d6f65882812e2e48432512a27cc17e28fc54efe0
  • main default protected
  • development
  • integration
  • v1.1.5
  • v1.1.4
  • v1.1.3
  • v1.1.2
  • v1.1.1
  • v1.1.0
  • v1.0.0
11 results

E2EAddService.swift

Blame
  • user.go 6.13 KiB
    ///////////////////////////////////////////////////////////////////////////////
    // 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 (
    	"gitlab.com/elixxir/crypto/diffieHellman"
    	"regexp"
    	"runtime"
    	"strings"
    	"sync"
    
    	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)
    
    	// Salt, UID, etc gen
    	stream := rng.GetStream()
    	transmissionSalt = make([]byte, SaltSize)
    
    	n, err := stream.Read(transmissionSalt)
    
    	if err != nil {
    		jww.FATAL.Panicf(err.Error())
    	}
    	if n != SaltSize {
    		jww.FATAL.Panicf("transmissionSalt size too small: %d", n)
    	}
    
    	receptionSalt = make([]byte, SaltSize)
    
    	n, err = stream.Read(receptionSalt)
    
    	if err != nil {
    		jww.FATAL.Panicf(err.Error())
    	}
    	if n != SaltSize {
    		jww.FATAL.Panicf("transmissionSalt size too small: %d", n)
    	}
    
    	stream.Close()
    
    	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(1)
    
    	// RSA Keygen (4096 bit defaults)
    	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())
    		}
    	}()
    	wg.Wait()
    
    	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),
    	}
    }