1// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Plan9 cryptographically secure pseudorandom number
6// generator.
7
8package rand
9
10import (
11	"crypto/aes"
12	"internal/byteorder"
13	"io"
14	"os"
15	"sync"
16	"time"
17)
18
19const randomDevice = "/dev/random"
20
21func init() {
22	Reader = &reader{}
23}
24
25// reader is a new pseudorandom generator that seeds itself by
26// reading from /dev/random. The Read method on the returned
27// reader always returns the full amount asked for, or else it
28// returns an error. The generator is a fast key erasure RNG.
29type reader struct {
30	mu      sync.Mutex
31	seeded  sync.Once
32	seedErr error
33	key     [32]byte
34}
35
36func (r *reader) Read(b []byte) (n int, err error) {
37	r.seeded.Do(func() {
38		t := time.AfterFunc(time.Minute, func() {
39			println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
40		})
41		defer t.Stop()
42		entropy, err := os.Open(randomDevice)
43		if err != nil {
44			r.seedErr = err
45			return
46		}
47		defer entropy.Close()
48		_, r.seedErr = io.ReadFull(entropy, r.key[:])
49	})
50	if r.seedErr != nil {
51		return 0, r.seedErr
52	}
53
54	r.mu.Lock()
55	blockCipher, err := aes.NewCipher(r.key[:])
56	if err != nil {
57		r.mu.Unlock()
58		return 0, err
59	}
60	var (
61		counter uint64
62		block   [aes.BlockSize]byte
63	)
64	inc := func() {
65		counter++
66		if counter == 0 {
67			panic("crypto/rand counter wrapped")
68		}
69		byteorder.LePutUint64(block[:], counter)
70	}
71	blockCipher.Encrypt(r.key[:aes.BlockSize], block[:])
72	inc()
73	blockCipher.Encrypt(r.key[aes.BlockSize:], block[:])
74	inc()
75	r.mu.Unlock()
76
77	n = len(b)
78	for len(b) >= aes.BlockSize {
79		blockCipher.Encrypt(b[:aes.BlockSize], block[:])
80		inc()
81		b = b[aes.BlockSize:]
82	}
83	if len(b) > 0 {
84		blockCipher.Encrypt(block[:], block[:])
85		copy(b, block[:])
86	}
87	return n, nil
88}
89