{"record":{"id":"c0ad23b5813aa7c5","repo":"golang/go","slug":"tls-client-sent-invalid-encrypted-client-hello-ex","errorCode":null,"errorMessage":"tls: client sent invalid encrypted client hello extension","messagePattern":"tls: client sent invalid encrypted client hello extension","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/handshake_server_tls13.go","lineNumber":602,"sourceCode":"\t\treturn nil, err\n\t}\n\n\tclientHello, ok := msg.(*clientHelloMsg)\n\tif !ok {\n\t\tc.sendAlert(alertUnexpectedMessage)\n\t\treturn nil, unexpectedMessageError(clientHello, msg)\n\t}\n\n\tif hs.echContext != nil {\n\t\tif len(clientHello.encryptedClientHello) == 0 {\n\t\t\tc.sendAlert(alertMissingExtension)\n\t\t\treturn nil, errors.New(\"tls: second client hello missing encrypted client hello extension\")\n\t\t}\n\n\t\techType, echCiphersuite, configID, encap, payload, err := parseECHExt(clientHello.encryptedClientHello)\n\t\tif err != nil {\n\t\t\tc.sendAlert(alertDecodeError)\n\t\t\treturn nil, errors.New(\"tls: client sent invalid encrypted client hello extension\")\n\t\t}\n\n\t\tif echType == outerECHExt && hs.echContext.inner || echType == innerECHExt && !hs.echContext.inner {\n\t\t\tc.sendAlert(alertDecodeError)\n\t\t\treturn nil, errors.New(\"tls: unexpected switch in encrypted client hello extension type\")\n\t\t}\n\n\t\tif echType == outerECHExt {\n\t\t\tif echCiphersuite != hs.echContext.ciphersuite || configID != hs.echContext.configID || len(encap) != 0 {\n\t\t\t\tc.sendAlert(alertIllegalParameter)\n\t\t\t\treturn nil, errors.New(\"tls: second client hello encrypted client hello extension does not match\")\n\t\t\t}\n\n\t\t\tencodedInner, err := decryptECHPayload(hs.echContext.hpkeContext, clientHello.original, payload)\n\t\t\tif err != nil {\n\t\t\t\tc.sendAlert(alertDecryptError)\n\t\t\t\treturn nil, errors.New(\"tls: failed to decrypt second client hello encrypted client hello extension payload\")\n\t\t\t}","sourceCodeStart":584,"sourceCodeEnd":620,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_server_tls13.go#L584-L620","documentation":"parseECHExt failed to parse the ECH extension bytes — the extension is structurally malformed (bad type, KEM/KDF/AEAD suite, config_id, encap, or payload layout). The server sends decode_error. Note: this same message is reused at line 625 for a different failure (inner ClientHello decode); here it is the outer parse.","triggerScenarios":"Client sends a truncated or wrongly-encoded ECH extension. Hand-rolled ECH, an ECH draft-version mismatch with the server, or a fuzzer.","commonSituations":"Hand-rolled ECH serialization, client/server on different ECH draft revisions, buggy ECH library.","solutions":["Ensure the ECH extension is serialized per the draft (type, suite, config_id, enc, payload) the server expects","Match the ECH draft revision between client and server","Use a vetted ECH implementation instead of hand-encoding"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Client-side sanity check before sending (conceptual):\nif _, _, _, _, _, err := parseECHExt(echBytes); err != nil {\n    return fmt.Errorf(\"ECH extension malformed: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := tlsConn.Handshake(); err != nil {\n    if strings.Contains(err.Error(), \"invalid encrypted client hello extension\") {\n        log.Printf(\"malformed ECH extension from %v\", remote)\n    }\n    c.Close()\n    return\n}","preventionTips":["Match the ECH draft revision between client and server","Serialize the ECH extension (type, suite, config_id, enc, payload) exactly per the draft","Use a vetted ECH implementation rather than hand-encoding"],"tags":["tls","go","ech","encrypted-client-hello","parsing","draft","handshake"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}