Repository navigation
Description
Contact Details
No response
Version
5.9.4
Description
With --enable-dtls13 --enable-ech, a DTLS 1.3 handshake between the example client and server never accepts ECH: the client aborts with ECH_REQUIRED_E (-519), with or without the server's HRR cookie. The same commands over TLS 1.3 work.
The DTLS code paths have several independent problems (links point to v5.9.4-stable):
-
Client: the inner ClientHello and the AAD are taken at TLS offsets. In DTLS the ClientHello body follows a 13-byte record header and a 12-byte handshake header (tls13.c L4930-L4935), but the inner ClientHello is copied from
output + RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ(L5253-L5257) and the same offset is passed toTLSX_FinalizeEchas the AAD (L5344-L5346). Both start 16 bytes early, so they include header bytes that RFC 9849 §5.1/§5.2 exclude, and the inner ClientHello loses the last 16 bytes before its extensions. The server cannot decrypt it ("ECH rejected"). -
Server: the stateless HelloRetryRequest ignores ECH.
--enable-dtls13enablesWOLFSSL_SEND_HRR_COOKIE, and DTLS 1.3 servers turn the cookie on by default (internal.c L2442-L2458), so the first ClientHello is answered bySendStatelessReplyDtls13(dtls.c L668-L972). It never processesencrypted_client_hello, so the HRR carries no confirmation (RFC 9849 §7.2.1) and the client treats ECH as rejected (§6.1.4, tls13.c L6173-L6182). -
Server: ECH ClientHello parsing ignores
legacy_cookie. For DTLS, RFC 9849 §5.1/§5.2 use the ClientHello of RFC 9147 §5.3, which haslegacy_cookieafterlegacy_session_id.TLSX_ECH_CheckInnerPadding(tls.c L14511-L14520),TLSX_ECH_ExpandOuterExtensions(L14742-L14746) andTLSX_ECH_FindOuterExtension(L14575-L14581) parse it with the TLS layout. A correctly encoded inner ClientHello fails withBUFFER_ERROR(-328) right after "ECH accepted"; past that check, extensions referenced byech_outer_extensionsare not found (INVALID_PARAMETER). -
Server: no acceptance confirmation in DTLS.
SendTls13ServerHellocallsEchWriteAcceptanceonly in the TLS branch (tls13.c L8884-L8887); the DTLS branch only hashes the message (L8860-L8865). The server continues with ClientHelloInner while the client, finding no confirmation, continues with ClientHelloOuter, and the handshake stalls. -
The ECH transcript includes DTLS-only header fields.
EchHashHelloInner(tls13.c L4061-L4075) andEchCalcAcceptance(L4149-L4172) hash the full 12-byte DTLS handshake header, includingmessage_seq,fragment_offsetandfragment_length, which RFC 9147 §5.2 excludes from the transcript (asDtls13HashHandshakedoes, dtls13.c L529-L550). This goes unnoticed between two wolfSSL peers, but both the confirmation and the transcript after acceptance differ from those of a peer that follows RFC 9147. In the HRR case,messageHashHeaderreceives a 4-byte header (AddTls13HandShakeHeaderspecial-casesmessage_hash), yet 12 bytes are hashed, 8 of them uninitialized: built with-ftrivial-auto-var-init=pattern, the hashed bytes arefe 00 00 30 fe fe fe fe fe fe fe fe.
With 1, 3 and 4 fixed locally, the handshake with -J n (no HRR cookie) completes with ECH accepted on both sides; with the default cookie it still fails because of 2.
Build: ./configure --enable-dtls13 --enable-ech --enable-debug; x86_64 Linux, GCC 16.2.1.
Reproduction steps
./configure --enable-dtls13 --enable-ech --enable-debug && make./examples/server/server -u -v 4 -d --ech public.example, which printsECH config (base64): <config>./examples/client/client -u -v 4 -d -S secret.example --ech <config>fails with -519 (problem 2).- Repeat with
-J nadded to the server. The client still fails with -519 and the server logs "ECH rejected" (problem 1).
-d on both sides lets the fallback handshake finish: the test certificate does not match these names, and after the rejection the client sends an empty Certificate (RFC 9849 §6.1.7).
Relevant log output
# Excerpts from the --enable-debug output
# Step 3, server
wolfSSL Entering DoClientHelloStateless
wolfSSL Entering DoClientHelloStateless
ECH rejected
# Step 3, client
HelloRetryRequest format
SendAlert: 121 ech_required
wolfSSL_connect error -519, ECH offered but rejected by server
# Step 4 (-J n), server
ECH rejected
# Step 4 (-J n), client
ECH rejected
SendAlert: 121 ech_required
wolfSSL_connect error -519, ECH offered but rejected by server
# Step 4 with 1 fixed locally, server
ECH accepted
SSL_accept error -328, malformed buffer input error
# client
Alert type: decode_error
# Step 4 with 1 and 3 fixed locally, server
ECH accepted
ECH: expanded ech_outer_extensions successfully
# client (the handshake then stalls)
ECH rejected
Activity
sebastian-carpenter commentedon Nov 5, 2026
Hey HMBSbige,
Thank you for the report. Currently, there has not been much work put into ECH with DTLS, so I'm not surprised it doesn't work. I'll add this to our feature request list for the time being.
I'll put up a PR shortly to error/warn when a client or server attempts ECH over DTLS.