package rand
import (
- "crypto/aes"
"internal/byteorder"
+ "internal/chacha8rand"
"io"
"os"
"sync"
- "time"
)
const randomDevice = "/dev/random"
// This is a pseudorandom generator that seeds itself by reading from
// /dev/random. The read function always returns the full amount asked for, or
-// else it returns an error. The generator is a fast key erasure RNG.
+// else it returns an error.
var (
mu sync.Mutex
seeded sync.Once
seedErr error
- key [32]byte
+ state chacha8rand.State
)
func read(b []byte) error {
seeded.Do(func() {
- t := time.AfterFunc(time.Minute, func() {
- println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
- })
- defer t.Stop()
entropy, err := os.Open(randomDevice)
if err != nil {
seedErr = err
return
}
defer entropy.Close()
- _, seedErr = io.ReadFull(entropy, key[:])
+ var seed [32]byte
+ _, err = io.ReadFull(entropy, seed[:])
+ if err != nil {
+ seedErr = err
+ return
+ }
+ state.Init(seed)
})
if seedErr != nil {
return seedErr
}
mu.Lock()
- blockCipher, err := aes.NewCipher(key[:])
- if err != nil {
- mu.Unlock()
- return err
+ defer mu.Unlock()
+
+ for len(b) >= 8 {
+ if x, ok := state.Next(); ok {
+ byteorder.BePutUint64(b, x)
+ b = b[8:]
+ } else {
+ state.Refill()
+ }
}
- var (
- counter uint64
- block [aes.BlockSize]byte
- )
- inc := func() {
- counter++
- if counter == 0 {
- panic("crypto/rand counter wrapped")
+ for len(b) > 0 {
+ if x, ok := state.Next(); ok {
+ var buf [8]byte
+ byteorder.BePutUint64(buf[:], x)
+ n := copy(b, buf[:])
+ b = b[n:]
+ } else {
+ state.Refill()
}
- byteorder.LePutUint64(block[:], counter)
}
- blockCipher.Encrypt(key[:aes.BlockSize], block[:])
- inc()
- blockCipher.Encrypt(key[aes.BlockSize:], block[:])
- inc()
- mu.Unlock()
+ state.Reseed()
- for len(b) >= aes.BlockSize {
- blockCipher.Encrypt(b[:aes.BlockSize], block[:])
- inc()
- b = b[aes.BlockSize:]
- }
- if len(b) > 0 {
- blockCipher.Encrypt(block[:], block[:])
- copy(b, block[:])
- }
return nil
}