diff --git a/src/dtls13.c b/src/dtls13.c index fd3da647c01..99402ba7005 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -360,24 +360,18 @@ static void Dtls13MsgWasProcessed(WOLFSSL* ssl, enum HandShakeType hs) int Dtls13ProcessBufferedMessages(WOLFSSL* ssl) { - DtlsMsg* msg = ssl->dtls_rx_msg_list; + DtlsMsg* msg; word32 idx = 0; int ret = 0; WOLFSSL_ENTER("Dtls13ProcessBufferedMessages"); - while (msg != NULL) { + DtlsMsgListPurge(ssl); + + while ((msg = DtlsMsgListNextReady(ssl)) != NULL) { int downgraded = 0; idx = 0; - /* message not in order */ - if (ssl->keys.dtls_expected_peer_handshake_number != msg->seq) - break; - - /* message not complete */ - if (!msg->ready) - break; - #ifndef WOLFSSL_DISABLE_EARLY_SANITY_CHECKS ret = MsgCheckEncryption(ssl, msg->type, msg->encrypted); if (ret != 0) { @@ -421,10 +415,8 @@ int Dtls13ProcessBufferedMessages(WOLFSSL* ssl) * incremented. */ ssl->keys.dtls_expected_peer_handshake_number++; - ssl->dtls_rx_msg_list = msg->next; - DtlsMsgDelete(msg, ssl->heap); - msg = ssl->dtls_rx_msg_list; - ssl->dtls_rx_msg_list_sz--; + DtlsMsgListRemove(ssl, msg); + DtlsMsgListPurge(ssl); } if (ret != 0) @@ -438,10 +430,8 @@ int Dtls13ProcessBufferedMessages(WOLFSSL* ssl) static int Dtls13NextMessageComplete(WOLFSSL* ssl) { - return ssl->dtls_rx_msg_list != NULL && - ssl->dtls_rx_msg_list->ready && - ssl->dtls_rx_msg_list->seq == - ssl->keys.dtls_expected_peer_handshake_number; + DtlsMsgListPurge(ssl); + return DtlsMsgListNextReady(ssl) != NULL; } static WC_INLINE int FragIsInOutputBuffer(WOLFSSL* ssl, const byte* frag) @@ -527,7 +517,7 @@ int Dtls13HashClientHello(const WOLFSSL* ssl, byte* hash, int* hashSz, } /* Handshake header DTLS only fields are not included in the transcript hash */ -int Dtls13HashHandshake(WOLFSSL* ssl, const byte* input, word16 length) +int Dtls13HashHandshake(WOLFSSL* ssl, const byte* input, word32 length) { int ret; @@ -546,7 +536,7 @@ int Dtls13HashHandshake(WOLFSSL* ssl, const byte* input, word16 length) input += OPAQUE64_LEN; length -= OPAQUE64_LEN; - return HashRaw(ssl, input, length); + return HashRaw(ssl, input, (int)length); } static int Dtls13SendFragment(WOLFSSL* ssl, byte* output, word16 output_size, @@ -864,6 +854,7 @@ static int Dtls13DetectDisruption(WOLFSSL* ssl, word32 fragOffset) /* is not the next fragment in the message (the check is not 100% perfect, in the worst case, we don't detect the disruption and wait for the other peer retransmission) */ + DtlsMsgListPurge(ssl); if (ssl->dtls_rx_msg_list != NULL) { DtlsFragBucket* last = ssl->dtls_rx_msg_list->fragBucketList; while (last != NULL && last->m.m.next != NULL) @@ -1949,8 +1940,12 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, return 0; } - if (fragOff + fragLength > messageLength) - return BUFFER_ERROR; + /* Same error code as the DTLS 1.2 check so a stateless server drops it */ + if (fragOff + fragLength > messageLength) { + WOLFSSL_MSG("Fragment outside of the handshake message"); + WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR); + return LENGTH_ERROR; + } ret = Dtls13RtxMsgRecvd(ssl, (enum HandShakeType)handshakeType, fragOff, &implicitAck); @@ -2016,6 +2011,7 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, ssl->keys.dtls_peer_handshake_number > ssl->keys.dtls_expected_peer_handshake_number || usingAsyncCrypto) { + DtlsMsgListPurge(ssl); if (ssl->dtls_rx_msg_list_sz < DTLS_POOL_SZ) { /* Only ack a fragment we really buffered. */ if (DtlsMsgStore(ssl, (word16)w64GetLow32(ssl->keys.curEpoch64), @@ -2028,6 +2024,7 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, else { /* DTLS_POOL_SZ outstanding messages is way more than enough for any * valid peer */ + WOLFSSL_ERROR_VERBOSE(DTLS_TOO_MANY_FRAGMENTS_E); return DTLS_TOO_MANY_FRAGMENTS_E; } diff --git a/src/internal.c b/src/internal.c index 5fa235b6963..688b255392f 100644 --- a/src/internal.c +++ b/src/internal.c @@ -11142,6 +11142,63 @@ void DtlsMsgListDelete(DtlsMsg* head, void* heap) } } +static DtlsMsg* DtlsMsgListUnlink(DtlsMsg* head, DtlsMsg* item) +{ + DtlsMsg** prevNext = &head; + + while (*prevNext != NULL && *prevNext != item) + prevNext = &(*prevNext)->next; + if (*prevNext == item) + *prevNext = item->next; + item->next = NULL; + return head; +} + +/* First complete message with the expected sequence number, wherever it is + * in the list. */ +DtlsMsg* DtlsMsgListNextReady(WOLFSSL* ssl) +{ + DtlsMsg* cur = ssl->dtls_rx_msg_list; + + while (cur != NULL && !(cur->ready && + cur->seq == ssl->keys.dtls_expected_peer_handshake_number)) { + cur = cur->next; + } + return cur; +} + +/* Take item out of the rx list and free it. */ +void DtlsMsgListRemove(WOLFSSL* ssl, DtlsMsg* item) +{ + ssl->dtls_rx_msg_list = DtlsMsgListUnlink(ssl->dtls_rx_msg_list, item); + DtlsMsgDelete(item, ssl->heap); + ssl->dtls_rx_msg_list_sz--; +} + +/* Drop stored messages the handshake already moved past. A retransmission + * fragmented differently can complete a message through the record buffer + * and leave an older partial copy behind. */ +void DtlsMsgListPurge(WOLFSSL* ssl) +{ + word32 expected = ssl->keys.dtls_expected_peer_handshake_number; + DtlsMsg** prevNext = &ssl->dtls_rx_msg_list; + DtlsMsg* cur = ssl->dtls_rx_msg_list; + + WOLFSSL_ENTER("DtlsMsgListPurge"); + while (cur != NULL) { + DtlsMsg* next = cur->next; + if (cur->seq < expected) { + *prevNext = next; + DtlsMsgDelete(cur, ssl->heap); + ssl->dtls_rx_msg_list_sz--; + } + else { + prevNext = &cur->next; + } + cur = next; + } +} + /** * Drop messages when they are no longer going to be retransmitted */ @@ -11219,7 +11276,8 @@ static DtlsFragBucket* DtlsMsgCombineFragBuckets(DtlsMsg* msg, newSz = newOffsetEnd - newOffset; - /* Expand the larger bucket if data bridges the gap between cur and next */ + /* Expand the larger bucket if data bridges the gap between cur and + * next */ if (!combineNext || cur->m.m.sz >= next->m.m.sz) { chosenBucket = &cur; otherBucket = next; @@ -11230,16 +11288,17 @@ static DtlsFragBucket* DtlsMsgCombineFragBuckets(DtlsMsg* msg, } { -#ifdef XREALLOC - DtlsFragBucket* tmp = (DtlsFragBucket*)XREALLOC(*chosenBucket, + DtlsFragBucket* tmp; +#ifndef WOLFSSL_NO_REALLOC + tmp = (DtlsFragBucket*)XREALLOC(*chosenBucket, sizeof(DtlsFragBucket) + newSz, heap, DYNAMIC_TYPE_DTLS_FRAG); + if (tmp == NULL) + return NULL; #else - DtlsFragBucket* tmp = (DtlsFragBucket*)XMALLOC( - sizeof(DtlsFragBucket) + newSz, heap, DYNAMIC_TYPE_DTLS_FRAG); -#endif + tmp = (DtlsFragBucket*)XMALLOC(sizeof(DtlsFragBucket) + newSz, heap, + DYNAMIC_TYPE_DTLS_FRAG); if (tmp == NULL) return NULL; -#ifndef XREALLOC XMEMCPY(tmp, *chosenBucket, sizeof(DtlsFragBucket) + (*chosenBucket)->m.m.sz); #endif @@ -11250,7 +11309,7 @@ static DtlsFragBucket* DtlsMsgCombineFragBuckets(DtlsMsg* msg, beforeNext = beforeNext->m.m.next; beforeNext->m.m.next = tmp; } -#ifndef XREALLOC +#ifdef WOLFSSL_NO_REALLOC XFREE(*chosenBucket, heap, DYNAMIC_TYPE_DTLS_FRAG); #endif newBucket = *chosenBucket = tmp; @@ -11318,7 +11377,7 @@ static DtlsFragBucket* DtlsMsgCombineFragBuckets(DtlsMsg* msg, return newBucket; } -static void DtlsMsgAssembleCompleteMessage(DtlsMsg* msg) +static int DtlsMsgAssembleCompleteMessage(DtlsMsg* msg, void* heap) { DtlsHandShakeHeader* dtls; @@ -11326,7 +11385,16 @@ static void DtlsMsgAssembleCompleteMessage(DtlsMsg* msg) if (msg->fragBucketListCount != 1 || msg->fragBucketList->m.m.offset != 0 || msg->fragBucketList->m.m.sz != msg->sz) { WOLFSSL_MSG("Major error in fragment assembly logic"); - return; + /* Drop the buckets so a retransmission can rebuild the message. */ + while (msg->fragBucketList != NULL) { + DtlsFragBucket* next = msg->fragBucketList->m.m.next; + DtlsMsgDestroyFragBucket(msg->fragBucketList, heap); + msg->fragBucketList = next; + } + msg->fragBucketListCount = 0; + msg->bytesReceived = 0; + WOLFSSL_ERROR_VERBOSE(BAD_STATE_E); + return BAD_STATE_E; } /* Re-cycle the DtlsFragBucket as the buffer that holds the complete @@ -11358,6 +11426,8 @@ static void DtlsMsgAssembleCompleteMessage(DtlsMsg* msg) c16toa((word16)msg->seq, dtls->message_seq); c32to24(0, dtls->fragment_offset); c32to24(msg->sz, dtls->fragment_length); + + return 0; } int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch, const byte* data, byte type, @@ -11368,8 +11438,10 @@ int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch, const byte* data, byte ty WOLFSSL_ENTER("DtlsMsgSet"); - if (msg == NULL || data == NULL || msg->sz != totalLen || - fragOffsetEnd > totalLen) { + /* no_shake marks an unpopulated msg so it is not a valid wire type. */ + if (msg == NULL || data == NULL || type == no_shake || + msg->sz != totalLen || fragSz > totalLen || + fragOffset > totalLen - fragSz) { WOLFSSL_ERROR_VERBOSE(BAD_FUNC_ARG); return BAD_FUNC_ARG; } @@ -11392,9 +11464,15 @@ int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch, const byte* data, byte ty msg->encrypted = encrypted; } + if (fragSz == 0 && msg->sz != 0) { + /* Nothing to store. Don't let it take up a bucket. */ + return 0; + } + if (msg->fragBucketList == NULL) { /* Clean list. Create first fragment. */ - msg->fragBucketList = DtlsMsgCreateFragBucket(fragOffset, data, fragSz, heap); + msg->fragBucketList = DtlsMsgCreateFragBucket(fragOffset, data, fragSz, + heap); if (msg->fragBucketList != NULL) { msg->bytesReceived = fragSz; msg->fragBucketListCount++; @@ -11430,8 +11508,8 @@ int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch, const byte* data, byte ty WOLFSSL_ERROR_VERBOSE(DTLS_TOO_MANY_FRAGMENTS_E); return DTLS_TOO_MANY_FRAGMENTS_E; } - prev->m.m.next = - DtlsMsgCreateFragBucket(fragOffset, data, fragSz, heap); + prev->m.m.next = DtlsMsgCreateFragBucket(fragOffset, data, + fragSz, heap); if (prev->m.m.next == NULL) return MEMORY_ERROR; msg->bytesReceived += fragSz; @@ -11480,7 +11558,7 @@ int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch, const byte* data, byte ty } if (msg->bytesReceived == msg->sz) - DtlsMsgAssembleCompleteMessage(msg); + return DtlsMsgAssembleCompleteMessage(msg, heap); return 0; } @@ -11576,8 +11654,8 @@ int DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data, DtlsMsg* DtlsMsgInsert(DtlsMsg* head, DtlsMsg* item) { WOLFSSL_ENTER("DtlsMsgInsert"); - if (head == NULL || (item->epoch <= head->epoch && - item->seq < head->seq)) { + /* message_seq is one counter across epochs. Order by it alone. */ + if (head == NULL || item->seq < head->seq) { item->next = head; head = item; } @@ -11588,8 +11666,7 @@ DtlsMsg* DtlsMsgInsert(DtlsMsg* head, DtlsMsg* item) DtlsMsg* cur = head->next; DtlsMsg* prev = head; while (cur) { - if (item->epoch <= cur->epoch && - item->seq < cur->seq) { + if (item->seq < cur->seq) { item->next = cur; prev->next = item; break; @@ -12391,7 +12468,7 @@ int HashInput(WOLFSSL* ssl, const byte* input, int sz) #ifdef WOLFSSL_DTLS13 if (IsAtLeastTLSv1_3(ssl->version)) - return Dtls13HashHandshake(ssl, adj, (word16)sz); + return Dtls13HashHandshake(ssl, adj, (word32)sz); #endif /* WOLFSSL_DTLS13 */ } @@ -13951,7 +14028,10 @@ static int GetRecordHeader(WOLFSSL* ssl, word32* inOutIdx, #ifdef WOLFSSL_DTLS_CID needsEnc = needsEnc || rh->type == dtls12_cid; #endif - if (!_DtlsCheckWindow(ssl) || + /* Epoch 0 records are not protected, so they are neither checked + * against nor counted in the replay window (RFC 9147 Section 4.5.1). + * Duplicate handshake messages are caught by their message_seq. */ + if ((ssl->keys.curEpoch != 0 && !_DtlsCheckWindow(ssl)) || (needsEnc && ssl->keys.curEpoch == 0) || (rh->type == alert && ssl->options.handShakeDone && ssl->keys.curEpoch == 0 && ssl->keys.dtls_epoch != 0)) { @@ -22336,26 +22416,27 @@ static int Dtls13UpdateWindow(WOLFSSL* ssl) int Dtls13UpdateWindowRecordRecvd(WOLFSSL* ssl) { - int ret = Dtls13UpdateWindow(ssl); - if (ret != 0) - return ret; + if (!w64IsZero(ssl->keys.curEpoch64)) { + int ret = Dtls13UpdateWindow(ssl); + if (ret != 0) + return ret; + } return Dtls13RecordRecvd(ssl); } #endif /* WOLFSSL_DTLS13 */ int DtlsMsgDrain(WOLFSSL* ssl) { - DtlsMsg* item = ssl->dtls_rx_msg_list; + DtlsMsg* item; int ret = 0; WOLFSSL_ENTER("DtlsMsgDrain"); - /* While there is an item in the store list, and it is the expected - * message, and it is complete, and there hasn't been an error in the - * last message... */ - while (item != NULL && - ssl->keys.dtls_expected_peer_handshake_number == item->seq && - item->ready && ret == 0) { + DtlsMsgListPurge(ssl); + + /* While the expected message is complete in the store list, and there + * hasn't been an error in the last message... */ + while (ret == 0 && (item = DtlsMsgListNextReady(ssl)) != NULL) { word32 idx = 0; #ifndef WOLFSSL_DISABLE_EARLY_SANITY_CHECKS @@ -22381,15 +22462,14 @@ int DtlsMsgDrain(WOLFSSL* ssl) ret = SOCKET_ERROR_E; } } - #ifdef WOLFSSL_ASYNC_CRYPT - if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { + #ifdef WOLFSSL_HAVE_HS_SUSPEND + if (IsHsSuspendErr(ret)) { + /* Keep the message. It is processed again on resume. */ break; } #endif - ssl->dtls_rx_msg_list = item->next; - DtlsMsgDelete(item, ssl->heap); - item = ssl->dtls_rx_msg_list; - ssl->dtls_rx_msg_list_sz--; + DtlsMsgListRemove(ssl, item); + DtlsMsgListPurge(ssl); } WOLFSSL_LEAVE("DtlsMsgDrain()", ret); @@ -22571,7 +22651,7 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx, fragOffset, fragSz, ssl->heap); *inOutIdx += fragSz; ret = 0; - if (ssl->dtls_rx_msg_list != NULL && ssl->dtls_rx_msg_list->ready) + if (ssl->dtls_rx_msg_list != NULL) ret = DtlsMsgDrain(ssl); } else { @@ -26367,8 +26447,9 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) } #endif -#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_ASYNC_CRYPT) - /* process any pending DTLS messages - this flow can happen with async */ +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_HAVE_HS_SUSPEND) + /* Process any pending DTLS messages. A message that suspended the + * handshake stays in the list and is processed again here. */ if (ssl->dtls_rx_msg_list != NULL) { word32 pendingMsg = ssl->dtls_rx_msg_list_sz; if(IsAtLeastTLSv1_3(ssl->version)) { @@ -26887,7 +26968,11 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) #endif #ifdef WOLFSSL_DTLS13 if (IsAtLeastTLSv1_3(ssl->version)) { - if (!Dtls13CheckWindow(ssl)) { + /* Epoch 0 records are not protected, so they are neither + * checked against nor counted in the replay window + * (RFC 9147 Section 4.5.1). */ + if (!w64IsZero(ssl->keys.curEpoch64) && + !Dtls13CheckWindow(ssl)) { /* drop packet */ WOLFSSL_MSG("Dropping DTLS record outside receiving " "window"); @@ -26915,8 +27000,10 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) } else #endif /* WOLFSSL_DTLS13 */ - /* Only update the window once we enter stateful parsing */ - if (IsDtlsNotSctpMode(ssl) && ssl->options.dtlsStateful) { + /* Only update the window once we enter stateful parsing, and + * only from a protected record (RFC 9147 Section 4.5.1). */ + if (IsDtlsNotSctpMode(ssl) && ssl->options.dtlsStateful && + ssl->keys.curEpoch != 0) { #ifdef WOLFSSL_DTLS_CID dtlsPeerNewer = dtlsRecordIsNewest(ssl); #endif @@ -27148,12 +27235,11 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) * processing so let's move on. */ && (!ssl->options.dtls || ret != WC_NO_ERR_TRACE(WANT_WRITE)) -#ifdef WOLFSSL_ASYNC_CRYPT - /* In async case, on pending, move onto next message. - * Current message should have been DtlsMsgStore'ed and - * should be processed with DtlsMsgDrain */ - && (!ssl->options.dtls - || ret != WC_NO_ERR_TRACE(WC_PENDING_E)) +#ifdef WOLFSSL_HAVE_HS_SUSPEND + /* A suspended DTLS message is processed again on the next + * call: from the rx list when it was buffered there, else + * from the record. Finish the record bookkeeping first. */ + && (!ssl->options.dtls || !IsHsSuspendErr(ret)) #endif ) { WOLFSSL_ERROR(ret); @@ -27280,6 +27366,11 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) * by higher layers. */ if (ret != 0) return ret; +#elif defined(WOLFSSL_DTLS) && defined(WOLFSSL_HAVE_HS_SUSPEND) + /* The caller stores a suspend error in ssl->error, which stops the + * message from being hashed again when it is processed again. */ + if (IsHsSuspendErr(ret)) + return ret; #endif #if defined(WOLFSSL_DTLS13) || defined(HAVE_SECURE_RENEGOTIATION) /* Signal to user that we have application data ready to read */ diff --git a/tests/api.c b/tests/api.c index 9d8957744f4..794cd6a4cbc 100644 --- a/tests/api.c +++ b/tests/api.c @@ -45669,6 +45669,7 @@ TEST_CASE testCases[] = { TEST_DECL_GROUP("ocsp", test_ocsp_no_url_crl_fallback_nonleaf), TEST_DECL_GROUP("ocsp", test_ocsp_no_url_crl_not_loaded), TEST_DECL_GROUP("ocsp", test_tls13_nonblock_ocsp_low_mfl), + TEST_DECL_GROUP("ocsp", test_dtls12_nonblock_ocsp), TEST_DECL_GROUP("ocsp", test_ocsp_ctx_request_cache), TEST_DECL_GROUP("ocsp", test_ocsp_responder), TEST_DECL_GROUP("ocsp", test_wolfIO_DecodeUrl_crlf_reject), diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index e6e4541f0fb..ce4c8b34ebc 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -5863,12 +5863,791 @@ int test_wolfSSL_DTLS_fragment_buckets(void) return EXPECT_RESULT(); } +#define DFBR_MAX_LEN 300 + +/* Small LCG so the fragment sequence is reproducible. */ +static word32 dfbr_rand_state; +static word32 dfbr_rand(void) +{ + dfbr_rand_state = dfbr_rand_state * 1103515245U + 12345U; + return (dfbr_rand_state >> 8) & 0xFFFFFFU; +} + +#if defined(USE_WOLFSSL_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY) && \ + !defined(WOLFSSL_DEBUG_MEMORY) && !defined(WOLFSSL_NO_MALLOC) && \ + !defined(XMALLOC_USER) && !defined(XMALLOC_OVERRIDE) && \ + !defined(WOLFSSL_LINUXKM) +#define DFBR_ALLOC_FAIL +#include +/* Allocator that fails dfbr_fail allocations after letting dfbr_skip pass. */ +static int dfbr_skip; +static int dfbr_fail; +static int dfbr_should_fail(void) +{ + if (dfbr_skip > 0) { + dfbr_skip--; + return 0; + } + if (dfbr_fail > 0) { + dfbr_fail--; + return 1; + } + return 0; +} +static int dfbr_allocs; +static void* dfbr_malloc(size_t sz) +{ + dfbr_allocs++; + return dfbr_should_fail() ? NULL : malloc(sz); +} +static void dfbr_free(void* p) +{ + free(p); +} +static void* dfbr_realloc(void* p, size_t sz) +{ + dfbr_allocs++; + return dfbr_should_fail() ? NULL : realloc(p, sz); +} +#endif + +static void dfbr_free_list(WOLFSSL* ssl) +{ + DtlsMsgListDelete(ssl->dtls_rx_msg_list, ssl->heap); + ssl->dtls_rx_msg_list = NULL; + ssl->dtls_rx_msg_list_sz = 0; +} + +/* Check msg against the coverage bitmap and the source pattern. */ +static int dfbr_check(DtlsMsg* msg, const byte* pattern, const byte* covered, + word32 len, word32 seq) +{ + EXPECT_DECLS; + word32 i; + word32 covSum = 0; + + for (i = 0; i < len; i++) + covSum += covered[i]; + + ExpectNotNull(msg); + if (msg == NULL) + return EXPECT_RESULT(); + + ExpectIntEQ(msg->sz, len); + ExpectIntEQ(msg->type, certificate); + ExpectIntEQ(msg->seq, seq); + ExpectIntEQ(msg->ready, covSum == len); + if (msg->ready) { + const byte* hdr; + + ExpectNull(msg->fragBucketList); + ExpectIntEQ(msg->fragBucketListCount, 0); + ExpectIntEQ(msg->bytesReceived, len); + ExpectNotNull(msg->fullMsg); + ExpectNotNull(msg->raw); + if (msg->fullMsg != NULL) { + ExpectBufEQ(msg->fullMsg, pattern, len); + /* The reconstructed header sits right before the message. */ + hdr = msg->fullMsg - DTLS_HANDSHAKE_HEADER_SZ; + ExpectIntEQ(hdr[0], certificate); + ExpectIntEQ(((word32)hdr[1] << 16) | ((word32)hdr[2] << 8) | + hdr[3], len); + ExpectIntEQ(((word32)hdr[4] << 8) | hdr[5], seq & 0xFFFF); + ExpectIntEQ(((word32)hdr[6] << 16) | ((word32)hdr[7] << 8) | + hdr[8], 0); + ExpectIntEQ(((word32)hdr[9] << 16) | ((word32)hdr[10] << 8) | + hdr[11], len); + } + } + else { + DtlsFragBucket* fb; + word32 sum = 0; + word32 count = 0; + word32 lastEnd = 0; + + ExpectNull(msg->raw); + for (fb = msg->fragBucketList; EXPECT_SUCCESS() && fb != NULL; + fb = fb->m.m.next) { + /* Sorted and separated by at least one byte */ + if (count > 0) + ExpectIntGT(fb->m.m.offset, lastEnd); + ExpectIntGT(fb->m.m.sz, 0); + ExpectIntLE(fb->m.m.offset + fb->m.m.sz, len); + ExpectBufEQ(fb->buf, pattern + fb->m.m.offset, fb->m.m.sz); + for (i = fb->m.m.offset; i < fb->m.m.offset + fb->m.m.sz; i++) + ExpectIntEQ(covered[i], 1); + lastEnd = fb->m.m.offset + fb->m.m.sz; + sum += fb->m.m.sz; + count++; + } + ExpectIntEQ(sum, covSum); + ExpectIntEQ(msg->bytesReceived, sum); + ExpectIntEQ(msg->fragBucketListCount, count); + ExpectIntLE(count, DTLS_FRAG_POOL_SZ); + } + return EXPECT_RESULT(); +} + +/* Feed random fragments and compare against the coverage bitmap. */ +static int dfbr_random_pass(WOLFSSL* ssl, const byte* pattern, int injectFail) +{ + EXPECT_DECLS; + byte covered[DFBR_MAX_LEN]; + word32 iter; + word32 i; + word32 n = 0; + + for (iter = 0; EXPECT_SUCCESS() && iter < 1500; iter++) { + word32 len = dfbr_rand() % (DFBR_MAX_LEN + 1); + DtlsMsg* msg = NULL; + + XMEMSET(covered, 0, sizeof(covered)); + for (n = 0; EXPECT_SUCCESS() && n < 64; n++) { + word32 off = 0; + word32 fsz = 0; + int failed = 0; + int ret; + + switch (dfbr_rand() % 8) { + case 0: /* zero-length anywhere, including at the end */ + off = dfbr_rand() % (len + 1); + break; + case 1: /* whole message */ + fsz = len; + break; + case 2: /* start right after covered data */ + for (i = 1; i < len; i++) { + if (covered[i - 1] && !covered[i]) { + off = i; + break; + } + } + if (len > off) + fsz = 1 + dfbr_rand() % (len - off); + break; + case 3: /* end right where covered data starts */ + for (i = 1; i < len; i++) { + if (!covered[i - 1] && covered[i]) { + fsz = 1 + dfbr_rand() % i; + off = i - fsz; + break; + } + } + break; + default: + off = dfbr_rand() % (len + 1); + fsz = dfbr_rand() % (len - off + 1); + break; + } + +#ifdef DFBR_ALLOC_FAIL + msg = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 1); + if (injectFail && msg != NULL && dfbr_rand() % 4 == 0) { + dfbr_skip = 0; + dfbr_fail = 1; + failed = 1; + } +#else + (void)injectFail; + (void)failed; +#endif + ret = DtlsMsgStore(ssl, 0, 1, pattern + off, len, certificate, + off, fsz, NULL); +#ifdef DFBR_ALLOC_FAIL + if (failed) { + /* Either an allocation failed or none was needed */ + ExpectTrue(ret == 0 || ret == WC_NO_ERR_TRACE(MEMORY_ERROR) || + ret == WC_NO_ERR_TRACE(DTLS_TOO_MANY_FRAGMENTS_E)); + ExpectIntEQ(dfbr_fail, + ret == WC_NO_ERR_TRACE(MEMORY_ERROR) ? 0 : 1); + dfbr_fail = 0; + } + else +#endif + { + ExpectTrue(ret == 0 || + ret == WC_NO_ERR_TRACE(DTLS_TOO_MANY_FRAGMENTS_E)); + } + if (ret == 0) { + for (i = off; i < off + fsz; i++) + covered[i] = 1; + } + msg = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 1); + EXPECT_TEST(dfbr_check(msg, pattern, covered, len, 1)); + if (msg != NULL && msg->ready) + break; + } + if (EXPECT_FAIL()) { + printf("Failed at iteration %u fragment %u len %u\n", iter, n, + len); + } + dfbr_free_list(ssl); + } + return EXPECT_RESULT(); +} + +#ifdef DFBR_ALLOC_FAIL +/* A bucket holds only the bytes received: one allocation per message and + * bucket, one growth per combined fragment. */ +static int dfbr_alloc_count_cases(WOLFSSL* ssl, const byte* pattern) +{ + EXPECT_DECLS; + DtlsMsg* msg = NULL; + + ssl->keys.decryptedCur = 1; + dfbr_allocs = 0; + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern, 90, certificate, 0, 30, + NULL), 0); + /* The message and its bucket */ + ExpectIntEQ(dfbr_allocs, 2); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern + 30, 90, certificate, 30, 30, + NULL), 0); + ExpectIntEQ(dfbr_allocs, 3); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern + 60, 90, certificate, 60, 30, + NULL), 0); + ExpectIntEQ(dfbr_allocs, 4); + ExpectNotNull(msg = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 1)); + ExpectIntEQ(msg->ready, 1); + ExpectBufEQ(msg->fullMsg, pattern, 90); + + dfbr_free_list(ssl); + return EXPECT_RESULT(); +} + +/* Every allocation site fails once and leaves the message consistent. */ +static int dfbr_alloc_fail_cases(WOLFSSL* ssl, const byte* pattern) +{ + EXPECT_DECLS; + DtlsMsg* msg = NULL; + + /* Failed combine leaves both buckets intact */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern, 30, certificate, 0, 10, + NULL), 0); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern + 20, 30, certificate, 20, 10, + NULL), 0); + ExpectNotNull(msg = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 1)); + dfbr_fail = 1; + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern + 10, 30, certificate, 10, 10, + NULL), WC_NO_ERR_TRACE(MEMORY_ERROR)); + ExpectIntEQ(dfbr_fail, 0); + ExpectIntEQ(msg->fragBucketListCount, 2); + ExpectIntEQ(msg->bytesReceived, 20); + ExpectNotNull(msg->fragBucketList); + ExpectIntEQ(msg->fragBucketList->m.m.offset, 0); + ExpectNotNull(msg->fragBucketList->m.m.next); + ExpectIntEQ(msg->fragBucketList->m.m.next->m.m.offset, 20); + /* Retry succeeds and completes the message */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern + 10, 30, certificate, 10, 10, + NULL), 0); + ExpectIntEQ(msg->ready, 1); + ExpectBufEQ(msg->fullMsg, pattern, 30); + + /* Failed insert before the first bucket leaves the list intact */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 2, pattern + 20, 30, certificate, 20, 10, + NULL), 0); + ExpectNotNull(msg = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 2)); + dfbr_fail = 1; + ExpectIntEQ(DtlsMsgStore(ssl, 0, 2, pattern, 30, certificate, 0, 5, NULL), + WC_NO_ERR_TRACE(MEMORY_ERROR)); + ExpectIntEQ(dfbr_fail, 0); + ExpectIntEQ(msg->fragBucketListCount, 1); + ExpectIntEQ(msg->bytesReceived, 10); + ExpectNotNull(msg->fragBucketList); + ExpectIntEQ(msg->fragBucketList->m.m.offset, 20); + ExpectNull(msg->fragBucketList->m.m.next); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 2, pattern, 30, certificate, 0, 5, NULL), + 0); + ExpectIntEQ(msg->fragBucketListCount, 2); + + /* Failed append after the last bucket leaves the list intact */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 3, pattern, 30, certificate, 0, 10, + NULL), 0); + ExpectNotNull(msg = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 3)); + dfbr_fail = 1; + ExpectIntEQ(DtlsMsgStore(ssl, 0, 3, pattern + 20, 30, certificate, 20, 5, + NULL), WC_NO_ERR_TRACE(MEMORY_ERROR)); + ExpectIntEQ(dfbr_fail, 0); + ExpectIntEQ(msg->fragBucketListCount, 1); + ExpectIntEQ(msg->bytesReceived, 10); + ExpectNotNull(msg->fragBucketList); + ExpectNull(msg->fragBucketList->m.m.next); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 3, pattern + 20, 30, certificate, 20, 5, + NULL), 0); + ExpectIntEQ(msg->fragBucketListCount, 2); + + /* Failed first bucket of a message created by a zero-length fragment */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 4, pattern + 5, 30, certificate, 5, 0, + NULL), 0); + ExpectNotNull(msg = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 4)); + dfbr_fail = 1; + ExpectIntEQ(DtlsMsgStore(ssl, 0, 4, pattern, 30, certificate, 0, 30, + NULL), WC_NO_ERR_TRACE(MEMORY_ERROR)); + ExpectIntEQ(dfbr_fail, 0); + ExpectNull(msg->fragBucketList); + ExpectIntEQ(msg->fragBucketListCount, 0); + ExpectIntEQ(msg->bytesReceived, 0); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 4, pattern, 30, certificate, 0, 30, + NULL), 0); + ExpectIntEQ(msg->ready, 1); + + /* Failed DtlsMsg allocation for a new message */ + dfbr_fail = 1; + ExpectIntEQ(DtlsMsgStore(ssl, 0, 5, pattern, 30, certificate, 0, 30, + NULL), WC_NO_ERR_TRACE(MEMORY_E)); + ExpectIntEQ(dfbr_fail, 0); + ExpectNull(DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 5)); + ExpectIntEQ(ssl->error, WC_NO_ERR_TRACE(MEMORY_E)); + ssl->error = 0; + /* Failed first bucket of a new message drops the message */ + dfbr_skip = 1; + dfbr_fail = 1; + ExpectIntEQ(DtlsMsgStore(ssl, 0, 5, pattern, 30, certificate, 0, 30, + NULL), WC_NO_ERR_TRACE(MEMORY_ERROR)); + ExpectIntEQ(dfbr_fail, 0); + ExpectNull(DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 5)); + ExpectIntEQ(ssl->dtls_rx_msg_list_sz, 4); + dfbr_free_list(ssl); + + /* Failed DtlsMsg allocation with an empty list */ + dfbr_fail = 1; + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern, 30, certificate, 0, 30, + NULL), WC_NO_ERR_TRACE(MEMORY_E)); + ExpectIntEQ(dfbr_fail, 0); + ExpectNull(ssl->dtls_rx_msg_list); + ExpectIntEQ(ssl->error, WC_NO_ERR_TRACE(MEMORY_E)); + ssl->error = 0; + return EXPECT_RESULT(); +} +#endif + +int test_wolfSSL_DTLS_fragment_buckets_random(void) +{ + EXPECT_DECLS; + WOLFSSL ssl[1]; + static byte pattern[DFBR_MAX_LEN]; + DtlsMsg* msg = NULL; + word32 i; +#ifdef DFBR_ALLOC_FAIL + wolfSSL_Malloc_cb mf = NULL; + wolfSSL_Free_cb ff = NULL; + wolfSSL_Realloc_cb rf = NULL; + byte* p = NULL; + int injectOk = 1; +#endif + + XMEMSET(ssl, 0, sizeof(*ssl)); + for (i = 0; i < DFBR_MAX_LEN; i++) + pattern[i] = (byte)(i * 7 + 3); + dfbr_rand_state = 0x12345678; + + /* no_shake is the "unpopulated" marker, never a valid wire type */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern, 10, no_shake, 0, 5, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectNull(ssl->dtls_rx_msg_list); + ExpectIntEQ(ssl->dtls_rx_msg_list_sz, 0); + + /* Fragments of an authenticated message must agree on the type */ + ssl->keys.decryptedCur = 1; + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern, 10, certificate, 0, 5, NULL), + 0); + ssl->keys.decryptedCur = 0; + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern + 5, 10, server_hello_done, + 5, 5, NULL), WC_NO_ERR_TRACE(SEQUENCE_ERROR)); + ExpectNotNull(msg = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 1)); + ExpectIntEQ(msg->fragBucketListCount, 1); + ExpectIntEQ(msg->type, certificate); + dfbr_free_list(ssl); + + /* A zero-length fragment is accepted but does not take a bucket */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern + 5, 10, certificate, 5, 0, + NULL), 0); + ExpectNotNull(msg = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 1)); + ExpectNull(msg->fragBucketList); + ExpectIntEQ(msg->fragBucketListCount, 0); + ExpectIntEQ(msg->bytesReceived, 0); + ExpectIntEQ(msg->type, certificate); + ExpectIntEQ(msg->ready, 0); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern, 10, certificate, 0, 10, + NULL), 0); + ExpectIntEQ(msg->ready, 1); + /* An empty message assembles from its single empty fragment */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 2, pattern, 0, server_hello_done, 0, 0, + NULL), 0); + ExpectNotNull(msg = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 2)); + ExpectIntEQ(msg->ready, 1); + ExpectIntEQ(msg->sz, 0); + dfbr_free_list(ssl); + + /* Inconsistent bookkeeping at completion drops the buckets and fails */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern, 30, certificate, 0, 10, + NULL), 0); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern + 20, 30, certificate, 20, 10, + NULL), 0); + ExpectNotNull(msg = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 1)); + ExpectIntEQ(msg->bytesReceived, 20); + /* Corrupt the count so the next fragment appears to complete the msg */ + if (msg != NULL) + msg->bytesReceived = 25; + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern + 10, 30, certificate, 10, 5, + NULL), WC_NO_ERR_TRACE(BAD_STATE_E)); + ExpectNull(msg->fragBucketList); + ExpectIntEQ(msg->fragBucketListCount, 0); + ExpectIntEQ(msg->bytesReceived, 0); + ExpectIntEQ(msg->ready, 0); + ExpectNull(msg->raw); + /* A retransmission rebuilds the message */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern, 30, certificate, 0, 30, + NULL), 0); + ExpectIntEQ(msg->ready, 1); + ExpectBufEQ(msg->fullMsg, pattern, 30); + dfbr_free_list(ssl); + + /* Bucket limit, then a bridging fragment reclaims the slots */ + if (3 * DTLS_FRAG_POOL_SZ + 2 <= DFBR_MAX_LEN) { + word32 len = 3 * (word32)DTLS_FRAG_POOL_SZ + 2; + word32 k; + + for (k = 0; EXPECT_SUCCESS() && k < (word32)DTLS_FRAG_POOL_SZ; k++) { + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern + 3 * k + 2, len, + certificate, 3 * k + 2, 1, NULL), 0); + } + ExpectNotNull(msg = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, 1)); + ExpectIntEQ(msg->fragBucketListCount, DTLS_FRAG_POOL_SZ); + ExpectIntEQ(msg->bytesReceived, DTLS_FRAG_POOL_SZ); + /* Disjoint after the last bucket */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern + len - 1, len, + certificate, len - 1, 1, NULL), + WC_NO_ERR_TRACE(DTLS_TOO_MANY_FRAGMENTS_E)); + /* Disjoint before the first bucket */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern, len, certificate, 0, 1, + NULL), WC_NO_ERR_TRACE(DTLS_TOO_MANY_FRAGMENTS_E)); + ExpectIntEQ(msg->fragBucketListCount, DTLS_FRAG_POOL_SZ); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern, len, certificate, 0, + len - 2, NULL), 0); + ExpectIntEQ(msg->fragBucketListCount, 1); + ExpectIntEQ(msg->bytesReceived, len - 2); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, pattern + len - 2, len, + certificate, len - 2, 2, NULL), 0); + ExpectIntEQ(msg->ready, 1); + ExpectBufEQ(msg->fullMsg, pattern, len); + dfbr_free_list(ssl); + } + +#if defined(DFBR_ALLOC_FAIL) && defined(USE_WOLFSSL_MEMORY) + wolfSSL_GetAllocators(&mf, &ff, &rf); + ExpectIntEQ(wolfSSL_SetAllocators(dfbr_malloc, dfbr_free, dfbr_realloc), + 0); + /* Some ports route XMALLOC past wolfSSL_Malloc. Check injection works. */ + dfbr_skip = 0; + dfbr_fail = 1; + p = (byte*)XMALLOC(1, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (p != NULL) { + XFREE(p, NULL, DYNAMIC_TYPE_TMP_BUFFER); + injectOk = 0; + } + dfbr_fail = 0; + if (injectOk) { + EXPECT_TEST(dfbr_alloc_count_cases(ssl, pattern)); + EXPECT_TEST(dfbr_alloc_fail_cases(ssl, pattern)); + /* Random passes with injected allocation failures */ + EXPECT_TEST(dfbr_random_pass(ssl, pattern, 1)); + ssl->keys.decryptedCur = 1; + EXPECT_TEST(dfbr_random_pass(ssl, pattern, 1)); + ssl->keys.decryptedCur = 0; + } + + dfbr_skip = 0; + dfbr_fail = 0; + dfbr_free_list(ssl); + wolfSSL_SetAllocators(mf, ff, rf); +#endif + + /* Random passes without failures, plaintext and authenticated */ + EXPECT_TEST(dfbr_random_pass(ssl, pattern, 0)); + ssl->keys.decryptedCur = 1; + EXPECT_TEST(dfbr_random_pass(ssl, pattern, 0)); + ssl->keys.decryptedCur = 0; + dfbr_free_list(ssl); + + return EXPECT_RESULT(); +} + #else int test_wolfSSL_DTLS_fragment_buckets(void) { return TEST_SKIPPED; } +int test_wolfSSL_DTLS_fragment_buckets_random(void) +{ + return TEST_SKIPPED; +} +#endif + +int test_wolfSSL_DTLS_msg_list_purge(void) +{ + EXPECT_DECLS; +#ifdef WOLFSSL_DTLS + WOLFSSL ssl[1]; + static byte msg[100]; + DtlsMsg* cur = NULL; + + XMEMSET(ssl, 0, sizeof(*ssl)); + XMEMSET(msg, 7, sizeof(msg)); + /* seq 1 partial, seq 2 complete, seq 3 partial */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 1, msg, 100, certificate, 0, 50, NULL), + 0); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 2, msg, 100, certificate, 0, 100, NULL), + 0); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 3, msg, 100, certificate, 0, 50, NULL), + 0); + ExpectIntEQ(ssl->dtls_rx_msg_list_sz, 3); + + /* Nothing is stale yet */ + ssl->keys.dtls_expected_peer_handshake_number = 1; + DtlsMsgListPurge(ssl); + ExpectIntEQ(ssl->dtls_rx_msg_list_sz, 3); + /* seq 1 and 2 were processed outside of the list */ + ssl->keys.dtls_expected_peer_handshake_number = 3; + DtlsMsgListPurge(ssl); + ExpectIntEQ(ssl->dtls_rx_msg_list_sz, 1); + ExpectNotNull(cur = ssl->dtls_rx_msg_list); + ExpectIntEQ(cur->seq, 3); + ExpectNull(cur->next); + DtlsMsgListDelete(ssl->dtls_rx_msg_list, NULL); + ssl->dtls_rx_msg_list = NULL; +#endif + return EXPECT_RESULT(); +} + + +int test_wolfSSL_DTLS_msg_list_order(void) +{ + EXPECT_DECLS; +#ifdef WOLFSSL_DTLS + WOLFSSL ssl[1]; + static byte msg[100]; + DtlsMsg* cur = NULL; + + XMEMSET(ssl, 0, sizeof(*ssl)); + XMEMSET(msg, 7, sizeof(msg)); + + /* Ordered by sequence number across epochs */ + ExpectIntEQ(DtlsMsgStore(ssl, 0, 50, msg, 100, server_hello, 0, 100, + NULL), 0); + ExpectIntEQ(DtlsMsgStore(ssl, 0, 2, msg, 100, certificate, 0, 50, NULL), + 0); + ExpectIntEQ(DtlsMsgStore(ssl, 2, 2, msg, 100, certificate, 0, 100, NULL), + 0); + ExpectIntEQ(DtlsMsgStore(ssl, 2, 7, msg, 100, certificate, 0, 50, NULL), + 0); + ExpectIntEQ(ssl->dtls_rx_msg_list_sz, 4); + ExpectNotNull(cur = ssl->dtls_rx_msg_list); + ExpectIntEQ(cur->seq, 2); + ExpectIntEQ(cur->epoch, 0); + ExpectNotNull(cur = cur->next); + ExpectIntEQ(cur->seq, 2); + ExpectIntEQ(cur->epoch, 2); + ExpectNotNull(cur = cur->next); + ExpectIntEQ(cur->seq, 7); + ExpectNotNull(cur = cur->next); + ExpectIntEQ(cur->seq, 50); + ExpectNull(cur->next); + + /* The complete expected message is found behind a partial one with the + * same number from another epoch */ + ssl->keys.dtls_expected_peer_handshake_number = 2; + ExpectNotNull(cur = DtlsMsgListNextReady(ssl)); + ExpectIntEQ(cur->epoch, 2); + ExpectIntEQ(cur->ready, 1); + ssl->keys.dtls_expected_peer_handshake_number = 7; + ExpectNull(DtlsMsgListNextReady(ssl)); + ssl->keys.dtls_expected_peer_handshake_number = 50; + ExpectNotNull(cur = DtlsMsgListNextReady(ssl)); + ExpectIntEQ(cur->seq, 50); + + DtlsMsgListDelete(ssl->dtls_rx_msg_list, NULL); + ssl->dtls_rx_msg_list = NULL; #endif + return EXPECT_RESULT(); +} + +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + defined(WOLFSSL_DTLS_MTU) +/* A complete in-order message is processed from the record buffer. A partial + * copy of it stored earlier must not block the list behind it. */ +static int test_dtls_stale_partial_head(method_provider method_c, + method_provider method_s, word32 shSeq, int highSeq) +{ + EXPECT_DECLS; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + static byte junk[200]; + const word16 mtu = 1000; /* below the certificate message size */ + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(junk, 0x5a, sizeof(junk)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + /* Fragment the server's certificate so it has to go through the list */ + ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_s, mtu), WOLFSSL_SUCCESS); + + /* Unauthenticated partial ServerHello */ + ExpectIntEQ(DtlsMsgStore(ssl_c, 0, shSeq, junk, sizeof(junk), + server_hello, 0, 50, NULL), 0); + /* Unauthenticated complete entry far ahead */ + if (highSeq) { + ExpectIntEQ(DtlsMsgStore(ssl_c, 0, 50, junk, sizeof(junk), + server_hello, 0, sizeof(junk), NULL), 0); + } + ExpectIntEQ(ssl_c->dtls_rx_msg_list_sz, 1 + (highSeq ? 1 : 0)); + + /* The MTU also caps what the server reads. Make sure the ClientHello + * fits with room for a cookie. */ + ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_READ); + ExpectIntLE(test_ctx.s_len + 128, mtu); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + /* Only the entry far ahead may remain */ + ExpectIntEQ(ssl_c->dtls_rx_msg_list_sz, highSeq ? 1 : 0); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif + +int test_wolfSSL_DTLS_stale_partial_head(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + defined(WOLFSSL_DTLS_MTU) +#ifndef WOLFSSL_NO_TLS12 + /* ServerHello is seq 1 after the HelloVerifyRequest */ + EXPECT_TEST(test_dtls_stale_partial_head(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method, 1, 0)); +#endif +#ifdef WOLFSSL_DTLS13 + EXPECT_TEST(test_dtls_stale_partial_head(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method, 0, 0)); + /* An entry far ahead must not hide the messages behind it */ + EXPECT_TEST(test_dtls_stale_partial_head(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method, 0, 1)); +#endif +#endif + return EXPECT_RESULT(); +} + +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) +/* An unauthenticated record ahead of the server's first flight must not stall + * the handshake. Mode 1: a copy of the first record with a future message_seq, + * which is only buffered, far ahead in record sequence; it must not move the + * replay window. Mode 2: an exact duplicate of the first record, so the + * original is not taken for a replay. Such records come from a stale peer on + * the same port, from duplication on the path, or from a blind sender. */ +static int test_dtls_plaintext_record(method_provider method_c, + method_provider method_s, int mode) +{ + EXPECT_DECLS; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + static char flight[TEST_MEMIO_BUF_SZ]; + int sizes[TEST_MEMIO_MAX_MSGS]; + int count = 0; + int off = 0; + int recLen = 0; + int i; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + + /* ClientHello to the server, the server's first flight back */ + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(test_ctx.c_msg_count, 0); + + /* Keep the server's datagrams to replay them after the bad record */ + count = test_ctx.c_msg_count; + for (i = 0; EXPECT_SUCCESS() && i < count; i++) { + sizes[i] = (int)sizeof(flight) - off; + ExpectIntEQ(test_memio_copy_message(&test_ctx, 1, flight + off, + &sizes[i], i), 0); + off += sizes[i]; + } + /* The first record is a plaintext handshake message */ + ExpectIntGE(sizes[0], DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ); + ExpectIntEQ((byte)flight[0], handshake); + if (EXPECT_SUCCESS()) { + recLen = (((byte)flight[DTLS_RECORD_HEADER_SZ - 2]) << 8) | + (byte)flight[DTLS_RECORD_HEADER_SZ - 1]; + } + ExpectIntLE(DTLS_RECORD_HEADER_SZ + recLen, sizes[0]); + + test_memio_clear_buffer(&test_ctx, 1); + /* First the copy of that record, then the flight as it was */ + if (EXPECT_SUCCESS()) { + byte* recSeq = (byte*)flight + 3; + byte* msgSeq = (byte*)flight + DTLS_RECORD_HEADER_SZ + 4; + byte saved[DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ]; + + XMEMCPY(saved, flight, sizeof(saved)); + if (mode == 1) { + /* Record sequence 2^30 ahead, message_seq 100 */ + XMEMSET(recSeq, 0, 8); + recSeq[4] = 0x40; + c16toa(100, msgSeq); + } + ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, flight, + DTLS_RECORD_HEADER_SZ + recLen), 0); + XMEMCPY(flight, saved, sizeof(saved)); + } + off = 0; + for (i = 0; EXPECT_SUCCESS() && i < count; i++) { + ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, flight + off, + sizes[i]), 0); + off += sizes[i]; + } + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif + +/* Unauthenticated records do not move the replay window, so one record far + * ahead in sequence cannot shut out the real flight, and a duplicated + * plaintext datagram is handled by the handshake layer. */ +int test_wolfSSL_DTLS_plaintext_record_window(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) + int mode; + + for (mode = 1; mode <= 2; mode++) { +#ifndef WOLFSSL_NO_TLS12 + EXPECT_TEST(test_dtls_plaintext_record(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method, mode)); +#endif +#ifdef WOLFSSL_DTLS13 + EXPECT_TEST(test_dtls_plaintext_record(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method, mode)); +#endif + } +#endif + return EXPECT_RESULT(); +} #if !defined(NO_FILESYSTEM) && \ @@ -6990,6 +7769,9 @@ int test_dtls_seq_num_downgrade(void) } /*-- old_seq_number (api.c lines 31953,32005) ---*/ +/* A reused record sequence number on an unauthenticated record is not a + * replay: epoch 0 records are not counted in the replay window, so the flight + * is processed and the handshake completes without a retransmission. */ int test_dtls_old_seq_number(void) { EXPECT_DECLS; @@ -7024,17 +7806,10 @@ int test_dtls_old_seq_number(void) DtlsRecordLayerHeader* dtlsRH = (DtlsRecordLayerHeader*)test_ctx.s_buff; XMEMSET(dtlsRH->sequence_number, 0, sizeof(dtlsRH->sequence_number)); } - /* Server second flight */ - ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); - ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); - /* Server should not do anything as a pkt was dropped */ - ExpectIntEQ(test_ctx.c_len, 0); - ExpectIntEQ(test_ctx.s_len, 0); - /* Trigger rtx */ - ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); - - /* Complete connection */ - ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + /* Server second flight: the client's flight is taken as is */ + ExpectIntEQ(wolfSSL_negotiate(ssl_s), 1); + ExpectIntGT(test_ctx.c_len, 0); + ExpectIntEQ(wolfSSL_negotiate(ssl_c), 1); wolfSSL_free(ssl_c); wolfSSL_CTX_free(ctx_c); @@ -7096,6 +7871,52 @@ int test_dtls12_stateless_window(void) return EXPECT_RESULT(); } +/* A DTLS 1.3 server that has not yet accepted a cookie drops a ClientHello + * fragment that lies outside its message, like any other bad handshake header, + * instead of reporting an error for it. */ +int test_dtls13_stateless_bad_fragment(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + /* Handshake record, epoch 0, holding a client_hello fragment of 32 bytes at + * offset 32 of a 32 byte message. */ + const byte spoof[] = { + 0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2c, + 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + 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); + + ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)spoof, + (int)sizeof(spoof)), 0); + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + if (EXPECT_SUCCESS() && ssl_s != NULL) + ExpectIntEQ(ssl_s->options.dtlsStateful, 0); + + /* The cookie exchange and the rest of the handshake still run. */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + /* Re-frame a protected dtls12_cid record as the given wire type and strip the * CID. The MAC doesn't cover the wire type, so the record still authenticates * and is dispatched on the forged type. */ diff --git a/tests/api/test_dtls.h b/tests/api/test_dtls.h index ae10f8f8377..e13f7a9e785 100644 --- a/tests/api/test_dtls.h +++ b/tests/api/test_dtls.h @@ -141,6 +141,7 @@ int test_dtls_dropped_ccs(void); int test_dtls_seq_num_downgrade(void); int test_dtls_old_seq_number(void); int test_dtls12_stateless_window(void); +int test_dtls13_stateless_bad_fragment(void); int test_dtls12_seq_num_wrap(void); int test_dtls12_replay_window_hi_wrap(void); int test_dtls12_scr_epoch_wrap(void); @@ -191,6 +192,11 @@ int test_wolfSSL_dtls_stateless(void); int test_wolfSSL_dtls_stateless_hrr_group(void); int test_wolfSSL_DtlsUpdateWindow(void); int test_wolfSSL_DTLS_fragment_buckets(void); +int test_wolfSSL_DTLS_fragment_buckets_random(void); +int test_wolfSSL_DTLS_msg_list_purge(void); +int test_wolfSSL_DTLS_msg_list_order(void); +int test_wolfSSL_DTLS_stale_partial_head(void); +int test_wolfSSL_DTLS_plaintext_record_window(void); int test_wolfSSL_dtls_stateless2(void); int test_wolfSSL_dtls_stateless_maxfrag(void); int test_wolfSSL_dtls_stateless_resume(void); @@ -268,6 +274,11 @@ int test_dtls_no_cookie_ch_pause(WOLFSSL* ssl, void* ctx); TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_stateless_hrr_group), \ TEST_DECL_GROUP("dtls", test_wolfSSL_DtlsUpdateWindow), \ TEST_DECL_GROUP("dtls", test_wolfSSL_DTLS_fragment_buckets), \ + TEST_DECL_GROUP("dtls", test_wolfSSL_DTLS_fragment_buckets_random), \ + TEST_DECL_GROUP("dtls", test_wolfSSL_DTLS_msg_list_purge), \ + TEST_DECL_GROUP("dtls", test_wolfSSL_DTLS_msg_list_order), \ + TEST_DECL_GROUP("dtls", test_wolfSSL_DTLS_stale_partial_head), \ + TEST_DECL_GROUP("dtls", test_wolfSSL_DTLS_plaintext_record_window), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_stateless2), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_stateless_maxfrag), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_stateless_resume), \ @@ -285,6 +296,7 @@ int test_dtls_no_cookie_ch_pause(WOLFSSL* ssl, void* ctx); TEST_DECL_GROUP("dtls", test_dtls_seq_num_downgrade), \ TEST_DECL_GROUP("dtls", test_dtls_old_seq_number), \ TEST_DECL_GROUP("dtls", test_dtls12_stateless_window), \ + TEST_DECL_GROUP("dtls", test_dtls13_stateless_bad_fragment), \ TEST_DECL_GROUP("dtls", test_dtls12_seq_num_wrap), \ TEST_DECL_GROUP("dtls", test_dtls12_replay_window_hi_wrap), \ TEST_DECL_GROUP("dtls", test_dtls12_scr_epoch_wrap), \ diff --git a/tests/api/test_ocsp.c b/tests/api/test_ocsp.c index e182d13aad9..9ecaabc3ab3 100644 --- a/tests/api/test_ocsp.c +++ b/tests/api/test_ocsp.c @@ -2317,6 +2317,172 @@ int test_tls13_nonblock_ocsp_low_mfl(void) } #endif +#if defined(HAVE_OCSP) && defined(WOLFSSL_NONBLOCK_OCSP) && \ + defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_MTU) && \ + !defined(WOLFSSL_NO_TLS12) && \ + defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_RSA) && !defined(NO_SHA) +static int test_dtls12_nonblock_ocsp_cb_cnt; +static int test_dtls12_nonblock_ocsp_connect_cnt; +static int test_dtls12_nonblock_ocsp_cb_connect; +static int test_dtls12_nonblock_ocsp_spin; +static int test_dtls12_nonblock_ocsp_group; + +/* OCSP IO callback: a non-blocking responder that answers on the sixth + * call. A call after WANT_READ must come from a new wolfSSL_connect. */ +static int test_dtls12_nonblock_ocsp_io_cb(void* ioCtx, const char* url, + int urlSz, unsigned char* req, int reqSz, unsigned char** respBuf) +{ + (void)ioCtx; + (void)url; + (void)urlSz; + (void)req; + (void)reqSz; + + if (test_dtls12_nonblock_ocsp_cb_cnt > 0 && + test_dtls12_nonblock_ocsp_cb_cnt <= 5 && + test_dtls12_nonblock_ocsp_cb_connect == + test_dtls12_nonblock_ocsp_connect_cnt) { + test_dtls12_nonblock_ocsp_spin = 1; + } + test_dtls12_nonblock_ocsp_cb_connect = test_dtls12_nonblock_ocsp_connect_cnt; + + if (test_dtls12_nonblock_ocsp_cb_cnt++ < 5) + return WOLFSSL_CBIO_ERR_WANT_READ; + + *respBuf = (unsigned char*)resp_server1_cert; + return (int)sizeof(resp_server1_cert); +} + +static int test_dtls12_nonblock_ocsp_ctx_ready(WOLFSSL_CTX* ctx) +{ + EXPECT_DECLS; + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx, + "./certs/ocsp/intermediate1-ca-cert.pem", NULL), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_EnableOCSP(ctx, + WOLFSSL_OCSP_URL_OVERRIDE | WOLFSSL_OCSP_NO_NONCE), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_SetOCSP_OverrideURL(ctx, "http://example.com"), + WOLFSSL_SUCCESS); + /* NULL free-callback: resp points to static array, must not be freed. */ + ExpectIntEQ(wolfSSL_CTX_SetOCSP_Cb(ctx, test_dtls12_nonblock_ocsp_io_cb, + NULL, NULL), WOLFSSL_SUCCESS); + return EXPECT_RESULT(); +} + +/* Server SSL-ready callback: fragment the certificate message and, when + * asked, pack the flight into as few datagrams as the MTU allows. */ +static int test_dtls12_nonblock_ocsp_ssl_ready(WOLFSSL* ssl) +{ + EXPECT_DECLS; + ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl, 1000), WOLFSSL_SUCCESS); + if (test_dtls12_nonblock_ocsp_group) + ExpectIntEQ(wolfSSL_set_group_messages(ssl), WOLFSSL_SUCCESS); + return EXPECT_RESULT(); +} + +/* Server CTX-ready callback: send the leaf certificate alone. */ +static int test_dtls12_nonblock_ocsp_leaf_ctx_ready(WOLFSSL_CTX* ctx) +{ + EXPECT_DECLS; + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, + "./certs/ocsp/server1-cert.pem", WOLFSSL_FILETYPE_PEM), + WOLFSSL_SUCCESS); + return EXPECT_RESULT(); +} + +static int test_dtls12_nonblock_ocsp_case(int fragmented, int grouped) +{ + EXPECT_DECLS; + struct test_ssl_memio_ctx test_ctx; + int hsC = 0; + int hsS = 0; + int i; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + test_dtls12_nonblock_ocsp_cb_cnt = 0; + test_dtls12_nonblock_ocsp_connect_cnt = 0; + test_dtls12_nonblock_ocsp_cb_connect = 0; + test_dtls12_nonblock_ocsp_spin = 0; + test_dtls12_nonblock_ocsp_group = grouped; + + /* The chain needs more than one record at MTU 1000. The leaf alone fits + * in one record at the default MTU. */ + if (fragmented) { + test_ctx.s_cb.certPemFile = "./certs/ocsp/server1-chain-noroot.pem"; + test_ctx.s_cb.ssl_ready = test_dtls12_nonblock_ocsp_ssl_ready; + } + else { + test_ctx.s_cb.certPemFile = ""; + test_ctx.s_cb.ctx_ready = test_dtls12_nonblock_ocsp_leaf_ctx_ready; + } + test_ctx.s_cb.keyPemFile = "./certs/ocsp/server1-key.pem"; + test_ctx.s_cb.method = wolfDTLSv1_2_server_method; + + test_ctx.c_cb.caPemFile = "./certs/ocsp/root-ca-cert.pem"; + test_ctx.c_cb.method = wolfDTLSv1_2_client_method; + test_ctx.c_cb.ctx_ready = test_dtls12_nonblock_ocsp_ctx_ready; + + ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS); + for (i = 0; i < 40 && EXPECT_SUCCESS() && (!hsC || !hsS); i++) { + int ret; + int err; + + if (!hsC) { + test_dtls12_nonblock_ocsp_connect_cnt++; + ret = wolfSSL_connect(test_ctx.c_ssl); + if (ret == WOLFSSL_SUCCESS) { + hsC = 1; + } + else { + err = wolfSSL_get_error(test_ctx.c_ssl, ret); + ExpectTrue(err == WOLFSSL_ERROR_WANT_READ || + err == WOLFSSL_ERROR_WANT_WRITE || + err == WC_NO_ERR_TRACE(OCSP_WANT_READ)); + } + } + if (!hsS) { + ret = wolfSSL_accept(test_ctx.s_ssl); + if (ret == WOLFSSL_SUCCESS) { + hsS = 1; + } + else { + err = wolfSSL_get_error(test_ctx.s_ssl, ret); + ExpectTrue(err == WOLFSSL_ERROR_WANT_READ || + err == WOLFSSL_ERROR_WANT_WRITE); + } + } + } + ExpectIntEQ(hsC, 1); + ExpectIntEQ(hsS, 1); + /* The responder was retried until it answered. */ + ExpectIntGT(test_dtls12_nonblock_ocsp_cb_cnt, 5); + ExpectIntEQ(test_dtls12_nonblock_ocsp_spin, 0); + + test_ssl_memio_cleanup(&test_ctx); + return EXPECT_RESULT(); +} + +/* A certificate that suspends the handshake on the OCSP lookup is processed + * again on resume: from the rx list when it was reassembled from fragments, + * also with more records behind it in the datagram, and from the record when + * it arrived whole. */ +int test_dtls12_nonblock_ocsp(void) +{ + EXPECT_DECLS; + ExpectIntEQ(test_dtls12_nonblock_ocsp_case(1, 0), TEST_SUCCESS); + ExpectIntEQ(test_dtls12_nonblock_ocsp_case(1, 1), TEST_SUCCESS); + ExpectIntEQ(test_dtls12_nonblock_ocsp_case(0, 0), TEST_SUCCESS); + return EXPECT_RESULT(); +} +#else +int test_dtls12_nonblock_ocsp(void) +{ + return TEST_SKIPPED; +} +#endif + /* WOLFSSL_COPY_CERT (implied by OPENSSL_ALL) gives every WOLFSSL its own copy of * the CTX certificate, which sets ssl->buffers.weOwnCert and takes the CTX * request cache out of play entirely - there is nothing to test in that diff --git a/tests/api/test_ocsp.h b/tests/api/test_ocsp.h index 0f9e28e9480..2df40b4fe75 100644 --- a/tests/api/test_ocsp.h +++ b/tests/api/test_ocsp.h @@ -42,6 +42,7 @@ int test_ocsp_no_url_crl_fallback(void); int test_ocsp_no_url_crl_fallback_nonleaf(void); int test_ocsp_no_url_crl_not_loaded(void); int test_tls13_nonblock_ocsp_low_mfl(void); +int test_dtls12_nonblock_ocsp(void); int test_ocsp_ctx_request_cache(void); int test_ocsp_responder(void); int test_ocsp_ancestor_responder_rejected(void); diff --git a/wolfcrypt/src/memory.c b/wolfcrypt/src/memory.c index a2df2258ebe..5f4c2d9c6ce 100644 --- a/wolfcrypt/src/memory.c +++ b/wolfcrypt/src/memory.c @@ -501,7 +501,8 @@ void* wolfSSL_Realloc(void *ptr, size_t size) #else void* res = wolfSSL_Malloc(size); #endif - if (ptr != NULL) { + /* On failure leave the original pointer untouched like realloc. */ + if (res != NULL && ptr != NULL) { /* Copy the minimum of old and new size. */ size_t copySize = *(size_t*)(((unsigned char*)ptr) - MEM_ALIGN); if (size < copySize) { diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 30f5935b754..376a4e58b29 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1247,6 +1247,9 @@ enum { #ifndef WOLFSSL_DTLS_FRAG_POOL_SZ #define WOLFSSL_DTLS_FRAG_POOL_SZ 10 #endif +#if WOLFSSL_DTLS_FRAG_POOL_SZ > 255 + #error "WOLFSSL_DTLS_FRAG_POOL_SZ must be at most 255" +#endif /* set minimum DH key size allowed */ #ifndef WOLFSSL_MIN_DHKEY_BITS @@ -7937,12 +7940,17 @@ WOLFSSL_LOCAL int Tls13KeyUpdateLimitReached(WOLFSSL* ssl); #ifdef WOLFSSL_DTLS #ifdef WOLFSSL_API_PREFIX_MAP #define DtlsMsgListDelete wolfSSL_DtlsMsgListDelete + #define DtlsMsgListPurge wolfSSL_DtlsMsgListPurge + #define DtlsMsgListNextReady wolfSSL_DtlsMsgListNextReady #define DtlsMsgFind wolfSSL_DtlsMsgFind #define DtlsMsgStore wolfSSL_DtlsMsgStore #endif /* WOLFSSL_API_PREFIX_MAP */ WOLFSSL_LOCAL DtlsMsg* DtlsMsgNew(word32 sz, byte tx, void* heap); WOLFSSL_LOCAL void DtlsMsgDelete(DtlsMsg* item, void* heap); WOLFSSL_TEST_VIS void DtlsMsgListDelete(DtlsMsg* head, void* heap); + WOLFSSL_TEST_VIS void DtlsMsgListPurge(WOLFSSL* ssl); + WOLFSSL_TEST_VIS DtlsMsg* DtlsMsgListNextReady(WOLFSSL* ssl); + WOLFSSL_LOCAL void DtlsMsgListRemove(WOLFSSL* ssl, DtlsMsg* item); WOLFSSL_LOCAL void DtlsTxMsgListClean(WOLFSSL* ssl); WOLFSSL_LOCAL int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch, const byte* data, byte type, @@ -8312,7 +8320,7 @@ WOLFSSL_TEST_VIS int Dtls13RtxAddAck(WOLFSSL* ssl, w64wrapper epoch, w64wrapper WOLFSSL_LOCAL int Dtls13RtxProcessingCertificate(WOLFSSL* ssl, byte* input, word32 inputSize); WOLFSSL_LOCAL int Dtls13HashHandshake(WOLFSSL* ssl, const byte* input, - word16 length); + word32 length); WOLFSSL_LOCAL int Dtls13HashClientHello(const WOLFSSL* ssl, byte* hash, int* hashSz, const byte* body, word32 length, CipherSpecs* specs); WOLFSSL_LOCAL void Dtls13FreeFsmResources(WOLFSSL* ssl); diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index da49993e56c..32e022a7cce 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -882,7 +882,7 @@ enum { wolfSSL_Malloc((s), (h), (t), __func__, __LINE__) #ifdef WOLFSSL_XFREE_NO_NULLNESS_CHECK #define XFREE(p, h, t) \ - wolfSSL_Free(xp, h, t, __func__, __LINE__) + wolfSSL_Free((p), h, t, __func__, __LINE__) #else #define XFREE(p, h, t) do { void* xp = (p); if (xp) \ wolfSSL_Free(xp, h, t, __func__, __LINE__); } while (0) @@ -892,7 +892,7 @@ enum { #else #define XMALLOC(s, h, t) wolfSSL_Malloc((s), (h), (t)) #ifdef WOLFSSL_XFREE_NO_NULLNESS_CHECK - #define XFREE(p, h, t) wolfSSL_Free(xp, h, t) + #define XFREE(p, h, t) wolfSSL_Free((p), h, t) #else #define XFREE(p, h, t) do { void* xp = (p); if (xp) \ wolfSSL_Free(xp, h, t); } while (0)