{"record":{"id":"f81da6a9b7b955fa","repo":"golang/go","slug":"ed25519-bad-public-key-length-l","errorCode":null,"errorMessage":"ed25519: bad public key length: {l}","messagePattern":"ed25519: bad public key length: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/fips140/ed25519/ed25519.go","lineNumber":143,"sourceCode":"\t}\n\t// Note that we are not decompressing the public key point here,\n\t// because it takes > 20% of the time of a signature generation.\n\t// Signing doesn't use it as a point anyway.\n\tcopy(priv.pub[:], privBytes[32:])\n\n\tcopy(priv.prefix[:], h[32:])\n\n\treturn priv, nil\n}\n\nfunc NewPublicKey(pub []byte) (*PublicKey, error) {\n\tp := &PublicKey{}\n\treturn newPublicKey(p, pub)\n}\n\nfunc newPublicKey(pub *PublicKey, pubBytes []byte) (*PublicKey, error) {\n\tif l := len(pubBytes); l != publicKeySize {\n\t\treturn nil, errors.New(\"ed25519: bad public key length: \" + strconv.Itoa(l))\n\t}\n\t// SetBytes checks that the point is on the curve.\n\tif _, err := pub.a.SetBytes(pubBytes); err != nil {\n\t\treturn nil, errors.New(\"ed25519: bad public key\")\n\t}\n\tcopy(pub.aBytes[:], pubBytes)\n\treturn pub, nil\n}\n\n// Domain separation prefixes used to disambiguate Ed25519/Ed25519ph/Ed25519ctx.\n// See RFC 8032, Section 2 and Section 5.1.\nconst (\n\t// domPrefixPure is empty for pure Ed25519.\n\tdomPrefixPure = \"\"\n\t// domPrefixPh is dom2(phflag=1) for Ed25519ph. It must be followed by the\n\t// uint8-length prefixed context.\n\tdomPrefixPh = \"SigEd25519 no Ed25519 collisions\\x01\"\n\t// domPrefixCtx is dom2(phflag=0) for Ed25519ctx. It must be followed by the","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/ed25519/ed25519.go#L125-L161","documentation":"Returned by NewPublicKey when pubBytes is not exactly publicKeySize (32) bytes. Ed25519 public keys are the y-coordinate plus a sign bit packed into 32 bytes per RFC 8032.","triggerScenarios":"Calling fips140/ed25519.NewPublicKey(pub) with a slice whose length is not 32 (e.g. 64 bytes that are actually a private key, 33-byte compressed point from another curve, hex string).","commonSituations":"Passing a hex/base64 string instead of decoded bytes; passing a 64-byte private key where only the public key was expected; confusing an X25519 (Curve25519) public key with an Ed25519 one — both are 32 bytes but only the latter decodes.","solutions":["Supply exactly 32 raw bytes representing the Ed25519 public key.","Decode hex/base64 before calling and verify length==32.","Ensure the bytes are from an Ed25519 keypair, not X25519 — they will pass length but may fail the next check (bad public key)."],"exampleFix":"// before\npub, err := ed25519.NewPublicKey([]byte(pubHex)) // hex string as bytes\n\n// after\nb, err := hex.DecodeString(pubHex)\nif err != nil { return err }\nif len(b) != 32 { return fmt.Errorf(\"ed25519 pubkey must be 32 bytes\") }\npub, err := ed25519.NewPublicKey(b)","handlingStrategy":"validation","validationCode":"const ed25519PubSize = 32\nif len(b) != ed25519PubSize {\n    return nil, fmt.Errorf(\"ed25519 public key must be %d bytes, got %d\", ed25519PubSize, len(b))\n}\nreturn ed25519.NewPublicKey(b)","typeGuard":null,"tryCatchPattern":"pub, err := ed25519.NewPublicKey(b)\nif err != nil {\n    if strings.Contains(err.Error(), \"bad public key length\") {\n        return nil, ErrInvalidPublicKeyEncoding\n    }\n    return nil, err\n}","preventionTips":["Reject X25519 public keys at the caller via context (they pass length but fail curve check).","Decode all hex/base64 keys at the boundary.","Tag key material with its curve to avoid X25519/Ed25519 mix-ups."],"tags":["crypto","ed25519","fips140","validation","key-import"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}