mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-07 21:13:44 +02:00
finished testing new counter window
This commit is contained in:
parent
fea866ca39
commit
eee16167df
4 changed files with 97 additions and 27 deletions
|
@ -79,6 +79,7 @@ pub(crate) const KEY_HISTORY_SIZE: usize = 3;
|
||||||
|
|
||||||
/// Maximum difference between out-of-order incoming packet counters, and size of deduplication buffer.
|
/// Maximum difference between out-of-order incoming packet counters, and size of deduplication buffer.
|
||||||
pub(crate) const COUNTER_MAX_DELTA: u32 = 16;
|
pub(crate) const COUNTER_MAX_DELTA: u32 = 16;
|
||||||
|
pub(crate) const COUNTER_MAX_ALLOWED_OOO: usize = 16;
|
||||||
|
|
||||||
// Packet types can range from 0 to 15 (4 bits) -- 0-3 are defined and 4-15 are reserved for future use
|
// Packet types can range from 0 to 15 (4 bits) -- 0-3 are defined and 4-15 are reserved for future use
|
||||||
pub(crate) const PACKET_TYPE_DATA: u8 = 0;
|
pub(crate) const PACKET_TYPE_DATA: u8 = 0;
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::sync::{
|
||||||
|
|
||||||
use zerotier_crypto::random;
|
use zerotier_crypto::random;
|
||||||
|
|
||||||
use crate::constants::COUNTER_MAX_DELTA;
|
use crate::constants::{COUNTER_MAX_DELTA, COUNTER_MAX_ALLOWED_OOO};
|
||||||
|
|
||||||
/// Outgoing packet counter with strictly ordered atomic semantics.
|
/// Outgoing packet counter with strictly ordered atomic semantics.
|
||||||
///
|
///
|
||||||
|
@ -114,34 +114,56 @@ impl CounterWindowAlt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct CounterWindow(Mutex<(usize, [u64; COUNTER_MAX_DELTA as usize])>);
|
pub(crate) struct CounterWindow(Mutex<(usize, [u64; COUNTER_MAX_ALLOWED_OOO as usize])>);
|
||||||
|
|
||||||
impl CounterWindow {
|
impl CounterWindow {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(initial: u32) -> Self {
|
pub fn new(initial: u32) -> Self {
|
||||||
|
// we want our nonces to wrap at the exact same time that the counter wraps, so we shift them up to the u64 boundary
|
||||||
let initial_nonce = (initial as u64).wrapping_shl(32);
|
let initial_nonce = (initial as u64).wrapping_shl(32);
|
||||||
Self(Mutex::new((0, std::array::from_fn(|_| initial_nonce))))
|
Self(Mutex::new((0, [initial_nonce; COUNTER_MAX_ALLOWED_OOO as usize])))
|
||||||
}
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new_uninit() -> Self {
|
||||||
|
Self(Mutex::new((usize::MAX, [0; COUNTER_MAX_ALLOWED_OOO as usize])))
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn init(&self, initial: u32) {
|
||||||
|
let initial_nonce = (initial as u64).wrapping_shl(6);
|
||||||
|
let mut data = self.0.lock().unwrap();
|
||||||
|
data.0 = 0;
|
||||||
|
data.1 = [initial_nonce; COUNTER_MAX_ALLOWED_OOO as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn message_received(&self, received_counter_value: u32, received_fragment_no: u8) -> bool {
|
pub fn message_received(&self, received_counter_value: u32, received_fragment_no: u8) -> bool {
|
||||||
let fragment_nonce = (received_counter_value as u64).wrapping_shl(6) | (received_fragment_no as u64);
|
let fragment_nonce = (received_counter_value as u64).wrapping_shl(32) | (received_fragment_no as u64);
|
||||||
//everything past this point must be atomic, i.e. these instructions must be run mutually exclusive to completion;
|
//everything past this point must be atomic, i.e. these instructions must be run mutually exclusive to completion;
|
||||||
//atomic instructions are only ever atomic within themselves;
|
//atomic instructions are only ever atomic within themselves;
|
||||||
//sequentially consistent atomics do not guarantee that the thread is not preempted between individual atomic instructions
|
//sequentially consistent atomics do not guarantee that the thread is not preempted between individual atomic instructions
|
||||||
let mut data = self.0.lock().unwrap();
|
let mut data = self.0.lock().unwrap();
|
||||||
|
if data.0 == usize::MAX {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
const NONCE_MAX_DELTA: i64 = (2*COUNTER_MAX_ALLOWED_OOO as i64).wrapping_shl(32);
|
||||||
let mut is_in = false;
|
let mut is_in = false;
|
||||||
let mut is_gt_min = false;
|
let mut idx = 0;
|
||||||
for nonce in data.1 {
|
let mut smallest = fragment_nonce;
|
||||||
|
for i in 0..data.1.len() {
|
||||||
|
let nonce = data.1[i];
|
||||||
is_in |= nonce == fragment_nonce;
|
is_in |= nonce == fragment_nonce;
|
||||||
is_gt_min |= nonce.wrapping_sub(fragment_nonce) > 0;
|
let delta = (smallest as i64).wrapping_sub(nonce as i64);
|
||||||
|
if delta > 0 {
|
||||||
|
smallest = nonce;
|
||||||
|
idx = i;
|
||||||
}
|
}
|
||||||
if !is_in & is_gt_min {
|
}
|
||||||
let idx = data.0;
|
if !is_in & (smallest != fragment_nonce) & ((fragment_nonce as i64).wrapping_sub(smallest as i64) < NONCE_MAX_DELTA) {
|
||||||
data.1[idx] = fragment_nonce;
|
data.1[idx] = fragment_nonce;
|
||||||
data.0 = (idx + 1) % (COUNTER_MAX_DELTA as usize);
|
return true
|
||||||
return true;
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,14 +218,62 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn xorshift64(x: &mut u64) -> u32 {
|
||||||
|
*x ^= x.wrapping_shl(13);
|
||||||
|
*x ^= x.wrapping_shr(7);
|
||||||
|
*x ^= x.wrapping_shl(17);
|
||||||
|
*x as u32
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn counter_window() {
|
fn counter_window() {
|
||||||
let w = CounterWindow::new(0xffffffff);
|
let sqrt_out_of_order_max = (COUNTER_MAX_ALLOWED_OOO as f32).sqrt() as u32;
|
||||||
assert!(!w.message_received(0xffffffff));
|
let mut rng = 8234;
|
||||||
assert!(w.message_received(0));
|
let mut counter = u32::MAX - 16;
|
||||||
assert!(w.message_received(1));
|
let mut fragment_no: u8 = 0;
|
||||||
assert!(w.message_received(COUNTER_MAX_DELTA * 2));
|
let mut history = Vec::<(u32, u8)>::new();
|
||||||
assert!(!w.message_received(0xffffffff));
|
|
||||||
assert!(w.message_received(0xfffffffe));
|
let mut w = CounterWindow::new(counter.wrapping_sub(1));
|
||||||
|
for i in 1..1000000 {
|
||||||
|
let p = xorshift64(&mut rng)%1000;
|
||||||
|
let c;
|
||||||
|
let f;
|
||||||
|
if p < 250 {
|
||||||
|
let r = xorshift64(&mut rng);
|
||||||
|
c = counter.wrapping_add(r%sqrt_out_of_order_max);
|
||||||
|
f = fragment_no + 1 + ((r/sqrt_out_of_order_max)%sqrt_out_of_order_max) as u8;
|
||||||
|
} else if p < 500 {
|
||||||
|
if history.len() > 0 {
|
||||||
|
let idx = xorshift64(&mut rng) as usize%history.len();
|
||||||
|
let (c, f) = history[idx];
|
||||||
|
assert!(!w.message_received(c, f));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if p < 750 {
|
||||||
|
fragment_no = u8::min(fragment_no + 1, 63);
|
||||||
|
c = counter;
|
||||||
|
f = fragment_no;
|
||||||
|
} else if p < 999 {
|
||||||
|
counter = counter.wrapping_add(1);
|
||||||
|
fragment_no = 0;
|
||||||
|
c = counter;
|
||||||
|
f = fragment_no;
|
||||||
|
} else {
|
||||||
|
//simulate rekeying
|
||||||
|
counter = xorshift64(&mut rng);
|
||||||
|
fragment_no = 0;
|
||||||
|
w = CounterWindow::new(counter.wrapping_sub(1));
|
||||||
|
history = Vec::<(u32, u8)>::new();
|
||||||
|
c = counter;
|
||||||
|
f = fragment_no;
|
||||||
|
}
|
||||||
|
if history.contains(&(c, f)) {
|
||||||
|
assert!(!w.message_received(c, f));
|
||||||
|
} else {
|
||||||
|
assert!(w.message_received(c, f));
|
||||||
|
history.push((c, f));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,6 +254,7 @@ impl<Application: ApplicationLayer> Session<Application> {
|
||||||
id: local_session_id,
|
id: local_session_id,
|
||||||
application_data,
|
application_data,
|
||||||
send_counter,
|
send_counter,
|
||||||
|
receive_window: CounterWindow::new_uninit(),//alice does not know bob's counter yet
|
||||||
psk: psk.clone(),
|
psk: psk.clone(),
|
||||||
noise_ss,
|
noise_ss,
|
||||||
header_check_cipher,
|
header_check_cipher,
|
||||||
|
@ -879,6 +880,7 @@ impl<Application: ApplicationLayer> ReceiveContext<Application> {
|
||||||
id: new_session_id,
|
id: new_session_id,
|
||||||
application_data: associated_object,
|
application_data: associated_object,
|
||||||
send_counter: Counter::new(),
|
send_counter: Counter::new(),
|
||||||
|
receive_window: CounterWindow::new(counter),
|
||||||
psk,
|
psk,
|
||||||
noise_ss,
|
noise_ss,
|
||||||
header_check_cipher,
|
header_check_cipher,
|
||||||
|
@ -1030,7 +1032,6 @@ impl<Application: ApplicationLayer> ReceiveContext<Application> {
|
||||||
Role::Bob,
|
Role::Bob,
|
||||||
current_time,
|
current_time,
|
||||||
reply_counter,
|
reply_counter,
|
||||||
counter,
|
|
||||||
last_ratchet_count + 1,
|
last_ratchet_count + 1,
|
||||||
hybrid_kk.is_some(),
|
hybrid_kk.is_some(),
|
||||||
);
|
);
|
||||||
|
@ -1154,10 +1155,10 @@ impl<Application: ApplicationLayer> ReceiveContext<Application> {
|
||||||
Role::Alice,
|
Role::Alice,
|
||||||
current_time,
|
current_time,
|
||||||
reply_counter,
|
reply_counter,
|
||||||
counter,
|
|
||||||
last_ratchet_count + 1,
|
last_ratchet_count + 1,
|
||||||
hybrid_kk.is_some(),
|
hybrid_kk.is_some(),
|
||||||
);
|
);
|
||||||
|
session.receive_window.init(counter);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
// packet encoding for post-noise session start ack
|
// packet encoding for post-noise session start ack
|
||||||
|
@ -1497,7 +1498,6 @@ impl SessionKey {
|
||||||
role: Role,
|
role: Role,
|
||||||
current_time: i64,
|
current_time: i64,
|
||||||
current_counter: CounterValue,
|
current_counter: CounterValue,
|
||||||
remote_counter: u32,
|
|
||||||
ratchet_count: u64,
|
ratchet_count: u64,
|
||||||
jedi: bool,
|
jedi: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -1511,7 +1511,6 @@ impl SessionKey {
|
||||||
secret_fingerprint: public_fingerprint_of_secret(key.as_bytes())[..16].try_into().unwrap(),
|
secret_fingerprint: public_fingerprint_of_secret(key.as_bytes())[..16].try_into().unwrap(),
|
||||||
creation_time: current_time,
|
creation_time: current_time,
|
||||||
creation_counter: current_counter,
|
creation_counter: current_counter,
|
||||||
receive_window: CounterWindow::new(remote_counter),
|
|
||||||
lifetime: KeyLifetime::new(current_counter, current_time),
|
lifetime: KeyLifetime::new(current_counter, current_time),
|
||||||
ratchet_key: kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_RATCHETING),
|
ratchet_key: kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_RATCHETING),
|
||||||
receive_key,
|
receive_key,
|
||||||
|
|
Loading…
Add table
Reference in a new issue