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::{
|
||||
ints::SessionId,
|
||||
sessionid::SessionId,
|
||||
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 counter;
|
||||
mod ints;
|
||||
mod sessionid;
|
||||
mod tests;
|
||||
mod zssp;
|
||||
|
||||
pub mod constants;
|
||||
|
||||
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};
|
||||
|
|
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::constants::*;
|
||||
use crate::counter::{Counter, CounterValue};
|
||||
use crate::ints::*;
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// types
|
||||
////////////////////////////////////////////////////////////////
|
||||
use crate::sessionid::SessionId;
|
||||
|
||||
pub enum Error {
|
||||
/// The packet was addressed to an unrecognized local session (should usually be ignored)
|
||||
|
@ -83,6 +79,15 @@ pub enum ReceiveResult<'a, H: ApplicationLayer> {
|
|||
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.
|
||||
///
|
||||
/// 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,
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// functions
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// "Canonical header" for generating 96-bit AES-GCM nonce and for inclusion in key exchange 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)]
|
||||
struct CanonicalHeader(pub u64, pub u32);
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
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::InvalidParameter => f.write_str("InvalidParameter"),
|
||||
Self::FailedAuthentication => f.write_str("FailedAuthentication"),
|
||||
|
@ -968,7 +979,7 @@ impl<Layer: ApplicationLayer> ReceiveContext<Layer> {
|
|||
let plaintext_end = idx;
|
||||
|
||||
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)?; // no meta-data in counter-offers (could be used in the future)
|
||||
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;
|
||||
|
||||
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 = safe_write_all(&mut packet_buf, idx, alice_s_public_blob)?;
|
||||
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 {
|
||||
/// 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 {
|
||||
|
@ -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.
|
||||
fn hmac_sha384_2(key: &[u8], a: &[u8], b: &[u8]) -> [u8; 48] {
|
||||
let mut hmac = HMACSHA384::new(key);
|
||||
|
|
Loading…
Add table
Reference in a new issue