From f6bc39881b363a4992d7e4204a232e8a16c4ea4a Mon Sep 17 00:00:00 2001 From: sebastian-carpenter Date: Mon, 27 Apr 2026 11:39:27 -0600 Subject: [PATCH 1/2] tls ech padding improvements --- src/ssl_ech.c | 24 +++++++++++++++++------- src/tls13.c | 16 +++++++++++++++- tests/api.c | 6 ++++-- wolfssl/internal.h | 1 + wolfssl/ssl.h | 3 +++ 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/ssl_ech.c b/src/ssl_ech.c index abe2ab8a55..1dd3bca813 100644 --- a/src/ssl_ech.c +++ b/src/ssl_ech.c @@ -32,6 +32,15 @@ /* create the hpke key and ech config to send to clients */ int wolfSSL_CTX_GenerateEchConfig(WOLFSSL_CTX* ctx, const char* publicName, word16 kemId, word16 kdfId, word16 aeadId) +{ + return wolfSSL_CTX_GenerateEchConfigEx(ctx, publicName, kemId, kdfId, + aeadId, 0); +} + +/* create the hpke key and ech config to send to clients + * maximum_name_length may also be set for a more stable padding length */ +int wolfSSL_CTX_GenerateEchConfigEx(WOLFSSL_CTX* ctx, const char* publicName, + word16 kemId, word16 kdfId, word16 aeadId, byte maxNameLen) { int ret = 0; WOLFSSL_EchConfig* newConfig; @@ -129,8 +138,8 @@ int wolfSSL_CTX_GenerateEchConfig(WOLFSSL_CTX* ctx, const char* publicName, ret = MEMORY_E; } else { - XMEMCPY(newConfig->publicName, publicName, - XSTRLEN(publicName) + 1); + XMEMCPY(newConfig->publicName, publicName, XSTRLEN(publicName) + 1); + newConfig->maxNameLen = maxNameLen; } } @@ -418,8 +427,8 @@ int GetEchConfig(WOLFSSL_EchConfig* config, byte* output, word32* outputLen) output += 2; } - /* set maximum name length to 0 */ - *output = 0; + /* maximum name len */ + *output = config->maxNameLen; output++; /* publicName len */ @@ -430,7 +439,7 @@ int GetEchConfig(WOLFSSL_EchConfig* config, byte* output, word32* outputLen) XMEMCPY(output, config->publicName, publicNameLen); output += publicNameLen; - /* terminating zeros */ + /* no extensions, print zeros */ c16toa(0, output); /* output += 2; */ @@ -656,11 +665,12 @@ int SetEchConfigsEx(WOLFSSL_EchConfig** outputConfigs, void* heap, idx += 4; } - /* ignore maximum name length */ + /* maxNameLen */ if (idx + 1 > length) { ret = BUFFER_E; break; } + workingConfig->maxNameLen = echConfig[idx]; idx += 1; /* publicName */ @@ -701,7 +711,7 @@ int SetEchConfigsEx(WOLFSSL_EchConfig** outputConfigs, void* heap, } ret = EchConfigCheckExtensions(echConfig + idx, extensionsLen); - if (ret < 0) + if (ret < 0 && ret != WC_NO_ERR_TRACE(UNSUPPORTED_EXTENSION)) break; /* KEM, ciphersuite, or mandatory extension not supported, free this diff --git a/src/tls13.c b/src/tls13.c index 794e3e068d..36a8e8e913 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -4817,9 +4817,23 @@ int SendTls13ClientHello(WOLFSSL* ssl) if (ret != 0) return ret; + /* calculate padding (RFC 9849, section 6.1.3) */ + if (args->ech->privateName != NULL) { + word16 nameLen = (word16)XSTRLEN(args->ech->privateName); + if (nameLen > args->ech->echConfig->maxNameLen) + args->ech->paddingLen = 0; + else + args->ech->paddingLen = + (word16)args->ech->echConfig->maxNameLen - nameLen; + } + else { + args->ech->paddingLen = args->ech->echConfig->maxNameLen + 9; + } + /* innerClientHelloLen and padding are based on the * encoded (sealed) inner */ - args->ech->paddingLen = 31 - ((encodedLen - 1) % 32); + args->ech->paddingLen += 31 - + ((encodedLen + args->ech->paddingLen - 1) % 32); args->ech->innerClientHelloLen = encodedLen + args->ech->paddingLen + args->ech->hpke->Nt; diff --git a/tests/api.c b/tests/api.c index c8df1a9f36..020299c28a 100644 --- a/tests/api.c +++ b/tests/api.c @@ -15066,8 +15066,10 @@ static int test_ech_server_ctx_ready(WOLFSSL_CTX* ctx) { int ret; - ret = wolfSSL_CTX_GenerateEchConfig(ctx, echCbTestPublicName, - echCbTestKemID, echCbTestKdfID, echCbTestAeadID); + /* +20 for this isn't significant, it just exercises the padding code */ + ret = wolfSSL_CTX_GenerateEchConfigEx(ctx, echCbTestPublicName, + echCbTestKemID, echCbTestKdfID, echCbTestAeadID, + XSTRLEN(echCbTestPublicName) + 20); if (ret != WOLFSSL_SUCCESS) return TEST_FAIL; diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 5daab5620f..64faa29bfc 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3147,6 +3147,7 @@ typedef struct WOLFSSL_EchConfig { byte configId; byte numCipherSuites; byte receiverPubkey[HPKE_Npk_MAX]; + byte maxNameLen; } WOLFSSL_EchConfig; typedef struct WOLFSSL_ECH { diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 4dd5a6bd55..fad6fe6ed3 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1231,6 +1231,9 @@ WOLFSSL_API WOLFSSL_METHOD *wolfSSLv23_method(void); #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) WOLFSSL_API int wolfSSL_CTX_GenerateEchConfig(WOLFSSL_CTX* ctx, const char* publicName, word16 kemId, word16 kdfId, word16 aeadId); +WOLFSSL_API int wolfSSL_CTX_GenerateEchConfigEx(WOLFSSL_CTX* ctx, + const char* publicName, word16 kemId, word16 kdfId, word16 aeadId, + byte maxNameLen); WOLFSSL_API int wolfSSL_CTX_SetEchConfigsBase64(WOLFSSL_CTX* ctx, const char* echConfigs64, word32 echConfigs64Len); From d029aed38fbdf3639e876df677133b64404f7914 Mon Sep 17 00:00:00 2001 From: sebastian-carpenter Date: Tue, 12 May 2026 11:32:25 -0600 Subject: [PATCH 2/2] shrink ech config decoding for Arduino --- src/ssl_ech.c | 84 ++++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/src/ssl_ech.c b/src/ssl_ech.c index 1dd3bca813..c8dafcc149 100644 --- a/src/ssl_ech.c +++ b/src/ssl_ech.c @@ -175,32 +175,51 @@ int wolfSSL_CTX_GenerateEchConfigEx(WOLFSSL_CTX* ctx, const char* publicName, return ret; } +/* base64-decode echConfigs into a freshly allocated buffer */ +static int DecodeEchConfigsBase64(void* heap, const char* echConfigs64, + word32 echConfigs64Len, byte** decodedConfigs, word32* decodedLen) +{ + int ret = 0; + byte* buf; + word32 len = echConfigs64Len * 3 / 4 + 1; + + if (echConfigs64 == NULL || echConfigs64Len == 0) + return BAD_FUNC_ARG; + + buf = (byte*)XMALLOC(len, heap, DYNAMIC_TYPE_TMP_BUFFER); + + if (buf == NULL) + return MEMORY_E; + + buf[len - 1] = 0; + + /* decode the echConfigs */ + ret = Base64_Decode((const byte*)echConfigs64, echConfigs64Len, buf, &len); + + if (ret != 0) { + XFREE(buf, heap, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + + *decodedConfigs = buf; + *decodedLen = len; + return 0; +} + int wolfSSL_CTX_SetEchConfigsBase64(WOLFSSL_CTX* ctx, const char* echConfigs64, word32 echConfigs64Len) { - int ret = 0; - word32 decodedLen = echConfigs64Len * 3 / 4 + 1; + int ret; + word32 decodedLen; byte* decodedConfigs; - if (ctx == NULL || echConfigs64 == NULL || echConfigs64Len == 0) + if (ctx == NULL) return BAD_FUNC_ARG; - decodedConfigs = (byte*)XMALLOC(decodedLen, ctx->heap, - DYNAMIC_TYPE_TMP_BUFFER); - - if (decodedConfigs == NULL) - return MEMORY_E; - - decodedConfigs[decodedLen - 1] = 0; - - /* decode the echConfigs */ - ret = Base64_Decode((const byte*)echConfigs64, echConfigs64Len, - decodedConfigs, &decodedLen); - - if (ret != 0) { - XFREE(decodedConfigs, ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); + ret = DecodeEchConfigsBase64(ctx->heap, echConfigs64, echConfigs64Len, + &decodedConfigs, &decodedLen); + if (ret != 0) return ret; - } ret = wolfSSL_CTX_SetEchConfigs(ctx, decodedConfigs, decodedLen); @@ -258,34 +277,17 @@ void wolfSSL_CTX_SetEchEnable(WOLFSSL_CTX* ctx, byte enable) int wolfSSL_SetEchConfigsBase64(WOLFSSL* ssl, const char* echConfigs64, word32 echConfigs64Len) { - int ret = 0; - word32 decodedLen = echConfigs64Len * 3 / 4 + 1; + int ret; + word32 decodedLen; byte* decodedConfigs; - if (ssl == NULL || echConfigs64 == NULL || echConfigs64Len == 0) + if (ssl == NULL) return BAD_FUNC_ARG; - /* already have ech configs */ - if (ssl->echConfigs != NULL) { - return WOLFSSL_FATAL_ERROR; - } - - decodedConfigs = (byte*)XMALLOC(decodedLen, ssl->heap, - DYNAMIC_TYPE_TMP_BUFFER); - - if (decodedConfigs == NULL) - return MEMORY_E; - - decodedConfigs[decodedLen - 1] = 0; - - /* decode the echConfigs */ - ret = Base64_Decode((const byte*)echConfigs64, echConfigs64Len, - decodedConfigs, &decodedLen); - - if (ret != 0) { - XFREE(decodedConfigs, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); + ret = DecodeEchConfigsBase64(ssl->heap, echConfigs64, echConfigs64Len, + &decodedConfigs, &decodedLen); + if (ret != 0) return ret; - } ret = wolfSSL_SetEchConfigs(ssl, decodedConfigs, decodedLen);