diff --git a/src/internal.c b/src/internal.c index 203629a96..4a90c7ea4 100644 --- a/src/internal.c +++ b/src/internal.c @@ -18818,6 +18818,9 @@ static int checkDTLS13AEADFailLimit(byte bulk_cipher_algorithm, } break; #endif + case wolfssl_cipher_null: + /* No encryption being done. The MAC failed must have failed. */ + return 0; default: WOLFSSL_MSG("Unrecognized ciphersuite for AEAD limit check"); return AEAD_LIMIT_FAIL; @@ -22031,6 +22034,66 @@ static int ModifyForMTU(WOLFSSL* ssl, int buffSz, int outputSz, int mtuSz) } #endif /* WOLFSSL_DTLS */ +#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) +/* + * Enforce limits specified in + * https://www.rfc-editor.org/rfc/rfc8446#section-5.5 + */ +static int CheckTLS13AEADSendLimit(WOLFSSL* ssl) +{ + w64wrapper seq; + w64wrapper limit; + + switch (ssl->specs.bulk_cipher_algorithm) { +#ifdef BUILD_AESGCM + case wolfssl_aes_gcm: + /* Limit is 2^24.5 */ + limit = AEAD_AES_LIMIT; + break; +#endif +#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) + case wolfssl_chacha: + /* For ChaCha20/Poly1305, the record sequence number would wrap + * before the safety limit is reached. */ + return 0; +#endif +#ifdef HAVE_AESCCM + case wolfssl_aes_ccm: + /* Use the limits calculated in the DTLS 1.3 spec + * https://www.rfc-editor.org/rfc/rfc9147.html#name-analysis-of-limits-on-ccm-u */ +#ifdef WOLFSSL_DTLS13 + if (ssl->options.dtls) + limit = DTLS_AEAD_AES_CCM_LIMIT; /* Limit is 2^23 */ + else +#endif + limit = AEAD_AES_LIMIT; /* Limit is 2^24.5 */ + break; +#endif + case wolfssl_cipher_null: + /* No encryption being done */ + return 0; + default: + WOLFSSL_MSG("Unrecognized ciphersuite for AEAD limit check"); + return BAD_STATE_E; + + } +#ifdef WOLFSSL_DTLS13 + if (ssl->options.dtls) { + seq = ssl->dtls13EncryptEpoch->nextSeqNumber; + } + else +#endif + { + seq = w64From32(ssl->keys.sequence_number_hi, + ssl->keys.sequence_number_lo); + } + + if (w64GTE(seq, limit)) + return Tls13UpdateKeys(ssl); /* Need to generate new keys */ + + return 0; +} +#endif /* WOLFSSL_TLS13 && !WOLFSSL_TLS13_IGNORE_AEAD_LIMITS */ int SendData(WOLFSSL* ssl, const void* data, int sz) { @@ -22169,6 +22232,16 @@ int SendData(WOLFSSL* ssl, const void* data, int sz) } #endif /* WOLFSSL_DTLS13 */ +#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) + if (IsAtLeastTLSv1_3(ssl->version)) { + ret = CheckTLS13AEADSendLimit(ssl); + if (ret != 0) { + ssl->error = ret; + return WOLFSSL_FATAL_ERROR; + } + } +#endif + #ifdef WOLFSSL_DTLS if (ssl->options.dtls) { buffSz = wolfSSL_GetMaxFragSize(ssl, sz - sent); diff --git a/src/ssl.c b/src/ssl.c index 17b732258..27c988cb0 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -1534,7 +1534,7 @@ int wolfSSL_dtls_get_drop_stats(WOLFSSL* ssl, else { ret = WOLFSSL_SUCCESS; if (macDropCount != NULL) - *macDropCount = ssl->macDropCount; + *macDropCount = w64GetLow32(ssl->macDropCount); if (replayDropCount != NULL) *replayDropCount = ssl->replayDropCount; } diff --git a/src/tls13.c b/src/tls13.c index 00708b934..a55ec2334 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -2103,7 +2103,7 @@ static WC_INLINE void WriteSEQTls13(WOLFSSL* ssl, int verifyOrder, byte* out) Dtls13GetSeq(ssl, verifyOrder, seq, 1); #endif /* WOLFSSL_DTLS13 */ } - else if (verifyOrder) { + else if (verifyOrder == PEER_ORDER) { seq[0] = ssl->keys.peer_sequence_number_hi; seq[1] = ssl->keys.peer_sequence_number_lo++; /* handle rollover */ diff --git a/tests/api.c b/tests/api.c index 625a96ec9..8f1776460 100644 --- a/tests/api.c +++ b/tests/api.c @@ -55308,6 +55308,7 @@ static int test_wolfSSL_dtls_fragments(void) { #ifdef WOLFSSL_DTLS13 static byte test_AEAD_fail_decryption = 0; +static byte test_AEAD_seq_num = 0; static byte test_AEAD_done = 0; static int test_AEAD_cbiorecv(WOLFSSL *ssl, char *buf, int sz, void *ctx) @@ -55325,6 +55326,45 @@ static int test_AEAD_cbiorecv(WOLFSSL *ssl, char *buf, int sz, void *ctx) return ret; } +static void test_AEAD_get_limits(WOLFSSL* ssl, w64wrapper* hardLimit, + w64wrapper* keyUpdateLimit, w64wrapper* sendLimit) +{ + if (sendLimit) + w64Zero(sendLimit); + switch (ssl->specs.bulk_cipher_algorithm) { + case wolfssl_aes_gcm: + if (sendLimit) + *sendLimit = AEAD_AES_LIMIT; + FALL_THROUGH; + case wolfssl_chacha: + if (hardLimit) + *hardLimit = DTLS_AEAD_AES_GCM_CHACHA_FAIL_LIMIT; + if (keyUpdateLimit) + *keyUpdateLimit = DTLS_AEAD_AES_GCM_CHACHA_FAIL_KU_LIMIT; + break; + case wolfssl_aes_ccm: + if (sendLimit) + *sendLimit = DTLS_AEAD_AES_CCM_LIMIT; + if (ssl->specs.aead_mac_size == AES_CCM_8_AUTH_SZ) { + if (hardLimit) + *hardLimit = DTLS_AEAD_AES_CCM_8_FAIL_LIMIT; + if (keyUpdateLimit) + *keyUpdateLimit = DTLS_AEAD_AES_CCM_8_FAIL_KU_LIMIT; + } + else { + if (hardLimit) + *hardLimit = DTLS_AEAD_AES_CCM_FAIL_LIMIT; + if (keyUpdateLimit) + *keyUpdateLimit = DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT; + } + break; + default: + fprintf(stderr, "Unrecognized bulk cipher"); + AssertFalse(1); + break; + } +} + static void test_AEAD_limit_client(WOLFSSL* ssl) { int ret; @@ -55334,28 +55374,9 @@ static void test_AEAD_limit_client(WOLFSSL* ssl) w64wrapper hardLimit; w64wrapper keyUpdateLimit; w64wrapper counter; + w64wrapper sendLimit; - switch (ssl->specs.bulk_cipher_algorithm) { - case wolfssl_aes_gcm: - case wolfssl_chacha: - hardLimit = DTLS_AEAD_AES_GCM_CHACHA_FAIL_LIMIT; - keyUpdateLimit = DTLS_AEAD_AES_GCM_CHACHA_FAIL_KU_LIMIT; - break; - case wolfssl_aes_ccm: - if (ssl->specs.aead_mac_size == AES_CCM_8_AUTH_SZ) { - hardLimit = DTLS_AEAD_AES_CCM_8_FAIL_LIMIT; - keyUpdateLimit = DTLS_AEAD_AES_CCM_8_FAIL_KU_LIMIT; - } - else { - hardLimit = DTLS_AEAD_AES_CCM_FAIL_LIMIT; - keyUpdateLimit = DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT; - } - break; - default: - fprintf(stderr, "Unrecognized bulk cipher"); - AssertFalse(1); - break; - } + test_AEAD_get_limits(ssl, &hardLimit, &keyUpdateLimit, &sendLimit); w64Zero(&counter); AssertTrue(w64Equal(ssl->macDropCount, counter)); @@ -55390,6 +55411,30 @@ static void test_AEAD_limit_client(WOLFSSL* ssl) w64Zero(&counter); AssertTrue(w64Equal(ssl->macDropCount, counter)); + if (!w64IsZero(sendLimit)) { + /* Test the sending limit for AEAD ciphers */ + didReKey = 0; + AssertNotNull(ssl->dtls13EncryptEpoch); + ssl->dtls13EncryptEpoch->nextSeqNumber = sendLimit; + test_AEAD_seq_num = 1; + ret = wolfSSL_write(ssl, msgBuf, sizeof(msgBuf)); + AssertIntGT(ret, 0); + /* 100 read calls should be enough to complete the key update */ + for (i = 0; i < 100; i++) { + /* Key update should be sent and negotiated */ + ret = wolfSSL_read(ssl, msgBuf, sizeof(msgBuf)); + AssertIntGT(ret, 0); + /* Epoch after another key update is 5 */ + if (w64Equal(ssl->dtls13Epoch, w64From32(0, 5))) { + didReKey = 1; + break; + } + } + AssertTrue(didReKey); + w64Zero(&counter); + AssertTrue(w64Equal(ssl->macDropCount, counter)); + } + test_AEAD_fail_decryption = 2; ssl->macDropCount = hardLimit; w64Decrement(&ssl->macDropCount); @@ -55401,16 +55446,27 @@ static void test_AEAD_limit_client(WOLFSSL* ssl) test_AEAD_done = 1; } +int counter = 0; static void test_AEAD_limit_server(WOLFSSL* ssl) { char msgBuf[] = "Sending data"; int ret = WOLFSSL_SUCCESS; + w64wrapper sendLimit; SOCKET_T fd = wolfSSL_get_fd(ssl); struct timespec delay; XMEMSET(&delay, 0, sizeof(delay)); - delay.tv_nsec = 50000000; /* wait 0.05 seconds */ + delay.tv_nsec = 100000000; /* wait 0.1 seconds */ tcp_set_nonblocking(&fd); /* So that read doesn't block */ + test_AEAD_get_limits(ssl, NULL, NULL, &sendLimit); while (!test_AEAD_done && ret > 0) { + counter++; + if (test_AEAD_seq_num) { + /* We need to update the seq number so that we can understand the + * peer. Otherwise we will incorrectly interpret the seq number. */ + AssertNotNull(ssl->dtls13DecryptEpoch); + ssl->dtls13DecryptEpoch->nextPeerSeqNumber = sendLimit; + test_AEAD_seq_num = 1; + } (void)wolfSSL_read(ssl, msgBuf, sizeof(msgBuf)); ret = wolfSSL_write(ssl, msgBuf, sizeof(msgBuf)); nanosleep(&delay, NULL); diff --git a/wolfssl/internal.h b/wolfssl/internal.h index e2585c663..8c77b9286 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1268,6 +1268,14 @@ enum { #endif +/* Limit is 2^24.5 + * https://www.rfc-editor.org/rfc/rfc8446#section-5.5 + * Without the fraction is 23726566 (0x016A09E6) */ +#define AEAD_AES_LIMIT w64From32(0x016A, 0x09E6) +/* Limit is 2^23 + * https://www.rfc-editor.org/rfc/rfc9147.html#name-integrity-limits */ +#define DTLS_AEAD_AES_CCM_LIMIT w64From32(0, 1 << 22) + /* Limit is 2^36 * https://www.rfc-editor.org/rfc/rfc9147.html#name-aead-limits */ #define DTLS_AEAD_AES_GCM_CHACHA_FAIL_LIMIT w64From32(1 << 3, 0) diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 5e959e36e..e41848c90 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -2766,8 +2766,9 @@ extern void uITRON4_free(void *p) ; #define NO_SESSION_CACHE_REF #endif -/* DTLS v1.3 requires 64-bit number wrappers */ -#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_W64_WRAPPER) +/* (D)TLS v1.3 requires 64-bit number wrappers */ +#if defined(WOLFSSL_TLS13) || defined(WOLFSSL_DTLS_DROP_STATS) + #undef WOLFSSL_W64_WRAPPER #define WOLFSSL_W64_WRAPPER #endif