{"record":{"id":"4ad6532cd0c81040","repo":"golang/go","slug":"tls-failed-to-decrypt-second-client-hello-encrypt","errorCode":null,"errorMessage":"tls: failed to decrypt second client hello encrypted client hello extension payload","messagePattern":"tls: failed to decrypt second client hello encrypted client hello extension payload","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/handshake_server_tls13.go","lineNumber":619,"sourceCode":"\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}\n\n\t\t\techInner, err := decodeInnerClientHello(clientHello, encodedInner)\n\t\t\tif err != nil {\n\t\t\t\tc.sendAlert(alertIllegalParameter)\n\t\t\t\treturn nil, errors.New(\"tls: client sent invalid encrypted client hello extension\")\n\t\t\t}\n\n\t\t\tclientHello = echInner\n\t\t}\n\t}\n\n\tif len(clientHello.keyShares) != 1 {\n\t\tc.sendAlert(alertIllegalParameter)\n\t\treturn nil, errors.New(\"tls: client didn't send one key share in second ClientHello\")\n\t}\n\tks := &clientHello.keyShares[0]\n","sourceCodeStart":601,"sourceCodeEnd":637,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_server_tls13.go#L601-L637","documentation":"After the HPKE context is established from ClientHello1, decrypting the ECH payload in ClientHello2 fails (decryptECHPayload error). The ciphertext is wrong, the HPKE context diverged, or the bytes were tampered with. The server sends decrypt_error.","triggerScenarios":"ClientHello2's ECH payload cannot be decrypted with the HPKE context from ClientHello1. Tampering, a client bug, or an HPKE algorithm/context mismatch.","commonSituations":"ECH client bug, network corruption, attacker tampering, HPKE KEM/KDF/AEAD mismatch between client and server.","solutions":["Ensure the ECH HPKE context is consistent between ClientHello1 and ClientHello2","Verify client and server use the same HPKE KEM/KDF/AEAD algorithms","Update the ECH library on both sides to the same draft revision"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := tlsConn.Handshake(); err != nil {\n    if strings.Contains(err.Error(), \"failed to decrypt second client hello encrypted client hello\") {\n        log.Printf(\"ECH payload decrypt failed on retry from %v\", remote)\n    }\n    c.Close()\n    return\n}","preventionTips":["Keep the HPKE context consistent between ClientHello1 and ClientHello2","Ensure client and server use the same HPKE KEM/KDF/AEAD algorithms and ECH draft revision"],"tags":["tls","go","ech","hpke","decryption","encrypted-client-hello","handshake"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:17:08.281Z"}