From 599eec673e1e26957775e0f2f92ab24f22127d46 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Mon, 23 Feb 2026 15:30:33 +0000 Subject: [PATCH] Fix ImportKeyState wordAdj always-zero bug in DTLS session import In ImportKeyState(), wordAdj was always zero because it was computed after clamping wordCount, and the subtraction direction was reversed. This caused misaligned parsing of all subsequent fields when importing state from a peer compiled with a larger WOLFSSL_DTLS_WINDOW_WORDS. Fix both window and prevWindow blocks to compute the adjustment before clamping, with the correct subtraction direction. Add test that imports a state buffer with wordCount > WOLFSSL_DTLS_WINDOW_WORDS to verify the fix. --- src/internal.c | 4 +- tests/api.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/src/internal.c b/src/internal.c index eb59f8133c..2017fc9977 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1123,8 +1123,8 @@ static int ImportKeyState(WOLFSSL* ssl, const byte* exp, word32 len, byte ver, idx += OPAQUE16_LEN; if (wordCount > WOLFSSL_DTLS_WINDOW_WORDS) { + wordAdj = (wordCount - WOLFSSL_DTLS_WINDOW_WORDS) * sizeof(word32); wordCount = WOLFSSL_DTLS_WINDOW_WORDS; - wordAdj = (WOLFSSL_DTLS_WINDOW_WORDS - wordCount) * sizeof(word32); } XMEMSET(keys->peerSeq[0].window, 0xFF, DTLS_SEQ_SZ); @@ -1139,8 +1139,8 @@ static int ImportKeyState(WOLFSSL* ssl, const byte* exp, word32 len, byte ver, idx += OPAQUE16_LEN; if (wordCount > WOLFSSL_DTLS_WINDOW_WORDS) { + wordAdj = (wordCount - WOLFSSL_DTLS_WINDOW_WORDS) * sizeof(word32); wordCount = WOLFSSL_DTLS_WINDOW_WORDS; - wordAdj = (WOLFSSL_DTLS_WINDOW_WORDS - wordCount) * sizeof(word32); } XMEMSET(keys->peerSeq[0].prevWindow, 0xFF, DTLS_SEQ_SZ); diff --git a/tests/api.c b/tests/api.c index d431bf875b..730b3e5897 100644 --- a/tests/api.c +++ b/tests/api.c @@ -9172,6 +9172,117 @@ static int test_wolfSSL_dtls_export_peers(void) return EXPECT_RESULT(); } +/* Test that ImportKeyState correctly skips extra window words when importing + * state from a peer compiled with a larger WOLFSSL_DTLS_WINDOW_WORDS. */ +static int test_wolfSSL_dtls_import_state_extra_window_words(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_SESSION_EXPORT) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + unsigned int stateSz = 0; + byte* state = NULL; + byte* modified = NULL; + unsigned int modifiedSz; + word16 origKeyLen; + word16 origTotalLen; + /* Offset from start of key state data to the first wordCount field. + * Layout: 4 sequence numbers (16 bytes) + DTLS-specific fields (42 bytes) + + * encryptSz(4) + padSz(4) + encryptionOn(1) + decryptedCur(1) = 68 */ + const int keyStateWindowOffset = 68; + /* Buffer header: 2 proto + 2 total_len + 2 key_len = 6 */ + const int headerSz = 6; + int idx, modIdx; + int extraPerWindow = 2 * (int)sizeof(word32); /* 8 bytes extra per window */ + int totalExtra = extraPerWindow * 2; /* 16 bytes extra total */ + + /* Create DTLS context and SSL object */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfDTLSv1_2_server_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* Get required buffer size and export state-only */ + ExpectIntEQ(wolfSSL_dtls_export_state_only(ssl, NULL, &stateSz), 0); + ExpectIntGT((int)stateSz, 0); + state = (byte*)XMALLOC(stateSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + ExpectNotNull(state); + ExpectIntGT(wolfSSL_dtls_export_state_only(ssl, state, &stateSz), 0); + + /* Build a modified buffer that simulates a peer with + * WOLFSSL_DTLS_WINDOW_WORDS = WOLFSSL_DTLS_WINDOW_WORDS + 2. + * Each window section gets 2 extra word32 values (8 bytes). + * Two windows => 16 extra bytes total. */ + modifiedSz = stateSz + totalExtra; + modified = (byte*)XMALLOC(modifiedSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + ExpectNotNull(modified); + + if (EXPECT_SUCCESS()) { + int windowWords = WOLFSSL_DTLS_WINDOW_WORDS; + int windowDataSz = windowWords * (int)sizeof(word32); + + XMEMSET(modified, 0, modifiedSz); + + /* Copy protocol/version bytes (first 2 bytes) */ + XMEMCPY(modified, state, 2); + + /* Read original total length and key state length */ + ato16(state + 2, &origTotalLen); + ato16(state + 4, &origKeyLen); + + /* Write updated total length and key state length */ + c16toa((word16)(origTotalLen + totalExtra), modified + 2); + c16toa((word16)(origKeyLen + totalExtra), modified + 4); + + /* Copy key state data up to first window section */ + idx = headerSz; + modIdx = headerSz; + XMEMCPY(modified + modIdx, state + idx, keyStateWindowOffset); + idx += keyStateWindowOffset; + modIdx += keyStateWindowOffset; + + /* First window: write increased wordCount */ + c16toa((word16)(windowWords + 2), modified + modIdx); + idx += OPAQUE16_LEN; + modIdx += OPAQUE16_LEN; + + /* Copy original window data */ + XMEMCPY(modified + modIdx, state + idx, windowDataSz); + idx += windowDataSz; + modIdx += windowDataSz; + + /* Insert 2 extra word32 padding values */ + XMEMSET(modified + modIdx, 0, extraPerWindow); + modIdx += extraPerWindow; + + /* Second window (prevWindow): same transformation */ + c16toa((word16)(windowWords + 2), modified + modIdx); + idx += OPAQUE16_LEN; + modIdx += OPAQUE16_LEN; + + XMEMCPY(modified + modIdx, state + idx, windowDataSz); + idx += windowDataSz; + modIdx += windowDataSz; + + XMEMSET(modified + modIdx, 0, extraPerWindow); + modIdx += extraPerWindow; + + /* Copy remainder of key state (after both windows) */ + XMEMCPY(modified + modIdx, state + idx, stateSz - idx); + } + + /* Import the modified state - should succeed with the fix */ + wolfSSL_free(ssl); + ssl = NULL; + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntGT(wolfSSL_dtls_import(ssl, modified, modifiedSz), 0); + + XFREE(state, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(modified, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + static int test_wolfSSL_UseTrustedCA(void) { EXPECT_DECLS; @@ -32913,6 +33024,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_tls_export), #endif TEST_DECL(test_wolfSSL_dtls_export_peers), + TEST_DECL(test_wolfSSL_dtls_import_state_extra_window_words), TEST_DECL(test_wolfSSL_SetMinVersion), TEST_DECL(test_wolfSSL_CTX_SetMinVersion),