{"record":{"id":"a1d79ecfe2c38f28","repo":"golang/go","slug":"crypto-ecdh-invalid-private-key-a1d79e","errorCode":null,"errorMessage":"crypto/ecdh: invalid private key","messagePattern":"crypto/ecdh: invalid private key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/fips140/ecdh/ecdh.go","lineNumber":194,"sourceCode":"\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif !bytes.Equal(p1.Bytes(), privateKey.pub.q) {\n\t\t\t\treturn errors.New(\"crypto/ecdh: public key does not match private key\")\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\n\t\treturn privateKey, nil\n\t}\n}\n\nfunc NewPrivateKey[P Point[P]](c *Curve[P], key []byte) (*PrivateKey, error) {\n\t// SP 800-56A Rev. 3, Section 5.6.1.2.2 checks that c <= n – 2 and then\n\t// returns d = c + 1. Note that it follows that 0 < d < n. Equivalently,\n\t// we check that 0 < d < n, and return d.\n\tif len(key) != len(c.N) || isZero(key) || !isLess(key, c.N) {\n\t\treturn nil, errors.New(\"crypto/ecdh: invalid private key\")\n\t}\n\n\tp, err := c.newPoint().ScalarBaseMult(key)\n\tif err != nil {\n\t\t// This is unreachable because the only error condition of\n\t\t// ScalarBaseMult is if the input is not the right size.\n\t\tpanic(\"crypto/ecdh: internal error: nistec ScalarBaseMult failed for a fixed-size input\")\n\t}\n\n\tpublicKey := p.Bytes()\n\tif len(publicKey) == 1 {\n\t\t// The encoding of the identity is a single 0x00 byte. This is\n\t\t// unreachable because the only scalar that generates the identity is\n\t\t// zero, which is rejected above.\n\t\tpanic(\"crypto/ecdh: internal error: public key is the identity element\")\n\t}\n\n\tk := &PrivateKey{d: bytes.Clone(key), pub: PublicKey{curve: c.curve, q: publicKey}}","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/ecdh/ecdh.go#L176-L212","documentation":"Thrown by fips140/ecdh.NewPrivateKey when the key byte slice fails SP 800-56A Rev. 3 §5.6.1.2.2: the length must equal len(c.N), the bytes must not be all zero (isZero), and the value must be strictly less than the curve order n (isLess). Equivalently the private scalar d must satisfy 0 < d < n.","triggerScenarios":"Constructing an ECDH private key whose byte length differs from the curve order, is all zeros, or represents a value >= n. Reached through crypto/ecdh which backs onto this FIPS implementation.","commonSituations":"Importing a scalar generated for a different curve, a truncated or zeroed key buffer, or a scalar that happens to be >= the order after reduction/masking.","solutions":["Confirm the key length matches the curve order length (e.g. 32 bytes for P-256, 48 for P-384, 66 for P-521).","Regenerate the key via the proper GenerateKey path rather than supplying raw bytes.","If sourcing bytes from another library, reduce them mod n (or reduce-and-add-1 per SP 800-56A) and ensure the result is non-zero and < n."],"exampleFix":"// before\npriv, err := ecdh.NewPrivateKey(curve, rawBytes) // rawBytes may be wrong length / >= n\n\n// after: derive through the approved generator\npriv, err := curve.GenerateKey(rand.Reader)","handlingStrategy":"validation","validationCode":"// Pre-check SP 800-56A preconditions before NewPrivateKey.\nif len(key) != len(curveN) {\n    return fmt.Errorf(\"private key must be %d bytes\", len(curveN))\n}\nif allZero(key) {\n    return errors.New(\"private key is zero\")\n}\nif !bytesLess(key, curveN) {\n    return errors.New(\"private key >= curve order\")\n}","typeGuard":"func validECDHScalar(key, order []byte) bool {\n    if len(key) != len(order) || allZero(key) { return false }\n    return bytes.Compare(key, order) < 0\n}","tryCatchPattern":"priv, err := ecdh.NewPrivateKey(curve, key)\nif err != nil {\n    // regenerate from the approved RNG rather than retrying the same bytes\n    priv, err = curve.GenerateKey(rand.Reader)\n    if err != nil { return err }\n}","preventionTips":["Generate ECDH keys via GenerateKey rather than importing raw bytes.","When importing, validate length, non-zero, and < n before calling NewPrivateKey.","Tag imported key material with its source curve."],"tags":["go","crypto","fips","ecdh","key-validation","input-validation"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}