@@ -2236,7 +2236,7 @@ static int state_write_calls = 0;
22362236static uint32_t state_last_oseq = 0 ;
22372237
22382238static int state_test_read_cb (const uint8_t * spi , uint32_t * oseq ,
2239- uint32_t * hi_seq , uint32_t * bitmap )
2239+ uint32_t * hi_seq , uint64_t * bitmap )
22402240{
22412241 state_read_calls ++ ;
22422242 if (memcmp (spi , state_test_spi , ESP_SPI_LEN ) == 0 ) {
@@ -2248,7 +2248,7 @@ static int state_test_read_cb(const uint8_t *spi, uint32_t *oseq,
22482248}
22492249
22502250static int state_test_write_cb (const uint8_t * spi , uint32_t oseq ,
2251- uint32_t hi_seq , uint32_t bitmap )
2251+ uint32_t hi_seq , uint64_t bitmap )
22522252{
22532253 state_write_calls ++ ;
22542254 if (memcmp (spi , state_test_spi , ESP_SPI_LEN ) == 0 ) {
@@ -2273,7 +2273,7 @@ state_test_wire_seq(const struct wolfIP_ip_packet *ip)
22732273static uint8_t state_fail_spi [ESP_SPI_LEN ] = {0x99 , 0x88 , 0x77 , 0x66 };
22742274
22752275static int state_fail_read_cb (const uint8_t * spi , uint32_t * oseq ,
2276- uint32_t * hi_seq , uint32_t * bitmap )
2276+ uint32_t * hi_seq , uint64_t * bitmap )
22772277{
22782278 if (memcmp (spi , state_fail_spi , ESP_SPI_LEN ) == 0 ) {
22792279 * oseq = 0xDEADBEEFU ;
@@ -2319,6 +2319,99 @@ START_TEST(test_esp_state_restore_failed_read_keeps_fresh_state)
23192319}
23202320END_TEST
23212321
2322+ /* F-11440: the persistence callbacks must carry the full 64-bit replay
2323+ * bitmap. The old uint32_t callback types truncated bits 32..63 on save
2324+ * and reconstituted only the lower half on restore, so a duplicate in
2325+ * the upper half of the window was accepted after a restore. */
2326+ static uint8_t state_64bit_spi [ESP_SPI_LEN ] = {0x55 , 0x44 , 0x33 , 0x22 };
2327+ static uint32_t state64_oseq = 0 ;
2328+ static uint32_t state64_hi_seq = 0 ;
2329+ static uint64_t state64_bitmap = 0 ;
2330+
2331+ static int state64_read_cb (const uint8_t * spi , uint32_t * oseq ,
2332+ uint32_t * hi_seq , uint64_t * bitmap )
2333+ {
2334+ if (memcmp (spi , state_64bit_spi , ESP_SPI_LEN ) == 0 ) {
2335+ * oseq = state64_oseq ;
2336+ * hi_seq = state64_hi_seq ;
2337+ * bitmap = state64_bitmap ;
2338+ return 0 ;
2339+ }
2340+ return -1 ; /* unknown SPI: start fresh */
2341+ }
2342+
2343+ static int state64_write_cb (const uint8_t * spi , uint32_t oseq ,
2344+ uint32_t hi_seq , uint64_t bitmap )
2345+ {
2346+ if (memcmp (spi , state_64bit_spi , ESP_SPI_LEN ) == 0 ) {
2347+ state64_oseq = oseq ;
2348+ state64_hi_seq = hi_seq ;
2349+ state64_bitmap = bitmap ;
2350+ }
2351+ return 0 ;
2352+ }
2353+
2354+ START_TEST (test_esp_state_persistence_keeps_64bit_bitmap )
2355+ {
2356+ int ret ;
2357+ wolfIP_esp_sa * esp_sa ;
2358+
2359+ esp_setup ();
2360+ state64_oseq = 0 ;
2361+ state64_hi_seq = 0 ;
2362+ state64_bitmap = 0 ;
2363+
2364+ ret = wolfIP_esp_state_set_cbs (state64_write_cb , state64_read_cb );
2365+ ck_assert_int_eq (ret , 0 );
2366+
2367+ ret = wolfIP_esp_sa_new_cbc_hmac (1 , state_64bit_spi ,
2368+ atoip4 (T_SRC ), atoip4 (T_DST ),
2369+ (uint8_t * )k_aes128 , sizeof (k_aes128 ),
2370+ ESP_AUTH_SHA256_RFC4868 ,
2371+ (uint8_t * )k_auth16 , sizeof (k_auth16 ),
2372+ ESP_ICVLEN_HMAC_128 );
2373+ ck_assert_int_eq (ret , 0 );
2374+ esp_sa = esp_sa_get (1 , state_64bit_spi );
2375+ ck_assert_ptr_nonnull (esp_sa );
2376+
2377+ /* A window with a set bit in the upper half of the bitmap:
2378+ * hi_seq 100, seq 60 already accepted (bit 100-60 = 40). */
2379+ esp_sa -> replay .oseq = 7 ;
2380+ esp_sa -> replay .hi_seq = 100 ;
2381+ esp_sa -> replay .bitmap = (1ULL << 40 ) | 1ULL ;
2382+
2383+ /* SA deletion is a save event: the write callback must receive the
2384+ * full 64-bit bitmap. */
2385+ wolfIP_esp_sa_del (1 , state_64bit_spi );
2386+ ck_assert_uint_eq (state64_bitmap , (1ULL << 40 ) | 1ULL );
2387+ ck_assert_uint_eq (state64_hi_seq , 100U );
2388+ ck_assert_uint_eq (state64_oseq , 7U );
2389+
2390+ /* Recreating the SA must restore the full window, upper half
2391+ * included. */
2392+ ret = wolfIP_esp_sa_new_cbc_hmac (1 , state_64bit_spi ,
2393+ atoip4 (T_SRC ), atoip4 (T_DST ),
2394+ (uint8_t * )k_aes128 , sizeof (k_aes128 ),
2395+ ESP_AUTH_SHA256_RFC4868 ,
2396+ (uint8_t * )k_auth16 , sizeof (k_auth16 ),
2397+ ESP_ICVLEN_HMAC_128 );
2398+ ck_assert_int_eq (ret , 0 );
2399+ esp_sa = esp_sa_get (1 , state_64bit_spi );
2400+ ck_assert_ptr_nonnull (esp_sa );
2401+ ck_assert_uint_eq (esp_sa -> replay .bitmap , (1ULL << 40 ) | 1ULL );
2402+ ck_assert_uint_eq (esp_sa -> replay .hi_seq , 100U );
2403+ ck_assert_uint_eq (esp_sa -> replay .oseq , 7U );
2404+
2405+ /* The restored window must actually reject the upper-half duplicate
2406+ * (seq 60) and still accept new sequences. */
2407+ ck_assert_int_ne (esp_replay_check (& esp_sa -> replay , 60U ), 0 );
2408+ ck_assert_int_eq (esp_replay_check (& esp_sa -> replay , 101U ), 0 );
2409+
2410+ wolfIP_esp_sa_del_all ();
2411+ wolfIP_esp_state_set_cbs (NULL , NULL );
2412+ }
2413+ END_TEST
2414+
23222415START_TEST (test_esp_state_persistence_callbacks )
23232416{
23242417 static uint8_t buf [LINK_MTU + 256 ];
@@ -2495,6 +2588,7 @@ static Suite *esp_suite(void)
24952588 tcase_add_test (tc , test_sa_del_all );
24962589 tcase_add_test (tc , test_esp_state_persistence_callbacks );
24972590 tcase_add_test (tc , test_esp_state_restore_failed_read_keeps_fresh_state );
2591+ tcase_add_test (tc , test_esp_state_persistence_keeps_64bit_bitmap );
24982592 suite_add_tcase (s , tc );
24992593
25002594 /* Replay window */
0 commit comments