{"record":{"id":"2eb15cc991bcbe0a","repo":"golang/go","slug":"newprivatekeyecdh-wrong-key-length","errorCode":null,"errorMessage":"NewPrivateKeyECDH: wrong key length","messagePattern":"NewPrivateKeyECDH: wrong key length","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/boring/ecdh.go","lineNumber":74,"sourceCode":"\tif !ok {\n\t\tC._goboringcrypto_EC_POINT_free(key)\n\t\treturn nil, errors.New(\"point not on curve\")\n\t}\n\n\tk := &PublicKeyECDH{curve, key, append([]byte(nil), bytes...)}\n\t// Note: Because of the finalizer, any time k.key is passed to cgo,\n\t// that call must be followed by a call to runtime.KeepAlive(k),\n\t// to make sure k is not collected (and finalized) before the cgo\n\t// call returns.\n\truntime.SetFinalizer(k, (*PublicKeyECDH).finalize)\n\treturn k, nil\n}\n\nfunc (k *PublicKeyECDH) Bytes() []byte { return k.bytes }\n\nfunc NewPrivateKeyECDH(curve string, bytes []byte) (*PrivateKeyECDH, error) {\n\tif len(bytes) != curveSize(curve) {\n\t\treturn nil, errors.New(\"NewPrivateKeyECDH: wrong key length\")\n\t}\n\n\tnid, err := curveNID(curve)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tkey := C._goboringcrypto_EC_KEY_new_by_curve_name(nid)\n\tif key == nil {\n\t\treturn nil, fail(\"EC_KEY_new_by_curve_name\")\n\t}\n\tb := bytesToBN(bytes)\n\tok := b != nil && C._goboringcrypto_EC_KEY_set_private_key(key, b) != 0\n\tif b != nil {\n\t\tC._goboringcrypto_BN_free(b)\n\t}\n\tif !ok {\n\t\tC._goboringcrypto_EC_KEY_free(key)\n\t\treturn nil, fail(\"EC_KEY_set_private_key\")","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/boring/ecdh.go#L56-L92","documentation":"Raised by boring.NewPrivateKeyECDH when the supplied scalar bytes are not exactly curveSize(curve) bytes — the byte length of a single curve coordinate/scalar. P-256: 32 bytes, P-384: 48, P-521: 66.","triggerScenarios":"Calling NewPrivateKeyECDH(curve, bytes) with len(bytes) != curveSize(curve): wrong-size scalar, a public point passed instead of a private scalar, or bytes from a different curve.","commonSituations":"Passing the uncompressed public point (65 bytes) where the 32-byte scalar is expected; mixing curve scalar sizes; truncated or padded scalars; DER/PEM-encoded keys not decoded first.","solutions":["Supply the raw fixed-length scalar (32/48/66 bytes for P-256/384/521).","Obtain it via standard ecdh.PrivateKey.Bytes() (which returns the scalar).","Match the curve string to the scalar's actual curve."],"exampleFix":"// before\npriv, err := boring.NewPrivateKeyECDH(\"P-256\", pubPoint65Bytes) // 65 != 32\n\n// after\npriv, err := boring.NewPrivateKeyECDH(\"P-256\", scalar32Bytes)","handlingStrategy":"validation","validationCode":"func expectedEcdhScalarLen(curve string) int {\n    switch curve {\n    case \"P-256\": return 32\n    case \"P-384\": return 48\n    case \"P-521\": return 66\n    }\n    return -1\n}\nfunc validateEcdhPriv(curve string, b []byte) error {\n    if want := expectedEcdhScalarLen(curve); want < 0 || len(b) != want {\n        return fmt.Errorf(\"expected %d-byte scalar for %s, got %d\", want, curve, len(b))\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Obtain scalars via ecdh.PrivateKey.Bytes().","Do not pass a public point where a private scalar is expected.","Match the curve string to the scalar's curve."],"tags":["crypto","ecdh","boringcrypto","fips","validation"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:17:08.281Z"}