Source file src/crypto/rand/rand_plan9.go

     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  
     8  package rand
     9  
    10  import (
    11  	"crypto/aes"
    12  	"encoding/binary"
    13  	"io"
    14  	"os"
    15  	"sync"
    16  	"time"
    17  )
    18  
    19  const randomDevice = "/dev/random"
    20  
    21  func 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.
    29  type reader struct {
    30  	mu      sync.Mutex
    31  	seeded  sync.Once
    32  	seedErr error
    33  	key     [32]byte
    34  }
    35  
    36  func (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  		binary.LittleEndian.PutUint64(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  

View as plain text