{"record":{"id":"d5a770312942cae2","repo":"FiloSottile/age","slug":"invalid-tag-recipient-public-key-v","errorCode":null,"errorMessage":"invalid tag recipient public key: %v","messagePattern":"invalid tag recipient public key: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tag/tag.go","lineNumber":76,"sourceCode":"\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)\n\t}\n\treturn &Recipient{k}, nil\n}\n","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/tag/tag.go#L58-L94","documentation":"After the length check, NewClassicRecipient decodes the bytes as a compressed P-256 point via nistec.NewP256Point().SetBytes. This error reports a point-decoding failure: the bytes have the right size but do not represent a point on the P-256 curve (bad prefix byte or invalid coordinates).","triggerScenarios":"tag.NewClassicRecipient with a 33-byte slice whose first byte is not 0x02/0x03, or whose x-coordinate has no matching y on the curve.","commonSituations":"Random/corrupted bytes passed as a key; endian or format confusion (e.g. Ed25519 or Curve25519 keys fed as P-256); manually assembled key material.","solutions":["Check the wrapped %v for the exact SetBytes failure.","Verify the key really is a P-256 (NIST) public key, not X25519/Ed25519 or another curve.","Ensure the first byte is the SEC1 compressed prefix 0x02 or 0x03.","Re-export the key from its source in compressed SEC1 form rather than hand-assembling bytes."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if len(publicKey) == 33 && (publicKey[0] == 0x02 || publicKey[0] == 0x03) {\n    if _, err := tag.NewClassicRecipient(publicKey); err != nil {\n        return fmt.Errorf(\"not a valid P-256 point: %w\", err)\n    }\n}","typeGuard":"func isCompressedP256Point(b []byte) bool {\n    if len(b) != 33 || (b[0] != 0x02 && b[0] != 0x03) {\n        return false\n    }\n    _, err := nistec.NewP256Point().SetBytes(b)\n    return err == nil\n}","tryCatchPattern":null,"preventionTips":["Confirm key provenance: P-256 only, never X25519/Ed25519 material.","Validate points with nistec.SetBytes before handing keys to constructors.","Regenerate keys from trusted generators rather than assembling bytes manually."],"tags":["go","p256","crypto","point-decoding"],"backgroundTag":"invalid-public-key-encoding","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}