{"record":{"id":"5e7b40204f06ad7c","repo":"golang/go","slug":"crypto-ecdh-only-crypto-rand-reader-is-allowed-in","errorCode":null,"errorMessage":"crypto/ecdh: only crypto/rand.Reader is allowed in FIPS 140-only mode","messagePattern":"crypto/ecdh: only crypto/rand\\.Reader is allowed in FIPS 140-only mode","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/ecdh/nist.go","lineNumber":51,"sourceCode":"\t\t\treturn nil, err\n\t\t}\n\t\tpub, err := key.PublicKey()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tk := &PrivateKey{\n\t\t\tcurve:      c,\n\t\t\tprivateKey: bytes,\n\t\t\tpublicKey:  &PublicKey{curve: c, publicKey: pub.Bytes(), boring: pub},\n\t\t\tboring:     key,\n\t\t}\n\t\treturn k, nil\n\t}\n\n\tr = rand.CustomReader(r)\n\n\tif fips140only.Enforced() && !fips140only.ApprovedRandomReader(r) {\n\t\treturn nil, errors.New(\"crypto/ecdh: only crypto/rand.Reader is allowed in FIPS 140-only mode\")\n\t}\n\n\tprivateKey, err := c.generate(r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tk := &PrivateKey{\n\t\tcurve:      c,\n\t\tprivateKey: privateKey.Bytes(),\n\t\tfips:       privateKey,\n\t\tpublicKey: &PublicKey{\n\t\t\tcurve:     c,\n\t\t\tpublicKey: privateKey.PublicKey().Bytes(),\n\t\t\tfips:      privateKey.PublicKey(),\n\t\t},\n\t}\n\tif boring.Enabled {","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/ecdh/nist.go#L33-L69","documentation":"In FIPS 140-only mode, NIST-curve ECDH key generation must use crypto/rand.Reader specifically; any other io.Reader is rejected as a non-approved randomness source. The check runs after wiring the reader through rand.CustomReader, comparing against fips140only.ApprovedRandomReader.","triggerScenarios":"Calling ecdh.P256().GenerateKey(customReader) (or P384/P521) in a FIPS-only binary where customReader is not crypto/rand.Reader, e.g. a deterministic test reader or a custom entropy pool.","commonSituations":"Passing a seeded test reader in FIPS builds; using a hardware RNG wrapper that is not crypto/rand.Reader; code that abstracted randomness behind an interface.","solutions":["Pass crypto/rand.Reader (or nil where the API defaults to it) in FIPS builds.","For tests, use testing/cryptotest.SetGlobalRandom instead of injecting a custom reader.","Remove custom reader indirection in production FIPS paths."],"exampleFix":"// before\npriv, err := curve.GenerateKey(myTestReader) // fails in FIPS mode\n// after\npriv, err := curve.GenerateKey(rand.Reader)","handlingStrategy":"validation","validationCode":"func genECDH(curve ecdh.Curve, r io.Reader) (*ecdh.PrivateKey, error) {\n    if r == nil || (fipsEnabled() && !isApprovedReader(r)) {\n        r = cryptoRand.Reader\n    }\n    return curve.GenerateKey(r)\n}","typeGuard":"func isApprovedReader(r io.Reader) bool { return r == cryptoRand.Reader }","tryCatchPattern":"priv, err := curve.GenerateKey(r)\nif err != nil && strings.Contains(err.Error(), \"FIPS 140-only mode\") {\n    priv, err = curve.GenerateKey(cryptoRand.Reader)\n}","preventionTips":["Always pass crypto/rand.Reader for key generation in FIPS builds.","Use testing/cryptotest.SetGlobalRandom for tests instead of custom readers.","Avoid abstracting randomness behind custom reader types in production."],"tags":["crypto","ecdh","fips","random","compliance","go","security"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}