/* * Copyright (c) 2012-2020 MIRACL UK Ltd. * * This file is part of MIRACL Core * (see https://github.com/miracl/core). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Cryptographic strong random number generator * * Unguessable seed -> SHA -> PRNG internal state -> SHA -> random numbers * Slow - but secure * * See ftp://ftp.rsasecurity.com/pub/pdfs/bull-1.pdf for a justification */ /* Marsaglia & Zaman Random number generator constants */ package core //import "fmt" const rand_NK int = 21 const rand_NJ int = 6 const rand_NV int = 8 type RAND struct { ira [rand_NK]uint32 /* random number... */ rndptr int borrow uint32 pool_ptr int pool [32]byte } /* Terminate and clean up */ func (R *RAND) Clean() { /* kill internal state */ R.pool_ptr = 0 R.rndptr = 0 for i := 0; i < 32; i++ { R.pool[i] = 0 } for i := 0; i < rand_NK; i++ { R.ira[i] = 0 } R.borrow = 0 } func NewRAND() *RAND { R := new(RAND) R.Clean() return R } func (R *RAND) sbrand() uint32 { /* Marsaglia & Zaman random number generator */ R.rndptr++ if R.rndptr < rand_NK { return R.ira[R.rndptr] } R.rndptr = 0 k := rand_NK - rand_NJ for i := 0; i < rand_NK; i++ { /* calculate next NK values */ if k == rand_NK { k = 0 } t := R.ira[k] pdiff := t - R.ira[i] - R.borrow if pdiff < t { R.borrow = 0 } if pdiff > t { R.borrow = 1 } R.ira[i] = pdiff k++ } return R.ira[0] } func (R *RAND) sirand(seed uint32) { var m uint32 = 1 R.borrow = 0 R.rndptr = 0 R.ira[0] ^= seed for i := 1; i < rand_NK; i++ { /* fill initialisation vector */ in := (rand_NV * i) % rand_NK R.ira[in] ^= m /* note XOR */ t := m m = seed - m seed = t } for i := 0; i < 10000; i++ { R.sbrand() } /* "warm-up" & stir the generator */ } func (R *RAND) fill_pool() { sh := NewHASH256() for i := 0; i < 128; i++ { sh.Process(byte(R.sbrand() & 0xff)) } W := sh.Hash() for i := 0; i < 32; i++ { R.pool[i] = W[i] } R.pool_ptr = 0 } func pack(b [4]byte) uint32 { /* pack 4 bytes into a 32-bit Word */ return (((uint32(b[3])) & 0xff) << 24) | ((uint32(b[2]) & 0xff) << 16) | ((uint32(b[1]) & 0xff) << 8) | (uint32(b[0]) & 0xff) } /* Initialize RNG with some real entropy from some external source */ func (R *RAND) Seed(rawlen int, raw []byte) { /* initialise from at least 128 byte string of raw random entropy */ var b [4]byte sh := NewHASH256() R.pool_ptr = 0 for i := 0; i < rand_NK; i++ { R.ira[i] = 0 } if rawlen > 0 { for i := 0; i < rawlen; i++ { sh.Process(raw[i]) } digest := sh.Hash() /* initialise PRNG from distilled randomness */ for i := 0; i < 8; i++ { b[0] = digest[4*i] b[1] = digest[4*i+1] b[2] = digest[4*i+2] b[3] = digest[4*i+3] R.sirand(pack(b)) } } R.fill_pool() } /* get random byte */ func (R *RAND) GetByte() byte { r := R.pool[R.pool_ptr] R.pool_ptr++ if R.pool_ptr >= 32 { R.fill_pool() } return byte(r & 0xff) } /* test main program */ /* func main() { var raw [100]byte rng:=NewRAND() rng.Clean() for i:=0;i<100;i++ {raw[i]=byte(i)} rng.Seed(100,raw[:]) for i:=0;i<1000;i++ { fmt.Printf("%03d ",rng.GetByte()) } } */