{"record":{"id":"c307f34646f7b9e0","repo":"golang/go","slug":"crypto-ecdh-invalid-private-key-size","errorCode":null,"errorMessage":"crypto/ecdh: invalid private key size","messagePattern":"crypto/ecdh: invalid private key size","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/ecdh/x25519.go","lineNumber":54,"sourceCode":"\nfunc (c *x25519Curve) GenerateKey(r io.Reader) (*PrivateKey, error) {\n\tif fips140only.Enforced() {\n\t\treturn nil, errors.New(\"crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode\")\n\t}\n\tr = rand.CustomReader(r)\n\tkey := make([]byte, x25519PrivateKeySize)\n\tif _, err := io.ReadFull(r, key); err != nil {\n\t\treturn nil, err\n\t}\n\treturn c.NewPrivateKey(key)\n}\n\nfunc (c *x25519Curve) NewPrivateKey(key []byte) (*PrivateKey, error) {\n\tif fips140only.Enforced() {\n\t\treturn nil, errors.New(\"crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode\")\n\t}\n\tif len(key) != x25519PrivateKeySize {\n\t\treturn nil, errors.New(\"crypto/ecdh: invalid private key size\")\n\t}\n\tpublicKey := make([]byte, x25519PublicKeySize)\n\tx25519Basepoint := [32]byte{9}\n\tx25519ScalarMult(publicKey, key, x25519Basepoint[:])\n\t// We don't check for the all-zero public key here because the scalar is\n\t// never zero because of clamping, and the basepoint is not the identity in\n\t// the prime-order subgroup(s).\n\treturn &PrivateKey{\n\t\tcurve:      c,\n\t\tprivateKey: bytes.Clone(key),\n\t\tpublicKey:  &PublicKey{curve: c, publicKey: publicKey},\n\t}, nil\n}\n\nfunc (c *x25519Curve) NewPublicKey(key []byte) (*PublicKey, error) {\n\tif fips140only.Enforced() {\n\t\treturn nil, errors.New(\"crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode\")\n\t}","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/ecdh/x25519.go#L36-L72","documentation":"X25519 private keys are exactly 32 bytes (x25519PrivateKeySize). NewPrivateKey checks len(key) != x25519PrivateKeySize and returns this error for any other length. (The FIPS check runs first; this fires only when FIPS mode is not enforced.)","triggerScenarios":"Calling ecdh.X25519().NewPrivateKey(key) with len(key) != 32, e.g. a 31-byte key, a hex/base64 string instead of raw bytes, or a longer buffer.","commonSituations":"Passing a hex-encoded string instead of decoded bytes; truncated read; concatenating extra metadata into the key buffer; off-by-one slicing.","solutions":["Supply exactly 32 raw bytes: ensure len(key) == 32 before calling.","Decode hex/base64 to raw bytes first (hex.DecodeString / base64.StdEncoding.DecodeString).","Prefer GenerateKey(rand.Reader) to obtain correctly-sized keys rather than hand-constructing them."],"exampleFix":"// before\npriv, err := ecdh.X25519().NewPrivateKey([]byte(hexString)) // wrong length\n// after\nraw, _ := hex.DecodeString(hexString) // 32 bytes\npriv, err := ecdh.X25519().NewPrivateKey(raw)","handlingStrategy":"validation","validationCode":"func newX25519Priv(key []byte) (*ecdh.PrivateKey, error) {\n    if len(key) != 32 {\n        return nil, fmt.Errorf(\"X25519 private key must be 32 bytes, got %d\", len(key))\n    }\n    return ecdh.X25519().NewPrivateKey(key)\n}","typeGuard":"func isX25519PrivSize(key []byte) bool { return len(key) == 32 }","tryCatchPattern":null,"preventionTips":["Decode hex/base64 to raw bytes before constructing the key.","Assert len(key)==32 in a test for any key-loading code path.","Prefer GenerateKey over hand-loading raw bytes."],"tags":["crypto","ecdh","x25519","validation","go"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}