mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-09 16:24:45 +00:00
rekey: apply symmetric jitter to desynchronize dual-initiation
Add a per-session signed jitter offset (uniform [-15, +15] seconds) to the rekey timer triggers in check_rekey (FMP) and check_session_rekey (FSP). The configured `node.rekey.after_secs` becomes the nominal interval rather than a floor; mean is preserved. Desynchronizes both endpoints in symmetric-start meshes so the dual-initiation race stops occurring rather than being resolved after the fact by the smaller-NodeAddr tie-breaker. Per-session storage means each rekey cutover reconstructs the session and redraws the jitter naturally — successive cycles get independent offsets, preventing drift back into sync.
This commit is contained in:
@@ -10,9 +10,16 @@ use std::time::Instant;
|
||||
use crate::NodeAddr;
|
||||
use crate::config::SessionMmpConfig;
|
||||
use crate::mmp::MmpSessionState;
|
||||
use crate::node::REKEY_JITTER_SECS;
|
||||
use crate::noise::{HandshakeState, NoiseSession};
|
||||
use rand::RngExt;
|
||||
use secp256k1::PublicKey;
|
||||
|
||||
/// Draw a fresh per-session rekey jitter from `[-REKEY_JITTER_SECS, +REKEY_JITTER_SECS]`.
|
||||
fn draw_rekey_jitter() -> i64 {
|
||||
rand::rng().random_range(-REKEY_JITTER_SECS..=REKEY_JITTER_SECS)
|
||||
}
|
||||
|
||||
/// State machine for an end-to-end session.
|
||||
///
|
||||
/// `Established` is intentionally larger than the handshake variants:
|
||||
@@ -121,6 +128,12 @@ pub(crate) struct SessionEntry {
|
||||
/// When the FSP rekey handshake completed (initiator sent msg3, Unix ms).
|
||||
/// Used to defer cutover until msg3 has time to reach the responder.
|
||||
rekey_completed_ms: u64,
|
||||
/// Per-session symmetric jitter applied to the rekey timer trigger.
|
||||
/// Drawn once at construction (and at each cutover) uniformly from
|
||||
/// `[-REKEY_JITTER_SECS, +REKEY_JITTER_SECS]`. Desynchronizes
|
||||
/// dual-initiation in symmetric-start meshes; mean interval is
|
||||
/// preserved.
|
||||
rekey_jitter_secs: i64,
|
||||
}
|
||||
|
||||
impl SessionEntry {
|
||||
@@ -157,6 +170,7 @@ impl SessionEntry {
|
||||
rekey_initiator: false,
|
||||
last_peer_rekey_ms: 0,
|
||||
rekey_completed_ms: 0,
|
||||
rekey_jitter_secs: draw_rekey_jitter(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,6 +412,16 @@ impl SessionEntry {
|
||||
self.rekey_completed_ms
|
||||
}
|
||||
|
||||
/// Per-session symmetric rekey-timer jitter offset (seconds).
|
||||
///
|
||||
/// Drawn at session construction and at each rekey cutover; uniform
|
||||
/// over `[-REKEY_JITTER_SECS, +REKEY_JITTER_SECS]`. Callers add this
|
||||
/// to the configured `node.rekey.after_secs` to obtain the effective
|
||||
/// trigger interval for this session.
|
||||
pub(crate) fn rekey_jitter_secs(&self) -> i64 {
|
||||
self.rekey_jitter_secs
|
||||
}
|
||||
|
||||
/// Record when the FSP rekey handshake completed (initiator side).
|
||||
pub(crate) fn set_rekey_completed_ms(&mut self, ms: u64) {
|
||||
self.rekey_completed_ms = ms;
|
||||
@@ -443,6 +467,7 @@ impl SessionEntry {
|
||||
self.rekey_state = None;
|
||||
self.rekey_initiator = false;
|
||||
self.rekey_completed_ms = 0;
|
||||
self.rekey_jitter_secs = draw_rekey_jitter();
|
||||
|
||||
// Reset MMP counters to avoid metric discontinuity
|
||||
let now = Instant::now();
|
||||
@@ -471,6 +496,7 @@ impl SessionEntry {
|
||||
self.session_start_ms = now_ms;
|
||||
self.rekey_state = None;
|
||||
self.rekey_initiator = false;
|
||||
self.rekey_jitter_secs = draw_rekey_jitter();
|
||||
|
||||
// Reset MMP counters to avoid metric discontinuity
|
||||
let now = Instant::now();
|
||||
|
||||
Reference in New Issue
Block a user