diff --git a/src/dtls.c b/src/dtls.c index 27fe518102..a9ae17daba 100644 --- a/src/dtls.c +++ b/src/dtls.c @@ -1323,6 +1323,16 @@ int TLSX_ConnectionID_Parse(WOLFSSL* ssl, const byte* input, word16 length, if (cidSz + OPAQUE8_LEN > length) return BUFFER_ERROR; +#if DTLS_CID_MAX_SIZE < 255 + /* The peer's CID becomes our TX CID. RFC 9146 allows up to 255 bytes, our + * send buffers are sized for DTLS_CID_MAX_SIZE. */ + if (cidSz > DTLS_CID_MAX_SIZE) { + WOLFSSL_MSG("Peer CID larger than DTLS_CID_MAX_SIZE"); + WOLFSSL_ERROR_VERBOSE(DTLS_CID_ERROR); + return DTLS_CID_ERROR; + } +#endif + info = DtlsCidGetInfo(ssl); if (info == NULL) return BAD_STATE_E; diff --git a/src/dtls13.c b/src/dtls13.c index 32b4e00df0..5d01f5b099 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -803,6 +803,18 @@ int Dtls13RtxAddAck(WOLFSSL* ssl, w64wrapper epoch, w64wrapper seq) return 0; } +/* RFC 9147 Sec 7: only ack a record whose message we processed or buffered. + * Acking a dropped message stops the peer retransmitting it and deadlocks. */ +static void Dtls13RtxAddAckForCurRecord(WOLFSSL* ssl) +{ + /* Stateless processing must not touch the ssl object. */ + if (!ssl->options.dtlsStateful) + return; + + if (Dtls13RtxAddAck(ssl, ssl->keys.curEpoch64, ssl->keys.curSeq) != 0) + WOLFSSL_MSG("can't save ack fragment"); +} + static void Dtls13RtxFlushAcks(WOLFSSL* ssl) { Dtls13RecordNumber *list, *rn; @@ -934,8 +946,9 @@ static void Dtls13SaveOrFlushClientHello(WOLFSSL* ssl) } } +/* implicitAck is set when the flight we send already acks this record. */ static int Dtls13RtxMsgRecvd(WOLFSSL* ssl, enum HandShakeType hs, - word32 fragOffset) + word32 fragOffset, byte* implicitAck) { WOLFSSL_ENTER("Dtls13RtxMsgRecvd"); @@ -965,6 +978,10 @@ static int Dtls13RtxMsgRecvd(WOLFSSL* ssl, enum HandShakeType hs, /* retransmission detected. */ ssl->dtls13Rtx.retransmit = 1; + /* Already processed, so acking is allowed. It stops a peer that + * retransmitted because our earlier ack was lost. */ + Dtls13RtxAddAckForCurRecord(ssl); + /* the other peer may have retransmitted because an ACK for a flight that needs explicit ACK was lost.*/ if (ssl->dtls13Rtx.seenRecords != NULL) @@ -985,6 +1002,7 @@ static int Dtls13RtxMsgRecvd(WOLFSSL* ssl, enum HandShakeType hs, should be rare and simplifies the code. Otherwise, it would be necessary to track which record number contained a CertificateRequest with a particular context id */ + *implicitAck = 1; Dtls13RtxRemoveCurAck(ssl); } @@ -1616,19 +1634,14 @@ int Dtls13ParseUnifiedRecordLayer(WOLFSSL* ssl, const byte* input, int Dtls13RecordRecvd(WOLFSSL* ssl) { - int ret; - if (ssl->curRL.type != handshake) return 0; if (!ssl->options.dtls13SendMoreAcks) ssl->dtls13FastTimeout = 1; - ret = Dtls13RtxAddAck(ssl, ssl->keys.curEpoch64, ssl->keys.curSeq); - if (ret != 0) - WOLFSSL_MSG("can't save ack fragment"); - - return ret; + /* Acking happens in Dtls13RtxAddAckForCurRecord(). */ + return 0; } static void Dtls13RtxMoveToEndOfList(WOLFSSL* ssl, Dtls13RtxRecord** prevNext, @@ -1874,10 +1887,12 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, byte usingAsyncCrypto; word32 messageLength; byte handshakeType; + byte implicitAck; word32 idx; int ret; idx = 0; + implicitAck = 0; ret = GetDtlsHandShakeHeader(ssl, input, &idx, &handshakeType, &messageLength, &fragOff, &fragLength, size); if (ret != 0) @@ -1937,7 +1952,8 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, if (fragOff + fragLength > messageLength) return BUFFER_ERROR; - ret = Dtls13RtxMsgRecvd(ssl, (enum HandShakeType)handshakeType, fragOff); + ret = Dtls13RtxMsgRecvd(ssl, (enum HandShakeType)handshakeType, fragOff, + &implicitAck); if (ret != 0) return ret; @@ -2001,10 +2017,13 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, ssl->keys.dtls_expected_peer_handshake_number || usingAsyncCrypto) { if (ssl->dtls_rx_msg_list_sz < DTLS_POOL_SZ) { - DtlsMsgStore(ssl, (word16)w64GetLow32(ssl->keys.curEpoch64), - ssl->keys.dtls_peer_handshake_number, - input + DTLS_HANDSHAKE_HEADER_SZ, messageLength, handshakeType, - fragOff, fragLength, ssl->heap); + /* Only ack a fragment we really buffered. */ + if (DtlsMsgStore(ssl, (word16)w64GetLow32(ssl->keys.curEpoch64), + ssl->keys.dtls_peer_handshake_number, + input + DTLS_HANDSHAKE_HEADER_SZ, messageLength, + handshakeType, fragOff, fragLength, ssl->heap) == 0) { + Dtls13RtxAddAckForCurRecord(ssl); + } } else { /* DTLS_POOL_SZ outstanding messages is way more than enough for any @@ -2025,6 +2044,9 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, if (ret != 0) return ret; + if (!implicitAck) + Dtls13RtxAddAckForCurRecord(ssl); + Dtls13MsgWasProcessed(ssl, (enum HandShakeType)handshakeType); /* check if we have buffered some message */ diff --git a/src/internal.c b/src/internal.c index 004943c4e7..77972152e7 100644 --- a/src/internal.c +++ b/src/internal.c @@ -10306,6 +10306,30 @@ static WC_INLINE void DtlsSEQIncrement(WOLFSSL* ssl, int order) } } } + +#ifndef WOLFSSL_NO_TLS12 +/* Is the send sequence number at its last legal value? DtlsSEQIncrement() + * would wrap the word16 high half to 0 and reuse sequence numbers. */ +static WC_INLINE int DtlsSEQAtMax(WOLFSSL* ssl, int order) +{ +#ifdef HAVE_SECURE_RENEGOTIATION + order = DtlsCheckOrder(ssl, order); +#endif + + if (order == PREV_ORDER) { + return ssl->keys.dtls_prev_sequence_number_hi == 0xFFFF && + ssl->keys.dtls_prev_sequence_number_lo == 0xFFFFFFFFU; + } + else if (order == PEER_ORDER) { + /* the peer's sequence number is taken from the record */ + return 0; + } + else { + return ssl->keys.dtls_sequence_number_hi == 0xFFFF && + ssl->keys.dtls_sequence_number_lo == 0xFFFFFFFFU; + } +} +#endif /* !WOLFSSL_NO_TLS12 */ #endif /* WOLFSSL_DTLS */ #if defined(WOLFSSL_DTLS) || !defined(WOLFSSL_NO_TLS12) @@ -10717,10 +10741,10 @@ int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch, const byte* data, byte ty } prev->m.m.next = DtlsMsgCreateFragBucket(fragOffset, data, fragSz, heap); - if (prev->m.m.next != NULL) { - msg->bytesReceived += fragSz; - msg->fragBucketListCount++; - } + if (prev->m.m.next == NULL) + return MEMORY_ERROR; + msg->bytesReceived += fragSz; + msg->fragBucketListCount++; } else if (fragOffsetEnd < cur->m.m.offset) { /* Fragment is entirely before cur with a gap */ @@ -10741,6 +10765,7 @@ int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch, const byte* data, byte ty else { /* reset on error */ *prev_next = cur; + return MEMORY_ERROR; } } else { @@ -10754,8 +10779,11 @@ int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch, const byte* data, byte ty /* We can combine the buckets */ *prev_next = DtlsMsgCombineFragBuckets(msg, cur, next, fragOffset, data, fragSz, heap); - if (*prev_next == NULL) /* reset on error */ + if (*prev_next == NULL) { + /* reset on error */ *prev_next = cur; + return MEMORY_ERROR; + } } } } @@ -10777,7 +10805,8 @@ DtlsMsg* DtlsMsgFind(DtlsMsg* head, word16 epoch, word32 seq) } -void DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data, +/* Returns 0 when the fragment was stored, negative when it was dropped. */ +int DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data, word32 dataSz, byte type, word32 fragOffset, word32 fragSz, void* heap) { /* See if seq exists in the list. If it isn't in the list, make @@ -10799,6 +10828,7 @@ void DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data, DtlsMsg* head = ssl->dtls_rx_msg_list; byte encrypted = ssl->keys.decryptedCur == 1; + int ret = 0; WOLFSSL_ENTER("DtlsMsgStore"); if (head != NULL) { @@ -10807,11 +10837,13 @@ void DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data, cur = DtlsMsgNew(dataSz, 0, heap); if (cur == NULL) { WOLFSSL_MSG("DtlsMsgNew allocation failed"); - ssl->error = MEMORY_E; + ret = MEMORY_E; + ssl->error = ret; } else { - if (DtlsMsgSet(cur, seq, epoch, data, type, - fragOffset, fragSz, heap, dataSz, encrypted) < 0) { + ret = DtlsMsgSet(cur, seq, epoch, data, type, + fragOffset, fragSz, heap, dataSz, encrypted); + if (ret < 0) { DtlsMsgDelete(cur, heap); } else { @@ -10822,7 +10854,7 @@ void DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data, } else { /* If this fails, the data is just dropped. */ - DtlsMsgSet(cur, seq, epoch, data, type, fragOffset, + ret = DtlsMsgSet(cur, seq, epoch, data, type, fragOffset, fragSz, heap, dataSz, encrypted); } } @@ -10830,10 +10862,11 @@ void DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data, head = DtlsMsgNew(dataSz, 0, heap); if (head == NULL) { WOLFSSL_MSG("DtlsMsgNew allocation failed"); - ssl->error = MEMORY_E; + ret = MEMORY_E; + ssl->error = ret; } - else if (DtlsMsgSet(head, seq, epoch, data, type, fragOffset, - fragSz, heap, dataSz, encrypted) < 0) { + else if ((ret = DtlsMsgSet(head, seq, epoch, data, type, fragOffset, + fragSz, heap, dataSz, encrypted)) < 0) { DtlsMsgDelete(head, heap); head = NULL; } @@ -10843,6 +10876,8 @@ void DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data, } ssl->dtls_rx_msg_list = head; + + return ret; } @@ -13085,7 +13120,14 @@ static int GetDtlsRecordHeader(WOLFSSL* ssl, word32* inOutIdx, } #ifdef WOLFSSL_DTLS_CID - if (rh->type == dtls12_cid && (cidSz = DtlsGetCidRxSize(ssl)) == 0) + if (rh->type == dtls12_cid) { + if ((cidSz = DtlsGetCidRxSize(ssl)) == 0) + return DTLS_CID_ERROR; + } + /* RFC 9146 Sec 4: with a receive CID every protected record must use the + * dtls12_cid type. The MAC covers the inner content type, not the wire + * type, so a record re-framed as application_data still authenticates. */ + else if (ssl->keys.curEpoch != 0 && DtlsGetCidRxSize(ssl) != 0) return DTLS_CID_ERROR; #endif @@ -20075,6 +20117,17 @@ int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, *inOutIdx = expectedIdx; return SendAlert(ssl, alert_warning, no_renegotiation); } +#ifdef WOLFSSL_DTLS + /* RFC 6347 Sec 4.1: the epoch must not wrap. Both directions are + * checked because a second ClientHello can arrive between the peer's + * CCS and our own. */ + if (ssl->options.dtls && (ssl->keys.dtls_epoch == 0xFFFF || + ssl->keys.peerSeq[0].nextEpoch == 0xFFFF)) { + WOLFSSL_MSG("Refusing renegotiation. Epoch would wrap"); + *inOutIdx = expectedIdx; + return SendAlert(ssl, alert_warning, no_renegotiation); + } +#endif ret = ResetHandshakeStateForReneg(ssl); if (ret != 0) return ret; @@ -26327,7 +26380,7 @@ int BuildMessage(WOLFSSL* ssl, byte* output, int outSz, const byte* input, * increments, so refuse at hi == lo == 0xFFFFFFFF (2^64-1): that last legal * value is deliberately sacrificed to avoid wrapping to 0 and reusing * sequence number 0. The caller must renegotiate or close. DTLS sequence - * numbers are epoch-scoped and handled elsewhere. */ + * numbers are epoch-scoped and checked just below. */ if (!sizeOnly && !ssl->options.dtls && ssl->keys.sequence_number_hi == 0xFFFFFFFFU && ssl->keys.sequence_number_lo == 0xFFFFFFFFU) { @@ -26336,6 +26389,17 @@ int BuildMessage(WOLFSSL* ssl, byte* output, int outSz, const byte* input, return SEQUENCE_NUMBER_E; } +#ifdef WOLFSSL_DTLS + /* RFC 6347 Sec 4.1: don't wrap the sequence number. Only protected records + * reach here, so the epoch 0 counter SendHelloVerifyRequest() copies from + * the peer is unaffected. */ + if (!sizeOnly && ssl->options.dtls && DtlsSEQAtMax(ssl, epochOrder)) { + WOLFSSL_MSG("DTLS write sequence number would wrap"); + WOLFSSL_ERROR_VERBOSE(SEQUENCE_NUMBER_E); + return SEQUENCE_NUMBER_E; + } +#endif + #ifdef WOLFSSL_ASYNC_CRYPT ret = WC_NO_PENDING_E; if (asyncOkay) { diff --git a/src/ssl_api_ext.c b/src/ssl_api_ext.c index 2be0da0375..ffadca0721 100644 --- a/src/ssl_api_ext.c +++ b/src/ssl_api_ext.c @@ -1008,7 +1008,8 @@ static int _Rehandshake(WOLFSSL* ssl) ret = SECURE_RENEGOTIATION_E; } #ifdef WOLFSSL_DTLS - else if ((ssl->options.dtls) && (ssl->keys.dtls_epoch == 0xFFFF)) { + else if ((ssl->options.dtls) && ((ssl->keys.dtls_epoch == 0xFFFF) || + (ssl->keys.peerSeq[0].nextEpoch == 0xFFFF))) { WOLFSSL_MSG("Secure Renegotiation not allowed. Epoch would wrap"); ret = SECURE_RENEGOTIATION_E; } diff --git a/src/tls13.c b/src/tls13.c index 5f3a749b04..b4138d677f 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -12900,6 +12900,12 @@ static int SendTls13Finished(WOLFSSL* ssl) #endif /* WOLFSSL_DTLS13 */ outputSz = WC_MAX_DIGEST_SIZE + DTLS_HANDSHAKE_HEADER_SZ + MAX_MSG_EXTRA; +#ifdef WOLFSSL_DTLS13 + /* MAX_MSG_EXTRA only budgets RECORD_HEADER_SZ. The DTLS 1.3 unified header + * is longer and grows with the TX CID. */ + if (isDtls) + outputSz += Dtls13GetRlHeaderLength(ssl, 1); +#endif /* WOLFSSL_DTLS13 */ /* Check buffers are big enough and grow if needed. */ if ((ret = CheckAvailableSize(ssl, outputSz)) != 0) return ret; @@ -13167,7 +13173,9 @@ int SendTls13KeyUpdate(WOLFSSL* ssl) } } - outputSz = OPAQUE8_LEN + MAX_MSG_EXTRA; + /* i already carries the real record and handshake header lengths. + * MAX_MSG_EXTRA only budgets RECORD_HEADER_SZ. */ + outputSz = (int)i + OPAQUE8_LEN + MAX_MSG_EXTRA; /* Check buffers are big enough and grow if needed. */ if ((ret = CheckAvailableSize(ssl, outputSz)) != 0) return ret; diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index fa70f7126d..93253eab82 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -737,10 +737,14 @@ 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]; + /* largest body a caller builds: a cid_immediate with an oversized CID */ + byte msg[DTLS_HANDSHAKE_HEADER_SZ + 2 + 1 + (DTLS_CID_MAX_SIZE + 1) + 1]; size_t idx = 0; - ExpectIntLE(DTLS_HANDSHAKE_HEADER_SZ + bodyLen, sizeof(msg)); + if (DTLS_HANDSHAKE_HEADER_SZ + bodyLen > (int)sizeof(msg)) { + ExpectFail(); + return EXPECT_RESULT(); + } msg[idx++] = hsType; c32to24(bodyLen, msg + idx); @@ -996,6 +1000,53 @@ int test_dtls13_request_connection_id(void) return EXPECT_RESULT(); } +/* Parse a connection_id extension of the given CID length as a ServerHello. */ +#if defined(WOLFSSL_DTLS_CID) && !defined(NO_WOLFSSL_CLIENT) && \ + DTLS_CID_MAX_SIZE < 255 +static int test_dtls_cid_negotiate_sz(byte cidSz, int expected) +{ + EXPECT_DECLS; + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + byte ext[4 + 1 + 255]; + word16 extSz = 0; + word16 i; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntEQ(wolfSSL_dtls_cid_use(ssl), 1); + + c16toa((word16)TLSX_CONNECTION_ID, ext + extSz); + extSz += OPAQUE16_LEN; + c16toa((word16)(cidSz + 1), ext + extSz); + extSz += OPAQUE16_LEN; + ext[extSz++] = cidSz; + for (i = 0; i < cidSz; i++) + ext[extSz++] = 0x5A; + + ExpectIntEQ(TLSX_Parse(ssl, ext, extSz, server_hello, NULL), expected); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); + + return EXPECT_RESULT(); +} +#endif + +int test_dtls_cid_negotiate_oversize(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_DTLS_CID) && !defined(NO_WOLFSSL_CLIENT) && \ + DTLS_CID_MAX_SIZE < 255 + /* send paths size their buffers for at most DTLS_CID_MAX_SIZE */ + ExpectIntEQ(test_dtls_cid_negotiate_sz(DTLS_CID_MAX_SIZE + 1, + WC_NO_ERR_TRACE(DTLS_CID_ERROR)), TEST_SUCCESS); + ExpectIntEQ(test_dtls_cid_negotiate_sz(DTLS_CID_MAX_SIZE, 0), + TEST_SUCCESS); +#endif + return EXPECT_RESULT(); +} + int test_dtls13_cid_msg_malformed(void) { EXPECT_DECLS; @@ -6110,6 +6161,184 @@ int test_dtls_old_seq_number(void) 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. */ +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS_CID) \ + && !defined(WOLFSSL_NO_TLS12) +static int test_dtls12_cid_type_swap_to(byte type) +{ + 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 client_cid[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + const char msg[] = "hello"; + char readBuf[32]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_c), 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); + + test_memio_clear_buffer(&test_ctx, 0); + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)), (int)sizeof(msg)); + ExpectIntEQ(test_ctx.s_buff[0], dtls12_cid); + ExpectIntGT(test_ctx.s_len, (int)(DTLS12_CID_OFFSET + sizeof(client_cid))); + + /* drop the CID and forge the wire type */ + if (EXPECT_SUCCESS()) { + int cidSz = (int)sizeof(client_cid); + test_ctx.s_buff[0] = type; + XMEMMOVE(test_ctx.s_buff + DTLS12_CID_OFFSET, + test_ctx.s_buff + DTLS12_CID_OFFSET + cidSz, + (size_t)(test_ctx.s_len - DTLS12_CID_OFFSET - cidSz)); + test_ctx.s_len -= cidSz; + test_ctx.s_msg_sizes[0] -= cidSz; + } + + /* the record must be dropped, not delivered or acted on */ + XMEMSET(readBuf, 0, sizeof(readBuf)); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + + return EXPECT_RESULT(); +} +#endif + +int test_dtls12_cid_record_type_swap(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS_CID) \ + && !defined(WOLFSSL_NO_TLS12) + ExpectIntEQ(test_dtls12_cid_type_swap_to(application_data), TEST_SUCCESS); + ExpectIntEQ(test_dtls12_cid_type_swap_to(alert), TEST_SUCCESS); +#endif + return EXPECT_RESULT(); +} + +/* Renegotiate at the given server epoch and report whether it was allowed. */ +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SECURE_RENEGOTIATION) && \ + defined(HAVE_SERVER_RENEGOTIATION_INFO) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(NO_WOLFSSL_CLIENT) +static int test_dtls12_scr_epoch_wrap_at(word16 epoch, int expectAccept) +{ + 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; + char readBuf[16]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Only the server's send epoch. Moving the receive epochs would drop the + * ClientHello, and moving the client's would trip _Rehandshake(). */ + if (EXPECT_SUCCESS() && ssl_s != NULL) + ssl_s->keys.dtls_epoch = epoch; + + ExpectIntEQ(wolfSSL_Rehandshake(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + + XMEMSET(readBuf, 0, sizeof(readBuf)); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1); + + ExpectIntEQ(wolfSSL_SSL_renegotiate_pending(ssl_s), + expectAccept ? 1 : 0); + if (!expectAccept) { + /* refused with a warning alert, epoch untouched */ + XMEMSET(&h, 0, sizeof(h)); + ExpectIntEQ(wolfSSL_get_alert_history(ssl_s, &h), WOLFSSL_SUCCESS); + ExpectIntEQ(h.last_tx.level, alert_warning); + ExpectIntEQ(h.last_tx.code, no_renegotiation); + if (EXPECT_SUCCESS() && ssl_s != NULL) + ExpectIntEQ((int)ssl_s->keys.dtls_epoch, (int)epoch); + } + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + + return EXPECT_RESULT(); +} +#endif + +int test_dtls12_scr_epoch_wrap(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SECURE_RENEGOTIATION) && \ + defined(HAVE_SERVER_RENEGOTIATION_INFO) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(NO_WOLFSSL_CLIENT) + ExpectIntEQ(test_dtls12_scr_epoch_wrap_at(0xFFFF, 0), TEST_SUCCESS); + /* one below the boundary still renegotiates */ + ExpectIntEQ(test_dtls12_scr_epoch_wrap_at(0xFFFE, 1), TEST_SUCCESS); +#endif + return EXPECT_RESULT(); +} + +int test_dtls12_seq_num_wrap(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + const char msg[] = "wrap"; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* one below the last legal 48 bit sequence number */ + if (EXPECT_SUCCESS() && ssl_c != NULL) { + ssl_c->keys.dtls_sequence_number_hi = 0xFFFF; + ssl_c->keys.dtls_sequence_number_lo = 0xFFFFFFFEU; + } + + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)), (int)sizeof(msg)); + if (EXPECT_SUCCESS() && ssl_c != NULL) { + ExpectIntEQ((int)ssl_c->keys.dtls_sequence_number_hi, 0xFFFF); + ExpectIntEQ(ssl_c->keys.dtls_sequence_number_lo, 0xFFFFFFFFU); + } + + /* the next record would wrap the counter back to 0 */ + test_memio_clear_buffer(&test_ctx, 0); + ExpectIntLT(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)), 0); + ExpectIntEQ(test_ctx.s_len, 0); + if (EXPECT_SUCCESS() && ssl_c != NULL) { + ExpectIntEQ((int)ssl_c->keys.dtls_sequence_number_hi, 0xFFFF); + ExpectIntEQ(ssl_c->keys.dtls_sequence_number_lo, 0xFFFFFFFFU); + } + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + /*-- dtls12_missing_finished (api.c lines 32007,32068) ---*/ int test_dtls12_missing_finished(void) { diff --git a/tests/api/test_dtls.h b/tests/api/test_dtls.h index 37d8a8a2a4..6a157245b9 100644 --- a/tests/api/test_dtls.h +++ b/tests/api/test_dtls.h @@ -30,6 +30,7 @@ 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_cid_negotiate_oversize(void); int test_dtls_version_checking(void); int test_dtls_drop_invalid_record_during_handshake(void); int test_dtls_short_ciphertext(void); @@ -87,6 +88,9 @@ int test_dtls_client_hello_timeout(void); int test_dtls_dropped_ccs(void); int test_dtls_seq_num_downgrade(void); int test_dtls_old_seq_number(void); +int test_dtls12_seq_num_wrap(void); +int test_dtls12_scr_epoch_wrap(void); +int test_dtls12_cid_record_type_swap(void); int test_dtls12_missing_finished(void); int test_wolfSSL_dtls_export(void); int test_wolfSSL_dtls_export_peers(void); @@ -118,6 +122,7 @@ int test_WOLFSSL_dtls_version_alert(void); 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_cid_negotiate_oversize), \ TEST_DECL_GROUP("dtls", test_dtls_version_checking), \ TEST_DECL_GROUP("dtls", \ test_dtls_drop_invalid_record_during_handshake), \ @@ -172,6 +177,9 @@ int test_WOLFSSL_dtls_version_alert(void); TEST_DECL_GROUP("dtls", test_dtls_dropped_ccs), \ TEST_DECL_GROUP("dtls", test_dtls_seq_num_downgrade), \ TEST_DECL_GROUP("dtls", test_dtls_old_seq_number), \ + TEST_DECL_GROUP("dtls", test_dtls12_seq_num_wrap), \ + TEST_DECL_GROUP("dtls", test_dtls12_scr_epoch_wrap), \ + TEST_DECL_GROUP("dtls", test_dtls12_cid_record_type_swap), \ TEST_DECL_GROUP("dtls", test_dtls12_missing_finished), \ TEST_DECL_GROUP("dtls", test_dtls12_export_import_etm), \ TEST_DECL_GROUP("dtls", test_dtls13_min_rtx_interval), \ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index dd20de7d6c..410a56be69 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -7431,7 +7431,7 @@ WOLFSSL_LOCAL word32 MacSize(const WOLFSSL* ssl); word32 totalLen, byte encrypted); WOLFSSL_TEST_VIS DtlsMsg* DtlsMsgFind(DtlsMsg* head, word16 epoch, word32 seq); - WOLFSSL_TEST_VIS void DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, + WOLFSSL_TEST_VIS int DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data, word32 dataSz, byte type, word32 fragOffset, word32 fragSz, void* heap);