Skip to content

Commit 356af77

Browse files
gasbytesdanielinux
authored andcommitted
update anti-replay window in the essp code after icv verification of a
packet
1 parent 1c4203c commit 356af77

2 files changed

Lines changed: 76 additions & 18 deletions

File tree

src/test/unit/unit_esp.c

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -424,8 +424,9 @@ START_TEST(test_replay_duplicate_rejected)
424424
{
425425
replay_t r;
426426
esp_replay_init(r);
427-
ck_assert_int_eq(esp_check_replay(&r, 5U), 0); /* first time: ok */
428-
ck_assert_int_ne(esp_check_replay(&r, 5U), 0); /* second time: replayed */
427+
ck_assert_int_eq(esp_check_replay(&r, 5U), 0);
428+
esp_replay_commit(&r, 5U); /* ICV passed */
429+
ck_assert_int_ne(esp_check_replay(&r, 5U), 0); /* second time: replayed */
429430
}
430431
END_TEST
431432

@@ -437,6 +438,7 @@ START_TEST(test_replay_multiple_in_window)
437438
esp_replay_init(r); /* window [1..32] */
438439
for (i = 1U; i <= 31U; i++) {
439440
ck_assert_int_eq(esp_check_replay(&r, i), 0);
441+
esp_replay_commit(&r, i);
440442
}
441443
}
442444
END_TEST
@@ -447,7 +449,8 @@ START_TEST(test_replay_below_window_rejected)
447449
replay_t r;
448450
esp_replay_init(r);
449451
/* Advance the window by receiving a high sequence number. */
450-
ck_assert_int_eq(esp_check_replay(&r, 64U), 0); /* hi_seq=64, seq_low=34 */
452+
ck_assert_int_eq(esp_check_replay(&r, 64U), 0);
453+
esp_replay_commit(&r, 64U); /* hi_seq=64, seq_low=34 */
451454
/* seq=1 is now below the window floor. */
452455
ck_assert_int_ne(esp_check_replay(&r, 1U), 0);
453456
}
@@ -459,6 +462,7 @@ START_TEST(test_replay_advance_hi_seq)
459462
replay_t r;
460463
esp_replay_init(r); /* hi_seq=32 */
461464
ck_assert_int_eq(esp_check_replay(&r, 33U), 0);
465+
esp_replay_commit(&r, 33U);
462466
ck_assert_uint_eq(r.hi_seq, 33U);
463467
}
464468
END_TEST
@@ -469,6 +473,7 @@ START_TEST(test_replay_advanced_hi_seq_duplicate_rejected)
469473
replay_t r;
470474
esp_replay_init(r); /* hi_seq=32 */
471475
ck_assert_int_eq(esp_check_replay(&r, 33U), 0);
476+
esp_replay_commit(&r, 33U);
472477
ck_assert_int_ne(esp_check_replay(&r, 33U), 0);
473478
}
474479
END_TEST
@@ -491,9 +496,12 @@ START_TEST(test_replay_jump_resets_bitmap)
491496
esp_replay_init(r);
492497
/* Accept some sequences so the bitmap has bits set. */
493498
ck_assert_int_eq(esp_check_replay(&r, 1U), 0);
499+
esp_replay_commit(&r, 1U);
494500
ck_assert_int_eq(esp_check_replay(&r, 2U), 0);
501+
esp_replay_commit(&r, 2U);
495502
/* Jump more than ESP_REPLAY_WIN (32) ahead. */
496503
ck_assert_int_eq(esp_check_replay(&r, 1000U), 0);
504+
esp_replay_commit(&r, 1000U);
497505
ck_assert_uint_eq(r.hi_seq, 1000U);
498506
/* seq=1 is now far outside the window. */
499507
ck_assert_int_ne(esp_check_replay(&r, 1U), 0);
@@ -507,12 +515,44 @@ START_TEST(test_replay_old_seqs_after_jump)
507515
replay_t r;
508516
esp_replay_init(r);
509517
ck_assert_int_eq(esp_check_replay(&r, 10U), 0);
510-
ck_assert_int_eq(esp_check_replay(&r, 500U), 0); /* jump > 32 */
518+
esp_replay_commit(&r, 10U);
519+
ck_assert_int_eq(esp_check_replay(&r, 500U), 0);
520+
esp_replay_commit(&r, 500U); /* jump > 32 */
511521
/* 10 is now well below the new window floor (500-31=469). */
512522
ck_assert_int_ne(esp_check_replay(&r, 10U), 0);
513523
}
514524
END_TEST
515525

526+
/* RFC 4303 s3.4.3: the replay window must not be updated until after
527+
* ICV verification succeeds. esp_check_replay must be read-only;
528+
* esp_replay_commit updates the window after ICV passes. */
529+
START_TEST(test_regression_replay_window_not_updated_before_icv)
530+
{
531+
replay_t r;
532+
replay_t saved;
533+
534+
esp_replay_init(r);
535+
536+
/* Accept a few packets to establish window state */
537+
ck_assert_int_eq(esp_check_replay(&r, 1U), 0);
538+
esp_replay_commit(&r, 1U);
539+
ck_assert_int_eq(esp_check_replay(&r, 2U), 0);
540+
esp_replay_commit(&r, 2U);
541+
542+
/* Save the replay state before the "unverified" packet arrives */
543+
memcpy(&saved, &r, sizeof(r));
544+
545+
/* Simulate receiving seq=10. This should only CHECK, not UPDATE.
546+
* In the real flow, ICV verification would follow and might fail. */
547+
ck_assert_int_eq(esp_check_replay(&r, 10U), 0);
548+
549+
/* esp_check_replay is now read-only (correct behavior), so the
550+
* replay state must be unchanged. */
551+
ck_assert_uint_eq(r.bitmap, saved.bitmap);
552+
ck_assert_uint_eq(r.hi_seq, saved.hi_seq);
553+
}
554+
END_TEST
555+
516556
/* The transmitted sequence number must never be allowed to overflow. */
517557
START_TEST(test_replay_overflow)
518558
{
@@ -1273,6 +1313,7 @@ static Suite *esp_suite(void)
12731313
tcase_add_test(tc, test_replay_jump_resets_bitmap);
12741314
tcase_add_test(tc, test_replay_old_seqs_after_jump);
12751315
tcase_add_test(tc, test_replay_overflow);
1316+
tcase_add_test(tc, test_regression_replay_window_not_updated_before_icv);
12761317
suite_add_tcase(s, tc);
12771318

12781319
/* Unwrap error paths */

src/wolfesp.c

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,19 +1159,20 @@ esp_check_icv_hmac(const wolfIP_esp_sa * esp_sa, uint8_t * esp_data,
11591159
}
11601160

11611161
/**
1162-
* Check sequence number against replay_t state.
1162+
* Check sequence number against replay_t state (read-only).
1163+
* RFC 4303 s3.4.3: the window must not be updated until after ICV
1164+
* verification succeeds. Call esp_replay_commit() after ICV passes.
11631165
*
11641166
* return 0 on success.
11651167
* */
11661168
static int
1167-
esp_check_replay(struct replay_t * replay, uint32_t seq)
1169+
esp_check_replay(const struct replay_t * replay, uint32_t seq)
11681170
{
11691171
#if !defined(ESP_REPLAY_WIN)
11701172
/* anti-replay service not enabled */
11711173
(void)replay;
11721174
(void)seq;
11731175
#else
1174-
uint32_t diff = 0;
11751176
uint32_t bitn = 0;
11761177
uint32_t seq_low = 1U;
11771178

@@ -1201,29 +1202,41 @@ esp_check_replay(struct replay_t * replay, uint32_t seq)
12011202
ESP_LOG("error: seq replayed: %u, %d\n", bitn, seq);
12021203
return -1;
12031204
}
1204-
else {
1205-
ESP_DEBUG("info: new seq : %d\n", seq);
1206-
replay->bitmap |= bitn;
1207-
}
1205+
}
1206+
#endif /* ESP_REPLAY_WIN */
1207+
1208+
return 0;
1209+
}
1210+
1211+
/**
1212+
* Commit a verified sequence number to the replay window.
1213+
* Called only after ICV verification succeeds (RFC 4303 s3.4.3).
1214+
* */
1215+
static void
1216+
esp_replay_commit(struct replay_t * replay, uint32_t seq)
1217+
{
1218+
#if !defined(ESP_REPLAY_WIN)
1219+
(void)replay;
1220+
(void)seq;
1221+
#else
1222+
uint32_t diff;
1223+
1224+
if (seq <= replay->hi_seq) {
1225+
/* Within window: mark the bit. */
1226+
replay->bitmap |= 1U << (replay->hi_seq - seq);
12081227
}
12091228
else {
1210-
/* seq number above window. */
1211-
ESP_DEBUG("info: new hi_seq : %d, %d\n", replay->hi_seq, seq);
1229+
/* Above window: slide up. */
12121230
diff = seq - replay->hi_seq;
12131231
if (diff < ESP_REPLAY_WIN) {
1214-
/* within a window width, slide up. */
12151232
replay->bitmap = (replay->bitmap << diff) | 1U;
12161233
}
12171234
else {
1218-
/* reset window. */
12191235
replay->bitmap = 1;
12201236
}
1221-
12221237
replay->hi_seq = seq;
12231238
}
12241239
#endif /* ESP_REPLAY_WIN */
1225-
1226-
return 0;
12271240
}
12281241

12291242
/**
@@ -1349,6 +1362,10 @@ esp_transport_unwrap(struct wolfIP_ip_packet *ip, uint32_t * frame_len)
13491362
}
13501363
}
13511364

1365+
/* ICV verified; now safe to commit the sequence to the replay window
1366+
* (RFC 4303 s3.4.3). */
1367+
esp_replay_commit(&esp_sa->replay, seq);
1368+
13521369
/* icv check good, now finish unwrapping esp packet. */
13531370
if (iv_len != 0) {
13541371
/* Decrypt the payload in place. */

0 commit comments

Comments
 (0)