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

  1. Pass crypto/rand.Reader (a working OS entropy source) as the rand argument.
  2. Fix the environment: mount /dev/urandom in containers, add an RNG device to the VM.
  3. For deterministic tests use a reader backed by a sufficiently large fixed buffer, never a failing one.
  4. 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

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


AI-assisted analysis of ethereum/go-ethereum@6bb0588ad8 (2026-08-15). Data as JSON: /api/errors/60aff85606a9a00f. Report an issue: GitHub.