From 03beac5721d376201102c6fa7d380ab59989c162 Mon Sep 17 00:00:00 2001 From: Mattia Moffa Date: Fri, 5 Jun 2026 20:01:18 +0200 Subject: [PATCH 1/3] DTLS: check CID is newest before promoting new peer address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows RFC 9146 § 6. Fixes #10609 --- src/internal.c | 55 +++++++++++++++++--- tests/api/test_dtls.c | 118 ++++++++++++++++++++++++++++++++++++++++++ tests/api/test_dtls.h | 2 + 3 files changed, 169 insertions(+), 6 deletions(-) diff --git a/src/internal.c b/src/internal.c index 8b84e7f2b9..281ac90a7e 100644 --- a/src/internal.c +++ b/src/internal.c @@ -22994,14 +22994,46 @@ static void dtlsClearPeer(WOLFSSL_SOCKADDR* peer) peer->bufSz = 0; } +static int dtlsRecordIsNewest(WOLFSSL* ssl) +{ + WOLFSSL_DTLS_PEERSEQ* peerSeq; + +#ifdef WOLFSSL_DTLS13 + if (IsAtLeastTLSv1_3(ssl->version)) { + Dtls13Epoch* e = ssl->dtls13DecryptEpoch; + + if (!w64Equal(ssl->keys.curEpoch64, ssl->dtls13PeerEpoch)) + return w64GT(ssl->keys.curEpoch64, ssl->dtls13PeerEpoch); + if (e == NULL || !w64Equal(ssl->keys.curEpoch64, e->epochNumber)) + e = Dtls13GetEpoch(ssl, ssl->keys.curEpoch64); + if (e == NULL) + return 0; + return w64GTE(ssl->keys.curSeq, e->nextPeerSeqNumber); + } +#endif + + peerSeq = ssl->keys.peerSeq; +#ifdef WOLFSSL_MULTICAST + if (ssl->options.haveMcast) + return 1; +#endif + + if (ssl->keys.curEpoch != peerSeq->nextEpoch) + return ssl->keys.curEpoch > peerSeq->nextEpoch; + if (ssl->keys.curSeq_hi != peerSeq->nextSeq_hi) + return ssl->keys.curSeq_hi > peerSeq->nextSeq_hi; + return ssl->keys.curSeq_lo >= peerSeq->nextSeq_lo; +} + /** * @brief Handle pending peer during record processing. * @param ssl WOLFSSL object. * @param deprotected 0 when we have not decrypted the record yet * 1 when we have decrypted and verified the record + * @param isNewest 1 when the record is newer than any previously received */ -static void dtlsProcessPendingPeer(WOLFSSL* ssl, int deprotected) +static void dtlsProcessPendingPeer(WOLFSSL* ssl, int deprotected, int isNewest) { if (ssl->buffers.dtlsCtx.pendingPeer.sa != NULL) { if (!deprotected) { @@ -23019,9 +23051,11 @@ static void dtlsProcessPendingPeer(WOLFSSL* ssl, int deprotected) } else { /* Pending peer present and record deprotected. Update the peer. */ - (void)wolfSSL_dtls_set_peer(ssl, - ssl->buffers.dtlsCtx.pendingPeer.sa, - ssl->buffers.dtlsCtx.pendingPeer.sz); + if (isNewest) { + (void)wolfSSL_dtls_set_peer(ssl, + ssl->buffers.dtlsCtx.pendingPeer.sa, + ssl->buffers.dtlsCtx.pendingPeer.sz); + } ssl->buffers.dtlsCtx.processingPendingRecord = 0; dtlsClearPeer(&ssl->buffers.dtlsCtx.pendingPeer); } @@ -23410,7 +23444,7 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) #ifdef WOLFSSL_DTLS #ifdef WOLFSSL_DTLS_CID if (ssl->options.dtls) - dtlsProcessPendingPeer(ssl, 0); + dtlsProcessPendingPeer(ssl, 0, 0); #endif if (ssl->options.dtls && DtlsShouldDrop(ssl, ret)) { DropAndRestartProcessReply(ssl); @@ -23729,6 +23763,9 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) case runProcessingOneRecord: #ifdef WOLFSSL_DTLS if (ssl->options.dtls) { +#ifdef WOLFSSL_DTLS_CID + int dtlsPeerNewer = 1; +#endif #ifdef WOLFSSL_DTLS13 if (IsAtLeastTLSv1_3(ssl->version)) { if (!Dtls13CheckWindow(ssl)) { @@ -23747,6 +23784,9 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) /* Only update the window once we enter stateful parsing */ if (ssl->options.dtlsStateful) { +#ifdef WOLFSSL_DTLS_CID + dtlsPeerNewer = dtlsRecordIsNewest(ssl); +#endif ret = Dtls13UpdateWindowRecordRecvd(ssl); if (ret != 0) { WOLFSSL_ERROR(ret); @@ -23757,12 +23797,15 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) else #endif /* WOLFSSL_DTLS13 */ if (IsDtlsNotSctpMode(ssl)) { +#ifdef WOLFSSL_DTLS_CID + dtlsPeerNewer = dtlsRecordIsNewest(ssl); +#endif DtlsUpdateWindow(ssl); } #ifdef WOLFSSL_DTLS_CID /* Update the peer if we were able to de-protect the message */ if (IsEncryptionOn(ssl, 0)) - dtlsProcessPendingPeer(ssl, 1); + dtlsProcessPendingPeer(ssl, 1, dtlsPeerNewer); #endif } #endif /* WOLFSSL_DTLS */ diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index f160cdc594..7984818350 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -562,6 +562,124 @@ int test_wolfSSL_dtls_set_pending_peer(void) return EXPECT_RESULT(); } +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID) +/* Capture the single client->server record currently buffered, then reset the + * server's incoming buffer so the next record can be captured independently. */ +static int test_dtls_capture_record(struct test_memio_ctx* test_ctx, + byte* out, int* outLen) +{ + EXPECT_DECLS; + ExpectIntEQ(test_ctx->s_msg_count, 1); + if (EXPECT_SUCCESS()) { + *outLen = test_ctx->s_len; + XMEMCPY(out, test_ctx->s_buff, (size_t)test_ctx->s_len); + test_ctx->s_len = 0; + test_ctx->s_msg_count = 0; + test_ctx->s_msg_pos = 0; + } + return EXPECT_RESULT(); +} + +/* Deliver a previously captured record to the server's incoming buffer. */ +static void test_dtls_inject_record(struct test_memio_ctx* test_ctx, + const byte* rec, int recLen) +{ + XMEMCPY(test_ctx->s_buff, rec, (size_t)recLen); + test_ctx->s_len = recLen; + test_ctx->s_msg_sizes[0] = recLen; + test_ctx->s_msg_count = 1; + test_ctx->s_msg_pos = 0; +} + +static int test_dtls_pending_peer_not_newest(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + unsigned char peer[10]; + unsigned int peerSz; + unsigned char readBuf[10]; + unsigned char client_cid[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + unsigned char server_cid[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + byte recA[512], recB[512], recC[512]; + int lenA = 0, lenB = 0, lenC = 0; + + 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); + + ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_c), 1); + ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_c, server_cid, sizeof(server_cid)), 1); + ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_s), 1); + ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_s, client_cid, sizeof(client_cid)), 1); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Capture three consecutive client records with increasing seq numbers. */ + ExpectIntEQ(wolfSSL_write(ssl_c, "msgA", 5), 5); + ExpectIntEQ(test_dtls_capture_record(&test_ctx, recA, &lenA), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_write(ssl_c, "msgB", 5), 5); + ExpectIntEQ(test_dtls_capture_record(&test_ctx, recB, &lenB), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_write(ssl_c, "msgC", 5), 5); + ExpectIntEQ(test_dtls_capture_record(&test_ctx, recC, &lenC), TEST_SUCCESS); + + /* Deliver the newer record (B) first to advance the receive window. */ + test_dtls_inject_record(&test_ctx, recB, lenB); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 5); + ExpectStrEQ(readBuf, "msgB"); + peerSz = sizeof(peer); + ExpectIntEQ(wolfSSL_dtls_get_peer(ssl_s, peer, &peerSz), 0); + + /* Nominate a pending peer, then deliver the older record (A). It is a valid + * CID record but not newer than B, so the peer must NOT be updated. */ + ExpectIntEQ(wolfSSL_dtls_set_pending_peer(ssl_s, (void*)"123", 4), 1); + test_dtls_inject_record(&test_ctx, recA, lenA); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 5); + ExpectStrEQ(readBuf, "msgA"); + peerSz = sizeof(peer); + ExpectIntEQ(wolfSSL_dtls_get_peer(ssl_s, peer, &peerSz), 0); + + /* Nominate again, then deliver the newest record (C). Now the peer must be + * updated. */ + ExpectIntEQ(wolfSSL_dtls_set_pending_peer(ssl_s, (void*)"456", 4), 1); + test_dtls_inject_record(&test_ctx, recC, lenC); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 5); + ExpectStrEQ(readBuf, "msgC"); + peerSz = sizeof(peer); + ExpectIntEQ(wolfSSL_dtls_get_peer(ssl_s, peer, &peerSz), 1); + ExpectIntEQ(peerSz, 4); + ExpectStrEQ(peer, "456"); + + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); + + return EXPECT_RESULT(); +} +#endif + +int test_wolfSSL_dtls_set_pending_peer_not_newest(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID) +#ifndef WOLFSSL_NO_TLS12 + ExpectIntEQ(test_dtls_pending_peer_not_newest(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); +#endif +#ifdef WOLFSSL_DTLS13 + ExpectIntEQ(test_dtls_pending_peer_not_newest(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); +#endif +#endif + return EXPECT_RESULT(); +} + int test_dtls_version_checking(void) { diff --git a/tests/api/test_dtls.h b/tests/api/test_dtls.h index 4523cdef1e..4d9d29a3e9 100644 --- a/tests/api/test_dtls.h +++ b/tests/api/test_dtls.h @@ -25,6 +25,7 @@ int test_dtls12_basic_connection_id(void); int test_wolfSSL_dtls_cid_parse(void); int test_wolfSSL_dtls_set_pending_peer(void); +int test_wolfSSL_dtls_set_pending_peer_not_newest(void); int test_dtls_version_checking(void); int test_dtls_short_ciphertext(void); int test_dtls12_record_length_mismatch(void); @@ -105,6 +106,7 @@ int test_WOLFSSL_dtls_version_alert(void); TEST_DECL_GROUP("dtls", test_dtls12_basic_connection_id), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_cid_parse), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_set_pending_peer), \ + TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_set_pending_peer_not_newest),\ TEST_DECL_GROUP("dtls", test_dtls_version_checking), \ TEST_DECL_GROUP("dtls", test_dtls_short_ciphertext), \ TEST_DECL_GROUP("dtls", test_dtls12_record_length_mismatch), \ From f4666a7981ad6220c27cc993b5c5100d63e5c4d7 Mon Sep 17 00:00:00 2001 From: Mattia Moffa Date: Thu, 11 Jun 2026 18:13:04 +0200 Subject: [PATCH 2/3] CMake: allow CID in DTLS 1.2 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 772da1456d..6f4796c68c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -447,8 +447,8 @@ add_option("WOLFSSL_DTLS_CID" "no" "yes;no") if(WOLFSSL_DTLS_CID) - if(NOT WOLFSSL_DTLS13) - message(FATAL_ERROR "CID are supported only for DTLSv1.3") + if(NOT WOLFSSL_DTLS) + message(FATAL_ERROR "CID requires DTLS") endif() list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_DTLS_CID") endif() From f4952005f74d95a05c2b9c1b1889d93409189be7 Mon Sep 17 00:00:00 2001 From: Mattia Moffa Date: Fri, 12 Jun 2026 22:30:00 +0200 Subject: [PATCH 3/3] DTLS 1.3: receive-side support for RequestConnectionId/NewConnectionId MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements the receive-side of RFC 9147 § 9. On NewConnectionId, if usage is cid_immediate, switches our TX CID to the first usable CID; if usage is cid_spare, discards it. RequestConnectionId is parsed and ignored (responding is SHOULD in the RFC). --- src/dtls.c | 22 +++ src/dtls13.c | 103 ++++++++++++++ src/internal.c | 10 ++ src/tls13.c | 72 +++++++++- tests/api/test_dtls.c | 313 ++++++++++++++++++++++++++++++++++++++++++ tests/api/test_dtls.h | 8 ++ wolfssl/internal.h | 15 ++ 7 files changed, 541 insertions(+), 2 deletions(-) diff --git a/src/dtls.c b/src/dtls.c index 61929cd7a8..7d01e3b782 100644 --- a/src/dtls.c +++ b/src/dtls.c @@ -1407,6 +1407,28 @@ int wolfSSL_dtls_cid_set(WOLFSSL* ssl, unsigned char* cid, unsigned int size) return WOLFSSL_SUCCESS; } +/* Replace the CID we use when sending records, after the peer provided a new + * one in a NewConnectionId message (RFC 9147 Section 9). */ +int DtlsCidReplaceTx(WOLFSSL* ssl, const byte* cid, byte size) +{ + CIDInfo* cidInfo; + ConnectionID* newCid; + + if (ssl == NULL || cid == NULL || size == 0) + return BAD_FUNC_ARG; + + cidInfo = DtlsCidGetInfo(ssl); + if (cidInfo == NULL) + return BAD_STATE_E; + + newCid = DtlsCidNew(cid, size, ssl->heap); + if (newCid == NULL) + return MEMORY_ERROR; + XFREE(cidInfo->tx, ssl->heap, DYNAMIC_TYPE_TLSX); + cidInfo->tx = newCid; + return 0; +} + int wolfSSL_dtls_cid_get_rx_size(WOLFSSL* ssl, unsigned int* size) { return DtlsCidGetSize(ssl, size, 1); diff --git a/src/dtls13.c b/src/dtls13.c index ad69f2612a..fb678506a9 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -230,6 +230,8 @@ static byte Dtls13TypeIsEncrypted(enum HandShakeType hs_type) case finished: case certificate_status: case key_update: + case request_connection_id: + case new_connection_id: case change_cipher_hs: case message_hash: case no_shake: @@ -330,6 +332,11 @@ static byte Dtls13RtxMsgNeedsAck(WOLFSSL* ssl, enum HandShakeType hs) if (hs == session_ticket || hs == key_update) return 1; +#ifdef WOLFSSL_DTLS_CID + if (hs == request_connection_id || hs == new_connection_id) + return 1; +#endif + return 0; } @@ -1806,6 +1813,8 @@ int Dtls13CheckEpoch(WOLFSSL* ssl, enum HandShakeType type) case change_cipher_hs: case key_update: case session_ticket: + case request_connection_id: + case new_connection_id: if (!w64GTE(ssl->keys.curEpoch64, t0Epoch)) { WOLFSSL_MSG("Msg should be epoch 3+"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); @@ -2915,6 +2924,100 @@ int DoDtls13KeyUpdateAck(WOLFSSL* ssl) return ret; } +#ifdef WOLFSSL_DTLS_CID +/* RequestConnectionId: struct { uint8 num_cids; } (RFC 9147 Section 9). + * Responding with NewConnectionId is a SHOULD; we never send records with a + * CID of our choosing, so we ignore the request. */ +int DoDtls13RequestConnectionId(WOLFSSL* ssl, const byte* input, + word32* inOutIdx, word32 size) +{ + (void)ssl; + (void)input; + + if (size != OPAQUE8_LEN) { + WOLFSSL_ERROR_VERBOSE(BUFFER_ERROR); + return BUFFER_ERROR; + } + + *inOutIdx += size; + return 0; +} + +/* NewConnectionId: + * struct { + * ConnectionId cids<0..2^16-1>; + * ConnectionIdUsage usage; + * } (RFC 9147 Section 9), where ConnectionId is opaque<0..2^8-1>. */ +int DoDtls13NewConnectionId(WOLFSSL* ssl, const byte* input, + word32* inOutIdx, word32 size) +{ + word32 idx = *inOutIdx; + const byte* newCid = NULL; + byte newCidLen = 0; + word16 cidsLen; + word32 i; + byte usage; + int ret; + + if (size < OPAQUE16_LEN + OPAQUE8_LEN) { + WOLFSSL_ERROR_VERBOSE(BUFFER_ERROR); + return BUFFER_ERROR; + } + + ato16(input + idx, &cidsLen); + idx += OPAQUE16_LEN; + + if ((word32)cidsLen + OPAQUE16_LEN + OPAQUE8_LEN != size) { + WOLFSSL_ERROR_VERBOSE(BUFFER_ERROR); + return BUFFER_ERROR; + } + + /* walk the cids list, remembering the first non-empty CID */ + for (i = 0; i < cidsLen;) { + byte cidLen = input[idx + i]; + + i += OPAQUE8_LEN; + if (i + cidLen > cidsLen) { + WOLFSSL_ERROR_VERBOSE(BUFFER_ERROR); + return BUFFER_ERROR; + } + if (newCid == NULL && cidLen > 0) { + newCid = input + idx + i; + newCidLen = cidLen; + } + i += cidLen; + } + idx += cidsLen; + + usage = input[idx]; + idx += OPAQUE8_LEN; + + if (usage != cid_immediate && usage != cid_spare) { + WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); + return INVALID_PARAMETER; + } + + if (usage == cid_immediate) { + /* one of the new CIDs MUST be used immediately for all future + * records */ + if (newCid == NULL) { + WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); + return INVALID_PARAMETER; + } + /* replace before the ACK for this record is sent so that the ACK + * already uses the new CID */ + ret = DtlsCidReplaceTx(ssl, newCid, newCidLen); + if (ret != 0) + return ret; + } + /* cid_spare: we MAY simply discard the CIDs and keep using the + * current one */ + + *inOutIdx = idx; + return 0; +} +#endif /* WOLFSSL_DTLS_CID */ + int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize, word32* processedSize) { diff --git a/src/internal.c b/src/internal.c index 281ac90a7e..bcd6a62aba 100644 --- a/src/internal.c +++ b/src/internal.c @@ -11884,6 +11884,8 @@ int MsgCheckEncryption(WOLFSSL* ssl, byte type, byte encrypted) case finished: case certificate_status: case key_update: + case request_connection_id: + case new_connection_id: if (!encrypted) { WOLFSSL_MSG("Message always has to be encrypted"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); @@ -11944,6 +11946,8 @@ int MsgCheckEncryption(WOLFSSL* ssl, byte type, byte encrypted) case key_update: case encrypted_extensions: case end_of_early_data: + case request_connection_id: + case new_connection_id: case message_hash: case no_shake: default: @@ -11992,6 +11996,8 @@ static int MsgCheckBoundary(const WOLFSSL* ssl, byte type, case certificate_status: case key_update: case change_cipher_hs: + case request_connection_id: + case new_connection_id: break; case server_hello_done: case message_hash: @@ -12029,6 +12035,8 @@ static int MsgCheckBoundary(const WOLFSSL* ssl, byte type, case hello_retry_request: case encrypted_extensions: case key_update: + case request_connection_id: + case new_connection_id: case message_hash: case no_shake: default: @@ -12065,6 +12073,8 @@ static int MsgCheckBoundary(const WOLFSSL* ssl, byte type, case key_update: case change_cipher_hs: break; + case request_connection_id: + case new_connection_id: case message_hash: case no_shake: default: diff --git a/src/tls13.c b/src/tls13.c index 18ca7c5dcd..da018b8769 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -13602,6 +13602,54 @@ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type) break; #endif /* WOLFSSL_DTLS13 && !WOLFSSL_NO_TLS12*/ +#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) + case request_connection_id: + case new_connection_id: + { + CIDInfo* cidInfo = ssl->dtlsCidInfo; + + /* DTLS 1.3 only (RFC 9147) */ + if (!ssl->options.dtls) { + WOLFSSL_MSG("CID message received but not DTLS"); + WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); + return SANITY_MSG_E; + } + /* RFC 9147 Section 9: if CIDs were not negotiated, MUST abort + * with an unexpected_message alert */ + if (cidInfo == NULL || !cidInfo->negotiated) { + WOLFSSL_MSG("CID message received but CID not negotiated"); + WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); + return SANITY_MSG_E; + } + if (ssl->options.handShakeState != HANDSHAKE_DONE) { + WOLFSSL_MSG("CID message received out of order"); + WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); + return OUT_OF_ORDER_E; + } + if (type == request_connection_id) { + /* the peer MUST NOT request CIDs while sending an empty + * CID itself */ + if (cidInfo->rx == NULL || cidInfo->rx->length == 0) { + WOLFSSL_MSG("RequestConnectionId from peer sending an " + "empty CID"); + WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); + return SANITY_MSG_E; + } + } + else { + /* the peer MUST NOT issue CIDs after negotiating receiving + * an empty CID */ + if (cidInfo->tx == NULL || cidInfo->tx->length == 0) { + WOLFSSL_MSG("NewConnectionId from peer that negotiated " + "an empty CID"); + WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); + return SANITY_MSG_E; + } + } + break; + } +#endif /* WOLFSSL_DTLS13 && WOLFSSL_DTLS_CID */ + default: WOLFSSL_MSG("Unknown message type"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); @@ -13664,7 +13712,11 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, if (ssl->options.handShakeState == HANDSHAKE_DONE && type != session_ticket && type != certificate_request && - type != certificate && type != key_update && type != finished) { + type != certificate && type != key_update && type != finished +#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) + && type != request_connection_id && type != new_connection_id +#endif + ) { WOLFSSL_MSG("HandShake message after handshake complete"); SendAlert(ssl, alert_fatal, unexpected_message); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); @@ -13846,6 +13898,18 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, ret = DoTls13KeyUpdate(ssl, input, inOutIdx, size); break; +#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) + case request_connection_id: + WOLFSSL_MSG("processing request connection id"); + ret = DoDtls13RequestConnectionId(ssl, input, inOutIdx, size); + break; + + case new_connection_id: + WOLFSSL_MSG("processing new connection id"); + ret = DoDtls13NewConnectionId(ssl, input, inOutIdx, size); + break; +#endif /* WOLFSSL_DTLS13 && WOLFSSL_DTLS_CID */ + #if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_NO_TLS12) && \ !defined(NO_WOLFSSL_CLIENT) case hello_verify_request: @@ -13878,7 +13942,11 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, } #endif if (ret == 0 && type != client_hello && type != session_ticket && - type != key_update) { + type != key_update +#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) + && type != request_connection_id && type != new_connection_id +#endif + ) { ret = HashInput(ssl, input + inIdx, (int)size); } diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index 7984818350..1723ca4769 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -680,6 +680,319 @@ int test_wolfSSL_dtls_set_pending_peer_not_newest(void) return EXPECT_RESULT(); } +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) +/* Set up a DTLS 1.3 connection, optionally negotiating CID, and drain all + * post-handshake messages (session tickets, ACKs) so both buffers are empty. */ +static int test_dtls13_cid_setup(struct test_memio_ctx* test_ctx, + WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s, WOLFSSL** ssl_c, + WOLFSSL** ssl_s, int useCid) +{ + EXPECT_DECLS; + unsigned char client_cid[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + unsigned char server_cid[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + byte readBuf[16]; + int i; + + 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); + + if (useCid) { + ExpectIntEQ(wolfSSL_dtls_cid_use(*ssl_c), 1); + ExpectIntEQ(wolfSSL_dtls_cid_set(*ssl_c, server_cid, + sizeof(server_cid)), 1); + ExpectIntEQ(wolfSSL_dtls_cid_use(*ssl_s), 1); + ExpectIntEQ(wolfSSL_dtls_cid_set(*ssl_s, client_cid, + sizeof(client_cid)), 1); + } + + ExpectIntEQ(test_memio_do_handshake(*ssl_c, *ssl_s, 10, NULL), 0); + + for (i = 0; i < 5 && EXPECT_SUCCESS() && + (test_ctx->c_len > 0 || test_ctx->s_len > 0); i++) { + if (test_ctx->c_len > 0) + ExpectIntEQ(wolfSSL_read(*ssl_c, readBuf, sizeof(readBuf)), -1); + if (test_ctx->s_len > 0) + ExpectIntEQ(wolfSSL_read(*ssl_s, readBuf, sizeof(readBuf)), -1); + } + ExpectIntEQ(test_ctx->c_len, 0); + ExpectIntEQ(test_ctx->s_len, 0); + + return EXPECT_RESULT(); +} + +/* Build a post-handshake handshake message of type hsType with the given body + * and encrypt it with the client's current traffic keys. The message sequence + * number is the one the server expects next. */ +static int test_dtls13_build_post_hs_msg(WOLFSSL* ssl_c, WOLFSSL* ssl_s, + byte hsType, const byte* body, word16 bodyLen, byte* rec, int* recSz) +{ + EXPECT_DECLS; + byte msg[64]; + size_t idx = 0; + + ExpectIntLE(DTLS_HANDSHAKE_HEADER_SZ + bodyLen, sizeof(msg)); + + msg[idx++] = hsType; + c32to24(bodyLen, msg + idx); + idx += 3; + c16toa(ssl_s->keys.dtls_expected_peer_handshake_number, msg + idx); + idx += 2; + /* fragment_offset */ + c32to24(0, msg + idx); + idx += 3; + /* fragment_length */ + c32to24(bodyLen, msg + idx); + idx += 3; + XMEMCPY(msg + idx, body, bodyLen); + idx += bodyLen; + + ExpectIntGT(*recSz = BuildTls13Message(ssl_c, rec, *recSz, msg, (int)idx, + handshake, 0, 0, 0), 0); + + return EXPECT_RESULT(); +} + +/* Inject a crafted post-handshake message into the server, expect it to fail + * with expErr and to alert the client with a fatal alert of type expAlert. */ +static int test_dtls13_post_hs_cid_msg_err(byte hsType, const byte* body, + word16 bodyLen, int useCid, int expErr, int expAlert) +{ + EXPECT_DECLS; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + WOLFSSL_ALERT_HISTORY h; + byte rec[256]; + int recSz = (int)sizeof(rec); + byte readBuf[16]; + + ExpectIntEQ(test_dtls13_cid_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, + &ssl_s, useCid), TEST_SUCCESS); + ExpectIntEQ(test_dtls13_build_post_hs_msg(ssl_c, ssl_s, hsType, body, + bodyLen, rec, &recSz), TEST_SUCCESS); + ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)rec, + recSz), 0); + + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), expErr); + + /* the server must send a fatal alert */ + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + XMEMSET(&h, 0, sizeof(h)); + ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS); + ExpectIntEQ(h.last_rx.level, alert_fatal); + ExpectIntEQ(h.last_rx.code, expAlert); + + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); + + return EXPECT_RESULT(); +} +#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && WOLFSSL_DTLS13 && + * WOLFSSL_DTLS_CID */ + +int test_dtls13_new_connection_id(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + unsigned char server_cid[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + /* one 3-byte CID, usage cid_spare */ + const byte spareBody[] = { 0x00, 0x04, 0x03, 0xaa, 0xbb, 0xcc, 0x01 }; + /* one zero-length CID followed by a 3-byte CID, usage cid_immediate */ + const byte immBody[] = { 0x00, 0x05, 0x00, 0x03, 0x11, 0x22, 0x33, 0x00 }; + const byte newCid[] = { 0x11, 0x22, 0x33 }; + unsigned char cidBuf[DTLS_CID_MAX_SIZE]; + unsigned int cidSz = 0; + byte rec[256]; + int recSz = (int)sizeof(rec); + byte readBuf[16]; + const byte* parsedCid = NULL; + + /* usage cid_spare: the new CID may be discarded, ours must not change */ + ExpectIntEQ(test_dtls13_cid_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, + &ssl_s, 1), TEST_SUCCESS); + ExpectIntEQ(test_dtls13_build_post_hs_msg(ssl_c, ssl_s, new_connection_id, + spareBody, sizeof(spareBody), rec, &recSz), TEST_SUCCESS); + ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)rec, + recSz), 0); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + /* the server ACKed the message */ + ExpectIntGT(test_ctx.c_len, 0); + ExpectIntEQ(wolfSSL_dtls_cid_get_tx_size(ssl_s, &cidSz), 1); + ExpectIntEQ(cidSz, sizeof(server_cid)); + ExpectIntEQ(wolfSSL_dtls_cid_get_tx(ssl_s, cidBuf, sizeof(cidBuf)), 1); + ExpectBufEQ(cidBuf, server_cid, sizeof(server_cid)); + + /* duplicate delivery: the retransmitted record must be dropped */ + ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)rec, + recSz), 0); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + + /* connection must still work in both directions */ + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_write(ssl_c, "test", 5), 5); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 5); + ExpectStrEQ(readBuf, "test"); + ExpectIntEQ(wolfSSL_write(ssl_s, "back", 5), 5); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), 5); + ExpectStrEQ(readBuf, "back"); + + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); + ssl_c = ssl_s = NULL; + ctx_c = ctx_s = NULL; + + /* usage cid_immediate: the first non-empty CID must be used for all + * records sent from now on, including the ACK of this message */ + ExpectIntEQ(test_dtls13_cid_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, + &ssl_s, 1), TEST_SUCCESS); + recSz = (int)sizeof(rec); + ExpectIntEQ(test_dtls13_build_post_hs_msg(ssl_c, ssl_s, new_connection_id, + immBody, sizeof(immBody), rec, &recSz), TEST_SUCCESS); + ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)rec, + recSz), 0); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + cidSz = 0; + ExpectIntEQ(wolfSSL_dtls_cid_get_tx_size(ssl_s, &cidSz), 1); + ExpectIntEQ(cidSz, sizeof(newCid)); + ExpectIntEQ(wolfSSL_dtls_cid_get_tx(ssl_s, cidBuf, sizeof(cidBuf)), 1); + ExpectBufEQ(cidBuf, newCid, sizeof(newCid)); + /* the ACK record already carries the new CID */ + ExpectIntGT(test_ctx.c_len, 0); + ExpectNotNull(parsedCid = wolfSSL_dtls_cid_parse(test_ctx.c_buff, + (unsigned int)test_ctx.c_len, sizeof(newCid))); + if (parsedCid != NULL) + ExpectBufEQ(parsedCid, newCid, sizeof(newCid)); + + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); +#endif + return EXPECT_RESULT(); +} + +int test_dtls13_new_connection_id_not_negotiated(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) + /* one 3-byte CID, usage cid_spare */ + const byte body[] = { 0x00, 0x04, 0x03, 0xaa, 0xbb, 0xcc, 0x01 }; + + /* RFC 9147 Section 9: receiving a CID message when CIDs were not + * negotiated must be answered with an unexpected_message alert */ + ExpectIntEQ(test_dtls13_post_hs_cid_msg_err(new_connection_id, body, + sizeof(body), 0, WC_NO_ERR_TRACE(SANITY_MSG_E), + unexpected_message), TEST_SUCCESS); + ExpectIntEQ(test_dtls13_post_hs_cid_msg_err(request_connection_id, + (const byte*)"\x02", 1, 0, WC_NO_ERR_TRACE(SANITY_MSG_E), + unexpected_message), TEST_SUCCESS); +#endif + return EXPECT_RESULT(); +} + +int test_dtls13_request_connection_id(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + const byte body[] = { 0x02 }; /* num_cids */ + byte rec[256]; + int recSz = (int)sizeof(rec); + byte readBuf[16]; + + /* the request is ACKed but ignored: we never send NewConnectionId */ + ExpectIntEQ(test_dtls13_cid_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, + &ssl_s, 1), TEST_SUCCESS); + ExpectIntEQ(test_dtls13_build_post_hs_msg(ssl_c, ssl_s, + request_connection_id, body, sizeof(body), rec, &recSz), + TEST_SUCCESS); + ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)rec, + recSz), 0); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(test_ctx.c_len, 0); + /* nothing but the ACK reaches the client */ + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ctx.c_len, 0); + ExpectIntEQ(test_ctx.s_len, 0); + + /* connection must still work in both directions */ + ExpectIntEQ(wolfSSL_write(ssl_c, "test", 5), 5); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 5); + ExpectStrEQ(readBuf, "test"); + ExpectIntEQ(wolfSSL_write(ssl_s, "back", 5), 5); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), 5); + ExpectStrEQ(readBuf, "back"); + + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); +#endif + return EXPECT_RESULT(); +} + +int test_dtls13_cid_msg_malformed(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) + /* cids length exceeding the body */ + const byte truncated[] = { 0x00, 0x10, 0x03, 0x11, 0x22, 0x33, 0x01 }; + /* CID length exceeding the cids vector */ + const byte innerOverrun[] = { 0x00, 0x03, 0x05, 0x11, 0x22, 0x01 }; + /* RequestConnectionId must be exactly one byte */ + const byte reqTooLong[] = { 0x02, 0x00 }; + /* invalid usage */ + const byte badUsage[] = { 0x00, 0x04, 0x03, 0x11, 0x22, 0x33, 0x02 }; + /* cid_immediate with no CID to use */ + const byte immEmpty[] = { 0x00, 0x00, 0x00 }; + const byte immZeroLenCid[] = { 0x00, 0x01, 0x00, 0x00 }; + + ExpectIntEQ(test_dtls13_post_hs_cid_msg_err(new_connection_id, truncated, + sizeof(truncated), 1, WC_NO_ERR_TRACE(BUFFER_ERROR), + decode_error), TEST_SUCCESS); + ExpectIntEQ(test_dtls13_post_hs_cid_msg_err(new_connection_id, + innerOverrun, sizeof(innerOverrun), 1, + WC_NO_ERR_TRACE(BUFFER_ERROR), decode_error), TEST_SUCCESS); + ExpectIntEQ(test_dtls13_post_hs_cid_msg_err(request_connection_id, + reqTooLong, sizeof(reqTooLong), 1, WC_NO_ERR_TRACE(BUFFER_ERROR), + decode_error), TEST_SUCCESS); + ExpectIntEQ(test_dtls13_post_hs_cid_msg_err(new_connection_id, badUsage, + sizeof(badUsage), 1, WC_NO_ERR_TRACE(INVALID_PARAMETER), + illegal_parameter), TEST_SUCCESS); + ExpectIntEQ(test_dtls13_post_hs_cid_msg_err(new_connection_id, immEmpty, + sizeof(immEmpty), 1, WC_NO_ERR_TRACE(INVALID_PARAMETER), + illegal_parameter), TEST_SUCCESS); + ExpectIntEQ(test_dtls13_post_hs_cid_msg_err(new_connection_id, + immZeroLenCid, sizeof(immZeroLenCid), 1, + WC_NO_ERR_TRACE(INVALID_PARAMETER), illegal_parameter), + TEST_SUCCESS); +#endif + return EXPECT_RESULT(); +} + int test_dtls_version_checking(void) { diff --git a/tests/api/test_dtls.h b/tests/api/test_dtls.h index 4d9d29a3e9..6ab63ccfe7 100644 --- a/tests/api/test_dtls.h +++ b/tests/api/test_dtls.h @@ -26,6 +26,10 @@ int test_dtls12_basic_connection_id(void); int test_wolfSSL_dtls_cid_parse(void); int test_wolfSSL_dtls_set_pending_peer(void); int test_wolfSSL_dtls_set_pending_peer_not_newest(void); +int test_dtls13_new_connection_id(void); +int test_dtls13_new_connection_id_not_negotiated(void); +int test_dtls13_request_connection_id(void); +int test_dtls13_cid_msg_malformed(void); int test_dtls_version_checking(void); int test_dtls_short_ciphertext(void); int test_dtls12_record_length_mismatch(void); @@ -107,6 +111,10 @@ int test_WOLFSSL_dtls_version_alert(void); TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_cid_parse), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_set_pending_peer), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_set_pending_peer_not_newest),\ + TEST_DECL_GROUP("dtls", test_dtls13_new_connection_id), \ + TEST_DECL_GROUP("dtls", test_dtls13_new_connection_id_not_negotiated), \ + TEST_DECL_GROUP("dtls", test_dtls13_request_connection_id), \ + TEST_DECL_GROUP("dtls", test_dtls13_cid_msg_malformed), \ TEST_DECL_GROUP("dtls", test_dtls_version_checking), \ TEST_DECL_GROUP("dtls", test_dtls_short_ciphertext), \ TEST_DECL_GROUP("dtls", test_dtls12_record_length_mismatch), \ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 90856252b3..3c6135a1e1 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3888,6 +3888,7 @@ WOLFSSL_LOCAL int TLSX_ConnectionID_Parse(WOLFSSL* ssl, const byte* input, WOLFSSL_LOCAL void DtlsCIDOnExtensionsParsed(WOLFSSL* ssl); WOLFSSL_LOCAL byte DtlsCIDCheck(WOLFSSL* ssl, const byte* input, word16 inputSize); +WOLFSSL_LOCAL int DtlsCidReplaceTx(WOLFSSL* ssl, const byte* cid, byte size); WOLFSSL_LOCAL int Dtls13UnifiedHeaderCIDPresent(byte flags); #endif /* WOLFSSL_DTLS_CID */ WOLFSSL_LOCAL byte DtlsGetCidTxSize(WOLFSSL* ssl); @@ -6082,6 +6083,12 @@ typedef struct CIDInfo { ConnectionID* rx; byte negotiated : 1; } CIDInfo; + +/* ConnectionIdUsage of the NewConnectionId message (RFC 9147 Section 9) */ +enum ConnectionIdUsage { + cid_immediate = 0, + cid_spare = 1 +}; #endif /* WOLFSSL_DTLS_CID */ /* The idea is to reuse the context suites object whenever possible to save @@ -6786,6 +6793,8 @@ enum HandShakeType { end_of_early_data = 5, hello_retry_request = 6, encrypted_extensions = 8, + request_connection_id = 9, /* DTLS v1.3 addition (RFC 9147) */ + new_connection_id = 10, /* DTLS v1.3 addition (RFC 9147) */ certificate = 11, server_key_exchange = 12, certificate_request = 13, @@ -7435,6 +7444,12 @@ WOLFSSL_LOCAL int Dtls13HandshakeAddHeader(WOLFSSL* ssl, byte* output, #define EE_MASK (0x3) WOLFSSL_LOCAL int Dtls13FragmentsContinue(WOLFSSL* ssl); WOLFSSL_LOCAL int DoDtls13KeyUpdateAck(WOLFSSL* ssl); +#ifdef WOLFSSL_DTLS_CID +WOLFSSL_LOCAL int DoDtls13RequestConnectionId(WOLFSSL* ssl, const byte* input, + word32* inOutIdx, word32 size); +WOLFSSL_LOCAL int DoDtls13NewConnectionId(WOLFSSL* ssl, const byte* input, + word32* inOutIdx, word32 size); +#endif /* WOLFSSL_DTLS_CID */ WOLFSSL_LOCAL int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize, word32* processedSize); WOLFSSL_LOCAL int Dtls13ReconstructEpochNumber(WOLFSSL* ssl, byte epochBits,