{"record":{"id":"c6a2067cc523f6ad","repo":"FiloSottile/age","slug":"failed-to-create-hybrid-public-key-v","errorCode":null,"errorMessage":"failed to create hybrid public key: %v","messagePattern":"failed to create hybrid public key: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/encode.go","lineNumber":105,"sourceCode":"// identities that are compatible with native recipients.\nfunc EncodeX25519Recipient(pk *ecdh.PublicKey) (string, error) {\n\tif pk.Curve() != ecdh.X25519() {\n\t\treturn \"\", fmt.Errorf(\"wrong ecdh Curve\")\n\t}\n\treturn bech32.Encode(\"age\", pk.Bytes())\n}\n\n// EncodeHybridRecipient encodes a native MLKEM768-X25519 recipient from a\n// [crypto/mlkem.EncapsulationKey768] and a [crypto/ecdh.X25519] public key.\n// It's meant for plugins that implement identities that are compatible with\n// native recipients.\nfunc EncodeHybridRecipient(pq *mlkem.EncapsulationKey768, t *ecdh.PublicKey) (string, error) {\n\tif t.Curve() != ecdh.X25519() {\n\t\treturn \"\", fmt.Errorf(\"wrong ecdh Curve\")\n\t}\n\tpk, err := hpke.NewHybridPublicKey(pq, t)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create hybrid public key: %v\", err)\n\t}\n\treturn bech32.Encode(\"age1pq\", pk.Bytes())\n}\n","sourceCodeStart":87,"sourceCodeEnd":109,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/plugin/encode.go#L87-L109","documentation":"After curve validation, EncodeHybridRecipient calls hpke.NewHybridPublicKey to combine the MLKEM768 encapsulation key and X25519 key. If that construction fails (nil/invalid key material, wrong key sizes, malformed encapsulation key), the error is wrapped as 'failed to create hybrid public key'.","triggerScenarios":"Passing a nil or zero-value *mlkem.EncapsulationKey768, an encapsulation key not produced by mlkem key generation/decapsulation-key export, or key bytes that fail hpke.NewHybridPublicKey's internal validation.","commonSituations":"Deserializing PQ keys from untrusted/corrupted storage; passing an EncapsulationKey64 (MLKEM512) where 768 is required; constructing keys from truncated byte slices.","solutions":["Verify pq is non-nil and is a valid *mlkem.EncapsulationKey768 (Bytes() returns 1184 bytes).","Regenerate keys with mlkem.NewEncapsulationKey768 / GenerateKey768 rather than hand-constructing.","Check t.Bytes() is exactly 32 bytes of X25519 public key.","Log the underlying wrapped error from hpke.NewHybridPublicKey for the specific cause."],"exampleFix":"// before\nvar pq *mlkem.EncapsulationKey768 // nil\ns, err := plugin.EncodeHybridRecipient(pq, t.PublicKey())\n// after\npq, err := mlkem.NewEncapsulationKey768(pqBytes)\nif err != nil { return err }\ns, err := plugin.EncodeHybridRecipient(pq, t.PublicKey())","handlingStrategy":"try-catch","validationCode":"if pq == nil || len(pq.Bytes()) != 1184 { return errors.New(\"invalid MLKEM768 encapsulation key\") }\nif t == nil || len(t.Bytes()) != 32 { return errors.New(\"invalid X25519 public key\") }","typeGuard":"func validHybridInputs(pq *mlkem.EncapsulationKey768, t *ecdh.PublicKey) bool {\n\treturn pq != nil && len(pq.Bytes()) == 1184 && t != nil && t.Curve() == ecdh.X25519()\n}","tryCatchPattern":"s, err := plugin.EncodeHybridRecipient(pq, t)\nif err != nil {\n\treturn fmt.Errorf(\"EncodeHybridRecipient: %w\", err) // log wrapped hpke cause\n}","preventionTips":["Regenerate keys via mlkem rather than hand-building from bytes","Validate key byte lengths (1184 for MLKEM768 ek, 32 for X25519) after deserialization","Check the mlkem768 variant, not 512/1024"],"tags":["go","crypto","hpke","post-quantum","mlkem"],"backgroundTag":"invalid-cryptographic-key","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}