{"record":{"id":"69905c3663887c32","repo":"golang/go","slug":"invalid-pq-kem-for-x25519-hybrid","errorCode":null,"errorMessage":"invalid PQ KEM for X25519 hybrid","messagePattern":"invalid PQ KEM for X25519 hybrid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/hpke/pq.go","lineNumber":146,"sourceCode":"\n// NewHybridPublicKey returns a PublicKey implementing one of\n//\n//   - MLKEM768-X25519 (a.k.a. X-Wing)\n//   - MLKEM768-P256\n//   - MLKEM1024-P384\n//\n// from draft-ietf-hpke-pq, depending on the underlying curve of t\n// ([ecdh.X25519], [ecdh.P256], or [ecdh.P384]) and the type of pq (either\n// *[mlkem.EncapsulationKey768] or *[mlkem.EncapsulationKey1024]).\n//\n// This function is meant for applications that already have instantiated\n// crypto/ecdh and crypto/mlkem public keys. Otherwise, applications should use\n// the [KEM.NewPublicKey] method of e.g. [MLKEM768X25519].\nfunc NewHybridPublicKey(pq crypto.Encapsulator, t *ecdh.PublicKey) (PublicKey, error) {\n\tswitch t.Curve() {\n\tcase ecdh.X25519():\n\t\tif _, ok := pq.(*mlkem.EncapsulationKey768); !ok {\n\t\t\treturn nil, errors.New(\"invalid PQ KEM for X25519 hybrid\")\n\t\t}\n\t\treturn &hybridPublicKey{mlkem768X25519, t, pq}, nil\n\tcase ecdh.P256():\n\t\tif _, ok := pq.(*mlkem.EncapsulationKey768); !ok {\n\t\t\treturn nil, errors.New(\"invalid PQ KEM for P-256 hybrid\")\n\t\t}\n\t\treturn &hybridPublicKey{mlkem768P256, t, pq}, nil\n\tcase ecdh.P384():\n\t\tif _, ok := pq.(*mlkem.EncapsulationKey1024); !ok {\n\t\t\treturn nil, errors.New(\"invalid PQ KEM for P-384 hybrid\")\n\t\t}\n\t\treturn &hybridPublicKey{mlkem1024P384, t, pq}, nil\n\tdefault:\n\t\treturn nil, errors.New(\"unsupported curve\")\n\t}\n}\n\nfunc (kem *hybridKEM) NewPublicKey(data []byte) (PublicKey, error) {","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/hpke/pq.go#L128-L164","documentation":"NewHybridPublicKey builds an ML-KEM + ECDH hybrid (draft-ietf-hpke-pq). For X25519 the only valid pairing is ML-KEM-768 (the X-Wing combiner). If the pq argument is not *mlkem.EncapsulationKey768 (e.g. it is EncapsulationKey1024 or another Encapsulator), the constructor rejects it.","triggerScenarios":"Calling hpke.NewHybridPublicKey(pq, x25519Pub) with pq being *mlkem.EncapsulationKey1024 or any non-EncapsulationKey768 Encapsulator.","commonSituations":"Copy-pasting hybrid setup code across X25519 and P-384 builds; instantiating ML-KEM-1024 and assuming it pairs with X25519; reading the wrong draft revision.","solutions":["Pair X25519 only with *mlkem.EncapsulationKey768.","Use MLKEM768X25519().NewPublicKey(data) for the combined parsing path, which selects types automatically.","Type-check pq before calling NewHybridPublicKey: switch pq := pq.(type) { case *mlkem.EncapsulationKey768: ... }."],"exampleFix":"// before\npq := mlkem.NewEncapsulationKey1024(pqBytes)\nhpkePub, err := hpke.NewHybridPublicKey(pq, x25519Pub) // \"invalid PQ KEM for X25519 hybrid\"\n\n// after\npq, _ := mlkem.NewEncapsulationKey768(pqBytes)\nhpkePub, err := hpke.NewHybridPublicKey(pq, x25519Pub)","handlingStrategy":"type-guard","validationCode":"func x25519HybridPub(pq crypto.Encapsulator, t *ecdh.PublicKey) (hpke.PublicKey, error) {\n    if _, ok := pq.(*mlkem.EncapsulationKey768); !ok {\n        return nil, fmt.Errorf(\"X25519 hybrid requires *mlkem.EncapsulationKey768, got %T\", pq)\n    }\n    return hpke.NewHybridPublicKey(pq, t)\n}","typeGuard":"func isMLKEM768Encapsulator(pq crypto.Encapsulator) bool {\n    _, ok := pq.(*mlkem.EncapsulationKey768)\n    return ok\n}","tryCatchPattern":"pub, err := hpke.NewHybridPublicKey(pq, x25519Pub)\nif err != nil && err.Error() == \"invalid PQ KEM for X25519 hybrid\" {\n    return nil, fmt.Errorf(\"need *mlkem.EncapsulationKey768, got %T\", pq)\n}","preventionTips":["Pair X25519/P-256 with ML-KEM-768; P-384 with ML-KEM-1024 — encode this in a table.","Prefer MLKEM768X25519().NewPublicKey(blob) for combined parsing so types are auto-selected.","Unit-test each combiner with its correct ML-KEM parameter set."],"tags":["hpke","post-quantum","ml-kem","hybrid","go"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}