Enhancement: Public extension manager for ECH#10568
Enhancement: Public extension manager for ECH#10568sebastian-carpenter wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR refactors TLS 1.3 ECH public-vs-private extension handling by introducing a dedicated “public extensions” list on the WOLFSSL_ECH object and swapping extensions by type, replacing the previous SNI-only in-place string swap approach. It also updates SNI/ECH parsing and handshake flow to install the correct extensions on accept/reject paths, and adds/updates tests to validate wire visibility of public vs private SNI.
Changes:
- Add
WOLFSSL_ECH::extensionsto hold “public” extensions and implement swapping/replacement helpers used during ClientHello size/write and accept/reject handling. - Rework SNI parsing and ECH handshake transitions to remove the old
sniState/privateNamemachinery and support public-name matching for outer ClientHello. - Update API tests to cover new behaviors (wire-SNI visibility, no-inner-SNI behavior, new bad config cases) and relocate long-SNI length validation coverage.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
wolfssl/internal.h |
Removes old SNI-state fields and adds WOLFSSL_ECH::extensions plus TLSX_EchReplaceExtensions() prototype. |
src/tls.c |
Implements public-extension swapping/replacement, updates SNI parsing, and updates ECH lifecycle management. |
src/tls13.c |
Adjusts ClientHello padding derivation and installs correct extension sets at ECH accept/reject points (incl. HRR). |
tests/api.c |
Adds/updates ECH/SNI tests (wire visibility, no-private-name behavior, new config cases) and relocates length checks. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
2e125d1 to
aa4831f
Compare
|
retest this please |
|
8c2fa10 to
61e1638
Compare
|
Jenkins retest this please. |
dgarske
left a comment
There was a problem hiding this comment.
Skoll Code Review
Scan type: reviewOverall recommendation: COMMENT
Findings: 4 total — 4 posted, 0 skipped
4 finding(s) posted as inline comments (see file-level comments below)
Posted findings
- [Medium] Multi-public-extension swap/reversal path is untested —
src/tls.c:16682-16734 - [Low] ECH padding length only considers ssl-extensions, not ctx-level inner SNI —
src/tls13.c:4844-4855 - [Info] Pointer declaration style inconsistent with repo convention —
src/tls.c:2378 - [Info] TLSX_EchShouldHideInner can be a single boolean expression —
src/tls.c:16665-16671
Review generated by Skoll
4eef82d to
b4058e9
Compare
dgarske
left a comment
There was a problem hiding this comment.
Skoll Code Review
Scan type: reviewOverall recommendation: COMMENT
Findings: 2 total — 2 posted, 0 skipped
2 finding(s) posted as inline comments (see file-level comments below)
Posted findings
- [Low] TLSX_FreeAll echList tracks only a single ECH extension per list —
src/tls.c:15190-15216 - [Low] BAD_STATE_E on restore-swap leaves ssl-extensions inconsistent without repair —
src/tls.c:16895-16901, 17111-17117
Review generated by Skoll
b4058e9 to
a965758
Compare
dgarske
left a comment
There was a problem hiding this comment.
Skoll Code Review
Scan type: reviewOverall recommendation: REQUEST_CHANGES
Findings: 6 total — 6 posted, 0 skipped
5 finding(s) posted as inline comments (see file-level comments below)
Posted findings
- [High] Servers with WOLFSSL_SNI_ABORT_ON_ABSENCE now abort every ECH handshake at the outer ClientHello, even when a valid inner SNI is present —
src/tls.c:2520-2557 - [Medium] test_wolfSSL_Tls13_ECH_no_private_name ABORT_ON_ABSENCE case encodes the outer-parse abort; missing test for ABORT_ON_ABSENCE with inner SNI present —
tests/api.c:14625-14639 - [Low] BAD_STATE_E restore-failure paths log with WOLFSSL_MSG only, no WOLFSSL_ERROR_VERBOSE —
src/tls.c:16900-16907, src/tls.c:17117-17124 - [Low] Inline reimplementation of TLSX_SetResponse in TLSX_SNI_Parse —
src/tls.c:2550-2557 - [Low] EchCheckAcceptance calls TLSX_EchReplaceExtensions even when EchCalcAcceptance failed —
src/tls13.c:5310-5314 - [Info] PUB_NAME changed to example.com for all interop scenarios, not just the new reject tests —
.github/scripts/openssl-ech.sh:97-99
Review generated by Skoll
dgarske
left a comment
There was a problem hiding this comment.
See these skoll review items -> #10568 (review)
a965758 to
ee10978
Compare
dgarske
left a comment
There was a problem hiding this comment.
Skoll Code Review
Scan type: reviewOverall recommendation: COMMENT
Findings: 2 total — 2 posted, 0 skipped
2 finding(s) posted as inline comments (see file-level comments below)
Posted findings
- [Medium] ECH reconciliation block in TLSX_Parse omits the TLS1.3 version guard used by sibling SNI logic —
src/tls.c:18715-18717 - [Low] Duplicated client/server invocation block in openssl-ech.sh reject branches —
.github/scripts/openssl-ech.sh:175-197,275-307
Review generated by Skoll
dgarske
left a comment
There was a problem hiding this comment.
#10568 (review)
Make sure you rebase to latest master to pull in CI optimizations.
dgarske
left a comment
There was a problem hiding this comment.
Skoll Multi-Scan Review
Modes: review + review-securityOverall recommendation: COMMENT
Findings: 4 total — 4 posted, 0 skipped
3 finding(s) posted as inline comments (see file-level comments below)
Posted findings
- [Medium] [review] Server now hard-rejects an accepted ECH handshake when the outer ClientHello SNI did not match a publicName —
src/tls.c:18724-18741 - [Medium] [review] New UNKNOWN_SNI_HOST_NAME_E error path is not exercised by tests —
src/tls.c:18727-18734 - [Low] [review] Duplicated install/restore swap boilerplate in TLSX_GetSizeWithEch and TLSX_WriteWithEch —
src/tls.c:16911-16935, 17076-17152 - [Info] [review-security] CTX-level private SNI suppression in ECH outer hello relies on implicit semaphore ordering and is untested —
src/tls.c:16896-16937 (TLSX_GetSizeWithEch), src/tls.c:17062-17153 (TLSX_WriteWithEch)
Review generated by Skoll
ee10978 to
b996def
Compare
dgarske
left a comment
There was a problem hiding this comment.
Skoll Multi-Scan Review
Modes: review + review-securityOverall recommendation: COMMENT
Findings: 3 total — 3 posted, 0 skipped
3 finding(s) posted as inline comments (see file-level comments below)
Posted findings
- [Medium] [review+review-security] ECH outer ClientHello reconciliation may falsely reject valid handshakes (new public-name SNI gate) —
src/tls.c:18766-18784 - [Low] [review] Redundant TLSX_Find(TLSX_ECH) lookups in TLSX_SNI_Parse —
src/tls.c:2456-2458, 2513-2519 - [Info] [review] ForceZero used to zero-initialize a non-secret Hpke struct —
src/tls.c:13890
Review generated by Skoll
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Comments suppressed due to low confidence (1)
tests/api.c:1
- This helper will underflow
storage--when called withcount == 0, causing an out-of-bounds write tostorage->next. Even if current call sites always passcount > 0, it’s brittle for future edits. Add an early return whencount <= 0.
|
This PR has many changes too late in release process. There are a few minor good things that @sebastian-carpenter is going to pull out into a smaller PR to see if it can go into the release. |
b996def to
1d5e380
Compare
- *_wire_sni test is now more efficient - openssl-ech workflow now does interop with ECH rejection extra improvements: - tested TLSX_EchSwapExtensions - added ctx level SNI to padding calculation - Improvement of SNI handling for ECH - Changed EchSwapExtensions to append instead of prepend - Reworked ECH testing
1d5e380 to
d3f80bf
Compare
Description
Reworks ECH public-vs-private extension handling. The old path swapped a single SNI string in place on the live extension list - this was fragile and SNI-only. This PR generalizes it to a public-extension list on the ECH struct that's swapped by extension type, so the outer ClientHello can carry arbitrary public extensions.
ZD#21931
Public extension manager
TLSX* extensionstoWOLFSSL_ECHholding the public extensions (e.g. the public-name SNI), populated inTLSX_ECH_UsefromechConfig->publicName.TLSX_EchChangeSNI/TLSX_EchRestoreSNIwith:TLSX_EchShouldHideInner- true when ECH is the outer type.TLSX_EchSwapExtensions- swaps matching extension types betweenssl->extensionsandech->extensions; unmatched public exts are prepended;popCountreverses leading nodes to preserve OuterExtensions ordering.TLSX_EchReplaceExtensions- accept: free the public list; reject: swap public exts intossl->extensions.TLSX_GetSizeWithEch/TLSX_WriteWithEchinstall the public exts before size/write and swap back after.ForceZerothe hpke; freeech->extensionsinTLSX_ECH_Free.SNI changes
EchStateSNIenum and thesniState/privateNamefields, plus the sniState transitions inDoTls13HandShakeMsgType.TLSX_SNI_Parse: drop the sniState-based private-name save/restore. On outer-CH parse, match against anyechConfig->publicNameand write a matched public SNI toech->extensions, notssl->extensions; simplify thematched/cacheOnlyand response-flag logic.TLSX_EchChangeSNI.SendTls13ClientHello: read padding length viaTLSX_SNI_GetRequestinstead ofech->privateName.TLSX_EchReplaceExtensionsat the accept/reject points (EchCheckAcceptance,DoTls13ServerHelloreject fallback,DoTls13ClientHello). This installs the correct SNI for use later in the handshake.Padding
Testing
test_wolfSSL_Tls13_ECH_wire_sni: drive the handshake manually and assert the public name is present and the private name absent in raw CH1/CH2 bytes, across accept/reject.test_wolfSSL_Tls13_ECH_no_private_name: no-inner-SNI now succeeds against a permissive server (ACCEPTED); rejected only withABORT_ON_ABSENCE.b64MandatoryFirstconfig case;test_ech_server_sni_callbackrejects a wrong public name; add a double-public-SNI scenario tobad_configs_ex.test_wolfSSL_Tls13_ECH_long_SNI(its overflow target - the deleted swap helpers - no longer exist) and relocate the over-long-SNIBAD_LENGTH_Echeck intotest_wolfSSL_UseSNI_params.TLSX_EchSwapExtensionsfunction:test_TLSX_EchSwapExtensions. This emulates the swap between ssl->extensions and ech->extensions with various configs: 'match', 'no match', 'match and non-match', 'match and no-match + out of order'. This required qualifying the function withWOLFSSL_TEST_VIS.test_wolfSSL_Tls13_ECH_sni_parse).Added testing from #10542:
Follow-ups
Checklist