Merge pull request #10626 from mattia-moffa/20260605-dtls-cid-check-newest

DTLS bugfixes
This commit is contained in:
Daniel Pouzzner
2026-07-03 01:18:38 -05:00
committed by GitHub
8 changed files with 768 additions and 10 deletions
+2 -2
View File
@@ -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()
+26
View File
@@ -1407,6 +1407,32 @@ 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;
if (size > DTLS_CID_MAX_SIZE)
return LENGTH_ERROR;
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;
ssl->recordSzOverhead = 0;
return 0;
}
int wolfSSL_dtls_cid_get_rx_size(WOLFSSL* ssl, unsigned int* size)
{
return DtlsCidGetSize(ssl, size, 1);
+103
View File
@@ -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;
}
@@ -1807,6 +1814,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);
@@ -2916,6 +2925,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 CID we can use. */
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 && cidLen <= DTLS_CID_MAX_SIZE) {
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)
{
+59 -6
View File
@@ -11894,6 +11894,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);
@@ -11954,6 +11956,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:
@@ -12002,6 +12006,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:
@@ -12039,6 +12045,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:
@@ -12075,6 +12083,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:
@@ -23002,14 +23012,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) {
@@ -23027,9 +23069,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);
}
@@ -23418,7 +23462,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);
@@ -23736,6 +23780,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)) {
@@ -23754,6 +23801,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);
@@ -23764,12 +23814,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 */
+70 -2
View File
@@ -13709,6 +13709,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);
@@ -13771,7 +13819,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);
@@ -13954,6 +14006,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:
@@ -13986,7 +14050,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);
}
+483
View File
@@ -562,6 +562,489 @@ 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();
}
#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, const byte* serverCid, word32 serverCidSz)
{
EXPECT_DECLS;
unsigned char client_cid[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
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 (serverCid != NULL) {
ExpectIntEQ(wolfSSL_dtls_cid_use(*ssl_c), 1);
ExpectIntEQ(wolfSSL_dtls_cid_set(*ssl_c, (unsigned char*)serverCid,
serverCidSz), 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;
const byte serverCid[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
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 ? serverCid : NULL,
useCid ? (word32)sizeof(serverCid) : 0), 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 };
/* one 3-byte CID, usage cid_spare */
const byte spareBody[] = { 0x00, 0x04, 0x03, 0xaa, 0xbb, 0xcc, 0x01 };
/* NewConnectionId(cid_immediate) carrying a CID longer than the negotiated
* one, so a stale AEAD record-size cache would under-allocate. This ensures
* the cache is cleared when setting the CID.
* The cids list holds a leading empty CID (must be skipped) followed by the
* new CID. */
byte newCid[8];
byte immBody[2 + 1 + 1 + sizeof(newCid) + 1];
word16 immBodyLen = 0;
unsigned char cidBuf[64];
unsigned int cidSz = 0;
word16 i;
int grsOld = 0, grsNew = 0;
byte rec[256];
int recSz = (int)sizeof(rec);
byte readBuf[16];
const byte* parsedCid = NULL;
for (i = 0; i < (word16)sizeof(newCid); i++)
newCid[i] = (byte)(0xA0 + i);
immBody[immBodyLen++] = 0x00; /* cidsLen hi */
immBody[immBodyLen++] = (byte)(1 + 1 + sizeof(newCid)); /* cidsLen lo */
immBody[immBodyLen++] = 0x00; /* leading empty CID */
immBody[immBodyLen++] = (byte)sizeof(newCid); /* CID length */
XMEMCPY(immBody + immBodyLen, newCid, sizeof(newCid));
immBodyLen += (word16)sizeof(newCid);
immBody[immBodyLen++] = 0x00; /* usage cid_immediate */
/* 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, server_cid, sizeof(server_cid)), 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, server_cid, sizeof(server_cid)), TEST_SUCCESS);
/* Exchange application data first so the server's traffic-epoch keys are
* fully installed. Otherwise the injected NewConnectionId would be the
* first epoch-3 record the server decrypts, which installs keys and
* incidentally clears the record-size cache. */
ExpectIntEQ(wolfSSL_write(ssl_c, "hi", 3), 3);
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 3);
/* prime the record-size overhead cache while the negotiated CID is
* still in use */
ExpectIntGT(grsOld = wolfssl_local_GetRecordSize(ssl_s, 200, 1), 0);
recSz = (int)sizeof(rec);
ExpectIntEQ(test_dtls13_build_post_hs_msg(ssl_c, ssl_s, new_connection_id,
immBody, immBodyLen, 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));
/* the cached record-size overhead must now reflect the longer CID */
ExpectIntGT(grsNew = wolfssl_local_GetRecordSize(ssl_s, 200, 1), 0);
ExpectIntEQ(grsNew - grsOld,
(int)sizeof(newCid) - (int)sizeof(server_cid));
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 */
const byte serverCid[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
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, serverCid, sizeof(serverCid)), 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);
#if DTLS_CID_MAX_SIZE < 255
/* cid_immediate with a single CID larger than we support: it is skipped,
* leaving no usable CID */
{
byte oversize[2 + 1 + (DTLS_CID_MAX_SIZE + 1) + 1];
word16 oversizeLen = 0;
word16 k;
oversize[oversizeLen++] = 0x00; /* len hi */
oversize[oversizeLen++] = (byte)(1 + DTLS_CID_MAX_SIZE + 1); /* len lo */
oversize[oversizeLen++] = (byte)(DTLS_CID_MAX_SIZE + 1); /* CID len */
for (k = 0; k < DTLS_CID_MAX_SIZE + 1; k++)
oversize[oversizeLen++] = 0x55;
oversize[oversizeLen++] = 0x00; /* immediate */
ExpectIntEQ(test_dtls13_post_hs_cid_msg_err(new_connection_id,
oversize, oversizeLen, 1, WC_NO_ERR_TRACE(INVALID_PARAMETER),
illegal_parameter), TEST_SUCCESS);
}
#endif /* DTLS_CID_MAX_SIZE < 255 */
#endif
return EXPECT_RESULT();
}
int test_dtls_version_checking(void)
{
+10
View File
@@ -25,6 +25,11 @@
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);
@@ -105,6 +110,11 @@ 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_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), \
+15
View File
@@ -3894,6 +3894,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);
@@ -6087,6 +6088,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
@@ -6800,6 +6807,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,
@@ -7448,6 +7457,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,