use super::schedule;
use crate::{crypto::awslc::open, packet::secret_control};
use once_cell::sync::OnceCell;
use s2n_quic_core::varint::VarInt;
use std::sync::atomic::{AtomicU64, Ordering};
type StatelessReset = [u8; secret_control::TAG_LEN];
#[derive(Debug)]
pub struct State {
current_id: AtomicU64,
pub(super) stateless_reset: StatelessReset,
control_secret: OnceCell<open::control::Secret>,
}
impl State {
pub fn new(stateless_reset: StatelessReset) -> Self {
Self {
current_id: AtomicU64::new(0),
stateless_reset,
control_secret: Default::default(),
}
}
pub fn next_key_id(&self) -> VarInt {
let id = self
.current_id
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
VarInt::try_from(current + 1)
.ok()
.filter(|id| *id != VarInt::MAX)
.map(|id| *id)
});
let id = id.expect("2^62 integer incremented per-path will not wrap");
VarInt::try_from(id).unwrap()
}
#[inline]
pub fn control_secret(&self, secret: &schedule::Secret) -> &open::control::Secret {
self.control_secret.get_or_init(|| secret.control_opener())
}
pub(super) fn update_for_stale_key(&self, min_key_id: VarInt) {
self.current_id.fetch_max(*min_key_id, Ordering::Relaxed);
}
}
#[test]
#[should_panic = "2^62 integer incremented"]
fn sender_does_not_wrap() {
let state = State::new([0; secret_control::TAG_LEN]);
assert_eq!(*state.next_key_id(), 0);
state.current_id.store((1 << 62) - 3, Ordering::Relaxed);
assert_eq!(*state.next_key_id(), (1 << 62) - 3);
assert_eq!(*state.next_key_id(), (1 << 62) - 2);
assert_eq!(*state.next_key_id(), (1 << 62) - 1);
state.next_key_id();
}
#[test]
fn update_restarts_sequence() {
let state = State::new([0; secret_control::TAG_LEN]);
assert_eq!(*state.next_key_id(), 0);
state.update_for_stale_key(VarInt::new(3).unwrap());
assert_eq!(*state.next_key_id(), 3);
}