From 0a1332c4dfcbd2a1fee765c336548f9640fee414 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 9 Sep 2022 12:36:16 +0200 Subject: [PATCH 1/8] Additional checks for secure renegotiation --- src/ssl.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ssl.c b/src/ssl.c index fb969ead0..7810c02d5 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -3168,6 +3168,11 @@ static int _Rehandshake(WOLFSSL* ssl) if (ssl == NULL) return BAD_FUNC_ARG; + if (IsAtLeastTLSv1_3(ssl->version)) { + WOLFSSL_MSG("Secure Renegotiation not supported in TLS 1.3"); + return SECURE_RENEGOTIATION_E; + } + if (ssl->secure_renegotiation == NULL) { WOLFSSL_MSG("Secure Renegotiation not forced on by user"); return SECURE_RENEGOTIATION_E; @@ -3178,6 +3183,11 @@ static int _Rehandshake(WOLFSSL* ssl) return SECURE_RENEGOTIATION_E; } + if (ssl->keys.dtls_epoch == 0xFFFF) { + WOLFSSL_MSG("Secure Renegotiation not allowed. Epoch would wrap"); + return SECURE_RENEGOTIATION_E; + } + /* If the client started the renegotiation, the server will already * have processed the client's hello. */ if (ssl->options.side != WOLFSSL_SERVER_END || From 7418ddcae6b07a5a1db596f74e2e3237ea2bde60 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 9 Sep 2022 16:04:30 +0200 Subject: [PATCH 2/8] Refactor nofail tests into test_wolfSSL_client_server_nofail --- tests/api.c | 119 +++++++++++++++++++++---------------------------- wolfssl/test.h | 3 ++ 2 files changed, 53 insertions(+), 69 deletions(-) diff --git a/tests/api.c b/tests/api.c index fafac1ee6..f9727467f 100644 --- a/tests/api.c +++ b/tests/api.c @@ -5496,6 +5496,49 @@ done: return 0; } +void test_wolfSSL_client_server_nofail(callback_functions* client_cb, + callback_functions* server_cb) +{ + func_args client_args; + func_args server_args; + tcp_ready ready; + THREAD_TYPE serverThread; + + XMEMSET(&client_args, 0, sizeof(func_args)); + XMEMSET(&server_args, 0, sizeof(func_args)); + +#ifdef WOLFSSL_TIRTOS + fdOpenSession(Task_self()); +#endif + + StartTCP(); + InitTcpReady(&ready); + +#if defined(USE_WINDOWS_API) + /* use RNG to get random port if using windows */ + ready.port = GetRandomPort(); +#endif + + server_args.signal = &ready; + server_args.callbacks = server_cb; + client_args.signal = &ready; + client_args.callbacks = client_cb; + + start_thread(test_server_nofail, &server_args, &serverThread); + wait_tcp_ready(&server_args); + test_client_nofail(&client_args, NULL); + join_thread(serverThread); + + client_cb->return_code = client_args.return_code; + server_cb->return_code = server_args.return_code; + + FreeTcpReady(&ready); + +#ifdef WOLFSSL_TIRTOS + fdOpenSession(Task_self()); +#endif +} + #if defined(OPENSSL_EXTRA) && !defined(NO_SESSION_CACHE) && \ !defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT) static void test_client_reuse_WOLFSSLobj(void* args, void *cb, void* server_args) @@ -55058,12 +55101,8 @@ static void test_wolfSSL_dtls_plaintext_client(WOLFSSL* ssl) static int test_wolfSSL_dtls_plaintext(void) { - tcp_ready ready; - func_args client_args; - func_args server_args; callback_functions func_cb_client; callback_functions func_cb_server; - THREAD_TYPE serverThread; size_t i; struct test_params { method_provider client_meth; @@ -55079,47 +55118,19 @@ static int test_wolfSSL_dtls_plaintext(void) printf(testingFmt, "test_wolfSSL_dtls_plaintext"); for (i = 0; i < sizeof(params)/sizeof(*params); i++) { - XMEMSET(&client_args, 0, sizeof(func_args)); - XMEMSET(&server_args, 0, sizeof(func_args)); XMEMSET(&func_cb_client, 0, sizeof(callback_functions)); XMEMSET(&func_cb_server, 0, sizeof(callback_functions)); - #ifdef WOLFSSL_TIRTOS - fdOpenSession(Task_self()); - #endif - - StartTCP(); - InitTcpReady(&ready); - -#if defined(USE_WINDOWS_API) - /* use RNG to get random port if using windows */ - ready.port = GetRandomPort(); -#endif - - server_args.signal = &ready; - server_args.callbacks = &func_cb_server; - client_args.signal = &ready; - client_args.callbacks = &func_cb_client; - func_cb_client.doUdp = func_cb_server.doUdp = 1; func_cb_server.method = params[i].server_meth; func_cb_client.method = params[i].client_meth; func_cb_client.on_result = params[i].on_result_client; func_cb_server.on_result = params[i].on_result_server; - start_thread(test_server_nofail, &server_args, &serverThread); - wait_tcp_ready(&server_args); - test_client_nofail(&client_args, NULL); - join_thread(serverThread); + test_wolfSSL_client_server_nofail(&func_cb_client, &func_cb_server); - AssertTrue(client_args.return_code); - AssertTrue(server_args.return_code); - - FreeTcpReady(&ready); - -#ifdef WOLFSSL_TIRTOS - fdOpenSession(Task_self()); -#endif + AssertTrue(func_cb_client.return_code); + AssertTrue(func_cb_server.return_code); } printf(resultFmt, passed); @@ -55244,12 +55255,8 @@ static void test_wolfSSL_dtls13_fragments_spammer(WOLFSSL* ssl) static int test_wolfSSL_dtls_fragments(void) { - tcp_ready ready; - func_args client_args; - func_args server_args; callback_functions func_cb_client; callback_functions func_cb_server; - THREAD_TYPE serverThread; size_t i; struct test_params { method_provider client_meth; @@ -55267,52 +55274,26 @@ static int test_wolfSSL_dtls_fragments(void) printf(testingFmt, "test_wolfSSL_dtls_fragments"); for (i = 0; i < sizeof(params)/sizeof(*params); i++) { - XMEMSET(&client_args, 0, sizeof(func_args)); - XMEMSET(&server_args, 0, sizeof(func_args)); XMEMSET(&func_cb_client, 0, sizeof(callback_functions)); XMEMSET(&func_cb_server, 0, sizeof(callback_functions)); - #ifdef WOLFSSL_TIRTOS - fdOpenSession(Task_self()); - #endif - - StartTCP(); - InitTcpReady(&ready); - -#if defined(USE_WINDOWS_API) - /* use RNG to get random port if using windows */ - ready.port = GetRandomPort(); -#endif - - server_args.signal = &ready; - server_args.callbacks = &func_cb_server; - client_args.signal = &ready; - client_args.callbacks = &func_cb_client; func_cb_client.doUdp = func_cb_server.doUdp = 1; func_cb_server.method = params[i].server_meth; func_cb_client.method = params[i].client_meth; func_cb_client.ssl_ready = params[i].spammer; - start_thread(test_server_nofail, &server_args, &serverThread); - wait_tcp_ready(&server_args); - test_client_nofail(&client_args, NULL); - join_thread(serverThread); + test_wolfSSL_client_server_nofail(&func_cb_client, &func_cb_server); + + AssertFalse(func_cb_client.return_code); + AssertFalse(func_cb_server.return_code); - AssertFalse(client_args.return_code); - AssertFalse(server_args.return_code); /* The socket should be closed by the server resulting in a * socket error */ AssertIntEQ(func_cb_client.last_err, SOCKET_ERROR_E); /* Check the server returned an error indicating the msg buffer * was full */ AssertIntEQ(func_cb_server.last_err, DTLS_TOO_MANY_FRAGMENTS_E); - - FreeTcpReady(&ready); - -#ifdef WOLFSSL_TIRTOS - fdOpenSession(Task_self()); -#endif } printf(resultFmt, passed); diff --git a/wolfssl/test.h b/wolfssl/test.h index ec4569973..537e0b54e 100644 --- a/wolfssl/test.h +++ b/wolfssl/test.h @@ -657,6 +657,9 @@ typedef THREAD_RETURN WOLFSSL_THREAD THREAD_FUNC(void*); void start_thread(THREAD_FUNC fun, func_args* args, THREAD_TYPE* thread); void join_thread(THREAD_TYPE thread); +void test_wolfSSL_client_server_nofail(callback_functions* client_cb, + callback_functions* server_cb); + /* wolfSSL */ #ifndef TEST_IPV6 static const char* const wolfSSLIP = "127.0.0.1"; From 4e9106c355ae1c41127136ca6a37a892c9596276 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 12 Sep 2022 12:32:19 +0200 Subject: [PATCH 3/8] Enforce maximum amount of failed decryptions in DTLS 1.3 --- src/dtls13.c | 14 +++++ src/internal.c | 133 ++++++++++++++++++++++++++++++++--------- src/ssl.c | 9 ++- src/tls13.c | 37 +++++++----- tests/api.c | 143 +++++++++++++++++++++++++++++++++++++++++++++ wolfssl/internal.h | 28 ++++++++- 6 files changed, 316 insertions(+), 48 deletions(-) diff --git a/src/dtls13.c b/src/dtls13.c index b9df18b92..bc25179c8 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -1998,6 +1998,13 @@ int Dtls13NewEpoch(WOLFSSL* ssl, w64wrapper epochNumber, int side) return BAD_STATE_E; } +#ifndef WOLFSSL_TLS13_IGNORE_AEAD_LIMITS + /* We are updating the receiving keys for this connection. We can restart + * the failed decryption counter. */ + if (side == ENCRYPT_AND_DECRYPT_SIDE || side == DECRYPT_SIDE_ONLY) + w64Zero(&ssl->macDropCount); +#endif + Dtls13EpochCopyKeys(ssl, e, &ssl->keys, side); if (!e->isValid) { @@ -2373,6 +2380,13 @@ int Dtls13DoScheduledWork(WOLFSSL* ssl) ssl->dtls13SendingAckOrRtx = 0; + if (ssl->dtls13DoKeyUpdate) { + ssl->dtls13DoKeyUpdate = 0; + ret = Tls13UpdateKeys(ssl); + if (ret != 0) + return ret; + } + return 0; } diff --git a/src/internal.c b/src/internal.c index 6aa2cf751..203629a96 100644 --- a/src/internal.c +++ b/src/internal.c @@ -80,6 +80,11 @@ * Verifies the ECC signature after signing in case of faults in the * calculation of the signature. Useful when signature fault injection is a * possible attack. + * WOLFSSL_TLS13_IGNORE_AEAD_LIMITS + * Ignore the AEAD limits for messages specified in the RFC. After + * reaching the limit, we initiate a key update. + * https://www.rfc-editor.org/rfc/rfc8446#section-5.5 + * https://www.rfc-editor.org/rfc/rfc9147.html#name-aead-limits */ @@ -18771,6 +18776,98 @@ static WC_INLINE int VerifyMac(WOLFSSL* ssl, const byte* input, word32 msgSz, return 0; } +enum { + AEAD_LIMIT_OK, + AEAD_LIMIT_KEY_UPDATE, + AEAD_LIMIT_FAIL, +}; + +#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) +/* Limits specified by + * https://www.rfc-editor.org/rfc/rfc9147.html#name-aead-limits + * We specify the limit by which we need to do a key update as the halfway point + * to the hard decryption fail limit. */ +static int checkDTLS13AEADFailLimit(byte bulk_cipher_algorithm, + word16 aead_mac_size, w64wrapper dropped) +{ + w64wrapper keyUpdateLimit; + w64wrapper hardLimit; + switch (bulk_cipher_algorithm) { +#if defined(BUILD_AESGCM) || (defined(HAVE_CHACHA) && defined(HAVE_POLY1305)) + 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; +#endif +#ifdef HAVE_AESCCM + case wolfssl_aes_ccm: + if (aead_mac_size == AES_CCM_8_AUTH_SZ) { + /* Limit is 2^7. The RFC recommends that + * "TLS_AES_128_CCM_8_SHA256 is not suitable for general use". + * We still should enforce the limit. */ + hardLimit = DTLS_AEAD_AES_CCM_8_FAIL_LIMIT; + keyUpdateLimit = DTLS_AEAD_AES_CCM_8_FAIL_KU_LIMIT; + } + else { + /* Limit is 2^23.5. + * Without the fraction is 11863283 (0x00B504F3) + * Half of this value is 5931641 (0x005A8279) */ + hardLimit = DTLS_AEAD_AES_CCM_FAIL_LIMIT; + keyUpdateLimit = DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT; + } + break; +#endif + default: + WOLFSSL_MSG("Unrecognized ciphersuite for AEAD limit check"); + return AEAD_LIMIT_FAIL; + } + if (w64GT(dropped, hardLimit)) { + WOLFSSL_MSG("Connection exceeded hard AEAD limit"); + return AEAD_LIMIT_FAIL; + } + else if (w64GT(dropped, keyUpdateLimit)) { + WOLFSSL_MSG("Connection exceeded key update limit. Issuing key update"); + return AEAD_LIMIT_KEY_UPDATE; + } + return AEAD_LIMIT_OK; +} +#endif + +#ifdef WOLFSSL_DTLS +static int HandleDTLSDecryptFailed(WOLFSSL* ssl) +{ + ssl->options.processReply = doProcessInit; + ssl->buffers.inputBuffer.idx = ssl->buffers.inputBuffer.length; + +#if defined(WOLFSSL_DTLS_DROP_STATS) || \ + (defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS)) + w64Increment(&ssl->macDropCount); + +#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) + /* Handle AEAD limits specified by the RFC for failed decryption */ + if (IsAtLeastTLSv1_3(ssl->version)) { + int ret = checkDTLS13AEADFailLimit(ssl->specs.bulk_cipher_algorithm, + ssl->specs.aead_mac_size, ssl->macDropCount); + if (ret == AEAD_LIMIT_KEY_UPDATE) { + /* If not waiting for a response then request a key update. */ + if (!ssl->keys.updateResponseReq) + ssl->dtls13DoKeyUpdate = 1; + } + else if (ret == AEAD_LIMIT_FAIL) { + /* We have reached the hard limit for failed decryptions. */ + WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR); + return DECRYPT_ERROR; + } + } +#endif +#endif + + WOLFSSL_MSG("DTLS: Ignoring failed decryption"); + return 0; +} +#endif + int ProcessReply(WOLFSSL* ssl) { return ProcessReplyEx(ssl, 0); @@ -19077,18 +19174,11 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) #endif if (ret < 0) { WOLFSSL_MSG("VerifyMacEnc failed"); - WOLFSSL_ERROR(ret); #ifdef WOLFSSL_DTLS /* If in DTLS mode, if the decrypt fails for any * reason, pretend the datagram never happened. */ - if (ssl->options.dtls) { - ssl->options.processReply = doProcessInit; - ssl->buffers.inputBuffer.idx = - ssl->buffers.inputBuffer.length; - #ifdef WOLFSSL_DTLS_DROP_STATS - ssl->macDropCount++; - #endif /* WOLFSSL_DTLS_DROP_STATS */ - } + if (ssl->options.dtls) + return HandleDTLSDecryptFailed(ssl); #endif /* WOLFSSL_DTLS */ #ifdef WOLFSSL_EXTRA_ALERTS if (!ssl->options.dtls) @@ -19243,16 +19333,8 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) #ifdef WOLFSSL_DTLS /* If in DTLS mode, if the decrypt fails for any * reason, pretend the datagram never happened. */ - if (ssl->options.dtls) { - WOLFSSL_MSG("DTLS: Ignoring failed decryption"); - ssl->options.processReply = doProcessInit; - ssl->buffers.inputBuffer.idx = - ssl->buffers.inputBuffer.length; - #ifdef WOLFSSL_DTLS_DROP_STATS - ssl->macDropCount++; - #endif /* WOLFSSL_DTLS_DROP_STATS */ - return 0; - } + if (ssl->options.dtls) + return HandleDTLSDecryptFailed(ssl); #endif /* WOLFSSL_DTLS */ #ifdef WOLFSSL_EARLY_DATA if (ssl->options.tls1_3) { @@ -19314,24 +19396,17 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) return ret; #endif if (ret < 0) { - WOLFSSL_MSG("VerifyMac failed"); - WOLFSSL_ERROR(ret); #ifdef WOLFSSL_DTLS /* If in DTLS mode, if the decrypt fails for any * reason, pretend the datagram never happened. */ - if (ssl->options.dtls) { - ssl->options.processReply = doProcessInit; - ssl->buffers.inputBuffer.idx = - ssl->buffers.inputBuffer.length; - #ifdef WOLFSSL_DTLS_DROP_STATS - ssl->macDropCount++; - #endif /* WOLFSSL_DTLS_DROP_STATS */ - } + if (ssl->options.dtls) + return HandleDTLSDecryptFailed(ssl); #endif /* WOLFSSL_DTLS */ #ifdef WOLFSSL_EXTRA_ALERTS if (!ssl->options.dtls) SendAlert(ssl, alert_fatal, bad_record_mac); #endif + WOLFSSL_MSG("VerifyMac failed"); WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR); return DECRYPT_ERROR; } diff --git a/src/ssl.c b/src/ssl.c index 7810c02d5..17b732258 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -3183,10 +3183,12 @@ static int _Rehandshake(WOLFSSL* ssl) return SECURE_RENEGOTIATION_E; } - if (ssl->keys.dtls_epoch == 0xFFFF) { +#ifdef WOLFSSL_DTLS + if (ssl->options.dtls && ssl->keys.dtls_epoch == 0xFFFF) { WOLFSSL_MSG("Secure Renegotiation not allowed. Epoch would wrap"); return SECURE_RENEGOTIATION_E; } +#endif /* If the client started the renegotiation, the server will already * have processed the client's hello. */ @@ -3282,6 +3284,11 @@ int wolfSSL_Rehandshake(WOLFSSL* ssl) /* CLIENT/SERVER: Reset peer authentication for full secure handshake. */ ssl->options.peerAuthGood = 0; +#ifdef WOLFSSL_DTLS_DROP_STATS + if (ssl->options.dtls) + w64Zero(&ssl->macDropCount); +#endif + #ifdef HAVE_SESSION_TICKET if (ret == WOLFSSL_SUCCESS) #endif diff --git a/src/tls13.c b/src/tls13.c index 50cb2de58..00708b934 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -11120,6 +11120,26 @@ int wolfSSL_no_dhe_psk(WOLFSSL* ssl) return 0; } + +int Tls13UpdateKeys(WOLFSSL* ssl) +{ + if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) + return BAD_FUNC_ARG; + +#ifdef WOLFSSL_DTLS13 + /* we are already waiting for the ack of a sent key update message. We can't + send another one before receiving its ack. Either wolfSSL_update_keys() + was invoked multiple times over a short period of time or we replied to a + KeyUpdate with update request. We'll just ignore sending this + KeyUpdate. */ + /* TODO: add WOLFSSL_ERROR_ALREADY_IN_PROGRESS type of error here */ + if (ssl->options.dtls && ssl->dtls13WaitKeyUpdateAck) + return 0; +#endif /* WOLFSSL_DTLS13 */ + + return SendTls13KeyUpdate(ssl); +} + /* Update the keys for encryption and decryption. * If using non-blocking I/O and WOLFSSL_ERROR_WANT_WRITE is returned then * calling wolfSSL_write() will have the message sent when ready. @@ -11132,22 +11152,7 @@ int wolfSSL_no_dhe_psk(WOLFSSL* ssl) int wolfSSL_update_keys(WOLFSSL* ssl) { int ret; - - if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) - return BAD_FUNC_ARG; - -#ifdef WOLFSSL_DTLS13 - /* we are already waiting for the ack of a sent key update message. We can't - send another one before receiving its ack. Either wolfSSL_update_keys() - was invoked multiple times over a short period of time or we replied to a - KeyUpdate with update request. We'll just ignore sending this - KeyUpdate. */ - /* TODO: add WOLFSSL_ERROR_ALREADY_IN_PROGRESS type of error here */ - if (ssl->options.dtls && ssl->dtls13WaitKeyUpdateAck) - return WOLFSSL_SUCCESS; -#endif /* WOLFSSL_DTLS13 */ - - ret = SendTls13KeyUpdate(ssl); + ret = Tls13UpdateKeys(ssl); if (ret == WANT_WRITE) ret = WOLFSSL_ERROR_WANT_WRITE; else if (ret == 0) diff --git a/tests/api.c b/tests/api.c index f9727467f..625a96ec9 100644 --- a/tests/api.c +++ b/tests/api.c @@ -55306,6 +55306,148 @@ static int test_wolfSSL_dtls_fragments(void) { } #endif +#ifdef WOLFSSL_DTLS13 +static byte test_AEAD_fail_decryption = 0; +static byte test_AEAD_done = 0; + +static int test_AEAD_cbiorecv(WOLFSSL *ssl, char *buf, int sz, void *ctx) +{ + int ret = recv(wolfSSL_get_fd(ssl), buf, sz, 0); + if (ret > 0) { + if (test_AEAD_fail_decryption) { + /* Modify the packet to trigger a decryption failure */ + buf[ret/2] ^= 0xFF; + if (test_AEAD_fail_decryption == 1) + test_AEAD_fail_decryption = 0; + } + } + (void)ctx; + return ret; +} + +static void test_AEAD_limit_client(WOLFSSL* ssl) +{ + int ret; + int i; + int didReKey = 0; + char msgBuf[20]; + w64wrapper hardLimit; + w64wrapper keyUpdateLimit; + w64wrapper counter; + + 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; + } + + w64Zero(&counter); + AssertTrue(w64Equal(ssl->macDropCount, counter)); + + wolfSSL_SSLSetIORecv(ssl, test_AEAD_cbiorecv); + + for (i = 0; i < 10; i++) { + /* Test some failed decryptions */ + test_AEAD_fail_decryption = 1; + w64Increment(&counter); + ret = wolfSSL_read(ssl, msgBuf, sizeof(msgBuf)); + /* Should succeed since decryption failures are dropped */ + AssertIntGT(ret, 0); + AssertTrue(w64Equal(ssl->macDropCount, counter)); + } + + test_AEAD_fail_decryption = 1; + ssl->macDropCount = keyUpdateLimit; + w64Increment(&ssl->macDropCount); + /* 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 one key update is 4 */ + if (w64Equal(ssl->dtls13Epoch, w64From32(0, 4))) { + didReKey = 1; + break; + } + } + AssertTrue(didReKey); + w64Zero(&counter); + AssertTrue(w64Equal(ssl->macDropCount, counter)); + + test_AEAD_fail_decryption = 2; + ssl->macDropCount = hardLimit; + w64Decrement(&ssl->macDropCount); + /* Connection should fail with a DECRYPT_ERROR */ + ret = wolfSSL_read(ssl, msgBuf, sizeof(msgBuf)); + AssertIntEQ(ret, WOLFSSL_FATAL_ERROR); + AssertIntEQ(wolfSSL_get_error(ssl, ret), DECRYPT_ERROR); + + test_AEAD_done = 1; +} + +static void test_AEAD_limit_server(WOLFSSL* ssl) +{ + char msgBuf[] = "Sending data"; + int ret = WOLFSSL_SUCCESS; + SOCKET_T fd = wolfSSL_get_fd(ssl); + struct timespec delay; + XMEMSET(&delay, 0, sizeof(delay)); + delay.tv_nsec = 50000000; /* wait 0.05 seconds */ + tcp_set_nonblocking(&fd); /* So that read doesn't block */ + while (!test_AEAD_done && ret > 0) { + (void)wolfSSL_read(ssl, msgBuf, sizeof(msgBuf)); + ret = wolfSSL_write(ssl, msgBuf, sizeof(msgBuf)); + nanosleep(&delay, NULL); + } +} + +static int test_wolfSSL_dtls_AEAD_limit(void) +{ + callback_functions func_cb_client; + callback_functions func_cb_server; + XMEMSET(&func_cb_client, 0, sizeof(callback_functions)); + XMEMSET(&func_cb_server, 0, sizeof(callback_functions)); + + printf(testingFmt, "test_wolfSSL_dtls_AEAD_limit"); + + func_cb_client.doUdp = func_cb_server.doUdp = 1; + func_cb_server.method = wolfDTLSv1_3_server_method; + func_cb_client.method = wolfDTLSv1_3_client_method; + func_cb_server.on_result = test_AEAD_limit_server; + func_cb_client.on_result = test_AEAD_limit_client; + + test_wolfSSL_client_server_nofail(&func_cb_client, &func_cb_server); + + AssertTrue(func_cb_client.return_code); + AssertTrue(func_cb_server.return_code); + + printf(resultFmt, passed); + + return 0; +} +#else +static int test_wolfSSL_dtls_AEAD_limit(void) +{ + return 0; +} +#endif + #if !defined(NO_RSA) && !defined(NO_SHA) && !defined(NO_FILESYSTEM) && \ !defined(NO_CERTS) && (!defined(NO_WOLFSSL_CLIENT) || \ !defined(WOLFSSL_NO_CLIENT_AUTH)) @@ -58015,6 +58157,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_either_side), TEST_DECL(test_wolfSSL_DTLS_either_side), TEST_DECL(test_wolfSSL_dtls_fragments), + TEST_DECL(test_wolfSSL_dtls_AEAD_limit), TEST_DECL(test_generate_cookie), TEST_DECL(test_wolfSSL_X509_STORE_set_flags), TEST_DECL(test_wolfSSL_X509_LOOKUP_load_file), diff --git a/wolfssl/internal.h b/wolfssl/internal.h index c62e2a6b5..e2585c663 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1267,6 +1267,22 @@ enum { #define TLS13_MAX_TICKET_AGE (7*24*60*60) #endif + +/* 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) +#define DTLS_AEAD_AES_GCM_CHACHA_FAIL_KU_LIMIT w64From32(1 << 2, 0) +/* Limit is 2^7 + * https://www.rfc-editor.org/rfc/rfc9147.html#name-limits-for-aead_aes_128_ccm */ +#define DTLS_AEAD_AES_CCM_8_FAIL_LIMIT w64From32(0, 1 << 6) +#define DTLS_AEAD_AES_CCM_8_FAIL_KU_LIMIT w64From32(0, 1 << 5) +/* Limit is 2^23.5. + * https://www.rfc-editor.org/rfc/rfc9147.html#name-integrity-limits + * Without the fraction is 11863283 (0x00B504F3) + * Half of this value is 5931641 (0x005A8279) */ +#define DTLS_AEAD_AES_CCM_FAIL_LIMIT w64From32(0x00B5, 0x04F3) +#define DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT w64From32(0x005A, 0x8279) + enum Misc { CIPHER_BYTE = 0x00, /* Default ciphers */ ECC_BYTE = 0xC0, /* ECC first cipher suite byte */ @@ -4026,6 +4042,10 @@ typedef struct Options { #endif #endif #ifdef WOLFSSL_DTLS +#ifdef HAVE_SECURE_RENEGOTIATION + word16 dtlsDoSCR:1; /* Enough packets were dropped. We + * need to re-key. */ +#endif word16 dtlsUseNonblock:1; /* are we using nonblocking socket */ word16 dtlsHsRetain:1; /* DTLS retaining HS data */ word16 haveMcast:1; /* using multicast ? */ @@ -4678,10 +4698,10 @@ typedef struct Dtls13Rtx { Dtls13RtxRecord *rtxRecords; Dtls13RtxRecord **rtxRecordTailPtr; Dtls13RecordNumber *seenRecords; + word32 lastRtx; byte triggeredRtxs; byte sendAcks:1; byte retransmit:1; - word32 lastRtx; } Dtls13Rtx; #endif /* WOLFSSL_DTLS13 */ @@ -4884,8 +4904,10 @@ struct WOLFSSL { #ifdef WOLFSSL_MULTICAST void* mcastHwCbCtx; /* Multicast highwater callback ctx */ #endif /* WOLFSSL_MULTICAST */ +#if defined(WOLFSSL_DTLS_DROP_STATS) || defined(WOLFSSL_DTLS13) + w64wrapper macDropCount; +#endif #ifdef WOLFSSL_DTLS_DROP_STATS - word32 macDropCount; word32 replayDropCount; #endif /* WOLFSSL_DTLS_DROP_STATS */ #ifdef WOLFSSL_SRTP @@ -4910,6 +4932,7 @@ struct WOLFSSL { byte dtls13SendingAckOrRtx:1; byte dtls13FastTimeout:1; byte dtls13WaitKeyUpdateAck:1; + byte dtls13DoKeyUpdate:1; word32 dtls13MessageLength; word32 dtls13FragOffset; byte dtls13FragHandshakeType; @@ -5657,6 +5680,7 @@ WOLFSSL_LOCAL int BuildMessage(WOLFSSL* ssl, byte* output, int outSz, /* Use WOLFSSL_API to use this function in tests/api.c */ WOLFSSL_API int BuildTls13Message(WOLFSSL* ssl, byte* output, int outSz, const byte* input, int inSz, int type, int hashOutput, int sizeOnly, int asyncOkay); +WOLFSSL_LOCAL int Tls13UpdateKeys(WOLFSSL* ssl); #endif WOLFSSL_LOCAL int AllocKey(WOLFSSL* ssl, int type, void** pKey); From 63ba2f7b8f48cf08bfd8945599b07ff3f2634132 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 12 Sep 2022 16:50:48 +0200 Subject: [PATCH 4/8] TLS 1.3: Check maximum records encrypted with one key set --- src/internal.c | 73 +++++++++++++++++++++++++ src/ssl.c | 2 +- src/tls13.c | 2 +- tests/api.c | 100 +++++++++++++++++++++++++++-------- wolfssl/internal.h | 8 +++ wolfssl/wolfcrypt/settings.h | 5 +- 6 files changed, 164 insertions(+), 26 deletions(-) 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 From 67473bac28a459f72b0a04a67669559c104b9a23 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 15 Sep 2022 11:34:30 +0200 Subject: [PATCH 5/8] Code review fixes - Mark old epochs as invalid so we don't attempt to decrypt with them - Return a non-zero value if possible in unit tests - Move Dtls13CheckAEADFailLimit to dtls13.c - Reset state in processreply --- src/dtls13.c | 90 +++++++++++++++++++++++++++- src/internal.c | 143 ++++++++++++++++----------------------------- tests/api.c | 38 +++++++----- wolfssl/internal.h | 7 ++- 4 files changed, 166 insertions(+), 112 deletions(-) diff --git a/src/dtls13.c b/src/dtls13.c index bc25179c8..18b392cc7 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -1846,6 +1846,20 @@ struct Dtls13Epoch* Dtls13GetEpoch(WOLFSSL* ssl, w64wrapper epochNumber) return NULL; } +void Dtls13SetOlderEpochSide(WOLFSSL* ssl, w64wrapper epochNumber, + int side) +{ + Dtls13Epoch* e; + int i; + + for (i = 0; i < DTLS13_EPOCH_SIZE; ++i) { + e = &ssl->dtls13Epochs[i]; + if (e->isValid && w64LT(e->epochNumber, epochNumber)) { + e->side = (byte)side; + } + } +} + static void Dtls13EpochCopyKeys(WOLFSSL* ssl, Dtls13Epoch* e, Keys* k, int side) { byte clientWrite, serverWrite; @@ -2000,8 +2014,10 @@ int Dtls13NewEpoch(WOLFSSL* ssl, w64wrapper epochNumber, int side) #ifndef WOLFSSL_TLS13_IGNORE_AEAD_LIMITS /* We are updating the receiving keys for this connection. We can restart - * the failed decryption counter. */ - if (side == ENCRYPT_AND_DECRYPT_SIDE || side == DECRYPT_SIDE_ONLY) + * the failed decryption counter if we haven't reached the key update + * limit. */ + if ((side == ENCRYPT_AND_DECRYPT_SIDE || side == DECRYPT_SIDE_ONLY) && + w64IsZero(ssl->dtls13InvalidateBefore)) w64Zero(&ssl->macDropCount); #endif @@ -2018,6 +2034,14 @@ int Dtls13NewEpoch(WOLFSSL* ssl, w64wrapper epochNumber, int side) e->side = ENCRYPT_AND_DECRYPT_SIDE; } + /* Once handshake is done. Mark epochs older than the last one as encrypt + * only so that they can't be used for decryption. */ + if (ssl->options.handShakeDone && (e->side == ENCRYPT_AND_DECRYPT_SIDE || + e->side == DECRYPT_SIDE_ONLY)) { + w64Decrement(&epochNumber); + Dtls13SetOlderEpochSide(ssl, epochNumber, ENCRYPT_SIDE_ONLY); + } + return 0; } @@ -2624,4 +2648,66 @@ int wolfSSL_dtls13_has_pending_msg(WOLFSSL* ssl) return ssl->dtls13Rtx.rtxRecords != NULL; } +#ifndef WOLFSSL_TLS13_IGNORE_AEAD_LIMITS +/* Limits specified by + * https://www.rfc-editor.org/rfc/rfc9147.html#name-aead-limits + * We specify the limit by which we need to do a key update as the halfway point + * to the hard decryption fail limit. */ +int Dtls13CheckAEADFailLimit(WOLFSSL* ssl) +{ + w64wrapper keyUpdateLimit; + w64wrapper hardLimit; + switch (ssl->specs.bulk_cipher_algorithm) { +#if defined(BUILD_AESGCM) || (defined(HAVE_CHACHA) && defined(HAVE_POLY1305)) + 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; +#endif +#ifdef HAVE_AESCCM + case wolfssl_aes_ccm: + if (ssl->specs.aead_mac_size == AES_CCM_8_AUTH_SZ) { + /* Limit is 2^7. The RFC recommends that + * "TLS_AES_128_CCM_8_SHA256 is not suitable for general use". + * We still should enforce the limit. */ + hardLimit = DTLS_AEAD_AES_CCM_8_FAIL_LIMIT; + keyUpdateLimit = DTLS_AEAD_AES_CCM_8_FAIL_KU_LIMIT; + } + else { + /* Limit is 2^23.5. + * Without the fraction is 11863283 (0x00B504F3) + * Half of this value is 5931641 (0x005A8279) */ + hardLimit = DTLS_AEAD_AES_CCM_FAIL_LIMIT; + keyUpdateLimit = DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT; + } + break; +#endif + case wolfssl_cipher_null: + /* No encryption being done. The MAC check must have failed. */ + return 0; + default: + WOLFSSL_MSG("Unrecognized ciphersuite for AEAD limit check"); + WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR); + return DECRYPT_ERROR; + } + if (w64GT(ssl->macDropCount, hardLimit)) { + /* We have reached the hard limit for failed decryptions. */ + WOLFSSL_MSG("Connection exceeded hard AEAD limit"); + WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR); + return DECRYPT_ERROR; + } + else if (w64GT(ssl->macDropCount, keyUpdateLimit)) { + WOLFSSL_MSG("Connection exceeded key update limit. Issuing key update"); + /* If not waiting for a response then request a key update. */ + if (!ssl->keys.updateResponseReq) { + ssl->dtls13DoKeyUpdate = 1; + ssl->dtls13InvalidateBefore = ssl->dtls13PeerEpoch; + w64Increment(&ssl->dtls13InvalidateBefore); + } + } + return 0; +} +#endif + #endif /* WOLFSSL_DTLS13 */ diff --git a/src/internal.c b/src/internal.c index 4a90c7ea4..635a3b917 100644 --- a/src/internal.c +++ b/src/internal.c @@ -82,7 +82,8 @@ * possible attack. * WOLFSSL_TLS13_IGNORE_AEAD_LIMITS * Ignore the AEAD limits for messages specified in the RFC. After - * reaching the limit, we initiate a key update. + * reaching the limit, we initiate a key update. We enforce the AEAD limits + * by default. * https://www.rfc-editor.org/rfc/rfc8446#section-5.5 * https://www.rfc-editor.org/rfc/rfc9147.html#name-aead-limits */ @@ -18192,6 +18193,24 @@ int DoApplicationData(WOLFSSL* ssl, byte* input, word32* inOutIdx, int sniff) return OUT_OF_ORDER_E; } + +#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) + /* Check if we want to invalidate old epochs. If + * ssl->dtls13InvalidateBefore is set then we want to mark all old + * epochs as encrypt only. This is done when we detect too many failed + * decryptions. We do this here to confirm that the peer has updated its + * keys and we can stop using the old keys. */ + if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) { + if (!w64IsZero(ssl->dtls13InvalidateBefore) && + w64Equal(ssl->keys.curEpoch64, ssl->dtls13InvalidateBefore)) { + Dtls13SetOlderEpochSide(ssl, ssl->dtls13InvalidateBefore, + ENCRYPT_SIDE_ONLY); + w64Zero(&ssl->dtls13InvalidateBefore); + w64Zero(&ssl->macDropCount); + } + } +#endif + #ifndef WOLFSSL_AEAD_ONLY if (ssl->specs.cipher_type == block) { if (ssl->options.tls1_1) @@ -18776,98 +18795,24 @@ static WC_INLINE int VerifyMac(WOLFSSL* ssl, const byte* input, word32 msgSz, return 0; } -enum { - AEAD_LIMIT_OK, - AEAD_LIMIT_KEY_UPDATE, - AEAD_LIMIT_FAIL, -}; - -#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) -/* Limits specified by - * https://www.rfc-editor.org/rfc/rfc9147.html#name-aead-limits - * We specify the limit by which we need to do a key update as the halfway point - * to the hard decryption fail limit. */ -static int checkDTLS13AEADFailLimit(byte bulk_cipher_algorithm, - word16 aead_mac_size, w64wrapper dropped) -{ - w64wrapper keyUpdateLimit; - w64wrapper hardLimit; - switch (bulk_cipher_algorithm) { -#if defined(BUILD_AESGCM) || (defined(HAVE_CHACHA) && defined(HAVE_POLY1305)) - 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; -#endif -#ifdef HAVE_AESCCM - case wolfssl_aes_ccm: - if (aead_mac_size == AES_CCM_8_AUTH_SZ) { - /* Limit is 2^7. The RFC recommends that - * "TLS_AES_128_CCM_8_SHA256 is not suitable for general use". - * We still should enforce the limit. */ - hardLimit = DTLS_AEAD_AES_CCM_8_FAIL_LIMIT; - keyUpdateLimit = DTLS_AEAD_AES_CCM_8_FAIL_KU_LIMIT; - } - else { - /* Limit is 2^23.5. - * Without the fraction is 11863283 (0x00B504F3) - * Half of this value is 5931641 (0x005A8279) */ - hardLimit = DTLS_AEAD_AES_CCM_FAIL_LIMIT; - keyUpdateLimit = DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT; - } - 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; - } - if (w64GT(dropped, hardLimit)) { - WOLFSSL_MSG("Connection exceeded hard AEAD limit"); - return AEAD_LIMIT_FAIL; - } - else if (w64GT(dropped, keyUpdateLimit)) { - WOLFSSL_MSG("Connection exceeded key update limit. Issuing key update"); - return AEAD_LIMIT_KEY_UPDATE; - } - return AEAD_LIMIT_OK; -} -#endif - #ifdef WOLFSSL_DTLS static int HandleDTLSDecryptFailed(WOLFSSL* ssl) { - ssl->options.processReply = doProcessInit; - ssl->buffers.inputBuffer.idx = ssl->buffers.inputBuffer.length; - + int ret = 0; #if defined(WOLFSSL_DTLS_DROP_STATS) || \ (defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS)) w64Increment(&ssl->macDropCount); #if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) /* Handle AEAD limits specified by the RFC for failed decryption */ - if (IsAtLeastTLSv1_3(ssl->version)) { - int ret = checkDTLS13AEADFailLimit(ssl->specs.bulk_cipher_algorithm, - ssl->specs.aead_mac_size, ssl->macDropCount); - if (ret == AEAD_LIMIT_KEY_UPDATE) { - /* If not waiting for a response then request a key update. */ - if (!ssl->keys.updateResponseReq) - ssl->dtls13DoKeyUpdate = 1; - } - else if (ret == AEAD_LIMIT_FAIL) { - /* We have reached the hard limit for failed decryptions. */ - WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR); - return DECRYPT_ERROR; - } - } + if (IsAtLeastTLSv1_3(ssl->version)) + ret = Dtls13CheckAEADFailLimit(ssl); #endif #endif + (void)ssl; WOLFSSL_MSG("DTLS: Ignoring failed decryption"); - return 0; + return ret; } #endif @@ -19180,8 +19125,12 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) #ifdef WOLFSSL_DTLS /* If in DTLS mode, if the decrypt fails for any * reason, pretend the datagram never happened. */ - if (ssl->options.dtls) + if (ssl->options.dtls) { + ssl->options.processReply = doProcessInit; + ssl->buffers.inputBuffer.idx = + ssl->buffers.inputBuffer.length; return HandleDTLSDecryptFailed(ssl); + } #endif /* WOLFSSL_DTLS */ #ifdef WOLFSSL_EXTRA_ALERTS if (!ssl->options.dtls) @@ -19336,8 +19285,12 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) #ifdef WOLFSSL_DTLS /* If in DTLS mode, if the decrypt fails for any * reason, pretend the datagram never happened. */ - if (ssl->options.dtls) + if (ssl->options.dtls) { + ssl->options.processReply = doProcessInit; + ssl->buffers.inputBuffer.idx = + ssl->buffers.inputBuffer.length; return HandleDTLSDecryptFailed(ssl); + } #endif /* WOLFSSL_DTLS */ #ifdef WOLFSSL_EARLY_DATA if (ssl->options.tls1_3) { @@ -19402,8 +19355,12 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) #ifdef WOLFSSL_DTLS /* If in DTLS mode, if the decrypt fails for any * reason, pretend the datagram never happened. */ - if (ssl->options.dtls) + if (ssl->options.dtls) { + ssl->options.processReply = doProcessInit; + ssl->buffers.inputBuffer.idx = + ssl->buffers.inputBuffer.length; return HandleDTLSDecryptFailed(ssl); + } #endif /* WOLFSSL_DTLS */ #ifdef WOLFSSL_EXTRA_ALERTS if (!ssl->options.dtls) @@ -22196,6 +22153,16 @@ int SendData(WOLFSSL* ssl, const void* data, int sz) byte comp[MAX_RECORD_SIZE + MAX_COMP_EXTRA]; #endif +#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_DTLS13 if (ssl->options.dtls && ssl->options.tls1_3) { byte isEarlyData = 0; @@ -22232,16 +22199,6 @@ 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/tests/api.c b/tests/api.c index 8f1776460..e320e9ad6 100644 --- a/tests/api.c +++ b/tests/api.c @@ -55129,8 +55129,10 @@ static int test_wolfSSL_dtls_plaintext(void) test_wolfSSL_client_server_nofail(&func_cb_client, &func_cb_server); - AssertTrue(func_cb_client.return_code); - AssertTrue(func_cb_server.return_code); + if (!func_cb_client.return_code) + return -1; + if (!func_cb_server.return_code) + return -2; } printf(resultFmt, passed); @@ -55397,42 +55399,43 @@ static void test_AEAD_limit_client(WOLFSSL* ssl) ssl->macDropCount = keyUpdateLimit; w64Increment(&ssl->macDropCount); /* 100 read calls should be enough to complete the key update */ + w64Zero(&counter); 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 one key update is 4 */ - if (w64Equal(ssl->dtls13Epoch, w64From32(0, 4))) { + if (w64Equal(ssl->dtls13PeerEpoch, w64From32(0, 4)) && + w64Equal(ssl->macDropCount, counter)) { didReKey = 1; break; } } AssertTrue(didReKey); - 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; + Dtls13Epoch* e = Dtls13GetEpoch(ssl, ssl->dtls13Epoch); + AssertNotNull(e); + e->nextSeqNumber = sendLimit; test_AEAD_seq_num = 1; ret = wolfSSL_write(ssl, msgBuf, sizeof(msgBuf)); AssertIntGT(ret, 0); + didReKey = 0; + w64Zero(&counter); /* 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))) { + if (w64Equal(ssl->dtls13Epoch, w64From32(0, 5)) && + w64Equal(ssl->macDropCount, counter)) { didReKey = 1; break; } } AssertTrue(didReKey); - w64Zero(&counter); - AssertTrue(w64Equal(ssl->macDropCount, counter)); } test_AEAD_fail_decryption = 2; @@ -55463,9 +55466,10 @@ static void test_AEAD_limit_server(WOLFSSL* ssl) 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; + Dtls13Epoch* e = Dtls13GetEpoch(ssl, ssl->dtls13PeerEpoch); + AssertNotNull(e); + e->nextPeerSeqNumber = sendLimit; + test_AEAD_seq_num = 0; } (void)wolfSSL_read(ssl, msgBuf, sizeof(msgBuf)); ret = wolfSSL_write(ssl, msgBuf, sizeof(msgBuf)); @@ -55490,8 +55494,10 @@ static int test_wolfSSL_dtls_AEAD_limit(void) test_wolfSSL_client_server_nofail(&func_cb_client, &func_cb_server); - AssertTrue(func_cb_client.return_code); - AssertTrue(func_cb_server.return_code); + if (!func_cb_client.return_code) + return -1; + if (!func_cb_server.return_code) + return -2; printf(resultFmt, passed); diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 8c77b9286..54db08cf5 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -4931,6 +4931,7 @@ struct WOLFSSL { Dtls13Epoch *dtls13DecryptEpoch; w64wrapper dtls13Epoch; w64wrapper dtls13PeerEpoch; + w64wrapper dtls13InvalidateBefore; byte dtls13CurRL[DTLS_RECVD_RL_HEADER_MAX_SZ]; word16 dtls13CurRlLength; @@ -5749,8 +5750,11 @@ WOLFSSL_API int wolfSSL_DtlsUpdateWindow(word16 cur_hi, word32 cur_lo, #ifdef WOLFSSL_DTLS13 -WOLFSSL_LOCAL struct Dtls13Epoch* Dtls13GetEpoch(WOLFSSL* ssl, +/* Use WOLFSSL_API to use this function in tests/api.c */ +WOLFSSL_API struct Dtls13Epoch* Dtls13GetEpoch(WOLFSSL* ssl, w64wrapper epochNumber); +WOLFSSL_LOCAL void Dtls13SetOlderEpochSide(WOLFSSL* ssl, w64wrapper epochNumber, + int side); WOLFSSL_LOCAL int Dtls13NewEpoch(WOLFSSL* ssl, w64wrapper epochNumber, int side); WOLFSSL_LOCAL int Dtls13SetEpochKeys(WOLFSSL* ssl, w64wrapper epochNumber, @@ -5802,6 +5806,7 @@ WOLFSSL_LOCAL int Dtls13HashHandshake(WOLFSSL* ssl, const byte* output, WOLFSSL_LOCAL void Dtls13FreeFsmResources(WOLFSSL* ssl); WOLFSSL_LOCAL int Dtls13RtxTimeout(WOLFSSL* ssl); WOLFSSL_LOCAL int Dtls13ProcessBufferedMessages(WOLFSSL* ssl); +WOLFSSL_LOCAL int Dtls13CheckAEADFailLimit(WOLFSSL* ssl); #endif /* WOLFSSL_DTLS13 */ #ifdef WOLFSSL_STATIC_EPHEMERAL From 1941fb2b35b82c81da71afb146d0c51c909c2bdb Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 15 Sep 2022 15:49:05 +0200 Subject: [PATCH 6/8] Keep a separate drop counter for each epoch --- src/dtls13.c | 20 +++++++++----------- src/internal.c | 8 +++----- src/ssl.c | 7 +------ tests/api.c | 20 +++++++++----------- wolfssl/internal.h | 8 +++++--- 5 files changed, 27 insertions(+), 36 deletions(-) diff --git a/src/dtls13.c b/src/dtls13.c index 18b392cc7..718e2bb05 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -2012,15 +2012,6 @@ int Dtls13NewEpoch(WOLFSSL* ssl, w64wrapper epochNumber, int side) return BAD_STATE_E; } -#ifndef WOLFSSL_TLS13_IGNORE_AEAD_LIMITS - /* We are updating the receiving keys for this connection. We can restart - * the failed decryption counter if we haven't reached the key update - * limit. */ - if ((side == ENCRYPT_AND_DECRYPT_SIDE || side == DECRYPT_SIDE_ONLY) && - w64IsZero(ssl->dtls13InvalidateBefore)) - w64Zero(&ssl->macDropCount); -#endif - Dtls13EpochCopyKeys(ssl, e, &ssl->keys, side); if (!e->isValid) { @@ -2691,13 +2682,20 @@ int Dtls13CheckAEADFailLimit(WOLFSSL* ssl) WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR); return DECRYPT_ERROR; } - if (w64GT(ssl->macDropCount, hardLimit)) { + if (ssl->dtls13DecryptEpoch == NULL) { + WOLFSSL_MSG("Dtls13CheckAEADFailLimit: ssl->dtls13DecryptEpoch should " + "not be NULL"); + WOLFSSL_ERROR_VERBOSE(BAD_STATE_E); + return BAD_STATE_E; + } + w64Increment(&ssl->dtls13DecryptEpoch->dropCount); + if (w64GT(ssl->dtls13DecryptEpoch->dropCount, hardLimit)) { /* We have reached the hard limit for failed decryptions. */ WOLFSSL_MSG("Connection exceeded hard AEAD limit"); WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR); return DECRYPT_ERROR; } - else if (w64GT(ssl->macDropCount, keyUpdateLimit)) { + else if (w64GT(ssl->dtls13DecryptEpoch->dropCount, keyUpdateLimit)) { WOLFSSL_MSG("Connection exceeded key update limit. Issuing key update"); /* If not waiting for a response then request a key update. */ if (!ssl->keys.updateResponseReq) { diff --git a/src/internal.c b/src/internal.c index 635a3b917..77b7a7414 100644 --- a/src/internal.c +++ b/src/internal.c @@ -18206,7 +18206,6 @@ int DoApplicationData(WOLFSSL* ssl, byte* input, word32* inOutIdx, int sniff) Dtls13SetOlderEpochSide(ssl, ssl->dtls13InvalidateBefore, ENCRYPT_SIDE_ONLY); w64Zero(&ssl->dtls13InvalidateBefore); - w64Zero(&ssl->macDropCount); } } #endif @@ -18799,15 +18798,14 @@ static WC_INLINE int VerifyMac(WOLFSSL* ssl, const byte* input, word32 msgSz, static int HandleDTLSDecryptFailed(WOLFSSL* ssl) { int ret = 0; -#if defined(WOLFSSL_DTLS_DROP_STATS) || \ - (defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS)) - w64Increment(&ssl->macDropCount); +#ifdef WOLFSSL_DTLS_DROP_STATS + ssl->macDropCount++; +#endif #if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) /* Handle AEAD limits specified by the RFC for failed decryption */ if (IsAtLeastTLSv1_3(ssl->version)) ret = Dtls13CheckAEADFailLimit(ssl); -#endif #endif (void)ssl; diff --git a/src/ssl.c b/src/ssl.c index 27c988cb0..4d7bb73ce 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 = w64GetLow32(ssl->macDropCount); + *macDropCount = ssl->macDropCount; if (replayDropCount != NULL) *replayDropCount = ssl->replayDropCount; } @@ -3284,11 +3284,6 @@ int wolfSSL_Rehandshake(WOLFSSL* ssl) /* CLIENT/SERVER: Reset peer authentication for full secure handshake. */ ssl->options.peerAuthGood = 0; -#ifdef WOLFSSL_DTLS_DROP_STATS - if (ssl->options.dtls) - w64Zero(&ssl->macDropCount); -#endif - #ifdef HAVE_SESSION_TICKET if (ret == WOLFSSL_SUCCESS) #endif diff --git a/tests/api.c b/tests/api.c index e320e9ad6..a4753972b 100644 --- a/tests/api.c +++ b/tests/api.c @@ -55381,7 +55381,7 @@ static void test_AEAD_limit_client(WOLFSSL* ssl) test_AEAD_get_limits(ssl, &hardLimit, &keyUpdateLimit, &sendLimit); w64Zero(&counter); - AssertTrue(w64Equal(ssl->macDropCount, counter)); + AssertTrue(w64Equal(Dtls13GetEpoch(ssl, ssl->dtls13Epoch)->dropCount, counter)); wolfSSL_SSLSetIORecv(ssl, test_AEAD_cbiorecv); @@ -55392,12 +55392,12 @@ static void test_AEAD_limit_client(WOLFSSL* ssl) ret = wolfSSL_read(ssl, msgBuf, sizeof(msgBuf)); /* Should succeed since decryption failures are dropped */ AssertIntGT(ret, 0); - AssertTrue(w64Equal(ssl->macDropCount, counter)); + AssertTrue(w64Equal(Dtls13GetEpoch(ssl, ssl->dtls13PeerEpoch)->dropCount, counter)); } test_AEAD_fail_decryption = 1; - ssl->macDropCount = keyUpdateLimit; - w64Increment(&ssl->macDropCount); + Dtls13GetEpoch(ssl, ssl->dtls13PeerEpoch)->dropCount = keyUpdateLimit; + w64Increment(&Dtls13GetEpoch(ssl, ssl->dtls13PeerEpoch)->dropCount); /* 100 read calls should be enough to complete the key update */ w64Zero(&counter); for (i = 0; i < 100; i++) { @@ -55406,7 +55406,7 @@ static void test_AEAD_limit_client(WOLFSSL* ssl) AssertIntGT(ret, 0); /* Epoch after one key update is 4 */ if (w64Equal(ssl->dtls13PeerEpoch, w64From32(0, 4)) && - w64Equal(ssl->macDropCount, counter)) { + w64Equal(Dtls13GetEpoch(ssl, ssl->dtls13PeerEpoch)->dropCount, counter)) { didReKey = 1; break; } @@ -55415,9 +55415,7 @@ static void test_AEAD_limit_client(WOLFSSL* ssl) if (!w64IsZero(sendLimit)) { /* Test the sending limit for AEAD ciphers */ - Dtls13Epoch* e = Dtls13GetEpoch(ssl, ssl->dtls13Epoch); - AssertNotNull(e); - e->nextSeqNumber = sendLimit; + Dtls13GetEpoch(ssl, ssl->dtls13Epoch)->nextSeqNumber = sendLimit; test_AEAD_seq_num = 1; ret = wolfSSL_write(ssl, msgBuf, sizeof(msgBuf)); AssertIntGT(ret, 0); @@ -55430,7 +55428,7 @@ static void test_AEAD_limit_client(WOLFSSL* ssl) AssertIntGT(ret, 0); /* Epoch after another key update is 5 */ if (w64Equal(ssl->dtls13Epoch, w64From32(0, 5)) && - w64Equal(ssl->macDropCount, counter)) { + w64Equal(Dtls13GetEpoch(ssl, ssl->dtls13Epoch)->dropCount, counter)) { didReKey = 1; break; } @@ -55439,8 +55437,8 @@ static void test_AEAD_limit_client(WOLFSSL* ssl) } test_AEAD_fail_decryption = 2; - ssl->macDropCount = hardLimit; - w64Decrement(&ssl->macDropCount); + Dtls13GetEpoch(ssl, ssl->dtls13PeerEpoch)->dropCount = hardLimit; + w64Decrement(&Dtls13GetEpoch(ssl, ssl->dtls13PeerEpoch)->dropCount); /* Connection should fail with a DECRYPT_ERROR */ ret = wolfSSL_read(ssl, msgBuf, sizeof(msgBuf)); AssertIntEQ(ret, WOLFSSL_FATAL_ERROR); diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 54db08cf5..49e4c7779 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -4651,6 +4651,10 @@ typedef struct Dtls13Epoch { w64wrapper nextSeqNumber; w64wrapper nextPeerSeqNumber; +#ifndef WOLFSSL_TLS13_IGNORE_AEAD_LIMITS + w64wrapper dropCount; /* Amount of records that failed decryption */ +#endif + word32 window[WOLFSSL_DTLS_WINDOW_WORDS]; /* key material for the epoch */ @@ -4912,10 +4916,8 @@ struct WOLFSSL { #ifdef WOLFSSL_MULTICAST void* mcastHwCbCtx; /* Multicast highwater callback ctx */ #endif /* WOLFSSL_MULTICAST */ -#if defined(WOLFSSL_DTLS_DROP_STATS) || defined(WOLFSSL_DTLS13) - w64wrapper macDropCount; -#endif #ifdef WOLFSSL_DTLS_DROP_STATS + word32 macDropCount; word32 replayDropCount; #endif /* WOLFSSL_DTLS_DROP_STATS */ #ifdef WOLFSSL_SRTP From bf4be901e435ae7c952cb696f51d567d7f3f3fcf Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 16 Sep 2022 11:28:29 +0200 Subject: [PATCH 7/8] Add prototypes for w64wrapper functions --- wolfssl/wolfcrypt/misc.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/wolfssl/wolfcrypt/misc.h b/wolfssl/wolfcrypt/misc.h index 8f8fc1764..74d249c8d 100644 --- a/wolfssl/wolfcrypt/misc.h +++ b/wolfssl/wolfcrypt/misc.h @@ -130,6 +130,24 @@ WOLFSSL_LOCAL void ctMaskCopy(byte mask, byte* dst, byte* src, word16 size); WOLFSSL_LOCAL word32 MakeWordFromHash(const byte* hashID); WOLFSSL_LOCAL word32 HashObject(const byte* o, word32 len, int* error); +WOLFSSL_LOCAL void w64Increment(w64wrapper *n); +WOLFSSL_LOCAL void w64Decrement(w64wrapper *n); +WOLFSSL_LOCAL byte w64Equal(w64wrapper a, w64wrapper b); +WOLFSSL_LOCAL word32 w64GetLow32(w64wrapper n); +WOLFSSL_LOCAL word32 w64GetHigh32(w64wrapper n); +WOLFSSL_LOCAL void w64SetLow32(w64wrapper *n, word32 low); +WOLFSSL_LOCAL w64wrapper w64Add32(w64wrapper a, word32 b, byte *wrap); +WOLFSSL_LOCAL w64wrapper w64Sub32(w64wrapper a, word32 b, byte *wrap); +WOLFSSL_LOCAL byte w64GT(w64wrapper a, w64wrapper b); +WOLFSSL_LOCAL byte w64IsZero(w64wrapper a); +WOLFSSL_LOCAL void c64toa(const w64wrapper *a, byte *out); +WOLFSSL_LOCAL void ato64(const byte *in, w64wrapper *w64); +WOLFSSL_LOCAL w64wrapper w64From32(word32 hi, word32 lo); +WOLFSSL_LOCAL byte w64GTE(w64wrapper a, w64wrapper b); +WOLFSSL_LOCAL byte w64LT(w64wrapper a, w64wrapper b); +WOLFSSL_LOCAL w64wrapper w64Sub(w64wrapper a, w64wrapper b); +WOLFSSL_LOCAL void w64Zero(w64wrapper *a); + #endif /* NO_INLINE */ From 23e9cf2dc1de1cbc1da6fb2a15ac20dbcc15d44b Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 19 Sep 2022 10:30:29 +0200 Subject: [PATCH 8/8] Test AEAD limits only without WOLFSSL_TLS13_IGNORE_AEAD_LIMITS --- tests/api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/api.c b/tests/api.c index a4753972b..3c0e8fccf 100644 --- a/tests/api.c +++ b/tests/api.c @@ -55308,7 +55308,7 @@ static int test_wolfSSL_dtls_fragments(void) { } #endif -#ifdef WOLFSSL_DTLS13 +#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) static byte test_AEAD_fail_decryption = 0; static byte test_AEAD_seq_num = 0; static byte test_AEAD_done = 0;