feat: enhance quota user lock management and testing

- Adjusted QUOTA_USER_LOCKS_MAX based on test and non-test configurations to improve flexibility.
- Implemented logic to retain existing locks when the maximum quota is reached, ensuring efficient memory usage.
- Added comprehensive tests for quota user lock functionality, including cache reuse, saturation behavior, and race conditions.
- Enhanced StatsIo struct to manage wake scheduling for read and write operations, preventing unnecessary self-wakes.
- Introduced separate replay checker domains for handshake and TLS to ensure isolation and prevent cross-pollution of keys.
- Added security tests for replay checker to validate domain separation and window clamping behavior.
This commit is contained in:
David Osipov
2026-03-18 23:55:08 +04:00
parent 20e205189c
commit c7cf37898b
18 changed files with 1896 additions and 49 deletions

View File

@@ -1949,6 +1949,138 @@ fn server_hello_new_session_ticket_count_is_safely_capped() {
);
}
#[test]
fn boot_time_handshake_replay_remains_blocked_after_cache_window_expires() {
let secret = b"gap_t01_boot_replay";
let secrets = vec![("user".to_string(), secret.to_vec())];
let handshake = make_valid_tls_handshake(secret, 1);
let validation = validate_tls_handshake_with_replay_window(&handshake, &secrets, false, 2)
.expect("boot-time handshake must validate on first use");
let checker = crate::stats::ReplayChecker::new(128, std::time::Duration::from_millis(40));
let digest_half = &validation.digest[..TLS_DIGEST_HALF_LEN];
assert!(
!checker.check_and_add_tls_digest(digest_half),
"first use must not be treated as replay"
);
assert!(
checker.check_and_add_tls_digest(digest_half),
"immediate second use must be detected as replay"
);
std::thread::sleep(std::time::Duration::from_millis(70));
let validation_after_expiry = validate_tls_handshake_with_replay_window(&handshake, &secrets, false, 2)
.expect("boot-time handshake must still cryptographically validate after cache expiry");
let digest_half_after_expiry = &validation_after_expiry.digest[..TLS_DIGEST_HALF_LEN];
assert_eq!(digest_half, digest_half_after_expiry, "replay key must be stable for same handshake");
assert!(
checker.check_and_add_tls_digest(digest_half_after_expiry),
"after cache window expiry, the same boot-time handshake must still be treated as replay"
);
}
#[test]
fn adversarial_boot_time_handshake_should_not_be_replayable_after_cache_expiry() {
let secret = b"gap_t01_boot_replay_adversarial";
let secrets = vec![("user".to_string(), secret.to_vec())];
let handshake = make_valid_tls_handshake(secret, 1);
let validation = validate_tls_handshake_with_replay_window(&handshake, &secrets, false, 2)
.expect("boot-time handshake must validate on first use");
let checker = crate::stats::ReplayChecker::new(128, std::time::Duration::from_millis(40));
let digest_half = &validation.digest[..TLS_DIGEST_HALF_LEN];
assert!(
!checker.check_and_add_tls_digest(digest_half),
"first use must not be treated as replay"
);
assert!(
checker.check_and_add_tls_digest(digest_half),
"immediate reuse must be rejected as replay"
);
std::thread::sleep(std::time::Duration::from_millis(70));
let validation_after_expiry = validate_tls_handshake_with_replay_window(&handshake, &secrets, false, 2)
.expect("boot-time handshake still validates cryptographically after cache expiry");
let digest_half_after_expiry = &validation_after_expiry.digest[..TLS_DIGEST_HALF_LEN];
assert_eq!(
digest_half, digest_half_after_expiry,
"replay key must remain stable for the same captured handshake"
);
assert!(
checker.check_and_add_tls_digest(digest_half_after_expiry),
"security expectation: a boot-time handshake should remain replay-protected even after cache expiry"
);
}
#[test]
fn stress_short_replay_window_boot_timestamp_replay_cycles_remain_fail_closed_in_window() {
let secret = b"gap_t01_boot_replay_stress";
let secrets = vec![("user".to_string(), secret.to_vec())];
let handshake = make_valid_tls_handshake(secret, 1);
let checker = crate::stats::ReplayChecker::new(256, std::time::Duration::from_millis(25));
for cycle in 0..64 {
let validation = validate_tls_handshake_with_replay_window(&handshake, &secrets, false, 2)
.expect("boot-time handshake must validate");
let digest_half = &validation.digest[..TLS_DIGEST_HALF_LEN];
if cycle == 0 {
assert!(
!checker.check_and_add_tls_digest(digest_half),
"cycle 0: first use must be fresh"
);
assert!(
checker.check_and_add_tls_digest(digest_half),
"cycle 0: second use must be replay"
);
} else {
assert!(
checker.check_and_add_tls_digest(digest_half),
"cycle {cycle}: digest must remain replay-protected across short-window churn"
);
}
std::thread::sleep(std::time::Duration::from_millis(30));
}
}
#[test]
fn light_fuzz_boot_time_timestamp_matrix_with_short_replay_window_obeys_boot_cap() {
let secret = b"gap_t01_boot_replay_fuzz";
let secrets = vec![("user".to_string(), secret.to_vec())];
let mut s: u64 = 0xA1B2_C3D4_55AA_7733;
for _ in 0..2048 {
s ^= s << 7;
s ^= s >> 9;
s ^= s << 8;
let ts = (s as u32) % 8;
let handshake = make_valid_tls_handshake(secret, ts);
let accepted = validate_tls_handshake_with_replay_window(&handshake, &secrets, false, 2)
.is_some();
if ts < 2 {
assert!(accepted, "timestamp {ts} must remain boot-time compatible under 2s cap");
} else {
assert!(
!accepted,
"timestamp {ts} must be rejected when outside replay-window boot cap"
);
}
}
}
#[test]
fn server_hello_application_data_contains_alpn_marker_when_selected() {
let secret = b"alpn_marker_test";