Skip to content

Commit

Permalink
Added go.mod; clarified comments and added some error checking.
Browse files Browse the repository at this point in the history
  • Loading branch information
opencoff committed Jun 1, 2019
1 parent 2220f1e commit dac0c42
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 15 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
File renamed without changes.
6 changes: 6 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -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
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
62 changes: 53 additions & 9 deletions prime.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Expand Down Expand Up @@ -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
}

Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
Expand All @@ -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
//
Expand Down
36 changes: 30 additions & 6 deletions srp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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() {

Expand Down

0 comments on commit dac0c42

Please sign in to comment.