diff --git a/README.md b/README.md index be0d2ef..14bf55a 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,18 @@ other bits.: rawkey := s.RawKey() ``` +### Generating new Safe Primes & Prime Field Generators +The SRP library uses a pre-calculated list of large safe prime for common widths +along wit their field generators. But, this is not advisable for large scale +production use. It is best that a separate background process be used to generate +safe primes & the corresponding field generators - and store them in some cache. +The function `findPrimeField()` can be modified to fetch from this cache. Depending +on the security stance, the cache can decide on a "use once" policy or +"use N times" policy. + +The function `srp.NewPrimeField()` generates and returns a new large safe prime +and its field generator. + ### Building SRP There is an example program that shows you the API usage (documented diff --git a/primefield/primefield.go b/example/primefield.go similarity index 100% rename from primefield/primefield.go rename to example/primefield.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cbd144d --- /dev/null +++ b/go.mod @@ -0,0 +1,6 @@ +module github.com/opencoff/go-srp + +require ( + golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 + golang.org/x/sys v0.0.0-20190412213103-97732733099d +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7f8e779 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/prime.go b/prime.go index 9199c51..c58651b 100644 --- a/prime.go +++ b/prime.go @@ -14,6 +14,21 @@ import ( "math/big" ) +// smallPrimes is a list of small, prime numbers that allows us to rapidly +// exclude some fraction of composite candidates when searching for a random +// prime. This list is truncated at the point where smallPrimesProduct exceeds +// a uint64. It does not include two because we ensure that the candidates are +// odd by construction. +var smallPrimes = []uint8{ + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, +} + +// smallPrimesProduct is the product of the values in smallPrimes and allows us +// to reduce a candidate prime by this number and then determine whether it's +// coprime to all the elements of smallPrimes without further big.Int +// operations. +var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365) + // safePrime generates a safe prime; i.e., a prime 'p' such that 2p+1 is also prime. func safePrime(bits int) (*big.Int, error) { @@ -43,7 +58,7 @@ func safePrime(bits int) (*big.Int, error) { // Prime will return error for any error returned by rand.Read or if bits < 2. func prime(bits int) (p *big.Int, err error) { if bits < 2 { - err = errors.New("crypto/rand: prime size must be at least 2-bit") + err = errors.New("srp: prime size must be at least 2-bit") return } @@ -55,6 +70,8 @@ func prime(bits int) (p *big.Int, err error) { bytes := make([]byte, (bits+7)/8) p = new(big.Int) + bigMod := new(big.Int) + for { _, err = io.ReadFull(rand.Reader, bytes) if err != nil { @@ -84,6 +101,30 @@ func prime(bits int) (p *big.Int, err error) { // * and setting bit 1 guarantees that (p-1)/2 will be odd. bytes[len(bytes)-1] |= 3 p.SetBytes(bytes) + + // Calculate the value mod the product of smallPrimes. If it's + // a multiple of any of these primes we add two until it isn't. + // The probability of overflowing is minimal and can be ignored + // because we still perform Miller-Rabin tests on the result. + bigMod.Mod(p, smallPrimesProduct) + mod := bigMod.Uint64() + + NextDelta: + for delta := uint64(0); delta < 1<<20; delta += 2 { + m := mod + delta + for _, prime := range smallPrimes { + if m%uint64(prime) == 0 && (bits > 6 || m != uint64(prime)) { + continue NextDelta + } + } + + if delta > 0 { + bigMod.SetUint64(delta) + p.Add(p, bigMod) + } + break + } + if p.ProbablyPrime(20) && p.BitLen() == bits { return } @@ -92,32 +133,35 @@ func prime(bits int) (p *big.Int, err error) { // Return true if g is a generator for safe prime p // -// Stinson and Paterson (Th. 6.8 pp 196): +// From Cryptography Theory & Practive, Stinson and Paterson (Th. 6.8 pp 196): // If p > 2 is a prime and g is in Zp*, then -// g is a generator modulo p iff g ^ (p-1)/q != 1 (mod p) +// g is a primitive element modulo p iff g ^ (p-1)/q != 1 (mod p) // for all primes q such that q divides (p-1). // -// Code added as a result of bug pointed out by Dharmalingam G. (May 2019) +// "Primitive Element" and "Generator" are the same thing in Number Theory. +// +// Code below added as a result of bug pointed out by Dharmalingam G. (May 2019) func isGenerator(g, p *big.Int) bool { p1 := big.NewInt(0).Sub(p, one) q := big.NewInt(0).Rsh(p1, 1) // q = p-1/2 = ((p-1) >> 1) // p is a safe prime. i.e., it is of the form 2q+1 where q is prime. - // p-1 = 2q, where q is a prime. // - // All factors that divide p-1 are: {2, q, 2q} + // => p-1 = 2q, where q is a prime. + // + // All factors of p-1 are: {2, q, 2q} // // So, our check really comes down to: - // 1) g ^ (p1/2q) != 1 mod p + // 1) g ^ (p-1/2q) != 1 mod p // => g ^ (2q/2q) != 1 mod p // => g != 1 mod p // Trivial case. We ignore this. // - // 2) g ^ (p1/2) != 1 mod p + // 2) g ^ (p-1/2) != 1 mod p // => g ^ (2q/2) != 1 mod p // => g ^ q != 1 mod p // - // 3) g ^ (p1/q) != 1 mod p + // 3) g ^ (p-1/q) != 1 mod p // => g ^ (2q/q) != 1 mod p // => g ^ 2 != 1 mod p // diff --git a/srp.go b/srp.go index 08e8610..a44c178 100644 --- a/srp.go +++ b/srp.go @@ -162,12 +162,9 @@ func New(bits int) (*SRP, error) { // 'bits' sized prime-field size. func NewWithHash(h crypto.Hash, bits int) (*SRP, error) { - // XXX In the future we have to asynchronously generate a list of prime+generator - // pairs in the background so we can pick one dynamically. For now, we use a small - // list of primes per bit - pf, ok := pflist[bits] - if !ok { - return nil, fmt.Errorf("srp: invalid prime-field size: %d", bits) + pf, err := findPrimeField(bits) + if err != nil { + return nil, err } s := &SRP{ @@ -611,6 +608,12 @@ func randBigInt(bits int) *big.Int { func NewPrimeField(nbits int) (p, g *big.Int, err error) { var pf *primeField + if nbits < 0 { + return nil, nil, fmt.Errorf("srp: bad field size %d", nbits) + } else if nbits == 0 { + nbits = 2048 + } + pf, err = newPrimeField(nbits) if err != nil { return nil, nil, err @@ -646,6 +649,27 @@ func newPrimeField(nbits int) (*primeField, error) { return nil, fmt.Errorf("can't find generator after 100 tries") } +// Find a pre-generated safe-prime and its generator from our list below. +// In the future, we can use some other external eternal source of such things. +// NB: Generating large safe-primes is computationally taxing! It is best done offline. +func findPrimeField(bits int) (*primeField, error) { + + switch { + case bits < 0: + return nil, fmt.Errorf("srp: invalid prime-field size %d", bits) + + case bits == 0: + bits = 2048 + fallthrough + + default: + if pf, ok := pflist[bits]; ok { + return pf, nil + } + return nil, fmt.Errorf("srp: invalid prime-field size %d", bits) + } +} + // build the database of prime fields and generators func init() {