From 8f40d08d5ba920b5a53bf9ccf3bf3ccfc1222706 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Wed, 17 Jan 2024 17:43:53 -0500 Subject: [PATCH] fixed ratchet sync bug --- performance/examples/basic_test.rs | 30 ++- performance/examples/benchmark.rs | 8 +- performance/src/application.rs | 25 ++- performance/src/proto.rs | 12 +- performance/src/ratchet_state.rs | 259 ++++++++++++++++--------- performance/src/zeta.rs | 183 +++++++++-------- performance/src/zssp.rs | 21 +- reference/examples/extensive_test.rs | 35 +++- reference/examples/ping_pong.rs | 8 +- reference/src/application.rs | 25 ++- reference/src/context.rs | 28 +-- reference/src/fragmentation.rs | 8 +- reference/src/proto.rs | 5 +- reference/src/ratchet_state.rs | 267 ++++++++++++++++--------- reference/src/result.rs | 4 +- reference/src/zeta.rs | 280 ++++++++++++++------------- whitepaper/zssp.pdf | Bin 423683 -> 426253 bytes whitepaper/zssp.tex | 77 ++++---- 18 files changed, 770 insertions(+), 505 deletions(-) diff --git a/performance/examples/basic_test.rs b/performance/examples/basic_test.rs index ecde22c..37cf5ba 100644 --- a/performance/examples/basic_test.rs +++ b/performance/examples/basic_test.rs @@ -1,3 +1,4 @@ +use std::collections::hash_map::Entry; use std::collections::HashMap; use std::iter::ExactSizeIterator; use std::str::FromStr; @@ -10,7 +11,7 @@ use rand_core::OsRng; use rand_core::RngCore; use zssp::application::{ - AcceptAction, ApplicationLayer, CryptoLayer, IncomingSessionAction, RatchetState, RatchetStates, RatchetUpdate, + AcceptAction, ApplicationLayer, CompareAndSwap, CryptoLayer, IncomingSessionAction, RatchetState, RatchetStates, Settings, RATCHET_SIZE, }; use zssp::crypto::P384KeyPair; @@ -114,14 +115,29 @@ impl ApplicationLayer for &TestApplication { &mut self, remote_static_key: &CrateP384PublicKey, session_data: &u128, - update_data: RatchetUpdate<'_>, - ) -> Result<(), std::io::Error> { + update_data: CompareAndSwap<'_>, + ) -> Result { let mut ratchets = self.ratchets.lock().unwrap(); - ratchets.peer_map.insert(*session_data, update_data.to_states()); + match ratchets.peer_map.entry(*session_data) { + Entry::Occupied(mut entry) => { + if update_data.compare(&entry.get()) { + entry.insert(update_data.to_new_states()); + } else { + return Ok(false); + } + } + Entry::Vacant(entry) => { + if update_data.cur_is_initial_states() { + entry.insert(update_data.to_new_states()); + } else { + return Ok(false); + } + } + } if let Some(rf) = update_data.added_fingerprint() { - ratchets.rf_map.insert(*rf, update_data.state1.clone()); - println!("[{}] new ratchet #{}", self.name, update_data.state1.chain_len()); + ratchets.rf_map.insert(*rf, update_data.new_state1.clone()); + println!("[{}] new ratchet #{}", self.name, update_data.new_state1.chain_len()); } if let Some(rf) = update_data.deleted_fingerprint1() { ratchets.rf_map.remove(rf); @@ -129,7 +145,7 @@ impl ApplicationLayer for &TestApplication { if let Some(rf) = update_data.deleted_fingerprint2() { ratchets.rf_map.remove(rf); } - Ok(()) + Ok(true) } fn time(&mut self) -> i64 { diff --git a/performance/examples/benchmark.rs b/performance/examples/benchmark.rs index 02c060f..edb77cd 100644 --- a/performance/examples/benchmark.rs +++ b/performance/examples/benchmark.rs @@ -8,7 +8,7 @@ use rand_core::OsRng; use rand_core::RngCore; use zssp::application::{ - AcceptAction, ApplicationLayer, IncomingSessionAction, RatchetState, RatchetStates, RatchetUpdate, RATCHET_SIZE, + AcceptAction, ApplicationLayer, CompareAndSwap, IncomingSessionAction, RatchetState, RatchetStates, RATCHET_SIZE, }; use zssp::crypto::P384KeyPair; use zssp::crypto_impl::*; @@ -106,9 +106,9 @@ impl ApplicationLayer for &TestApplication { &mut self, remote_static_key: &CrateP384PublicKey, session_data: &(), - update_data: RatchetUpdate<'_>, - ) -> Result<(), std::io::Error> { - Ok(()) + update_data: CompareAndSwap<'_>, + ) -> Result { + Ok(true) } fn time(&mut self) -> i64 { diff --git a/performance/src/application.rs b/performance/src/application.rs index 9ef5882..d568e97 100644 --- a/performance/src/application.rs +++ b/performance/src/application.rs @@ -296,11 +296,16 @@ pub trait ApplicationLayer: Sized { session_data: &C::SessionData, fingerprint_data: Option<&C::FingerprintData>, ) -> Result, std::io::Error>; - /// Atomically commit the update specified by `update_data` to storage, or return an error if - /// the update could not be made. - /// The implementor is free to choose how to apply these updates to storage. + /// Atomically compare-and-swap (a.k.a. compare-exchange) `update` to storage. /// - /// If this returns `Err(())`, the packet which triggered this function to be called will be + /// If `update.cur_state1` and `update.cur_state2` are currently in storage, they must + /// be swapped with `update.new_state1` and `update.new_state2`. + /// Otherwise, storage must remain unchanged. + /// + /// `Ok(true)` must be returned if the compare-and-swap was successful. + /// `Ok(false)` must be returned if comparison failed and the swap was cancelled. + /// + /// If this returns `Err`, the packet which triggered this function to be called will be /// dropped, and no session state will be mutated, preserving synchronization. The remote peer /// will eventually resend that packet and so this function will be called again. /// @@ -312,12 +317,20 @@ pub trait ApplicationLayer: Sized { /// This function may also save state to volatile storage, in which case all peers which connect /// to us will have to allow downgrade across the board. /// Otherwise, when we restart, we will not be allowed to reconnect. + /// + /// # Security + /// Implementations must not perform comparison operations (equals, less than, etc.) directly + /// on two ratchet keys. Instead, comparison operations must be performed indirectly + /// upon their ratchet fingerprints. If two ratchet states have the same ratchet fingerprint, + /// it should be assumed that they also have the same ratchet key. + /// + /// The implementations of `PartialEq` for `RatchetState` and `RatchetStates` do this by default. fn save_ratchet_state( &mut self, remote_static_key: &C::PublicKey, session_data: &C::SessionData, - update_data: RatchetUpdate<'_>, - ) -> Result<(), std::io::Error>; + update: CompareAndSwap<'_>, + ) -> Result; /// Receives a stream of events that occur during an execution of ZSSP. /// These are provided for debugging, logging or metrics purposes, and must be used for diff --git a/performance/src/proto.rs b/performance/src/proto.rs index 5b1c3cb..2435b34 100644 --- a/performance/src/proto.rs +++ b/performance/src/proto.rs @@ -124,14 +124,12 @@ pub(crate) const PACKET_TYPE_DATA: u8 = 8; pub(crate) const PACKET_TYPE_CHALLENGE: u8 = 9; pub(crate) const PACKET_TYPE_USES_COUNTER_RANGE: std::ops::Range = 3..9; -pub(crate) const HANDSHAKE_HELLO_MIN_SIZE: usize = - KID_SIZE + P384_PUBLIC_KEY_SIZE + KYBER_PUBLIC_KEY_SIZE + AES_GCM_TAG_SIZE + AES_GCM_TAG_SIZE; -pub(crate) const HANDSHAKE_HELLO_MAX_SIZE: usize = HANDSHAKE_HELLO_MIN_SIZE + RATCHET_SIZE + RATCHET_SIZE; +pub(crate) const HANDSHAKE_HELLO_SIZE: usize = + KID_SIZE + P384_PUBLIC_KEY_SIZE + KYBER_PUBLIC_KEY_SIZE + AES_GCM_TAG_SIZE + 2 * RATCHET_SIZE + AES_GCM_TAG_SIZE; -pub(crate) const HANDSHAKE_HELLO_CHALLENGE_MIN_SIZE: usize = HANDSHAKE_HELLO_MIN_SIZE + CHALLENGE_SIZE; -pub(crate) const HANDSHAKE_HELLO_CHALLENGE_MAX_SIZE: usize = HANDSHAKE_HELLO_MAX_SIZE + CHALLENGE_SIZE; +pub(crate) const HANDSHAKE_HELLO_CHALLENGE_SIZE: usize = HANDSHAKE_HELLO_SIZE + CHALLENGE_SIZE; -pub(crate) const HEADERED_HANDSHAKE_HELLO_CHALLENGE_MAX_SIZE: usize = HANDSHAKE_HELLO_CHALLENGE_MAX_SIZE + HEADER_SIZE; +pub(crate) const HEADERED_HANDSHAKE_HELLO_CHALLENGE_SIZE: usize = HANDSHAKE_HELLO_CHALLENGE_SIZE + HEADER_SIZE; pub(crate) const HANDSHAKE_RESPONSE_SIZE: usize = P384_PUBLIC_KEY_SIZE + KYBER_CIPHERTEXT_SIZE + AES_GCM_TAG_SIZE + KID_SIZE + AES_GCM_TAG_SIZE; @@ -184,6 +182,6 @@ pub(crate) const MAX_UNASSOCIATED_FRAGMENTS: usize = 32 * 32; /// The maximum size a packet that is not associated to a session may be. /// Excludes the size of headers for fragmentation. -pub(crate) const MAX_UNASSOCIATED_PACKET_SIZE: usize = HANDSHAKE_HELLO_CHALLENGE_MAX_SIZE; +pub(crate) const MAX_UNASSOCIATED_PACKET_SIZE: usize = HANDSHAKE_HELLO_CHALLENGE_SIZE; pub(crate) const SESSION_MAX_FRAGMENTS_OOO: usize = 64; diff --git a/performance/src/ratchet_state.rs b/performance/src/ratchet_state.rs index b12aa38..b41faef 100644 --- a/performance/src/ratchet_state.rs +++ b/performance/src/ratchet_state.rs @@ -14,30 +14,23 @@ use crate::proto::*; #[derive(Clone, Eq)] pub struct RatchetState { pub(crate) key: Zeroizing<[u8; RATCHET_SIZE]>, - pub(crate) fingerprint: Option>, + pub(crate) fingerprint: Zeroizing<[u8; RATCHET_SIZE]>, pub(crate) chain_len: u64, } impl PartialEq for RatchetState { fn eq(&self, other: &Self) -> bool { - let ret = match (self.fingerprint.as_ref(), other.fingerprint.as_ref()) { - (Some(rf1), Some(rf2)) => secure_eq(rf1, rf2), - (None, None) => true, - _ => false, - }; - ret & secure_eq(&self.key, &other.key) & (self.chain_len == other.chain_len) + self.fingerprint.eq(&other.fingerprint) & (self.chain_len == other.chain_len) } } impl std::hash::Hash for RatchetState { fn hash(&self, state: &mut H) { - if let Some(rf) = &self.fingerprint { - state.write_u64(u64::from_ne_bytes(rf[..8].try_into().unwrap())) - } + state.write(self.fingerprint.as_ref()) } } impl RatchetState { /// Creates a new ratchet state from the given ratchet key, ratchet fingerprint and chain length. pub fn new(key: Zeroizing<[u8; RATCHET_SIZE]>, fingerprint: Zeroizing<[u8; RATCHET_SIZE]>, chain_len: u64) -> Self { - RatchetState { key, fingerprint: Some(fingerprint), chain_len } + RatchetState { key, fingerprint, chain_len } } /// Creates a new ratchet state from the given ratchet key, ratchet fingerprint and chain length. /// @@ -46,28 +39,10 @@ impl RatchetState { pub fn new_raw(key: [u8; RATCHET_SIZE], fingerprint: [u8; RATCHET_SIZE], chain_len: u64) -> Self { RatchetState { key: Zeroizing::new(key), - fingerprint: Some(Zeroizing::new(fingerprint)), + fingerprint: Zeroizing::new(fingerprint), chain_len, } } - /// The ratchet key for this ratchet state. This is directly mixed into the master secret of a - /// session and so is very sensitive. All operations upon a ratchet key must be implemented - /// in constant time. The user should prefer to do nothing with the ratchet key besides copying - /// it to or from a storage device. - /// - /// If `fingerprint` returns `None` then this is the "empty" ratchet state and the key will be - /// all zeros. - pub fn key(&self) -> &[u8; RATCHET_SIZE] { - &self.key - } - /// Ratchet keys and fingerprints are "chained together", where each set is derived from the - /// previous set. - /// - /// This function outputs the total length of that chain, as in the total number of previous - /// ratchet states that this ratchet state was derived from. - pub fn chain_len(&self) -> u64 { - self.chain_len - } /// Creates a new "empty" ratchet state, where the ratchet fingerprint is the /// empty string, the ratchet key is all zeros, and the chain length is 0. /// @@ -75,7 +50,7 @@ impl RatchetState { pub fn empty() -> Self { RatchetState { key: Zeroizing::new([0u8; RATCHET_SIZE]), - fingerprint: None, + fingerprint: Zeroizing::new([0u8; RATCHET_SIZE]), chain_len: 0, } } @@ -99,15 +74,15 @@ impl RatchetState { Self::new(rk, rf, 1) } - /// Returns true if this is the "empty" ratchet state, where the ratchet fingerprint is the - /// empty string, the ratchet key is all zeros, and the chain length is 0. - pub fn is_empty(&self) -> bool { - self.fingerprint.is_none() - } - /// Checks if the fingerprint of this ratchet state equals the fingerprint contained in argument - /// `rf`. Uses constant time equality. - pub fn fingerprint_eq(&self, rf: &[u8; RATCHET_SIZE]) -> bool { - self.fingerprint.as_ref().map_or(false, |rf0| secure_eq(rf0, rf)) + /// The ratchet key for this ratchet state. This is directly mixed into the master secret of a + /// session and so is very sensitive. All operations upon a ratchet key must be implemented + /// in constant time. The user should prefer to do nothing with the ratchet key besides copying + /// it to or from a storage device. + /// + /// If `fingerprint` returns `None` then this is the "empty" ratchet state and the key will be + /// all zeros. + pub fn key(&self) -> &[u8; RATCHET_SIZE] { + &self.key } /// The ratchet fingerprint for this ratchet state. /// @@ -118,8 +93,43 @@ impl RatchetState { /// but the security of ZSSP can survive having this value leaked. /// Operations on a ratchet fingerprint should be implemented in constant time, /// but it is ok if they are not. - pub fn fingerprint(&self) -> Option<&[u8; RATCHET_SIZE]> { - self.fingerprint.as_deref() + pub fn fingerprint(&self) -> &[u8; RATCHET_SIZE] { + &self.fingerprint + } + /// Ratchet keys and fingerprints are "chained together", where each set is derived from the + /// previous set. + /// + /// This function outputs the total length of that chain, as in the total number of previous + /// ratchet states that this ratchet state was derived from. + pub fn chain_len(&self) -> u64 { + self.chain_len + } + /// Returns true if this is the "empty" ratchet state, where the ratchet fingerprint is the + /// empty string, the ratchet key is all zeros, and the chain length is 0. + pub fn is_empty(&self) -> bool { + secure_eq(self.fingerprint(), &[0u8; RATCHET_SIZE]) + } + /// Checks if the fingerprint of this ratchet state equals the + /// fingerprint contained in argument `rf`. + /// + /// Uses constant time equality. + pub fn fingerprint_eq(&self, rf: &[u8; RATCHET_SIZE]) -> bool { + secure_eq(self.fingerprint(), rf) + } + /// Returns true if the fingerprint of argument `this` equals the fingerprint represented by + /// argument `rf`. + /// If `this` is `None`, `this` is considered to be "null", as in a ratchet state that does not + /// exist or has been deleted. + /// If `rf` is `None`, `rf` is considered to be "null" as well. + /// If `rf` is `Some(None)`, `rf` is considered to be the empty ratchet fingerprint. + /// + /// Uses constant time equality. + pub fn fingerprint_eq_nullable(this: Option<&Self>, rf: Option<&[u8; RATCHET_SIZE]>) -> bool { + match (this, rf) { + (Some(this), Some(rf)) => this.fingerprint_eq(rf), + (None, None) => true, + _ => false, + } } } impl Default for RatchetState { @@ -137,7 +147,8 @@ impl Default for RatchetState { pub struct RatchetStates { /// The first ratchet state from the pair. pub state1: RatchetState, - /// The second ratchet state from the pair. It can, and usually will be `None`. + /// The second ratchet state from the pair. + /// It can, and usually will be `None`, which means that this ratchet state is "null". pub state2: Option, } impl RatchetStates { @@ -172,7 +183,6 @@ impl Default for RatchetStates { Self::new_initial_states() } } - /// A set of references to ratchet states specifying how a remote peer's persistent storage should /// be updated. This struct is designed to provide any and all potentially needed data for /// maintaining a store of these ratchet states. It should be straightforward to commit these updates @@ -181,69 +191,138 @@ impl Default for RatchetStates { /// There will only be up to two ratchet states saved to storage at a time per peer. /// Every time a third ratchet state is generated, a previous ratchet state will be deleted. /// -/// These are sensitive values should they ought to be securely stored, with restricted read-write -/// permissions if stored on disk. +/// As the name implies, these updates should be applied as one atomic compare-and-swap operation. +/// If the two ratchet states currently in storage equal `cur_state1` and `cur_state2`, +/// then `new_state1` and `new_state2` should overwrite them. +/// If not, then storage must not be modified. /// -/// To prevent desync or resource leakage, these updates should be committed atomically. If that is -/// not possible, then new ratchet states should be written before old ratchet states are deleted +/// Ratchet keys must never be checked for equality. Instead, if two ratchet states have equal +/// ratchet fingerprints, it should be assumed that they also have equal ratchet keys. +/// This policy exists to reduce the security impact of timing and other side-channel attacks. +/// +/// These are sensitive values and ought to be securely stored, with restricted read-write +/// permissions if stored on disk. #[derive(Clone)] -pub struct RatchetUpdate<'a> { - /// The ratchet key and fingerprint to store in the first slot. - pub state1: &'a RatchetState, - /// The ratchet key and fingerprint to store in the second slot. - pub state2: Option<&'a RatchetState>, - /// Whether `state1` is a brand new ratchet state, or if it was previously saved. - pub state1_was_just_added: bool, - /// A previous ratchet key and fingerprint that now must be deleted from storage. - /// This will have been a previously given value of `state1` or `state2`. - pub deleted_state1: Option<&'a RatchetState>, - /// A previous ratchet key and fingerprint that now must be deleted from storage. - /// It is extremely rare that this field is occupied. - pub deleted_state2: Option<&'a RatchetState>, +pub struct CompareAndSwap<'a> { + /// The ratchet state to store in the first slot. + pub new_state1: &'a RatchetState, + /// The ratchet state to store in the second slot. + /// A value of `None` implies that the second slot should be set to null. + pub new_state2: Option<&'a RatchetState>, + /// This field is `true` if and only if `new_state1 != cur_state1` and `new_state1 != cur_state2`. + /// + /// This implies whether `state1` is a brand new ratchet state, or if it was previously saved. + pub new_state1_was_just_added: bool, + /// The ratchet state that we expect to see in the first slot. + /// If this value is not currently stored in the first slot, the entire update must be aborted. + pub cur_state1: &'a RatchetState, + /// The ratchet state that we expect to see in the second slot. + /// A value of `None` implies we expect the second slot to be set to "null". + /// + /// If this value is not currently stored in the second slot, the entire update must be aborted. + pub cur_state2: Option<&'a RatchetState>, + /// This field is `true` if and only if `cur_state1 != new_state1` and `cur_state1 != new_state2`. + pub cur_state1_was_just_deleted: bool, + /// This field is `true` if and only if `cur_state2 != new_state1` and `cur_state2 != new_state2`. + pub cur_state2_was_just_deleted: bool, } -impl<'a> RatchetUpdate<'a> { - /// Returns the final `RatchetStates` that should be the only thing saved after this update is - /// fully committed. Future calls to `ApplicationLayer::restore_by_identity` should return this struct. - pub fn to_states(&self) -> RatchetStates { - RatchetStates::new(self.state1.clone(), self.state2.cloned()) +impl<'a> CompareAndSwap<'a> { + pub(crate) fn new( + new_state1: &'a RatchetState, + new_state2: Option<&'a RatchetState>, + new_state1_was_just_added: bool, + cur_state1: &'a RatchetState, + cur_state2: Option<&'a RatchetState>, + cur_state1_was_just_deleted: bool, + cur_state2_was_just_deleted: bool, + ) -> Self { + Self { + new_state1, + new_state2, + new_state1_was_just_added, + cur_state1, + cur_state2, + cur_state1_was_just_deleted, + cur_state2_was_just_deleted, + } + } + /// Returns the final `RatchetStates` that must be swapped into storage if this update is + /// fully committed. + /// Future calls to `ApplicationLayer::restore_by_identity` should return this struct. + pub fn to_new_states(&self) -> RatchetStates { + RatchetStates::new(self.new_state1.clone(), self.new_state2.cloned()) + } + /// Returns the `RatchetStates` that is expected to currently be in storage for this peer. + /// This value must be compared with the current value in storage, and if they are equal, + /// the return value of `CompareAndSwap::to_new_states` must overwrite it. + pub fn to_cur_states(&self) -> RatchetStates { + RatchetStates::new(self.cur_state1.clone(), self.cur_state2.cloned()) } /// If this update specifies adding a brand new ratchet fingerprint, this function will return it. /// The returned ratchet fingerprint will always be the ratchet fingerprint of field `state1`. /// - /// If a fingerprint is returned then it is guaranteed that `state1_was_just_added` will be true. + /// If a fingerprint is returned then it is guaranteed that `state1_was_just_added` will be `true`. pub fn added_fingerprint(&self) -> Option<&[u8; RATCHET_SIZE]> { - if self.state1_was_just_added { - self.state1.fingerprint() - } else { - None - } + self.new_state1_was_just_added.then_some(self.new_state1.fingerprint()) } - /// If this updated specifies to delete an old ratchet fingerprint, this function will return it. - /// The returned ratchet fingerprint will always be the ratchet fingerprint of field - /// `deleted_state1`. + /// If this updated specifies to delete `cur_state1`, and `cur_state1` has a non-zero ratchet + /// fingerprint, this function will return the ratchet fingerprint. + /// + /// If `cur_state1` is not being deleted (i.e. `cur_state1 == new_state1` or + /// `cur_state1 == new_state2`), then this will return `None`. /// /// There may be a second ratchet fingerprint to be deleted, which function /// `deleted_fingerprint2` will return. pub fn deleted_fingerprint1(&self) -> Option<&[u8; RATCHET_SIZE]> { - if let Some(rs) = &self.deleted_state1 { - rs.fingerprint() - } else { - None + if self.cur_state1_was_just_deleted { + if !self.cur_state1.is_empty() { + return Some(self.cur_state1.fingerprint()); + } } + None } - /// If this updated specifies to delete two ratchet fingerprints, this function will return the - /// second one. `deleted_fingerprint1` will return the first one. - /// The returned ratchet fingerprint will always be the ratchet fingerprint of field - /// `deleted_state2`. + /// If this updated specifies to delete `cur_state2`, and `cur_state2` has a non-zero ratchet + /// fingerprint, this function will return the ratchet fingerprint. /// - /// It is exceptionally rare that there will be more than one ratchet fingerprint to be deleted. - /// Care should be taken to make sure that this update will still be correctly committed in the - /// rare event that this returns `Some`. + /// If `cur_state2` is not being deleted (i.e. `cur_state2 == new_state1` or + /// `cur_state2 == new_state2`), then this will return `None`. + /// + /// There may be a second ratchet fingerprint to be deleted, which function + /// `deleted_fingerprint1` will return. pub fn deleted_fingerprint2(&self) -> Option<&[u8; RATCHET_SIZE]> { - if let Some(rs) = &self.deleted_state2 { - rs.fingerprint() - } else { - None + if self.cur_state2_was_just_deleted { + if let Some(rf) = self.cur_state2 { + if !rf.is_empty() { + return Some(rf.fingerprint()); + } + } } + None + } + /// Returns true if the currently stored ratchet states are expected to be the initial ratchet + /// states. This is the default value for a peer's ratchet states in the event they could not + /// be found in storage. + /// + /// If a peer could not be found in storage, and this function returns true, + /// then the peer should be added to storage with the new ratchet states specified by this + /// `CompareAndSwap` struct. + pub fn cur_is_initial_states(&self) -> bool { + self.cur_state1.is_empty() && self.cur_state2.is_none() + } + /// Compares the ratchet fingerprints of `cur_state1` and `cur_state2` with `rf1` and `rf2`. + /// If they are equal this function will return `true`. + /// + /// If this function returns `true`, then the implementation may proceed to swap out the ratchet + /// states these fingerprints come from with `new_state1` and `new_state2`. + pub fn compare_fingerprints(&self, rf1: &[u8; RATCHET_SIZE], rf2: Option<&[u8; RATCHET_SIZE]>) -> bool { + self.cur_state1.fingerprint_eq(rf1) & RatchetState::fingerprint_eq_nullable(self.cur_state2, rf2) + } + /// Compares `cur_state1` and `cur_state2` with `other.state1` and `other.state2`. + /// If they are equal this function will return `true`. + /// + /// If this function returns `true`, then the implementation may proceed to swap `other.state1` + /// and `other.state2` with `new_state1` and `new_state2`. + pub fn compare(&self, other: &RatchetStates) -> bool { + self.cur_state1.eq(&other.state1) & self.cur_state2.eq(&other.state2.as_ref()) } } diff --git a/performance/src/zeta.rs b/performance/src/zeta.rs index 5ef163a..7536419 100644 --- a/performance/src/zeta.rs +++ b/performance/src/zeta.rs @@ -97,7 +97,7 @@ pub(crate) struct StateA1 { e_secret: C::KeyPair, e1_secret: C::Kem, identity: ArrayVec, - x1: ArrayVec, + x1: ArrayVec, } pub(crate) struct StateA3 { @@ -280,7 +280,7 @@ fn create_a1_state( // ... // -> e, es, e1 let mut noise = SymmetricState::::initialize(PROTOCOL_NAME_NOISE_XK); - let mut x1 = ArrayVec::::new(); + let mut x1 = ArrayVec::::new(); x1.extend([0u8; HEADER_SIZE]); // Noise process prologue. let kid = kid_recv.get().to_ne_bytes(); @@ -299,12 +299,9 @@ fn create_a1_state( x1.extend(tag); // Process message pattern 1 payload. let i = x1.len(); - if let Some(rf) = ratchet_state1.fingerprint() { - x1.try_extend_from_slice(rf).unwrap(); - } - if let Some(Some(rf)) = ratchet_state2.map(|rs| rs.fingerprint()) { - x1.try_extend_from_slice(rf).unwrap(); - } + x1.try_extend_from_slice(ratchet_state1.fingerprint()).unwrap(); + x1.try_extend_from_slice(ratchet_state2.map_or(&[0u8; RATCHET_SIZE], |r| r.fingerprint())) + .unwrap(); let tag = noise.encrypt_and_hash_in_place(hash, to_nonce(PACKET_TYPE_HANDSHAKE_HELLO, 1), &mut x1[i..]); x1.extend(tag); @@ -426,11 +423,11 @@ pub(crate) fn received_x1_trans>( // ... // -> e, es, e1 // <- e, ee, ekem1, psk - if !(HANDSHAKE_HELLO_MIN_SIZE..=HANDSHAKE_HELLO_MAX_SIZE).contains(&x1.len()) { + if x1.len() != HANDSHAKE_HELLO_SIZE { return Err(fault!(InvalidPacket, true)); } - if n[AES_GCM_NONCE_SIZE - 8..] != x1[x1.len() - 8..] { + if !secure_eq(&n[AES_GCM_NONCE_SIZE - 8..], &x1[x1.len() - 8..]) { return Err(fault!(FailedAuth, true)); } let hmac = &mut C::Hmac::new(); @@ -460,35 +457,48 @@ pub(crate) fn received_x1_trans>( let e1_end = j; i = k; // Process message pattern 1 payload. - let k = x1.len(); - let j = k - AES_GCM_TAG_SIZE; + let j = i + RATCHET_SIZE + RATCHET_SIZE; + let k = j + AES_GCM_TAG_SIZE; let tag = x1[j..k].try_into().unwrap(); if !noise.decrypt_and_hash_in_place(hash, to_nonce(PACKET_TYPE_HANDSHAKE_HELLO, 1), &mut x1[i..j], tag) { return Err(fault!(FailedAuth, true)); } + debug_assert_eq!(k, x1.len()); + let rf1 = &x1[i..i + RATCHET_SIZE]; + let rf2 = &x1[i + RATCHET_SIZE..j]; let mut lookup_data = None; let mut ratchet_state = None; - while i + RATCHET_SIZE <= j { - match app.restore_by_fingerprint((&x1[i..i + RATCHET_SIZE]).try_into().unwrap()) { + if !secure_eq(rf1, &[0u8; RATCHET_SIZE]) { + match app.restore_by_fingerprint(rf1.try_into().unwrap()) { Ok(None) => {} Ok(Some((rs, data))) => { lookup_data = Some(data); ratchet_state = Some(rs); - break; } Err(e) => return Err(ReceiveError::StorageError(e)), } - i += RATCHET_SIZE; } - let ratchet_state = if let Some(rs) = ratchet_state { - rs - } else { - if app.hello_requires_recognized_ratchet() { - return Err(fault!(FailedAuth, true)); + if ratchet_state.is_none() { + if !secure_eq(rf2, &[0u8; RATCHET_SIZE]) { + match app.restore_by_fingerprint(rf1.try_into().unwrap()) { + Ok(None) => {} + Ok(Some((rs, data))) => { + lookup_data = Some(data); + ratchet_state = Some(rs); + } + Err(e) => return Err(ReceiveError::StorageError(e)), + } } - RatchetState::empty() - }; + if ratchet_state.is_none() { + if app.hello_requires_recognized_ratchet() { + return Err(fault!(FailedAuth, true)); + } + } + } + // If we get to this point and haven't found a full ratchet state, + // set it to the empty ratchet state. + let ratchet_state = ratchet_state.unwrap_or_default(); let mut hk_recv = Zeroizing::new([0u8; HASHLEN]); let mut hk_send = Zeroizing::new([0u8; HASHLEN]); @@ -589,7 +599,7 @@ pub(crate) fn received_x2_trans>( return Err(fault!(UnknownLocalKeyId, true, session)); } let (_, c) = from_nonce(n); - if c >= COUNTER_WINDOW_MAX_SKIP_AHEAD || n[AES_GCM_NONCE_SIZE - 3..] != x2[x2.len() - 3..] { + if c >= COUNTER_WINDOW_MAX_SKIP_AHEAD || !secure_eq(&n[AES_GCM_NONCE_SIZE - 3..], &x2[x2.len() - 3..]) { return Err(fault!(FailedAuth, true, session)); } @@ -690,23 +700,27 @@ pub(crate) fn received_x2_trans>( let new_ratchet_state = create_ratchet_state(hmac, &noise, chain_len); - let (ratchet_to_preserve, ratchet_to_delete) = if ratchet_i == 1 { - (Some(&state.ratchet_state1), state.ratchet_state2.as_ref()) + let ratchet_to_preserve = if ratchet_i == 1 { + Some(&state.ratchet_state1) } else { - (state.ratchet_state2.as_ref(), Some(&state.ratchet_state1)) + state.ratchet_state2.as_ref() }; - app.save_ratchet_state( + let result = app.save_ratchet_state( &session.s_remote, &session.session_data, - RatchetUpdate { - state1: &new_ratchet_state, - state2: ratchet_to_preserve, - state1_was_just_added: true, - deleted_state1: ratchet_to_delete, - deleted_state2: None, - }, - ) - .map_err(|e| ReceiveError::StorageError(e))?; + CompareAndSwap::new( + &new_ratchet_state, + ratchet_to_preserve, + true, + &state.ratchet_state1, + state.ratchet_state2.as_ref(), + ratchet_i == 2, + ratchet_i == 1, + ), + ); + if !result.map_err(ReceiveError::StorageError)? { + return Err(fault!(OutOfSequence, true, session, true)); + } let mut kek_recv = Zeroizing::new([0u8; HASHLEN]); let mut kek_send = Zeroizing::new([0u8; HASHLEN]); @@ -736,7 +750,7 @@ pub(crate) fn received_x2_trans>( a1 } else { // This return is unreachable. - return Err(fault!(FailedAuth, true, session)); + return Err(fault!(FailedAuth, true, session, true)); }; state.beta = ZetaAutomata::A3(Box::new(StateA3 { identity: a1.identity.clone(), x3: x3.clone() })); resend_timer @@ -858,7 +872,7 @@ pub(crate) fn received_x3_trans>( let mut should_warn_missing_ratchet = false; if (zeta.ratchet_state != state1) & (Some(&zeta.ratchet_state) != state2.as_ref()) { - if !responder_disallows_downgrade && zeta.ratchet_state.fingerprint().is_none() { + if !responder_disallows_downgrade && zeta.ratchet_state.is_empty() { should_warn_missing_ratchet = true; } else { if !responder_silently_rejects { @@ -877,18 +891,14 @@ pub(crate) fn received_x3_trans>( noise.split(hmac, &mut nk_send, &mut nk_recv); // We must make sure the ratchet key is saved before we transition. - app.save_ratchet_state( + let result = app.save_ratchet_state( &s_remote, &session_data, - RatchetUpdate { - state1: &new_ratchet_state, - state2: None, - state1_was_just_added: true, - deleted_state1: Some(&state1), - deleted_state2: state2.as_ref(), - }, - ) - .map_err(|e| ReceiveError::StorageError(e))?; + CompareAndSwap::new(&new_ratchet_state, None, true, &state1, state2.as_ref(), true, true), + ); + if !result.map_err(ReceiveError::StorageError)? { + return Err(fault!(OutOfSequence, true)); + } let (session, reduced_service_time) = { let mut session_map = ctx.session_map.write().unwrap(); @@ -1004,18 +1014,25 @@ pub(crate) fn received_c1_trans>( if is_other { if let ZetaAutomata::A3 { .. } | ZetaAutomata::R2 { .. } = &state.beta { if state.ratchet_state2.is_some() { - app.save_ratchet_state( + let result = app.save_ratchet_state( &session.s_remote, &session.session_data, - RatchetUpdate { - state1: &state.ratchet_state1, - state2: None, - state1_was_just_added: false, - deleted_state1: state.ratchet_state2.as_ref(), - deleted_state2: None, - }, - ) - .map_err(|e| ReceiveError::StorageError(e))?; + CompareAndSwap::new( + &state.ratchet_state1, + None, + false, + &state.ratchet_state1, + state.ratchet_state2.as_ref(), + false, + true, + ), + ); + if !result.map_err(ReceiveError::StorageError)? { + drop(state); + drop(kex_lock); + session.expire(); + return Err(fault!(OutOfSequence, true, session, true)); + } } drop(state); let timeout_timer = { @@ -1415,18 +1432,22 @@ pub(crate) fn received_k1_trans>( k2.extend(tag); let new_ratchet_state = create_ratchet_state(hmac, &noise, state.ratchet_state1.chain_len); - app.save_ratchet_state( + let result = app.save_ratchet_state( &session.s_remote, &session.session_data, - RatchetUpdate { - state1: &new_ratchet_state, - state2: Some(&state.ratchet_state1), - state1_was_just_added: true, - deleted_state1: state.ratchet_state2.as_ref(), - deleted_state2: None, - }, - ) - .map_err(|e| ReceiveError::StorageError(e))?; + CompareAndSwap::new( + &new_ratchet_state, + Some(&state.ratchet_state1), + true, + &state.ratchet_state1, + state.ratchet_state2.as_ref(), + false, + true, + ), + ); + if !result.map_err(ReceiveError::StorageError)? { + return Err(fault!(OutOfSequence, true, session, true)); + } let mut kek_recv = Zeroizing::new([0u8; HASHLEN]); let mut kek_send = Zeroizing::new([0u8; HASHLEN]); @@ -1534,18 +1555,22 @@ pub(crate) fn received_k2_trans>( .ok_or_else(|| fault!(InvalidPacket, true, session, true))?; let new_ratchet_state = create_ratchet_state(hmac, &noise, state.ratchet_state1.chain_len); - app.save_ratchet_state( + let result = app.save_ratchet_state( &session.s_remote, &session.session_data, - RatchetUpdate { - state1: &new_ratchet_state, - state2: None, - state1_was_just_added: true, - deleted_state1: Some(&state.ratchet_state1), - deleted_state2: state.ratchet_state2.as_ref(), - }, - ) - .map_err(|e| ReceiveError::StorageError(e))?; + CompareAndSwap::new( + &new_ratchet_state, + None, + true, + &state.ratchet_state1, + state.ratchet_state2.as_ref(), + true, + true, + ), + ); + if !result.map_err(ReceiveError::StorageError)? { + return Err(fault!(OutOfSequence, true, session, true)); + } let mut kek_recv = Zeroizing::new([0u8; HASHLEN]); let mut kek_send = Zeroizing::new([0u8; HASHLEN]); diff --git a/performance/src/zssp.rs b/performance/src/zssp.rs index f50ec15..8825834 100644 --- a/performance/src/zssp.rs +++ b/performance/src/zssp.rs @@ -173,16 +173,25 @@ impl Context { .restore_by_identity(&static_remote_key, &session_data, None) .map_err(OpenError::StorageError)? .unwrap_or_default(); - self.open_with_ratchet(app, send, mtu, static_remote_key, session_data, identity, ratchet_states) + self.open_with_ratchet( + app, + send, + mtu, + static_remote_key, + session_data, + identity, + ratchet_states, + ) } /// Create a new session and send initialization packets to Bob, our remote peer. /// This function will use the specified `ratchet_states` to connect to Bob, as opposed to /// calling `app.restore_by_identity`. This can be used to open a session with a specific /// one-time-password using `RatchetStates::new_otp_states()`, or in situations where it is - /// desireable to avoid having Alice call `app.restore_by_identity`. + /// desireable to avoid having Alice call `app.restore_by_identity`. Keep in mind that + /// `save_ratchet_state` likely will eventually be called to delete this ratchet state. /// /// If using a one-time-password, a call to `app.initiator_disallows_downgrade` on the created - /// session **MUST** return `true`. Otherwise the remote peer would be able to avoid having + /// session **must** return `true`. Otherwise the remote peer would be able to avoid having /// to demonstrate knowledge of the otp by requesting a ratchet downgrade. /// Only after the created session is established may a call to /// `app.initiator_disallows_downgrade` return false. @@ -546,7 +555,7 @@ impl Context { return Err(fault!(InvalidPacket, true)); } - let mut buffer = ArrayVec::::new(); + let mut buffer = ArrayVec::::new(); let assembled_packet = if fragment_count > 1 { let mut next_service_time = self.0.unassociated_defrag_cache.lock().unwrap().assemble( &nonce, @@ -578,9 +587,7 @@ impl Context { if packet_type == PACKET_TYPE_HANDSHAKE_HELLO { log!(app, ReceivedRawX1); - if !(HANDSHAKE_HELLO_CHALLENGE_MIN_SIZE..=HANDSHAKE_HELLO_CHALLENGE_MAX_SIZE) - .contains(&assembled_packet.len()) - { + if HANDSHAKE_HELLO_CHALLENGE_SIZE != assembled_packet.len() { return Err(fault!(InvalidPacket, true)); } // Process recv challenge layer. diff --git a/reference/examples/extensive_test.rs b/reference/examples/extensive_test.rs index c5306ab..02496e7 100644 --- a/reference/examples/extensive_test.rs +++ b/reference/examples/extensive_test.rs @@ -6,6 +6,7 @@ * https://www.zerotier.com/ */ +use std::collections::hash_map::Entry; use std::collections::HashMap; use std::iter::ExactSizeIterator; use std::str::FromStr; @@ -18,7 +19,7 @@ use rand_core::OsRng; use rand_core::RngCore; use zssp_proto::application::{ - AcceptAction, ApplicationLayer, CryptoLayer, RatchetState, RatchetStates, RatchetUpdate, Settings, RATCHET_SIZE, + AcceptAction, ApplicationLayer, CompareAndSwap, CryptoLayer, RatchetState, RatchetStates, Settings, RATCHET_SIZE, }; use zssp_proto::crypto::P384KeyPair; use zssp_proto::crypto_impl::{ @@ -105,21 +106,37 @@ impl ApplicationLayer for &mut TestApplication { &mut self, remote_static_key: &P384CratePublicKey, session_data: &u128, - update_data: RatchetUpdate<'_>, - ) -> Result<(), std::io::Error> { - self.ratchets.peer_map.insert(*session_data, update_data.to_states()); + update_data: CompareAndSwap<'_>, + ) -> Result { + let mut ratchets = &mut self.ratchets; + match ratchets.peer_map.entry(*session_data) { + Entry::Occupied(mut entry) => { + if update_data.compare(&entry.get()) { + entry.insert(update_data.to_new_states()); + } else { + return Ok(false); + } + } + Entry::Vacant(entry) => { + if update_data.cur_is_initial_states() { + entry.insert(update_data.to_new_states()); + } else { + return Ok(false); + } + } + } if let Some(rf) = update_data.added_fingerprint() { - self.ratchets.rf_map.insert(*rf, update_data.state1.clone()); - println!("[{}] new ratchet #{}", self.name, update_data.state1.chain_len()); + ratchets.rf_map.insert(*rf, update_data.new_state1.clone()); + println!("[{}] new ratchet #{}", self.name, update_data.new_state1.chain_len()); } if let Some(rf) = update_data.deleted_fingerprint1() { - self.ratchets.rf_map.remove(rf); + ratchets.rf_map.remove(rf); } if let Some(rf) = update_data.deleted_fingerprint2() { - self.ratchets.rf_map.remove(rf); + ratchets.rf_map.remove(rf); } - Ok(()) + Ok(true) } fn time(&mut self) -> i64 { diff --git a/reference/examples/ping_pong.rs b/reference/examples/ping_pong.rs index cb007d7..85b633e 100644 --- a/reference/examples/ping_pong.rs +++ b/reference/examples/ping_pong.rs @@ -12,7 +12,7 @@ use std::sync::Arc; use std::time::Instant; use zssp_proto::application::{ - AcceptAction, ApplicationLayer, CryptoLayer, RatchetState, RatchetStates, RatchetUpdate, RATCHET_SIZE, + AcceptAction, ApplicationLayer, CompareAndSwap, CryptoLayer, RatchetState, RatchetStates, RATCHET_SIZE, }; use zssp_proto::crypto::{rand_core::OsRng, P384KeyPair}; use zssp_proto::crypto_impl::{ @@ -81,9 +81,9 @@ impl ApplicationLayer for &mut MyApp { &mut self, remote_static_key: &P384CratePublicKey, session_data: &(), - update_data: RatchetUpdate<'_>, - ) -> Result<(), std::io::Error> { - Ok(()) + update_data: CompareAndSwap<'_>, + ) -> Result { + Ok(true) } fn time(&mut self) -> i64 { diff --git a/reference/src/application.rs b/reference/src/application.rs index 5758acc..67c0151 100644 --- a/reference/src/application.rs +++ b/reference/src/application.rs @@ -213,11 +213,16 @@ pub trait ApplicationLayer: Sized { remote_static_key: &::PublicKey, session_data: &::SessionData, ) -> Result, std::io::Error>; - /// Atomically commit the update specified by `update_data` to storage, or return an error if - /// the update could not be made. - /// The implementor is free to choose how to apply these updates to storage. + /// Atomically compare-and-swap (a.k.a. compare-exchange) `update` to storage. /// - /// If this returns `Err(IoError)`, the packet which triggered this function to be called will be + /// If `update.cur_state1` and `update.cur_state2` are currently in storage, they must + /// be swapped with `update.new_state1` and `update.new_state2`. + /// Otherwise, storage must remain unchanged. + /// + /// `Ok(true)` must be returned if the compare-and-swap was successful. + /// `Ok(false)` must be returned if comparison failed and the swap was cancelled. + /// + /// If this returns `Err`, the packet which triggered this function to be called will be /// dropped, and no session state will be mutated, preserving synchronization. The remote peer /// will eventually resend that packet and so this function will be called again. /// @@ -229,12 +234,20 @@ pub trait ApplicationLayer: Sized { /// This function may also save state to volatile storage, in which case all peers which connect /// to us will have to allow downgrade across the board. /// Otherwise, when we restart, we will not be allowed to reconnect. + /// + /// # Security + /// Implementations must not perform comparison operations (equals, less than, etc.) directly + /// on two ratchet keys. Instead, comparison operations must be performed indirectly + /// upon their ratchet fingerprints. If two ratchet states have the same ratchet fingerprint, + /// it should be assumed that they also have the same ratchet key. + /// + /// The implementations of `PartialEq` for `RatchetState` and `RatchetStates` do this by default. fn save_ratchet_state( &mut self, remote_static_key: &::PublicKey, session_data: &::SessionData, - update_data: RatchetUpdate<'_>, - ) -> Result<(), std::io::Error>; + update_data: CompareAndSwap<'_>, + ) -> Result; /// Receives a stream of events that occur during an execution of ZSSP. /// These are provided for debugging, logging or metrics purposes, and must be used for diff --git a/reference/src/context.rs b/reference/src/context.rs index e6cbd58..e45b0fe 100644 --- a/reference/src/context.rs +++ b/reference/src/context.rs @@ -11,7 +11,7 @@ use crate::challenge::ChallengeContext; use crate::crypto::{AES_256_KEY_SIZE, AES_GCM_NONCE_SIZE}; use crate::fragmentation::{send_with_fragmentation, DefragBuffer}; use crate::proto::*; -use crate::result::{byzantine_fault, OpenError, ReceiveError, ReceiveOk, SendError, SessionEvent}; +use crate::result::{fault, OpenError, ReceiveError, ReceiveOk, SendError, SessionEvent}; use crate::zeta::*; #[cfg(feature = "logging")] use crate::LogEvent::*; @@ -161,24 +161,24 @@ impl Context { if !matches!(&zeta.beta, ZetaAutomata::A1(_)) { // A resent handshake response from Bob may have arrived out of order, // after we already received one. - return Err(byzantine_fault!(OutOfSequence, false)); + return Err(fault!(OutOfSequence, false)); } if c >= COUNTER_WINDOW_MAX_SKIP_AHEAD { - return Err(byzantine_fault!(ExpiredCounter, true)); + return Err(fault!(ExpiredCounter, true)); } Ok(()) } else if PACKET_TYPE_USES_COUNTER_RANGE.contains(&p) { if !zeta.check_counter_window(c) { // The counter window has finite memory and so will occasionally give // false positives on very out-of-order packets. - return Err(byzantine_fault!(ExpiredCounter, false)); + return Err(fault!(ExpiredCounter, false)); } Ok(()) } else if p == PACKET_TYPE_HANDSHAKE_COMPLETION { // The handshake completion packet could have been resent. - return Err(byzantine_fault!(InvalidPacket, false)); + return Err(fault!(InvalidPacket, false)); } else { - return Err(byzantine_fault!(InvalidPacket, true)); + return Err(fault!(InvalidPacket, true)); } })?; if let Some((pn, mut assembled_packet)) = result { @@ -295,7 +295,7 @@ impl Context { log!(app, DIsAuthClosedSession(&session)); SessionEvent::Rejected } - _ => return Err(byzantine_fault!(InvalidPacket, true)), // This is unreachable. + _ => return Err(fault!(InvalidPacket, true)), // This is unreachable. }; drop(zeta); Ok(ReceiveOk::Session(session, ret)) @@ -315,7 +315,7 @@ impl Context { if p == PACKET_TYPE_HANDSHAKE_COMPLETION && c == 0 { Ok(()) } else { - Err(byzantine_fault!(InvalidPacket, true)) + Err(fault!(InvalidPacket, true)) } })?; if let Some((_, assembled_packet)) = result { @@ -351,7 +351,7 @@ impl Context { } else { // When sessions are added or dropped or packets arrive extremely delayed it is // possible to receive no longer recognized key ids. - Err(byzantine_fault!(UnknownLocalKeyId, false)) + Err(fault!(UnknownLocalKeyId, false)) } } } else { @@ -365,7 +365,7 @@ impl Context { if p == PACKET_TYPE_HANDSHAKE_HELLO || p == PACKET_TYPE_CHALLENGE { Ok(()) } else { - Err(byzantine_fault!(InvalidPacket, true)) + Err(fault!(InvalidPacket, true)) } }, )?; @@ -394,7 +394,7 @@ impl Context { None, ); // If we issue a challenge the first hello packet will always fail. - return Err(byzantine_fault!(FailedAuth, false)); + return Err(fault!(FailedAuth, false)); } else if let Ok(true) = result { log!(app, X1SucceededChallenge); } @@ -423,7 +423,7 @@ impl Context { log!(app, ReceivedRawChallenge); // Process recv challenge layer. if assembled_packet.len() != KID_SIZE + CHALLENGE_SIZE { - return Err(byzantine_fault!(InvalidPacket, true)); + return Err(fault!(InvalidPacket, true)); } if let Some(kid_recv) = NonZeroU32::new(u32::from_be_bytes(assembled_packet[..KID_SIZE].try_into().unwrap())) @@ -439,9 +439,9 @@ impl Context { return Ok(ReceiveOk::Unassociated); } } - Err(byzantine_fault!(UnknownLocalKeyId, true)) + Err(fault!(UnknownLocalKeyId, true)) } else { - Err(byzantine_fault!(InvalidPacket, true)) + Err(fault!(InvalidPacket, true)) } } else { Ok(ReceiveOk::Unassociated) diff --git a/reference/src/fragmentation.rs b/reference/src/fragmentation.rs index 626fc69..34a847b 100644 --- a/reference/src/fragmentation.rs +++ b/reference/src/fragmentation.rs @@ -7,7 +7,7 @@ use zeroize::Zeroizing; use crate::application::CryptoLayer; use crate::crypto::{Aes256Prp, AES_256_KEY_SIZE}; use crate::proto::*; -use crate::result::{byzantine_fault, ReceiveError}; +use crate::result::{fault, ReceiveError}; /// Corresponds to Figure 13 found in Section 6. fn create_fragment_header( @@ -94,7 +94,7 @@ impl DefragBuffer { ) -> Result)>, ReceiveError> { use crate::result::FaultType::*; if raw_fragment.len() < MIN_PACKET_SIZE { - return Err(byzantine_fault!(InvalidPacket, true)); + return Err(fault!(InvalidPacket, true)); } if let Some(hk_recv) = self.hk_recv.as_ref() { @@ -109,7 +109,7 @@ impl DefragBuffer { let fragment_no = raw_fragment[FRAGMENT_NO_IDX] as usize; let fragment_count = raw_fragment[FRAGMENT_COUNT_IDX] as usize; if fragment_no >= fragment_count || fragment_count > MAX_FRAGMENTS { - return Err(byzantine_fault!(InvalidPacket, true)); + return Err(fault!(InvalidPacket, true)); } let n = raw_fragment[PACKET_NONCE_START..HEADER_SIZE].try_into().unwrap(); @@ -125,7 +125,7 @@ impl DefragBuffer { || raw_fragment.len() > buffer.fragment_max_size { // Some parts of the protocol can cause duplicate fragments to be sent. - return Err(byzantine_fault!(InvalidPacket, false)); + return Err(fault!(InvalidPacket, false)); } buffer.fragments[fragment_no] = Some(raw_fragment); buffer.total += 1; diff --git a/reference/src/proto.rs b/reference/src/proto.rs index 65947e9..67d9c13 100644 --- a/reference/src/proto.rs +++ b/reference/src/proto.rs @@ -112,9 +112,8 @@ pub(crate) const PACKET_TYPE_DATA: u8 = 8; pub(crate) const PACKET_TYPE_CHALLENGE: u8 = 9; pub(crate) const PACKET_TYPE_USES_COUNTER_RANGE: std::ops::Range = 3..9; -pub(crate) const HANDSHAKE_HELLO_MIN_SIZE: usize = - KID_SIZE + P384_PUBLIC_KEY_SIZE + KYBER_PUBLIC_KEY_SIZE + AES_GCM_TAG_SIZE + AES_GCM_TAG_SIZE; -pub(crate) const HANDSHAKE_HELLO_MAX_SIZE: usize = HANDSHAKE_HELLO_MIN_SIZE + RATCHET_SIZE + RATCHET_SIZE; +pub(crate) const HANDSHAKE_HELLO_SIZE: usize = + KID_SIZE + P384_PUBLIC_KEY_SIZE + KYBER_PUBLIC_KEY_SIZE + AES_GCM_TAG_SIZE + 2 * RATCHET_SIZE + AES_GCM_TAG_SIZE; pub(crate) const HANDSHAKE_RESPONSE_SIZE: usize = P384_PUBLIC_KEY_SIZE + KYBER_CIPHERTEXT_SIZE + AES_GCM_TAG_SIZE + KID_SIZE + AES_GCM_TAG_SIZE; diff --git a/reference/src/ratchet_state.rs b/reference/src/ratchet_state.rs index fc2c85d..9c37054 100644 --- a/reference/src/ratchet_state.rs +++ b/reference/src/ratchet_state.rs @@ -12,31 +12,24 @@ use crate::proto::*; /// Corresponds to the Ratchet Key and Ratchet Fingerprint described in Section 3. #[derive(Clone, Eq)] pub struct RatchetState { - key: Zeroizing<[u8; RATCHET_SIZE]>, - fingerprint: Option>, - chain_len: u64, + pub(crate) key: Zeroizing<[u8; RATCHET_SIZE]>, + pub(crate) fingerprint: Zeroizing<[u8; RATCHET_SIZE]>, + pub(crate) chain_len: u64, } impl PartialEq for RatchetState { fn eq(&self, other: &Self) -> bool { - let ret = match (self.fingerprint.as_ref(), other.fingerprint.as_ref()) { - (Some(rf1), Some(rf2)) => secure_eq(rf1, rf2), - (None, None) => true, - _ => false, - }; - ret & secure_eq(&self.key, &other.key) & (self.chain_len == other.chain_len) + self.fingerprint.eq(&other.fingerprint) & (self.chain_len == other.chain_len) } } impl std::hash::Hash for RatchetState { fn hash(&self, state: &mut H) { - if let Some(rf) = &self.fingerprint { - state.write_u64(u64::from_ne_bytes(rf[..8].try_into().unwrap())) - } + state.write(self.fingerprint.as_ref()) } } impl RatchetState { /// Creates a new ratchet state from the given ratchet key, ratchet fingerprint and chain length. pub fn new(key: Zeroizing<[u8; RATCHET_SIZE]>, fingerprint: Zeroizing<[u8; RATCHET_SIZE]>, chain_len: u64) -> Self { - RatchetState { key, fingerprint: Some(fingerprint), chain_len } + RatchetState { key, fingerprint, chain_len } } /// Creates a new ratchet state from the given ratchet key, ratchet fingerprint and chain length. /// @@ -45,28 +38,10 @@ impl RatchetState { pub fn new_raw(key: [u8; RATCHET_SIZE], fingerprint: [u8; RATCHET_SIZE], chain_len: u64) -> Self { RatchetState { key: Zeroizing::new(key), - fingerprint: Some(Zeroizing::new(fingerprint)), + fingerprint: Zeroizing::new(fingerprint), chain_len, } } - /// The ratchet key for this ratchet state. This is directly mixed into the master secret of a - /// session and so is very sensitive. All operations upon a ratchet key must be implemented - /// in constant time. The user should prefer to do nothing with the ratchet key besides copying - /// it to or from a storage device. - /// - /// If `fingerprint` returns `None` then this is the "empty" ratchet state and the key will be - /// all zeros. - pub fn key(&self) -> &[u8; RATCHET_SIZE] { - &self.key - } - /// Ratchet keys and fingerprints are "chained together", where each set is derived from the - /// previous set. - /// - /// This function outputs the total length of that chain, as in the total number of previous - /// ratchet states that this ratchet state was derived from. - pub fn chain_len(&self) -> u64 { - self.chain_len - } /// Creates a new "empty" ratchet state, where the ratchet fingerprint is the /// empty string, the ratchet key is all zeros, and the chain length is 0. /// @@ -74,7 +49,7 @@ impl RatchetState { pub fn empty() -> Self { RatchetState { key: Zeroizing::new([0u8; RATCHET_SIZE]), - fingerprint: None, + fingerprint: Zeroizing::new([0u8; RATCHET_SIZE]), chain_len: 0, } } @@ -84,7 +59,7 @@ impl RatchetState { pub fn new_from_otp(otp: &[u8]) -> RatchetState { let mut buffer = Vec::new(); buffer.push(1); - buffer.extend(LABEL_OTP_TO_RATCHET); + buffer.extend(*LABEL_OTP_TO_RATCHET); buffer.push(0x00); buffer.extend((1024u16).to_be_bytes()); let r1 = Hmac::hmac(otp, &buffer); @@ -96,15 +71,15 @@ impl RatchetState { 1, ) } - /// Returns true if this is the "empty" ratchet state, where the ratchet fingerprint is the - /// empty string, the ratchet key is all zeros, and the chain length is 0. - pub fn is_empty(&self) -> bool { - self.fingerprint.is_none() - } - /// Checks if the fingerprint of this ratchet state equals the fingerprint contained in argument - /// `rf`. Uses constant time equality. - pub fn fingerprint_eq(&self, rf: &[u8; RATCHET_SIZE]) -> bool { - self.fingerprint.as_ref().map_or(false, |rf0| secure_eq(rf0, rf)) + /// The ratchet key for this ratchet state. This is directly mixed into the master secret of a + /// session and so is very sensitive. All operations upon a ratchet key must be implemented + /// in constant time. The user should prefer to do nothing with the ratchet key besides copying + /// it to or from a storage device. + /// + /// If `fingerprint` returns `None` then this is the "empty" ratchet state and the key will be + /// all zeros. + pub fn key(&self) -> &[u8; RATCHET_SIZE] { + &self.key } /// The ratchet fingerprint for this ratchet state. /// @@ -115,8 +90,43 @@ impl RatchetState { /// but the security of ZSSP can survive having this value leaked. /// Operations on a ratchet fingerprint should be implemented in constant time, /// but it is ok if they are not. - pub fn fingerprint(&self) -> Option<&[u8; RATCHET_SIZE]> { - self.fingerprint.as_deref() + pub fn fingerprint(&self) -> &[u8; RATCHET_SIZE] { + &self.fingerprint + } + /// Ratchet keys and fingerprints are "chained together", where each set is derived from the + /// previous set. + /// + /// This function outputs the total length of that chain, as in the total number of previous + /// ratchet states that this ratchet state was derived from. + pub fn chain_len(&self) -> u64 { + self.chain_len + } + /// Returns true if this is the "empty" ratchet state, where the ratchet fingerprint is the + /// empty string, the ratchet key is all zeros, and the chain length is 0. + pub fn is_empty(&self) -> bool { + secure_eq(self.fingerprint(), &[0u8; RATCHET_SIZE]) + } + /// Checks if the fingerprint of this ratchet state equals the + /// fingerprint contained in argument `rf`. + /// + /// Uses constant time equality. + pub fn fingerprint_eq(&self, rf: &[u8; RATCHET_SIZE]) -> bool { + secure_eq(self.fingerprint(), rf) + } + /// Returns true if the fingerprint of argument `this` equals the fingerprint represented by + /// argument `rf`. + /// If `this` is `None`, `this` is considered to be "null", as in a ratchet state that does not + /// exist or has been deleted. + /// If `rf` is `None`, `rf` is considered to be "null" as well. + /// If `rf` is `Some(None)`, `rf` is considered to be the empty ratchet fingerprint. + /// + /// Uses constant time equality. + pub fn fingerprint_eq_nullable(this: Option<&Self>, rf: Option<&[u8; RATCHET_SIZE]>) -> bool { + match (this, rf) { + (Some(this), Some(rf)) => this.fingerprint_eq(rf), + (None, None) => true, + _ => false, + } } } impl Default for RatchetState { @@ -134,7 +144,8 @@ impl Default for RatchetState { pub struct RatchetStates { /// The first ratchet state from the pair. pub state1: RatchetState, - /// The second ratchet state from the pair. It can, and usually will be `None`. + /// The second ratchet state from the pair. + /// It can, and usually will be `None`, which means that this ratchet state is "null". pub state2: Option, } impl RatchetStates { @@ -169,7 +180,6 @@ impl Default for RatchetStates { Self::new_initial_states() } } - /// A set of references to ratchet states specifying how a remote peer's persistent storage should /// be updated. This struct is designed to provide any and all potentially needed data for /// maintaining a store of these ratchet states. It should be straightforward to commit these updates @@ -178,69 +188,138 @@ impl Default for RatchetStates { /// There will only be up to two ratchet states saved to storage at a time per peer. /// Every time a third ratchet state is generated, a previous ratchet state will be deleted. /// -/// These are sensitive values should they ought to be securely stored, with restricted read-write -/// permissions if stored on disk. +/// As the name implies, these updates should be applied as one atomic compare-and-swap operation. +/// If the two ratchet states currently in storage equal `cur_state1` and `cur_state2`, +/// then `new_state1` and `new_state2` should overwrite them. +/// If not, then storage must not be modified. /// -/// To prevent desync or resource leakage, these updates should be committed atomically. If that is -/// not possible, then new ratchet states should be written before old ratchet states are deleted -#[derive(Clone, Copy)] -pub struct RatchetUpdate<'a> { - /// The ratchet key and fingerprint to store in the first slot. - pub state1: &'a RatchetState, - /// The ratchet key and fingerprint to store in the second slot. - pub state2: Option<&'a RatchetState>, - /// Whether `state1` is a brand new ratchet state, or if it was previously saved. - pub state1_was_just_added: bool, - /// A previous ratchet key and fingerprint that now must be deleted from storage. - /// This will have been a previously given value of `state1` or `state2`. - pub deleted_state1: Option<&'a RatchetState>, - /// A previous ratchet key and fingerprint that now must be deleted from storage. - /// It is extremely rare that this field is occupied. - pub deleted_state2: Option<&'a RatchetState>, +/// Ratchet keys must never be checked for equality. Instead, if two ratchet states have equal +/// ratchet fingerprints, it should be assumed that they also have equal ratchet keys. +/// This policy exists to reduce the security impact of timing and other side-channel attacks. +/// +/// These are sensitive values and ought to be securely stored, with restricted read-write +/// permissions if stored on disk. +#[derive(Clone)] +pub struct CompareAndSwap<'a> { + /// The ratchet state to store in the first slot. + pub new_state1: &'a RatchetState, + /// The ratchet state to store in the second slot. + /// A value of `None` implies that the second slot should be set to null. + pub new_state2: Option<&'a RatchetState>, + /// This field is `true` if and only if `new_state1 != cur_state1` and `new_state1 != cur_state2`. + /// + /// This implies whether `state1` is a brand new ratchet state, or if it was previously saved. + pub new_state1_was_just_added: bool, + /// The ratchet state that we expect to see in the first slot. + /// If this value is not currently stored in the first slot, the entire update must be aborted. + pub cur_state1: &'a RatchetState, + /// The ratchet state that we expect to see in the second slot. + /// A value of `None` implies we expect the second slot to be set to "null". + /// + /// If this value is not currently stored in the second slot, the entire update must be aborted. + pub cur_state2: Option<&'a RatchetState>, + /// This field is `true` if and only if `cur_state1 != new_state1` and `cur_state1 != new_state2`. + pub cur_state1_was_just_deleted: bool, + /// This field is `true` if and only if `cur_state2 != new_state1` and `cur_state2 != new_state2`. + pub cur_state2_was_just_deleted: bool, } -impl<'a> RatchetUpdate<'a> { - /// Returns the final `RatchetStates` that should be the only thing saved after this update is - /// fully committed. Future calls to `ApplicationLayer::restore_by_identity` should return this struct. - pub fn to_states(&self) -> RatchetStates { - RatchetStates::new(self.state1.clone(), self.state2.cloned()) +impl<'a> CompareAndSwap<'a> { + pub(crate) fn new( + new_state1: &'a RatchetState, + new_state2: Option<&'a RatchetState>, + new_state1_was_just_added: bool, + cur_state1: &'a RatchetState, + cur_state2: Option<&'a RatchetState>, + cur_state1_was_just_deleted: bool, + cur_state2_was_just_deleted: bool, + ) -> Self { + Self { + new_state1, + new_state2, + new_state1_was_just_added, + cur_state1, + cur_state2, + cur_state1_was_just_deleted, + cur_state2_was_just_deleted, + } + } + /// Returns the final `RatchetStates` that must be swapped into storage if this update is + /// fully committed. + /// Future calls to `ApplicationLayer::restore_by_identity` should return this struct. + pub fn to_new_states(&self) -> RatchetStates { + RatchetStates::new(self.new_state1.clone(), self.new_state2.cloned()) + } + /// Returns the `RatchetStates` that is expected to currently be in storage for this peer. + /// This value must be compared with the current value in storage, and if they are equal, + /// the return value of `CompareAndSwap::to_new_states` must overwrite it. + pub fn to_cur_states(&self) -> RatchetStates { + RatchetStates::new(self.cur_state1.clone(), self.cur_state2.cloned()) } /// If this update specifies adding a brand new ratchet fingerprint, this function will return it. /// The returned ratchet fingerprint will always be the ratchet fingerprint of field `state1`. /// - /// If a fingerprint is returned then it is guaranteed that `state1_was_just_added` will be true. + /// If a fingerprint is returned then it is guaranteed that `state1_was_just_added` will be `true`. pub fn added_fingerprint(&self) -> Option<&[u8; RATCHET_SIZE]> { - if self.state1_was_just_added { - self.state1.fingerprint() - } else { - None - } + self.new_state1_was_just_added.then_some(self.new_state1.fingerprint()) } - /// If this updated specifies to delete an old ratchet fingerprint, this function will return it. - /// The returned ratchet fingerprint will always be the ratchet fingerprint of field - /// `deleted_state1`. + /// If this updated specifies to delete `cur_state1`, and `cur_state1` has a non-zero ratchet + /// fingerprint, this function will return the ratchet fingerprint. + /// + /// If `cur_state1` is not being deleted (i.e. `cur_state1 == new_state1` or + /// `cur_state1 == new_state2`), then this will return `None`. /// /// There may be a second ratchet fingerprint to be deleted, which function /// `deleted_fingerprint2` will return. pub fn deleted_fingerprint1(&self) -> Option<&[u8; RATCHET_SIZE]> { - if let Some(rs) = &self.deleted_state1 { - rs.fingerprint() - } else { - None + if self.cur_state1_was_just_deleted { + if !self.cur_state1.is_empty() { + return Some(self.cur_state1.fingerprint()); + } } + None } - /// If this updated specifies to delete two ratchet fingerprints, this function will return the - /// second one. `deleted_fingerprint1` will return the first one. - /// The returned ratchet fingerprint will always be the ratchet fingerprint of field - /// `deleted_state2`. + /// If this updated specifies to delete `cur_state2`, and `cur_state2` has a non-zero ratchet + /// fingerprint, this function will return the ratchet fingerprint. /// - /// It is exceptionally rare that there will be more than one ratchet fingerprint to be deleted. - /// Care should be taken to make sure that this update will still be correctly committed in the - /// rare event that this returns `Some`. + /// If `cur_state2` is not being deleted (i.e. `cur_state2 == new_state1` or + /// `cur_state2 == new_state2`), then this will return `None`. + /// + /// There may be a second ratchet fingerprint to be deleted, which function + /// `deleted_fingerprint1` will return. pub fn deleted_fingerprint2(&self) -> Option<&[u8; RATCHET_SIZE]> { - if let Some(rs) = &self.deleted_state2 { - rs.fingerprint() - } else { - None + if self.cur_state2_was_just_deleted { + if let Some(rf) = self.cur_state2 { + if !rf.is_empty() { + return Some(rf.fingerprint()); + } + } } + None + } + /// Returns true if the currently stored ratchet states are expected to be the initial ratchet + /// states. This is the default value for a peer's ratchet states in the event they could not + /// be found in storage. + /// + /// If a peer could not be found in storage, and this function returns true, + /// then the peer should be added to storage with the new ratchet states specified by this + /// `CompareAndSwap` struct. + pub fn cur_is_initial_states(&self) -> bool { + self.cur_state1.is_empty() && self.cur_state2.is_none() + } + /// Compares the ratchet fingerprints of `cur_state1` and `cur_state2` with `rf1` and `rf2`. + /// If they are equal this function will return `true`. + /// + /// If this function returns `true`, then the implementation may proceed to swap out the ratchet + /// states these fingerprints come from with `new_state1` and `new_state2`. + pub fn compare_fingerprints(&self, rf1: &[u8; RATCHET_SIZE], rf2: Option<&[u8; RATCHET_SIZE]>) -> bool { + self.cur_state1.fingerprint_eq(rf1) & RatchetState::fingerprint_eq_nullable(self.cur_state2, rf2) + } + /// Compares `cur_state1` and `cur_state2` with `other.state1` and `other.state2`. + /// If they are equal this function will return `true`. + /// + /// If this function returns `true`, then the implementation may proceed to swap `other.state1` + /// and `other.state2` with `new_state1` and `new_state2`. + pub fn compare(&self, other: &RatchetStates) -> bool { + self.cur_state1.eq(&other.state1) & self.cur_state2.eq(&other.state2.as_ref()) } } diff --git a/reference/src/result.rs b/reference/src/result.rs index 4fa54f3..380f15d 100644 --- a/reference/src/result.rs +++ b/reference/src/result.rs @@ -166,7 +166,7 @@ pub enum ReceiveError { WriteError(std::io::Error), } -macro_rules! byzantine_fault { +macro_rules! fault { ($name:expr, $unnatural:ident) => { ReceiveError::ByzantineFault(crate::result::ByzantineFault { #[cfg(feature = "debug")] @@ -178,7 +178,7 @@ macro_rules! byzantine_fault { }) }; } -pub(crate) use byzantine_fault; +pub(crate) use fault; /// Result generated by the context packet receive function, with possible payloads. #[derive(Clone)] diff --git a/reference/src/zeta.rs b/reference/src/zeta.rs index 3c333db..b5ba5ac 100644 --- a/reference/src/zeta.rs +++ b/reference/src/zeta.rs @@ -7,13 +7,13 @@ use std::sync::{Arc, Weak}; use rand_core::RngCore; use zeroize::Zeroizing; -use crate::application::{ApplicationLayer, CryptoLayer, RatchetState, RatchetStates, RatchetUpdate}; +use crate::application::{ApplicationLayer, CompareAndSwap, CryptoLayer, RatchetState, RatchetStates}; use crate::challenge::{gen_null_response, respond_to_challenge_in_place}; use crate::context::{log, ContextInner, SessionMap}; use crate::crypto::*; use crate::fragmentation::DefragBuffer; use crate::proto::*; -use crate::result::{byzantine_fault, FaultType, OpenError, ReceiveError, SendError}; +use crate::result::{fault, FaultType, OpenError, ReceiveError, SendError}; use crate::symmetric_state::SymmetricState; #[cfg(feature = "logging")] use crate::LogEvent::*; @@ -265,12 +265,8 @@ fn create_a1_state( noise.encrypt_and_hash_in_place(to_nonce(PACKET_TYPE_HANDSHAKE_HELLO, 0), i, &mut x1); // Process message pattern 1 payload. let i = x1.len(); - if let Some(rf) = ratchet_state1.fingerprint() { - x1.extend(rf.as_ref()); - } - if let Some(Some(rf)) = ratchet_state2.map(|rs| rs.fingerprint()) { - x1.extend(rf.as_ref()); - } + x1.extend(ratchet_state1.fingerprint()); + x1.extend(ratchet_state2.map_or(&[0u8; RATCHET_SIZE], |rs| rs.fingerprint())); noise.encrypt_and_hash_in_place(to_nonce(PACKET_TYPE_HANDSHAKE_HELLO, 1), i, &mut x1); let c = u64::from_be_bytes(x1[x1.len() - 8..].try_into().unwrap()); @@ -365,23 +361,23 @@ pub(crate) fn received_x1_trans>( // ... // -> e, es, e1 // <- e, ee, ekem1, psk - if !(HANDSHAKE_HELLO_MIN_SIZE..=HANDSHAKE_HELLO_MAX_SIZE).contains(&x1.len()) { - return Err(byzantine_fault!(InvalidPacket, true)); + if HANDSHAKE_HELLO_SIZE != x1.len() { + return Err(fault!(InvalidPacket, true)); } if n[AES_GCM_NONCE_SIZE - 8..] != x1[x1.len() - 8..] { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } let mut noise = SymmetricState::::initialize(PROTOCOL_NAME_NOISE_XK); let mut i = 0; // Noise process prologue. let j = i + KID_SIZE; noise.mix_hash(&x1[i..j]); - let kid_send = NonZeroU32::new(u32::from_be_bytes(x1[i..j].try_into().unwrap())) - .ok_or(byzantine_fault!(InvalidPacket, true))?; + let kid_send = + NonZeroU32::new(u32::from_be_bytes(x1[i..j].try_into().unwrap())).ok_or(fault!(InvalidPacket, true))?; noise.mix_hash(&ctx.s_secret.public_key_bytes()); i = j; // Process message pattern 1 e token. - let e_remote = noise.read_e(&mut i, &x1).ok_or(byzantine_fault!(FailedAuth, true))?; + let e_remote = noise.read_e(&mut i, &x1).ok_or(fault!(FailedAuth, true))?; // Process message pattern 1 es token. noise.mix_dh(&ctx.s_secret, &e_remote); // Process message pattern 1 e1 token. @@ -389,40 +385,52 @@ pub(crate) fn received_x1_trans>( let k = j + AES_GCM_TAG_SIZE; let tag = x1[j..k].try_into().unwrap(); if !noise.decrypt_and_hash_in_place(to_nonce(PACKET_TYPE_HANDSHAKE_HELLO, 0), &mut x1[i..j], tag) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } let e1_start = i; let e1_end = j; i = k; // Process message pattern 1 payload. - let k = x1.len(); - let j = k - AES_GCM_TAG_SIZE; + let j = i + RATCHET_SIZE + RATCHET_SIZE; + let k = j + AES_GCM_TAG_SIZE; let tag = x1[j..k].try_into().unwrap(); if !noise.decrypt_and_hash_in_place(to_nonce(PACKET_TYPE_HANDSHAKE_HELLO, 1), &mut x1[i..j], tag) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } + debug_assert_eq!(k, x1.len()); + let rf1 = &x1[i..i + RATCHET_SIZE]; + let rf2 = &x1[i + RATCHET_SIZE..j]; let mut ratchet_state = None; - while i + RATCHET_SIZE <= j { - let rf = (&x1[i..i + RATCHET_SIZE]).try_into().unwrap(); - match app.restore_by_fingerprint(rf) { + if !secure_eq(rf1, &[0u8; RATCHET_SIZE]) { + match app.restore_by_fingerprint(rf1.try_into().unwrap()) { Ok(None) => {} Ok(Some(rs)) => { ratchet_state = Some(rs); - break; } Err(e) => return Err(ReceiveError::StorageError(e)), } - i += RATCHET_SIZE; } - let ratchet_state = if let Some(rs) = ratchet_state { - rs - } else { - if app.hello_requires_recognized_ratchet() { - return Err(byzantine_fault!(FailedAuth, true)); + if ratchet_state.is_none() { + if !secure_eq(rf2, &[0u8; RATCHET_SIZE]) { + match app.restore_by_fingerprint(rf1.try_into().unwrap()) { + Ok(None) => {} + Ok(Some(rs)) => { + ratchet_state = Some(rs); + } + Err(e) => return Err(ReceiveError::StorageError(e)), + } } - RatchetState::empty() - }; + if ratchet_state.is_none() { + if app.hello_requires_recognized_ratchet() { + return Err(fault!(FailedAuth, true)); + } + } + } + // If we get to this point and haven't found a full ratchet state, + // set it to the empty ratchet state. + let ratchet_state = ratchet_state.unwrap_or_default(); + let (hk_send, hk_recv) = noise.get_ask(LABEL_HEADER_KEY); let mut x2 = Vec::new(); @@ -437,7 +445,7 @@ pub(crate) fn received_x1_trans>( (&x1[e1_start..e1_end]).try_into().unwrap(), ) .map(|(ct, secret)| (ct, Zeroizing::new(secret))) - .ok_or(byzantine_fault!(FailedAuth, true))?; + .ok_or(fault!(FailedAuth, true))?; x2.extend(ekem1); noise.encrypt_and_hash_in_place(to_nonce(PACKET_TYPE_HANDSHAKE_RESPONSE, 0), i, &mut x2); noise.mix_key(ekem1_secret.as_ref()); @@ -493,14 +501,14 @@ pub(crate) fn received_x2_trans>( // <- e, ee, ekem1, psk // -> s, se if HANDSHAKE_RESPONSE_SIZE != x2.len() { - return Err(byzantine_fault!(InvalidPacket, true)); + return Err(fault!(InvalidPacket, true)); } if Some(kid) != zeta.key_ref(true).recv.kid { - return Err(byzantine_fault!(UnknownLocalKeyId, true)); + return Err(fault!(UnknownLocalKeyId, true)); } let (_, c) = from_nonce(&n); if c >= COUNTER_WINDOW_MAX_SKIP_AHEAD || n[AES_GCM_NONCE_SIZE - 3..] != x2[x2.len() - 3..] { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } let mut should_warn_missing_ratchet = false; let result = (|| { @@ -508,7 +516,7 @@ pub(crate) fn received_x2_trans>( let mut noise = noise.clone(); let mut i = 0; // Process message pattern 2 e token. - let e_remote = noise.read_e(&mut i, &x2).ok_or(byzantine_fault!(FailedAuth, true))?; + let e_remote = noise.read_e(&mut i, &x2).ok_or(fault!(FailedAuth, true))?; // Process message pattern 2 ee token. noise.mix_dh(e_secret, &e_remote); // Process message pattern 2 ekem1 token. @@ -516,12 +524,12 @@ pub(crate) fn received_x2_trans>( let k = j + AES_GCM_TAG_SIZE; let tag = x2[j..k].try_into().unwrap(); if !noise.decrypt_and_hash_in_place(to_nonce(PACKET_TYPE_HANDSHAKE_RESPONSE, 0), &mut x2[i..j], tag) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } let ekem1_secret = e1_secret .decapsulate((&x2[i..j]).try_into().unwrap()) .map(Zeroizing::new) - .ok_or(byzantine_fault!(FailedAuth, true))?; + .ok_or(fault!(FailedAuth, true))?; noise.mix_key(ekem1_secret.as_ref()); drop(ekem1_secret); i = k; @@ -568,7 +576,7 @@ pub(crate) fn received_x2_trans>( } } - let (kid_send, mut noise) = result.ok_or(byzantine_fault!(FailedAuth, true))?; + let (kid_send, mut noise) = result.ok_or(fault!(FailedAuth, true))?; let mut x3 = Vec::new(); // Process message pattern 3 s token. @@ -585,24 +593,26 @@ pub(crate) fn received_x2_trans>( let (rk, rf) = noise.get_ask(LABEL_RATCHET_STATE); let new_ratchet_state = RatchetState::new(rk, rf, chain_len + 1); - let (ratchet_to_preserve, ratchet_to_delete) = if ratchet_i == 1 { - (Some(&zeta.ratchet_state1), zeta.ratchet_state2.as_ref()) + let ratchet_to_preserve = if ratchet_i == 1 { + Some(&zeta.ratchet_state1) } else { - (zeta.ratchet_state2.as_ref(), Some(&zeta.ratchet_state1)) + zeta.ratchet_state2.as_ref() }; let result = app.save_ratchet_state( &zeta.s_remote, &zeta.session_data, - RatchetUpdate { - state1: &new_ratchet_state, - state2: ratchet_to_preserve, - state1_was_just_added: true, - deleted_state1: ratchet_to_delete, - deleted_state2: None, - }, + CompareAndSwap::new( + &new_ratchet_state, + ratchet_to_preserve, + true, + &zeta.ratchet_state1, + zeta.ratchet_state2.as_ref(), + ratchet_i == 2, + ratchet_i == 1, + ), ); - if let Err(e) = result { - return Err(ReceiveError::StorageError(e)); + if !result.map_err(ReceiveError::StorageError)? { + return Err(fault!(OutOfSequence, true)); } let (kek_recv, kek_send) = noise.get_ask(LABEL_KEX_KEY); @@ -626,7 +636,7 @@ pub(crate) fn received_x2_trans>( Ok(packet) } else { - Err(byzantine_fault!(FailedAuth, true)) + Err(fault!(FailedAuth, true)) } })(); match &result { @@ -672,10 +682,10 @@ pub(crate) fn received_x3_trans>( use FaultType::*; // -> s, se if !(HANDSHAKE_COMPLETION_MIN_SIZE..=HANDSHAKE_COMPLETION_MAX_SIZE).contains(&x3.len()) { - return Err(byzantine_fault!(InvalidPacket, true)); + return Err(fault!(InvalidPacket, true)); } if kid != zeta.kid_recv { - return Err(byzantine_fault!(UnknownLocalKeyId, true)); + return Err(fault!(UnknownLocalKeyId, true)); } let mut noise = zeta.noise.clone(); @@ -685,10 +695,9 @@ pub(crate) fn received_x3_trans>( let k = j + AES_GCM_TAG_SIZE; let tag = x3[j..k].try_into().unwrap(); if !noise.decrypt_and_hash_in_place(to_nonce(PACKET_TYPE_HANDSHAKE_COMPLETION, 1), &mut x3[i..j], tag) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } - let s_remote = - C::PublicKey::from_bytes((&x3[i..j]).try_into().unwrap()).ok_or(byzantine_fault!(FailedAuth, true))?; + let s_remote = C::PublicKey::from_bytes((&x3[i..j]).try_into().unwrap()).ok_or(fault!(FailedAuth, true))?; i = k; // Process message pattern 3 se token. noise.mix_dh(&zeta.e_secret, &s_remote); @@ -697,7 +706,7 @@ pub(crate) fn received_x3_trans>( let j = k - AES_GCM_TAG_SIZE; let tag = x3[j..k].try_into().unwrap(); if !noise.decrypt_and_hash_in_place(to_nonce(PACKET_TYPE_HANDSHAKE_COMPLETION, 0), &mut x3[i..j], tag) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } let identity_start = i; let identity_end = j; @@ -727,13 +736,13 @@ pub(crate) fn received_x3_trans>( let mut should_warn_missing_ratchet = false; if (zeta.ratchet_state != state1) & (Some(&zeta.ratchet_state) != state2.as_ref()) { - if !responder_disallows_downgrade && zeta.ratchet_state.fingerprint().is_none() { + if !responder_disallows_downgrade && zeta.ratchet_state.is_empty() { should_warn_missing_ratchet = true; } else { if !responder_silently_rejects { send(&create_reject(), Some(&zeta.hk_send)) } - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } } @@ -743,16 +752,10 @@ pub(crate) fn received_x3_trans>( let result = app.save_ratchet_state( &s_remote, &session_data, - RatchetUpdate { - state1: &new_ratchet_state, - state2: None, - state1_was_just_added: true, - deleted_state1: Some(&state1), - deleted_state2: state2.as_ref(), - }, + CompareAndSwap::new(&new_ratchet_state, None, true, &state1, state2.as_ref(), true, true), ); - if let Err(e) = result { - return Err(ReceiveError::StorageError(e)); + if !result.map_err(ReceiveError::StorageError)? { + return Err(fault!(OutOfSequence, true)); } let mut c1 = Vec::new(); @@ -772,7 +775,7 @@ pub(crate) fn received_x3_trans>( let entry = match session_map.entry(zeta.kid_recv) { // We could have issued the kid that we initially offered Alice to someone else // before Alice was able to respond. It is unlikely but possible. - Occupied(_) => return Err(byzantine_fault!(OutOfSequence, false)), + Occupied(_) => return Err(fault!(OutOfSequence, false)), Vacant(entry) => entry, }; let session = Arc::new(Session(RefCell::new(Zeta { @@ -807,7 +810,7 @@ pub(crate) fn received_x3_trans>( if !responder_silently_rejects { send(&create_reject(), Some(&zeta.hk_send)) } - Err(byzantine_fault!(FailedAuth, true)) + Err(fault!(FailedAuth, true)) } } /// Corresponds to Transition Algorithm 5 found in Section 4.3. @@ -823,7 +826,7 @@ pub(crate) fn received_c1_trans>( use FaultType::*; if c1.len() != KEY_CONFIRMATION_SIZE { - return Err(byzantine_fault!(InvalidPacket, true)); + return Err(fault!(InvalidPacket, true)); } let is_other = if Some(kid) == zeta.key_ref(true).recv.kid { true @@ -832,7 +835,7 @@ pub(crate) fn received_c1_trans>( } else { // Some key confirmation may have arrived extremely delayed. // It is unlikely but possible. - return Err(byzantine_fault!(OutOfSequence, false)); + return Err(fault!(OutOfSequence, false)); }; let specified_key = zeta @@ -840,14 +843,14 @@ pub(crate) fn received_c1_trans>( .recv .kek .as_ref() - .ok_or(byzantine_fault!(OutOfSequence, true))?; + .ok_or(fault!(OutOfSequence, true))?; let tag = c1[..].try_into().unwrap(); if !C::Aead::decrypt_in_place(specified_key, &n, None, &mut [], tag) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } let (_, c) = from_nonce(&n); if !zeta.update_counter_window(c) { - return Err(byzantine_fault!(ExpiredCounter, true)); + return Err(fault!(ExpiredCounter, true)); } let just_establised = is_other && matches!(&zeta.beta, ZetaAutomata::A3 { .. }); @@ -857,16 +860,19 @@ pub(crate) fn received_c1_trans>( let result = app.save_ratchet_state( &zeta.s_remote, &zeta.session_data, - RatchetUpdate { - state1: &zeta.ratchet_state1, - state2: None, - state1_was_just_added: false, - deleted_state1: zeta.ratchet_state2.as_ref(), - deleted_state2: None, - }, + CompareAndSwap::new( + &zeta.ratchet_state1, + None, + false, + &zeta.ratchet_state1, + zeta.ratchet_state2.as_ref(), + false, + true, + ), ); - if let Err(e) = result { - return Err(ReceiveError::StorageError(e)); + if !result.map_err(ReceiveError::StorageError)? { + zeta.expire(); + return Err(fault!(OutOfSequence, true)); } } @@ -880,7 +886,7 @@ pub(crate) fn received_c1_trans>( } let c2 = Vec::new(); if !send_control::(zeta, PACKET_TYPE_ACK, c2, send) { - return Err(byzantine_fault!(OutOfSequence, true)); + return Err(fault!(OutOfSequence, true)); } Ok(just_establised) @@ -898,24 +904,24 @@ pub(crate) fn received_c2_trans>( use FaultType::*; if c2.len() != ACKNOWLEDGEMENT_SIZE { - return Err(byzantine_fault!(InvalidPacket, true)); + return Err(fault!(InvalidPacket, true)); } if Some(kid) != zeta.key_ref(false).recv.kid { // Some acknowledgement may have arrived extremely delayed. - return Err(byzantine_fault!(UnknownLocalKeyId, false)); + return Err(fault!(UnknownLocalKeyId, false)); } if !matches!(&zeta.beta, ZetaAutomata::S1) { // Some acknowledgement may have arrived extremely delayed. - return Err(byzantine_fault!(OutOfSequence, false)); + return Err(fault!(OutOfSequence, false)); } let tag = c2[..].try_into().unwrap(); if !C::Aead::decrypt_in_place(zeta.key_ref(false).recv.kek.as_ref().unwrap(), &n, None, &mut [], tag) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } let (_, c) = from_nonce(&n); if !zeta.update_counter_window(c) { - return Err(byzantine_fault!(ExpiredCounter, true)); + return Err(fault!(ExpiredCounter, true)); } let r = rng.borrow_mut().next_u64() % C::SETTINGS.rekey_time_max_jitter; @@ -935,19 +941,19 @@ pub(crate) fn received_d_trans( use FaultType::*; if d.len() != SESSION_REJECTED_SIZE { - return Err(byzantine_fault!(InvalidPacket, true)); + return Err(fault!(InvalidPacket, true)); } if Some(kid) != zeta.key_ref(true).recv.kid || !matches!(&zeta.beta, ZetaAutomata::A3 { .. }) { - return Err(byzantine_fault!(OutOfSequence, true)); + return Err(fault!(OutOfSequence, true)); } let tag = d[..].try_into().unwrap(); if !C::Aead::decrypt_in_place(zeta.key_ref(true).recv.kek.as_ref().unwrap(), &n, None, &mut [], tag) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } let (_, c) = from_nonce(&n); if !zeta.update_counter_window(c) { - return Err(byzantine_fault!(ExpiredCounter, true)); + return Err(fault!(ExpiredCounter, true)); } zeta.expire(); @@ -1103,11 +1109,11 @@ pub(crate) fn received_k1_trans>( // -> psk, e, es, ss // <- e, ee, se if k1.len() != REKEY_SIZE { - return Err(byzantine_fault!(InvalidPacket, true)); + return Err(fault!(InvalidPacket, true)); } if Some(kid) != zeta.key_ref(false).recv.kid { // Some rekey packet may have arrived extremely delayed. - return Err(byzantine_fault!(UnknownLocalKeyId, false)); + return Err(fault!(UnknownLocalKeyId, false)); } let should_rekey_as_bob = match &zeta.beta { ZetaAutomata::S2 { .. } => true, @@ -1116,7 +1122,7 @@ pub(crate) fn received_k1_trans>( }; if !should_rekey_as_bob { // Some rekey packet may have arrived extremely delayed. - return Err(byzantine_fault!(OutOfSequence, false)); + return Err(fault!(OutOfSequence, false)); } let i = k1.len() - AES_GCM_TAG_SIZE; @@ -1128,11 +1134,11 @@ pub(crate) fn received_k1_trans>( &mut k1[..i], &tag, ) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } let (_, c) = from_nonce(&n); if !zeta.update_counter_window(c) { - return Err(byzantine_fault!(ExpiredCounter, true)); + return Err(fault!(ExpiredCounter, true)); } k1.truncate(i); @@ -1145,7 +1151,7 @@ pub(crate) fn received_k1_trans>( // Process message pattern 1 psk0 token. noise.mix_key_and_hash(zeta.ratchet_state1.key()); // Process message pattern 1 e token. - let e_remote = noise.read_e(&mut i, &k1).ok_or(byzantine_fault!(FailedAuth, true))?; + let e_remote = noise.read_e(&mut i, &k1).ok_or(fault!(FailedAuth, true))?; // Process message pattern 1 es token. noise.mix_dh(s_secret, &e_remote); // Process message pattern 1 ss token. @@ -1155,10 +1161,10 @@ pub(crate) fn received_k1_trans>( let k = j + AES_GCM_TAG_SIZE; let tag = k1[j..k].try_into().unwrap(); if !noise.decrypt_and_hash_in_place(to_nonce(PACKET_TYPE_REKEY_INIT, 0), &mut k1[i..j], tag) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } - let kid_send = NonZeroU32::new(u32::from_be_bytes(k1[i..j].try_into().unwrap())) - .ok_or(byzantine_fault!(FailedAuth, true))?; + let kid_send = + NonZeroU32::new(u32::from_be_bytes(k1[i..j].try_into().unwrap())).ok_or(fault!(FailedAuth, true))?; let mut k2 = Vec::new(); // Process message pattern 2 e token. @@ -1178,16 +1184,18 @@ pub(crate) fn received_k1_trans>( let result = app.save_ratchet_state( &zeta.s_remote, &zeta.session_data, - RatchetUpdate { - state1: &new_ratchet_state, - state2: Some(&zeta.ratchet_state1), - state1_was_just_added: true, - deleted_state1: zeta.ratchet_state2.as_ref(), - deleted_state2: None, - }, + CompareAndSwap::new( + &new_ratchet_state, + Some(&zeta.ratchet_state1), + true, + &zeta.ratchet_state1, + zeta.ratchet_state2.as_ref(), + false, + true, + ), ); - if let Err(e) = result { - return Err(ReceiveError::StorageError(e)); + if !result.map_err(ReceiveError::StorageError)? { + return Err(fault!(OutOfSequence, true)); } let (kek_send, kek_recv) = noise.get_ask(LABEL_KEX_KEY); let (nk_send, nk_recv) = noise.split(); @@ -1226,15 +1234,15 @@ pub(crate) fn received_k2_trans>( use FaultType::*; // <- e, ee, se if k2.len() != REKEY_SIZE { - return Err(byzantine_fault!(InvalidPacket, true)); + return Err(fault!(InvalidPacket, true)); } if Some(kid) != zeta.key_ref(false).recv.kid { // Some rekey packet may have arrived extremely delayed. - return Err(byzantine_fault!(UnknownLocalKeyId, false)); + return Err(fault!(UnknownLocalKeyId, false)); } if !matches!(&zeta.beta, ZetaAutomata::R1 { .. }) { // Some rekey packet may have arrived extremely delayed. - return Err(byzantine_fault!(OutOfSequence, false)); + return Err(fault!(OutOfSequence, false)); } let i = k2.len() - AES_GCM_TAG_SIZE; @@ -1246,11 +1254,11 @@ pub(crate) fn received_k2_trans>( &mut k2[..i], &tag, ) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } let (_, c) = from_nonce(&n); if !zeta.update_counter_window(c) { - return Err(byzantine_fault!(ExpiredCounter, true)); + return Err(fault!(ExpiredCounter, true)); } k2.truncate(i); let result = (|| { @@ -1258,7 +1266,7 @@ pub(crate) fn received_k2_trans>( let mut noise = noise.clone(); let mut i = 0; // Process message pattern 2 e token. - let e_remote = noise.read_e(&mut i, &k2).ok_or(byzantine_fault!(FailedAuth, true))?; + let e_remote = noise.read_e(&mut i, &k2).ok_or(fault!(FailedAuth, true))?; // Process message pattern 2 ee token. noise.mix_dh(e_secret, &e_remote); // Process message pattern 2 se token. @@ -1268,26 +1276,28 @@ pub(crate) fn received_k2_trans>( let k = j + AES_GCM_TAG_SIZE; let tag = k2[j..k].try_into().unwrap(); if !noise.decrypt_and_hash_in_place(to_nonce(PACKET_TYPE_REKEY_COMPLETE, 0), &mut k2[i..j], tag) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } - let kid_send = NonZeroU32::new(u32::from_be_bytes(k2[i..j].try_into().unwrap())) - .ok_or(byzantine_fault!(InvalidPacket, true))?; + let kid_send = + NonZeroU32::new(u32::from_be_bytes(k2[i..j].try_into().unwrap())).ok_or(fault!(InvalidPacket, true))?; let (rk, rf) = noise.get_ask(LABEL_RATCHET_STATE); let new_ratchet_state = RatchetState::new(rk, rf, zeta.ratchet_state1.chain_len() + 1); let result = app.save_ratchet_state( &zeta.s_remote, &zeta.session_data, - RatchetUpdate { - state1: &new_ratchet_state, - state2: None, - state1_was_just_added: true, - deleted_state1: Some(&zeta.ratchet_state1), - deleted_state2: zeta.ratchet_state2.as_ref(), - }, + CompareAndSwap::new( + &new_ratchet_state, + None, + true, + &zeta.ratchet_state1, + zeta.ratchet_state2.as_ref(), + true, + true, + ), ); - if let Err(e) = result { - return Err(ReceiveError::StorageError(e)); + if !result.map_err(ReceiveError::StorageError)? { + return Err(fault!(OutOfSequence, true)); } let (kek_recv, kek_send) = noise.get_ask(LABEL_KEX_KEY); let (nk_recv, nk_send) = noise.split(); @@ -1363,7 +1373,7 @@ pub(crate) fn received_payload_in_place( use FaultType::*; if payload.len() < AES_GCM_TAG_SIZE { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } let is_other = if Some(kid) == zeta.key_ref(true).recv.kid { true @@ -1372,21 +1382,21 @@ pub(crate) fn received_payload_in_place( } else { // A packet would have to be delayed by around an hour for this error to occur, but it can // occur naturally due to just out-of-order transport. - return Err(byzantine_fault!(OutOfSequence, false)); + return Err(fault!(OutOfSequence, false)); }; let i = payload.len() - AES_GCM_TAG_SIZE; let specified_key = zeta.key_ref(is_other).recv.nk.as_ref(); - let specified_key = specified_key.ok_or(byzantine_fault!(OutOfSequence, true))?; + let specified_key = specified_key.ok_or(fault!(OutOfSequence, true))?; let tag = payload[i..].try_into().unwrap(); if !C::Aead::decrypt_in_place(specified_key, &n, None, &mut payload[..i], &tag) { - return Err(byzantine_fault!(FailedAuth, true)); + return Err(fault!(FailedAuth, true)); } let (_, c) = from_nonce(&n); if !zeta.update_counter_window(c) { // This error is marked as not happening naturally, but it could occur if something about // the transport protocol is duplicating packets. - return Err(byzantine_fault!(ExpiredCounter, true)); + return Err(fault!(ExpiredCounter, true)); } payload.truncate(i); diff --git a/whitepaper/zssp.pdf b/whitepaper/zssp.pdf index 3a17d5d40b69ad54c481e9867bc14810b641a86e..be71c712bfddb798ca5d028ac676eba35b65336a 100644 GIT binary patch delta 69217 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 delta 66695 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 Zv8be?C^e1C#K_RX+>lFE)z#mP3ji68OltrD diff --git a/whitepaper/zssp.tex b/whitepaper/zssp.tex index 2dc4a9d..ebd55a0 100644 --- a/whitepaper/zssp.tex +++ b/whitepaper/zssp.tex @@ -214,13 +214,13 @@ So Alice and Bob will need to allow potentially the last two derived ratchet key To minimize the number of ratchet keys Alice and Bob have to store in memory, the final step of every key exchange in ZSSP is to delete the previous ratchet key, once both peers are certain the other peer has derived the new one. So usually Alice and Bob will only have and use the most recently derived ratchet key, but if a peer disappears during key exchange, Alice and Bob will have up to the last two ratchet keys, and will be able to coordinate using the one they are certain both peers have access to. -If this is the first time Alice and Bob are communicating, Alice will send an empty string instead of a ratchet fingerprint to signal this fact. Both Alice and Bob understand this to mean they ought to complete the key exchange with a ratchet key of all zeros. When Alice reveals their identity in the third message of Noise XK, Bob will have to check that it is indeed true that Alice has never communicated with Bob before, and they don't already have a ratchet key and fingerprint. Similarly, if Alice does use a valid ratchet fingerprint and key, Bob must check if the pair is actually associated with Alice, and isn't being stolen from a different peer. +If this is the first time Alice and Bob are communicating, Alice will send a string of all zeros instead of a ratchet fingerprint to signal this fact. Both Alice and Bob understand this to mean they ought to complete the key exchange with a ratchet key of all zeros. When Alice reveals their identity in the third message of Noise XK, Bob will have to check that it is indeed true that Alice has never communicated with Bob before, and they don't already have a ratchet key and fingerprint. Similarly, if Alice does use a valid ratchet fingerprint and key, Bob must check if the pair is actually associated with Alice, and isn't being stolen from a different peer. Since the ratchet fingerprint is included in the first message of Noise XK, with weak forward secrecy, there is some concern it could violate identity hiding. However, since the ratchet fingerprint is a constantly rotating ASK, information about the communicating peers can only be leaked by it if the same ratchet fingerprint is used twice. In normal operation, not only will the ratchet fingerprint only be used once, but it will be deleted with the ratchet key when the key exchange is completed, providing replay protection. Furthermore, if the same ratchet fingerprint is used more than once, all this will reveal is that the same pair of peers are initiating a session with each other, and only if Bob's private key is compromised. -Actually this protocol give ZSSP a lot of useful properties. For one thing Bob can require Alice to include a recognized, nonempty ratchet fingerprint in their first message. This is how ZSSP can achieve ``Silence is Golden'' despite the fact that the first Noise XK packet is usually anonymous and replayable. It also allows Bob to authenticate Alice's identity immediately, as opposed to waiting for Alice's next message. This protocol also is integral to how Alice and Bob are able to coordinate the ratchet key management described in \sectionref{sec:key_management} without any additional round trips. +Actually this protocol give ZSSP a lot of useful properties. For one thing Bob can require Alice to include a recognized, non-zero ratchet fingerprint in their first message. This is how ZSSP can achieve ``Silence is Golden'' despite the fact that the first Noise XK packet is usually anonymous and replayable. It also allows Bob to authenticate Alice's identity immediately, as opposed to waiting for Alice's next message. This protocol also is integral to how Alice and Bob are able to coordinate the ratchet key management described in \sectionref{sec:key_management} without any additional round trips. -If Alice has corrupted their storage, Alice can either send the empty string or send whatever corrupted ratchet fingerprint they still have to Bob. If Bob sees the empty string, and Bob is in opportunistic mode, Bob completes the key exchange with the zero ratchet key, making it possible for Alice to connect. If Alice sent a corrupted ratchet fingerprint, or Bob is the one who corrupted their storage, and if Bob is in opportunistic mode, Bob will ignore the unrecognized ratchet fingerprint and instead uses the zero ratchet key as the PSK. When Alice receives Bob's reply, if Alice is in opportunistic mode, Alice will decrypt Bob's reply with both the ratchet key they intended to use and the zero ratchet key, and go with the one that successfully decrypts the reply. This allows Alice and Bob to reset their ratchet keys in such a way that an attacker cannot perform a downgrade attack without at least one peer's static private key. And if Alice and Bob are in persistent mode, a downgrade attack is simply not possible. +If Alice has corrupted their storage, Alice can either send zeros or send whatever corrupted ratchet fingerprint they still have to Bob. If Bob sees zeros instead of a ratchet fingerprint, and Bob is in opportunistic mode, Bob completes the key exchange with the zero ratchet key, making it possible for Alice to connect. If Alice sent a corrupted ratchet fingerprint, or Bob is the one who corrupted their storage, and if Bob is in opportunistic mode, Bob will ignore the unrecognized ratchet fingerprint and instead uses the zero ratchet key as the PSK. When Alice receives Bob's reply, if Alice is in opportunistic mode, Alice will decrypt Bob's reply with both the ratchet key they intended to use and the zero ratchet key, and go with the one that successfully decrypts the reply. This allows Alice and Bob to reset their ratchet keys in such a way that an attacker cannot perform a downgrade attack without at least one peer's static private key. And if Alice and Bob are in persistent mode, a downgrade attack is simply not possible. \section{Zeta Key Exchange} @@ -271,8 +271,8 @@ The next section will be dedicated to describing ZKE in explicit detail. We will \wordbox[tlr]{2}{\texttt{e1} (1568 bytes)} \\ \bitbox[blr]{2}{} & \bitbox{2}{\texttt{e1\_tag} (16 bytes)} \\ \begin{rightwordgroup}{Noise XK\\Payload} - \wordbox{2}{$\texttt{rf}_1$ (optional 32 bytes)} \\ - \wordbox{2}{$\texttt{rf}_2$ (optional 32 bytes)} \\ + \wordbox{2}{$\texttt{rf}_1$ (32 bytes)} \\ + \wordbox{2}{$\texttt{rf}_2$ (32 bytes)} \\ \wordbox{1}{\texttt{rf\_tag} (16 bytes)} \end{rightwordgroup} \\ \end{bytefield} @@ -542,7 +542,9 @@ Given $(x, g^x)$ and $(y, g^y)$, two output keypairs from $\DHGEN()$, a \emph{ke \subsection{Transition Algorithms}\label{sec:trans_alg} -With the necessary background established, we can now define the final component of the Zeta state machine, the state transition algorithms. As is described in \definitionref{def:state_machine}, each state transition within $\zeta$ has a corresponding algorithm that must be computed. All of these algorithms are described in this section. They have access to a collection of global variables, defined below. +With the necessary background established, we can now define the final component of the Zeta state machine, the state transition algorithms. Anyone looking to implement the Zeta state machine must read this section in its entireity. Many paragraphs contain rules not represented within the diagrams or algorithm psuedocode. Each rule qualified with \emph{must} must be implemented. + +As is described in \definitionref{def:state_machine}, each state transition within $\zeta$ has a corresponding algorithm. These algorithms \emph{must} be computed atomically with the state transition. If the algorithm fails or aborts, then the corresponding state transition \emph{must} not occur. All of these algorithms are described in this section. They have access to a collection of global variables, defined below. \begin{itemize} \item Persistent ratchet keys $\texttt{rk}_1, \texttt{rk}_2$, to be used as a handshake PSK @@ -562,16 +564,20 @@ With the necessary background established, we can now define the final component \item Confirmed key index $\texttt{i}$, used so peers know the most recently confirmed Noise key. \end{itemize} -All of these variables are independent \emph{per instance of} $\zeta$. So each instance of $\zeta$ stores a unique and independent set of these variables, and they are never shared between separate instances of $\zeta$. The persistent state variables $\texttt{rk}$ and $\texttt{rf}$ are a partial exception to this rule, as they can be accessed by multiple instances of $\zeta$ over time. However each of these instances must be associated with a single, unique remote peer, and two instances of $\zeta$ must never access the same set of persistent state variables at the same time. +All of these variables, except $\texttt{rk}$ and $\texttt{rf}$, are independent \emph{per instance of} $\zeta$. So each instance of $\zeta$ stores a unique and independent set of these variables, and they are never shared between separate instances of $\zeta$. -What constitutes a ``single, unique remote peer'' is up to the upper protocol to decide. Usually remote peers are identified and differentiated by their static public keys ($g^u$ or $g^v$). However ZKE does not strictly enforce a one-to-one relationship between static public keys and identity specifically so the upper protocol is able to rotate the static public keys of peers. +Variables $\texttt{rk}$ and $\texttt{rf}$ are stored on a persistent storage device, and can be accessed by multiple instances of $\zeta$ over time. Implementations \emph{must} guarantee each instance of persistent variables $\texttt{rk}$ and $\texttt{rf}$ is associated with a single, unique remote peer. When implementing $\zeta$, each instance of $\zeta$ \emph{must} store a local copy of $\texttt{rf}$. Writes to $\texttt{rk}$ and $\texttt{rf}$ \emph{must} be batched together as one atomic operation. Implementations \emph{must} check that the local copy of $\texttt{rf}$ is consistent with $\texttt{rf}$ prior to every write. If they are inconsistent, then the entire write \emph{must} be cancelled, and $\zeta$ \emph{must} immediately abort the transition algorithm which triggered the write. Then, $\zeta$ \emph{must} be deleted (i.e. $\zeta$ \emph{must} perform the timeout transition, $\tau$). This policy guarantees that if multiple, concurrent instances of $\zeta$ exist for the same remote peer, only one of them will be allowed to write to $\texttt{rk}$ and $\texttt{rf}$, while the others will be deleted. + +It \emph{must} be infeasible to recover a non-zero value of $\texttt{rk}$ or $\texttt{rf}$ based on the amount of time read and write operations on $\texttt{rk}$ or $\texttt{rf}$ take. To this end, implementations must not directly operate on ratchet keys (check for equality, create a search index, etc.). Instead, these operations must be performed indirectly upon their ratchet fingerprints. This is to ensure that, in the event a side-channel attack is found, only the ratchet fingerprint can be leaked. + +What constitutes a ``single, unique remote peer'' is up to the upper protocol to decide. Usually remote peers are identified and differentiated by their static public keys ($g^u$ or $g^v$). However ZKE does not strictly enforce a one-to-one relationship between static public keys and identity specifically so the upper protocol is able to rotate the static public keys of peers. There \emph{must} be a well-defined equality relation between peers. \begin{definition}[H][Session Counter] Every instance of $\zeta$ contains a single monotonically increasing counter, that is initialized to 0. We will use the notation $\COUNTER()$ to represent incrementing this counter by 1 and returning the previous counter value. So the very first call to $\COUNTER()$ within $\zeta$ will return 0, the second call will return 1, and so on. - It is assumed that that output of $\COUNTER()$, the counter value, will be used as an AES-GCM nonce. When a remote peer decrypts an AES-GCM ciphertext that uses a counter value nonce, they must first statefully check that this counter has never been received before, and fail authentication if it has been replayed. + It is assumed that that output of $\COUNTER()$, the counter value, will be used as an AES-GCM nonce. When a remote peer decrypts an AES-GCM ciphertext that uses a counter value nonce, they \emph{must} first statefully check that this counter has never been received before, and fail authentication if it has been replayed. \end{definition} ZKE uses a 64-bit (8 byte) counter, but AES-GCM uses a 96-bit (12 byte) nonce/IV. The Noise protocol does specify that, in order to use a counter value as an AES nonce, it should be encoded as a big-endian integer with 4 bytes of zero padding to the left of it. However, for good reason, we have decided to utilize the rightmost byte of that 4 byte padding to store the packet type number, as depicted in \figureref{fig:nonce}. This accomplishes two goals. First it explicitly authenticates the packet type of a packet, which makes it infeasible for an attacker to change the packet type field of a header without triggering an authentication failure. We could have used the additional authentication data parameter of AES-GCM to accomplish this, but the Noise protocol already makes extensive use of this parameter. Second, this nonce construction reduces the risk of catastrophic nonce reuse due to implementation error, specifically during key exchanges. While a correctly implemented counter would be sufficient for security, this adds an extra layer of defence. Given a packet type number $p$, and a counter value $c$, we will use the notation $p||c$ to represent this nonce construction. @@ -587,16 +593,15 @@ ZKE uses a 64-bit (8 byte) counter, but AES-GCM uses a 96-bit (12 byte) nonce/IV \end{figure} \begin{definition}[H][Key Id Generation] - \algn{KID-Gen} is a stateful algorithm that outputs a locally unique, uniform random 32-bit string. The output of this algorithm will be used as the \emph{key id} for a session key, allowing the local peer to multiplex many keys for many remote peers simultaneously. The output of $\algn{KID-Gen}()$ must be locally unique from all other key ids currently in use. $\algn{KID-Gen}()$ may only repeat an output if that output is currently not being used as a key id. + \algn{KID-Gen} is a stateful algorithm that outputs a locally unique, uniform random 32-bit string. The output of this algorithm will be used as the \emph{key id} for a session key, allowing the local peer to multiplex many keys for many remote peers simultaneously. The output of $\algn{KID-Gen}()$ \emph{must} be locally unique from all other key ids currently in use. $\algn{KID-Gen}()$ may only repeat an output if that output is currently not being used as a key id. \end{definition} - \begin{definition}[H][Persistent State] The variables $\texttt{rk}$ and $\texttt{rf}$ are considered the \emph{persistent state} of ZKE. The values stored in these variables are not lost when a session ends, and can be \emph{restored} at any time to initiate a new session. - $\texttt{rk}$ is a tuple of up to two 256-bit \emph{ratchet keys}. Similarly, $\texttt{rf}$ is a tuple of up to two 256-bit \emph{ratchet fingerprints}. $\texttt{rk}$ is initialized to $(0^{256}, \bot)$ and $\texttt{rf}$ is initialized to $(\varepsilon, \bot)$. + $\texttt{rk}$ is a tuple of up to two 256-bit \emph{ratchet keys}. Similarly, $\texttt{rf}$ is a tuple of up to two 256-bit \emph{ratchet fingerprints}. Both $\texttt{rk}$ and $\texttt{rf}$ are initialized to $(0^{256}, \bot)$. - If two peers are in persistent mode, and wish to connect for the first time, they may use a preshared one time password, $p$, to do so. $p$ must be a random string with at least 256-bits of entropy. If a one time password is being used then $r\gets \KDF(p, \texttt{"ZSSP\_OTP\_TO\_RATCHET"}, \varepsilon, 2)$ must be computed. Both peers then initialize $\texttt{rk}$ to $(r_1, \bot)$ and $\texttt{rf}$ to $(r_2, \bot)$. $p$ must only be used once; A fresh instance of $p$ must be generated for every new pair of peers who wish to connect. + If two peers are in persistent mode, and wish to connect for the first time, they may use a preshared one time password, $p$, to do so. $p$ \emph{must} be a random string with at least 256-bits of entropy. If a one time password is being used then $r\gets \KDF(p, \texttt{"ZSSP\_OTP\_TO\_RATCHET"}, \varepsilon, 2)$ \emph{must} be computed. Both peers then initialize $\texttt{rk}$ to $(r_1, \bot)$ and $\texttt{rf}$ to $(r_2, \bot)$. $p$ \emph{must} only be used once; A fresh instance of $p$ \emph{must} be generated for every new pair of peers who wish to connect. \end{definition} $\bot$ represents the ``null'' value. When a variable is set to $\bot$, it means whatever was previous stored within the variable is being permanently deleted from memory. All variables are initialized to $\bot$ unless otherwise stated. $\varepsilon$ is the empty string. @@ -666,7 +671,11 @@ Prior to the execution of any of the following algorithms, $\zeta$ will check if \State $X_1\texttt{.key\_id} \gets \texttt{kid}^1_1$ \State $X_1\texttt{.e} \gets g^x$ \State $X_1\texttt{.e1} \gets \AEAD(k_4, 0||0, h_3, e_{pub})$ - \State $X_1\texttt{.rf} \gets \AEAD(k_4, 0||1, h_5, \texttt{rf})$ + \If{$\texttt{rf}_2=\bot$} + \State $X_1\texttt{.rf} \gets \AEAD(k_4, 0||1, h_5,\,\texttt{rf}_1||0^{256})$ + \Else + \State $X_1\texttt{.rf} \gets \AEAD(k_4, 0||1, h_5,\,\texttt{rf}_1||\texttt{rf}_2)$ + \EndIf \State $\texttt{hk} \gets \KDF(h_6, \texttt{"ASKH"}, c_4, 2)$ \State $c \gets X_1\texttt{.rf\_tag}\ \text{(last 8 bytes)}$ \State $\textbf{Send } (0,\ 0||c,\ X_1)$ @@ -686,15 +695,15 @@ Including the ratchet fingerprint in the Hello packet does not compromise identi \EndIf \State $\textbf{Decrypt } X_1$ according to Noise XK \State $\texttt{rf}' \gets X_1\texttt{.rf}_1$ - \State $\textbf{Restore } \texttt{rk}' \text{ based on } \texttt{rf}'$ + \State $\textbf{Restore } \texttt{rk}' \text{ based on } \texttt{rf}'$ (or $\texttt{rk}'\gets \bot$ if $\texttt{rf}' = 0^{256}$) \If{$\texttt{rk}' = \bot$} \State $\texttt{rf}' \gets X_1\texttt{.rf}_2$ - \State $\textbf{Restore } \texttt{rk}' \text{ based on } \texttt{rf}'$ + \State $\textbf{Restore } \texttt{rk}' \text{ based on } \texttt{rf}'$ (or $\texttt{rk}'\gets \bot$ if $\texttt{rf}' = 0^{256}$) \If{$\texttt{rk}' = \bot$} \If{$\pi_1=1$} \State \textbf{Fail authentication} \Else - \State $\texttt{rf}' \gets \varepsilon$ + \State $\texttt{rf}' \gets 0^{256}$ \State $\texttt{rk}' \gets 0^{256}$ \EndIf \EndIf @@ -719,12 +728,12 @@ Including the ratchet fingerprint in the Hello packet does not compromise identi \If{$c \geq 2^{24} \orb c \neq X_2\texttt{.key\_id\_tag}$ (last 3 bytes)} \State \textbf{Fail authentication} \EndIf - \State $\textbf{Decrypt } X_2$ according to Noise XK using PSK $\texttt{rk}_1$ \State $i \gets 1$ - \If{$X_2$ failed authentication} - \State $\textbf{Decrypt } X_2$ according to Noise XK using PSK $\texttt{rk}_2$ + \State $\textbf{Decrypt } X_2$ according to Noise XK using PSK $\texttt{rk}_1$ + \If{$X_2$ failed to decrypt} \State $i \gets 2$ - \If{$X_2$ failed authentication} + \State $\textbf{Decrypt } X_2$ according to Noise XK using PSK $\texttt{rk}_2$ + \If{$X_2$ failed to decrypt} \If{$\pi_2=1$} \State \textbf{Fail authentication} \Else @@ -736,10 +745,10 @@ Including the ratchet fingerprint in the Hello packet does not compromise identi \State $\texttt{kid}^1_2 \gets X_2\texttt{.key\_id}$ \State $X_3\texttt{.s} \gets \AEAD(k_{10}, 2||1, h_{11}, g^u)$ \State $X_3\texttt{.identity} \gets \AEAD(k_{13}, 2||0, h_{12}, \texttt{identity})$ - \State $\texttt{kek}^1 \gets \KDF(h_{14}, \texttt{"ASKK"}, c_{13}, 2)$ - \State $\texttt{nk}^1 \gets \KDF(\varepsilon, L, c_{13}, 2)$ \State $(\texttt{rk}_2, \texttt{rf}_2) \gets (\texttt{rk}_i, \texttt{rf}_i)$ \State $(\texttt{rk}_1, \texttt{rf}_1) \gets \KDF(h_{14}, \texttt{"ASKR"}, c_{13}, 2)$ + \State $\texttt{kek}^1 \gets \KDF(h_{14}, \texttt{"ASKK"}, c_{13}, 2)$ + \State $\texttt{nk}^1 \gets \KDF(\varepsilon, L, c_{13}, 2)$ \State $\texttt{r} \gets 1$ \State $\texttt{i} \gets 0$ \State $\textbf{Send } (\texttt{kid}^1_2,\ 2||0,\ X_3)$ @@ -748,7 +757,7 @@ Including the ratchet fingerprint in the Hello packet does not compromise identi Packet types $X_2$ and $K_1$ represent the second to last message of Noise XK and Noise KK. The remote peer will not have derived the Noise key yet, so if this peer attempted to encrypt with this key immediately, there is a risk its packets will arrive out of order and be undecryptable. Receiving a valid $C_1$ packet from the remote peer confirms that they have successfully derived the correct Noise key, which signals it can now be reliably used for encryption. -Similarly, the new ratchet key and fingerprint will not have been derived by the remote peer yet. So this peer will have to save these keys to persistent storage, but not yet delete the previous ratchet key and fingerprint until the remote peer sends a key confirmation. This means that for a brief period of time Alice has two sets of ratchet keys and fingerprints saved, instead of just one. We want to make sure Alice and Bob never have more than two of these keys saved at a time. So during the Noise XK handshake if Alice has two sets of keys and fingerprints, they must send both fingerprints to Bob, and delete the key and fingerprint that Bob chooses not to use as the PSK. +Similarly, the new ratchet key and fingerprint will not have been derived by the remote peer yet. So this peer will have to save these keys to persistent storage, but not yet delete the previous ratchet key and fingerprint until the remote peer sends a key confirmation. This means that for a brief period of time Alice has two sets of ratchet keys and fingerprints saved, instead of just one. We want to make sure Alice and Bob never have more than two of these keys saved at a time. So during the Noise XK handshake if Alice has two sets of keys and fingerprints, they \emph{must} send both fingerprints to Bob, and delete the key and fingerprint that Bob chooses not to use as the PSK. \begin{algorithm}[H] \caption{Transition $\delta(B_2, X_3)=S_1$ -- Bob receives the final packet of Noise XK. Input $\algn{Accept}$ is a function provided by the upper protocol that takes as input Alice's identity and outputs security flags $(\pi_3, \pi_4)$, $\pi_3$ can be $\bot$. If $\pi_3=\bot$, Bob will reject Alice's session. If $\pi_3=1$, and Bob is aware of a ratchet key Alice should have, but Alice is not connecting with it, Bob will reject Alice's session (similar to when $\pi_2=1$). If Bob rejects Alice's session and $\pi_4 = 0$, Bob will send a packet to Alice explicitly rejecting their session.}\label{alg:recv_x3} @@ -760,8 +769,8 @@ Similarly, the new ratchet key and fingerprint will not have been derived by the \State $(\pi_3, \pi_4) \gets \algn{Accept}(X_3\texttt{.s}, X_3\texttt{.identity})$ \If{$\pi_3 \neq \bot$} \State $\textbf{Restore } (\texttt{rk}, \texttt{rf}) \text{ based on } (X_3\texttt{.s}, X_3\texttt{.identity})$ - \If{$(\texttt{rf}', \texttt{rk}') \neq (\texttt{rf}_1, \texttt{rk}_1) \andb (\texttt{rf}', \texttt{rk}') \neq (\texttt{rf}_2, \texttt{rk}_2)$} - \If{$\pi_3 = 0 \andb \texttt{rf}' = \varepsilon$} + \If{$\texttt{rf}' \neq \texttt{rf}_1 \andb \texttt{rf}' \neq \texttt{rf}_2$} + \If{$\pi_3 = 0 \andb \texttt{rf}' = 0^{256}$} \State \textbf{Warn} the upper protocol that Alice does not have the ratchet key \Else \If{$\pi_4 = 0$} @@ -771,9 +780,9 @@ Similarly, the new ratchet key and fingerprint will not have been derived by the \State \textbf{Fail authentication} \EndIf \EndIf - \State $\texttt{nk}^1 \gets \KDF(\varepsilon, L, c_{13}, 2)$ \State $(\texttt{rk}_2, \texttt{rf}_2) \gets \bot$ \State $(\texttt{rk}_1, \texttt{rf}_1) \gets \KDF(h_{14}, \texttt{"ASKR"}, c_{13}, 2)$ + \State $\texttt{nk}^1 \gets \KDF(\varepsilon, L, c_{13}, 2)$ \State $\texttt{r} \gets 2$ \State $\texttt{i} \gets 1$ \State $C_1\texttt{.kek\_tag} \gets \AEAD(\texttt{kek}^1_1, 3||c, \varepsilon, \varepsilon)$ @@ -788,12 +797,14 @@ Similarly, the new ratchet key and fingerprint will not have been derived by the \end{algorithmic} \end{algorithm} -When packet $X_3$ is received, Bob might refuse to start a session with Alice based on their static key and identity. The preferred way for Bob to do this is to simply do nothing, and ignore Alice's packets. This would follow the ``Silence is Golden" principle of security. However there are some applications where, usually for UX reasons, Alice needs to be able tell apart having a bad connection with Bob from Bob rejecting Alice's identity. If Bob always goes silent Alice has no reliable way to tell these two situations apart. Since both Bob and Alice have the key exchange key at this point in the handshake, it was a very natural and secure extension of the protocol to allow Bob to notify Alice that the session has been refused. +If, when function $\algn{Accept}$ is called, any other instance of $\zeta$ exists with the same remote peer, then $\algn{Accept}$ \emph{must} be implemented to do one of the following: Either the other instance of $\zeta$ is deleted, or $\algn{Accept}$ returns $\pi_3=\bot$ to reject the new session. Instances of $\zeta$ in state $B_2$ \emph{must} be ignored in regards to the prior rule, since the identity of the remote peer will not have been confirmed and might not be known. Keep in mind that this rule implies that if Alice attempts to open multiple sessions with Bob at once, only one of them will be accepted. + +When packet $X_3$ is received, Bob might refuse to start a session with Alice based on their static key and identity. The preferred way for Bob to do this is to simply do nothing, and ignore Alice's packets. This would follow the ``Silence is Golden" principle of security. However there are some applications where, usually for UX reasons, Alice needs to be able tell apart having a bad connection with Bob from Bob rejecting Alice's identity. If Bob always goes silent, Alice has no reliable way to tell these two situations apart. This is the reason why packet $D$ is created and sent at the bottom of \algorithmref{alg:recv_x3}. Receipt of packet $D$ implies Bob has explicitly rejected a session with Alice. Packet types $X_3$ and $K_2$ represent the final messages of Noise XK and Noise KK, respectively. The peer who receives these packets can be sure that the remote peer has derived an identical Noise key already, and so the new key can be reliably used immediately. \begin{algorithm}[H] - \caption{Transition $\delta(A_3, C_1)=S_2$ and $\delta(R_2, C_1)=S_2$ -- A key confirmation for either the Noise XK initial handshake or Noise KK rekeying has been received. This peer must send an Acknowledgement, \figureref{packet:key_conf}, to let the remote peer know that it has been received.}\label{alg:key_conf} + \caption{Transition $\delta(A_3, C_1)=S_2$ and $\delta(R_2, C_1)=S_2$ -- A key confirmation for either the Noise XK initial handshake or Noise KK rekeying has been received. This peer \emph{must} send an Acknowledgement, \figureref{packet:key_conf}, to let the remote peer know that it has been received.}\label{alg:key_conf} \begin{algorithmic} \Require $(\texttt{kid}^{\texttt{i} + 1}_{\texttt{r}},\ 3||c,\ C_1)$ \State $\textbf{Decrypt } C_1$ using $\texttt{kek}^{\texttt{i} + 1}_{\texttt{r}}$ @@ -820,7 +831,6 @@ Packet types $C_2$ and $D$ have trivial transition algorithms. They merely decry \end{algorithmic} \end{algorithm} - \begin{algorithm}[H] \caption{Transition $\delta(S_2, K_1)=R_2$ and $\delta(R_1, K_1)=R_2$ -- Bob has received Alice's request to rekey and sends a Rekey Completion packet, \figureref{packet:rekey}, in reply. Bob has now finished Noise KK and has obtained a new Noise key. A new pair of key ids are generated so Bob can tell apart packets encrypted with the new key from those encrypted with the previous key.} \begin{algorithmic} @@ -833,28 +843,27 @@ Packet types $C_2$ and $D$ have trivial transition algorithms. They merely decry \State $K_2\texttt{.key\_id} \gets \AEAD(k_{10}', 6||0, h_{8}, \texttt{kid}^{\texttt{i} + 1}_\texttt{r})$ \State $c \gets \COUNTER()$ \State $K_2 \gets \AEAD(\texttt{kek}^\texttt{i}_{3 - \texttt{r}}, 6||c, \varepsilon, K_2)$ + \State $(\texttt{rk}_2, \texttt{rf}_2) \gets (\texttt{rk}_1, \texttt{rf}_1)$ + \State $(\texttt{rk}_1, \texttt{rf}_1) \gets \KDF(h_{11}', \texttt{"ASKR"}, c_{10}', 2)$ \State $\texttt{kek}^{\texttt{i} - 1} \gets \bot$ \State $(\texttt{kek}^{\texttt{i} + 1}_{3 - \texttt{r}}, \texttt{kek}^{\texttt{i} + 1}_\texttt{r}) \gets \KDF(h_{11}', \texttt{"ASKK"}, c_{10}', 2)$ \State $\texttt{nk}^{\texttt{i} - 1} \gets \bot$ \State $(\texttt{nk}^{\texttt{i} + 1}_{3 - \texttt{r}}, \texttt{nk}^{\texttt{i} + 1}_\texttt{r}) \gets \KDF(\varepsilon, L, c_{10}', 2)$ - \State $(\texttt{rk}_2, \texttt{rf}_2) \gets (\texttt{rk}_1, \texttt{rf}_1)$ - \State $(\texttt{rk}_1, \texttt{rf}_1) \gets \KDF(h_{11}', \texttt{"ASKR"}, c_{10}', 2)$ \State $\textbf{Send } (\texttt{kid}^\texttt{i}_{3 - \texttt{r}},\ 6||c,\ K_2)$ \end{algorithmic} \end{algorithm} - \begin{algorithm}[H] \caption{Transition $\delta(R_1, K_2)=S_1$ -- Alice has received Bob's reply and so can also finish Noise KK. They send a Key Confirmation, \figureref{packet:key_conf}, to signal the completion of the handshake. The second to last Noise key is deleted for forward secrecy.} \begin{algorithmic} \Require $(\texttt{kid}^\texttt{i}_\texttt{r},\ 6||c,\ K_2)$ \State $\textbf{Decrypt } K_2$ using $\texttt{kek}^\texttt{i}_\texttt{r}$, and Noise KK \State $\texttt{kid}^{\texttt{i} + 1}_{3 - \texttt{r}} \gets K_2\texttt{.key\_id}$ + \State $(\texttt{rk}_1, \texttt{rf}_1) \gets \KDF(h_{11}', \texttt{"ASKR"}, c_{10}', 2)$ \State $\texttt{kek}^{\texttt{i} - 1} \gets \bot$ \State $(\texttt{kek}^{\texttt{i} + 1}_\texttt{r}, \texttt{kek}^{\texttt{i} + 1}_{3 - \texttt{r}}) \gets \KDF(h_{11}', \texttt{"ASKK"}, c_{10}', 2)$ \State $\texttt{nk}^{\texttt{i} - 1} \gets \bot$ \State $(\texttt{nk}^{\texttt{i} + 1}_\texttt{r}, \texttt{nk}^{\texttt{i} + 1}_{3 - \texttt{r}}) \gets \KDF(\varepsilon, L, c_{10}', 2)$ - \State $(\texttt{rk}_1, \texttt{rf}_1) \gets \KDF(h_{11}', \texttt{"ASKR"}, c_{10}', 2)$ \State $\texttt{i} \gets \texttt{i} + 1$ \State $c \gets \COUNTER()$ \State $C_1\texttt{.kek\_tag} \gets \AEAD(\texttt{kek}^\texttt{i}_{3 - \texttt{r}}, 3||c, \varepsilon, \varepsilon)$