Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e545193
callback for testing TLS ECH: client side hook
sebastian-carpenter Mar 25, 2026
6712d1e
add ech_required abort
sebastian-carpenter Mar 27, 2026
5306a1f
fix return value
sebastian-carpenter Mar 31, 2026
9d05c57
reject configs on grease connection
sebastian-carpenter Mar 31, 2026
1925a80
error when missing ECH extension after HRR
sebastian-carpenter Mar 31, 2026
aa331ff
more compliance fixes for ECH
sebastian-carpenter Apr 1, 2026
07f6769
return empty cert
sebastian-carpenter Apr 2, 2026
52e0601
remove server ech testing callback
sebastian-carpenter Apr 2, 2026
bfb603d
prevent client inner from offering downgrade
sebastian-carpenter Apr 3, 2026
746e864
parse ech config extensions
sebastian-carpenter Apr 3, 2026
06583b9
verify ech config on grease, ignore session ticket
sebastian-carpenter Apr 3, 2026
82c3533
allow ech rejection through hrr
sebastian-carpenter Apr 3, 2026
2e52a98
do not resume in outer hello
sebastian-carpenter Apr 3, 2026
0253558
cert authentication on echConfig publicName
sebastian-carpenter Apr 6, 2026
4d6ed1a
no ech_outer_extensions in inner or outer hello
sebastian-carpenter Apr 6, 2026
98bf7dc
tweaks to errors, tests, c89 declarations
sebastian-carpenter Apr 7, 2026
1f00d99
potentially uninitialized var
sebastian-carpenter Apr 7, 2026
3bb5495
change ECH testing macro to WOLFSSL_TEST_ECH
sebastian-carpenter Apr 8, 2026
bad64ea
force peer verify (on client) in ech cert test
sebastian-carpenter Apr 8, 2026
b3dfade
stricter NULL checks
sebastian-carpenter Apr 8, 2026
1ca4edc
unify domain name checking
sebastian-carpenter Apr 13, 2026
9f07865
ECH retry configs
sebastian-carpenter Apr 15, 2026
e450b65
extra HRR testing for ECH
sebastian-carpenter Apr 16, 2026
1051590
better errors when setting ech configs
sebastian-carpenter Apr 16, 2026
e77a5f2
improve ECH tests even further
sebastian-carpenter Apr 16, 2026
04bd3a7
skip ECH decrypt when ECH was rejected
sebastian-carpenter Apr 16, 2026
9b4eb15
correct error propagation for unsupported KEM
sebastian-carpenter Apr 17, 2026
d5ba0cb
remove bad symbols
sebastian-carpenter Apr 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/openssl-ech.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ jobs:
with:
path: wolfssl
configure: >-
--enable-ech --enable-sha512 --enable-aes CFLAGS='-DUSE_FLAT_TEST_H'
--enable-ech --enable-sha512 --enable-aes
CFLAGS='-DUSE_FLAT_TEST_H -DWOLFSSL_TEST_ECH'
check: true
install: true

- name: tar build-dir
Expand Down
72 changes: 52 additions & 20 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -8773,6 +8773,10 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl)
FreeEchConfigs(ssl->echConfigs, ssl->heap);
ssl->echConfigs = NULL;
}
if (ssl->echRetryConfigs != NULL) {
FreeEchConfigs(ssl->echRetryConfigs, ssl->heap);
ssl->echRetryConfigs = NULL;
}
#endif /* HAVE_ECH */
#endif /* WOLFSSL_TLS13 */
#ifdef WOLFSSL_HAVE_TLS_UNIQUE
Expand Down Expand Up @@ -15713,6 +15717,8 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
byte* subjectHash = NULL;
int alreadySigner = 0;

char* domainName = NULL;

#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
int addToPendingCAs = 0;
#endif
Expand Down Expand Up @@ -16901,17 +16907,34 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
}
#endif

if (!ssl->options.verifyNone && ssl->buffers.domainName.buffer) {
domainName = (char*)ssl->buffers.domainName.buffer;
#if !defined(NO_WOLFSSL_CLIENT) && defined(HAVE_ECH)
/* RFC 9849 s6.1.7: ECH offered but rejected by the server...
* verify cert is valid for ECHConfig.public_name */
if (ssl->options.side == WOLFSSL_CLIENT_END &&
ssl->echConfigs != NULL &&
!ssl->options.echAccepted) {
TLSX* echX = TLSX_Find(ssl->extensions, TLSX_ECH);
if (echX != NULL && echX->data != NULL) {
WOLFSSL_ECH* ech = (WOLFSSL_ECH*)echX->data;
if (ech->echConfig != NULL &&
ech->echConfig->publicName != NULL) {
domainName = ech->echConfig->publicName;
}
}
}
#endif

if (!ssl->options.verifyNone && domainName) {
#ifndef WOLFSSL_ALLOW_NO_CN_IN_SAN
/* Per RFC 5280 section 4.2.1.6, "Whenever such identities
* are to be bound into a certificate, the subject
* alternative name extension MUST be used." */
if (args->dCert->altNames) {
if (CheckForAltNames(args->dCert,
(char*)ssl->buffers.domainName.buffer,
(ssl->buffers.domainName.buffer == NULL ? 0 :
(word32)XSTRLEN(
(const char *)ssl->buffers.domainName.buffer)),
if (CheckForAltNames(
args->dCert,
domainName,
(word32)XSTRLEN((const char *)domainName),
NULL, 0, 0) != 1) {
WOLFSSL_MSG("DomainName match on alt names failed");
/* try to get peer key still */
Expand All @@ -16924,11 +16947,9 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
if (MatchDomainName(
args->dCert->subjectCN,
args->dCert->subjectCNLen,
(char*)ssl->buffers.domainName.buffer,
(ssl->buffers.domainName.buffer == NULL ? 0 :
(word32)XSTRLEN(
(const char *)ssl->buffers.domainName.buffer)
), 0) == 0)
domainName,
(word32)XSTRLEN((const char *)domainName),
0) == 0)
#endif
{
WOLFSSL_MSG("DomainName match failed");
Expand All @@ -16939,18 +16960,19 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
#else /* WOLFSSL_ALL_NO_CN_IN_SAN */
/* Old behavior. */
#ifndef WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY
if (MatchDomainName(args->dCert->subjectCN,
if (MatchDomainName(
args->dCert->subjectCN,
args->dCert->subjectCNLen,
(char*)ssl->buffers.domainName.buffer,
(ssl->buffers.domainName.buffer == NULL ? 0 :
(word32)XSTRLEN(ssl->buffers.domainName.buffer)), 0) == 0)
domainName,
(word32)XSTRLEN((const char *)domainName),
0) == 0)
#endif
{
if (CheckForAltNames(args->dCert,
(char*)ssl->buffers.domainName.buffer,
(ssl->buffers.domainName.buffer == NULL ? 0 :
(word32)XSTRLEN(ssl->buffers.domainName.buffer)),
NULL, 0, 0) != 1) {
if (CheckForAltNames(
args->dCert,
domainName,
(word32)XSTRLEN((const char *)domainName),
NULL, 0, 0) != 1) {
WOLFSSL_MSG("DomainName match failed");
/* try to get peer key still */
ret = DOMAIN_NAME_MISMATCH;
Expand Down Expand Up @@ -22160,6 +22182,13 @@ const char* AlertTypeToString(int type)
return no_application_protocol_str;
}

case ech_required:
{
static const char ech_required_str[] =
"ech_required";
return ech_required_str;
}

default:
WOLFSSL_MSG("Unknown Alert");
return NULL;
Expand Down Expand Up @@ -27766,6 +27795,9 @@ const char* wolfSSL_ERR_reason_error_string(unsigned long e)

case SESSION_TICKET_NONCE_OVERFLOW:
return "Session ticket nonce overflow";

case ECH_REQUIRED_E:
return "ECH offered but rejected by server";
}

return "unknown error number";
Expand Down
Loading
Loading