More simplification, move logic to get subset array of session ID into SessionId itself.

This commit is contained in:
Adam Ierymenko 2022-12-16 09:41:28 -05:00
parent 611ca97ee4
commit 0e518f679e
5 changed files with 113 additions and 104 deletions

View file

@ -6,7 +6,7 @@ use zerotier_crypto::{
};
use crate::{
ints::SessionId,
sessionid::SessionId,
zssp::{ReceiveContext, Session},
};

View file

@ -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,
}

View file

@ -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
View 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)))
}
}

View file

@ -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);