github.com/ronperry/cryptoedge@v0.0.0-20150815114006-cc363e290743/csprng/bbs/impl.go (about)

     1  // Package bbs implements the Blum-Blum-Shub pseudo-random number generator. The implementation should be considered
     2  // to be insecure.
     3  // x(n+1)=x(n)^2 mod (p*q).  p,q are large primes,
     4  // gcd(φ(p − 1), φ(q − 1)) should be small. p and q, should both be congruent to 3 (mod 4)
     5  // initial seed xn should be neither 1 or 0, and not divisible by p or q.
     6  // xi = x0^(2^i mod lcm(p-1,q-1)) mod p*q
     7  package bbs
     8  
     9  import (
    10  	"crypto/rand"
    11  	"io"
    12  	"math"
    13  	"math/big"
    14  	"sync"
    15  )
    16  
    17  var (
    18  	// Some  big.Int constants
    19  	zero  = big.NewInt(0)
    20  	one   = big.NewInt(1)
    21  	two   = big.NewInt(2)
    22  	three = big.NewInt(3)
    23  	four  = big.NewInt(4)
    24  	ceil  = big.NewInt(3) // That's the smallest GCD possible
    25  )
    26  
    27  // Reader is a conveniance reader
    28  var Reader io.Reader
    29  
    30  // Rand is the upstream random source for initialisation
    31  var Rand = rand.Reader
    32  
    33  func init() {
    34  	// Set up a small/less secure global random source
    35  	b := New(Params(128, 0))
    36  	Reader = b
    37  }
    38  
    39  // BBS contains the state of a Blum-Blum-Shub
    40  type BBS struct {
    41  	xn      *big.Int // last x
    42  	X0      *big.Int // x at 0
    43  	M       *big.Int // M = p*q
    44  	L       *big.Int // lcm(p-1,q-1)
    45  	rlock   *sync.Mutex
    46  	Step    int64
    47  	Maxbits int // log (bits M)
    48  }
    49  
    50  func lcmMinusOne(a, b *big.Int) *big.Int {
    51  	a1 := new(big.Int).Sub(a, one)
    52  	b1 := new(big.Int).Sub(b, one)
    53  	mul := new(big.Int).Mul(a1, b1)
    54  	gcd := new(big.Int).GCD(nil, nil, a1, b1)
    55  	return new(big.Int).Div(mul, gcd)
    56  }
    57  
    58  // return a likely prime that is congruent 3 mod 4
    59  func getPrime(bits int) *big.Int {
    60  	var err error
    61  	p := new(big.Int)
    62  	for {
    63  		p, err = rand.Prime(Rand, bits)
    64  		if err != nil {
    65  			panic("rand reader failed")
    66  		}
    67  		p3 := new(big.Int).Mod(new(big.Int).Sub(p, three), four)
    68  		if p3.Cmp(zero) == 0 {
    69  			return p
    70  		}
    71  	}
    72  }
    73  
    74  func calcX(p, q *big.Int) *big.Int {
    75  	var err error
    76  	var x *big.Int
    77  	max := q
    78  	if p.Cmp(q) > 0 {
    79  		max = p
    80  	}
    81  	for {
    82  		x, err = rand.Int(Rand, max)
    83  		if err != nil {
    84  			panic("rand reader failed")
    85  		}
    86  		if x.Cmp(one) == 0 || x.Cmp(zero) == 0 {
    87  			continue
    88  		}
    89  		if new(big.Int).Div(x, p).Cmp(zero) > 0 {
    90  			continue
    91  		}
    92  		if new(big.Int).Div(x, q).Cmp(zero) > 0 {
    93  			continue
    94  		}
    95  		return x
    96  	}
    97  }
    98  
    99  // Params generates new BBS params. bits is the number of bits that initial values should have (the more the better),
   100  // step is the step to which the RNG should jump when using it like this: New(Params(bits,lastStep))
   101  func Params(bits int, lastStep int64) (p, q, x *big.Int, step int64) {
   102  	p, q = new(big.Int), new(big.Int)
   103  	p = getPrime(bits)
   104  	for {
   105  		q = getPrime(bits)
   106  		p1 := new(big.Int).Sub(p, one)
   107  		q1 := new(big.Int).Sub(q, one)
   108  		gcd := new(big.Int).GCD(nil, nil, p1, q1)
   109  		if gcd.Cmp(ceil) == -1 { // that's almost not necessary
   110  			break
   111  		}
   112  	}
   113  	x = calcX(p, q)
   114  	return p, q, x, lastStep
   115  }
   116  
   117  // New sets up a new BBS
   118  func New(p, q, x *big.Int, step int64) *BBS {
   119  	bbs := new(BBS)
   120  	bbs.X0 = x
   121  	bbs.M = new(big.Int).Mul(p, q)
   122  	bbs.L = lcmMinusOne(p, q)
   123  	bbs.Maxbits = int(math.Log(float64(bbs.M.BitLen())))
   124  	mbits := bbs.Maxbits % 8
   125  	mbytes := bbs.Maxbits / 8
   126  	if mbits > 0 {
   127  		mbytes++
   128  	}
   129  	bbs.Step = int64(
   130  		math.Pow(float64(p.BitLen()), 2)*
   131  			math.Pow(float64(q.BitLen()), 2)*
   132  			math.Pow(float64(x.BitLen()), 2)) / int64((mbytes*8*2)/bbs.Maxbits)
   133  
   134  	bbs.rlock = new(sync.Mutex)
   135  	if step > 0 {
   136  		bbs.BytesAt(bbs.Step-step, 1)
   137  	}
   138  	return bbs
   139  }
   140  
   141  func (bbs *BBS) step() {
   142  	var x *big.Int
   143  	if bbs.Step == 1 {
   144  		panic("RNG Exhausted!!!")
   145  	}
   146  	bbs.Step--
   147  	x = bbs.xn
   148  	if bbs.xn == nil {
   149  		x = bbs.X0
   150  	}
   151  	bbs.xn = new(big.Int).Exp(x, two, bbs.M)
   152  }
   153  
   154  // bits returns Maxbits next random bits, rounded up to the next byte
   155  func (bbs *BBS) bits() []byte {
   156  	bbs.step()
   157  	fullBytes := bbs.Maxbits / 8
   158  	missingBits := bbs.Maxbits % 8
   159  	if missingBits > 0 {
   160  		fullBytes++
   161  	}
   162  	d := bbs.xn.Bytes()
   163  	if len(d) < fullBytes {
   164  		fullBytes = len(d)
   165  	}
   166  	dn := d[len(d)-fullBytes:]
   167  	return dn
   168  }
   169  
   170  // Bytes returns n bytes of random data from the generator
   171  func (bbs *BBS) Bytes(n int) []byte {
   172  	bbs.rlock.Lock()
   173  	defer bbs.rlock.Unlock()
   174  	return bbs.bytes(n)
   175  }
   176  
   177  func (bbs *BBS) bytes(n int) []byte {
   178  	ret := make([]byte, 0, n)
   179  	for {
   180  		x := bbs.bits()
   181  		ret = append(ret, x...)
   182  		if len(ret) >= n {
   183  			break
   184  		}
   185  	}
   186  	return ret[:n]
   187  }
   188  
   189  // BytesAt returns m bytes from position xn. This moves the whole generator to n.
   190  func (bbs *BBS) BytesAt(n int64, m int) []byte {
   191  	// xi = x0^(2^i mod lcm(p-1,q-1)) mod p*q
   192  	bbs.rlock.Lock()
   193  	defer bbs.rlock.Unlock()
   194  	pos := new(big.Int).Exp(two, big.NewInt(n), bbs.L)
   195  	bbs.xn = new(big.Int).Exp(bbs.X0, pos, bbs.M)
   196  	b := bbs.bytes(m)
   197  	return b
   198  }
   199  
   200  // Read implements BBS as io.Reader as drop-in RNG
   201  func (bbs *BBS) Read(p []byte) (n int, err error) {
   202  	l := len(p)
   203  	copy(p, bbs.Bytes(l))
   204  	return l, nil
   205  }