mirror of https://github.com/golang/go.git
89 lines
1.8 KiB
Go
89 lines
1.8 KiB
Go
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Plan9 cryptographically secure pseudorandom number
|
|
// generator.
|
|
|
|
package rand
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"encoding/binary"
|
|
"io"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const randomDevice = "/dev/random"
|
|
|
|
func init() {
|
|
Reader = &reader{}
|
|
}
|
|
|
|
// reader is a new pseudorandom generator that seeds itself by
|
|
// reading from /dev/random. The Read method on the returned
|
|
// reader always returns the full amount asked for, or else it
|
|
// returns an error. The generator is a fast key erasure RNG.
|
|
type reader struct {
|
|
mu sync.Mutex
|
|
seeded sync.Once
|
|
seedErr error
|
|
key [32]byte
|
|
}
|
|
|
|
func (r *reader) Read(b []byte) (n int, err error) {
|
|
r.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 {
|
|
r.seedErr = err
|
|
return
|
|
}
|
|
defer entropy.Close()
|
|
_, r.seedErr = io.ReadFull(entropy, r.key[:])
|
|
})
|
|
if r.seedErr != nil {
|
|
return 0, r.seedErr
|
|
}
|
|
|
|
r.mu.Lock()
|
|
blockCipher, err := aes.NewCipher(r.key[:])
|
|
if err != nil {
|
|
r.mu.Unlock()
|
|
return 0, err
|
|
}
|
|
var (
|
|
counter uint64
|
|
block [aes.BlockSize]byte
|
|
)
|
|
inc := func() {
|
|
counter++
|
|
if counter == 0 {
|
|
panic("crypto/rand counter wrapped")
|
|
}
|
|
binary.LittleEndian.PutUint64(block[:], counter)
|
|
}
|
|
blockCipher.Encrypt(r.key[:aes.BlockSize], block[:])
|
|
inc()
|
|
blockCipher.Encrypt(r.key[aes.BlockSize:], block[:])
|
|
inc()
|
|
r.mu.Unlock()
|
|
|
|
n = len(b)
|
|
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 n, nil
|
|
}
|