TLS 1.3: Check maximum records encrypted with one key set

This commit is contained in:
Juliusz Sosinowicz
2022-09-12 16:50:48 +02:00
parent 4e9106c355
commit 63ba2f7b8f
6 changed files with 164 additions and 26 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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 */

View File

@ -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);

View File

@ -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)

View File

@ -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