@@ -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}
430431END_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}
442444END_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}
464468END_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}
474479END_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}
514524END_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. */
517557START_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 */
0 commit comments