crypto/rand: replace crypto/aes with internal/chacha8rand for plan9

We will need to import this functionality from the FIPS module, and we
can't import AES from there. Plan 9 is not going to be FIPS validated
anyway, so we can use non-approved cryptography.

For #69536

Change-Id: I7921ec0829b576de2e80f3a7d0a9a776ff387684
Reviewed-on: https://go-review.googlesource.com/c/go/+/624975
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
Filippo Valsorda 2024-11-04 13:05:46 +01:00 committed by Gopher Robot
parent c4aea46784
commit f705cf8f96
1 changed files with 29 additions and 37 deletions

View File

@ -5,76 +5,68 @@
package rand
import (
"crypto/aes"
"internal/byteorder"
"internal/chacha8rand"
"io"
"os"
"sync"
"time"
)
const randomDevice = "/dev/random"
// This is a pseudorandom generator that seeds itself by reading from
// /dev/random. The read function always returns the full amount asked for, or
// else it returns an error. The generator is a fast key erasure RNG.
// else it returns an error.
var (
mu sync.Mutex
seeded sync.Once
seedErr error
key [32]byte
state chacha8rand.State
)
func read(b []byte) error {
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 {
seedErr = err
return
}
defer entropy.Close()
_, seedErr = io.ReadFull(entropy, key[:])
var seed [32]byte
_, err = io.ReadFull(entropy, seed[:])
if err != nil {
seedErr = err
return
}
state.Init(seed)
})
if seedErr != nil {
return seedErr
}
mu.Lock()
blockCipher, err := aes.NewCipher(key[:])
if err != nil {
mu.Unlock()
return err
}
var (
counter uint64
block [aes.BlockSize]byte
)
inc := func() {
counter++
if counter == 0 {
panic("crypto/rand counter wrapped")
}
byteorder.LePutUint64(block[:], counter)
}
blockCipher.Encrypt(key[:aes.BlockSize], block[:])
inc()
blockCipher.Encrypt(key[aes.BlockSize:], block[:])
inc()
mu.Unlock()
defer mu.Unlock()
for len(b) >= aes.BlockSize {
blockCipher.Encrypt(b[:aes.BlockSize], block[:])
inc()
b = b[aes.BlockSize:]
for len(b) >= 8 {
if x, ok := state.Next(); ok {
byteorder.BePutUint64(b, x)
b = b[8:]
} else {
state.Refill()
}
}
if len(b) > 0 {
blockCipher.Encrypt(block[:], block[:])
copy(b, block[:])
for len(b) > 0 {
if x, ok := state.Next(); ok {
var buf [8]byte
byteorder.BePutUint64(buf[:], x)
n := copy(b, buf[:])
b = b[n:]
} else {
state.Refill()
}
}
state.Reseed()
return nil
}