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 }