Skip to content

Commit 8f41e29

Browse files
committed
F-11440: carry the full 64-bit ESP replay bitmap through the persistence callbacks
The replay window is 64 bits (ESP_REPLAY_WIN) but the state persistence callbacks accepted and returned only a uint32_t bitmap: esp_state_save dropped bits 32..63 and esp_state_restore reconstituted only the lower half. A duplicate represented by a discarded upper-half bit was therefore accepted after a restore, still inside the active replay window (RFC 4303 s3.4.3). widen the callback API (wolfIP_esp_state_write_cb/read_cb) to uint64_t and use it in esp_state_restore. wolfIP is pre-1.0, so the breaking typedef change lands now rather than a second callback pair. Unit test (test_esp_state_persistence_keeps_64bit_bitmap): SA with a window whose bitmap has bit 40 set; delete (save event) must hand the write callback the full 64-bit value, and recreating the SA must restore it so the upper-half duplicate (seq 60) is rejected. Fails pre-fix: the bitmap arrives truncated to 1.
1 parent 729b484 commit 8f41e29

3 files changed

Lines changed: 104 additions & 6 deletions

File tree

src/test/unit/unit_esp.c

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2236,7 +2236,7 @@ static int state_write_calls = 0;
22362236
static uint32_t state_last_oseq = 0;
22372237

22382238
static 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

22502250
static 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)
22732273
static uint8_t state_fail_spi[ESP_SPI_LEN] = {0x99, 0x88, 0x77, 0x66};
22742274

22752275
static 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
}
23202320
END_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+
23222415
START_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 */

src/wolfesp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ esp_state_restore(wolfIP_esp_sa *sa)
6161
{
6262
uint32_t oseq;
6363
uint32_t hi_seq;
64-
uint32_t bitmap;
64+
uint64_t bitmap;
6565

6666
if (esp_state_read_cb) {
6767
oseq = sa->replay.oseq;

wolfesp.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,19 @@ typedef struct wolfIP_esp_sa wolfIP_esp_sa;
109109
* The read callback is invoked when a new SA is created, keyed by SPI.
110110
* It may fill the (fresh) oseq/hi_seq/bitmap values with persisted state.
111111
* Return 0 if state was restored, anything else to start the SA fresh.
112+
*
113+
* The bitmap is the full 64-bit inbound replay window
114+
* (ESP_REPLAY_WIN); persisting only half of it lets a duplicate in the
115+
* upper half of the window be accepted after a restore.
112116
* */
113117
typedef int (*wolfIP_esp_state_write_cb)(const uint8_t *spi,
114118
uint32_t oseq,
115119
uint32_t hi_seq,
116-
uint32_t bitmap);
120+
uint64_t bitmap);
117121
typedef int (*wolfIP_esp_state_read_cb)(const uint8_t *spi,
118122
uint32_t *oseq,
119123
uint32_t *hi_seq,
120-
uint32_t *bitmap);
124+
uint64_t *bitmap);
121125
int wolfIP_esp_state_set_cbs(wolfIP_esp_state_write_cb write,
122126
wolfIP_esp_state_read_cb read);
123127

0 commit comments

Comments
 (0)