github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/gnovm/stdlibs/crypto/chacha20/rand/rand.gno (about) 1 package rand 2 3 import ( 4 "crypto/chacha20/chacha" 5 "encoding/binary" 6 "math" 7 "strconv" 8 ) 9 10 func erase(b []byte) { 11 // compiles to memclr 12 for i := range b { 13 b[i] = 0 14 } 15 } 16 17 func copyAndErase(dst, src []byte) int { 18 n := copy(dst, src) 19 erase(src[:n]) 20 return n 21 } 22 23 // An RNG is a cryptographically-strong RNG constructed from the ChaCha stream 24 // cipher. 25 type RNG struct { 26 buf []byte 27 n int 28 rounds int 29 } 30 31 // Read fills b with random data. It always returns len(b), nil. 32 // 33 // For performance reasons, calling Read once on a "large" buffer (larger than 34 // the RNG's internal buffer) will produce different output than calling Read 35 // multiple times on smaller buffers. If deterministic output is required, 36 // clients should call Read in a loop; when copying to an io.Writer, use 37 // io.CopyBuffer instead of io.Copy. Callers should also be aware that b is 38 // xored with random data, not directly overwritten; this means that the new 39 // contents of b depend on its previous contents. 40 func (r *RNG) Read(b []byte) (int, error) { 41 if len(b) <= len(r.buf[r.n:]) { 42 // can fill b entirely from buffer 43 r.n += copyAndErase(b, r.buf[r.n:]) 44 } else if len(b) <= len(r.buf[r.n:])+len(r.buf[chacha.KeySize:]) { 45 // b is larger than current buffer, but can be filled after a reseed 46 n := copy(b, r.buf[r.n:]) 47 chacha.XORKeyStream(r.buf, r.buf, make([]byte, chacha.NonceSize), r.buf[:chacha.KeySize], r.rounds) 48 r.n = chacha.KeySize + copyAndErase(b[n:], r.buf[chacha.KeySize:]) 49 } else { 50 // filling b would require multiple reseeds; instead, generate a 51 // temporary key, then write directly into b using that key 52 tmpKey := make([]byte, chacha.KeySize) 53 r.Read(tmpKey) 54 chacha.XORKeyStream(b, b, make([]byte, chacha.NonceSize), tmpKey, r.rounds) 55 erase(tmpKey) 56 } 57 return len(b), nil 58 } 59 60 // Bytes is a helper function that allocates and returns n bytes of random data. 61 func (r *RNG) Bytes(n int) []byte { 62 b := make([]byte, n) 63 r.Read(b) 64 return b 65 } 66 67 // Entropy256 is a helper function that returns 256 bits of random data. 68 func (r *RNG) Entropy256() (entropy [32]byte) { 69 r.Read(entropy[:]) 70 return 71 } 72 73 // Entropy192 is a helper function that returns 192 bits of random data. 74 func (r *RNG) Entropy192() (entropy [24]byte) { 75 r.Read(entropy[:]) 76 return 77 } 78 79 // Entropy128 is a helper function that returns 128 bits of random data. 80 func (r *RNG) Entropy128() (entropy [16]byte) { 81 r.Read(entropy[:]) 82 return 83 } 84 85 // Uint64n returns a uniform random uint64 in [0,n). It panics if n == 0. 86 func (r *RNG) Uint64n(n uint64) uint64 { 87 if n == 0 { 88 panic("frand: argument to Uint64n is 0") 89 } 90 // To eliminate modulo bias, keep selecting at random until we fall within 91 // a range that is evenly divisible by n. 92 // NOTE: since n is at most math.MaxUint64, max is minimized when: 93 // n = math.MaxUint64/2 + 1 -> max = math.MaxUint64 - math.MaxUint64/2 94 // This gives an expected 2 tries before choosing a value < max. 95 max := math.MaxUint64 - math.MaxUint64%n 96 b := make([]byte, 8) 97 again: 98 r.Read(b) 99 i := binary.LittleEndian.Uint64(b) 100 if i >= max { 101 goto again 102 } 103 return i % n 104 } 105 106 // Intn returns a uniform random int in [0,n). It panics if n <= 0. 107 func (r *RNG) Intn(n int) int { 108 if n <= 0 { 109 panic("frand: argument to Intn is <= 0: " + strconv.Itoa(n)) 110 } 111 // NOTE: since n is at most math.MaxUint64/2, max is minimized when: 112 // n = math.MaxUint64/4 + 1 -> max = math.MaxUint64 - math.MaxUint64/4 113 // This gives an expected 1.333 tries before choosing a value < max. 114 return int(r.Uint64n(uint64(n))) 115 } 116 117 /* XXX floats not supported 118 // Float64 returns a random float64 in [0,1). 119 func (r *RNG) Float64() float64 { 120 return float64(r.Uint64n(1<<53)) / (1 << 53) 121 } 122 */ 123 124 // Perm returns a random permutation of the integers [0,n). It panics if n < 0. 125 func (r *RNG) Perm(n int) []int { 126 m := make([]int, n) 127 for i := 1; i < n; i++ { 128 j := r.Intn(i + 1) 129 m[i] = m[j] 130 m[j] = i 131 } 132 return m 133 } 134 135 // Shuffle randomly permutes n elements by repeatedly calling swap in the range 136 // [0,n). It panics if n < 0. 137 func (r *RNG) Shuffle(n int, swap func(i, j int)) { 138 for i := n - 1; i > 0; i-- { 139 swap(i, r.Intn(i+1)) 140 } 141 } 142 143 // NewCustom returns a new RNG instance seeded with the provided entropy and 144 // using the specified buffer size and number of ChaCha rounds. It panics if 145 // len(seed) != 32, bufsize < 32, or rounds != 8, 12 or 20. 146 func NewCustom(seed []byte, bufsize int, rounds int) *RNG { 147 if len(seed) != chacha.KeySize { 148 panic("frand: invalid seed size") 149 } else if bufsize < chacha.KeySize { 150 panic("frand: bufsize must be at least 32") 151 } else if !(rounds == 8 || rounds == 12 || rounds == 20) { 152 panic("frand: rounds must be 8, 12, or 20") 153 } 154 buf := make([]byte, chacha.KeySize+bufsize) 155 chacha.XORKeyStream(buf, buf, make([]byte, chacha.NonceSize), seed, rounds) 156 return &RNG{ 157 buf: buf, 158 n: chacha.KeySize, 159 rounds: rounds, 160 } 161 } 162 163 func NewFromSeed(seed []byte) *RNG { 164 return NewCustom(seed, 1024, 12) 165 } 166 167 /* XXX masterRNG deleted to make package pure 168 // "master" RNG, seeded from crypto/rand; RNGs returned by New derive their seed 169 // from this RNG. This means we only ever need to read system entropy a single 170 // time, at startup. 171 var masterRNG = func() *RNG { 172 seed := make([]byte, chacha.KeySize) 173 n, err := rand.Read(seed) 174 if err != nil || n != len(seed) { 175 panic("not enough system entropy to seed master RNG") 176 } 177 return NewCustom(seed, 1024, 12) 178 }() 179 var masterMu sync.Mutex 180 181 // New returns a new RNG instance. The instance is seeded with entropy from 182 // crypto/rand, albeit indirectly; its seed is generated by a global "master" 183 // RNG, which itself is seeded from crypto/rand. This means the frand package 184 // only reads system entropy once, at startup. 185 func New() *RNG { 186 masterMu.Lock() 187 defer masterMu.Unlock() 188 return NewCustom(masterRNG.Bytes(32), 1024, 12) 189 } 190 191 // Global versions of each RNG method, leveraging a pool of RNGs. 192 193 var rngpool = sync.Pool{ 194 New: func() interface{} { 195 return New() 196 }, 197 } 198 199 // Read fills b with random data. It always returns len(b), nil. 200 func Read(b []byte) (int, error) { 201 r := rngpool.Get().(*RNG) 202 defer rngpool.Put(r) 203 return r.Read(b) 204 } 205 206 // Bytes is a helper function that allocates and returns n bytes of random data. 207 func Bytes(n int) []byte { 208 r := rngpool.Get().(*RNG) 209 defer rngpool.Put(r) 210 return r.Bytes(n) 211 } 212 213 // Entropy256 is a helper function that returns 256 bits of random data. 214 func Entropy256() [32]byte { 215 r := rngpool.Get().(*RNG) 216 defer rngpool.Put(r) 217 return r.Entropy256() 218 } 219 220 // Entropy192 is a helper function that returns 192 bits of random data. 221 func Entropy192() [24]byte { 222 r := rngpool.Get().(*RNG) 223 defer rngpool.Put(r) 224 return r.Entropy192() 225 } 226 227 // Entropy128 is a helper function that returns 128 bits of random data. 228 func Entropy128() [16]byte { 229 r := rngpool.Get().(*RNG) 230 defer rngpool.Put(r) 231 return r.Entropy128() 232 } 233 234 // Uint64n returns a uniform random uint64 in [0,n). It panics if n == 0. 235 func Uint64n(n uint64) uint64 { 236 r := rngpool.Get().(*RNG) 237 defer rngpool.Put(r) 238 return r.Uint64n(n) 239 } 240 241 // Intn returns a uniform random int in [0,n). It panics if n <= 0. 242 func Intn(n int) int { 243 r := rngpool.Get().(*RNG) 244 defer rngpool.Put(r) 245 return r.Intn(n) 246 } 247 248 // Float64 returns a random float64 in [0,1). 249 func Float64() float64 { 250 r := rngpool.Get().(*RNG) 251 defer rngpool.Put(r) 252 return r.Float64() 253 } 254 255 // Perm returns a random permutation of the integers [0,n). It panics if n < 0. 256 func Perm(n int) []int { 257 r := rngpool.Get().(*RNG) 258 defer rngpool.Put(r) 259 return r.Perm(n) 260 } 261 262 // Shuffle randomly permutes n elements by repeatedly calling swap in the range 263 // [0,n). It panics if n < 0. 264 func Shuffle(n int, swap func(i, j int)) { 265 r := rngpool.Get().(*RNG) 266 defer rngpool.Put(r) 267 r.Shuffle(n, swap) 268 } 269 270 // Reader is a global, shared instance of a cryptographically strong pseudo- 271 // random generator. Reader is safe for concurrent use by multiple goroutines. 272 var Reader rngReader 273 274 type rngReader struct{} 275 276 func (rngReader) Read(b []byte) (int, error) { return Read(b) } 277 278 */ 279 280 // A Source is a math/rand-compatible source of entropy. It is safe for 281 // concurrent use by multiple goroutines 282 type Source struct { 283 rng *RNG 284 // mu sync.Mutex XXX 285 } 286 287 // Seed uses the provided seed value to initialize the Source to a 288 // deterministic state. 289 func (s *Source) Seed(i int64) { 290 // s.mu.Lock() 291 // defer s.mu.Unlock() 292 seed := make([]byte, chacha.KeySize) 293 binary.LittleEndian.PutUint64(seed, uint64(i)) 294 for i := range s.rng.buf { 295 s.rng.buf[i] = 0 296 } 297 chacha.XORKeyStream(s.rng.buf, s.rng.buf, make([]byte, chacha.NonceSize), seed, s.rng.rounds) 298 s.rng.n = chacha.KeySize 299 } 300 301 // Int63 returns a non-negative random 63-bit integer as an int64. 302 func (s *Source) Int63() int64 { 303 // s.mu.Lock() 304 // defer s.mu.Unlock() 305 return int64(s.rng.Uint64n(1 << 63)) 306 } 307 308 // Uint64 returns a random 64-bit integer. 309 func (s *Source) Uint64() uint64 { 310 // s.mu.Lock() 311 // defer s.mu.Unlock() 312 return s.rng.Uint64n(math.MaxUint64) 313 } 314 315 // NewSource returns a source for use with the math/rand package. 316 func NewSourceFromSeed(seed []byte) *Source { return &Source{rng: NewFromSeed(seed)} }