ethereum/go-ethereum · critical
key generation: could not read from random source:
Error message
key generation: could not read from random source:
What it means
NewKeyForDirectICAP reads 64 random bytes from the supplied io.Reader to seed secp256k1 key generation. If rand.Read returns an error it panics with 'key generation: could not read from random source: ' plus the cause. The function recursively retries until the derived address starts with 0x00 (fits direct ICAP), so it consumes randomness repeatedly and needs a healthy source.
Source
Thrown at accounts/keystore/key.go:152
if err != nil {
panic(fmt.Sprintf("Could not create random uuid: %v", err))
}
key := &Key{
Id: id,
Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
PrivateKey: privateKeyECDSA,
}
return key
}
// NewKeyForDirectICAP generates a key whose address fits into < 155 bits so it can fit
// into the Direct ICAP spec. for simplicity and easier compatibility with other libs, we
// retry until the first byte is 0.
func NewKeyForDirectICAP(rand io.Reader) *Key {
randBytes := make([]byte, 64)
_, err := rand.Read(randBytes)
if err != nil {
panic("key generation: could not read from random source: " + err.Error())
}
reader := bytes.NewReader(randBytes)
privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader)
if err != nil {
panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
}
key := newKeyFromECDSA(privateKeyECDSA)
if !strings.HasPrefix(key.Address.Hex(), "0x00") {
return NewKeyForDirectICAP(rand)
}
return key
}
func newKey(rand io.Reader) (*Key, error) {
privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand)
if err != nil {
return nil, err
}View on GitHub (pinned to 6bb0588ad8)
Solutions
- Pass crypto/rand.Reader (a working OS entropy source) as the rand argument.
- Fix the environment: mount /dev/urandom in containers, add an RNG device to the VM.
- For deterministic tests use a reader backed by a sufficiently large fixed buffer, never a failing one.
- Inspect err.Error() in the panic to pinpoint the blocked source.
Example fix
// before k := keystore.NewKeyForDirectICAP(brokenReader) // panics on read error // after import "crypto/rand" k := keystore.NewKeyForDirectICAP(rand.Reader)
Defensive patterns
Strategy: validation
Validate before calling
func healthyRand() (io.Reader, error) {
buf := make([]byte, 64)
if _, err := rand.Read(buf); err != nil { // crypto/rand
return nil, fmt.Errorf("entropy source unavailable: %w", err)
}
return rand.Reader, nil
} Prevention
- Always pass crypto/rand.Reader to key generation APIs.
- Smoke-test the entropy source at process startup in restricted environments.
- Never stub randomness in production paths.
When it happens
Trigger: Calling NewKeyForDirectICAP with a reader that fails: crypto/rand.Reader blocked or broken in the environment, a custom io.Reader that returns an error or too few bytes, or rand being an exhausted PRNG in tests.
Common situations: Containers/VMs with unavailable /dev/urandom, passing a zero-value reader or a closed file as rand in code or tests, hardened sandboxes blocking getrandom(2).
Related errors
- Could not create random uuid: %v
- key generation: ecdsa.GenerateKey failed:
- reading from crypto/rand failed:
- nil chainID
- error decoding contract deployer hex %s: %v
AI-assisted analysis of ethereum/go-ethereum@6bb0588ad8 (2026-08-15).
Data as JSON: /api/errors/60aff85606a9a00f.
Report an issue: GitHub.