package rand
import (
)
const urandomDevice = "/dev/urandom"
func () {
if runtime.GOOS == "plan9" {
Reader = newReader(nil)
} else {
Reader = &devReader{name: urandomDevice}
}
}
type devReader struct {
name string
f io.Reader
mu sync.Mutex
used int32
}
var altGetRandom func([]byte) (ok bool)
func () {
println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
}
func ( *devReader) ( []byte) ( int, error) {
if atomic.CompareAndSwapInt32(&.used, 0, 1) {
:= time.AfterFunc(60*time.Second, warnBlocked)
defer .Stop()
}
if altGetRandom != nil && .name == urandomDevice && altGetRandom() {
return len(), nil
}
.mu.Lock()
defer .mu.Unlock()
if .f == nil {
, := os.Open(.name)
if == nil {
return 0,
}
if runtime.GOOS == "plan9" {
.f =
} else {
.f = bufio.NewReader(hideAgainReader{})
}
}
return .f.Read()
}
var isEAGAIN func(error) bool
type hideAgainReader struct {
r io.Reader
}
func ( hideAgainReader) ( []byte) ( int, error) {
, = .r.Read()
if != nil && isEAGAIN != nil && isEAGAIN() {
= nil
}
return
}
func ( io.Reader) io.Reader {
if == nil {
= &devReader{name: "/dev/random"}
}
return &reader{entropy: }
}
type reader struct {
mu sync.Mutex
budget int
cipher cipher.Block
entropy io.Reader
time, seed, dst, key [aes.BlockSize]byte
}
func ( *reader) ( []byte) ( int, error) {
.mu.Lock()
defer .mu.Unlock()
= len()
for len() > 0 {
if .budget == 0 {
, := io.ReadFull(.entropy, .seed[0:])
if != nil {
return - len(),
}
_, = io.ReadFull(.entropy, .key[0:])
if != nil {
return - len(),
}
.cipher, = aes.NewCipher(.key[0:])
if != nil {
return - len(),
}
.budget = 1 << 20
}
.budget -= aes.BlockSize
:= time.Now().UnixNano()
binary.BigEndian.PutUint64(.time[:], uint64())
.cipher.Encrypt(.time[0:], .time[0:])
for := 0; < aes.BlockSize; ++ {
.dst[] = .time[] ^ .seed[]
}
.cipher.Encrypt(.dst[0:], .dst[0:])
for := 0; < aes.BlockSize; ++ {
.seed[] = .time[] ^ .dst[]
}
.cipher.Encrypt(.seed[0:], .seed[0:])
:= copy(, .dst[0:])
= [:]
}
return , nil
}