mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-08 13:33:44 +02:00
restructured check code
This commit is contained in:
parent
fbd5e025d3
commit
cbae1d8f4c
1 changed files with 25 additions and 24 deletions
|
@ -117,7 +117,7 @@ pub struct Session<Application: ApplicationLayer> {
|
||||||
/// An arbitrary application defined object associated with each session
|
/// An arbitrary application defined object associated with each session
|
||||||
pub application_data: Application::Data,
|
pub application_data: Application::Data,
|
||||||
|
|
||||||
ratchet_counts: [AtomicU64; 2], // Number of preceding session keys in ratchet
|
ratchet_counts: [AtomicU64; 2], // Number of session keys in ratchet, starts at 1
|
||||||
header_check_cipher: Aes, // Cipher used for header check codes (not Noise related)
|
header_check_cipher: Aes, // Cipher used for header check codes (not Noise related)
|
||||||
receive_windows: [CounterWindow; 2], // Receive window for anti-replay and deduplication
|
receive_windows: [CounterWindow; 2], // Receive window for anti-replay and deduplication
|
||||||
state: RwLock<SessionMutableState>, // Mutable parts of state (other than defrag buffers)
|
state: RwLock<SessionMutableState>, // Mutable parts of state (other than defrag buffers)
|
||||||
|
@ -131,7 +131,7 @@ pub struct Session<Application: ApplicationLayer> {
|
||||||
|
|
||||||
struct SessionMutableState {
|
struct SessionMutableState {
|
||||||
remote_session_id: Option<SessionId>, // The other side's 48-bit session ID
|
remote_session_id: Option<SessionId>, // The other side's 48-bit session ID
|
||||||
send_counters: [Counter; 2], // Outgoing packet counter and nonce state
|
send_counters: [Counter; 2], // Outgoing packet counter and nonce state, starts at 1
|
||||||
session_keys: [Option<SessionKey>; 2], // Buffers to store current, next, and last active key
|
session_keys: [Option<SessionKey>; 2], // Buffers to store current, next, and last active key
|
||||||
cur_session_key_id: bool, // Pointer used for keys[] circular buffer
|
cur_session_key_id: bool, // Pointer used for keys[] circular buffer
|
||||||
offer: Option<EphemeralOffer>, // Most recent ephemeral offer sent to remote
|
offer: Option<EphemeralOffer>, // Most recent ephemeral offer sent to remote
|
||||||
|
@ -164,7 +164,6 @@ struct EphemeralOffer {
|
||||||
id: [u8; 16], // Arbitrary random offer ID
|
id: [u8; 16], // Arbitrary random offer ID
|
||||||
key_id: bool, // The key_id bound to this offer, for handling OOO rekeying
|
key_id: bool, // The key_id bound to this offer, for handling OOO rekeying
|
||||||
creation_time: i64, // Local time when offer was created
|
creation_time: i64, // Local time when offer was created
|
||||||
ratchet_count: u64, // Ratchet count (starting at zero) for initial offer
|
|
||||||
ratchet_key: Option<Secret<64>>, // Ratchet key from previous offer or None if first offer
|
ratchet_key: Option<Secret<64>>, // Ratchet key from previous offer or None if first offer
|
||||||
ss_key: Secret<64>, // Noise session key "under construction" at state after offer sent
|
ss_key: Secret<64>, // Noise session key "under construction" at state after offer sent
|
||||||
alice_e_keypair: P384KeyPair, // NIST P-384 key pair (Noise ephemeral key for Alice)
|
alice_e_keypair: P384KeyPair, // NIST P-384 key pair (Noise ephemeral key for Alice)
|
||||||
|
@ -425,6 +424,9 @@ impl<Application: ApplicationLayer> Session<Application> {
|
||||||
.map_or(true, |o| (current_time - o.creation_time) > Application::REKEY_RATE_LIMIT_MS)
|
.map_or(true, |o| (current_time - o.creation_time) > Application::REKEY_RATE_LIMIT_MS)
|
||||||
{
|
{
|
||||||
if let Some(remote_s_public) = P384PublicKey::from_bytes(&self.remote_s_public_p384_bytes) {
|
if let Some(remote_s_public) = P384PublicKey::from_bytes(&self.remote_s_public_p384_bytes) {
|
||||||
|
//this routine handles sending a rekeying packet, resending lost rekeying packets, and resending lost initial offer packets
|
||||||
|
//the protocol has been designed such that initial rekeying packets are identical to resent rekeying packets, except for the counter, so we can reuse the same code for doing both
|
||||||
|
let has_existing_session = state.remote_session_id.is_some();
|
||||||
//mark the previous key as no longer being supported because it is about to be overwritten
|
//mark the previous key as no longer being supported because it is about to be overwritten
|
||||||
//it should not be possible for a session to accidentally invalidate the key currently in use solely because of the read lock
|
//it should not be possible for a session to accidentally invalidate the key currently in use solely because of the read lock
|
||||||
self.receive_windows[(!current_key_id) as usize].invalidate();
|
self.receive_windows[(!current_key_id) as usize].invalidate();
|
||||||
|
@ -434,7 +436,7 @@ impl<Application: ApplicationLayer> Session<Application> {
|
||||||
&mut send,
|
&mut send,
|
||||||
state.send_counters[current_key_id as usize].next(),
|
state.send_counters[current_key_id as usize].next(),
|
||||||
current_key_id,
|
current_key_id,
|
||||||
!current_key_id,
|
has_existing_session && !current_key_id,
|
||||||
self.id,
|
self.id,
|
||||||
state.remote_session_id,
|
state.remote_session_id,
|
||||||
app.get_local_s_public_blob(),
|
app.get_local_s_public_blob(),
|
||||||
|
@ -444,11 +446,7 @@ impl<Application: ApplicationLayer> Session<Application> {
|
||||||
&self.noise_ss,
|
&self.noise_ss,
|
||||||
state.session_keys[current_key_id as usize].as_ref(),
|
state.session_keys[current_key_id as usize].as_ref(),
|
||||||
self.ratchet_counts[current_key_id as usize].load(Ordering::Relaxed),
|
self.ratchet_counts[current_key_id as usize].load(Ordering::Relaxed),
|
||||||
if state.remote_session_id.is_some() {
|
if has_existing_session { Some(&self.header_check_cipher) } else { None },
|
||||||
Some(&self.header_check_cipher)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
mtu,
|
mtu,
|
||||||
current_time,
|
current_time,
|
||||||
&mut offer,
|
&mut offer,
|
||||||
|
@ -883,7 +881,8 @@ impl<Application: ApplicationLayer> ReceiveContext<Application> {
|
||||||
(None, state.send_counters[key_id as usize].next(), !key_id, ratchet_key)
|
(None, state.send_counters[key_id as usize].next(), !key_id, ratchet_key)
|
||||||
} else {
|
} else {
|
||||||
if key_id != false {
|
if key_id != false {
|
||||||
//all new sessions must start with key_id 0, this has no security implications
|
// All new sessions must start with key_id 0
|
||||||
|
// This has no security implications, it just makes programming the initial offer easier
|
||||||
return Ok(ReceiveResult::Ignored);
|
return Ok(ReceiveResult::Ignored);
|
||||||
}
|
}
|
||||||
if let Some((new_session_id, psk, associated_object)) =
|
if let Some((new_session_id, psk, associated_object)) =
|
||||||
|
@ -1169,11 +1168,8 @@ impl<Application: ApplicationLayer> ReceiveContext<Application> {
|
||||||
let mut session_key = noise_ik_complete;
|
let mut session_key = noise_ik_complete;
|
||||||
|
|
||||||
// Mix ratchet key from previous session key (if any) and Kyber1024 hybrid shared key (if any).
|
// Mix ratchet key from previous session key (if any) and Kyber1024 hybrid shared key (if any).
|
||||||
let last_ratchet_count = if bob_ratchet_key_id.is_some() && offer.ratchet_key.is_some() {
|
if bob_ratchet_key_id.is_some() && offer.ratchet_key.is_some() {
|
||||||
session_key = Secret(hmac_sha512(offer.ratchet_key.as_ref().unwrap().as_bytes(), session_key.as_bytes()));
|
session_key = Secret(hmac_sha512(offer.ratchet_key.as_ref().unwrap().as_bytes(), session_key.as_bytes()));
|
||||||
offer.ratchet_count
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
};
|
||||||
if let Some(hybrid_kk) = hybrid_kk.as_ref() {
|
if let Some(hybrid_kk) = hybrid_kk.as_ref() {
|
||||||
session_key = Secret(hmac_sha512(hybrid_kk.as_bytes(), session_key.as_bytes()));
|
session_key = Secret(hmac_sha512(hybrid_kk.as_bytes(), session_key.as_bytes()));
|
||||||
|
@ -1201,12 +1197,13 @@ impl<Application: ApplicationLayer> ReceiveContext<Application> {
|
||||||
);
|
);
|
||||||
|
|
||||||
let new_key_id = offer.key_id;
|
let new_key_id = offer.key_id;
|
||||||
|
let has_existing_session = state.remote_session_id.is_some();
|
||||||
drop(state);
|
drop(state);
|
||||||
//TODO: check for correct orderings
|
//TODO: check for correct orderings
|
||||||
let mut state = session.state.write().unwrap();
|
let mut state = session.state.write().unwrap();
|
||||||
let _ = state.remote_session_id.replace(bob_session_id);
|
let _ = state.remote_session_id.replace(bob_session_id);
|
||||||
let _ = state.session_keys[new_key_id as usize].replace(session_key);
|
let _ = state.session_keys[new_key_id as usize].replace(session_key);
|
||||||
if last_ratchet_count > 0 && state.cur_session_key_id != new_key_id {
|
if has_existing_session && state.cur_session_key_id != new_key_id {
|
||||||
//when an brand new key offer is sent, it is sent using the new_key_id==false counter, we cannot reset it in that case.
|
//when an brand new key offer is sent, it is sent using the new_key_id==false counter, we cannot reset it in that case.
|
||||||
//NOTE: the following code should be properly threadsafe, see the large comment above at the end of KEY_OFFER decoding for more info
|
//NOTE: the following code should be properly threadsafe, see the large comment above at the end of KEY_OFFER decoding for more info
|
||||||
session.receive_windows[new_key_id as usize].reset_for_new_key_offer();
|
session.receive_windows[new_key_id as usize].reset_for_new_key_offer();
|
||||||
|
@ -1221,6 +1218,9 @@ impl<Application: ApplicationLayer> ReceiveContext<Application> {
|
||||||
return Ok(ReceiveResult::Ignored);
|
return Ok(ReceiveResult::Ignored);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
unlikely_branch();
|
||||||
|
return Err(Error::SessionNotEstablished);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just ignore counter-offers that are out of place. They probably indicate that this side
|
// Just ignore counter-offers that are out of place. They probably indicate that this side
|
||||||
|
@ -1384,7 +1384,6 @@ fn send_ephemeral_offer<SendFunction: FnMut(&mut [u8])>(
|
||||||
id,
|
id,
|
||||||
key_id: new_key_id,
|
key_id: new_key_id,
|
||||||
creation_time: current_time,
|
creation_time: current_time,
|
||||||
ratchet_count,
|
|
||||||
ratchet_key,
|
ratchet_key,
|
||||||
ss_key,
|
ss_key,
|
||||||
alice_e_keypair,
|
alice_e_keypair,
|
||||||
|
@ -1467,11 +1466,13 @@ fn send_with_fragmentation<SendFunction: FnMut(&mut [u8])>(
|
||||||
/// Set 32-bit header check code, used to make fragmentation mechanism robust.
|
/// Set 32-bit header check code, used to make fragmentation mechanism robust.
|
||||||
fn set_header_check_code(packet: &mut [u8], ratchet_count: u64, header_check_cipher: &Aes) {
|
fn set_header_check_code(packet: &mut [u8], ratchet_count: u64, header_check_cipher: &Aes) {
|
||||||
debug_assert!(packet.len() >= MIN_PACKET_SIZE);
|
debug_assert!(packet.len() >= MIN_PACKET_SIZE);
|
||||||
|
//4 bytes is the ratchet key
|
||||||
|
//12 bytes is the header we want to verify
|
||||||
let mut header_mac = 0u128.to_le_bytes();
|
let mut header_mac = 0u128.to_le_bytes();
|
||||||
memory::store_raw((ratchet_count as u16).to_le_bytes(), &mut header_mac[0..2]);
|
memory::store_raw((ratchet_count as u32).to_le_bytes(), &mut header_mac[0..4]);
|
||||||
header_mac[2..16].copy_from_slice(&packet[4..18]);
|
header_mac[4..16].copy_from_slice(&packet[4..16]);
|
||||||
header_check_cipher.encrypt_block_in_place(&mut header_mac);
|
header_check_cipher.encrypt_block_in_place(&mut header_mac);
|
||||||
|
|
||||||
packet[..4].copy_from_slice(&header_mac[..4]);
|
packet[..4].copy_from_slice(&header_mac[..4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1479,13 +1480,13 @@ fn set_header_check_code(packet: &mut [u8], ratchet_count: u64, header_check_cip
|
||||||
/// This is not nearly enough entropy to be cryptographically secure, it only is meant for making DOS attacks very hard
|
/// This is not nearly enough entropy to be cryptographically secure, it only is meant for making DOS attacks very hard
|
||||||
fn verify_header_check_code(packet: &[u8], ratchet_count: u64, header_check_cipher: &Aes) -> bool {
|
fn verify_header_check_code(packet: &[u8], ratchet_count: u64, header_check_cipher: &Aes) -> bool {
|
||||||
debug_assert!(packet.len() >= MIN_PACKET_SIZE);
|
debug_assert!(packet.len() >= MIN_PACKET_SIZE);
|
||||||
//2 bytes is the ratchet key
|
//4 bytes is the ratchet key
|
||||||
//12 bytes is the header we want to verify
|
//12 bytes is the header we want to verify
|
||||||
//2 bytes is salt from the message
|
|
||||||
let mut header_mac = 0u128.to_le_bytes();
|
let mut header_mac = 0u128.to_le_bytes();
|
||||||
memory::store_raw((ratchet_count as u16).to_le_bytes(), &mut header_mac[0..2]);
|
memory::store_raw((ratchet_count as u32).to_le_bytes(), &mut header_mac[0..4]);
|
||||||
header_mac[2..16].copy_from_slice(&packet[4..18]);
|
header_mac[4..16].copy_from_slice(&packet[4..16]);
|
||||||
header_check_cipher.encrypt_block_in_place(&mut header_mac);
|
header_check_cipher.encrypt_block_in_place(&mut header_mac);
|
||||||
|
|
||||||
memory::load_raw::<u32>(&packet[..4]) == memory::load_raw::<u32>(&header_mac[..4])
|
memory::load_raw::<u32>(&packet[..4]) == memory::load_raw::<u32>(&header_mac[..4])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue