{"record":{"id":"90b9843a3d798669","repo":"FiloSottile/age","slug":"invalid-tag-recipient-public-key-size-d","errorCode":null,"errorMessage":"invalid tag recipient public key size %d","messagePattern":"invalid tag recipient public key size (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tag/tag.go","lineNumber":72,"sourceCode":"\t\treturn r, nil\n\tcase \"tagpq\":\n\t\tr, err := NewHybridRecipient(k)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"malformed recipient %q: %v\", s, err)\n\t\t}\n\t\treturn r, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"malformed recipient %q: invalid type %q\", s, t)\n\t}\n}\n\nconst compressedPointSize = 1 + 32\nconst uncompressedPointSize = 1 + 32 + 32\n\n// NewClassicRecipient returns a new P-256 [Recipient] from a raw public key.\nfunc NewClassicRecipient(publicKey []byte) (*Recipient, error) {\n\tif len(publicKey) != compressedPointSize {\n\t\treturn nil, fmt.Errorf(\"invalid tag recipient public key size %d\", len(publicKey))\n\t}\n\tp, err := nistec.NewP256Point().SetBytes(publicKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid tag recipient public key: %v\", err)\n\t}\n\tk, err := hpke.DHKEM(ecdh.P256()).NewPublicKey(p.Bytes())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid tag recipient public key: %v\", err)\n\t}\n\treturn &Recipient{k}, nil\n}\n\n// NewHybridRecipient returns a new hybrid P-256 + ML-KEM-768 [Recipient] from\n// raw concatenated public keys.\nfunc NewHybridRecipient(publicKey []byte) (*Recipient, error) {\n\tk, err := hpke.MLKEM768P256().NewPublicKey(publicKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid tagpq recipient public key: %v\", err)","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/tag/tag.go#L54-L90","documentation":"NewClassicRecipient requires a raw compressed P-256 public key of exactly 33 bytes (compressedPointSize = 1 + 32). If len(publicKey) differs, this error reports the actual size. It guards against keys in the wrong point encoding (e.g. uncompressed 65-byte keys) or truncated/garbage input.","triggerScenarios":"Calling tag.NewClassicRecipient(publicKey) with a byte slice whose length is not 33 — e.g. a 65-byte uncompressed SEC1 point, a raw 32-byte x-coordinate, or an empty/nil slice.","commonSituations":"Loading keys exported by tools that emit uncompressed points; passing x509/ecdsa keys without conversion; slicing errors when extracting the key from a larger buffer.","solutions":["Convert the key to compressed SEC1 form: use elliptic.MarshalCompressed(curve, x, y) or point.Bytes() on a nistec P256Point.","If you have an uncompressed 65-byte key, drop the 0x04 prefix and compress: keep the 0x02/0x03 prefix plus x-coordinate.","Verify the byte slice is not truncated or padded by checking its origin/export format.","Alternatively parse the Bech32 recipient string via tag.ParseRecipient, which handles the expected encoding."],"exampleFix":"// before\nr, err := tag.NewClassicRecipient(uncompressedPub) // 65 bytes\n// after\nx, y := elliptic.Unmarshal(elliptic.P256(), uncompressedPub)\ncompressed := elliptic.MarshalCompressed(elliptic.P256(), x, y) // 33 bytes\nr, err := tag.NewClassicRecipient(compressed)","handlingStrategy":"validation","validationCode":"const compressedPointSize = 33\nif len(publicKey) != compressedPointSize {\n    return fmt.Errorf(\"need 33-byte compressed P-256 key, got %d bytes\", len(publicKey))\n}\nr, err := tag.NewClassicRecipient(publicKey)","typeGuard":"func isCompressedP256Key(b []byte) bool {\n    return len(b) == 33 && (b[0] == 0x02 || b[0] == 0x03)\n}","tryCatchPattern":null,"preventionTips":["Always export P-256 keys in compressed SEC1 form (33 bytes, 0x02/0x03 prefix).","Convert uncompressed keys with elliptic.MarshalCompressed before calling the API.","Add a length+prefix assertion at the boundary where keys enter your code."],"tags":["go","p256","crypto","key-size"],"backgroundTag":"invalid-key-size","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}