From 1406d8c59d4f4fe4e42f3ff3243cd6a0f0e79f05 Mon Sep 17 00:00:00 2001 From: sebastian-carpenter Date: Fri, 23 Jan 2026 09:35:38 -0700 Subject: [PATCH 1/4] client and server fix for TLS ECH --- src/internal.c | 2 - src/tls.c | 3 + src/tls13.c | 341 ++++++++++++++++++++++++++++++++------------- wolfssl/internal.h | 1 - 4 files changed, 251 insertions(+), 96 deletions(-) diff --git a/src/internal.c b/src/internal.c index 74f59faff9..45fc6ccbcf 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8587,8 +8587,6 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl) /* try to free the ech hashes in case we errored out */ ssl->hsHashes = ssl->hsHashesEch; FreeHandshakeHashes(ssl); - ssl->hsHashes = ssl->hsHashesEchInner; - FreeHandshakeHashes(ssl); #endif XFREE(ssl->buffers.domainName.buffer, ssl->heap, DYNAMIC_TYPE_DOMAIN); diff --git a/src/tls.c b/src/tls.c index 7d7dcea86c..bc263e3de6 100644 --- a/src/tls.c +++ b/src/tls.c @@ -13405,6 +13405,8 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size, } /* HRR with special confirmation */ else if (msgType == hello_retry_request && ssl->options.useEch) { + /* TODO: confirmation may not exist -> segfault? */ + printf("\n\ngot special confirmation\n\n\n"); /* length must be 8 */ if (size != ECH_ACCEPT_CONFIRMATION_SZ) return BAD_FUNC_ARG; @@ -14242,6 +14244,7 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore, WOLFSSL_MSG("ECH extension to write"); ret = ECH_WRITE((WOLFSSL_ECH*)extension->data, msgType, output + offset, &offset); + fprintf(stderr, "\t\thit this\n"); break; #endif default: diff --git a/src/tls13.c b/src/tls13.c index 5b0f098507..29b3b79e2e 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -257,6 +257,7 @@ static int Tls13HKDFExpandKeyLabel(WOLFSSL* ssl, byte* okm, word32 okmLen, #endif #if !defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(6,0)) + printf("Running this\n"); ret = wc_Tls13_HKDF_Expand_Label_ex(okm, okmLen, prk, prkLen, protocol, protocolLen, label, labelLen, @@ -4196,7 +4197,35 @@ static int WritePSKBinders(WOLFSSL* ssl, byte* output, word32 idx) #endif #if defined(HAVE_ECH) -/* returns status after we hash the ech inner */ +/* returns the index of the first supported cipher suite, -1 if none */ +int EchConfigGetSupportedCipherSuite(WOLFSSL_EchConfig* config) +{ + int i, j, supported = 0; + + for (i = 0; i < config->numCipherSuites; i++) { + supported = 0; + + for (j = 0; j < HPKE_SUPPORTED_KDF_LEN; j++) { + if (config->cipherSuites[i].kdfId == hpkeSupportedKdf[j]) + break; + } + + if (j < HPKE_SUPPORTED_KDF_LEN) + for (j = 0; j < HPKE_SUPPORTED_AEAD_LEN; j++) { + if (config->cipherSuites[i].aeadId == hpkeSupportedAead[j]) { + supported = 1; + break; + } + } + + if (supported) + return i; + } + + return WOLFSSL_FATAL_ERROR; +} + +/* returns status after we hash the inner client hello */ static int EchHashHelloInner(WOLFSSL* ssl, WOLFSSL_ECH* ech) { int ret = 0; @@ -4208,52 +4237,44 @@ static int EchHashHelloInner(WOLFSSL* ssl, WOLFSSL_ECH* ech) byte falseHeader[HANDSHAKE_HEADER_SZ]; #endif - if (ssl == NULL || ech == NULL) + if (ssl == NULL || ech == NULL) { return BAD_FUNC_ARG; - realSz = ech->innerClientHelloLen - ech->paddingLen - ech->hpke->Nt; + } + + if (ssl->options.side == WOLFSSL_CLIENT_END) { + realSz = ech->innerClientHelloLen - ech->paddingLen - ech->hpke->Nt; + } + else { + realSz = ech->innerClientHelloLen; + } + tmpHashes = ssl->hsHashes; - ssl->hsHashes = NULL; - /* init the ech hashes */ - ret = InitHandshakeHashes(ssl); - if (ret == 0) { - ssl->hsHashesEch = ssl->hsHashes; - /* do the handshake header then the body */ - AddTls13HandShakeHeader(falseHeader, realSz, 0, 0, client_hello, ssl); - ret = HashRaw(ssl, falseHeader, HANDSHAKE_HEADER_SZ); - /* hash with inner */ + + ssl->hsHashes = ssl->hsHashesEch; + if (ssl->options.echAccepted == 0 && ssl->hsHashes == NULL) { + ret = InitHandshakeHashes(ssl); if (ret == 0) { - /* init hsHashesEchInner */ - if (ech->innerCount == 0) { - ssl->hsHashes = ssl->hsHashesEchInner; - ret = InitHandshakeHashes(ssl); - if (ret == 0) { - ssl->hsHashesEchInner = ssl->hsHashes; - ech->innerCount = 1; - } - } - else { - /* switch back to hsHashes so we have hrr -> echInner2 */ - ssl->hsHashes = tmpHashes; - ret = InitHandshakeHashesAndCopy(ssl, ssl->hsHashes, - &ssl->hsHashesEchInner); - } + ssl->hsHashesEch = ssl->hsHashes; + ech->innerCount = 1; + } + } + if (ret == 0) { + if (ssl->options.side == WOLFSSL_CLIENT_END) { + /* client-side: innerClientHello contains body only */ + AddTls13HandShakeHeader(falseHeader, realSz, 0, 0, client_hello, ssl); + ret = HashRaw(ssl, falseHeader, sizeof(falseHeader)); if (ret == 0) { - ssl->hsHashes = ssl->hsHashesEchInner; - ret = HashRaw(ssl, falseHeader, HANDSHAKE_HEADER_SZ); - ssl->hsHashes = ssl->hsHashesEch; + ret = HashRaw(ssl, ech->innerClientHello, realSz); } } + else { + /* server-side: innerClientHello contains header + body */ + ret = HashRaw(ssl, ech->innerClientHello, + sizeof(falseHeader) + realSz); + } } - /* hash the body */ - if (ret == 0) - ret = HashRaw(ssl, ech->innerClientHello, realSz); - /* hash with inner */ - if (ret == 0) { - ssl->hsHashes = ssl->hsHashesEchInner; - ret = HashRaw(ssl, ech->innerClientHello, realSz); - } - /* swap hsHashes back */ + ssl->hsHashes = tmpHashes; return ret; } @@ -4682,9 +4703,16 @@ int SendTls13ClientHello(WOLFSSL* ssl) XMEMCPY(args->ech->innerClientHello, args->output + RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ, args->idx - (RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ)); - /* copy the client random to inner */ - XMEMCPY(ssl->arrays->clientRandomInner, ssl->arrays->clientRandom, - RAN_LEN); + /* copy the client random to inner - only for first CH, not after HRR */ + if (!ssl->options.echAccepted) { + XMEMCPY(ssl->arrays->clientRandomInner, ssl->arrays->clientRandom, + RAN_LEN); + } + else { + /* After HRR, use the same inner random as CH1 */ + XMEMCPY(args->ech->innerClientHello + VERSION_SZ, + ssl->arrays->clientRandomInner, RAN_LEN); + } /* change the outer client random */ ret = wc_RNG_GenerateBlock(ssl->rng, args->output + args->clientRandomOffset, RAN_LEN); @@ -4748,8 +4776,9 @@ int SendTls13ClientHello(WOLFSSL* ssl) #if defined(HAVE_ECH) /* compute the inner hash */ if (ssl->options.useEch == 1 && !ssl->options.disableECH && - (ssl->options.echAccepted || args->ech->innerCount == 0)) + (ssl->options.echAccepted || args->ech->innerCount == 0)) { ret = EchHashHelloInner(ssl, args->ech); + } #endif /* compute the outer hash */ if (ret == 0) @@ -4841,43 +4870,77 @@ static int Dtls13ClientDoDowngrade(WOLFSSL* ssl) #endif /* WOLFSSL_DTLS13 && !WOLFSSL_NO_CLIENT*/ #if defined(HAVE_ECH) -/* check if the server accepted ech or not, must be run after an hsHashes - * restart */ -static int EchCheckAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz, - const byte* input, int acceptOffset, int helloSz) +static int EchCalcAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz, + const byte* input, int acceptOffset, int helloSz, byte isHrr, + byte* acceptExpanded) { int ret = 0; int digestType = 0; int digestSize = 0; + int hashSz = 0; HS_Hashes* tmpHashes; + HS_Hashes* acceptHash = NULL; byte zeros[WC_MAX_DIGEST_SIZE]; byte transcriptEchConf[WC_MAX_DIGEST_SIZE]; + byte clientHelloInnerHash[WC_MAX_DIGEST_SIZE]; byte expandLabelPrk[WC_MAX_DIGEST_SIZE]; - byte acceptConfirmation[ECH_ACCEPT_CONFIRMATION_SZ]; + byte messageHashHeader[HANDSHAKE_HEADER_SZ]; + XMEMSET(zeros, 0, sizeof(zeros)); XMEMSET(transcriptEchConf, 0, sizeof(transcriptEchConf)); + XMEMSET(clientHelloInnerHash, 0, sizeof(clientHelloInnerHash)); XMEMSET(expandLabelPrk, 0, sizeof(expandLabelPrk)); - XMEMSET(acceptConfirmation, 0, sizeof(acceptConfirmation)); - /* store so we can restore regardless of the outcome */ + tmpHashes = ssl->hsHashes; - /* swap hsHashes to hsHashesEch */ - ssl->hsHashes = ssl->hsHashesEch; - /* hash up to the last 8 bytes */ - ret = HashRaw(ssl, input, acceptOffset); - /* hash 8 zeros */ - if (ret == 0) + + if (isHrr) { + /* the transcript hash of ClientHelloInner1 */ + hashSz = GetMsgHash(ssl, clientHelloInnerHash); + if (hashSz <= 0) { + ret = hashSz; + } + + /* restart ECH transcript hash, similar to RestartHandshakeHash but + * don't add a cookie */ + if (ret == 0) { + ret = InitHandshakeHashes(ssl); + } + if (ret == 0) { + ssl->hsHashesEch = ssl->hsHashes; + AddTls13HandShakeHeader(messageHashHeader, (word32)hashSz, 0, 0, + message_hash, ssl); + ret = HashRaw(ssl, messageHashHeader, sizeof(messageHashHeader)); + } + if (ret == 0) { + ret = HashRaw(ssl, clientHelloInnerHash, (word32)hashSz); + } + } + + /* hash with zeros for confirmation computation */ + if (ret == 0) { + ret = InitHandshakeHashesAndCopy(ssl, ssl->hsHashesEch, &acceptHash); + } + if (ret == 0) { + ssl->hsHashes = acceptHash; + ret = HashRaw(ssl, input, acceptOffset); + } + if (ret == 0) { ret = HashRaw(ssl, zeros, ECH_ACCEPT_CONFIRMATION_SZ); - /* hash the rest of the hello */ + } if (ret == 0) { ret = HashRaw(ssl, input + acceptOffset + ECH_ACCEPT_CONFIRMATION_SZ, helloSz + HANDSHAKE_HEADER_SZ - (acceptOffset + ECH_ACCEPT_CONFIRMATION_SZ)); } + /* get the modified transcript hash */ - if (ret == 0) + if (ret == 0) { ret = GetMsgHash(ssl, transcriptEchConf); - if (ret > 0) - ret = 0; + if (ret > 0) { + ret = 0; + } + } + /* pick the right type and size based on mac_algorithm */ if (ret == 0) { switch (ssl->specs.mac_algorithm) { @@ -4910,6 +4973,7 @@ static int EchCheckAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz, break; } } + /* extract clientRandomInner with a key of all zeros */ if (ret == 0) { PRIVATE_KEY_UNLOCK(); @@ -4924,47 +4988,116 @@ static int EchCheckAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz, #endif PRIVATE_KEY_LOCK(); } + /* tls expand with the confirmation label */ if (ret == 0) { PRIVATE_KEY_UNLOCK(); - ret = Tls13HKDFExpandKeyLabel(ssl, acceptConfirmation, + ret = Tls13HKDFExpandKeyLabel(ssl, acceptExpanded, ECH_ACCEPT_CONFIRMATION_SZ, expandLabelPrk, (word32)digestSize, tls13ProtocolLabel, TLS13_PROTOCOL_LABEL_SZ, label, labelSz, transcriptEchConf, (word32)digestSize, digestType, WOLFSSL_SERVER_END); PRIVATE_KEY_LOCK(); } + + if (acceptHash != NULL) { + ssl->hsHashes = acceptHash; + FreeHandshakeHashes(ssl); + } + + ssl->hsHashes = tmpHashes; + return ret; +} + +/* check if the server accepted ech or not, return status */ +static int EchCheckAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz, + const byte* input, int acceptOffset, int helloSz) +{ + int ret = 0; + int isHrr = 0; + HS_Hashes* tmpHashes; + byte acceptConfirmation[ECH_ACCEPT_CONFIRMATION_SZ]; + + XMEMSET(acceptConfirmation, 0, sizeof(acceptConfirmation)); + + tmpHashes = ssl->hsHashes; + ssl->hsHashes = ssl->hsHashesEch; + + if (labelSz == ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ && + XMEMCMP(label, echHrrAcceptConfirmationLabel, labelSz) == 0) { + isHrr = 1; + } + + ret = EchCalcAcceptance(ssl, label, labelSz, input, acceptOffset, helloSz, + isHrr, acceptConfirmation); + if (ret == 0) { - /* last 8 bytes should match our expand output */ + /* last 8 bytes must match the expand output */ ret = XMEMCMP(acceptConfirmation, input + acceptOffset, - ECH_ACCEPT_CONFIRMATION_SZ); - /* ech accepted */ + ECH_ACCEPT_CONFIRMATION_SZ); + if (ret == 0) { - /* set echAccepted to 1 */ ssl->options.echAccepted = 1; - /* free hsHashes and go with inner */ - ssl->hsHashes = tmpHashes; - FreeHandshakeHashes(ssl); - ssl->hsHashes = ssl->hsHashesEch; - tmpHashes = ssl->hsHashesEchInner; - ssl->hsHashesEchInner = NULL; + + /* after HRR, hsHashesEch must contain: + * message_hash(ClientHelloInner1) || HRR (actual, not zeros) */ + if (isHrr) { + ssl->hsHashes = ssl->hsHashesEch; + ret = HashRaw(ssl, input, helloSz + HANDSHAKE_HEADER_SZ); + } + /* normal TLS code will calculate transcript of ServerHello */ + else { + ssl->hsHashes = tmpHashes; + FreeHandshakeHashes(ssl); + tmpHashes = ssl->hsHashesEch; + ssl->hsHashesEch = NULL; + } } - /* ech rejected */ else { - /* set echAccepted to 0, needed in case HRR */ ssl->options.echAccepted = 0; - /* free inner since we're continuing with outer */ - ssl->hsHashes = ssl->hsHashesEchInner; + ret = 0; + + /* ECH rejected, continue with outer transcript */ FreeHandshakeHashes(ssl); - ssl->hsHashesEchInner = NULL; + ssl->hsHashesEch = NULL; } - /* continue with outer if we failed to verify ech was accepted */ - ret = 0; } - FreeHandshakeHashes(ssl); - /* set hsHashesEch to NULL to avoid double free */ - ssl->hsHashesEch = NULL; - /* swap to tmp, will be inner if accepted, hsHashes if rejected */ + + ssl->hsHashes = tmpHashes; + return ret; +} + +/* replace the last acceptance field for either ServerHello or HRR with the ECH + * acceptance parameter, return status */ +static int EchWriteAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz, + byte* output, int acceptOffset, int helloSz, byte msgType) +{ + int ret = 0; + HS_Hashes* tmpHashes; + + tmpHashes = ssl->hsHashes; + ssl->hsHashes = ssl->hsHashesEch; + + ret = EchCalcAcceptance(ssl, label, labelSz, output, acceptOffset, + helloSz - HANDSHAKE_HEADER_SZ, msgType == hello_retry_request, + output + acceptOffset); + + /* after HRR, hsHashesEch must contain: + * message_hash(ClientHelloInner1) || HRR (actual, not zeros) */ + if (ret == 0 && msgType == hello_retry_request) { + ssl->hsHashes = ssl->hsHashesEch; + ret = HashRaw(ssl, output, helloSz); + } + /* normal TLS code will calculate transcript of ServerHello */ + else if (ret == 0) { + ssl->options.echAccepted = 1; + + ssl->hsHashes = tmpHashes; + FreeHandshakeHashes(ssl); + tmpHashes = ssl->hsHashesEch; + ssl->hsHashesEch = NULL; + } + ssl->hsHashes = tmpHashes; return ret; } @@ -5509,8 +5642,10 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if (args->extMsgType == hello_retry_request) { args->acceptOffset = (word32)(((WOLFSSL_ECH*)args->echX->data)->confBuf - input); + printf("\n\n\nCONFBUF %p\n", ((WOLFSSL_ECH*)args->echX->data)->confBuf); args->acceptLabel = (byte*)echHrrAcceptConfirmationLabel; args->acceptLabelSz = ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ; + printf("\n\nHELLO RETRY REQUEST\n\n\n"); } else { args->acceptLabel = (byte*)echAcceptConfirmationLabel; @@ -5518,6 +5653,7 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } /* check acceptance */ if (ret == 0) { + printf("inOutIdx %d acceptOffset %d\n", *inOutIdx, args->acceptOffset); ret = EchCheckAcceptance(ssl, args->acceptLabel, args->acceptLabelSz, input, args->acceptOffset, helloSz); } @@ -5586,6 +5722,7 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, ssl->options.tls1_3 = 1; ssl->options.serverState = SERVER_HELLO_RETRY_REQUEST_COMPLETE; + printf("\n\n\nRestarting HASH\n\n\n\n"); ret = RestartHandshakeHash(ssl); } @@ -6656,7 +6793,6 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #endif #if defined(HAVE_ECH) TLSX* echX = NULL; - HS_Hashes* tmpHashes; #endif WOLFSSL_START(WC_FUNC_CLIENT_HELLO_DO); @@ -6998,18 +7134,11 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #endif #if defined(HAVE_ECH) - /* hash clientHelloInner to hsHashesEch independently since it can't include - * the HRR */ + /* hash clientHelloInner to hsHashesEch */ if (ssl->ctx->echConfigs != NULL && !ssl->options.disableECH) { - tmpHashes = ssl->hsHashes; - ssl->hsHashes = NULL; - ret = InitHandshakeHashes(ssl); + ret = EchHashHelloInner(ssl, (WOLFSSL_ECH*)echX->data); if (ret != 0) goto exit_dch; - if ((ret = HashInput(ssl, input + args->begin, (int)helloSz)) != 0) - goto exit_dch; - ssl->hsHashesEch = ssl->hsHashes; - ssl->hsHashes = tmpHashes; } #endif @@ -12840,11 +12969,19 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, return OUT_OF_ORDER_E; } + if (ssl->options.echAccepted == 1) { + printf("\n\n\nHIGH LEVEL - ECH ACCEPTED\n\n\n\n"); + } + else { + printf("\n\n\nHIGH LEVEL - ECH REJECTED\n\n\n\n"); + } + /* above checks handshake state */ switch (type) { #ifndef NO_WOLFSSL_CLIENT /* Messages only received by client. */ case server_hello: + printf("\n\n\nProcessing SERVER HELLO\n\n\n\n"); WOLFSSL_MSG("processing server hello"); ret = DoTls13ServerHello(ssl, input, inOutIdx, size, &type); #if !defined(WOLFSSL_NO_CLIENT_AUTH) && \ @@ -12920,6 +13057,8 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, if (echX != NULL && ((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE) { + byte copyRandom = ((WOLFSSL_ECH*)echX->data)->innerCount == 0; + /* reset the inOutIdx to the outer start */ *inOutIdx = echInOutIdx; /* call again with the inner hello */ @@ -12931,8 +13070,16 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, } /* if the inner ech parsed successfully we have successfully * handled the hello and can skip the whole message */ - if (ret == 0) + if (ret == 0) { + /* Copy inner client random for ECH acceptance calculation. + * Only on first inner ClientHello (before HRR), not CH2. */ + if (copyRandom) { + XMEMCPY(ssl->arrays->clientRandomInner, + ((WOLFSSL_ECH*)echX->data)->innerClientHello + + HANDSHAKE_HEADER_SZ + VERSION_SZ, RAN_LEN); + } *inOutIdx += size; + } } } #endif /* HAVE_ECH */ @@ -12985,6 +13132,14 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, break; } + if (ssl->options.echAccepted == 1) { + printf("\n\n\nHIGH LEVEL 2 - ECH ACCEPTED\n\n\n\n"); + } + else { + printf("\n\n\nHIGH LEVEL 2 - ECH REJECTED\n\n\n\n"); + } + + #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_ASYNC_IO) /* if async, offset index so this msg will be processed again */ /* NOTE: check this now before other calls can overwrite ret */ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 59fb268aef..31593f54c6 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -5854,7 +5854,6 @@ struct WOLFSSL { HS_Hashes* hsHashes; #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) HS_Hashes* hsHashesEch; - HS_Hashes* hsHashesEchInner; #endif void* IOCB_ReadCtx; void* IOCB_WriteCtx; From 8cb3e7a4496500699ba5f9f5bd381c9170d34baf Mon Sep 17 00:00:00 2001 From: sebastian-carpenter Date: Wed, 28 Jan 2026 09:26:39 -0700 Subject: [PATCH 2/4] fixed seg fault when confirmation not present --- src/tls.c | 15 ++++++++++++--- src/tls13.c | 21 --------------------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/tls.c b/src/tls.c index bc263e3de6..d42b0b65f9 100644 --- a/src/tls.c +++ b/src/tls.c @@ -13405,8 +13405,6 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size, } /* HRR with special confirmation */ else if (msgType == hello_retry_request && ssl->options.useEch) { - /* TODO: confirmation may not exist -> segfault? */ - printf("\n\ngot special confirmation\n\n\n"); /* length must be 8 */ if (size != ECH_ACCEPT_CONFIRMATION_SZ) return BAD_FUNC_ARG; @@ -14244,7 +14242,6 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore, WOLFSSL_MSG("ECH extension to write"); ret = ECH_WRITE((WOLFSSL_ECH*)extension->data, msgType, output + offset, &offset); - fprintf(stderr, "\t\thit this\n"); break; #endif default: @@ -16781,6 +16778,18 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType, if (ret == 0) ret = TCA_VERIFY_PARSE(ssl, isRequest); +#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) + /* If client used ECH, server HRR must include ECH confirmation */ + if (ret == 0 && msgType == hello_retry_request && ssl->options.useEch == 1) { + TLSX* echX = TLSX_Find(ssl->extensions, TLSX_ECH); + if (echX == NULL || ((WOLFSSL_ECH*)echX->data)->confBuf == NULL) { + WOLFSSL_MSG("ECH used but HRR missing ECH confirmation"); + WOLFSSL_ERROR_VERBOSE(EXT_MISSING); + ret = EXT_MISSING; + } + } +#endif + WOLFSSL_LEAVE("Leaving TLSX_Parse", ret); return ret; } diff --git a/src/tls13.c b/src/tls13.c index 29b3b79e2e..3737b5503b 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -257,7 +257,6 @@ static int Tls13HKDFExpandKeyLabel(WOLFSSL* ssl, byte* okm, word32 okmLen, #endif #if !defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(6,0)) - printf("Running this\n"); ret = wc_Tls13_HKDF_Expand_Label_ex(okm, okmLen, prk, prkLen, protocol, protocolLen, label, labelLen, @@ -5642,10 +5641,8 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if (args->extMsgType == hello_retry_request) { args->acceptOffset = (word32)(((WOLFSSL_ECH*)args->echX->data)->confBuf - input); - printf("\n\n\nCONFBUF %p\n", ((WOLFSSL_ECH*)args->echX->data)->confBuf); args->acceptLabel = (byte*)echHrrAcceptConfirmationLabel; args->acceptLabelSz = ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ; - printf("\n\nHELLO RETRY REQUEST\n\n\n"); } else { args->acceptLabel = (byte*)echAcceptConfirmationLabel; @@ -5653,7 +5650,6 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } /* check acceptance */ if (ret == 0) { - printf("inOutIdx %d acceptOffset %d\n", *inOutIdx, args->acceptOffset); ret = EchCheckAcceptance(ssl, args->acceptLabel, args->acceptLabelSz, input, args->acceptOffset, helloSz); } @@ -5722,7 +5718,6 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, ssl->options.tls1_3 = 1; ssl->options.serverState = SERVER_HELLO_RETRY_REQUEST_COMPLETE; - printf("\n\n\nRestarting HASH\n\n\n\n"); ret = RestartHandshakeHash(ssl); } @@ -12969,19 +12964,11 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, return OUT_OF_ORDER_E; } - if (ssl->options.echAccepted == 1) { - printf("\n\n\nHIGH LEVEL - ECH ACCEPTED\n\n\n\n"); - } - else { - printf("\n\n\nHIGH LEVEL - ECH REJECTED\n\n\n\n"); - } - /* above checks handshake state */ switch (type) { #ifndef NO_WOLFSSL_CLIENT /* Messages only received by client. */ case server_hello: - printf("\n\n\nProcessing SERVER HELLO\n\n\n\n"); WOLFSSL_MSG("processing server hello"); ret = DoTls13ServerHello(ssl, input, inOutIdx, size, &type); #if !defined(WOLFSSL_NO_CLIENT_AUTH) && \ @@ -13132,14 +13119,6 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, break; } - if (ssl->options.echAccepted == 1) { - printf("\n\n\nHIGH LEVEL 2 - ECH ACCEPTED\n\n\n\n"); - } - else { - printf("\n\n\nHIGH LEVEL 2 - ECH REJECTED\n\n\n\n"); - } - - #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_ASYNC_IO) /* if async, offset index so this msg will be processed again */ /* NOTE: check this now before other calls can overwrite ret */ From ed6a77f583a50655be4a762984ed088ef26cc527 Mon Sep 17 00:00:00 2001 From: sebastian-carpenter Date: Fri, 30 Jan 2026 14:54:28 -0700 Subject: [PATCH 3/4] TLS ECH OuterExtensions support (Server side) --- src/tls.c | 332 +++++++++++++++++++++++++++++++++++++++++++++ src/tls13.c | 1 - wolfssl/internal.h | 2 + 3 files changed, 334 insertions(+), 1 deletion(-) diff --git a/src/tls.c b/src/tls.c index d42b0b65f9..0f5370bd39 100644 --- a/src/tls.c +++ b/src/tls.c @@ -13265,6 +13265,328 @@ static int TLSX_ECH_GetSize(WOLFSSL_ECH* ech, byte msgType) return (int)size; } +/* locate the given extension type, use the extOffset to start off after where a + * previous call to this function ended */ +static const byte* EchFindOuterExtension(const byte* outerCh, word32 chLen, + word16 extType, word16* extLen, word16* extOffset, + word16* extensionsStart, word16* extensionsLen) +{ + word32 idx = *extOffset; + byte sessionIdLen; + word16 cipherSuitesLen; + byte compressionLen; + word16 type; + word16 len; + + if (idx == 0) { + idx = OPAQUE16_LEN + RAN_LEN; + if (idx >= chLen) + return NULL; + + sessionIdLen = outerCh[idx++]; + idx += sessionIdLen; + if (idx + OPAQUE16_LEN > chLen) + return NULL; + + ato16(outerCh + idx, &cipherSuitesLen); + idx += OPAQUE16_LEN + cipherSuitesLen; + if (idx >= chLen) + return NULL; + + compressionLen = outerCh[idx++]; + idx += compressionLen; + if (idx + OPAQUE16_LEN > chLen) + return NULL; + + ato16(outerCh + idx, extensionsLen); + idx += OPAQUE16_LEN; + *extensionsStart = (word16)idx; + + if (idx + *extensionsLen > chLen) + return NULL; + } + + while (idx < chLen && (idx - *extensionsStart) < *extensionsLen) { + if (idx + OPAQUE16_LEN + OPAQUE16_LEN > chLen) + return NULL; + + ato16(outerCh + idx, &type); + idx += OPAQUE16_LEN; + ato16(outerCh + idx, &len); + idx += OPAQUE16_LEN; + + if (idx + len > chLen) + return NULL; + + if (type == extType) { + *extLen = len + OPAQUE16_LEN + OPAQUE16_LEN; + *extOffset = idx + len; + return outerCh + idx - OPAQUE16_LEN - OPAQUE16_LEN; + } + + idx += len; + } + + return NULL; +} + +/* if newInnerCh is NULL, validate ordering and existence of references + * - updates newInnerChLen with total length of selected extensions + * if not NULL, copy extensions into the provided buffer */ +static int EchCopyOuterExtensions(const byte* outerCh, word32 outerChLen, + byte** newInnerCh, word32* newInnerChLen, + word16 numOuterRefs, const byte* outerRefTypes) +{ + int ret = 0; + word16 refType; + word16 outerExtLen; + word16 outerExtOffset = 0; + word16 extsStart; + word16 extsLen; + const byte* outerExtData; + + if (newInnerCh == NULL) { + *newInnerChLen = 0; + + while (numOuterRefs-- > 0) { + ato16(outerRefTypes, &refType); + + if (refType == TLSXT_ECH) { + WOLFSSL_MSG("ECH: ech_outer_extensions references ECH"); + ret = INVALID_PARAMETER; + break; + } + + outerExtData = EchFindOuterExtension(outerCh, outerChLen, + refType, &outerExtLen, &outerExtOffset, + &extsStart, &extsLen); + + if (outerExtData == NULL) { + WOLFSSL_MSG("ECH: referenced extension not in outer CH"); + ret = INVALID_PARAMETER; + break; + } + + *newInnerChLen += outerExtLen; + + outerRefTypes += OPAQUE16_LEN; + } + } + else { + while (numOuterRefs-- > 0) { + ato16(outerRefTypes, &refType); + + outerExtData = EchFindOuterExtension(outerCh, outerChLen, + refType, &outerExtLen, &outerExtOffset, + &extsStart, &extsLen); + + if (outerExtData == NULL) { + ret = INVALID_PARAMETER; + break; + } + + XMEMCPY(*newInnerCh, outerExtData, outerExtLen); + *newInnerCh += outerExtLen; + + outerRefTypes += OPAQUE16_LEN; + } + } + + return ret; +} + +/* expand ech_outer_extensions in the inner ClientHello by copying referenced + * extensions from the outer ClientHello + */ +static int TLSX_ExpandEchOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech, + void* heap) +{ + int ret = 0; + const byte* innerCh; + word32 innerChLen; + const byte* outerCh; + word32 outerChLen; + word32 idx; + byte sessionIdLen; + word16 cipherSuitesLen; + byte compressionLen; + + word32 innerExtIdx; + word16 innerExtLen; + word32 echOuterExtIdx = 0; + word16 echOuterExtLen = 0; + int foundEchOuter = 0; + word16 numOuterRefs = 0; + const byte* outerRefTypes = NULL; + word32 extraSize = 0; + byte* newInnerCh = NULL; + byte* newInnerChRef; + word32 newInnerChLen; + word32 copyLen; + + WOLFSSL_ENTER("TLSX_ExpandEchOuterExtensions"); + + if (ech == NULL || ech->innerClientHello == NULL || ech->aad == NULL) + return BAD_FUNC_ARG; + + innerCh = ech->innerClientHello + HANDSHAKE_HEADER_SZ; + innerChLen = ech->innerClientHelloLen; + outerCh = ech->aad; + outerChLen = ech->aadLen; + + idx = OPAQUE16_LEN + RAN_LEN; + if (idx >= innerChLen) + return BUFFER_ERROR; + + sessionIdLen = innerCh[idx++]; + idx += sessionIdLen; + /* the ECH spec details that innerhello sessionID must initially be empty */ + if (sessionIdLen != 0) + return INVALID_PARAMETER; + if (idx + OPAQUE16_LEN > innerChLen) + return BUFFER_ERROR; + + ato16(innerCh + idx, &cipherSuitesLen); + idx += OPAQUE16_LEN + cipherSuitesLen; + if (idx >= innerChLen) + return BUFFER_ERROR; + + compressionLen = innerCh[idx++]; + idx += compressionLen; + if (idx + OPAQUE16_LEN > innerChLen) + return BUFFER_ERROR; + + ato16(innerCh + idx, &innerExtLen); + idx += OPAQUE16_LEN; + innerExtIdx = idx; + if (idx + innerExtLen > innerChLen) + return BUFFER_ERROR; + + /* validate ech_outer_extensions and calculate extra size */ + while (idx < innerChLen && (idx - innerExtIdx) < innerExtLen) { + word16 type; + word16 len; + byte outerExtListLen; + + if (idx + OPAQUE16_LEN + OPAQUE16_LEN > innerChLen) + return BUFFER_ERROR; + + ato16(innerCh + idx, &type); + idx += OPAQUE16_LEN; + ato16(innerCh + idx, &len); + idx += OPAQUE16_LEN; + + if (idx + len > innerChLen) + return BUFFER_ERROR; + + if (type == TLSXT_ECH_OUTER_EXTENSIONS) { + if (foundEchOuter) { + WOLFSSL_MSG("ECH: duplicate ech_outer_extensions"); + return INVALID_PARAMETER; + } + foundEchOuter = 1; + echOuterExtIdx = idx - OPAQUE16_LEN - OPAQUE16_LEN; + echOuterExtLen = len + OPAQUE16_LEN + OPAQUE16_LEN; + + /* ech_outer_extensions data format: 1-byte length + extension types + * ExtensionType OuterExtensions<2..254>; */ + if (len < 1) + return BUFFER_ERROR; + outerExtListLen = innerCh[idx]; + if (outerExtListLen + 1 != len || outerExtListLen < 2 || + outerExtListLen == 255) + return BUFFER_ERROR; + + outerRefTypes = innerCh + idx + 1; + numOuterRefs = outerExtListLen / OPAQUE16_LEN; + + ret = EchCopyOuterExtensions(outerCh, outerChLen, NULL, &extraSize, + numOuterRefs, outerRefTypes); + if (ret != 0) + return ret; + } + + idx += len; + } + + newInnerChLen = innerChLen - echOuterExtLen + extraSize - sessionIdLen + + ssl->session->sessionIDSz; + + if (!foundEchOuter && sessionIdLen == ssl->session->sessionIDSz) { + /* no extensions + no sessionID to copy */ + WOLFSSL_MSG("ECH: no EchOuterExtensions extension found"); + return ret; + } + else { + newInnerCh = (byte*)XMALLOC(newInnerChLen + HANDSHAKE_HEADER_SZ, heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (newInnerCh == NULL) + return MEMORY_E; + } + + /* note: The first HANDSHAKE_HEADER_SZ bytes are reserved for the header + * but not initialized here. The header will be properly set later by + * AddTls13HandShakeHeader() in DoTls13ClientHello(). */ + + /* copy everything up to EchOuterExtensions */ + newInnerChRef = newInnerCh + HANDSHAKE_HEADER_SZ; + copyLen = OPAQUE16_LEN + RAN_LEN; + XMEMCPY(newInnerChRef, innerCh, copyLen); + newInnerChRef += copyLen; + + *newInnerChRef = ssl->session->sessionIDSz; + newInnerChRef += OPAQUE8_LEN; + + copyLen = ssl->session->sessionIDSz; + XMEMCPY(newInnerChRef, ssl->session->sessionID, copyLen); + newInnerChRef += copyLen; + + if (!foundEchOuter) { + WOLFSSL_MSG("ECH: no EchOuterExtensions extension found"); + + copyLen = innerChLen - OPAQUE16_LEN - RAN_LEN - OPAQUE8_LEN - + sessionIdLen; + XMEMCPY(newInnerChRef, innerCh + OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN + + sessionIdLen, copyLen); + } + else { + copyLen = echOuterExtIdx - OPAQUE16_LEN - RAN_LEN - OPAQUE8_LEN - + sessionIdLen; + XMEMCPY(newInnerChRef, innerCh + OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN + + sessionIdLen, copyLen); + newInnerChRef += copyLen; + + /* update extensions length in the new ClientHello */ + innerExtIdx = innerExtIdx - sessionIdLen + ssl->session->sessionIDSz; + c16toa(innerExtLen - echOuterExtLen + (word16)extraSize, + newInnerChRef - OPAQUE16_LEN); + + /* insert expanded extensions from outer ClientHello */ + ret = EchCopyOuterExtensions(outerCh, outerChLen, &newInnerChRef, + &newInnerChLen, numOuterRefs, outerRefTypes); + if (ret == 0) { + /* copy remaining extensions after ech_outer_extensions */ + copyLen = innerChLen - (echOuterExtIdx + echOuterExtLen); + XMEMCPY(newInnerChRef, innerCh + echOuterExtIdx + echOuterExtLen, + copyLen); + + WOLFSSL_MSG("ECH: expanded ech_outer_extensions successfully"); + } + } + + if (ret == 0) { + XFREE(ech->innerClientHello, heap, DYNAMIC_TYPE_TMP_BUFFER); + ech->innerClientHello = newInnerCh; + ech->innerClientHelloLen = (word16)newInnerChLen; + newInnerCh = NULL; + } + + if (newInnerCh != NULL) + XFREE(newInnerCh, heap, DYNAMIC_TYPE_TMP_BUFFER); + + return ret; +} + /* return status after attempting to open the hpke encrypted ech extension, if * successful the inner client hello will be stored in * ech->innerClientHelloLen */ @@ -13516,6 +13838,16 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size, } /* subtract the length of the padding from the length */ ech->innerClientHelloLen -= i; + + /* expand EchOuterExtensions if present */ + ret = TLSX_ExpandEchOuterExtensions(ssl, ech, ssl->heap); + if (ret != 0) { + WOLFSSL_MSG_EX("ECH: failed to expand EchOuterExtensions"); + XFREE(ech->innerClientHello, ssl->heap, + DYNAMIC_TYPE_TMP_BUFFER); + ech->innerClientHello = NULL; + ech->state = ECH_WRITE_RETRY_CONFIGS; + } } XFREE(aadCopy, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); return 0; diff --git a/src/tls13.c b/src/tls13.c index 3737b5503b..0f09b540db 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -13045,7 +13045,6 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, if (echX != NULL && ((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE) { byte copyRandom = ((WOLFSSL_ECH*)echX->data)->innerCount == 0; - /* reset the inOutIdx to the outer start */ *inOutIdx = echInOutIdx; /* call again with the inner hello */ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 31593f54c6..a401b255ea 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2964,6 +2964,8 @@ typedef struct Options Options; #define TLSXT_KEY_QUIC_TP_PARAMS 0x0039 /* RFC 9001, ch. 8.2 */ #define TLSXT_ECH 0xfe0d /* from */ /* draft-ietf-tls-esni-13 */ +#define TLSXT_ECH_OUTER_EXTENSIONS 0xfd00 /* from + draft-ietf-tls-esni-13 */ /* The 0xFF section is experimental/custom/personal use */ #define TLSXT_CKS 0xff92 /* X9.146 */ #define TLSXT_RENEGOTIATION_INFO 0xff01 From a7a37e94fddc2732f18538a2b06906851f2002bb Mon Sep 17 00:00:00 2001 From: sebastian-carpenter Date: Tue, 3 Feb 2026 16:49:43 -0700 Subject: [PATCH 4/4] bad rebase, removing uneeded code --- src/tls13.c | 129 ---------------------------------------------------- 1 file changed, 129 deletions(-) diff --git a/src/tls13.c b/src/tls13.c index 0f09b540db..f88d6bccc4 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -4196,34 +4196,6 @@ static int WritePSKBinders(WOLFSSL* ssl, byte* output, word32 idx) #endif #if defined(HAVE_ECH) -/* returns the index of the first supported cipher suite, -1 if none */ -int EchConfigGetSupportedCipherSuite(WOLFSSL_EchConfig* config) -{ - int i, j, supported = 0; - - for (i = 0; i < config->numCipherSuites; i++) { - supported = 0; - - for (j = 0; j < HPKE_SUPPORTED_KDF_LEN; j++) { - if (config->cipherSuites[i].kdfId == hpkeSupportedKdf[j]) - break; - } - - if (j < HPKE_SUPPORTED_KDF_LEN) - for (j = 0; j < HPKE_SUPPORTED_AEAD_LEN; j++) { - if (config->cipherSuites[i].aeadId == hpkeSupportedAead[j]) { - supported = 1; - break; - } - } - - if (supported) - return i; - } - - return WOLFSSL_FATAL_ERROR; -} - /* returns status after we hash the inner client hello */ static int EchHashHelloInner(WOLFSSL* ssl, WOLFSSL_ECH* ech) { @@ -7401,107 +7373,6 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, return ret; } -#ifdef HAVE_ECH -/* replace the last acceptance field for either sever hello or hrr with the ech - * acceptance parameter, return status */ -static int EchWriteAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz, - byte* output, int acceptOffset, int helloSz, byte msgType) -{ - int ret = 0; - int digestType = 0; - int digestSize = 0; - HS_Hashes* tmpHashes = NULL; - byte zeros[WC_MAX_DIGEST_SIZE]; - byte transcriptEchConf[WC_MAX_DIGEST_SIZE]; - byte expandLabelPrk[WC_MAX_DIGEST_SIZE]; - XMEMSET(zeros, 0, sizeof(zeros)); - XMEMSET(transcriptEchConf, 0, sizeof(transcriptEchConf)); - XMEMSET(expandLabelPrk, 0, sizeof(expandLabelPrk)); - /* store so we can restore regardless of the outcome */ - tmpHashes = ssl->hsHashes; - ssl->hsHashes = ssl->hsHashesEch; - /* hash up to the acceptOffset */ - ret = HashRaw(ssl, output, acceptOffset); - /* hash 8 zeros */ - if (ret == 0) - ret = HashRaw(ssl, zeros, ECH_ACCEPT_CONFIRMATION_SZ); - /* hash the rest of the hello */ - if (ret == 0) { - ret = HashRaw(ssl, output + acceptOffset + ECH_ACCEPT_CONFIRMATION_SZ, - helloSz - (acceptOffset + ECH_ACCEPT_CONFIRMATION_SZ)); - } - /* get the modified transcript hash */ - if (ret == 0) - ret = GetMsgHash(ssl, transcriptEchConf); - if (ret > 0) - ret = 0; - /* pick the right type and size based on mac_algorithm */ - if (ret == 0) { - switch (ssl->specs.mac_algorithm) { -#ifndef NO_SHA256 - case sha256_mac: - digestType = WC_SHA256; - digestSize = WC_SHA256_DIGEST_SIZE; - break; -#endif /* !NO_SHA256 */ -#ifdef WOLFSSL_SHA384 - case sha384_mac: - digestType = WC_SHA384; - digestSize = WC_SHA384_DIGEST_SIZE; - break; -#endif /* WOLFSSL_SHA384 */ -#ifdef WOLFSSL_TLS13_SHA512 - case sha512_mac: - digestType = WC_SHA512; - digestSize = WC_SHA512_DIGEST_SIZE; - break; -#endif /* WOLFSSL_TLS13_SHA512 */ -#ifdef WOLFSSL_SM3 - case sm3_mac: - digestType = WC_SM3; - digestSize = WC_SM3_DIGEST_SIZE; - break; -#endif /* WOLFSSL_SM3 */ - default: - ret = WOLFSSL_FATAL_ERROR; - break; - } - } - /* extract clientRandom with a key of all zeros */ - if (ret == 0) { - PRIVATE_KEY_UNLOCK(); - #if !defined(HAVE_FIPS) || \ - (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(6,0)) - ret = wc_HKDF_Extract_ex(digestType, zeros, (word32)digestSize, - ssl->arrays->clientRandom, RAN_LEN, expandLabelPrk, - ssl->heap, ssl->devId); - #else - ret = wc_HKDF_Extract(digestType, zeros, digestSize, - ssl->arrays->clientRandom, RAN_LEN, expandLabelPrk); - #endif - PRIVATE_KEY_LOCK(); - } - /* tls expand with the confirmation label */ - if (ret == 0) { - PRIVATE_KEY_UNLOCK(); - ret = Tls13HKDFExpandKeyLabel(ssl, output + acceptOffset, - ECH_ACCEPT_CONFIRMATION_SZ, expandLabelPrk, (word32)digestSize, - tls13ProtocolLabel, TLS13_PROTOCOL_LABEL_SZ, label, labelSz, - transcriptEchConf, (word32)digestSize, digestType, - WOLFSSL_SERVER_END); - PRIVATE_KEY_LOCK(); - } - /* mark that ech was accepted */ - if (ret == 0 && msgType != hello_retry_request) - ssl->options.echAccepted = 1; - /* free hsHashesEch, if this is an HRR we will start at client hello 2*/ - FreeHandshakeHashes(ssl); - ssl->hsHashesEch = NULL; - ssl->hsHashes = tmpHashes; - return ret; -} -#endif - /* Send TLS v1.3 ServerHello message to client. * Only a server will send this message. *