diff --git a/src/tls.c b/src/tls.c index 62118d0678..2d5725c2b3 100644 --- a/src/tls.c +++ b/src/tls.c @@ -2362,13 +2362,13 @@ static int TLSX_SNI_Parse(WOLFSSL* ssl, const byte* input, word16 length, word16 offset = 0; int cacheOnly = 0; SNI *sni = NULL; + const char *hostName = NULL; byte type; byte matched; #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) TLSX* echX = NULL; WOLFSSL_ECH* ech = NULL; - WOLFSSL_EchConfig* workingConfig; - word16 privateNameLen; + WOLFSSL_EchConfig* workingConfig = NULL; #endif #endif /* !NO_WOLFSSL_SERVER */ TLSX *extension = TLSX_Find(ssl->extensions, TLSX_SERVER_NAME); @@ -2409,13 +2409,7 @@ static int TLSX_SNI_Parse(WOLFSSL* ssl, const byte* input, word16 length, } #endif -#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) - if ((!extension || !extension->data) || - (ech != NULL && ech->sniState == ECH_INNER_SNI && - ech->privateName == NULL)) { -#else if (!extension || !extension->data) { -#endif /* This will keep SNI even though TLSX_UseSNI has not been called. * Enable it so that the received sni is available to functions * that use a custom callback when SNI is received. @@ -2463,28 +2457,6 @@ static int TLSX_SNI_Parse(WOLFSSL* ssl, const byte* input, word16 length, if (!cacheOnly && !(sni = TLSX_SNI_Find((SNI*)extension->data, type))) return 0; /* not using this type of SNI. */ -#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) - if (ech != NULL && ech->sniState == ECH_INNER_SNI){ - /* SNI status is carried over from processing the outer hello so it is - * necessary to clear it before processing the inner hello */ - ech->sniState = ECH_INNER_SNI_ATTEMPT; - if (sni != NULL){ - sni->status = WOLFSSL_SNI_NO_MATCH; - } - } - else if (ech != NULL && ech->sniState == ECH_OUTER_SNI && - ech->privateName == NULL && sni != NULL){ - /* save the private SNI before it is overwritten by the public SNI */ - privateNameLen = (word16)XSTRLEN(sni->data.host_name) + 1; - ech->privateName = (char*)XMALLOC(privateNameLen, ssl->heap, - DYNAMIC_TYPE_TMP_BUFFER); - if (ech->privateName == NULL) - return MEMORY_E; - XMEMCPY((char*)ech->privateName, sni->data.host_name, - privateNameLen); - } -#endif - #if defined(WOLFSSL_TLS13) /* Don't process the second ClientHello SNI extension if there * was problems with the first. @@ -2493,42 +2465,46 @@ static int TLSX_SNI_Parse(WOLFSSL* ssl, const byte* input, word16 length, return 0; #endif -#if defined(HAVE_ECH) - if (ech != NULL && ech->sniState == ECH_INNER_SNI_ATTEMPT && - ech->privateName != NULL) { - matched = cacheOnly || (XSTRLEN(ech->privateName) == size && - XSTRNCMP(ech->privateName, (const char*)input + offset, size) == 0); - } - else -#endif - { - const char* hostName = (sni != NULL) ? sni->data.host_name : NULL; - matched = cacheOnly || (hostName != NULL && - XSTRLEN(hostName) == size && - XSTRNCMP(hostName, (const char*)input + offset, size) == 0); - } + hostName = (sni != NULL) ? sni->data.host_name : NULL; + matched = (hostName != NULL && + XSTRLEN(hostName) == size && + XSTRNCMP(hostName, (const char*)input + offset, size) == 0); #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) - if (!matched && ech != NULL && ech->sniState == ECH_OUTER_SNI) { + /* While parsing the outer CH accept a match against any + * echConfig publicName */ + if (!matched && ech != NULL && !ssl->options.echProcessingInner) { workingConfig = ech->echConfig; while (workingConfig != NULL) { matched = XSTRLEN(workingConfig->publicName) == size && XSTRNCMP(workingConfig->publicName, (const char*)input + offset, size) == 0; - if (matched) break; - workingConfig = workingConfig->next; } + + /* If a publicName is matched then this SNI is not something that should + * be forcibly cached */ + if (matched) + cacheOnly = 0; } #endif + if (!matched) + matched = cacheOnly; + if (matched || (sni != NULL && (sni->options & WOLFSSL_SNI_ANSWER_ON_MISMATCH))) { int matchStat; - int r = TLSX_UseSNI(&ssl->extensions, type, input + offset, size, - ssl->heap); + int r; + TLSX** writeList = &ssl->extensions; +#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) + if (workingConfig != NULL) + writeList = &ech->extensions; +#endif + + r = TLSX_UseSNI(writeList, type, input + offset, size, ssl->heap); if (r != WOLFSSL_SUCCESS) return r; /* throws error. */ @@ -2546,10 +2522,14 @@ static int TLSX_SNI_Parse(WOLFSSL* ssl, const byte* input, word16 length, matchStat = WOLFSSL_SNI_FAKE_MATCH; } - TLSX_SNI_SetStatus(ssl->extensions, type, (byte)matchStat); + TLSX_SNI_SetStatus(*writeList, type, (byte)matchStat); - if (!cacheOnly) - TLSX_SetResponse(ssl, TLSX_SERVER_NAME); + if (!cacheOnly) { + extension = TLSX_Find(*writeList, TLSX_SERVER_NAME); + + if (extension) + extension->resp = 1; + } } else if ((sni == NULL) || !(sni->options & WOLFSSL_SNI_CONTINUE_ON_MISMATCH)) { @@ -13732,19 +13712,26 @@ static int TLSX_ECH_Use(WOLFSSL_EchConfig* echConfig, TLSX** extensions, XFREE(ech, heap, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } + ForceZero(ech->hpke, sizeof(Hpke)); ret = wc_HpkeInit(ech->hpke, ech->kemId, ech->cipherSuite.kdfId, ech->cipherSuite.aeadId, heap); /* setup the ephemeralKey */ if (ret == 0) ret = wc_HpkeGenerateKeyPair(ech->hpke, &ech->ephemeralKey, rng); + /* use the chosen config's public name for the outer SNI */ if (ret == 0) { - ret = TLSX_Push(extensions, TLSX_ECH, ech, heap); - if (ret != 0) { - wc_HpkeFreeKey(ech->hpke, ech->hpke->kem, ech->ephemeralKey, - ech->hpke->heap); - } + ret = TLSX_UseSNI(&ech->extensions, WOLFSSL_SNI_HOST_NAME, + echConfig->publicName, (word16)XSTRLEN(echConfig->publicName), + heap); + if (ret == WOLFSSL_SUCCESS) + ret = 0; } + if (ret == 0) + ret = TLSX_Push(extensions, TLSX_ECH, ech, heap); if (ret != 0) { + TLSX_FreeAll(ech->extensions, heap); + wc_HpkeFreeKey(ech->hpke, ech->hpke->kem, ech->ephemeralKey, + ech->hpke->heap); XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(ech, heap, DYNAMIC_TYPE_TMP_BUFFER); } @@ -14716,15 +14703,13 @@ static void TLSX_ECH_Free(WOLFSSL_ECH* ech, void* heap) { XFREE(ech->innerClientHello, heap, DYNAMIC_TYPE_TMP_BUFFER); if (ech->hpke != NULL) { - if (ech->ephemeralKey != NULL) - wc_HpkeFreeKey(ech->hpke, ech->hpke->kem, ech->ephemeralKey, - ech->hpke->heap); + wc_HpkeFreeKey(ech->hpke, ech->hpke->kem, ech->ephemeralKey, + ech->hpke->heap); XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER); } if (ech->hpkeContext != NULL) XFREE(ech->hpkeContext, heap, DYNAMIC_TYPE_TMP_BUFFER); - if (ech->privateName != NULL) - XFREE((char*)ech->privateName, heap, DYNAMIC_TYPE_TMP_BUFFER); + TLSX_FreeAll(ech->extensions, heap); XFREE(ech, heap, DYNAMIC_TYPE_TMP_BUFFER); (void)heap; @@ -16441,97 +16426,95 @@ int TLSX_PopulateExtensions(WOLFSSL* ssl, byte isServer) #if defined(WOLFSSL_TLS13) || !defined(NO_WOLFSSL_CLIENT) #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) -static int TLSX_EchChangeSNI(WOLFSSL* ssl, TLSX** pEchX, - char* serverName, TLSX** pServerNameX, - TLSX*** pExtensions) +/* Returns 1 if the extensions should be hidden for this write */ +static int TLSX_EchShouldHideInner(WOLFSSL_ECH* ech) { - int ret = 0; - TLSX* echX = NULL; - TLSX* serverNameX = NULL; - TLSX** extensions = NULL; - - /* calculate the rest of the extensions length with inner ech */ - if (ssl->extensions) - echX = TLSX_Find(ssl->extensions, TLSX_ECH); - - if (echX == NULL && ssl->ctx && ssl->ctx->extensions) - /* if not NULL the semaphore will stop it from being counted */ - echX = TLSX_Find(ssl->ctx->extensions, TLSX_ECH); - - /* if type is outer change sni to public name */ - if (echX != NULL && - ((WOLFSSL_ECH*)echX->data)->type == ECH_TYPE_OUTER && - (ssl->options.echAccepted || - ((WOLFSSL_ECH*)echX->data)->innerCount == 0)) { - if (ssl->extensions) { - serverNameX = TLSX_Find(ssl->extensions, TLSX_SERVER_NAME); - - if (serverNameX != NULL) - extensions = &ssl->extensions; - } - - if (serverNameX == NULL && ssl->ctx && ssl->ctx->extensions) { - serverNameX = TLSX_Find(ssl->ctx->extensions, TLSX_SERVER_NAME); - if (serverNameX != NULL) - extensions = &ssl->ctx->extensions; - } + if (ech == NULL || ech->type != ECH_TYPE_OUTER) + return 0; + return 1; +} - /* ECH requires an inner SNI to be present for ClientHelloInner. - * Without it, fail instead of mutating extension lists. */ - if (serverNameX == NULL) { - ret = BAD_FUNC_ARG; +/* Swap matching extension types between *sslExts and *echExts. + * Non-matched extensions in *echExts are prepended to *sslExts + * popCount is the number of leading extensions to move from + * *sslExts to *echExts + * + * Ordering is kept in mind for OuterExtensions. This is why the leading + * popCount extensions are 'reversed' off the list. + * + * Returns a count of extensions prepended to sslExts. */ +static word16 TLSX_EchSwapExtensions(TLSX** sslExts, TLSX** echExts, + word16 popCount) +{ + TLSX* chunk = NULL; + TLSX* node; + TLSX* outer; + TLSX* inner; + TLSX** outerLink; + TLSX** innerLink; + word16 prepended = 0; + + /* unhook popCount nodes off *sslExts head into chunk. + * Head-prepend undoes the reversal caused by appending onto sslExts. */ + while (popCount > 0 && *sslExts != NULL) { + node = *sslExts; + *sslExts = node->next; + node->next = chunk; + chunk = node; + popCount--; + } + + outerLink = echExts; + while (*outerLink != NULL) { + innerLink = sslExts; + outer = *outerLink; + + while (*innerLink != NULL && (*innerLink)->type != outer->type) + innerLink = &(*innerLink)->next; + + if (*innerLink != NULL) { + inner = *innerLink; + + *innerLink = outer; + *outerLink = inner; + node = outer->next; + outer->next = inner->next; + inner->next = node; + + outerLink = &inner->next; } - - /* store the inner server name */ - if (ret == 0 && serverNameX != NULL) { - char* hostName = ((SNI*)serverNameX->data)->data.host_name; - word32 hostNameSz = (word32)XSTRLEN(hostName) + 1; - - if (hostNameSz > WOLFSSL_HOST_NAME_MAX) - ret = BAD_LENGTH_E; - else - XMEMCPY(serverName, hostName, hostNameSz); + else { + *outerLink = outer->next; + outer->next = *sslExts; + *sslExts = outer; + prepended++; } + } - /* only swap the SNI if one was found; extensions is non-NULL if an - * SNI entry was found on ssl->extensions or ctx->extensions */ - if (ret == 0 && extensions != NULL) { - /* remove the inner server name */ - TLSX_Remove(extensions, TLSX_SERVER_NAME, ssl->heap); + /* outerLink is at the tail of *echExts; append the chunk */ + *outerLink = chunk; - /* set the public name as the server name */ - if ((ret = TLSX_UseSNI(extensions, WOLFSSL_SNI_HOST_NAME, - ((WOLFSSL_ECH*)echX->data)->echConfig->publicName, - XSTRLEN(((WOLFSSL_ECH*)echX->data)->echConfig->publicName), - ssl->heap)) == WOLFSSL_SUCCESS) - ret = 0; - } - } - *pServerNameX = serverNameX; - *pExtensions = extensions; - *pEchX = echX; - return ret; + return prepended; } -static int TLSX_EchRestoreSNI(WOLFSSL* ssl, char* serverName, - TLSX* serverNameX, TLSX** extensions) +/* If ECH is accepted, delete ech->extensions + * If rejected, replace matching ssl->extensions with ech->extensions, + * prepending to head if necessary */ +void TLSX_EchReplaceExtensions(WOLFSSL* ssl, byte accepted) { - int ret = 0; + TLSX* echX; + WOLFSSL_ECH* ech; - /* always remove the publicName SNI we injected, regardless of whether - * there was a prior inner SNI to restore */ - if (extensions != NULL) - TLSX_Remove(extensions, TLSX_SERVER_NAME, ssl->heap); + echX = TLSX_Find(ssl->extensions, TLSX_ECH); + if (echX == NULL || echX->data == NULL) + return; + ech = (WOLFSSL_ECH*)echX->data; - if (serverNameX != NULL) { - /* restore the inner server name */ - ret = TLSX_UseSNI(extensions, WOLFSSL_SNI_HOST_NAME, - serverName, XSTRLEN(serverName), ssl->heap); + if (!accepted) + (void)TLSX_EchSwapExtensions(&ssl->extensions, &ech->extensions, 0); - if (ret == WOLFSSL_SUCCESS) - ret = 0; - } - return ret; + TLSX_FreeAll(ech->extensions, ssl->heap); + ech->extensions = NULL; } /* Returns 1 if the extension may be encoded into ech_outer_extensions, @@ -16641,45 +16624,47 @@ static int TLSX_ECH_BuildOuterExtensions(WOLFSSL* ssl, const byte* semaphore, static int TLSX_GetSizeWithEch(WOLFSSL* ssl, byte* semaphore, byte msgType, word16* pLength) { - int ret = 0, r = 0; + int ret = 0; TLSX* echX = NULL; - TLSX* serverNameX = NULL; - TLSX** extensions = NULL; WOLFSSL_ECH* ech = NULL; word16 count = 0; - WC_DECLARE_VAR(serverName, char, WOLFSSL_HOST_NAME_MAX, 0); - - WC_ALLOC_VAR_EX(serverName, char, WOLFSSL_HOST_NAME_MAX, NULL, - DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E); - - r = TLSX_EchChangeSNI(ssl, &echX, serverName, &serverNameX, &extensions); + word16 prepended = 0; + byte installed = 0; + if (ssl->extensions) + echX = TLSX_Find(ssl->extensions, TLSX_ECH); if (echX != NULL) ech = (WOLFSSL_ECH*)echX->data; + if (TLSX_EchShouldHideInner(ech)) { + prepended = TLSX_EchSwapExtensions(&ssl->extensions, + &ech->extensions, 0); + installed = 1; + } + /* If ECH won't be written exclude it from the size calculation */ - if (r == 0 && !ssl->options.echAccepted && ech != NULL && - ech->innerCount != 0) { + if (!ssl->options.echAccepted && ech != NULL && ech->innerCount != 0) { TURN_ON(semaphore, TLSX_ToSemaphore(echX->type)); } /* if encoding, then count encoded form of inner ClientHello. * `semaphore` is in/out so encodable extensions will later be ignored */ - if (r == 0 && ech != NULL && ech->type == ECH_TYPE_INNER && - ech->writeEncoded) { + if (ech != NULL && ech->type == ECH_TYPE_INNER && ech->writeEncoded) { ret = TLSX_ECH_BuildOuterExtensions(ssl, semaphore, msgType, NULL, pLength, &count, semaphore); } - if (r == 0 && ret == 0 && ssl->extensions) + if (ret == 0 && ssl->extensions) ret = TLSX_GetSize(ssl->extensions, semaphore, msgType, pLength); - if (r == 0 && ret == 0 && ssl->ctx && ssl->ctx->extensions) + if (ret == 0 && ssl->ctx && ssl->ctx->extensions) ret = TLSX_GetSize(ssl->ctx->extensions, semaphore, msgType, pLength); - if (r == 0) - r = TLSX_EchRestoreSNI(ssl, serverName, serverNameX, extensions); - WC_FREE_VAR_EX(serverName, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); - if (ret == 0 && r != 0) - ret = r; + if (installed) { + prepended = TLSX_EchSwapExtensions(&ssl->extensions, &ech->extensions, + prepended); + if (ret == 0 && prepended != 0) { + ret = BAD_STATE_E; + } + } return ret; } #endif @@ -16810,19 +16795,24 @@ int TLSX_GetRequestSize(WOLFSSL* ssl, byte msgType, word32* pLength) static int TLSX_WriteWithEch(WOLFSSL* ssl, byte* output, byte* semaphore, byte msgType, word16* pOffset) { - int r = 0, ret = 0; + int ret = 0; TLSX* echX = NULL; - TLSX* serverNameX = NULL; - TLSX** extensions = NULL; WOLFSSL_ECH* ech = NULL; - WC_DECLARE_VAR(serverName, char, WOLFSSL_HOST_NAME_MAX, 0); + word16 prepended = 0; + byte installed = 0; - WC_ALLOC_VAR_EX(serverName, char, WOLFSSL_HOST_NAME_MAX, NULL, - DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E); - r = TLSX_EchChangeSNI(ssl, &echX, serverName, &serverNameX, &extensions); - ret = r; - if (ret == 0 && echX != NULL) { + if (ssl->extensions) + echX = TLSX_Find(ssl->extensions, TLSX_ECH); + if (echX != NULL) ech = (WOLFSSL_ECH*)echX->data; + + if (TLSX_EchShouldHideInner(ech)) { + prepended = TLSX_EchSwapExtensions(&ssl->extensions, + &ech->extensions, 0); + installed = 1; + } + + if (echX != NULL) { /* turn ech on so it doesn't write, then write it last */ TURN_ON(semaphore, TLSX_ToSemaphore(echX->type)); } @@ -16870,15 +16860,12 @@ static int TLSX_WriteWithEch(WOLFSSL* ssl, byte* output, byte* semaphore, } /* only write ECH if there is a shot at acceptance */ - if (ret == 0 && echX != NULL && - (ssl->options.echAccepted || - ((WOLFSSL_ECH*)echX->data)->innerCount == 0)) { - if (echX != NULL) { - /* turn off and write it last */ - TURN_OFF(semaphore, TLSX_ToSemaphore(echX->type)); - } + if (ret == 0 && ech != NULL && + (ssl->options.echAccepted || ech->innerCount == 0)) { + /* turn off and write it last */ + TURN_OFF(semaphore, TLSX_ToSemaphore(echX->type)); - if (ret == 0 && ssl->extensions) { + if (ssl->extensions) { ret = TLSX_Write(ssl->extensions, output + *pOffset, semaphore, msgType, pOffset); } @@ -16889,12 +16876,13 @@ static int TLSX_WriteWithEch(WOLFSSL* ssl, byte* output, byte* semaphore, } } - if (r == 0) - r = TLSX_EchRestoreSNI(ssl, serverName, serverNameX, extensions); - WC_FREE_VAR_EX(serverName, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); - - if (ret == 0 && r != 0) - ret = r; + if (installed) { + prepended = TLSX_EchSwapExtensions(&ssl->extensions, &ech->extensions, + prepended); + if (ret == 0 && prepended != 0) { + ret = BAD_STATE_E; + } + } return ret; } #endif diff --git a/src/tls13.c b/src/tls13.c index 8d7efb9df4..666c45ead4 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -4589,7 +4589,7 @@ typedef struct Sch13Args { word32 length; #if defined(HAVE_ECH) int clientRandomOffset; - int preXLength; + word32 preXLength; word32 expandedInnerLen; WOLFSSL_ECH* ech; #endif @@ -4783,6 +4783,8 @@ int SendTls13ClientHello(WOLFSSL* ssl) #if defined(HAVE_ECH) if (ssl->echConfigs != NULL && !ssl->options.disableECH) { TLSX* echX = TLSX_Find(ssl->extensions, TLSX_ECH); + void* hostName = NULL; + word16 nameLen; if (echX == NULL) return WOLFSSL_FATAL_ERROR; @@ -4801,7 +4803,7 @@ int SendTls13ClientHello(WOLFSSL* ssl) /* set the type to inner */ args->ech->type = ECH_TYPE_INNER; - args->preXLength = (int)args->length; + args->preXLength = args->length; /* get expanded inner size (used for transcript) */ ret = TLSX_GetRequestSize(ssl, client_hello, &args->length); @@ -4831,8 +4833,9 @@ int SendTls13ClientHello(WOLFSSL* ssl) return ret; /* calculate padding (RFC 9849, section 6.1.3) */ - if (args->ech->privateName != NULL) { - word16 nameLen = (word16)XSTRLEN(args->ech->privateName); + nameLen = TLSX_SNI_GetRequest(ssl->extensions, + WOLFSSL_SNI_HOST_NAME, &hostName, 1); + if (hostName != NULL) { if (nameLen > args->ech->echConfig->maxNameLen) args->ech->paddingLen = 0; else @@ -4854,7 +4857,7 @@ int SendTls13ClientHello(WOLFSSL* ssl) return BUFFER_E; /* restore the length to pre-ClientHelloInner computations */ - args->length = (word32)args->preXLength; + args->length = args->preXLength; } } #endif @@ -5292,6 +5295,12 @@ static int EchCheckAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz, ssl->hsHashes = tmpHashes; } + /* Skip only when the HRR signals ECH acceptance + * -> CH2 still needs ech->extensions for inner/outer extension swap + * during write */ + if (msgType != hello_retry_request || !ssl->options.echAccepted) + TLSX_EchReplaceExtensions(ssl, ssl->options.echAccepted); + return ret; } #endif /* HAVE_ECH */ @@ -5857,6 +5866,8 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, /* server rejected ECH, fallback to outer */ Free_HS_Hashes(ssl->hsHashesEch, ssl->heap); ssl->hsHashesEch = NULL; + /* EchCheckAcceptance is bypassed, so replace extensions now */ + TLSX_EchReplaceExtensions(ssl, 0); } else { /* account for hrr extension instead of server random */ @@ -7581,15 +7592,18 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #if defined(HAVE_ECH) if (!ssl->options.echProcessingInner && echX != NULL && - ((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE) { - if (((WOLFSSL_ECH*)echX->data)->innerClientHello != NULL) { + ssl->ctx->echConfigs != NULL && !ssl->options.disableECH) { + if (((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE && + ((WOLFSSL_ECH*)echX->data)->innerClientHello != NULL) { + /* ECH accepted: use private extensions */ + TLSX_EchReplaceExtensions(ssl, ssl->options.echAccepted); /* Client sent real ECH and inner hello was decrypted, jump to * exit so the caller can re-invoke with the inner hello */ goto exit_dch; } else { - /* If ECH was accepted in ClientHello1 then ClientHello2 MUST - * contain an ECH extension */ + /* If ECH was accepted in CH1 then CH2 MUST contain + * an ECH extension */ if (ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE && ssl->options.echAccepted) { @@ -7597,10 +7611,16 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, "extension"); ERROR_OUT(INCOMPLETE_DATA, exit_dch); } - /* Server has ECH but client did not send ECH. Clear the - * response flag so the empty ECH extension is not written - * in EncryptedExtensions. */ - echX->resp = 0; + + /* Otherwise ECH rejected: use public extensions */ + if (((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE) { + TLSX_EchReplaceExtensions(ssl, ssl->options.echAccepted); + echX->resp = 0; + } + else if (((WOLFSSL_ECH*)echX->data)->state == + ECH_WRITE_RETRY_CONFIGS) { + TLSX_EchReplaceExtensions(ssl, ssl->options.echAccepted); + } } } #endif @@ -13661,18 +13681,12 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, *inOutIdx = echInOutIdx; /* call again with the inner hello */ if (ret == 0) { - if (((WOLFSSL_ECH*)echX->data)->sniState == ECH_OUTER_SNI) { - ((WOLFSSL_ECH*)echX->data)->sniState = ECH_INNER_SNI; - } - ssl->options.echProcessingInner = 1; ret = DoTls13ClientHello(ssl, ((WOLFSSL_ECH*)echX->data)->innerClientHello, &echInOutIdx, ((WOLFSSL_ECH*)echX->data)->innerClientHelloLen); ssl->options.echProcessingInner = 0; - - ((WOLFSSL_ECH*)echX->data)->sniState = ECH_SNI_DONE; } if (ret == 0 && ((WOLFSSL_ECH*)echX->data)->state != ECH_PARSED_INTERNAL) { diff --git a/tests/api.c b/tests/api.c index a2873fd747..b89a472fd3 100644 --- a/tests/api.c +++ b/tests/api.c @@ -7739,17 +7739,37 @@ static int test_wolfSSL_UseSNI_params(void) ExpectNotNull(ssl); /* invalid [ctx|ssl] */ - ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_UseSNI(NULL, 0, "ctx", 3)); - ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_UseSNI( NULL, 0, "ssl", 3)); + ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_UseSNI(NULL, WOLFSSL_SNI_HOST_NAME, + "ctx", 3)); + ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_UseSNI( NULL, WOLFSSL_SNI_HOST_NAME, + "ssl", 3)); /* invalid type */ ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_UseSNI(ctx, (byte)-1, "ctx", 3)); ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_UseSNI( ssl, (byte)-1, "ssl", 3)); /* invalid data */ - ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_UseSNI(ctx, 0, NULL, 3)); - ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_UseSNI( ssl, 0, NULL, 3)); + ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_UseSNI(ctx, WOLFSSL_SNI_HOST_NAME, + NULL, 3)); + ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_UseSNI( ssl, WOLFSSL_SNI_HOST_NAME, + NULL, 3)); + /* invalid length */ + if (EXPECT_SUCCESS()) { + /* 300 chars > WOLFSSL_HOST_NAME_MAX (256) */ + char longName[300]; + + XMEMSET(longName, 'a', sizeof(longName) - 1); + longName[sizeof(longName) - 1] = '\0'; + + /* host name >= WOLFSSL_HOST_NAME_MAX */ + ExpectIntEQ(BAD_LENGTH_E, wolfSSL_CTX_UseSNI(ctx, WOLFSSL_SNI_HOST_NAME, + longName, (word16)XSTRLEN(longName))); + ExpectIntEQ(BAD_LENGTH_E, wolfSSL_UseSNI( ssl, WOLFSSL_SNI_HOST_NAME, + longName, (word16)XSTRLEN(longName))); + } /* success case */ - ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_UseSNI(ctx, 0, "ctx", 3)); - ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_UseSNI( ssl, 0, "ssl", 3)); + ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_UseSNI(ctx, WOLFSSL_SNI_HOST_NAME, + "ctx", 3)); + ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_UseSNI( ssl, WOLFSSL_SNI_HOST_NAME, + "ssl", 3)); wolfSSL_free(ssl); wolfSSL_CTX_free(ctx); @@ -13430,6 +13450,8 @@ static int test_wolfSSL_Tls13_ECH_params_b64(void) const char* b64BadCiph = "AEX+DQBBFAAgACBuAoQI8+liEVYQbXKBDeVgTmF2rfXuKO2knhwrN7jgTgAE/v4AAQASY2xvdWRmbGFyZS1lY2guY29tAAA="; /* ech configs with unrecognized mandatory extension */ const char* b64Mandatory = "AEn+DQBFFAAgACBuAoQI8+liEVYQbXKBDeVgTmF2rfXuKO2knhwrN7jgTgAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAT6+gAA"; + /* ech configs with unrecognized mandatory extension first */ + const char* b64MandatoryFirst = "AJD+DQBGAQAgACCjR6+Qn9UYkMaWdXZzsby88vXFhPHJ2tWCDHQJLvMkEgAEAAEAAQATZWNoLXB1YmxpYy1uYW1lLmNvbQAE+voAAP4NAEICACAAIDDOry602zn7HwOn02yWPyLtC49sXhxDxlCXlMEBgGBeAAQAAQABABNlY2gtcHVibGljLW5hbWUuY29tAAA="; /* ech configs with bad version first */ const char* b64BadVers1 = "AIz+HQBCAQAgACCjR6+Qn9UYkMaWdXZzsby88vXFhPHJ2tWCDHQJLvMkEgAEAAEAAQATZWNoLXB1YmxpYy1uYW1lLmNvbQAA/g0AQgIAIAAgMM6vLrTbOfsfA6fTbJY/Iu0Lj2xeHEPGUJeUwQGAYF4ABAABAAEAE2VjaC1wdWJsaWMtbmFtZS5jb20AAA=="; /* ech configs with bad version second */ @@ -13497,6 +13519,20 @@ static int test_wolfSSL_Tls13_ECH_params_b64(void) ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigsBase64(ssl, b64Mandatory, (word32)XSTRLEN(b64Mandatory))); + /* unrecognized mandatory extension */ + ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigsBase64(ctx, + b64MandatoryFirst, (word32)XSTRLEN(b64MandatoryFirst))); + ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigsBase64(ssl, + b64MandatoryFirst, (word32)XSTRLEN(b64MandatoryFirst))); + ExpectIntEQ(2, ctx->echConfigs->configId); + ExpectIntEQ(2, ssl->echConfigs->configId); + + /* clear configs */ + wolfSSL_CTX_SetEchEnable(ctx, 0); + wolfSSL_CTX_SetEchEnable(ctx, 1); + wolfSSL_SetEchEnable(ssl, 0); + wolfSSL_SetEchEnable(ssl, 1); + /* bad version first, should only have config 2 set */ ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigsBase64(ctx, b64BadVers1, (word32)XSTRLEN(b64BadVers1))); @@ -13696,9 +13732,14 @@ static int test_ech_server_sni_callback(WOLFSSL* ssl, int* ad, void* arg) /* reached by *_disable_conn test: expect name to be the public SNI when * client has ECH enabled, otherwise it should be the private SNI */ - if (arg != NULL && *(int*)arg == 1 && - XSTRCMP(name, echCbTestPublicName) == 0) { - return 0; + if (arg != NULL && *(int*)arg == 1) { + if (XSTRCMP(name, echCbTestPublicName) == 0) { + return 0; + } + else { + *ad = WOLFSSL_AD_UNRECOGNIZED_NAME; + return fatal_return; + } } else if (XSTRCMP(name, echCbTestPrivateName) == 0) { return 0; @@ -13922,14 +13963,19 @@ static int test_wolfSSL_Tls13_ECH_no_private_name(void) ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS); + /* SNI is permissive by default, force a failure when SNI is absent */ + wolfSSL_SNI_SetOptions(test_ctx.s_ssl, WOLFSSL_SNI_HOST_NAME, + WOLFSSL_SNI_ABORT_ON_ABSENCE); + ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, echCbTestConfigs, echCbTestConfigsLen), WOLFSSL_SUCCESS); ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); + /* server fails before sending response to client */ ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl), - WOLFSSL_ECH_STATUS_NOT_OFFERED); + WOLFSSL_ECH_STATUS_REJECTED); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl), - WOLFSSL_ECH_STATUS_NOT_OFFERED); + WOLFSSL_ECH_STATUS_ACCEPTED); test_ssl_memio_cleanup(&test_ctx); @@ -13947,11 +13993,13 @@ static int test_wolfSSL_Tls13_ECH_no_private_name(void) ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, echCbTestConfigs, echCbTestConfigsLen), WOLFSSL_SUCCESS); - ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); + /* it is odd to have no private SNI but it's not necessarily an issue, + * there are other methods that could be used to route the connection */ + ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl), - WOLFSSL_ECH_STATUS_NOT_OFFERED); + WOLFSSL_ECH_STATUS_ACCEPTED); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl), - WOLFSSL_ECH_STATUS_NOT_OFFERED); + WOLFSSL_ECH_STATUS_ACCEPTED); test_ssl_memio_cleanup(&test_ctx); @@ -14058,6 +14106,42 @@ static int test_wolfSSL_Tls13_ECH_bad_configs_ex(int hrr, int sniCb) test_ssl_memio_cleanup(&test_ctx); + + /* verify with double public SNI */ + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + test_ctx.s_cb.method = wolfTLSv1_3_server_method; + test_ctx.c_cb.method = wolfTLSv1_3_client_method; + + test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; + test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready; + + ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS); + + /* set public SNI for private SNI on client */ + ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, echCbTestConfigs, + echCbTestConfigsLen), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, + echCbTestPublicName, (word16)XSTRLEN(echCbTestPublicName)), + WOLFSSL_SUCCESS); + + if (hrr) { + ExpectIntEQ(wolfSSL_NoKeyShares(test_ctx.c_ssl), WOLFSSL_SUCCESS); + } + if (sniCb) { + wolfSSL_CTX_set_servername_callback(test_ctx.s_ctx, + test_ech_server_sni_callback); + } + + ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl), + WOLFSSL_ECH_STATUS_REJECTED); + ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl), + WOLFSSL_ECH_STATUS_ACCEPTED); + + test_ssl_memio_cleanup(&test_ctx); + return EXPECT_RESULT(); } @@ -14608,34 +14692,31 @@ static int test_wolfSSL_Tls13_ECH_disable_conn_ex(int enableServer, return EXPECT_RESULT(); } -/* setup a server and client with ECH then disable on one, the other, or both. - * Verifies that disabling ECH prevents ECH from being used and that the - * public/private SNI's are verified correctly */ -static int test_wolfSSL_Tls13_ECH_disable_conn(void) +static const byte* test_find_bytes(const char* needle, + const byte* haystack, int hayLen) { - EXPECT_DECLS; - - ExpectIntEQ(test_wolfSSL_Tls13_ECH_disable_conn_ex(0, 1), TEST_SUCCESS); - ExpectIntEQ(test_wolfSSL_Tls13_ECH_disable_conn_ex(1, 0), TEST_SUCCESS); - ExpectIntEQ(test_wolfSSL_Tls13_ECH_disable_conn_ex(0, 0), TEST_SUCCESS); - - return EXPECT_RESULT(); + int needleLen = (int)XSTRLEN(needle); + int i; + if (hayLen < needleLen) + return NULL; + for (i = 0; i <= hayLen - needleLen; i++) { + if (XMEMCMP(haystack + i, needle, needleLen) == 0) + return haystack + i; + } + return NULL; } -/* Regression test: an inner SNI hostname >= MAX_PUBLIC_NAME_SZ (256) bytes - * must not cause a stack-buffer-overflow in TLSX_EchRestoreSNI. Before the - * fix, the truncated copy omitted the NUL terminator and XSTRLEN read past - * the buffer. */ -static int test_wolfSSL_Tls13_ECH_long_SNI(void) +/* The public name must be visible and the private name must not be visible */ +static int test_wolfSSL_Tls13_ECH_wire_sni_ex(int hrr, int accept) { EXPECT_DECLS; -#if !defined(NO_WOLFSSL_CLIENT) test_ssl_memio_ctx test_ctx; - /* 300 chars > MAX_PUBLIC_NAME_SZ (256) to exercise truncation */ - char longName[300]; - - XMEMSET(longName, 'a', sizeof(longName) - 1); - longName[sizeof(longName) - 1] = '\0'; + WOLFSSL_CTX* tempCtx = NULL; + byte badConfig[128]; + word32 badConfigLen = sizeof(badConfig); + const char* expectedSni = + accept ? echCbTestPrivateName : echCbTestPublicName; + void* sniName = NULL; XMEMSET(&test_ctx, 0, sizeof(test_ctx)); @@ -14644,26 +14725,109 @@ static int test_wolfSSL_Tls13_ECH_long_SNI(void) test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready; + /* Accept path uses the correct configs */ + if (accept) + test_ctx.c_cb.ssl_ready = test_ech_client_ssl_ready; ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS); - /* Set ECH configs on the client */ - ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, echCbTestConfigs, - echCbTestConfigsLen), WOLFSSL_SUCCESS); + /* Reject path installs bad configs (with the correct public name) */ + if (!accept) { + ExpectNotNull(tempCtx = wolfSSL_CTX_new(wolfTLSv1_3_server_method())); + ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(tempCtx, echCbTestPublicName, + 0, 0, 0), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_GetEchConfigs(tempCtx, badConfig, + &badConfigLen), WOLFSSL_SUCCESS); + wolfSSL_CTX_free(tempCtx); + ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, badConfig, + badConfigLen), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, + echCbTestPrivateName, (word16)XSTRLEN(echCbTestPrivateName)), + WOLFSSL_SUCCESS); + } - /* Try to set the over-long SNI as the inner hostname -- after the fix, this - * is expected to fail. - */ - ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, - longName, (word16)XSTRLEN(longName)), BAD_LENGTH_E); + if (hrr) + ExpectIntEQ(wolfSSL_NoKeyShares(test_ctx.c_ssl), WOLFSSL_SUCCESS); - /* Before the fix, the handshake would trigger TLSX_EchChangeSNI / - * TLSX_EchRestoreSNI, which would then stack-buffer-overflow in XSTRLEN. - */ - (void)test_ssl_memio_do_handshake(&test_ctx, 10, NULL); + /* On reject, client aborts with ech_required and won't send a cert. */ + if (!accept) { + wolfSSL_set_verify(test_ctx.s_ssl, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_PEER, NULL); + } + + /* client writes CH1 into s_buff */ + ExpectIntEQ(wolfSSL_connect(test_ctx.c_ssl), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_READ); + + /* CH1 wire bytes */ + ExpectNotNull(test_find_bytes(echCbTestPublicName, test_ctx.s_buff, + test_ctx.s_len)); + ExpectNull(test_find_bytes(echCbTestPrivateName, test_ctx.s_buff, + test_ctx.s_len)); + + if (hrr) { + /* server consumes CH1 and writes HRR into c_buff */ + ExpectIntEQ(wolfSSL_accept(test_ctx.s_ssl), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(test_ctx.s_ssl, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ctx.s_ssl->options.serverState, + SERVER_HELLO_RETRY_REQUEST_COMPLETE); + + /* client reads HRR from c_buff and writes CH2 into s_buff */ + ExpectIntEQ(wolfSSL_connect(test_ctx.c_ssl), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_READ); + + /* CH2 wire bytes: same property must hold */ + ExpectNotNull(test_find_bytes(echCbTestPublicName, test_ctx.s_buff, + test_ctx.s_len)); + ExpectNull(test_find_bytes(echCbTestPrivateName, test_ctx.s_buff, + test_ctx.s_len)); + } + + /* drive remaining rounds and verify the correct SNI is authoritative */ + if (accept) { + ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), + TEST_SUCCESS); + } + else { + ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), + TEST_SUCCESS); + } + + ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, accept ? 1 : 0); + wolfSSL_SNI_GetRequest(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, &sniName); + ExpectStrEQ((const char*)sniName, expectedSni); + sniName = NULL; + wolfSSL_SNI_GetRequest(test_ctx.s_ssl, WOLFSSL_SNI_HOST_NAME, &sniName); + ExpectStrEQ((const char*)sniName, expectedSni); test_ssl_memio_cleanup(&test_ctx); -#endif /* !NO_WOLFSSL_CLIENT */ + + return EXPECT_RESULT(); +} + +static int test_wolfSSL_Tls13_ECH_wire_sni(void) +{ + EXPECT_DECLS; + ExpectIntEQ(test_wolfSSL_Tls13_ECH_wire_sni_ex(0, 0), TEST_SUCCESS); + ExpectIntEQ(test_wolfSSL_Tls13_ECH_wire_sni_ex(0, 1), TEST_SUCCESS); + ExpectIntEQ(test_wolfSSL_Tls13_ECH_wire_sni_ex(1, 0), TEST_SUCCESS); + ExpectIntEQ(test_wolfSSL_Tls13_ECH_wire_sni_ex(1, 1), TEST_SUCCESS); + return EXPECT_RESULT(); +} + +/* setup a server and client with ECH then disable on one, the other, or both. + * Verifies that disabling ECH prevents ECH from being used and that the + * public/private SNI's are verified correctly */ +static int test_wolfSSL_Tls13_ECH_disable_conn(void) +{ + EXPECT_DECLS; + + ExpectIntEQ(test_wolfSSL_Tls13_ECH_disable_conn_ex(0, 1), TEST_SUCCESS); + ExpectIntEQ(test_wolfSSL_Tls13_ECH_disable_conn_ex(1, 0), TEST_SUCCESS); + ExpectIntEQ(test_wolfSSL_Tls13_ECH_disable_conn_ex(0, 0), TEST_SUCCESS); return EXPECT_RESULT(); } @@ -34456,8 +34620,8 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_Tls13_ECH_new_config), TEST_DECL(test_wolfSSL_Tls13_ECH_trial_decrypt), TEST_DECL(test_wolfSSL_Tls13_ECH_GREASE), + TEST_DECL(test_wolfSSL_Tls13_ECH_wire_sni), TEST_DECL(test_wolfSSL_Tls13_ECH_disable_conn), - TEST_DECL(test_wolfSSL_Tls13_ECH_long_SNI), TEST_DECL(test_wolfSSL_Tls13_ECH_HRR_rejection), TEST_DECL(test_wolfSSL_Tls13_ECH_ch2_no_ech), TEST_DECL(test_wolfSSL_Tls13_ECH_ch2_decrypt_error), diff --git a/wolfssl/internal.h b/wolfssl/internal.h index fc918fbc77..1d8ad9562d 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3125,13 +3125,6 @@ typedef enum { ECH_PARSED_INTERNAL, } EchState; -typedef enum { - ECH_OUTER_SNI, - ECH_INNER_SNI, - ECH_INNER_SNI_ATTEMPT, - ECH_SNI_DONE, -} EchStateSNI; - typedef struct EchCipherSuite { word16 kdfId; word16 aeadId; @@ -3155,11 +3148,12 @@ typedef struct WOLFSSL_ECH { Hpke* hpke; HpkeBaseContext* hpkeContext; const byte* aad; - const char* privateName; void* ephemeralKey; WOLFSSL_EchConfig* echConfig; byte* innerClientHello; byte* outerClientPayload; + /* the 'public' extensions (i.e., the public SNI would be stored here) */ + TLSX* extensions; byte* confBuf; EchCipherSuite cipherSuite; word32 aadLen; @@ -3168,7 +3162,6 @@ typedef struct WOLFSSL_ECH { word16 kemId; word16 encLen; EchState state; - EchStateSNI sniState; byte type; byte configId; byte enc[HPKE_Npk_MAX]; @@ -3180,6 +3173,8 @@ WOLFSSL_LOCAL int EchConfigGetSupportedCipherSuite(WOLFSSL_EchConfig* config); WOLFSSL_LOCAL int TLSX_FinalizeEch(WOLFSSL_ECH* ech, byte* aad, word32 aadLen); +WOLFSSL_LOCAL void TLSX_EchReplaceExtensions(WOLFSSL* ssl, byte accepted); + WOLFSSL_LOCAL int SetEchConfigsEx(WOLFSSL_EchConfig** outputConfigs, void* heap, const byte* echConfigs, word32 echConfigsLen);