diff --git a/configure.ac b/configure.ac index ddde97543..19c40fc78 100644 --- a/configure.ac +++ b/configure.ac @@ -4516,6 +4516,21 @@ then AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DTLS_CID" fi +# DTLS 1.3 Fragment Second ClientHello +AC_ARG_ENABLE([dtls-frag-ch], + [AS_HELP_STRING([--enable-dtls-frag-ch],[Enable wolfSSL DTLS 1.3 ClientHello fragmenting (default: disabled)])], + [ ENABLED_DTLS_CH_FRAG=$enableval ], + [ ENABLED_DTLS_CH_FRAG=no ] + ) +if test "x$ENABLED_DTLS_CH_FRAG" = "xyes" +then + if test "x$ENABLED_DTLS13" != "xyes" + then + AC_MSG_ERROR([You need to enable DTLSv1.3 to use DTLS ClientHello fragmenting]) + fi + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DTLS_CH_FRAG" +fi + # CODING AC_ARG_ENABLE([coding], [AS_HELP_STRING([--enable-coding],[Enable Coding base 16/64 (default: enabled)])], diff --git a/examples/server/server.c b/examples/server/server.c index 504aafde6..e882b0efd 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -3322,6 +3322,11 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) } #endif /* WOLFSSL_DTLS_CID */ +#ifdef WOLFSSL_DTLS_CH_FRAG + if (doDTLS) + wolfSSL_dtls13_allow_ch_frag(ssl, 1); +#endif + #ifndef WOLFSSL_CALLBACKS if (nonBlocking) { #ifdef WOLFSSL_DTLS diff --git a/src/dtls.c b/src/dtls.c index c207f33fe..aa7ba3155 100644 --- a/src/dtls.c +++ b/src/dtls.c @@ -26,6 +26,13 @@ * will consume less bandwidth (one ClientHello and one HelloVerifyRequest * less). On the other hand, if a valid SessionID is collected, forged * clientHello messages will consume resources on the server. + * WOLFSSL_DTLS_CH_FRAG + * Allow a server to process a fragmented second/verified (one containing a + * valid cookie response) ClientHello message. The first/unverifies (one + * without a cookie extension) ClientHello MUST be unfragmented so that the + * DTLS server can process it statelessly. This is only implemented for + * DTLS 1.3. The user MUST call wolfSSL_dtls13_allow_ch_frag() on the server + * to explicitly enable this during runtime. */ #ifdef HAVE_CONFIG_H @@ -263,10 +270,13 @@ static int CheckDtlsCookie(const WOLFSSL* ssl, WolfSSL_CH* ch, return ret; } -static int ParseClientHello(const byte* input, word32 helloSz, WolfSSL_CH* ch) +static int ParseClientHello(const byte* input, word32 helloSz, WolfSSL_CH* ch, + byte isFirstCHFrag) { word32 idx = 0; + (void)isFirstCHFrag; + /* protocol version, random and session id length check */ if (OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN > helloSz) return BUFFER_ERROR; @@ -288,9 +298,20 @@ static int ParseClientHello(const byte* input, word32 helloSz, WolfSSL_CH* ch) idx += ReadVector8(input + idx, &ch->compression); if (idx < helloSz - OPAQUE16_LEN) { /* Extensions are optional */ +#ifdef WOLFSSL_DTLS_CH_FRAG + word32 extStart = idx + OPAQUE16_LEN; +#endif idx += ReadVector16(input + idx, &ch->extension); - if (idx > helloSz) - return BUFFER_ERROR; + if (idx > helloSz) { +#ifdef WOLFSSL_DTLS_CH_FRAG + /* Allow incomplete extensions if we are parsing a fragment */ + if (isFirstCHFrag && extStart < helloSz) + ch->extension.size = helloSz - extStart; + else +#endif + return BUFFER_ERROR; + idx = helloSz; + } } if (idx != helloSz) return BUFFER_ERROR; @@ -860,17 +881,27 @@ static int ClientHelloSanityCheck(WolfSSL_CH* ch, byte isTls13) return 0; } -int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, - word32* inOutIdx, word32 helloSz) +int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, word32 helloSz, + byte isFirstCHFrag) { int ret; WolfSSL_CH ch; byte isTls13 = 0; + WOLFSSL_ENTER("DoClientHelloStateless"); + if (isFirstCHFrag) { +#ifdef WOLFSSL_DTLS_CH_FRAG + WOLFSSL_MSG("\tProcessing fragmented ClientHello"); +#else + WOLFSSL_MSG("\tProcessing fragmented ClientHello but " + "WOLFSSL_DTLS_CH_FRAG is not defined. This should not happen."); +#endif + } + XMEMSET(&ch, 0, sizeof(ch)); ssl->options.dtlsStateful = 0; - ret = ParseClientHello(input + *inOutIdx, helloSz, &ch); + ret = ParseClientHello(input, helloSz, &ch, isFirstCHFrag); if (ret != 0) return ret; @@ -894,7 +925,7 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, return ret; #ifdef WOLFSSL_DTLS_NO_HVR_ON_RESUME - if (!isTls13) { + if (!isTls13 && !isFirstCHFrag) { int resume = FALSE; ret = TlsResumptionIsValid(ssl, &ch, &resume); if (ret != 0) @@ -907,7 +938,13 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, #endif if (ch.cookie.size == 0 && ch.cookieExt.size == 0) { - ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13); +#ifdef WOLFSSL_DTLS_CH_FRAG + /* Don't send anything here when processing fragment */ + if (isFirstCHFrag) + ret = BUFFER_ERROR; + else +#endif + ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13); } else { byte cookieGood; @@ -921,6 +958,12 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, if (isTls13) ret = INVALID_PARAMETER; else +#endif +#ifdef WOLFSSL_DTLS_CH_FRAG + /* Don't send anything here when processing fragment */ + if (isFirstCHFrag) + ret = BUFFER_ERROR; + else #endif ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13); } diff --git a/src/dtls13.c b/src/dtls13.c index cd84f27d6..42c7ca7bd 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -1573,6 +1573,19 @@ static int Dtls13RtxSendBuffered(WOLFSSL* ssl) return 0; } +static int Dtls13AcceptFragmented(WOLFSSL *ssl, enum HandShakeType type) +{ + if (IsEncryptionOn(ssl, 0)) + return 1; + if (ssl->options.side == WOLFSSL_CLIENT_END && type == server_hello) + return 1; +#ifdef WOLFSSL_DTLS_CH_FRAG + if (ssl->options.side == WOLFSSL_SERVER_END && type == client_hello && + ssl->options.dtls13ChFrag && ssl->options.dtlsStateful) + return 1; +#endif + return 0; +} /** * Dtls13HandshakeRecv() - process an handshake message. Deal with fragmentation if needed @@ -1646,13 +1659,33 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, isFirst = fragOff == 0; isComplete = isFirst && fragLength == messageLength; - if (!isComplete && !IsEncryptionOn(ssl, 0)) { + if (!isComplete && !Dtls13AcceptFragmented(ssl, handshakeType)) { +#ifdef WOLFSSL_DTLS_CH_FRAG + /* check if the first CH fragment contains a valid cookie */ + if (ssl->options.dtls13ChFrag && !ssl->options.dtlsStateful && + isFirst && handshakeType == client_hello && + DoClientHelloStateless(ssl, input + idx, fragLength, 1) == 0) { + /* We can save this message and continue as stateful. */ + if (ssl->chGoodCb != NULL && !IsSCR(ssl)) { + int cbret = ssl->chGoodCb(ssl, ssl->chGoodCtx); + if (cbret < 0) { + ssl->error = cbret; + WOLFSSL_MSG("ClientHello Good Cb don't continue error"); + return WOLFSSL_FATAL_ERROR; + } + } + WOLFSSL_MSG("ClientHello fragment verified"); + } + else +#endif + { #ifdef WOLFSSL_DEBUG_TLS - WOLFSSL_MSG("DTLS1.3 not accepting fragmented plaintext message"); + WOLFSSL_MSG("DTLS1.3 not accepting fragmented plaintext message"); #endif /* WOLFSSL_DEBUG_TLS */ - /* ignore the message */ - *processedSize = idx + fragLength + ssl->keys.padSz; - return 0; + /* ignore the message */ + *processedSize = idx + fragLength + ssl->keys.padSz; + return 0; + } } usingAsyncCrypto = ssl->devId != INVALID_DEVID; @@ -2797,4 +2830,16 @@ int Dtls13CheckAEADFailLimit(WOLFSSL* ssl) } #endif +#ifdef WOLFSSL_DTLS_CH_FRAG +int wolfSSL_dtls13_allow_ch_frag(WOLFSSL *ssl, int enabled) +{ + if (ssl->options.side == WOLFSSL_CLIENT_END) { + return WOLFSSL_FAILURE; + } + ssl->options.dtls13ChFrag = !!enabled; + return WOLFSSL_SUCCESS; +} +#endif + + #endif /* WOLFSSL_DTLS13 */ diff --git a/src/internal.c b/src/internal.c index 638814f6f..e323acf6c 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8017,6 +8017,12 @@ void SSL_ResourceFree(WOLFSSL* ssl) ssl->dtls13ClientHello = NULL; ssl->dtls13ClientHelloSz = 0; } +#ifdef WOLFSSL_DTLS_CH_FRAG + if (ssl->dtls13KSE != NULL) { + TLSX_KeyShare_FreeAll(ssl->dtls13KSE, ssl->heap); + ssl->dtls13KSE = NULL; + } +#endif #endif /* WOLFSSL_DTLS13 */ #endif /* WOLFSSL_DTLS */ @@ -16806,6 +16812,8 @@ static WC_INLINE int Dtls13CheckWindow(WOLFSSL* ssl) int wordIndex; word32 diff; + WOLFSSL_ENTER("Dtls13CheckWindow"); + if (ssl->dtls13DecryptEpoch == NULL) { WOLFSSL_MSG("Can't find decrypting epoch"); return 0; @@ -17047,14 +17055,26 @@ static WC_INLINE int Dtls13UpdateWindow(WOLFSSL* ssl) int wordOffset; int wordIndex; word32 diff; + Dtls13Epoch* e = ssl->dtls13DecryptEpoch; + + WOLFSSL_ENTER("Dtls13UpdateWindow"); if (ssl->dtls13DecryptEpoch == NULL) { WOLFSSL_MSG("Can't find decrypting Epoch"); return BAD_STATE_E; } - nextSeq = ssl->dtls13DecryptEpoch->nextPeerSeqNumber; - window = ssl->dtls13DecryptEpoch->window; + if (!w64Equal(ssl->keys.curEpoch64, ssl->dtls13DecryptEpoch->epochNumber)) { + /* ssl->dtls13DecryptEpoch has been updated since we received the msg */ + e = Dtls13GetEpoch(ssl, ssl->keys.curEpoch64); + if (e == NULL) { + WOLFSSL_MSG("Can't find decrypting Epoch"); + return BAD_STATE_E; + } + } + + nextSeq = e->nextPeerSeqNumber; + window = e->window; seq = ssl->keys.curSeq; /* seq < nextSeq */ @@ -17075,7 +17095,7 @@ static WC_INLINE int Dtls13UpdateWindow(WOLFSSL* ssl) } window[wordIndex] |= (1 << wordOffset); - return 1; + return 0; } /* seq >= nextSeq, seq - nextSeq */ @@ -17086,9 +17106,9 @@ static WC_INLINE int Dtls13UpdateWindow(WOLFSSL* ssl) _DtlsUpdateWindowGTSeq(w64GetLow32(diff64), window); w64Increment(&seq); - ssl->dtls13DecryptEpoch->nextPeerSeqNumber = seq; + e->nextPeerSeqNumber = seq; - return 1; + return 0; } #endif /* WOLFSSL_DTLS13 */ @@ -20714,32 +20734,17 @@ default: /* the record layer is here */ case runProcessingOneRecord: #ifdef WOLFSSL_DTLS13 - if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) { + if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version) && + !Dtls13CheckWindow(ssl)) { + /* drop packet */ + WOLFSSL_MSG("Dropping DTLS record outside receiving window"); + ssl->options.processReply = doProcessInit; + ssl->buffers.inputBuffer.idx += ssl->curSize; + if (ssl->buffers.inputBuffer.idx > + ssl->buffers.inputBuffer.length) + return BUFFER_E; - if(!Dtls13CheckWindow(ssl)) { - /* drop packet */ - WOLFSSL_MSG( - "Dropping DTLS record outside receiving window"); - ssl->options.processReply = doProcessInit; - ssl->buffers.inputBuffer.idx += ssl->curSize; - if (ssl->buffers.inputBuffer.idx > - ssl->buffers.inputBuffer.length) - return BUFFER_E; - - continue; - } - - ret = Dtls13UpdateWindow(ssl); - if (ret != 1) { - WOLFSSL_ERROR(ret); - return ret; - } - - ret = Dtls13RecordRecvd(ssl); - if (ret != 0) { - WOLFSSL_ERROR(ret); - return ret; - } + continue; } #endif /* WOLFSSL_DTLS13 */ ssl->options.processReply = runProcessingOneMessage; @@ -20773,16 +20778,16 @@ default: } else #endif - /* TLS13 plaintext limit is checked earlier before decryption */ - /* For TLS v1.1 the block size and explicit IV are added to idx, - * so it needs to be included in this limit check */ - if (!IsAtLeastTLSv1_3(ssl->version) - && ssl->curSize - ssl->keys.padSz - - (ssl->buffers.inputBuffer.idx - startIdx) - > MAX_PLAINTEXT_SZ + /* TLS13 plaintext limit is checked earlier before decryption */ + /* For TLS v1.1 the block size and explicit IV are added to idx, + * so it needs to be included in this limit check */ + if (!IsAtLeastTLSv1_3(ssl->version) + && ssl->curSize - ssl->keys.padSz - + (ssl->buffers.inputBuffer.idx - startIdx) + > MAX_PLAINTEXT_SZ #ifdef WOLFSSL_ASYNC_CRYPT - && ssl->buffers.inputBuffer.length != - ssl->buffers.inputBuffer.idx + && ssl->buffers.inputBuffer.length != + ssl->buffers.inputBuffer.idx #endif ) { WOLFSSL_MSG("Plaintext too long"); @@ -20793,17 +20798,6 @@ default: return BUFFER_ERROR; } -#ifdef WOLFSSL_DTLS - if (IsDtlsNotSctpMode(ssl) && !IsAtLeastTLSv1_3(ssl->version)) { - _DtlsUpdateWindow(ssl); - } - - if (ssl->options.dtls) { - /* Reset timeout as we have received a valid DTLS message */ - ssl->dtls_timeout = ssl->dtls_timeout_init; - } -#endif /* WOLFSSL_DTLS */ - WOLFSSL_MSG("received record layer msg"); switch (ssl->curRL.type) { @@ -20813,16 +20807,23 @@ default: if (ssl->options.dtls) { #ifdef WOLFSSL_DTLS if (!IsAtLeastTLSv1_3(ssl->version)) { - ret = DoDtlsHandShakeMsg(ssl, - ssl->buffers.inputBuffer.buffer, - &ssl->buffers.inputBuffer.idx, - ssl->buffers.inputBuffer.length); - if (ret != 0) { - if (SendFatalAlertOnly(ssl, ret) - == SOCKET_ERROR_E) { - ret = SOCKET_ERROR_E; - } + ret = DoDtlsHandShakeMsg(ssl, + ssl->buffers.inputBuffer.buffer, + &ssl->buffers.inputBuffer.idx, + ssl->buffers.inputBuffer.length); + if (ret == 0 && ssl->options.dtlsStateful) { + if (IsDtlsNotSctpMode(ssl)) + _DtlsUpdateWindow(ssl); + /* Reset timeout as we have received a valid + * DTLS handshake message */ + ssl->dtls_timeout = ssl->dtls_timeout_init; + } + if (ret != 0) { + if (SendFatalAlertOnly(ssl, ret) + == SOCKET_ERROR_E) { + ret = SOCKET_ERROR_E; } + } } #endif #ifdef WOLFSSL_DTLS13 @@ -20831,6 +20832,18 @@ default: ssl->buffers.inputBuffer.buffer, &ssl->buffers.inputBuffer.idx, ssl->buffers.inputBuffer.length); + if (ret == 0 && ssl->options.dtlsStateful) { + ret = Dtls13UpdateWindow(ssl); + if (ret != 0) { + WOLFSSL_ERROR(ret); + return ret; + } + ret = Dtls13RecordRecvd(ssl); + if (ret != 0) { + WOLFSSL_ERROR(ret); + return ret; + } + } #ifdef WOLFSSL_EARLY_DATA if (ret == 0 && ssl->options.side == WOLFSSL_SERVER_END && @@ -20951,6 +20964,20 @@ default: WOLFSSL_ERROR_VERBOSE(UNKNOWN_RECORD_TYPE); return UNKNOWN_RECORD_TYPE; } +#ifdef WOLFSSL_DTLS13 + if (ssl->options.dtls) { + ret = Dtls13UpdateWindow(ssl); + if (ret != 0) { + WOLFSSL_ERROR(ret); + return ret; + } + ret = Dtls13RecordRecvd(ssl); + if (ret != 0) { + WOLFSSL_ERROR(ret); + return ret; + } + } +#endif break; } #endif @@ -21038,6 +21065,8 @@ default: #ifdef WOLFSSL_DTLS if (ssl->options.dtls) { WOLFSSL_DTLS_PEERSEQ* peerSeq = ssl->keys.peerSeq; + if (IsDtlsNotSctpMode(ssl)) + _DtlsUpdateWindow(ssl); #ifdef WOLFSSL_MULTICAST if (ssl->options.haveMcast) { peerSeq += ssl->keys.curPeerId; @@ -21099,10 +21128,32 @@ default: return SANITY_MSG_E; } #endif - if ((ret = DoApplicationData(ssl, - ssl->buffers.inputBuffer.buffer, - &ssl->buffers.inputBuffer.idx, - NO_SNIFF)) != 0) { + ret = DoApplicationData(ssl, + ssl->buffers.inputBuffer.buffer, + &ssl->buffers.inputBuffer.idx, NO_SNIFF); +#ifdef WOLFSSL_DTLS + if (ssl->options.dtls && + (ret == 0 || ret == APP_DATA_READY)) { +#ifdef WOLFSSL_DTLS13 + if (IsAtLeastTLSv1_3(ssl->version)) { + int updateRet = Dtls13UpdateWindow(ssl); + if (updateRet != 0) { + WOLFSSL_ERROR(updateRet); + return updateRet; + } + updateRet = Dtls13RecordRecvd(ssl); + if (updateRet != 0) { + WOLFSSL_ERROR(updateRet); + return updateRet; + } + } + else +#endif + if (IsDtlsNotSctpMode(ssl)) + _DtlsUpdateWindow(ssl); + } +#endif + if (ret != 0) { WOLFSSL_ERROR(ret); return ret; } @@ -21131,6 +21182,27 @@ default: /* Reset error if we got an alert level in ret */ if (ret > 0) ret = 0; +#ifdef WOLFSSL_DTLS + if (ssl->options.dtls) { +#ifdef WOLFSSL_DTLS13 + if (IsAtLeastTLSv1_3(ssl->version)) { + ret = Dtls13UpdateWindow(ssl); + if (ret != 0) { + WOLFSSL_ERROR(ret); + return ret; + } + ret = Dtls13RecordRecvd(ssl); + if (ret != 0) { + WOLFSSL_ERROR(ret); + return ret; + } + } + else +#endif + if (IsDtlsNotSctpMode(ssl)) + _DtlsUpdateWindow(ssl); + } +#endif break; #ifdef WOLFSSL_DTLS13 @@ -21147,6 +21219,16 @@ default: ssl->buffers.inputBuffer.idx += ssl->keys.padSz; if (ret != 0) return ret; + ret = Dtls13UpdateWindow(ssl); + if (ret != 0) { + WOLFSSL_ERROR(ret); + return ret; + } + ret = Dtls13RecordRecvd(ssl); + if (ret != 0) { + WOLFSSL_ERROR(ret); + return ret; + } break; } FALL_THROUGH; @@ -34214,8 +34296,15 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, "VerifyServerSuite() with MEMORY_E"); return 0; } - if (cs->clientKSE == NULL && searched) + if (cs->clientKSE == NULL && searched) { + #ifdef WOLFSSL_SEND_HRR_COOKIE + /* If the CH contains a cookie then we need to send an alert to + * start from scratch. */ + if (TLSX_Find(extensions, TLSX_COOKIE) != NULL) + return INVALID_PARAMETER; + #endif cs->doHelloRetry = 1; + } #ifdef WOLFSSL_ASYNC_CRYPT if (ret == WC_PENDING_E) return ret; @@ -34716,9 +34805,10 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #ifdef WOLFSSL_DTLS /* Update the ssl->options.dtlsStateful setting `if` statement in * wolfSSL_accept when changing this one. */ - if (IsDtlsNotSctpMode(ssl) && IsDtlsNotSrtpMode(ssl) && !IsSCR(ssl)) { + if (IsDtlsNotSctpMode(ssl) && IsDtlsNotSrtpMode(ssl) && !IsSCR(ssl) && + !ssl->options.dtlsStateful) { DtlsSetSeqNumForReply(ssl); - ret = DoClientHelloStateless(ssl, input, inOutIdx, helloSz); + ret = DoClientHelloStateless(ssl, input + *inOutIdx, helloSz, 0); if (ret != 0 || !ssl->options.dtlsStateful) { int alertType = TranslateErrorToAlert(ret); if (alertType != invalid_alert) { @@ -34735,6 +34825,14 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, ret = 0; return ret; } + if (ssl->chGoodCb != NULL) { + int cbret = ssl->chGoodCb(ssl, ssl->chGoodCtx); + if (cbret < 0) { + ssl->error = cbret; + WOLFSSL_MSG("ClientHello Good Cb don't continue error"); + return WOLFSSL_FATAL_ERROR; + } + } } ssl->options.dtlsStateful = 1; #endif /* WOLFSSL_DTLS */ diff --git a/src/ssl.c b/src/ssl.c index b0639d992..c0c562405 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -12987,17 +12987,6 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl, } #endif -#ifdef WOLFSSL_DTLS - if (ssl->chGoodCb != NULL && !IsSCR(ssl)) { - int cbret = ssl->chGoodCb(ssl, ssl->chGoodCtx); - if (cbret < 0) { - ssl->error = cbret; - WOLFSSL_MSG("ClientHello Good Cb don't continue error"); - return WOLFSSL_FATAL_ERROR; - } - } -#endif - ssl->options.acceptState = ACCEPT_FIRST_REPLY_DONE; WOLFSSL_MSG("accept state ACCEPT_FIRST_REPLY_DONE"); FALL_THROUGH; @@ -13275,7 +13264,6 @@ int wolfSSL_SetHsDoneCb(WOLFSSL* ssl, HandShakeDoneCb cb, void* user_ctx) ssl->hsDoneCb = cb; ssl->hsDoneCtx = user_ctx; - return WOLFSSL_SUCCESS; } @@ -19240,6 +19228,12 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, ssl->options.haveSessionId = 0; ssl->options.tls = 0; ssl->options.tls1_1 = 0; + #ifdef WOLFSSL_DTLS + ssl->options.dtlsStateful = 0; + #endif + #ifdef WOLFSSL_DTLS_CH_FRAG + ssl->options.dtlsSentEmptyKS = 0; + #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) ssl->options.noPskDheKe = 0; #ifdef HAVE_SUPPORTED_CURVES diff --git a/src/tls.c b/src/tls.c index 6091cb7f9..ede90f857 100644 --- a/src/tls.c +++ b/src/tls.c @@ -67,7 +67,6 @@ #if defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES) static int TLSX_KeyShare_IsSupported(int namedGroup); -static void TLSX_KeyShare_FreeAll(KeyShareEntry* list, void* heap); #endif #ifdef HAVE_SUPPORTED_CURVES @@ -7769,7 +7768,7 @@ int TLSX_KeyShare_GenKey(WOLFSSL *ssl, KeyShareEntry *kse) * list The linked list of key share entry objects. * heap The heap used for allocation. */ -static void TLSX_KeyShare_FreeAll(KeyShareEntry* list, void* heap) +void TLSX_KeyShare_FreeAll(KeyShareEntry* list, void* heap) { KeyShareEntry* current; @@ -8722,7 +8721,7 @@ int TLSX_KeyShare_Parse_ClientHello(const WOLFSSL* ssl, int TLSX_KeyShare_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType) { - int ret; + int ret = 0; KeyShareEntry *keyShareEntry = NULL; word16 group; @@ -8784,24 +8783,50 @@ int TLSX_KeyShare_Parse(WOLFSSL* ssl, const byte* input, word16 length, if (ssl->error != WC_PENDING_E) #endif { - /* Check the selected group was supported by ClientHello extensions. */ + /* Check the selected group was supported by ClientHello + * extensions. */ if (!TLSX_SupportedGroups_Find(ssl, group, ssl->extensions)) { WOLFSSL_ERROR_VERBOSE(BAD_KEY_SHARE_DATA); return BAD_KEY_SHARE_DATA; } - /* Check if the group was sent. */ - if (TLSX_KeyShare_Find(ssl, group)) { - WOLFSSL_ERROR_VERBOSE(BAD_KEY_SHARE_DATA); - return BAD_KEY_SHARE_DATA; +#ifdef WOLFSSL_DTLS_CH_FRAG + /* If we sent an empty key share then we can just limit the keyshare + * to the one selected by the server. */ + if (ssl->options.dtlsSentEmptyKS) { + if (!TLSX_KeyShare_SelectGroup(ssl, group)) { + /* Clear out all groups if not found */ + ret = TLSX_KeyShare_Empty(ssl); + if (ret != 0) + return ret; + } + } + else +#endif + { + /* Check if the group was sent. */ + if (TLSX_KeyShare_Find(ssl, group)) { + WOLFSSL_ERROR_VERBOSE(BAD_KEY_SHARE_DATA); + return BAD_KEY_SHARE_DATA; + } + + /* Clear out unusable key shares. */ + ret = TLSX_KeyShare_Empty(ssl); + if (ret != 0) + return ret; } - /* Clear out unusable key shares. */ - ret = TLSX_KeyShare_Empty(ssl); - if (ret != 0) - return ret; } + +#ifdef WOLFSSL_DTLS_CH_FRAG + /* Check if we were able to limit the keyshare entries to one group */ + if (ssl->options.dtlsSentEmptyKS && + TLSX_KeyShare_SelectGroup(ssl, group)) { + /* Nothing to do */ + } + else +#endif #ifdef HAVE_PQC /* For post-quantum groups, do this in TLSX_PopulateExtensions(). */ if (!WOLFSSL_NAMED_GROUP_IS_PQC(group)) @@ -9102,6 +9127,38 @@ int TLSX_KeyShare_Use(const WOLFSSL* ssl, word16 group, word16 len, byte* data, return 0; } +/* Clear out all entries except for group + * + * ssl The SSL/TLS object. + * returns 1 when the group was found and 0 when it wasn't found. + * */ +int TLSX_KeyShare_SelectGroup(WOLFSSL* ssl, word16 group) +{ + TLSX* extension; + KeyShareEntry* list; + KeyShareEntry** prev; + + /* Find the KeyShare extension if it exists. */ + extension = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE); + if (extension != NULL) { + for (prev = (KeyShareEntry**)&extension->data, + list = (KeyShareEntry*)extension->data; list != NULL; + prev = &list->next, list = list->next) { + if (list->group == group) { + /* Unlink it from the list */ + *prev = list->next; + list->next = NULL; + /* Free the list */ + TLSX_KeyShare_FreeAll((KeyShareEntry*)extension->data, + ssl->heap); + extension->data = list; + return 1; + } + } + } + return 0; +} + /* Set an empty Key Share extension. * * ssl The SSL/TLS object. diff --git a/src/tls13.c b/src/tls13.c index d0765a470..9a23bb2a0 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -4410,10 +4410,48 @@ int SendTls13ClientHello(WOLFSSL* ssl) } #endif - /* Include length of TLS extensions. */ - ret = TLSX_GetRequestSize(ssl, client_hello, &args->length); - if (ret != 0) - return ret; + { +#ifdef WOLFSSL_DTLS_CH_FRAG + int maxFrag = wolfSSL_GetMaxFragSize(ssl, MAX_RECORD_SIZE); + word16 lenWithoutExts = args->length; +#endif + + /* Include length of TLS extensions. */ + ret = TLSX_GetRequestSize(ssl, client_hello, &args->length); + if (ret != 0) + return ret; + +#ifdef WOLFSSL_DTLS_CH_FRAG + if (ssl->options.dtls && args->length > maxFrag && + TLSX_Find(ssl->extensions, TLSX_COOKIE) == NULL) { + /* Try again with an empty key share if we would be fragmenting + * without a cookie */ + TLSX* ks = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE); + if (ks == NULL) { + WOLFSSL_MSG("No key share and CH can't fit in one fragment."); + return BUFFER_ERROR; + } + args->length = lenWithoutExts; + if (ssl->dtls13KSE != NULL) + TLSX_KeyShare_FreeAll(ssl->dtls13KSE, ssl->heap); + ssl->dtls13KSE = (KeyShareEntry*)ks->data; + ks->data = NULL; + ret = TLSX_GetRequestSize(ssl, client_hello, &args->length); + if (ret != 0) { + /* Restore key share data */ + ks->data = ssl->dtls13KSE; + ssl->dtls13KSE = NULL; + return ret; + } + if (args->length > maxFrag) { + WOLFSSL_MSG("Can't fit first CH in one fragment."); + return BUFFER_ERROR; + } + WOLFSSL_MSG("Sending empty key share so we don't fragment CH1"); + ssl->options.dtlsSentEmptyKS = 1; + } +#endif + } /* Total message size. */ args->sendSz = args->length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ; @@ -4653,6 +4691,19 @@ int SendTls13ClientHello(WOLFSSL* ssl) if (ret == 0) FreeAsyncCtx(ssl, 0); #endif +#ifdef WOLFSSL_DTLS_CH_FRAG + if ((ret == 0 || ret == WANT_WRITE) && ssl->dtls13KSE != NULL) { + /* Restore the keyshare */ + TLSX* ks = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE); + if (ks == NULL || ks->data != NULL) { + WOLFSSL_MSG("Missing key share or key share data not NULL"); + return BUFFER_ERROR; + } + WOLFSSL_MSG("Restored key share"); + ks->data = ssl->dtls13KSE; + ssl->dtls13KSE = NULL; + } +#endif WOLFSSL_LEAVE("SendTls13ClientHello", ret); WOLFSSL_END(WC_FUNC_CLIENT_HELLO_SEND); @@ -6624,12 +6675,21 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_SEND_HRR_COOKIE) /* Update the ssl->options.dtlsStateful setting `if` statement in * wolfSSL_accept_TLSv13 when changing this one. */ - if (IsDtlsNotSctpMode(ssl) && ssl->options.sendCookie) { - ret = DoClientHelloStateless(ssl, input, inOutIdx, helloSz); + if (IsDtlsNotSctpMode(ssl) && ssl->options.sendCookie && + !ssl->options.dtlsStateful) { + ret = DoClientHelloStateless(ssl, input + *inOutIdx, helloSz, 0); if (ret != 0 || !ssl->options.dtlsStateful) { *inOutIdx += helloSz; goto exit_dch; } + if (ssl->chGoodCb != NULL && !IsSCR(ssl)) { + int cbret = ssl->chGoodCb(ssl, ssl->chGoodCtx); + if (cbret < 0) { + ssl->error = cbret; + WOLFSSL_MSG("ClientHello Good Cb don't continue error"); + return WOLFSSL_FATAL_ERROR; + } + } } ssl->options.dtlsStateful = 1; #endif /* WOLFSSL_DTLS */ @@ -13223,17 +13283,6 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl) case TLS13_ACCEPT_SECOND_REPLY_DONE : -#ifdef WOLFSSL_DTLS - if (ssl->chGoodCb != NULL) { - int cbret = ssl->chGoodCb(ssl, ssl->chGoodCtx); - if (cbret < 0) { - ssl->error = cbret; - WOLFSSL_MSG("ClientHello Good Cb don't continue error"); - return WOLFSSL_FATAL_ERROR; - } - } -#endif - if ((ssl->error = SendTls13ServerHello(ssl, server_hello)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; diff --git a/tests/api.c b/tests/api.c index 0d51fc840..4e5f6efed 100644 --- a/tests/api.c +++ b/tests/api.c @@ -65238,6 +65238,236 @@ static int test_revoked_loaded_int_cert(void) return EXPECT_RESULT(); } +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) \ + && defined(WOLFSSL_DTLS_MTU) && defined(WOLFSSL_DTLS_CH_FRAG) +static int test_dtls_frag_ch_count_records(byte* b, int len) +{ + DtlsRecordLayerHeader* dtlsRH; + int records = 0; + size_t recordLen; + while (len > 0) { + records++; + dtlsRH = (DtlsRecordLayerHeader*)b; + recordLen = (dtlsRH->length[0] << 8) | dtlsRH->length[1]; + b += sizeof(DtlsRecordLayerHeader) + recordLen; + len -= sizeof(DtlsRecordLayerHeader) + recordLen; + } + return records; +} +#endif + +static int test_dtls_frag_ch(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) \ + && defined(WOLFSSL_DTLS_MTU) && defined(WOLFSSL_DTLS_CH_FRAG) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + static unsigned int DUMMY_MTU = 256; + unsigned char four_frag_CH[] = { + 0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xda, 0x01, 0x00, 0x02, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xce, 0xfe, 0xfd, 0xf3, 0x94, 0x01, 0x33, 0x2c, 0xcf, 0x2c, 0x47, 0xb1, + 0xe5, 0xa1, 0x7b, 0x19, 0x3e, 0xac, 0x68, 0xdd, 0xe6, 0x17, 0x6b, 0x85, + 0xad, 0x5f, 0xfc, 0x7f, 0x6e, 0xf0, 0xb9, 0xe0, 0x2e, 0xca, 0x47, 0x00, + 0x00, 0x00, 0x36, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0xc0, 0x2c, 0xc0, + 0x2b, 0xc0, 0x30, 0xc0, 0x2f, 0x00, 0x9f, 0x00, 0x9e, 0xcc, 0xa9, 0xcc, + 0xa8, 0xcc, 0xaa, 0xc0, 0x27, 0xc0, 0x23, 0xc0, 0x28, 0xc0, 0x24, 0xc0, + 0x0a, 0xc0, 0x09, 0xc0, 0x14, 0xc0, 0x13, 0x00, 0x6b, 0x00, 0x67, 0x00, + 0x39, 0x00, 0x33, 0xcc, 0x14, 0xcc, 0x13, 0xcc, 0x15, 0x01, 0x00, 0x02, + 0x7c, 0x00, 0x2b, 0x00, 0x03, 0x02, 0xfe, 0xfc, 0x00, 0x0d, 0x00, 0x20, + 0x00, 0x1e, 0x06, 0x03, 0x05, 0x03, 0x04, 0x03, 0x02, 0x03, 0x08, 0x06, + 0x08, 0x0b, 0x08, 0x05, 0x08, 0x0a, 0x08, 0x04, 0x08, 0x09, 0x06, 0x01, + 0x05, 0x01, 0x04, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00, 0x0a, 0x00, 0x0c, + 0x00, 0x0a, 0x00, 0x19, 0x00, 0x18, 0x00, 0x17, 0x00, 0x15, 0x01, 0x00, + 0x00, 0x16, 0x00, 0x00, 0x00, 0x33, 0x02, 0x39, 0x02, 0x37, 0x00, 0x17, + 0x00, 0x41, 0x04, 0x94, 0xdf, 0x36, 0xd7, 0xb3, 0x90, 0x6d, 0x01, 0xa1, + 0xe6, 0xed, 0x67, 0xf4, 0xd9, 0x9d, 0x2c, 0xac, 0x57, 0x74, 0xff, 0x19, + 0xbe, 0x5a, 0xc9, 0x30, 0x11, 0xb7, 0x2b, 0x59, 0x47, 0x80, 0x7c, 0xa9, + 0xb7, 0x31, 0x8c, 0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0xda, 0x01, 0x00, 0x02, 0xdc, 0x00, 0x00, 0x00, 0x00, + 0xce, 0x00, 0x00, 0xce, 0x9e, 0x13, 0x74, 0x3b, 0x86, 0xba, 0x69, 0x1f, + 0x12, 0xf7, 0xcd, 0x78, 0x53, 0xe8, 0x50, 0x4d, 0x71, 0x3f, 0x4b, 0x4e, + 0xeb, 0x3e, 0xe5, 0x43, 0x54, 0x78, 0x17, 0x6d, 0x00, 0x18, 0x00, 0x61, + 0x04, 0xd1, 0x99, 0x66, 0x4f, 0xda, 0xc7, 0x12, 0x3b, 0xff, 0xb2, 0xd6, + 0x2f, 0x35, 0xb6, 0x17, 0x1f, 0xb3, 0xd0, 0xb6, 0x52, 0xff, 0x97, 0x8b, + 0x01, 0xe8, 0xd9, 0x68, 0x71, 0x40, 0x02, 0xd5, 0x68, 0x3a, 0x58, 0xb2, + 0x5d, 0xee, 0xa4, 0xe9, 0x5f, 0xf4, 0xaf, 0x3e, 0x30, 0x9c, 0x3e, 0x2b, + 0xda, 0x61, 0x43, 0x99, 0x02, 0x35, 0x33, 0x9f, 0xcf, 0xb5, 0xd3, 0x28, + 0x19, 0x9d, 0x1c, 0xbe, 0x69, 0x07, 0x9e, 0xfc, 0xe4, 0x8e, 0xcd, 0x86, + 0x4a, 0x1b, 0xf0, 0xfc, 0x17, 0x94, 0x66, 0x53, 0xda, 0x24, 0x5e, 0xaf, + 0xce, 0xec, 0x62, 0x4c, 0x06, 0xb4, 0x52, 0x94, 0xb1, 0x4a, 0x7a, 0x8c, + 0x4f, 0x00, 0x19, 0x00, 0x85, 0x04, 0x00, 0x27, 0xeb, 0x99, 0x49, 0x7f, + 0xcb, 0x2c, 0x46, 0x54, 0x2d, 0x93, 0x5d, 0x25, 0x92, 0x58, 0x5e, 0x06, + 0xc3, 0x7c, 0xfb, 0x9a, 0xa7, 0xec, 0xcd, 0x9f, 0xe1, 0x6b, 0x2d, 0x78, + 0xf5, 0x16, 0xa9, 0x20, 0x52, 0x48, 0x19, 0x0f, 0x1a, 0xd0, 0xce, 0xd8, + 0x68, 0xb1, 0x4e, 0x7f, 0x33, 0x03, 0x7d, 0x0c, 0x39, 0xdb, 0x9c, 0x4b, + 0xf4, 0xe7, 0xc2, 0xf5, 0xdd, 0x51, 0x9b, 0x03, 0xa8, 0x53, 0x2b, 0xe6, + 0x00, 0x15, 0x4b, 0xff, 0xd2, 0xa0, 0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xda, 0x01, 0x00, 0x02, 0xdc, 0x00, + 0x00, 0x00, 0x01, 0x9c, 0x00, 0x00, 0xce, 0x58, 0x30, 0x10, 0x3d, 0x46, + 0xcc, 0xca, 0x1a, 0x44, 0xc8, 0x58, 0x9b, 0x27, 0x17, 0x67, 0x31, 0x96, + 0x8a, 0x66, 0x39, 0xf4, 0xcc, 0xc1, 0x9f, 0x12, 0x1f, 0x01, 0x30, 0x50, + 0x16, 0xd6, 0x89, 0x97, 0xa3, 0x66, 0xd7, 0x99, 0x50, 0x09, 0x6e, 0x80, + 0x87, 0xe4, 0xa2, 0x88, 0xae, 0xb4, 0x23, 0x57, 0x2f, 0x12, 0x60, 0xe7, + 0x7d, 0x44, 0x2d, 0xad, 0xbe, 0xe9, 0x0d, 0x01, 0x00, 0x01, 0x00, 0xd5, + 0xdd, 0x62, 0xee, 0xf3, 0x0e, 0xd9, 0x30, 0x0e, 0x38, 0xf3, 0x48, 0xf4, + 0xc9, 0x8f, 0x8c, 0x20, 0xf7, 0xd3, 0xa8, 0xb3, 0x87, 0x3c, 0x98, 0x5d, + 0x70, 0xc5, 0x03, 0x76, 0xb7, 0xd5, 0x0b, 0x7b, 0x23, 0x97, 0x6b, 0xe3, + 0xb5, 0x18, 0xeb, 0x64, 0x55, 0x18, 0xb2, 0x8a, 0x90, 0x1a, 0x8f, 0x0e, + 0x15, 0xda, 0xb1, 0x8e, 0x7f, 0xee, 0x1f, 0xe0, 0x3b, 0xb9, 0xed, 0xfc, + 0x4e, 0x3f, 0x78, 0x16, 0x39, 0x95, 0x5f, 0xb7, 0xcb, 0x65, 0x55, 0x72, + 0x7b, 0x7d, 0x86, 0x2f, 0x8a, 0xe5, 0xee, 0xf7, 0x57, 0x40, 0xf3, 0xc4, + 0x96, 0x4f, 0x11, 0x4d, 0x85, 0xf9, 0x56, 0xfa, 0x3d, 0xf0, 0xc9, 0xa4, + 0xec, 0x1e, 0xaa, 0x47, 0x90, 0x53, 0xdf, 0xe1, 0xb7, 0x78, 0x18, 0xeb, + 0xdd, 0x0d, 0x89, 0xb7, 0xf6, 0x15, 0x0e, 0x55, 0x12, 0xb3, 0x23, 0x17, + 0x0b, 0x59, 0x6f, 0x83, 0x05, 0x6b, 0xa6, 0xf8, 0x6c, 0x3a, 0x9b, 0x1b, + 0x50, 0x93, 0x51, 0xea, 0x95, 0x2d, 0x99, 0x96, 0x38, 0x16, 0xfe, 0xfd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x7e, 0x01, 0x00, + 0x02, 0xdc, 0x00, 0x00, 0x00, 0x02, 0x6a, 0x00, 0x00, 0x72, 0x2d, 0x66, + 0x3e, 0xf2, 0x36, 0x5a, 0xf2, 0x23, 0x8f, 0x28, 0x09, 0xa9, 0x55, 0x8c, + 0x8f, 0xc0, 0x0d, 0x61, 0x98, 0x33, 0x56, 0x87, 0x7a, 0xfd, 0xa7, 0x50, + 0x71, 0x84, 0x2e, 0x41, 0x58, 0x00, 0x87, 0xd9, 0x27, 0xe5, 0x7b, 0xf4, + 0x6d, 0x84, 0x4e, 0x2e, 0x0c, 0x80, 0x0c, 0xf3, 0x8a, 0x02, 0x4b, 0x99, + 0x3a, 0x1f, 0x9f, 0x18, 0x7d, 0x1c, 0xec, 0xad, 0x60, 0x54, 0xa6, 0xa3, + 0x2c, 0x82, 0x5e, 0xf8, 0x8f, 0xae, 0xe1, 0xc4, 0x82, 0x7e, 0x43, 0x43, + 0xc5, 0x99, 0x49, 0x05, 0xd3, 0xf6, 0xdf, 0xa1, 0xb5, 0x2d, 0x0c, 0x13, + 0x2f, 0x1e, 0xb6, 0x28, 0x7c, 0x5c, 0xa1, 0x02, 0x6b, 0x8d, 0xa3, 0xeb, + 0xd4, 0x58, 0xe6, 0xa0, 0x7e, 0x6b, 0xaa, 0x09, 0x43, 0x67, 0x71, 0x87, + 0xa5, 0xcb, 0x68, 0xf3 + }; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + + /* Fragment msgs */ + ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_c, DUMMY_MTU), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_s, DUMMY_MTU), WOLFSSL_SUCCESS); + + /* Add in some key shares to make the CH long */ + ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, WOLFSSL_ECC_SECP256R1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, WOLFSSL_ECC_SECP384R1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, WOLFSSL_ECC_SECP521R1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, WOLFSSL_FFDHE_2048), WOLFSSL_SUCCESS); + + ExpectIntEQ(wolfSSL_dtls13_allow_ch_frag(ssl_s, 1), WOLFSSL_SUCCESS); + + /* Reject fragmented first CH */ + ExpectIntEQ(test_dtls_frag_ch_count_records(four_frag_CH, + sizeof(four_frag_CH)), 4); + XMEMCPY(test_ctx.s_buff, four_frag_CH, sizeof(four_frag_CH)); + test_ctx.s_len = sizeof(four_frag_CH); + while (test_ctx.s_len > 0 && EXPECT_SUCCESS()) { + int s_len = test_ctx.s_len; + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + /* Fail if we didn't advance the buffer to avoid infinite loops */ + ExpectIntLT(test_ctx.s_len, s_len); + } + /* Expect all fragments read */ + ExpectIntEQ(test_ctx.s_len, 0); + /* Expect quietly dropping fragmented first CH */ + ExpectIntEQ(test_ctx.c_len, 0); + + /* CH1 */ + ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + /* Count records. Expect 1 unfragmented CH */ + ExpectIntEQ(test_dtls_frag_ch_count_records(test_ctx.s_buff, + test_ctx.s_len), 1); + /* HRR */ + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + /* CH2 */ + ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + /* Count records. Expect fragmented CH */ + ExpectIntGT(test_dtls_frag_ch_count_records(test_ctx.s_buff, + test_ctx.s_len), 1); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + ssl_c = ssl_s = NULL; + ctx_c = ctx_s = NULL; +#endif + return EXPECT_RESULT(); +} + +static int test_dtls_empty_keyshare_with_cookie(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + unsigned char ch_empty_keyshare_with_cookie[] = { + 0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x12, 0x01, 0x00, 0x01, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x06, 0xfe, 0xfd, 0xfb, 0x8c, 0x9b, 0x28, 0xae, 0x50, 0x1c, 0x4d, 0xf3, + 0xb8, 0xcf, 0x4d, 0xd8, 0x7e, 0x93, 0x13, 0x7b, 0x9e, 0xd9, 0xeb, 0xe9, + 0x13, 0x4b, 0x0d, 0x7f, 0x2e, 0x43, 0x62, 0x8c, 0xe4, 0x57, 0x79, 0x00, + 0x00, 0x00, 0x36, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0xc0, 0x2c, 0xc0, + 0x2b, 0xc0, 0x30, 0xc0, 0x2f, 0x00, 0x9f, 0x00, 0x9e, 0xcc, 0xa9, 0xcc, + 0xa8, 0xcc, 0xaa, 0xc0, 0x27, 0xc0, 0x23, 0xc0, 0x28, 0xc0, 0x24, 0xc0, + 0x0a, 0xc0, 0x09, 0xc0, 0x14, 0xc0, 0x13, 0x00, 0x6b, 0x00, 0x67, 0x00, + 0x39, 0x00, 0x33, 0xcc, 0x14, 0xcc, 0x13, 0xcc, 0x15, 0x01, 0x00, 0x00, + 0xa6, 0x00, 0x2b, 0x00, 0x03, 0x02, 0xfe, 0xfc, 0x00, 0x2c, 0x00, 0x47, + 0x00, 0x45, 0x20, 0xee, 0x4b, 0x17, 0x70, 0x63, 0xa0, 0x4c, 0x82, 0xbf, + 0x43, 0x01, 0x7d, 0x8d, 0xc1, 0x1b, 0x4e, 0x9b, 0xa0, 0x3c, 0x53, 0x1f, + 0xb7, 0xd1, 0x10, 0x81, 0xa8, 0xdf, 0xdf, 0x8c, 0x7f, 0xf3, 0x11, 0x13, + 0x01, 0x02, 0x3d, 0x3b, 0x7d, 0x14, 0x2c, 0x31, 0xb3, 0x60, 0x72, 0x4d, + 0xe5, 0x1a, 0xb2, 0xa3, 0x61, 0x77, 0x73, 0x03, 0x40, 0x0e, 0x5f, 0xc5, + 0x61, 0x38, 0x43, 0x56, 0x21, 0x4a, 0x95, 0xd5, 0x35, 0xa8, 0x0d, 0x00, + 0x0d, 0x00, 0x2a, 0x00, 0x28, 0x06, 0x03, 0x05, 0x03, 0x04, 0x03, 0x02, + 0x03, 0xfe, 0x0b, 0xfe, 0x0e, 0xfe, 0xa0, 0xfe, 0xa3, 0xfe, 0xa5, 0x08, + 0x06, 0x08, 0x0b, 0x08, 0x05, 0x08, 0x0a, 0x08, 0x04, 0x08, 0x09, 0x06, + 0x01, 0x05, 0x01, 0x04, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00, 0x0a, 0x00, + 0x18, 0x00, 0x16, 0x00, 0x19, 0x00, 0x18, 0x00, 0x17, 0x00, 0x15, 0x01, + 0x00, 0x02, 0x3a, 0x02, 0x3c, 0x02, 0x3d, 0x2f, 0x3a, 0x2f, 0x3c, 0x2f, + 0x3d, 0x00, 0x16, 0x00, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x00 + }; + DtlsRecordLayerHeader* dtlsRH; + byte sequence_number[8]; + + XMEMSET(&sequence_number, 0, sizeof(sequence_number)); + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMCPY(test_ctx.s_buff, ch_empty_keyshare_with_cookie, + sizeof(ch_empty_keyshare_with_cookie)); + test_ctx.s_len = sizeof(ch_empty_keyshare_with_cookie); + ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s, + NULL, wolfDTLSv1_3_server_method), 0); + + /* CH1 */ + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + /* Expect an alert. A plaintext alert should be exactly 15 bytes. */ + ExpectIntEQ(test_ctx.c_len, 15); + dtlsRH = (DtlsRecordLayerHeader*)test_ctx.c_buff; + ExpectIntEQ(dtlsRH->type, alert); + ExpectIntEQ(dtlsRH->pvMajor, DTLS_MAJOR); + ExpectIntEQ(dtlsRH->pvMinor, DTLSv1_2_MINOR); + sequence_number[7] = 1; + ExpectIntEQ(XMEMCMP(sequence_number, dtlsRH->sequence_number, + sizeof(sequence_number)), 0); + ExpectIntEQ(dtlsRH->length[0], 0); + ExpectIntEQ(dtlsRH->length[1], 2); + ExpectIntEQ(test_ctx.c_buff[13], alert_fatal); + ExpectIntEQ(test_ctx.c_buff[14], illegal_parameter); + + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + /*----------------------------------------------------------------------------* | Main *----------------------------------------------------------------------------*/ @@ -66507,6 +66737,8 @@ TEST_CASE testCases[] = { TEST_DECL(test_dtls_dropped_ccs), TEST_DECL(test_certreq_sighash_algos), TEST_DECL(test_revoked_loaded_int_cert), + TEST_DECL(test_dtls_frag_ch), + TEST_DECL(test_dtls_empty_keyshare_with_cookie), /* This test needs to stay at the end to clean up any caches allocated. */ TEST_DECL(test_wolfSSL_Cleanup) }; diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 3866fe53b..dd1aeaf3f 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3353,8 +3353,10 @@ typedef struct KeyShareEntry { struct KeyShareEntry* next; /* List pointer */ } KeyShareEntry; +WOLFSSL_LOCAL void TLSX_KeyShare_FreeAll(KeyShareEntry* list, void* heap); WOLFSSL_LOCAL int TLSX_KeyShare_Use(const WOLFSSL* ssl, word16 group, word16 len, byte* data, KeyShareEntry **kse, TLSX** extensions); +WOLFSSL_LOCAL int TLSX_KeyShare_SelectGroup(WOLFSSL* ssl, word16 group); WOLFSSL_LOCAL int TLSX_KeyShare_Empty(WOLFSSL* ssl); WOLFSSL_LOCAL int TLSX_KeyShare_SetSupported(const WOLFSSL* ssl, TLSX** extensions); @@ -4616,7 +4618,12 @@ struct Options { word16 tls1_3:1; /* using TLSv1.3+ ? */ word16 seenUnifiedHdr:1; /* received msg with unified header */ word16 dtls:1; /* using datagrams ? */ +#ifdef WOLFSSL_DTLS word16 dtlsStateful:1; /* allow stateful processing ? */ +#endif +#ifdef WOLFSSL_DTLS_CH_FRAG + word16 dtlsSentEmptyKS:1; /* did we send an empty key share ? */ +#endif word16 connReset:1; /* has the peer reset */ word16 isClosed:1; /* if we consider conn closed */ word16 closeNotify:1; /* we've received a close notify */ @@ -4727,6 +4734,9 @@ struct Options { #ifdef WOLFSSL_DTLS13 word16 dtls13SendMoreAcks:1; /* Send more acks during the * handshake process */ +#ifdef WOLFSSL_DTLS_CH_FRAG + word16 dtls13ChFrag:1; +#endif #endif #ifdef WOLFSSL_TLS13 word16 tls13MiddleBoxCompat:1; /* TLSv1.3 middlebox compatibility */ @@ -5417,7 +5427,7 @@ struct WOLFSSL { #endif #if defined(WOLFSSL_DTLS) && !defined(NO_WOLFSSL_SERVER) ClientHelloGoodCb chGoodCb; /* notify user we parsed a verified - * ClientHello */ + * ClientHello that passed basic tests */ void* chGoodCtx; /* user ClientHello cb context */ #endif #ifndef NO_HANDSHAKE_DONE_CB @@ -5611,6 +5621,9 @@ struct WOLFSSL { Dtls13Rtx dtls13Rtx; byte *dtls13ClientHello; word16 dtls13ClientHelloSz; +#ifdef WOLFSSL_DTLS_CH_FRAG + KeyShareEntry* dtls13KSE; +#endif #endif /* WOLFSSL_DTLS13 */ #ifdef WOLFSSL_DTLS_CID @@ -6268,7 +6281,7 @@ WOLFSSL_LOCAL int cipherExtraData(WOLFSSL* ssl); #if !defined(NO_WOLFSSL_SERVER) WOLFSSL_LOCAL int DoClientHelloStateless(WOLFSSL* ssl, - const byte* input, word32* inOutIdx, word32 helloSz); + const byte* input, word32 helloSz, byte isFirstCHFrag); #endif /* !defined(NO_WOLFSSL_SERVER) */ #endif /* WOLFSSL_DTLS */ diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index d49e73ecb..c50c8d5c2 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -5218,6 +5218,10 @@ WOLFSSL_API int wolfSSL_dtls_cid_get_tx(WOLFSSL* ssl, unsigned char* buffer, unsigned int bufferSz); #endif /* defined(WOLFSSL_DTLS_CID) */ +#ifdef WOLFSSL_DTLS_CH_FRAG + WOLFSSL_API int wolfSSL_dtls13_allow_ch_frag(WOLFSSL *ssl, int enabled); +#endif + /* */ #define SSL2_VERSION 0x0002 #define SSL3_VERSION 0x0300 diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 0f9bb0104..54c623227 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -3014,6 +3014,14 @@ extern void uITRON4_free(void *p) ; #error Please do not define both HAVE_LIBOQS and HAVE_PQM4. #endif +#if defined(HAVE_PQC) && defined(WOLFSSL_DTLS13) && \ + !defined(WOLFSSL_DTLS_CH_FRAG) +#warning "Using DTLS 1.3 + pqc without WOLFSSL_DTLS_CH_FRAG will probably fail. Use --enable-dtls-frag-ch to enable it." +#endif +#if !defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CH_FRAG) +#error "WOLFSSL_DTLS_CH_FRAG only works with DTLS 1.3" +#endif + /* SRTP requires DTLS */ #if defined(WOLFSSL_SRTP) && !defined(WOLFSSL_DTLS) #error The SRTP extension requires DTLS