Skip to content

Commit 6a77706

Browse files
committed
TLS 1.3: AEAD limit fixed
Values were 16-bit each when they are 32-bit each. Add tests for KeyUpdate limits for TLS 1.3.
1 parent 7827872 commit 6a77706

3 files changed

Lines changed: 223 additions & 4 deletions

File tree

tests/api/test_tls13.c

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6189,3 +6189,212 @@ int test_tls13_cipher_fuzz_aes128_ccm_8_sha256(void)
61896189
#endif
61906190
return EXPECT_RESULT();
61916191
}
6192+
6193+
/* Regression test for the AEAD record-protection limit constants in
6194+
* internal.h. The macros expand to w64From32(hi, lo). A prior version split
6195+
* the intended 32-bit constants into 16-bit halves and passed each half as
6196+
* a separate 32-bit argument, producing a 64-bit value many orders of
6197+
* magnitude larger than RFC 8446 / RFC 9147 require. That made
6198+
* CheckTLS13AEADSendLimit's key-update trigger effectively unreachable.
6199+
* Compare against the hard-coded spec values so a recurrence is caught even
6200+
* if the macro is reused on both sides of the comparison. */
6201+
int test_tls13_AEAD_limit_macros(void)
6202+
{
6203+
EXPECT_DECLS;
6204+
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS)
6205+
w64wrapper limit;
6206+
6207+
/* RFC 8446 5.5: 2^24.5 ~= 23726566 (0x016A09E6). */
6208+
limit = AEAD_AES_LIMIT;
6209+
ExpectIntEQ(w64GetHigh32(limit), 0);
6210+
ExpectIntEQ(w64GetLow32(limit), 0x016A09E6);
6211+
6212+
#ifdef WOLFSSL_DTLS13
6213+
/* RFC 9147 (AES-CCM integrity): 2^23.5 ~= 11863283 (0x00B504F3). */
6214+
limit = DTLS_AEAD_AES_CCM_FAIL_LIMIT;
6215+
ExpectIntEQ(w64GetHigh32(limit), 0);
6216+
ExpectIntEQ(w64GetLow32(limit), 0x00B504F3);
6217+
6218+
/* Key-update threshold is half the fail limit: 5931641 (0x005A8279). */
6219+
limit = DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT;
6220+
ExpectIntEQ(w64GetHigh32(limit), 0);
6221+
ExpectIntEQ(w64GetLow32(limit), 0x005A8279);
6222+
#endif
6223+
#endif
6224+
return EXPECT_RESULT();
6225+
}
6226+
6227+
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
6228+
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
6229+
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
6230+
/* Drive the client's encrypt sequence number towards the spec limit for
6231+
* `suite` and verify CheckTLS13AEADSendLimit's KeyUpdate trigger fires at
6232+
* exactly the right boundary.
6233+
*
6234+
* Two writes are exercised:
6235+
* 1. Counter set to limit - 2. After the write the counter must read
6236+
* limit - 1 (record incremented it by 1) and no KeyUpdate must have
6237+
* been emitted. CheckTLS13AEADSendLimit uses `seq >= limit`, so neither
6238+
* the pre-send check nor the trailing loop check (which runs once more
6239+
* after the last record before wolfSSL_write exits) is allowed to fire.
6240+
* 2. A second write follows with the counter already sitting at limit - 1
6241+
* from the previous record. The user record goes out at seq = limit-1,
6242+
* which bumps the counter to limit; the trailing limit check then
6243+
* fires SendTls13KeyUpdate. SetKeysSide zeroes the encrypt counter, so
6244+
* the post-write counter is 0.
6245+
*
6246+
* With the previous broken AEAD-limit macros the limit was unreachable, no
6247+
* KeyUpdate would ever fire, and the counter would simply advance to
6248+
* limit_lo + 1 in the second case instead of being reset.
6249+
*
6250+
* The AEAD nonce mixes in the record sequence number on both sides, so the
6251+
* server's decrypt counter has to be advanced in lockstep with the client's
6252+
* encrypt counter or the record fails the integrity check. */
6253+
static int test_tls13_AEAD_limit_triggers_KeyUpdate_cs(const char* suite,
6254+
word32 limit_hi, word32 limit_lo, int expected_bulk_cipher)
6255+
{
6256+
EXPECT_DECLS;
6257+
struct test_memio_ctx test_ctx;
6258+
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
6259+
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
6260+
const char msg[] = "post-limit-record";
6261+
char buf[sizeof(msg)];
6262+
int written;
6263+
6264+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
6265+
test_ctx.c_ciphers = suite;
6266+
test_ctx.s_ciphers = suite;
6267+
6268+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
6269+
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
6270+
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
6271+
6272+
if (EXPECT_SUCCESS() && ssl_c != NULL && ssl_s != NULL) {
6273+
/* Sanity check: the negotiated bulk cipher matches what the caller
6274+
* intends to exercise. If a build flag combination falls through to
6275+
* a different suite, the limit constant would be wrong. */
6276+
ExpectIntEQ(ssl_c->specs.bulk_cipher_algorithm, expected_bulk_cipher);
6277+
6278+
/* Stage the counters two below the limit so the first write stays
6279+
* comfortably below the trigger threshold. */
6280+
ssl_c->keys.sequence_number_hi = limit_hi;
6281+
ssl_c->keys.sequence_number_lo = limit_lo - 2;
6282+
ssl_s->keys.peer_sequence_number_hi = limit_hi;
6283+
ssl_s->keys.peer_sequence_number_lo = limit_lo - 2;
6284+
}
6285+
6286+
/* First write: below the limit, no KeyUpdate expected. */
6287+
written = wolfSSL_write(ssl_c, msg, (int)sizeof(msg));
6288+
ExpectIntEQ(written, (int)sizeof(msg));
6289+
6290+
if (EXPECT_SUCCESS() && ssl_c != NULL) {
6291+
/* The record bumped the counter from limit-2 to limit-1. A
6292+
* KeyUpdate would have zeroed it via SetKeysSide and bumped to 1. */
6293+
ExpectIntEQ((int)ssl_c->keys.sequence_number_hi, (int)limit_hi);
6294+
ExpectIntEQ(ssl_c->keys.sequence_number_lo, limit_lo - 1);
6295+
}
6296+
6297+
/* Server consumes the below-limit record with its existing keys. */
6298+
XMEMSET(buf, 0, sizeof(buf));
6299+
ExpectIntEQ(wolfSSL_read(ssl_s, buf, (int)sizeof(buf)), (int)sizeof(msg));
6300+
ExpectIntEQ(XMEMCMP(buf, msg, sizeof(msg)), 0);
6301+
6302+
/* Second write: the client's counter is now at limit-1. Sending this
6303+
* record will push it to limit, at which point the trailing check
6304+
* inside SendData's loop fires SendTls13KeyUpdate. No manual counter
6305+
* adjustment is needed -- the counter is allowed to "naturally" reach
6306+
* the limit through the previous send. */
6307+
written = wolfSSL_write(ssl_c, msg, (int)sizeof(msg));
6308+
ExpectIntEQ(written, (int)sizeof(msg));
6309+
6310+
if (EXPECT_SUCCESS() && ssl_c != NULL) {
6311+
/* SendTls13KeyUpdate -> DeriveTls13Keys -> SetKeysSide zeroes the
6312+
* encrypt sequence number. The user record went out before the
6313+
* trigger fired, so no record was sent on the new keys. */
6314+
ExpectIntEQ((int)ssl_c->keys.sequence_number_hi, 0);
6315+
ExpectIntEQ((int)ssl_c->keys.sequence_number_lo, 0);
6316+
}
6317+
6318+
/* The server reads the user record (sent under the pre-update keys at
6319+
* seq = limit - 1) before it sees the KeyUpdate record. The KeyUpdate
6320+
* is consumed transparently on a subsequent read; for the test we just
6321+
* need to confirm the user data round-trips. */
6322+
XMEMSET(buf, 0, sizeof(buf));
6323+
{
6324+
int r = -1, attempts;
6325+
for (attempts = 0; attempts < 5; attempts++) {
6326+
r = wolfSSL_read(ssl_s, buf, (int)sizeof(buf));
6327+
if (r > 0)
6328+
break;
6329+
if (wolfSSL_get_error(ssl_s, r) != WOLFSSL_ERROR_WANT_READ)
6330+
break;
6331+
}
6332+
ExpectIntEQ(r, (int)sizeof(msg));
6333+
}
6334+
ExpectIntEQ(XMEMCMP(buf, msg, sizeof(msg)), 0);
6335+
6336+
wolfSSL_free(ssl_c);
6337+
wolfSSL_free(ssl_s);
6338+
wolfSSL_CTX_free(ctx_c);
6339+
wolfSSL_CTX_free(ctx_s);
6340+
6341+
return EXPECT_RESULT();
6342+
}
6343+
#endif
6344+
6345+
int test_tls13_AEAD_limit_KU_aes128_gcm_sha256(void)
6346+
{
6347+
EXPECT_DECLS;
6348+
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
6349+
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
6350+
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
6351+
defined(BUILD_TLS_AES_128_GCM_SHA256)
6352+
ExpectIntEQ(test_tls13_AEAD_limit_triggers_KeyUpdate_cs(
6353+
"TLS13-AES128-GCM-SHA256", 0, 0x016A09E6, wolfssl_aes_gcm),
6354+
TEST_SUCCESS);
6355+
#endif
6356+
return EXPECT_RESULT();
6357+
}
6358+
6359+
int test_tls13_AEAD_limit_KU_aes256_gcm_sha384(void)
6360+
{
6361+
EXPECT_DECLS;
6362+
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
6363+
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
6364+
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
6365+
defined(BUILD_TLS_AES_256_GCM_SHA384)
6366+
ExpectIntEQ(test_tls13_AEAD_limit_triggers_KeyUpdate_cs(
6367+
"TLS13-AES256-GCM-SHA384", 0, 0x016A09E6, wolfssl_aes_gcm),
6368+
TEST_SUCCESS);
6369+
#endif
6370+
return EXPECT_RESULT();
6371+
}
6372+
6373+
int test_tls13_AEAD_limit_KU_aes128_ccm_sha256(void)
6374+
{
6375+
EXPECT_DECLS;
6376+
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
6377+
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
6378+
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
6379+
defined(BUILD_TLS_AES_128_CCM_SHA256)
6380+
ExpectIntEQ(test_tls13_AEAD_limit_triggers_KeyUpdate_cs(
6381+
"TLS13-AES128-CCM-SHA256", 0, 0x016A09E6, wolfssl_aes_ccm),
6382+
TEST_SUCCESS);
6383+
#endif
6384+
return EXPECT_RESULT();
6385+
}
6386+
6387+
int test_tls13_AEAD_limit_KU_aes128_ccm_8_sha256(void)
6388+
{
6389+
EXPECT_DECLS;
6390+
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
6391+
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
6392+
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
6393+
defined(BUILD_TLS_AES_128_CCM_8_SHA256)
6394+
ExpectIntEQ(test_tls13_AEAD_limit_triggers_KeyUpdate_cs(
6395+
"TLS13-AES128-CCM-8-SHA256", 0, 0x016A09E6, wolfssl_aes_ccm),
6396+
TEST_SUCCESS);
6397+
#endif
6398+
return EXPECT_RESULT();
6399+
}
6400+

tests/api/test_tls13.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ int test_tls13_cipher_fuzz_aes256_gcm_sha384(void);
7474
int test_tls13_cipher_fuzz_chacha20_poly1305_sha256(void);
7575
int test_tls13_cipher_fuzz_aes128_ccm_sha256(void);
7676
int test_tls13_cipher_fuzz_aes128_ccm_8_sha256(void);
77+
int test_tls13_AEAD_limit_macros(void);
78+
int test_tls13_AEAD_limit_KU_aes128_gcm_sha256(void);
79+
int test_tls13_AEAD_limit_KU_aes256_gcm_sha384(void);
80+
int test_tls13_AEAD_limit_KU_aes128_ccm_sha256(void);
81+
int test_tls13_AEAD_limit_KU_aes128_ccm_8_sha256(void);
7782

7883
#define TEST_TLS13_DECLS \
7984
TEST_DECL_GROUP("tls13", test_tls13_apis), \
@@ -125,6 +130,11 @@ int test_tls13_cipher_fuzz_aes128_ccm_8_sha256(void);
125130
TEST_DECL_GROUP("tls13", test_tls13_cipher_fuzz_aes256_gcm_sha384), \
126131
TEST_DECL_GROUP("tls13", test_tls13_cipher_fuzz_chacha20_poly1305_sha256), \
127132
TEST_DECL_GROUP("tls13", test_tls13_cipher_fuzz_aes128_ccm_sha256), \
128-
TEST_DECL_GROUP("tls13", test_tls13_cipher_fuzz_aes128_ccm_8_sha256)
133+
TEST_DECL_GROUP("tls13", test_tls13_cipher_fuzz_aes128_ccm_8_sha256), \
134+
TEST_DECL_GROUP("tls13", test_tls13_AEAD_limit_macros), \
135+
TEST_DECL_GROUP("tls13", test_tls13_AEAD_limit_KU_aes128_gcm_sha256), \
136+
TEST_DECL_GROUP("tls13", test_tls13_AEAD_limit_KU_aes256_gcm_sha384), \
137+
TEST_DECL_GROUP("tls13", test_tls13_AEAD_limit_KU_aes128_ccm_sha256), \
138+
TEST_DECL_GROUP("tls13", test_tls13_AEAD_limit_KU_aes128_ccm_8_sha256)
129139

130140
#endif /* WOLFCRYPT_TEST_TLS13_H */

wolfssl/internal.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,7 +1419,7 @@ enum {
14191419
/* Limit is 2^24.5
14201420
* https://www.rfc-editor.org/rfc/rfc8446#section-5.5
14211421
* Without the fraction is 23726566 (0x016A09E6) */
1422-
#define AEAD_AES_LIMIT w64From32(0x016A, 0x09E6)
1422+
#define AEAD_AES_LIMIT w64From32(0, 0x016A09E6)
14231423
/* Limit is 2^23
14241424
* https://www.rfc-editor.org/rfc/rfc9147.html#name-integrity-limits */
14251425
#define DTLS_AEAD_AES_CCM_LIMIT w64From32(0, 1 << 22)
@@ -1436,8 +1436,8 @@ enum {
14361436
* https://www.rfc-editor.org/rfc/rfc9147.html#name-integrity-limits
14371437
* Without the fraction is 11863283 (0x00B504F3)
14381438
* Half of this value is 5931641 (0x005A8279) */
1439-
#define DTLS_AEAD_AES_CCM_FAIL_LIMIT w64From32(0x00B5, 0x04F3)
1440-
#define DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT w64From32(0x005A, 0x8279)
1439+
#define DTLS_AEAD_AES_CCM_FAIL_LIMIT w64From32(0, 0x00B504F3)
1440+
#define DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT w64From32(0, 0x005A8279)
14411441

14421442
/* Limit is (2^22 - 1) full messages [2^36 - 31 octets]
14431443
* https://www.rfc-editor.org/rfc/rfc8998.html#name-aead_sm4_gcm

0 commit comments

Comments
 (0)