mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-07 21:13:44 +02:00
More simplification, move logic to get subset array of session ID into SessionId itself.
This commit is contained in:
parent
611ca97ee4
commit
0e518f679e
5 changed files with 113 additions and 104 deletions
|
@ -6,7 +6,7 @@ use zerotier_crypto::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ints::SessionId,
|
sessionid::SessionId,
|
||||||
zssp::{ReceiveContext, Session},
|
zssp::{ReceiveContext, Session},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
use zerotier_crypto::random;
|
|
||||||
use zerotier_utils::memory;
|
|
||||||
|
|
||||||
/// "Canonical header" for generating 96-bit AES-GCM nonce and for inclusion in HMACs.
|
|
||||||
///
|
|
||||||
/// This is basically the actual header but with fragment count and fragment total set to zero.
|
|
||||||
/// Fragmentation is not considered when authenticating the entire packet. A separate header
|
|
||||||
/// check code is used to make fragmentation itself more robust, but that's outside the scope
|
|
||||||
/// of AEAD authentication.
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub(crate) struct CanonicalHeader(pub u64, pub u32);
|
|
||||||
impl CanonicalHeader {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn make(session_id: SessionId, packet_type: u8, counter: u32) -> Self {
|
|
||||||
CanonicalHeader(
|
|
||||||
(u64::from(session_id) | (packet_type as u64).wrapping_shl(48)).to_le(),
|
|
||||||
counter.to_le(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn as_bytes(&self) -> &[u8; 12] {
|
|
||||||
memory::as_byte_array(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 48-bit session ID (most significant 16 bits of u64 are unused)
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct SessionId(pub(crate) u64);
|
|
||||||
|
|
||||||
impl SessionId {
|
|
||||||
/// The nil session ID used in messages initiating a new session.
|
|
||||||
///
|
|
||||||
/// This is all 1's so that ZeroTier can easily tell the difference between ZSSP init packets
|
|
||||||
/// and ZeroTier V1 packets.
|
|
||||||
pub const NIL: SessionId = SessionId(0xffffffffffff);
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn new_from_u64(i: u64) -> Option<SessionId> {
|
|
||||||
if i < Self::NIL.0 {
|
|
||||||
Some(Self(i))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn new_random() -> Self {
|
|
||||||
Self(random::next_u64_secure() % Self::NIL.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SessionId> for u64 {
|
|
||||||
#[inline(always)]
|
|
||||||
fn from(sid: SessionId) -> Self {
|
|
||||||
sid.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Was this side the one who sent the first offer (Alice) or countered (Bob).
|
|
||||||
/// Note that role is not fixed. Either side can take either role. It's just who
|
|
||||||
/// initiated first.
|
|
||||||
pub enum Role {
|
|
||||||
Alice,
|
|
||||||
Bob,
|
|
||||||
}
|
|
|
@ -1,11 +1,11 @@
|
||||||
mod app_layer;
|
mod app_layer;
|
||||||
mod counter;
|
mod counter;
|
||||||
mod ints;
|
mod sessionid;
|
||||||
mod tests;
|
mod tests;
|
||||||
mod zssp;
|
mod zssp;
|
||||||
|
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
|
||||||
pub use crate::app_layer::ApplicationLayer;
|
pub use crate::app_layer::ApplicationLayer;
|
||||||
pub use crate::ints::{Role, SessionId};
|
pub use crate::sessionid::SessionId;
|
||||||
pub use crate::zssp::{Error, ReceiveContext, ReceiveResult, Session};
|
pub use crate::zssp::{Error, ReceiveContext, ReceiveResult, Session};
|
||||||
|
|
51
zssp/src/sessionid.rs
Normal file
51
zssp/src/sessionid.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use zerotier_crypto::random;
|
||||||
|
use zerotier_utils::memory::{array_range, as_byte_array};
|
||||||
|
|
||||||
|
use crate::constants::SESSION_ID_SIZE;
|
||||||
|
|
||||||
|
/// 48-bit session ID (most significant 16 bits of u64 are unused)
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct SessionId(u64); // stored little endian internally
|
||||||
|
|
||||||
|
impl SessionId {
|
||||||
|
/// The nil session ID used in messages initiating a new session.
|
||||||
|
///
|
||||||
|
/// This is all 1's so that ZeroTier can easily tell the difference between ZSSP init packets
|
||||||
|
/// and ZeroTier V1 packets.
|
||||||
|
pub const NIL: SessionId = SessionId(0xffffffffffffu64.to_le());
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn new_from_u64(i: u64) -> Option<SessionId> {
|
||||||
|
if i < Self::NIL.0 {
|
||||||
|
Some(Self(i.to_le()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn new_random() -> Self {
|
||||||
|
Self((random::next_u64_secure() % Self::NIL.0).to_le())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get this session ID as a 48-bit little endian byte array.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_bytes(&self) -> &[u8; SESSION_ID_SIZE] {
|
||||||
|
array_range::<u8, 8, 0, SESSION_ID_SIZE>(as_byte_array(&self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SessionId> for u64 {
|
||||||
|
fn from(sid: SessionId) -> Self {
|
||||||
|
u64::from_le(sid.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for SessionId {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_fmt(format_args!("{:06x}", u64::from_le(self.0)))
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,11 +20,7 @@ use zerotier_utils::varint;
|
||||||
use crate::app_layer::ApplicationLayer;
|
use crate::app_layer::ApplicationLayer;
|
||||||
use crate::constants::*;
|
use crate::constants::*;
|
||||||
use crate::counter::{Counter, CounterValue};
|
use crate::counter::{Counter, CounterValue};
|
||||||
use crate::ints::*;
|
use crate::sessionid::SessionId;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
// types
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// The packet was addressed to an unrecognized local session (should usually be ignored)
|
/// The packet was addressed to an unrecognized local session (should usually be ignored)
|
||||||
|
@ -83,6 +79,15 @@ pub enum ReceiveResult<'a, H: ApplicationLayer> {
|
||||||
Ignored,
|
Ignored,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Was this side the one who sent the first offer (Alice) or countered (Bob).
|
||||||
|
///
|
||||||
|
/// Note that the role can switch through the course of a session. It's the side that most recently
|
||||||
|
/// initiated a session or a rekey event. Initiator is Alice, responder is Bob.
|
||||||
|
pub enum Role {
|
||||||
|
Alice,
|
||||||
|
Bob,
|
||||||
|
}
|
||||||
|
|
||||||
/// State information to associate with receiving contexts such as sockets or remote paths/endpoints.
|
/// State information to associate with receiving contexts such as sockets or remote paths/endpoints.
|
||||||
///
|
///
|
||||||
/// This holds the data structures used to defragment incoming packets that are not associated with an
|
/// This holds the data structures used to defragment incoming packets that are not associated with an
|
||||||
|
@ -153,14 +158,20 @@ struct KeyLifetime {
|
||||||
rekey_at_or_after_timestamp: i64,
|
rekey_at_or_after_timestamp: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
/// "Canonical header" for generating 96-bit AES-GCM nonce and for inclusion in key exchange HMACs.
|
||||||
// functions
|
///
|
||||||
////////////////////////////////////////////////////////////////
|
/// This is basically the actual header but with fragment count and fragment total set to zero.
|
||||||
|
/// Fragmentation is not considered when authenticating the entire packet. A separate header
|
||||||
|
/// check code is used to make fragmentation itself more robust, but that's outside the scope
|
||||||
|
/// of AEAD authentication.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct CanonicalHeader(pub u64, pub u32);
|
||||||
|
|
||||||
impl std::fmt::Display for Error {
|
impl std::fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::UnknownLocalSessionId(id) => f.write_str(format!("UnknownLocalSessionId({})", id.0).as_str()),
|
Self::UnknownLocalSessionId(id) => f.write_str(format!("UnknownLocalSessionId({})", id).as_str()),
|
||||||
Self::InvalidPacket => f.write_str("InvalidPacket"),
|
Self::InvalidPacket => f.write_str("InvalidPacket"),
|
||||||
Self::InvalidParameter => f.write_str("InvalidParameter"),
|
Self::InvalidParameter => f.write_str("InvalidParameter"),
|
||||||
Self::FailedAuthentication => f.write_str("FailedAuthentication"),
|
Self::FailedAuthentication => f.write_str("FailedAuthentication"),
|
||||||
|
@ -968,7 +979,7 @@ impl<Layer: ApplicationLayer> ReceiveContext<Layer> {
|
||||||
let plaintext_end = idx;
|
let plaintext_end = idx;
|
||||||
|
|
||||||
idx = safe_write_all(&mut reply_buf, idx, offer_id)?;
|
idx = safe_write_all(&mut reply_buf, idx, offer_id)?;
|
||||||
idx = safe_write_all(&mut reply_buf, idx, &session.id.0.to_le_bytes()[..SESSION_ID_SIZE])?;
|
idx = safe_write_all(&mut reply_buf, idx, session.id.as_bytes())?;
|
||||||
idx = varint_safe_write(&mut reply_buf, idx, 0)?; // they don't need our static public; they have it
|
idx = varint_safe_write(&mut reply_buf, idx, 0)?; // they don't need our static public; they have it
|
||||||
idx = varint_safe_write(&mut reply_buf, idx, 0)?; // no meta-data in counter-offers (could be used in the future)
|
idx = varint_safe_write(&mut reply_buf, idx, 0)?; // no meta-data in counter-offers (could be used in the future)
|
||||||
if let Some(bob_hk_public) = bob_hk_public.as_ref() {
|
if let Some(bob_hk_public) = bob_hk_public.as_ref() {
|
||||||
|
@ -1251,7 +1262,7 @@ fn send_ephemeral_offer<SendFunction: FnMut(&mut [u8])>(
|
||||||
let plaintext_end = idx;
|
let plaintext_end = idx;
|
||||||
|
|
||||||
idx = safe_write_all(&mut packet_buf, idx, &id)?;
|
idx = safe_write_all(&mut packet_buf, idx, &id)?;
|
||||||
idx = safe_write_all(&mut packet_buf, idx, &alice_session_id.0.to_le_bytes()[..SESSION_ID_SIZE])?;
|
idx = safe_write_all(&mut packet_buf, idx, alice_session_id.as_bytes())?;
|
||||||
idx = varint_safe_write(&mut packet_buf, idx, alice_s_public_blob.len() as u64)?;
|
idx = varint_safe_write(&mut packet_buf, idx, alice_s_public_blob.len() as u64)?;
|
||||||
idx = safe_write_all(&mut packet_buf, idx, alice_s_public_blob)?;
|
idx = safe_write_all(&mut packet_buf, idx, alice_s_public_blob)?;
|
||||||
idx = varint_safe_write(&mut packet_buf, idx, alice_metadata.len() as u64)?;
|
idx = varint_safe_write(&mut packet_buf, idx, alice_metadata.len() as u64)?;
|
||||||
|
@ -1480,28 +1491,6 @@ fn parse_dec_key_offer_after_header(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyLifetime {
|
|
||||||
fn new(current_counter: CounterValue, current_time: i64) -> Self {
|
|
||||||
Self {
|
|
||||||
rekey_at_or_after_counter: current_counter
|
|
||||||
.counter_value_after_uses(REKEY_AFTER_USES)
|
|
||||||
.counter_value_after_uses((random::next_u32_secure() % REKEY_AFTER_USES_MAX_JITTER) as u64),
|
|
||||||
hard_expire_at_counter: current_counter.counter_value_after_uses(EXPIRE_AFTER_USES),
|
|
||||||
rekey_at_or_after_timestamp: current_time
|
|
||||||
+ REKEY_AFTER_TIME_MS
|
|
||||||
+ (random::next_u32_secure() % REKEY_AFTER_TIME_MS_MAX_JITTER) as i64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_rekey(&self, counter: CounterValue, current_time: i64) -> bool {
|
|
||||||
counter >= self.rekey_at_or_after_counter || current_time >= self.rekey_at_or_after_timestamp
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expired(&self, counter: CounterValue) -> bool {
|
|
||||||
counter >= self.hard_expire_at_counter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SessionKey {
|
impl SessionKey {
|
||||||
/// Create a new symmetric shared session key and set its key expiration times, etc.
|
/// Create a new symmetric shared session key and set its key expiration times, etc.
|
||||||
fn new(key: Secret<64>, role: Role, current_time: i64, current_counter: CounterValue, ratchet_count: u64, jedi: bool) -> Self {
|
fn new(key: Secret<64>, role: Role, current_time: i64, current_counter: CounterValue, ratchet_count: u64, jedi: bool) -> Self {
|
||||||
|
@ -1565,6 +1554,43 @@ impl SessionKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl KeyLifetime {
|
||||||
|
fn new(current_counter: CounterValue, current_time: i64) -> Self {
|
||||||
|
Self {
|
||||||
|
rekey_at_or_after_counter: current_counter
|
||||||
|
.counter_value_after_uses(REKEY_AFTER_USES)
|
||||||
|
.counter_value_after_uses((random::next_u32_secure() % REKEY_AFTER_USES_MAX_JITTER) as u64),
|
||||||
|
hard_expire_at_counter: current_counter.counter_value_after_uses(EXPIRE_AFTER_USES),
|
||||||
|
rekey_at_or_after_timestamp: current_time
|
||||||
|
+ REKEY_AFTER_TIME_MS
|
||||||
|
+ (random::next_u32_secure() % REKEY_AFTER_TIME_MS_MAX_JITTER) as i64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_rekey(&self, counter: CounterValue, current_time: i64) -> bool {
|
||||||
|
counter >= self.rekey_at_or_after_counter || current_time >= self.rekey_at_or_after_timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expired(&self, counter: CounterValue) -> bool {
|
||||||
|
counter >= self.hard_expire_at_counter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CanonicalHeader {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn make(session_id: SessionId, packet_type: u8, counter: u32) -> Self {
|
||||||
|
CanonicalHeader(
|
||||||
|
(u64::from(session_id) | (packet_type as u64).wrapping_shl(48)).to_le(),
|
||||||
|
counter.to_le(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_bytes(&self) -> &[u8; 12] {
|
||||||
|
memory::as_byte_array(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Shortcut to HMAC data split into two slices.
|
/// Shortcut to HMAC data split into two slices.
|
||||||
fn hmac_sha384_2(key: &[u8], a: &[u8], b: &[u8]) -> [u8; 48] {
|
fn hmac_sha384_2(key: &[u8], a: &[u8], b: &[u8]) -> [u8; 48] {
|
||||||
let mut hmac = HMACSHA384::new(key);
|
let mut hmac = HMACSHA384::new(key);
|
||||||
|
|
Loading…
Add table
Reference in a new issue