Skip to content

fix: resolve TLS error decoding message for ECH extension - #429

Open
AdamMagued wants to merge 1 commit into
Danny-Dasilva:mainfrom
AdamMagued:fix/issue-409
Open

AdamMagued wants to merge 1 commit into
Danny-Dasilva:mainfrom
AdamMagued:fix/issue-409

Conversation

@AdamMagued

Copy link
Copy Markdown

Summary

Fixes #409

This change resolves the TLS handshake failure uTlsConn.Handshake() error: remote error: tls: error decoding message that occurs when JA4r fingerprints include extension 0xfe0d (Encrypted Client Hello / ECH).

Root Cause

  1. In cycletls/extensions.go, extension ID 0xfe0d was handled by NewCustomECHExtension, which emitted 8 dummy zero bytes. According to the draft-ietf-tls-esni and RFC specifications, malformed ECH extension payloads cause compliant TLS 1.3 servers to abort the handshake with alert 50 (decode_error).
  2. Extension 0x000a (SupportedGroups) prepended utls.X25519MLKEM768 in TLS 1.3, but extension 0x0033 (KeyShare) only advertised classical X25519 and CurveP256. When post-quantum-aware servers received this configuration, they issued a HelloRetryRequest selecting X25519MLKEM768. Because uTLS does not support dynamic generation of hybrid KEM keys during HelloRetryRequest, this produced a client-side CurvePreferences includes unsupported curve handshake error.

Key Changes

  • Replaced CustomECHExtension with utls.BoringGREASEECH(), matching Chrome's standard GREASE ECH extension format.
  • Honored disableGrease for extension 0xfe0d: when GREASE is disabled, extension 0xfe0d is omitted (nil).
  • Removed unused placeholder types CustomECHExtension and constructor NewCustomECHExtension.
  • Updated TLS 1.3 KeyShareExtension to advertise both utls.X25519MLKEM768 and utls.X25519, matching ModernChromeSpec and preventing unhandled HelloRetryRequest rounds.
  • Added comprehensive unit tests in cycletls/tests/unit/issue409_test.go covering end-to-end TLS 1.3 handshake with JA4r, ECH generation with GREASE toggling, and curve/key share verification.

Verification

  • Verified cleanly inside Docker container sandbox:
    • go test -v -count=1 ./tests/unit/... passed.
    • go test -count=1 ./tests/... passed.

Replace the placeholder CustomECHExtension with utls.BoringGREASEECH for
extension 0xfe0d, and omit it when disableGrease is active. Update TLS 1.3
KeyShareExtension to provide X25519MLKEM768 alongside X25519, matching the
order in SupportedCurvesExtension and preventing server HelloRetryRequest
failures. Add comprehensive unit tests verifying handshake, ECH extension
generation, and curve order.

Signed-off-by: Adam Magued <adamismailmageud@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tls: error decoding message

1 participant