Merge pull request #29 from zerotier/dev

Updated Naming Convention and Ran Cargo Clippy
This commit is contained in:
Monica Moniot
2023-11-21 16:10:17 -05:00
committed by GitHub
26 changed files with 570 additions and 630 deletions
+1 -1
View File
@@ -166,7 +166,7 @@ fn alice_main(
alice_app,
|b: &mut [u8]| alice_out.send(b.to_vec()).is_ok(),
TEST_MTU,
bob_pubkey.clone(),
bob_pubkey,
0,
&[],
);
+1 -1
View File
@@ -136,7 +136,7 @@ fn alice_main(
alice_app,
|b: &mut [u8]| alice_out.send(alloc(b)).is_ok(),
TEST_MTU,
bob_pubkey.clone(),
bob_pubkey,
(),
&[],
);
+21 -21
View File
@@ -182,7 +182,7 @@ pub trait CryptoLayer: Sized {
///
/// Templating ZSSP on this trait lets the code here be almost entirely transport, OS,
/// and use case independent.
pub trait ApplicationLayer<Crypto: CryptoLayer>: Sized {
pub trait ApplicationLayer<C: CryptoLayer>: Sized {
/// Should return the current time in milliseconds. Does not have to be monotonic, nor synced
/// with remote peers (although both of these properties would help reliability slightly).
/// Used to determine if any current handshakes should be resent or timed-out, or if a session
@@ -228,7 +228,7 @@ pub trait ApplicationLayer<Crypto: CryptoLayer>: Sized {
///
/// Corresponds to the "Initiator Disallows Downgrade, π_2" security flag of Transition
/// Algorithm 3 within the ZSSP whitepaper.
fn initiator_disallows_downgrade(&mut self, session: &Arc<Session<Crypto>>) -> bool;
fn initiator_disallows_downgrade(&mut self, session: &Arc<Session<C>>) -> bool;
/// Function to accept sessions after final negotiation.
///
/// The implementor must verify that three arguments, `remote_static_key`, `identity` and
@@ -244,10 +244,10 @@ pub trait ApplicationLayer<Crypto: CryptoLayer>: Sized {
/// Corresponds to the **Accept** call of Transition Algorithm 4 within the ZSSP whitepaper.
fn check_accept_session(
&mut self,
remote_static_key: &Crypto::PublicKey,
remote_static_key: &C::PublicKey,
identity: &[u8],
fingerprint_data: Option<&Crypto::FingerprintData>,
) -> AcceptAction<Crypto>;
fingerprint_data: Option<&C::FingerprintData>,
) -> AcceptAction<C>;
/// Lookup a specific ratchet state based on its ratchet fingerprint.
/// This function will be called whenever Alice attempts to connect to us with a non-empty
@@ -272,7 +272,7 @@ pub trait ApplicationLayer<Crypto: CryptoLayer>: Sized {
fn restore_by_fingerprint(
&mut self,
ratchet_fingerprint: &[u8; RATCHET_SIZE],
) -> Result<Option<(RatchetState, Crypto::FingerprintData)>, std::io::Error>;
) -> Result<Option<(RatchetState, C::FingerprintData)>, std::io::Error>;
/// Lookup the specific ratchet states based on the identity of the peer being communicated with.
/// This function will be called whenever Alice attempts to open a session, or Bob attempts
/// to verify Alice's identity.
@@ -291,9 +291,9 @@ pub trait ApplicationLayer<Crypto: CryptoLayer>: Sized {
/// Corresponds to the **Restore** call of Transition Algorithm 1 and 4 within the ZSSP whitepaper.
fn restore_by_identity(
&mut self,
remote_static_key: &Crypto::PublicKey,
session_data: &Crypto::SessionData,
fingerprint_data: Option<&Crypto::FingerprintData>,
remote_static_key: &C::PublicKey,
session_data: &C::SessionData,
fingerprint_data: Option<&C::FingerprintData>,
) -> Result<Option<RatchetStates>, std::io::Error>;
/// Atomically commit the update specified by `update_data` to storage, or return an error if
/// the update could not be made.
@@ -313,8 +313,8 @@ pub trait ApplicationLayer<Crypto: CryptoLayer>: Sized {
/// Otherwise, when we restart, we will not be allowed to reconnect.
fn save_ratchet_state(
&mut self,
remote_static_key: &Crypto::PublicKey,
session_data: &Crypto::SessionData,
remote_static_key: &C::PublicKey,
session_data: &C::SessionData,
update_data: RatchetUpdate<'_>,
) -> Result<(), std::io::Error>;
@@ -323,7 +323,7 @@ pub trait ApplicationLayer<Crypto: CryptoLayer>: Sized {
/// nothing else. Do not base protocol-level decisions upon the events passed to this function.
#[cfg(feature = "logging")]
#[allow(unused)]
fn event_log(&mut self, event: crate::LogEvent<'_, Crypto>) {}
fn event_log(&mut self, event: crate::LogEvent<'_, C>) {}
}
/// Possible responses that can be made to Hello packets from an anonymous peer.
@@ -350,10 +350,10 @@ pub enum IncomingSessionAction {
/// used by Bob, the responder, at the very last stage of the key exchange.
///
/// Corresponds to the *Accept* callback of Transition Algorithm 4.
pub struct AcceptAction<Crypto: CryptoLayer> {
pub struct AcceptAction<C: CryptoLayer> {
/// The data object to be attached to the session if we successfully connect.
/// If this field is None then we will not connect to this remote peer.
pub session_data: Option<Crypto::SessionData>,
pub session_data: Option<C::SessionData>,
/// Whether or not we will accept a connection with the remote peer when they do not have a
/// ratchet key that we think they should have.
///
@@ -385,8 +385,8 @@ pub trait Sender {
/// A trait to genericize the process of borrowing the resources necessary to repeatedly
/// send packet fragments on some socket or network interface.
///
/// Is implemented by `FnMut(&Arc<Session<Crypto>>) -> Option<(Sender, usize)>` closures.
pub trait SendTo<Crypto: CryptoLayer> {
/// Is implemented by `FnMut(&Arc<Session<C>>) -> Option<(Sender, usize)>` closures.
pub trait SendTo<C: CryptoLayer> {
/// The `Sender` implementation that this `SendTo` implementation will return.
///
/// It is allowed to have a lifetime that is borrowed from the `SendTo` instance
@@ -394,7 +394,7 @@ pub trait SendTo<Crypto: CryptoLayer> {
/// in situations where that is more efficient.
type Sender<'a>: Sender
where
Crypto: 'a,
C: 'a,
Self: 'a;
/// Attempt to process and borrow the resources necessary to repeatedly send fragments of a
/// packet to the given session.
@@ -405,7 +405,7 @@ pub trait SendTo<Crypto: CryptoLayer> {
/// only called once.
///
/// If `None` is returned then sending to this session is cancelled.
fn init_send<'a>(&'a mut self, session: &'a Arc<Session<Crypto>>) -> Option<(Self::Sender<'a>, usize)>;
fn init_send<'a>(&'a mut self, session: &'a Arc<Session<C>>) -> Option<(Self::Sender<'a>, usize)>;
}
impl<F: FnMut(&mut [u8]) -> bool> Sender for F {
@@ -414,9 +414,9 @@ impl<F: FnMut(&mut [u8]) -> bool> Sender for F {
}
}
impl<Crypto: CryptoLayer, F: FnMut(&Arc<Session<Crypto>>) -> Option<(S, usize)>, S: Sender> SendTo<Crypto> for F {
type Sender<'a> = S where Crypto: 'a, F: 'a;
fn init_send<'a>(&'a mut self, session: &'a Arc<Session<Crypto>>) -> Option<(S, usize)> {
impl<C: CryptoLayer, F: FnMut(&Arc<Session<C>>) -> Option<(S, usize)>, S: Sender> SendTo<C> for F {
type Sender<'a> = S where C: 'a, F: 'a;
fn init_send<'a>(&'a mut self, session: &'a Arc<Session<C>>) -> Option<(S, usize)> {
self(session)
}
}
+1 -2
View File
@@ -26,7 +26,7 @@ pub fn respond_to_challenge_in_place(
challenge: &[u8; CHALLENGE_SIZE],
pre_response: &mut [u8; CHALLENGE_SIZE],
) {
if &challenge[POW_START..] == &pre_response[POW_START..] {
if challenge[POW_START..] == pre_response[POW_START..] {
pre_response.copy_from_slice(challenge);
let mut pow = rng.next_u64();
let mut work_buf = [0u8; SHA512_HASH_SIZE];
@@ -79,7 +79,6 @@ impl ChallengeContext {
hasher.write(&c.to_be_bytes());
addr.hash(&mut hasher);
hasher.write(&self.salt);
drop(hasher);
let mut mac = [0u8; SHA512_HASH_SIZE];
hash.finish_and_reset(&mut mac);
+40 -42
View File
@@ -16,18 +16,18 @@ struct PacketMetadata {
creation_time: i64,
}
pub(crate) struct UnassociatedFragCache<Crypto: CryptoLayer> {
pub(crate) struct UnassociatedFragCache<C: CryptoLayer> {
dos_salt: RandomState,
frags_first_unused: usize,
frags_unused_size: usize,
map: [PacketMetadata; MAX_UNASSOCIATED_PACKETS],
frags: [MaybeUninit<Crypto::IncomingPacketBuffer>; MAX_UNASSOCIATED_FRAGMENTS],
frags: [MaybeUninit<C::IncomingPacketBuffer>; MAX_UNASSOCIATED_FRAGMENTS],
map_idx: [u32; MAX_UNASSOCIATED_FRAGMENTS],
}
/// A combination of a hash table cache and a ring buffer for unassociated fragments.
/// Designed specifically to be extremely DDOS resistant.
/// This datastructure takes raw unauthenticated fragments straight from the network.
impl<Crypto: CryptoLayer> UnassociatedFragCache<Crypto> {
impl<C: CryptoLayer> UnassociatedFragCache<C> {
pub(crate) fn new() -> Self {
Self {
dos_salt: RandomState::new(),
@@ -53,11 +53,11 @@ impl<Crypto: CryptoLayer> UnassociatedFragCache<Crypto> {
nonce: &[u8; AES_GCM_NONCE_SIZE],
remote_address: impl Hash,
fragment_size: usize,
fragment: Crypto::IncomingPacketBuffer,
fragment: C::IncomingPacketBuffer,
fragment_no: usize,
fragment_count: usize,
current_time: i64,
ret_assembled: &mut Assembled<Crypto::IncomingPacketBuffer>,
ret_assembled: &mut Assembled<C::IncomingPacketBuffer>,
) -> Option<i64> {
debug_assert!(MAX_FRAGMENTS < MAX_UNASSOCIATED_FRAGMENTS);
if fragment_no >= fragment_count
@@ -95,7 +95,7 @@ impl<Crypto: CryptoLayer> UnassociatedFragCache<Crypto> {
} else if self.map[idx0].key == 0 || self.map[idx1].key == 0 {
if fragment_count > self.frags_unused_size {
// There are not enough free fragment slots so attempt to expire a bunch of entries.
let _ = self.check_for_expiry_inner(Crypto::SETTINGS.resend_time as i64, current_time);
let _ = self.check_for_expiry_inner(C::SETTINGS.resend_time as i64, current_time);
}
if self.map[idx0].key == 0 {
idx0
@@ -104,7 +104,7 @@ impl<Crypto: CryptoLayer> UnassociatedFragCache<Crypto> {
}
} else {
// No room for a new entry so attempt to expire a bunch of entries.
let _ = self.check_for_expiry_inner(Crypto::SETTINGS.resend_time as i64, current_time);
let _ = self.check_for_expiry_inner(C::SETTINGS.resend_time as i64, current_time);
if self.map[idx0].key == 0 {
idx0
} else if self.map[idx1].key == 0 {
@@ -118,7 +118,7 @@ impl<Crypto: CryptoLayer> UnassociatedFragCache<Crypto> {
if self.map[idx].key == 0 {
// This is a new entry so initialize it.
if fragment_count <= self.frags_unused_size {
new_expiry = Some(current_time + Crypto::SETTINGS.fragment_assembly_timeout as i64);
new_expiry = Some(current_time + C::SETTINGS.fragment_assembly_timeout as i64);
let entry = &mut self.map[idx];
entry.key = key;
entry.frags_idx = self.frags_first_unused as u32;
@@ -166,7 +166,7 @@ impl<Crypto: CryptoLayer> UnassociatedFragCache<Crypto> {
}
/// Returns the timestamp at which this function should be called again.
pub(crate) fn check_for_expiry(&mut self, current_time: i64) -> i64 {
self.check_for_expiry_inner(Crypto::SETTINGS.fragment_assembly_timeout as i64, current_time)
self.check_for_expiry_inner(C::SETTINGS.fragment_assembly_timeout as i64, current_time)
}
fn check_for_expiry_inner(&mut self, timeout: i64, current_time: i64) -> i64 {
while self.frags_unused_size < self.frags.len() {
@@ -219,7 +219,7 @@ impl<Crypto: CryptoLayer> UnassociatedFragCache<Crypto> {
}
}
}
impl<Crypto: CryptoLayer> Drop for UnassociatedFragCache<Crypto> {
impl<C: CryptoLayer> Drop for UnassociatedFragCache<C> {
fn drop(&mut self) {
for i in 0..self.map.len() {
if self.map[i].key != 0 {
@@ -243,8 +243,8 @@ fn test_cache() {
r.wrapping_mul(0x2545F4914F6CDD1Du64)
}
use crate::crypto_impl::*;
struct Crypto {}
impl CryptoLayer for Crypto {
struct C {}
impl CryptoLayer for C {
type Rng = rand_core::OsRng;
type PrpEnc = OpenSSLAes256Enc;
type PrpDec = OpenSSLAes256Dec;
@@ -261,7 +261,7 @@ fn test_cache() {
type IncomingPacketBuffer = Vec<u8>;
}
let mut cache = UnassociatedFragCache::<Crypto>::new();
let mut cache = UnassociatedFragCache::<C>::new();
let mut assembled = Assembled::new();
let mut time = 0;
@@ -317,39 +317,37 @@ fn test_cache() {
assert!(assembled.is_empty(), "Cache returned an incomplete packet");
}
}
if r > 200 {
if in_progress.len() > 0 {
let to_remain = (xorshift64_random() as usize % in_progress_fragments) + 16;
while in_progress_fragments > to_remain {
let (id, fragment_count, mut packet) =
in_progress.swap_remove(xorshift64_random() as usize % in_progress.len());
for _ in 0..((xorshift64_random() as usize % packet.len()) + 1) {
let (no, fragment) = packet.swap_remove(xorshift64_random() as usize % packet.len());
if r > 200 && !in_progress.is_empty() {
let to_remain = (xorshift64_random() as usize % in_progress_fragments) + 16;
while in_progress_fragments > to_remain {
let (id, fragment_count, mut packet) =
in_progress.swap_remove(xorshift64_random() as usize % in_progress.len());
for _ in 0..((xorshift64_random() as usize % packet.len()) + 1) {
let (no, fragment) = packet.swap_remove(xorshift64_random() as usize % packet.len());
assembled.clear();
let mut nonce = [0; 12];
nonce[..4].copy_from_slice(&id.to_be_bytes());
cache.assemble(
&nonce,
0,
fragment.len(),
fragment,
no as usize,
fragment_count as usize,
time,
&mut assembled,
);
time += 200;
in_progress_fragments -= 1;
assembled.clear();
let mut nonce = [0; 12];
nonce[..4].copy_from_slice(&id.to_be_bytes());
cache.assemble(
&nonce,
0,
fragment.len(),
fragment,
no as usize,
fragment_count as usize,
time,
&mut assembled,
);
time += 200;
in_progress_fragments -= 1;
if packet.len() > 0 {
assert!(assembled.is_empty(), "Cache returned an incomplete packet");
}
}
if packet.len() > 0 {
in_progress.push((id, fragment_count, packet));
if !packet.is_empty() {
assert!(assembled.is_empty(), "Cache returned an incomplete packet");
}
}
if !packet.is_empty() {
in_progress.push((id, fragment_count, packet));
}
}
}
}
+2 -2
View File
@@ -10,10 +10,10 @@ pub(crate) struct UnassociatedHandshakeCache<Application: CryptoLayer> {
cache: RwLock<CacheInner<Application>>,
}
/// SoA format
struct CacheInner<Crypto: CryptoLayer> {
struct CacheInner<C: CryptoLayer> {
local_ids: [Option<NonZeroU32>; MAX_UNASSOCIATED_HANDSHAKE_STATES],
expiries: [i64; MAX_UNASSOCIATED_HANDSHAKE_STATES],
handshakes: [Option<Arc<StateB2<Crypto>>>; MAX_UNASSOCIATED_HANDSHAKE_STATES],
handshakes: [Option<Arc<StateB2<C>>>; MAX_UNASSOCIATED_HANDSHAKE_STATES],
}
/// Linear-search cache for capping the memory consumption of handshake data.
+5 -1
View File
@@ -31,7 +31,11 @@ pub struct IndexedBinaryHeap<T, P> {
data: Vec<(T, P, usize)>,
map: Vec<(usize, u64)>,
}
impl<T, P: Ord> Default for IndexedBinaryHeap<T, P> {
fn default() -> Self {
Self::new()
}
}
impl<T, P: Ord> IndexedBinaryHeap<T, P> {
/// Create a new, empty binary heap.
pub fn new() -> Self {
+1
View File
@@ -37,6 +37,7 @@
//! - **AES-256**: Single block encryption of header to harden packet fragmentation protocol
//! - **AES-256-GCM**: Authenticated encryption
//#![warn(missing_docs, rust_2018_idioms)]
#![allow(clippy::too_many_arguments, clippy::type_complexity, clippy::assertions_on_constants)]
pub mod crypto;
pub mod crypto_impl;
+21 -21
View File
@@ -5,19 +5,19 @@ use crate::zeta::Session;
/// ZSSP events that might be interesting to log or aggregate into metrics.
#[allow(missing_docs)]
pub enum LogEvent<'a, Crypto: CryptoLayer> {
ResentX1(&'a Arc<Session<Crypto>>),
TimeoutX1(&'a Arc<Session<Crypto>>),
pub enum LogEvent<'a, C: CryptoLayer> {
ResentX1(&'a Arc<Session<C>>),
TimeoutX1(&'a Arc<Session<C>>),
TimeoutX2,
ResentX3(&'a Arc<Session<Crypto>>),
TimeoutX3(&'a Arc<Session<Crypto>>),
ResentKeyConfirm(&'a Arc<Session<Crypto>>),
TimeoutKeyConfirm(&'a Arc<Session<Crypto>>),
StartedRekeyingSentK1(&'a Arc<Session<Crypto>>),
ResentK1(&'a Arc<Session<Crypto>>),
TimeoutK1(&'a Arc<Session<Crypto>>),
ResentK2(&'a Arc<Session<Crypto>>),
TimeoutK2(&'a Arc<Session<Crypto>>),
ResentX3(&'a Arc<Session<C>>),
TimeoutX3(&'a Arc<Session<C>>),
ResentKeyConfirm(&'a Arc<Session<C>>),
TimeoutKeyConfirm(&'a Arc<Session<C>>),
StartedRekeyingSentK1(&'a Arc<Session<C>>),
ResentK1(&'a Arc<Session<C>>),
TimeoutK1(&'a Arc<Session<C>>),
ResentK2(&'a Arc<Session<C>>),
TimeoutK2(&'a Arc<Session<C>>),
/// `(packet_type, packet_counter, fragment_no, fragment_count)`
ReceivedRawFragment(u8, u64, usize, usize),
ReceivedRawX1,
@@ -25,24 +25,24 @@ pub enum LogEvent<'a, Crypto: CryptoLayer> {
X1SucceededChallenge,
X1IsAuthSentX2,
ReceivedRawChallenge,
ChallengeIsAuth(&'a Arc<Session<Crypto>>),
ChallengeIsAuth(&'a Arc<Session<C>>),
ReceivedRawX2,
X2IsAuthSentX3(&'a Arc<Session<Crypto>>),
X2IsAuthSentX3(&'a Arc<Session<C>>),
ReceivedRawX3,
X3IsAuthSentKeyConfirm(&'a Arc<Session<Crypto>>),
X3IsAuthSentKeyConfirm(&'a Arc<Session<C>>),
ReceivedRawKeyConfirm,
KeyConfirmIsAuthSentAck(&'a Arc<Session<Crypto>>),
KeyConfirmIsAuthSentAck(&'a Arc<Session<C>>),
ReceivedRawAck,
AckIsAuth(&'a Arc<Session<Crypto>>),
AckIsAuth(&'a Arc<Session<C>>),
ReceivedRawK1,
K1IsAuthSentK2(&'a Arc<Session<Crypto>>),
K1IsAuthSentK2(&'a Arc<Session<C>>),
ReceivedRawK2,
K2IsAuthSentKeyConfirm(&'a Arc<Session<Crypto>>),
K2IsAuthSentKeyConfirm(&'a Arc<Session<C>>),
ReceivedRawD,
DIsAuthClosedSession(&'a Arc<Session<Crypto>>),
DIsAuthClosedSession(&'a Arc<Session<C>>),
}
impl<'a, Crypto: CryptoLayer> std::fmt::Debug for LogEvent<'a, Crypto> {
impl<'a, C: CryptoLayer> std::fmt::Debug for LogEvent<'a, C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ResentX1(_) => f.debug_tuple("ResentX1").finish(),
+23 -23
View File
@@ -46,7 +46,7 @@ pub enum SendError {
/// Therefore it is no longer capable of sending, receiving or being serviced,
/// so it should be dropped.
#[derive(Clone)]
pub struct ExpiredError<Crypto: CryptoLayer>(pub Arc<Session<Crypto>>);
pub struct ExpiredError<C: CryptoLayer>(pub Arc<Session<C>>);
/// A type of fault occurred because we received a bad packet.
///
@@ -78,12 +78,12 @@ pub enum FaultType {
/// Because an unauthenticated remote peer can force these to occur with specific
/// contained information, it is recommended in production to either drop these
/// immediately, or log them safely to a local output stream and then drop them.
pub struct ByzantineFault<Crypto: CryptoLayer> {
pub struct ByzantineFault<C: CryptoLayer> {
/// The session associated with this fault, if there was one.
///
/// The sender specified this session within their packet, but they were not authenticated,
/// so the sender could theoretically be anyone.
pub session: Option<Arc<Session<Crypto>>>,
pub session: Option<Arc<Session<C>>>,
/// This field is true if this fault caused the session it was attached to to expire.
/// An expired session is no longer "owned" by the ZSSP context.
/// Therefore it is no longer capable of sending, receiving or being serviced,
@@ -123,7 +123,7 @@ pub struct ByzantineFault<Crypto: CryptoLayer> {
/// Keep in mind that when one of these occurs it inherently means that the packet from the remote
/// peer has either not been authenticated or has failed authentication. As such, an attacker could
/// trigger any of these. These errors should only be used for debugging and tracing.
pub enum ReceiveError<Crypto: CryptoLayer> {
pub enum ReceiveError<C: CryptoLayer> {
/// A type of fault that can occur because a remote peer sent us a bad packet.
/// Such packets will be ignored by ZSSP but a user of ZSSP might want to log
/// them for debugging or tracing.
@@ -131,11 +131,11 @@ pub enum ReceiveError<Crypto: CryptoLayer> {
/// Because an unauthenticated remote peer can force these to occur with specific
/// contained information, it is recommended in production to either drop these
/// immediately, or log them safely to a local output stream and then drop them.
ByzantineFault(ByzantineFault<Crypto>),
ByzantineFault(ByzantineFault<C>),
/// Rekeying failed and session secret has reached its hard usage count limit.
/// The associated session will no longer function and has to be dropped.
MaxKeyLifetimeExceeded(Arc<Session<Crypto>>),
MaxKeyLifetimeExceeded(Arc<Session<C>>),
/// Either the `ApplicationLayer::incoming_session` or `ApplicationLayer::check_accept_session`
/// callback rejected the remote peer's attempt to establish a new session.
@@ -147,7 +147,7 @@ pub enum ReceiveError<Crypto: CryptoLayer> {
/// An error was returned by the `output_buffer` passed to receive.
/// The received packet was dropped.
WriteError(std::io::Error, Arc<Session<Crypto>>),
WriteError(std::io::Error, Arc<Session<C>>),
}
macro_rules! fault {
@@ -183,18 +183,18 @@ pub(crate) use fault;
/// Result generated by the context packet receive function, with possible payloads.
#[derive(Clone)]
pub enum ReceiveOk<Crypto: CryptoLayer> {
pub enum ReceiveOk<C: CryptoLayer> {
/// The received packet superficially appeared valid but is not associated with a session yet.
/// This can occur because the packet was only a fragment of a larger packet,
/// or if it was a control packet that does not go through full Noise authentication.
Unassociated,
/// The received packet was authentic and belongs to this specific session.
Associated(Arc<Session<Crypto>>, SessionEvent),
Associated(Arc<Session<C>>, SessionEvent),
/// The received packet was a fragment of a larger packet.
///
/// ***The authenticity of this fragment cannot be fully known yet.***
/// This return value should only be used for debugging and tracing purposes.
Fragment(Arc<Session<Crypto>>),
Fragment(Arc<Session<C>>),
}
/// Something that can occur to an associated session when a packet is received successfully,
/// including receiving a payload of decrypted, authenticated data.
@@ -270,20 +270,20 @@ impl fmt::Display for SendError {
}
impl Error for SendError {}
impl<Crypto: CryptoLayer> fmt::Debug for ExpiredError<Crypto>
impl<C: CryptoLayer> fmt::Debug for ExpiredError<C>
where
Session<Crypto>: fmt::Debug,
Session<C>: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("ExpiredError").field(&self.0).finish()
}
}
impl<Crypto: CryptoLayer> fmt::Display for ExpiredError<Crypto> {
impl<C: CryptoLayer> fmt::Display for ExpiredError<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "session expired")
}
}
impl<Crypto: CryptoLayer> Error for ExpiredError<Crypto> where Session<Crypto>: fmt::Debug {}
impl<C: CryptoLayer> Error for ExpiredError<C> where Session<C>: fmt::Debug {}
impl fmt::Display for FaultType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -301,7 +301,7 @@ impl Error for FaultType {}
// I don't like getter methods but in this case they are the only way to implement
// conditionally compiled struct fields without the feature flag causing breaking changes.
impl<Crypto: CryptoLayer> ByzantineFault<Crypto> {
impl<C: CryptoLayer> ByzantineFault<C> {
/// The file of this implementation of ZSSP from which this error was generated.
#[cfg(feature = "debug")]
pub fn file(&self) -> &'static str {
@@ -316,9 +316,9 @@ impl<Crypto: CryptoLayer> ByzantineFault<Crypto> {
self.line
}
}
impl<Crypto: CryptoLayer> fmt::Debug for ByzantineFault<Crypto>
impl<C: CryptoLayer> fmt::Debug for ByzantineFault<C>
where
Session<Crypto>: fmt::Debug,
Session<C>: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ByzantineFault")
@@ -331,7 +331,7 @@ where
.finish()
}
}
impl<Crypto: CryptoLayer> fmt::Display for ByzantineFault<Crypto> {
impl<C: CryptoLayer> fmt::Display for ByzantineFault<C> {
#[cfg(feature = "debug")]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({}:{})", self.error, self.file, self.line)
@@ -341,11 +341,11 @@ impl<Crypto: CryptoLayer> fmt::Display for ByzantineFault<Crypto> {
self.error.fmt(f)
}
}
impl<Crypto: CryptoLayer> Error for ByzantineFault<Crypto> where Session<Crypto>: fmt::Debug {}
impl<C: CryptoLayer> Error for ByzantineFault<C> where Session<C>: fmt::Debug {}
impl<Crypto: CryptoLayer> fmt::Debug for ReceiveError<Crypto>
impl<C: CryptoLayer> fmt::Debug for ReceiveError<C>
where
Crypto::SessionData: fmt::Debug,
C::SessionData: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
@@ -357,7 +357,7 @@ where
}
}
}
impl<Crypto: CryptoLayer> fmt::Display for ReceiveError<Crypto> {
impl<C: CryptoLayer> fmt::Display for ReceiveError<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ReceiveError::ByzantineFault(e) => e.fmt(f),
@@ -368,4 +368,4 @@ impl<Crypto: CryptoLayer> fmt::Display for ReceiveError<Crypto> {
}
}
}
impl<Crypto: CryptoLayer> Error for ReceiveError<Crypto> where Crypto::SessionData: fmt::Debug {}
impl<C: CryptoLayer> Error for ReceiveError<C> where C::SessionData: fmt::Debug {}
+16 -27
View File
@@ -6,15 +6,15 @@ use crate::application::CryptoLayer;
use crate::crypto::*;
use crate::proto::*;
pub struct SymmetricState<Crypto: CryptoLayer> {
pub struct SymmetricState<C: CryptoLayer> {
k: Zeroizing<[u8; AES_256_KEY_SIZE]>,
ck: Zeroizing<[u8; HASHLEN]>,
h: [u8; HASHLEN],
/// If anyone knows a better way to get rid of the "parameter `App` is never used" error please
/// let me know.
_app: PhantomData<fn() -> Crypto::SessionData>,
_app: PhantomData<fn() -> C::SessionData>,
}
impl<Crypto: CryptoLayer> Clone for SymmetricState<Crypto> {
impl<C: CryptoLayer> Clone for SymmetricState<C> {
fn clone(&self) -> Self {
Self {
k: self.k.clone(),
@@ -25,7 +25,7 @@ impl<Crypto: CryptoLayer> Clone for SymmetricState<Crypto> {
}
}
impl<Crypto: CryptoLayer> SymmetricState<Crypto> {
impl<C: CryptoLayer> SymmetricState<C> {
/// HMAC-SHA512 key derivation based on KBKDF Counter Mode:
/// https://csrc.nist.gov/publications/detail/sp/800-108/rev-1/final.
/// Cryptographically this isn't meaningfully different from
@@ -40,7 +40,7 @@ impl<Crypto: CryptoLayer> SymmetricState<Crypto> {
/// Corresponds to Noise `HKDF`.
fn kbkdf(
&self,
hmac: &mut Crypto::Hmac,
hmac: &mut C::Hmac,
input_key_material: &[u8],
label: &[u8; 4],
num_outputs: u16,
@@ -86,7 +86,7 @@ impl<Crypto: CryptoLayer> SymmetricState<Crypto> {
}
}
/// Corresponds to Noise `MixKey`.
pub fn mix_key(&mut self, hmac: &mut Crypto::Hmac, input_key_material: &[u8]) {
pub fn mix_key(&mut self, hmac: &mut C::Hmac, input_key_material: &[u8]) {
let mut next_ck = Zeroizing::new([0u8; HASHLEN]);
let mut temp_k = Zeroizing::new([0u8; HASHLEN]);
@@ -104,7 +104,7 @@ impl<Crypto: CryptoLayer> SymmetricState<Crypto> {
self.k.clone_from_slice(&temp_k[..AES_256_KEY_SIZE]);
}
/// Corresponds to Noise `MixKey`.
pub fn mix_key_no_init(&mut self, hmac: &mut Crypto::Hmac, input_key_material: &[u8]) {
pub fn mix_key_no_init(&mut self, hmac: &mut C::Hmac, input_key_material: &[u8]) {
let mut next_ck = Zeroizing::new([0u8; HASHLEN]);
self.kbkdf(hmac, input_key_material, LABEL_KBKDF_CHAIN, 2, &mut next_ck, None, None);
@@ -112,13 +112,13 @@ impl<Crypto: CryptoLayer> SymmetricState<Crypto> {
*self.ck = *next_ck;
}
/// Corresponds to Noise `MixHash`.
pub fn mix_hash(&mut self, hash: &mut Crypto::Hash, data: &[u8]) {
pub fn mix_hash(&mut self, hash: &mut C::Hash, data: &[u8]) {
hash.update(&self.h);
hash.update(data);
hash.finish_and_reset(&mut self.h);
}
/// Corresponds to Noise `MixKeyAndHash`.
pub fn mix_key_and_hash(&mut self, hash: &mut Crypto::Hash, hmac: &mut Crypto::Hmac, input_key_material: &[u8]) {
pub fn mix_key_and_hash(&mut self, hash: &mut C::Hash, hmac: &mut C::Hmac, input_key_material: &[u8]) {
let mut next_ck = Zeroizing::new([0u8; HASHLEN]);
let mut temp_h = [0u8; HASHLEN];
let mut temp_k = Zeroizing::new([0u8; HASHLEN]);
@@ -138,12 +138,7 @@ impl<Crypto: CryptoLayer> SymmetricState<Crypto> {
self.k.clone_from_slice(&temp_k[..AES_256_KEY_SIZE]);
}
/// Corresponds to Noise `MixKeyAndHash`.
pub fn mix_key_and_hash_no_init(
&mut self,
hash: &mut Crypto::Hash,
hmac: &mut Crypto::Hmac,
input_key_material: &[u8],
) {
pub fn mix_key_and_hash_no_init(&mut self, hash: &mut C::Hash, hmac: &mut C::Hmac, input_key_material: &[u8]) {
let mut next_ck = Zeroizing::new([0u8; HASHLEN]);
let mut temp_h = [0u8; HASHLEN];
@@ -164,11 +159,11 @@ impl<Crypto: CryptoLayer> SymmetricState<Crypto> {
#[must_use]
pub fn encrypt_and_hash_in_place(
&mut self,
hash: &mut Crypto::Hash,
hash: &mut C::Hash,
iv: [u8; AES_GCM_NONCE_SIZE],
data: &mut [u8],
) -> [u8; AES_GCM_TAG_SIZE] {
let tag = Crypto::Aead::encrypt_in_place(&self.k, &iv, &self.h, data);
let tag = C::Aead::encrypt_in_place(&self.k, &iv, &self.h, data);
hash.update(&self.h);
hash.update(data);
hash.update(&tag);
@@ -179,7 +174,7 @@ impl<Crypto: CryptoLayer> SymmetricState<Crypto> {
#[must_use]
pub fn decrypt_and_hash_in_place(
&mut self,
hash: &mut Crypto::Hash,
hash: &mut C::Hash,
iv: [u8; AES_GCM_NONCE_SIZE],
data: &mut [u8],
tag: [u8; AES_GCM_TAG_SIZE],
@@ -187,25 +182,19 @@ impl<Crypto: CryptoLayer> SymmetricState<Crypto> {
hash.update(&self.h);
hash.update(data);
hash.update(&tag);
let is_auth = Crypto::Aead::decrypt_in_place(&self.k, &iv, &self.h, data, tag.as_ref().try_into().unwrap());
let is_auth = C::Aead::decrypt_in_place(&self.k, &iv, &self.h, data, tag.as_ref().try_into().unwrap());
hash.finish_and_reset(&mut self.h);
is_auth
}
/// Corresponds to Noise `Split`.
pub fn split(self, hmac: &mut Crypto::Hmac, key1: &mut [u8; HASHLEN], key2: &mut [u8; HASHLEN]) {
pub fn split(self, hmac: &mut C::Hmac, key1: &mut [u8; HASHLEN], key2: &mut [u8; HASHLEN]) {
self.kbkdf(hmac, &[], LABEL_KBKDF_CHAIN, 2, key1, Some(key2), None);
}
/// Get an additional symmetric key (ASK) that is a collision resistant hash of the transcript,
/// is forward secrect and is cryptographically independent from all other produced keys.
/// Based on Noise's unstable ASK mechanism, using KBKDF instead of HKDF.
/// https://github.com/noiseprotocol/noise_wiki/wiki/Additional-Symmetric-Keys.
pub fn get_ask(
&self,
hmac: &mut Crypto::Hmac,
label: &[u8; 4],
key1: &mut [u8; HASHLEN],
key2: &mut [u8; HASHLEN],
) {
pub fn get_ask(&self, hmac: &mut C::Hmac, label: &[u8; 4], key1: &mut [u8; HASHLEN], key2: &mut [u8; HASHLEN]) {
self.kbkdf(hmac, &self.h, label, 2, key1, Some(key2), None);
}
/// Used for internally debugging a key exchange.
+191 -200
View File
File diff suppressed because it is too large Load Diff
+37 -37
View File
@@ -37,43 +37,43 @@ pub(crate) use log;
/// defragment incoming packets that are not yet associated with a session.
///
/// Internally this is just a clonable Arc, so it can be safely shared with multiple threads.
pub struct Context<Crypto: CryptoLayer>(pub Arc<ContextInner<Crypto>>);
impl<Crypto: CryptoLayer> Clone for Context<Crypto> {
pub struct Context<C: CryptoLayer>(pub Arc<ContextInner<C>>);
impl<C: CryptoLayer> Clone for Context<C> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
pub(crate) type SessionMap<Crypto> = RwLock<HashMap<NonZeroU32, Weak<Session<Crypto>>>>;
pub(crate) type SessionMap<C> = RwLock<HashMap<NonZeroU32, Weak<Session<C>>>>;
pub(crate) type SessionQueue<Crypto> = IndexedBinaryHeap<Weak<Session<Crypto>>, Reverse<i64>>;
pub(crate) type SessionQueue<C> = IndexedBinaryHeap<Weak<Session<C>>, Reverse<i64>>;
/// The internal memory of the ZSSP context.
/// One of these is allocated as an `Arc` to initialize this implementation of ZSSP.
/// See `Context::new`.
pub struct ContextInner<Crypto: CryptoLayer> {
pub struct ContextInner<C: CryptoLayer> {
/// The `CryptoRng` instance that was passed to ZSSP when this context was created.
pub rng: Mutex<Crypto::Rng>,
pub rng: Mutex<C::Rng>,
pub(crate) next_service_time: AtomicI64,
pub(crate) s_secret: Crypto::KeyPair,
pub(crate) s_secret: C::KeyPair,
/// `session_queue -> state_machine_lock -> state -> session_map`
pub(crate) session_queue: Mutex<SessionQueue<Crypto>>,
pub(crate) session_queue: Mutex<SessionQueue<C>>,
/// `session_queue -> state_machine_lock -> state -> session_map`
pub(crate) session_map: SessionMap<Crypto>,
pub(crate) unassociated_defrag_cache: Mutex<UnassociatedFragCache<Crypto>>,
pub(crate) unassociated_handshake_states: UnassociatedHandshakeCache<Crypto>,
pub(crate) session_map: SessionMap<C>,
pub(crate) unassociated_defrag_cache: Mutex<UnassociatedFragCache<C>>,
pub(crate) unassociated_handshake_states: UnassociatedHandshakeCache<C>,
pub(crate) challenge: ChallengeContext,
}
impl<Crypto: CryptoLayer> ContextInner<Crypto> {
impl<C: CryptoLayer> ContextInner<C> {
pub(crate) fn reduce_next_service_time(&self, time: i64) -> Option<i64> {
(self.next_service_time.fetch_min(time, Ordering::Relaxed) > time).then_some(time)
}
}
fn parse_fragment_header<Crypto: CryptoLayer>(
fn parse_fragment_header<C: CryptoLayer>(
incoming_fragment: &[u8],
) -> Result<(usize, usize, [u8; AES_GCM_NONCE_SIZE]), ReceiveError<Crypto>> {
) -> Result<(usize, usize, [u8; AES_GCM_NONCE_SIZE]), ReceiveError<C>> {
let fragment_no = incoming_fragment[FRAGMENT_NO_IDX] as usize;
let fragment_count = incoming_fragment[FRAGMENT_COUNT_IDX] as usize;
if fragment_no >= fragment_count || fragment_count > MAX_FRAGMENTS {
@@ -122,9 +122,9 @@ fn send_with_fragmentation<PrpEnc: Aes256Enc>(
true
}
impl<Crypto: CryptoLayer> Context<Crypto> {
impl<C: CryptoLayer> Context<C> {
/// Create a new session context.
pub fn new(static_secret_key: Crypto::KeyPair, mut rng: Crypto::Rng) -> Self {
pub fn new(static_secret_key: C::KeyPair, mut rng: C::Rng) -> Self {
let challenge = ChallengeContext::new(&mut rng);
Self(Arc::new(ContextInner {
rng: Mutex::new(rng),
@@ -160,15 +160,15 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
/// object
/// * `identity` - Payload to be sent to Bob that contains the information necessary
/// for the upper protocol to authenticate and approve of Alice's identity.
pub fn open<App: ApplicationLayer<Crypto>>(
pub fn open<App: ApplicationLayer<C>>(
&self,
app: App,
send: impl Sender,
mut mtu: usize,
static_remote_key: Crypto::PublicKey,
session_data: Crypto::SessionData,
static_remote_key: C::PublicKey,
session_data: C::SessionData,
identity: &[u8],
) -> Result<(Arc<Session<Crypto>>, Option<i64>), OpenError> {
) -> Result<(Arc<Session<C>>, Option<i64>), OpenError> {
mtu = mtu.max(MIN_TRANSPORT_MTU);
if identity.len() > IDENTITY_MAX_SIZE {
return Err(OpenError::IdentityTooLarge);
@@ -199,16 +199,16 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
/// * `remote_address` - Whatever the remote address is, as long as you can Hash it
/// * `incoming_fragment_buf` - Buffer containing incoming wire packet (the context takes ownership)
/// * `output_buffer` - Buffer to receive decrypted and authenticated object data
pub fn receive<'a, App: ApplicationLayer<Crypto>>(
pub fn receive<App: ApplicationLayer<C>>(
&self,
mut app: App,
mut send_unassociated_reply: impl Sender,
mut send_unassociated_mtu: usize,
mut send_to: impl SendTo<Crypto>,
mut send_to: impl SendTo<C>,
remote_address: &impl Hash,
mut incoming_fragment_buf: Crypto::IncomingPacketBuffer,
mut incoming_fragment_buf: C::IncomingPacketBuffer,
output_buffer: impl Write,
) -> Result<(ReceiveOk<Crypto>, Option<i64>), ReceiveError<Crypto>> {
) -> Result<(ReceiveOk<C>, Option<i64>), ReceiveError<C>> {
use crate::result::FaultType::*;
let ctx = &self.0;
send_unassociated_mtu = send_unassociated_mtu.max(MIN_TRANSPORT_MTU);
@@ -324,7 +324,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
&mut incoming_fragment_buf.as_mut()[HEADER_SIZE..]
};
let send_associated = |packet: &mut [u8], hk_send: Option<&Crypto::PrpEnc>| {
let send_associated = |packet: &mut [u8], hk_send: Option<&C::PrpEnc>| {
if let Some((sender, mut mtu)) = send_to.init_send(&session) {
mtu = mtu.max(MIN_TRANSPORT_MTU);
send_with_fragmentation(sender, mtu, packet, hk_send);
@@ -416,7 +416,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
// Check for and handle PACKET_TYPE_ALICE_NOISE_XK_PATTERN_3
let zeta = self.0.unassociated_handshake_states.get(kid_recv);
if let Some(zeta) = zeta {
Crypto::PrpDec::new(&zeta.hk_recv).decrypt_in_place(
C::PrpDec::new(&zeta.hk_recv).decrypt_in_place(
(&mut incoming_fragment[HEADER_AUTH_START..HEADER_AUTH_END])
.try_into()
.unwrap(),
@@ -535,7 +535,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
}
// Process recv challenge layer.
let challenge_start = assembled_packet.len() - CHALLENGE_SIZE;
let hash = &mut Crypto::Hash::new();
let hash = &mut C::Hash::new();
match app.incoming_session() {
IncomingSessionAction::Allow => {}
IncomingSessionAction::Challenge => {
@@ -616,7 +616,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
/// * `data` - Data to send
pub fn send(
&self,
session: &Session<Crypto>,
session: &Session<C>,
send: impl Sender,
mtu_sized_buffer: &mut [u8],
data: &[u8],
@@ -631,7 +631,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
///
/// * `app` - Interface to application using ZSSP
/// * `send_to` - Function to get a sender and an MTU to send something over an active session
pub fn service<App: ApplicationLayer<Crypto>>(&self, mut app: App, mut send_to: impl SendTo<Crypto>) -> i64 {
pub fn service<App: ApplicationLayer<C>>(&self, mut app: App, mut send_to: impl SendTo<C>) -> i64 {
let current_time = app.time();
let next_service_time = loop {
match self.service_inner(&mut app, send_to, current_time) {
@@ -639,10 +639,10 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
Err((_, s)) => send_to = s,
}
};
let max_interval = Crypto::SETTINGS
let max_interval = C::SETTINGS
.fragment_assembly_timeout
.min(Crypto::SETTINGS.rekey_timeout)
.min(Crypto::SETTINGS.initial_offer_timeout);
.min(C::SETTINGS.rekey_timeout)
.min(C::SETTINGS.initial_offer_timeout);
(next_service_time - current_time).min(max_interval as i64)
}
@@ -667,20 +667,20 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
///
/// * `app` - Interface to application using ZSSP
/// * `send_to` - Function to get a sender and an MTU to send something over an active session
pub fn service_scheduled<App: ApplicationLayer<Crypto>>(
pub fn service_scheduled<App: ApplicationLayer<C>>(
&self,
mut app: App,
send_to: impl SendTo<Crypto>,
) -> Result<i64, ExpiredError<Crypto>> {
send_to: impl SendTo<C>,
) -> Result<i64, ExpiredError<C>> {
let current_time = app.time();
self.service_inner(&mut app, send_to, current_time).map_err(|e| e.0)
}
fn service_inner<App: ApplicationLayer<Crypto>, F: SendTo<Crypto>>(
fn service_inner<App: ApplicationLayer<C>, F: SendTo<C>>(
&self,
app: &mut App,
mut send_to: F,
current_time: i64,
) -> Result<i64, (ExpiredError<Crypto>, F)> {
) -> Result<i64, (ExpiredError<C>, F)> {
let ctx = &self.0;
let mut session_queue = ctx.session_queue.lock().unwrap();
let mut queue_service_time = i64::MAX;
+1 -1
View File
@@ -3,7 +3,7 @@ authors = ["ZeroTier, Inc. <contact@zerotier.com>", "Adam Ierymenko <adam.ieryme
edition = "2021"
license = "MPL-2.0"
name = "zssp-proto"
version = "0.1.0"
version = "0.3.0"
[lib]
name = "zssp_proto"
+2 -4
View File
@@ -65,9 +65,7 @@ impl CryptoLayer for TestApplication {
type SessionData = u128;
}
#[allow(unused)]
impl ApplicationLayer for &mut TestApplication {
type Crypto = TestApplication;
impl ApplicationLayer<TestApplication> for &mut TestApplication {
fn hello_requires_recognized_ratchet(&mut self) -> bool {
false
}
@@ -159,7 +157,7 @@ fn alice_main(
&mut alice_app,
|b| alice_out.send(b).is_ok(),
TEST_MTU,
bob_pubkey.clone(),
bob_pubkey,
0,
Vec::new(),
)
+1 -3
View File
@@ -44,9 +44,7 @@ impl CryptoLayer for MyApp {
/// In this example for simplicity we won't be hooking up ratchet keys to a filesystem backend.
/// They are dropped and peers ignore if they are missing.
#[allow(unused)]
impl ApplicationLayer for &mut MyApp {
type Crypto = MyApp;
impl ApplicationLayer<MyApp> for &mut MyApp {
fn hello_requires_recognized_ratchet(&mut self) -> bool {
false
}
+9 -12
View File
@@ -139,10 +139,7 @@ pub trait CryptoLayer: Sized {
///
/// Templating ZSSP on this trait lets the code here be almost entirely transport, OS,
/// and use case independent.
pub trait ApplicationLayer: Sized {
/// Specifies which concrete set of cryptography types will be used by this application.
type Crypto: CryptoLayer;
pub trait ApplicationLayer<C: CryptoLayer>: Sized {
/// Should return the current time in milliseconds. Does not have to be monotonic, nor synced
/// with remote peers (although both of these properties would help reliability slightly).
/// Used to determine if any current handshakes should be resent or timed-out, or if a session
@@ -174,7 +171,7 @@ pub trait ApplicationLayer: Sized {
/// least one party is misconfigured and got their ratchet keys corrupted or lost, or Bob has
/// been compromised and is being impersonated. An attacker must at least have Bob's private
/// static key to be able to ask Alice to downgrade.
fn initiator_disallows_downgrade(&mut self, session: &Arc<Session<Self::Crypto>>) -> bool;
fn initiator_disallows_downgrade(&mut self, session: &Arc<Session<C>>) -> bool;
/// Function to accept sessions after final negotiation.
/// The second argument is the identity that the remote peer sent us. The application
/// must verify this identity is associated with the remote peer's static key.
@@ -183,9 +180,9 @@ pub trait ApplicationLayer: Sized {
/// before returning.
fn check_accept_session(
&mut self,
remote_static_key: &<Self::Crypto as CryptoLayer>::PublicKey,
remote_static_key: &<C as CryptoLayer>::PublicKey,
identity: &[u8],
) -> AcceptAction<Self::Crypto>;
) -> AcceptAction<C>;
/// Lookup a specific ratchet state based on its ratchet fingerprint.
/// This function will be called whenever Alice attempts to connect to us with a non-empty
@@ -213,8 +210,8 @@ pub trait ApplicationLayer: Sized {
/// function `ApplicationLayer::check_accept_session`.
fn restore_by_identity(
&mut self,
remote_static_key: &<Self::Crypto as CryptoLayer>::PublicKey,
session_data: &<Self::Crypto as CryptoLayer>::SessionData,
remote_static_key: &<C as CryptoLayer>::PublicKey,
session_data: &<C as CryptoLayer>::SessionData,
) -> Result<Option<RatchetStates>, std::io::Error>;
/// Atomically commit the update specified by `update_data` to storage, or return an error if
/// the update could not be made.
@@ -234,8 +231,8 @@ pub trait ApplicationLayer: Sized {
/// Otherwise, when we restart, we will not be allowed to reconnect.
fn save_ratchet_state(
&mut self,
remote_static_key: &<Self::Crypto as CryptoLayer>::PublicKey,
session_data: &<Self::Crypto as CryptoLayer>::SessionData,
remote_static_key: &<C as CryptoLayer>::PublicKey,
session_data: &<C as CryptoLayer>::SessionData,
update_data: RatchetUpdate<'_>,
) -> Result<(), std::io::Error>;
@@ -244,7 +241,7 @@ pub trait ApplicationLayer: Sized {
/// nothing else. Do not base protocol-level decisions upon the events passed to this function.
#[cfg(feature = "logging")]
#[allow(unused)]
fn event_log(&mut self, event: LogEvent<'_, Self::Crypto>) {}
fn event_log(&mut self, event: LogEvent<'_, C>) {}
}
/// A collection of fields specifying how to complete the key exchange with a specific remote peer,
+1 -2
View File
@@ -24,7 +24,7 @@ pub fn respond_to_challenge_in_place<Rng: RngCore + CryptoRng, Hash: Sha512Hash>
challenge: &[u8; CHALLENGE_SIZE],
pre_response: &mut [u8; CHALLENGE_SIZE],
) {
if &challenge[POW_START..] == &pre_response[POW_START..] {
if challenge[POW_START..] == pre_response[POW_START..] {
pre_response.copy_from_slice(challenge);
let mut pow = rng.next_u64();
loop {
@@ -80,7 +80,6 @@ impl ChallengeContext {
hasher.write(&c.to_be_bytes());
addr.hash(&mut hasher);
hasher.write(&self.salt);
drop(hasher);
let mac = h.finish();
mac[..MAC_SIZE].try_into().unwrap()
+44 -53
View File
@@ -31,21 +31,21 @@ pub(crate) use log;
/// defragment incoming packets that are not yet associated with a session.
///
/// Internally this is just a clonable Arc, so it can be safely shared with multiple threads.
pub struct Context<Crypto: CryptoLayer>(Arc<ContextInner<Crypto>>);
impl<Crypto: CryptoLayer> Clone for Context<Crypto> {
pub struct Context<C: CryptoLayer>(Arc<ContextInner<C>>);
impl<C: CryptoLayer> Clone for Context<C> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
pub(crate) type SessionMap<Crypto> = RefCell<HashMap<NonZeroU32, Weak<Session<Crypto>>>>;
pub(crate) type SessionMap<C> = RefCell<HashMap<NonZeroU32, Weak<Session<C>>>>;
pub(crate) struct ContextInner<Crypto: CryptoLayer> {
pub(crate) rng: RefCell<Crypto::Rng>,
pub(crate) s_secret: Crypto::KeyPair,
pub(crate) session_map: SessionMap<Crypto>,
pub(crate) sessions: RefCell<HashMap<*const Session<Crypto>, Weak<Session<Crypto>>>>,
pub(crate) b2_map: RefCell<HashMap<NonZeroU32, StateB2<Crypto>>>,
pub(crate) struct ContextInner<C: CryptoLayer> {
pub(crate) rng: RefCell<C::Rng>,
pub(crate) s_secret: C::KeyPair,
pub(crate) session_map: SessionMap<C>,
pub(crate) sessions: RefCell<HashMap<*const Session<C>, Weak<Session<C>>>>,
pub(crate) b2_map: RefCell<HashMap<NonZeroU32, StateB2<C>>>,
hello_defrag: RefCell<DefragBuffer>,
challenge: RefCell<ChallengeContext>,
@@ -62,9 +62,9 @@ fn to_packet_nonce(n: &[u8; AES_GCM_NONCE_SIZE]) -> &[u8; PACKET_NONCE_SIZE] {
(&n[n.len() - PACKET_NONCE_SIZE..]).try_into().unwrap()
}
impl<Crypto: CryptoLayer> Context<Crypto> {
impl<C: CryptoLayer> Context<C> {
/// Create a new session context.
pub fn new(static_secret_key: Crypto::KeyPair, mut rng: Crypto::Rng) -> Self {
pub fn new(static_secret_key: C::KeyPair, mut rng: C::Rng) -> Self {
let challenge = ChallengeContext::new(&mut rng);
Self(Arc::new(ContextInner {
rng: RefCell::new(rng),
@@ -93,18 +93,15 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
/// object
/// * `identity` - Payload to be sent to Bob that contains the information necessary
/// for the upper protocol to authenticate and approve of Alice's identity
pub fn open<App: ApplicationLayer>(
pub fn open<App: ApplicationLayer<C>>(
&mut self,
app: App,
send: impl FnMut(Vec<u8>) -> bool,
mut mtu: usize,
static_remote_key: Crypto::PublicKey,
session_data: Crypto::SessionData,
static_remote_key: C::PublicKey,
session_data: C::SessionData,
identity: Vec<u8>,
) -> Result<Arc<Session<Crypto>>, OpenError>
where
App: ApplicationLayer<Crypto = Crypto>,
{
) -> Result<Arc<Session<C>>, OpenError> {
mtu = mtu.max(MIN_TRANSPORT_MTU);
if identity.len() > IDENTITY_MAX_SIZE {
return Err(OpenError::IdentityTooLarge);
@@ -120,7 +117,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
identity,
|Packet(kid, nonce, payload): &Packet| {
// Process fragmentation layer.
let _ = send_with_fragmentation::<Crypto>(send, mtu, *kid, to_packet_nonce(&nonce), payload, None);
let _ = send_with_fragmentation::<C>(send, mtu, *kid, to_packet_nonce(nonce), payload, None);
},
)
}
@@ -133,18 +130,15 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
/// * `send_to` - Function to get senders for existing sessions, permitting MTU and path lookup
/// * `remote_address` - Whatever the remote address is, as long as you can Hash it
/// * `raw_fragment` - Buffer containing incoming wire packet
pub fn receive<App, SendFn: FnMut(Vec<u8>) -> bool>(
pub fn receive<App: ApplicationLayer<C>, SendFn: FnMut(Vec<u8>) -> bool>(
&mut self,
mut app: App,
send_unassociated_reply: impl FnMut(Vec<u8>) -> bool,
mut send_unassociated_mtu: usize,
send_to: impl FnOnce(&Arc<Session<Crypto>>) -> Option<(SendFn, usize)>,
send_to: impl FnOnce(&Arc<Session<C>>) -> Option<(SendFn, usize)>,
remote_address: &impl Hash,
raw_fragment: Vec<u8>,
) -> Result<ReceiveOk<Crypto>, ReceiveError>
where
App: ApplicationLayer<Crypto = Crypto>,
{
) -> Result<ReceiveOk<C>, ReceiveError> {
use crate::result::FaultType::*;
send_unassociated_mtu = send_unassociated_mtu.max(MIN_TRANSPORT_MTU);
let ctx = &self.0;
@@ -158,7 +152,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
let mut zeta = session.0.borrow_mut();
let result =
zeta.defrag
.received_fragment::<App>(raw_fragment, app.time(), |n, frag_no, frag_count| {
.received_fragment::<C>(raw_fragment, app.time(), |n, frag_no, frag_count| {
let (p, c) = from_nonce(n);
if p != PACKET_TYPE_DATA {
log!(app, ReceivedRawFragment(p, c, frag_no, frag_count));
@@ -193,11 +187,11 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|Packet(kid, nonce, payload): &Packet, hk: Option<&[u8; AES_256_KEY_SIZE]>| {
if let Some((send_fragment, mut mtu)) = send_to(&session) {
mtu = mtu.max(MIN_TRANSPORT_MTU);
let _ = send_with_fragmentation::<Crypto>(
let _ = send_with_fragmentation::<C>(
send_fragment,
mtu,
*kid,
to_packet_nonce(&nonce),
to_packet_nonce(nonce),
payload,
hk,
);
@@ -207,7 +201,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
let (p, _) = from_nonce(&pn);
let ret = match p {
PACKET_TYPE_DATA => {
received_payload_in_place::<App>(
received_payload_in_place::<C>(
&mut zeta,
kid_recv,
to_aes_nonce(&pn),
@@ -221,7 +215,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
&mut zeta,
&session,
&mut app,
&ctx,
ctx,
kid_recv,
to_aes_nonce(&pn),
assembled_packet,
@@ -297,7 +291,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
}
PACKET_TYPE_SESSION_REJECTED => {
log!(app, ReceivedRawD);
received_d_trans::<App>(&mut zeta, kid_recv, to_aes_nonce(&pn), assembled_packet)?;
received_d_trans::<C>(&mut zeta, kid_recv, to_aes_nonce(&pn), assembled_packet)?;
log!(app, DIsAuthClosedSession(&session));
SessionEvent::Rejected
}
@@ -315,7 +309,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
// Process recv fragmentation layer.
let result =
zeta.defrag
.received_fragment::<App>(raw_fragment, app.time(), |n, frag_no, frag_count| {
.received_fragment::<C>(raw_fragment, app.time(), |n, frag_no, frag_count| {
let (p, c) = from_nonce(n);
log!(app, ReceivedRawFragment(p, c, frag_no, frag_count));
if p == PACKET_TYPE_HANDSHAKE_COMPLETION && c == 0 {
@@ -334,11 +328,11 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
kid_recv,
assembled_packet,
|Packet(kid, nonce, payload), hk| {
let _ = send_with_fragmentation::<Crypto>(
let _ = send_with_fragmentation::<C>(
send_unassociated_reply,
send_unassociated_mtu,
*kid,
to_packet_nonce(&nonce),
to_packet_nonce(nonce),
payload,
hk,
);
@@ -362,7 +356,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
}
} else {
// Process recv fragmentation layer.
let result = ctx.hello_defrag.borrow_mut().received_fragment::<App>(
let result = ctx.hello_defrag.borrow_mut().received_fragment::<C>(
raw_fragment,
app.time(),
|n, frag_no, frag_count| {
@@ -381,7 +375,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
log!(app, ReceivedRawX1);
// Process recv challenge layer.
let challenge_start = assembled_packet.len() - CHALLENGE_SIZE;
let result = ctx.challenge.borrow_mut().process_hello::<Crypto::Hash>(
let result = ctx.challenge.borrow_mut().process_hello::<C::Hash>(
remote_address,
(&assembled_packet[challenge_start..]).try_into().unwrap(),
);
@@ -391,7 +385,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
challenge_packet.extend(&assembled_packet[..KID_SIZE]);
challenge_packet.extend(&challenge);
let nonce = to_nonce(PACKET_TYPE_CHALLENGE, ctx.rng.borrow_mut().next_u64());
let _ = send_with_fragmentation::<Crypto>(
let _ = send_with_fragmentation::<C>(
send_unassociated_reply,
send_unassociated_mtu,
0,
@@ -409,15 +403,15 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
// Process recv zeta layer.
received_x1_trans(
&mut app,
&ctx,
ctx,
to_aes_nonce(&n),
assembled_packet,
|Packet(kid, nonce, payload), hk| {
let _ = send_with_fragmentation::<Crypto>(
let _ = send_with_fragmentation::<C>(
send_unassociated_reply,
send_unassociated_mtu,
*kid,
to_packet_nonce(&nonce),
to_packet_nonce(nonce),
payload,
Some(hk),
);
@@ -436,7 +430,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
{
if let Some(Some(session)) = ctx.session_map.borrow_mut().get(&kid_recv).map(|r| r.upgrade()) {
let mut zeta = session.0.borrow_mut();
respond_to_challenge::<App>(
respond_to_challenge::<C>(
&mut zeta,
&ctx.rng,
&assembled_packet[KID_SIZE..].try_into().unwrap(),
@@ -466,7 +460,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
/// * `payload` - Data to send
pub fn send(
&mut self,
session: &Arc<Session<Crypto>>,
session: &Arc<Session<C>>,
send: impl FnMut(Vec<u8>) -> bool,
mut mtu: usize,
payload: Vec<u8>,
@@ -474,8 +468,8 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
debug_assert_eq!(session.0.borrow().ctx.as_ptr(), Arc::as_ptr(&self.0));
mtu = mtu.max(MIN_TRANSPORT_MTU);
let mut zeta = session.0.borrow_mut();
send_payload::<Crypto>(&mut zeta, payload, |Packet(kid, nonce, payload), hk| {
let result = send_with_fragmentation::<Crypto>(send, mtu, *kid, to_packet_nonce(nonce), &payload, hk);
send_payload::<C>(&mut zeta, payload, |Packet(kid, nonce, payload), hk| {
let result = send_with_fragmentation::<C>(send, mtu, *kid, to_packet_nonce(nonce), payload, hk);
if matches!(result, Err(true)) {
return Err(SendError::DataTooLarge);
}
@@ -490,14 +484,11 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
/// a problem. It is completely fine to call this function more often than the returned interval.
///
/// * `send_to` - Function to get a sender and an MTU to send something over an active session
pub fn service<App, SendFn: FnMut(Vec<u8>) -> bool>(
pub fn service<App: ApplicationLayer<C>, SendFn: FnMut(Vec<u8>) -> bool>(
&mut self,
mut app: App,
mut send_to: impl FnMut(&Arc<Session<Crypto>>) -> Option<(SendFn, usize)>,
) -> i64
where
App: ApplicationLayer<Crypto = Crypto>,
{
mut send_to: impl FnMut(&Arc<Session<C>>) -> Option<(SendFn, usize)>,
) -> i64 {
let ctx = &self.0;
let sessions = ctx.sessions.borrow_mut();
let current_time = app.time();
@@ -514,11 +505,11 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|Packet(kid, nonce, payload): &Packet, hk| {
if let Some((send_fragment, mut mtu)) = send_to(&session) {
mtu = mtu.max(MIN_TRANSPORT_MTU);
let _ = send_with_fragmentation::<Crypto>(
let _ = send_with_fragmentation::<C>(
send_fragment,
mtu,
*kid,
to_packet_nonce(&nonce),
to_packet_nonce(nonce),
payload,
hk,
);
@@ -530,6 +521,6 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
}
}
ctx.hello_defrag.borrow_mut().service(current_time);
(Crypto::SETTINGS.resend_time as i64).min(next_timer - current_time)
(C::SETTINGS.resend_time as i64).min(next_timer - current_time)
}
}
+4 -9
View File
@@ -29,9 +29,9 @@ impl AesGcmAead for AesGcmCrate {
buffer: &mut [u8],
) -> [u8; AES_GCM_TAG_SIZE] {
let key = Key::<Aes256Gcm>::from_slice(key);
let mut cipher = Aes256Gcm::new(&key);
let mut cipher = Aes256Gcm::new(key);
cipher
.encrypt_in_place_detached(&Nonce::from_slice(iv), aad.unwrap_or(&[]), buffer)
.encrypt_in_place_detached(Nonce::from_slice(iv), aad.unwrap_or(&[]), buffer)
.unwrap()
.try_into()
.unwrap()
@@ -45,14 +45,9 @@ impl AesGcmAead for AesGcmCrate {
tag: &[u8; AES_GCM_TAG_SIZE],
) -> bool {
let key = Key::<Aes256Gcm>::from_slice(key);
let mut cipher = Aes256Gcm::new(&key);
let mut cipher = Aes256Gcm::new(key);
cipher
.decrypt_in_place_detached(
&Nonce::from_slice(iv),
aad.unwrap_or(&[]),
buffer,
&Tag::from_slice(tag),
)
.decrypt_in_place_detached(Nonce::from_slice(iv), aad.unwrap_or(&[]), buffer, Tag::from_slice(tag))
.is_ok()
}
}

Some files were not shown because too many files have changed in this diff Show More