{"record":{"id":"ade8d9249f198a9c","repo":"golang/go","slug":"tls-client-sent-encrypted-client-hello-extension","errorCode":null,"errorMessage":"tls: client sent encrypted_client_hello extension with unsupported versions","messagePattern":"tls: client sent encrypted_client_hello extension with unsupported versions","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/ech.go","lineNumber":393,"sourceCode":"\t}\n\n\thasTLS13 := false\n\tfor _, v := range inner.supportedVersions {\n\t\t// Skip GREASE values (values of the form 0x?A0A).\n\t\t// GREASE (Generate Random Extensions And Sustain Extensibility) is a mechanism used by\n\t\t// browsers like Chrome to ensure TLS implementations correctly ignore unknown values.\n\t\t// GREASE values follow a specific pattern: 0x?A0A, where ? can be any hex digit.\n\t\t// These values should be ignored when processing supported TLS versions.\n\t\tif v&0x0F0F == 0x0A0A && v&0xff == v>>8 {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Ensure at least TLS 1.3 is offered.\n\t\tif v == VersionTLS13 {\n\t\t\thasTLS13 = true\n\t\t} else if v < VersionTLS13 {\n\t\t\t// Reject if any non-GREASE value is below TLS 1.3, as ECH requires TLS 1.3+.\n\t\t\treturn nil, errors.New(\"tls: client sent encrypted_client_hello extension with unsupported versions\")\n\t\t}\n\t}\n\n\tif !hasTLS13 {\n\t\treturn nil, errors.New(\"tls: client sent encrypted_client_hello extension but did not offer TLS 1.3\")\n\t}\n\n\treturn inner, nil\n}\n\nfunc decryptECHPayload(context *hpke.Recipient, hello, payload []byte) ([]byte, error) {\n\touterAAD := bytes.Replace(hello[4:], payload, make([]byte, len(payload)), 1)\n\treturn context.Open(outerAAD, payload)\n}\n\nfunc generateOuterECHExt(id uint8, kdfID, aeadID uint16, encodedKey []byte, payload []byte) ([]byte, error) {\n\tvar b cryptobyte.Builder\n\tb.AddUint8(0) // outer","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/ech.go#L375-L411","documentation":"Thrown when the reconstructed ECH inner ClientHello's supported_versions extension contains a non-GREASE version below TLS 1.3 (0x0304). ECH is defined exclusively for TLS 1.3 and later per RFC 9460, so advertising TLS 1.2 (0x0303), TLS 1.1 (0x0302), TLS 1.0 (0x0301), or SSL 3.0 (0x0300) in the inner hello is a protocol violation. GREASE values (0x?A0A pattern) are skipped before this check.","triggerScenarios":"The inner ClientHello's supported_versions extension includes at least one non-GREASE version ID with a numeric value less than 0x0304 (VersionTLS13).","commonSituations":"Client configured with MinVersion below TLS 1.3 while using ECH — though the client-side makeClientHello also checks this, a custom or non-Go client might not. An ECH client library that does not enforce the TLS 1.3 minimum for the inner hello. A misconfigured client that includes legacy versions in the inner hello's supported_versions.","solutions":["Set the client's MinVersion to at least tls.VersionTLS13 when using ECH","Ensure the inner ClientHello's supported_versions extension only includes TLS 1.3 (0x0304) and optionally GREASE values","If using a non-Go ECH client, verify it enforces the TLS 1.3 minimum for ECH inner hellos","Remove any explicit version configuration that allows TLS 1.2 or earlier when ECH is enabled"],"exampleFix":"// before\nconfig := &tls.Config{\n    EncryptedClientHelloConfigList: echConfigList,\n    MinVersion: tls.VersionTLS12, // allows TLS 1.2 — invalid with ECH\n}\n// after\nconfig := &tls.Config{\n    EncryptedClientHelloConfigList: echConfigList,\n    MinVersion: tls.VersionTLS13,\n}","handlingStrategy":"validation","validationCode":"// Client-side: verify MinVersion before enabling ECH\nfunc validateECHVersionConfig(config *tls.Config) error {\n    if config.EncryptedClientHelloConfigList != nil {\n        if config.MinVersion != 0 && config.MinVersion < tls.VersionTLS13 {\n            return fmt.Errorf(\"MinVersion must be >= TLS 1.3 when ECH is enabled\")\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// Server-side: wrapped into errInvalidECHExt.\n// Client-side: prevent by validating config before dial:\n//\n//   if err := validateECHVersionConfig(config); err != nil {\n//       log.Fatal(err)\n//   }","preventionTips":["Always set MinVersion >= tls.VersionTLS13 when using EncryptedClientHelloConfigList","Validate the tls.Config in a constructor function before passing it to tls.Dial","Write unit tests that assert ECH configs require TLS 1.3"],"tags":["tls","ech","server-side","version-negotiation","tls13","rfc-9460"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:17:08.281Z"}