{"record":{"id":"73aef09d3918e14a","repo":"golang/go","slug":"hpke-invalid-hybrid-kem-secret-length","errorCode":null,"errorMessage":"hpke: invalid hybrid KEM secret length","messagePattern":"hpke: invalid hybrid KEM secret length","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/hpke/pq.go","lineNumber":256,"sourceCode":"//\n// This function is meant for applications that already have instantiated\n// crypto/ecdh and crypto/mlkem private keys, or another implementation of a\n// [ecdh.KeyExchanger] and [crypto.Decapsulator] (e.g. a hardware key).\n// Otherwise, applications should use the [KEM.NewPrivateKey] method of e.g.\n// [MLKEM768X25519].\nfunc NewHybridPrivateKey(pq crypto.Decapsulator, t ecdh.KeyExchanger) (PrivateKey, error) {\n\treturn newHybridPrivateKey(pq, t, nil)\n}\n\nfunc (kem *hybridKEM) GenerateKey() (PrivateKey, error) {\n\tseed := make([]byte, 32)\n\tdrbg.Read(seed)\n\treturn kem.NewPrivateKey(seed)\n}\n\nfunc (kem *hybridKEM) NewPrivateKey(priv []byte) (PrivateKey, error) {\n\tif len(priv) != 32 {\n\t\treturn nil, errors.New(\"hpke: invalid hybrid KEM secret length\")\n\t}\n\n\ts := sha3.NewSHAKE256()\n\ts.Write(priv)\n\n\tseedPQ := make([]byte, mlkem.SeedSize)\n\ts.Read(seedPQ)\n\tpq, err := kem.pqNewPrivateKey(seedPQ)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tseedT := make([]byte, kem.curveSeedSize)\n\tfor {\n\t\ts.Read(seedT)\n\t\tvar k ecdh.KeyExchanger\n\t\tfips140.WithoutEnforcement(func() { // Hybrid of ML-KEM, which is Approved.\n\t\t\tk, err = kem.curve.NewPrivateKey(seedT)","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/hpke/pq.go#L238-L274","documentation":"hybridKEM.NewPrivateKey derives the PQ and ECDH subkeys from a single 32-byte seed via SHAKE256 (draft-ietf-hpke-pq DeriveKeyPair). The seed length is fixed at 32 bytes; any other length is rejected because the KDF input size is part of the combiner's contract.","triggerScenarios":"Calling (*hybridKEM).NewPrivateKey(priv) directly with priv of length != 32. Indirectly via DeriveKeyPair(ikm) which derives a 32-byte dk first, so the direct call is the usual culprit.","commonSituations":"Passing a 64-byte X25519-style secret, an empty slice, or the raw ML-KEM seed instead of the combined 32-byte hybrid seed.","solutions":["Pass exactly 32 bytes; if you have a larger secret, hash/expand it down to 32 bytes first.","Use GenerateKey() to get a randomly-seeded key, or DeriveKeyPair(ikm) to derive from arbitrary input.","Round-trip keys via Bytes()/NewPrivateKey(Bytes()) to guarantee correct sizing."],"exampleFix":"// before\nseed := make([]byte, 64) // wrong size\nsk, err := hpke.MLKEM768X25519().NewPrivateKey(seed) // \"invalid hybrid KEM secret length\"\n\n// after\nseed := make([]byte, 32)\ncrand.Read(seed)\nsk, err := hpke.MLKEM768X25519().NewPrivateKey(seed)","handlingStrategy":"validation","validationCode":"const hybridSeedLen = 32\nfunc newHybridSecret(kem hpke.KEM, seed []byte) (hpke.PrivateKey, error) {\n    if len(seed) != hybridSeedLen {\n        return nil, fmt.Errorf(\"hybrid seed must be %d bytes, got %d\", hybridSeedLen, len(seed))\n    }\n    // Use the unexported path via GenerateKey/DeriveKeyPair by passing through NewPrivateKey if exposed.\n    return kem.NewPrivateKey(seed)\n}","typeGuard":null,"tryCatchPattern":"sk, err := kem.NewPrivateKey(seed)\nif err != nil && err.Error() == \"hpke: invalid hybrid KEM secret length\" {\n    h := sha3.NewShake256()\n    h.Write(seed)\n    out := make([]byte, 32)\n    h.Read(out)\n    sk, err = kem.NewPrivateKey(out)\n}\nif err != nil {\n    return nil, err\n}","preventionTips":["Standardize on a 32-byte seed constant for hybrid keys.","Prefer GenerateKey() for random keys and DeriveKeyPair(ikm) for arbitrary input.","Round-trip keys through Bytes()/NewPrivateKey(Bytes()) to guarantee size correctness."],"tags":["hpke","post-quantum","hybrid","validation","go"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}