summaryrefslogtreecommitdiffstats
path: root/src/crypto/rand/rand_plan9.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto/rand/rand_plan9.go')
-rw-r--r--src/crypto/rand/rand_plan9.go87
1 files changed, 87 insertions, 0 deletions
diff --git a/src/crypto/rand/rand_plan9.go b/src/crypto/rand/rand_plan9.go
new file mode 100644
index 0000000..5d0af09
--- /dev/null
+++ b/src/crypto/rand/rand_plan9.go
@@ -0,0 +1,87 @@
+// 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
+ }
+ _, 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
+}