{"record":{"id":"60aff85606a9a00f","repo":"ethereum/go-ethereum","slug":"key-generation-could-not-read-from-random-source","errorCode":null,"errorMessage":"key generation: could not read from random source: ","messagePattern":"key generation: could not read from random source: ","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"accounts/keystore/key.go","lineNumber":152,"sourceCode":"\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"Could not create random uuid: %v\", err))\n\t}\n\tkey := &Key{\n\t\tId:         id,\n\t\tAddress:    crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),\n\t\tPrivateKey: privateKeyECDSA,\n\t}\n\treturn key\n}\n\n// NewKeyForDirectICAP generates a key whose address fits into < 155 bits so it can fit\n// into the Direct ICAP spec. for simplicity and easier compatibility with other libs, we\n// retry until the first byte is 0.\nfunc NewKeyForDirectICAP(rand io.Reader) *Key {\n\trandBytes := make([]byte, 64)\n\t_, err := rand.Read(randBytes)\n\tif err != nil {\n\t\tpanic(\"key generation: could not read from random source: \" + err.Error())\n\t}\n\treader := bytes.NewReader(randBytes)\n\tprivateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader)\n\tif err != nil {\n\t\tpanic(\"key generation: ecdsa.GenerateKey failed: \" + err.Error())\n\t}\n\tkey := newKeyFromECDSA(privateKeyECDSA)\n\tif !strings.HasPrefix(key.Address.Hex(), \"0x00\") {\n\t\treturn NewKeyForDirectICAP(rand)\n\t}\n\treturn key\n}\n\nfunc newKey(rand io.Reader) (*Key, error) {\n\tprivateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand)\n\tif err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/ethereum/go-ethereum/blob/6bb0588ad8e7f922e4ad5580f51265a4097af08f/accounts/keystore/key.go#L134-L170","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nk := keystore.NewKeyForDirectICAP(brokenReader) // panics on read error\n\n// after\nimport \"crypto/rand\"\nk := keystore.NewKeyForDirectICAP(rand.Reader)","handlingStrategy":"validation","validationCode":"func healthyRand() (io.Reader, error) {\n\tbuf := make([]byte, 64)\n\tif _, err := rand.Read(buf); err != nil { // crypto/rand\n\t\treturn nil, fmt.Errorf(\"entropy source unavailable: %w\", err)\n\t}\n\treturn rand.Reader, nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["go-ethereum","keystore","entropy","icap","panic"],"backgroundTag":null,"analyzedSha":"6bb0588ad8e7f922e4ad5580f51265a4097af08f","analyzedAt":"2026-08-15T10:06:53.996Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}