mirror of
https://github.com/zerotier/zssp.git
synced 2026-05-22 16:28:40 -07:00
added more debug info
This commit is contained in:
@@ -187,9 +187,6 @@ fn alice_main(
|
||||
pkt,
|
||||
&mut output_data,
|
||||
) {
|
||||
Ok((Unassociated, _)) => {
|
||||
//println!("[alice] ok");
|
||||
}
|
||||
Ok((Associated(_, event), _)) => match event {
|
||||
Established => {
|
||||
up = true;
|
||||
@@ -201,9 +198,12 @@ fn alice_main(
|
||||
Control => (),
|
||||
_ => panic!(),
|
||||
},
|
||||
Ok(_) => {
|
||||
//println!("[alice] ok");
|
||||
}
|
||||
Err(e) => {
|
||||
println!("[alice] ERROR {:?}", e);
|
||||
if let ReceiveError::ByzantineFault(e) = e {
|
||||
if let ReceiveError::ByzantineFault(e, _) = e {
|
||||
assert!(!e.unnatural())
|
||||
}
|
||||
}
|
||||
@@ -278,7 +278,6 @@ fn bob_main(
|
||||
pkt,
|
||||
&mut output_data,
|
||||
) {
|
||||
Ok((Unassociated, _)) => {}
|
||||
Ok((Associated(s, event), _)) => match event {
|
||||
NewSession | NewDowngradedSession => {
|
||||
println!("[bob] new session, took {}s", current_time as f32 / 1000.0);
|
||||
@@ -300,9 +299,10 @@ fn bob_main(
|
||||
Control => (),
|
||||
_ => panic!(),
|
||||
},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("[bob] ERROR {:?}", e);
|
||||
if let ReceiveError::ByzantineFault(e) = e {
|
||||
if let ReceiveError::ByzantineFault(e, _) = e {
|
||||
assert!(!e.unnatural())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,9 +156,6 @@ fn alice_main(
|
||||
pkt,
|
||||
&mut output_data,
|
||||
) {
|
||||
Ok((Unassociated, _)) => {
|
||||
//println!("[alice] ok");
|
||||
}
|
||||
Ok((Associated(_, event), _)) => match event {
|
||||
Established => {
|
||||
up = true;
|
||||
@@ -170,9 +167,10 @@ fn alice_main(
|
||||
Control => (),
|
||||
_ => panic!(),
|
||||
},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("[alice] ERROR {:?}", e);
|
||||
if let ReceiveError::ByzantineFault(e) = e {
|
||||
if let ReceiveError::ByzantineFault(e, _) = e {
|
||||
assert!(!e.unnatural())
|
||||
}
|
||||
}
|
||||
@@ -238,7 +236,6 @@ fn bob_main(
|
||||
pkt,
|
||||
&mut output_data,
|
||||
) {
|
||||
Ok((Unassociated, _)) => {}
|
||||
Ok((Associated(s, event), _)) => match event {
|
||||
NewSession | NewDowngradedSession => {
|
||||
println!("[bob] new session, took {}s", current_time as f32 / 1000.0);
|
||||
@@ -260,9 +257,10 @@ fn bob_main(
|
||||
Control => (),
|
||||
_ => panic!(),
|
||||
},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("[bob] ERROR {:?}", e);
|
||||
if let ReceiveError::ByzantineFault(e) = e {
|
||||
if let ReceiveError::ByzantineFault(e, _) = e {
|
||||
assert!(!e.unnatural())
|
||||
}
|
||||
}
|
||||
|
||||
+61
-22
@@ -138,8 +138,11 @@ impl ByzantineFault {
|
||||
}
|
||||
|
||||
/// An error that occurred during the receipt of a given packet.
|
||||
#[derive(Debug)]
|
||||
pub enum ReceiveError {
|
||||
///
|
||||
/// Keep in mind that when one of these occurs it inherently means that the packet from the remote
|
||||
/// peer has either not been authenticated or has failed authentication. As such, an attacker could
|
||||
/// trigger any of these. These errors should only be used for debugging and tracing.
|
||||
pub enum ReceiveError<Crypto: CryptoLayer> {
|
||||
/// A type of fault that can occur because a remote peer sent us a bad packet.
|
||||
/// Such packets will be ignored by ZSSP but a user of ZSSP might want to log
|
||||
/// them for debugging or tracing.
|
||||
@@ -147,11 +150,11 @@ pub enum ReceiveError {
|
||||
/// Because an unauthenticated remote peer can force these to occur with specific
|
||||
/// contained information, it is recommended in production to either drop these
|
||||
/// immediately, or log them safely to a local output stream and then drop them.
|
||||
ByzantineFault(ByzantineFault),
|
||||
ByzantineFault(ByzantineFault, Option<Arc<Session<Crypto>>>),
|
||||
|
||||
/// Rekeying failed and session secret has reached its hard usage count limit.
|
||||
/// The associated session will no longer function and has to be dropped.
|
||||
MaxKeyLifetimeExceeded,
|
||||
MaxKeyLifetimeExceeded(Arc<Session<Crypto>>),
|
||||
|
||||
/// Either the `ApplicationLayer::incoming_session` or `ApplicationLayer::check_accept_session`
|
||||
/// callback rejected the remote peer's attempt to establish a new session.
|
||||
@@ -163,19 +166,35 @@ pub enum ReceiveError {
|
||||
|
||||
/// An error was returned by the `output_buffer` passed to receive.
|
||||
/// The received packet was dropped.
|
||||
WriteError(std::io::Error),
|
||||
WriteError(std::io::Error, Arc<Session<Crypto>>),
|
||||
}
|
||||
|
||||
macro_rules! fault {
|
||||
($name:expr, $unnatural:ident) => {
|
||||
ReceiveError::ByzantineFault(crate::result::ByzantineFault {
|
||||
#[cfg(feature = "debug")]
|
||||
file: file!(),
|
||||
#[cfg(feature = "debug")]
|
||||
line: line!(),
|
||||
error: $name,
|
||||
unnatural: $unnatural,
|
||||
})
|
||||
ReceiveError::ByzantineFault(
|
||||
crate::result::ByzantineFault {
|
||||
#[cfg(feature = "debug")]
|
||||
file: file!(),
|
||||
#[cfg(feature = "debug")]
|
||||
line: line!(),
|
||||
error: $name,
|
||||
unnatural: $unnatural,
|
||||
},
|
||||
None,
|
||||
)
|
||||
};
|
||||
($name:expr, $unnatural:ident, $session:ident) => {
|
||||
ReceiveError::ByzantineFault(
|
||||
crate::result::ByzantineFault {
|
||||
#[cfg(feature = "debug")]
|
||||
file: file!(),
|
||||
#[cfg(feature = "debug")]
|
||||
line: line!(),
|
||||
error: $name,
|
||||
unnatural: $unnatural,
|
||||
},
|
||||
Some($session.clone()),
|
||||
)
|
||||
};
|
||||
}
|
||||
pub(crate) use fault;
|
||||
@@ -183,12 +202,17 @@ pub(crate) use fault;
|
||||
/// Result generated by the context packet receive function, with possible payloads.
|
||||
#[derive(Clone)]
|
||||
pub enum ReceiveOk<Crypto: CryptoLayer> {
|
||||
/// Packet superficially appeared valid but is not associated with a session yet.
|
||||
/// The received packet superficially appeared valid but is not associated with a session yet.
|
||||
/// This can occur because the packet was only a fragment of a larger packet,
|
||||
/// or if it was a control packet that does not go through full Noise authentication.
|
||||
Unassociated,
|
||||
/// Packet was authentic and belongs to this specific session.
|
||||
/// The received packet was authentic and belongs to this specific session.
|
||||
Associated(Arc<Session<Crypto>>, SessionEvent),
|
||||
/// The received packet was a fragment of a larger packet.
|
||||
///
|
||||
/// ***The authenticity of this fragment cannot be fully known yet.***
|
||||
/// This return value should only be used for debugging and tracing purposes.
|
||||
Fragment(Arc<Session<Crypto>>),
|
||||
}
|
||||
/// Something that can occur to an associated session when a packet is received successfully,
|
||||
/// including receiving a payload of decrypted, authenticated data.
|
||||
@@ -290,15 +314,30 @@ impl Display for ByzantineFault {
|
||||
}
|
||||
impl Error for ByzantineFault {}
|
||||
|
||||
impl Display for ReceiveError {
|
||||
impl<Crypto: CryptoLayer> std::fmt::Debug for ReceiveError<Crypto>
|
||||
where
|
||||
Crypto::SessionData: std::fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ReceiveError::ByzantineFault(e) => e.fmt(f),
|
||||
ReceiveError::MaxKeyLifetimeExceeded => f.write_str("max key lifetime exceeded"),
|
||||
ReceiveError::Rejected => f.write_str("attempt to establish session rejected"),
|
||||
ReceiveError::StorageError(e) => e.fmt(f),
|
||||
ReceiveError::WriteError(e) => e.fmt(f),
|
||||
Self::ByzantineFault(arg0, arg1) => f.debug_tuple("ByzantineFault").field(arg0).field(arg1).finish(),
|
||||
Self::MaxKeyLifetimeExceeded(arg0) => f.debug_tuple("MaxKeyLifetimeExceeded").field(arg0).finish(),
|
||||
Self::Rejected => f.write_str("Rejected"),
|
||||
Self::StorageError(arg0) => f.debug_tuple("StorageError").field(arg0).finish(),
|
||||
Self::WriteError(arg0, arg1) => f.debug_tuple("WriteError").field(arg0).field(arg1).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Error for ReceiveError {}
|
||||
|
||||
impl<Crypto: CryptoLayer> Display for ReceiveError<Crypto> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ReceiveError::ByzantineFault(e, _) => e.fmt(f),
|
||||
ReceiveError::MaxKeyLifetimeExceeded(_) => f.write_str("max key lifetime exceeded"),
|
||||
ReceiveError::Rejected => f.write_str("attempt to establish session rejected"),
|
||||
ReceiveError::StorageError(e) => e.fmt(f),
|
||||
ReceiveError::WriteError(e, _) => e.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<Crypto: CryptoLayer> Error for ReceiveError<Crypto> where Crypto::SessionData: std::fmt::Debug {}
|
||||
|
||||
+70
-67
@@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU64, Ordering};
|
||||
use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard, Weak};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
@@ -425,7 +425,7 @@ pub(crate) fn received_x1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
n: &[u8; AES_GCM_NONCE_SIZE],
|
||||
x1: &mut [u8],
|
||||
send: impl FnOnce(&mut [u8], Option<&Crypto::PrpEnc>),
|
||||
) -> Result<Option<i64>, ReceiveError> {
|
||||
) -> Result<Option<i64>, ReceiveError<Crypto>> {
|
||||
use FaultType::*;
|
||||
// <- s
|
||||
// ...
|
||||
@@ -445,13 +445,13 @@ pub(crate) fn received_x1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
let j = i + KID_SIZE;
|
||||
noise.mix_hash(hash, &x1[i..j]);
|
||||
let kid_send =
|
||||
NonZeroU32::new(u32::from_ne_bytes(x1[i..j].try_into().unwrap())).ok_or(fault!(InvalidPacket, true))?;
|
||||
NonZeroU32::new(u32::from_ne_bytes(x1[i..j].try_into().unwrap())).ok_or_else(|| fault!(InvalidPacket, true))?;
|
||||
noise.mix_hash(hash, &ctx.s_secret.public_key_bytes());
|
||||
i = j;
|
||||
// Process message pattern 1 e token.
|
||||
let e_remote = noise
|
||||
.read_e_no_init(hash, hmac, &mut i, &x1)
|
||||
.ok_or(fault!(FailedAuth, true))?;
|
||||
.ok_or_else(|| fault!(FailedAuth, true))?;
|
||||
// Process message pattern 1 es token.
|
||||
noise.mix_dh(hmac, &ctx.s_secret, &e_remote);
|
||||
// Process message pattern 1 e1 token.
|
||||
@@ -512,7 +512,7 @@ pub(crate) fn received_x1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
(&x1[e1_start..e1_end]).try_into().unwrap(),
|
||||
&mut ekem1_secret,
|
||||
)
|
||||
.ok_or(fault!(FailedAuth, true))?;
|
||||
.ok_or_else(|| fault!(FailedAuth, true))?;
|
||||
x2.extend(ekem1);
|
||||
let tag = noise.encrypt_and_hash_in_place(hash, to_nonce(PACKET_TYPE_HANDSHAKE_RESPONSE, 0), &mut x2[i..]);
|
||||
x2.extend(tag);
|
||||
@@ -574,12 +574,12 @@ pub(crate) fn received_x2_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
n: &[u8; AES_GCM_NONCE_SIZE],
|
||||
x2: &mut [u8],
|
||||
send: impl FnOnce(&mut [u8], Option<&Crypto::PrpEnc>),
|
||||
) -> Result<(bool, Option<i64>), ReceiveError> {
|
||||
) -> Result<(bool, Option<i64>), ReceiveError<Crypto>> {
|
||||
use FaultType::*;
|
||||
// <- e, ee, ekem1, psk
|
||||
// -> s, se
|
||||
if HANDSHAKE_RESPONSE_SIZE != x2.len() {
|
||||
return Err(fault!(InvalidPacket, true));
|
||||
return Err(fault!(InvalidPacket, true, session));
|
||||
}
|
||||
|
||||
let kex_lock = session.state_machine_lock.lock().unwrap();
|
||||
@@ -588,25 +588,25 @@ pub(crate) fn received_x2_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
let hmac = &mut Crypto::Hmac::new();
|
||||
|
||||
if Some(kid) != state.key_ref(true).recv.kid {
|
||||
return Err(fault!(UnknownLocalKeyId, true));
|
||||
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..] {
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
}
|
||||
let mut should_warn_missing_ratchet = false;
|
||||
let mut result = (|| {
|
||||
let a1 = if let ZetaAutomata::A1(a1) = &state.beta {
|
||||
a1
|
||||
} else {
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
};
|
||||
let mut noise = a1.noise.clone();
|
||||
let mut i = 0;
|
||||
// Process message pattern 2 e token.
|
||||
let e_remote = noise
|
||||
.read_e_no_init(hash, hmac, &mut i, &x2)
|
||||
.ok_or(fault!(FailedAuth, true))?;
|
||||
.ok_or_else(|| fault!(FailedAuth, true, session))?;
|
||||
// Process message pattern 2 ee token.
|
||||
noise.mix_dh(hmac, &a1.e_secret, &e_remote);
|
||||
// Process message pattern 2 ekem1 token.
|
||||
@@ -614,14 +614,14 @@ pub(crate) fn received_x2_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
let k = j + AES_GCM_TAG_SIZE;
|
||||
let tag = x2[j..k].try_into().unwrap();
|
||||
if !noise.decrypt_and_hash_in_place(hash, to_nonce(PACKET_TYPE_HANDSHAKE_RESPONSE, 0), &mut x2[i..j], tag) {
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
}
|
||||
let mut ekem1_secret = Zeroizing::new([0u8; KYBER_PLAINTEXT_SIZE]);
|
||||
if !a1
|
||||
.e1_secret
|
||||
.decapsulate((&x2[i..j]).try_into().unwrap(), &mut ekem1_secret)
|
||||
{
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
}
|
||||
noise.mix_key_no_init(hmac, ekem1_secret.as_ref());
|
||||
drop(ekem1_secret);
|
||||
@@ -669,7 +669,7 @@ pub(crate) fn received_x2_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
}
|
||||
}
|
||||
|
||||
let (kid_send, mut noise) = result.ok_or(fault!(FailedAuth, true))?;
|
||||
let (kid_send, mut noise) = result.ok_or_else(|| fault!(FailedAuth, true, session))?;
|
||||
|
||||
let mut x3 = ArrayVec::<u8, HEADERED_HANDSHAKE_COMPLETION_MAX_SIZE>::new();
|
||||
x3.extend([0u8; HEADER_SIZE]);
|
||||
@@ -734,7 +734,7 @@ pub(crate) fn received_x2_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
a1
|
||||
} else {
|
||||
// This return is unreachable.
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
};
|
||||
state.beta = ZetaAutomata::A3(Box::new(StateA3 { identity: a1.identity.clone(), x3: x3.clone() }));
|
||||
resend_timer
|
||||
@@ -792,7 +792,7 @@ pub(crate) fn received_x3_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
kid: NonZeroU32,
|
||||
x3: &mut [u8],
|
||||
send: impl FnOnce(&mut [u8], Option<&Crypto::PrpEnc>),
|
||||
) -> Result<(Arc<Session<Crypto>>, bool, Option<i64>), ReceiveError> {
|
||||
) -> Result<(Arc<Session<Crypto>>, bool, Option<i64>), ReceiveError<Crypto>> {
|
||||
use FaultType::*;
|
||||
// -> s, se
|
||||
if x3.len() < HANDSHAKE_COMPLETION_MIN_SIZE {
|
||||
@@ -813,7 +813,8 @@ pub(crate) fn received_x3_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
if !noise.decrypt_and_hash_in_place(hash, to_nonce(PACKET_TYPE_HANDSHAKE_COMPLETION, 1), &mut x3[i..j], tag) {
|
||||
return Err(fault!(FailedAuth, true));
|
||||
}
|
||||
let s_remote = Crypto::PublicKey::from_bytes((&x3[i..j]).try_into().unwrap()).ok_or(fault!(FailedAuth, true))?;
|
||||
let s_remote =
|
||||
Crypto::PublicKey::from_bytes((&x3[i..j]).try_into().unwrap()).ok_or_else(|| fault!(FailedAuth, true))?;
|
||||
i = k;
|
||||
// Process message pattern 3 se token.
|
||||
noise.mix_dh(hmac, &zeta.e_secret, &s_remote);
|
||||
@@ -965,11 +966,11 @@ pub(crate) fn received_c1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
n: &[u8; AES_GCM_NONCE_SIZE],
|
||||
c1: &[u8],
|
||||
send: impl FnOnce(&mut [u8], Option<&Crypto::PrpEnc>),
|
||||
) -> Result<(bool, Option<i64>), ReceiveError> {
|
||||
) -> Result<(bool, Option<i64>), ReceiveError<Crypto>> {
|
||||
use FaultType::*;
|
||||
|
||||
if c1.len() != KEY_CONFIRMATION_SIZE {
|
||||
return Err(fault!(InvalidPacket, true));
|
||||
return Err(fault!(InvalidPacket, true, session));
|
||||
}
|
||||
|
||||
let kex_lock = session.state_machine_lock.lock().unwrap();
|
||||
@@ -982,18 +983,18 @@ pub(crate) fn received_c1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
} else {
|
||||
// Some key confirmation may have arrived extremely delayed.
|
||||
// It is unlikely but possible.
|
||||
return Err(fault!(OutOfSequence, false));
|
||||
return Err(fault!(OutOfSequence, false, session));
|
||||
};
|
||||
|
||||
let specified_key = state.key_ref(is_other).recv.kek.as_ref();
|
||||
let specified_key = specified_key.ok_or(fault!(OutOfSequence, true))?;
|
||||
let specified_key = specified_key.ok_or_else(|| fault!(OutOfSequence, true, session))?;
|
||||
let tag = c1[..].try_into().unwrap();
|
||||
if !Crypto::Aead::decrypt_in_place(specified_key, n, &[], &mut [], tag) {
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
}
|
||||
let (_, c) = from_nonce(n);
|
||||
if !session.window.update(c) {
|
||||
return Err(fault!(ExpiredCounter, true));
|
||||
return Err(fault!(ExpiredCounter, true, session));
|
||||
}
|
||||
let mut reduced_service_time = None;
|
||||
|
||||
@@ -1046,9 +1047,9 @@ pub(crate) fn received_c1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
Err(true) => {
|
||||
drop(state);
|
||||
session.expire();
|
||||
Err(fault!(ExpiredCounter, true))
|
||||
Err(fault!(ExpiredCounter, true, session))
|
||||
}
|
||||
Err(false) => Err(fault!(OutOfSequence, true)),
|
||||
Err(false) => Err(fault!(OutOfSequence, true, session)),
|
||||
}
|
||||
}
|
||||
/// Corresponds to the trivial Transition Algorithm described for processing C_2 packets found in
|
||||
@@ -1060,11 +1061,11 @@ pub(crate) fn received_c2_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
kid: NonZeroU32,
|
||||
n: &[u8; AES_GCM_NONCE_SIZE],
|
||||
c2: &[u8],
|
||||
) -> Result<Option<i64>, ReceiveError> {
|
||||
) -> Result<Option<i64>, ReceiveError<Crypto>> {
|
||||
use FaultType::*;
|
||||
|
||||
if c2.len() != ACKNOWLEDGEMENT_SIZE {
|
||||
return Err(fault!(InvalidPacket, true));
|
||||
return Err(fault!(InvalidPacket, true, session));
|
||||
}
|
||||
|
||||
let kex_lock = session.state_machine_lock.lock().unwrap();
|
||||
@@ -1072,20 +1073,20 @@ pub(crate) fn received_c2_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
|
||||
if Some(kid) != state.key_ref(false).recv.kid {
|
||||
// Some acknowledgement may have arrived extremely delayed.
|
||||
return Err(fault!(UnknownLocalKeyId, false));
|
||||
return Err(fault!(UnknownLocalKeyId, false, session));
|
||||
}
|
||||
if !matches!(&state.beta, ZetaAutomata::S1) {
|
||||
// Some acknowledgement may have arrived extremely delayed.
|
||||
return Err(fault!(OutOfSequence, false));
|
||||
return Err(fault!(OutOfSequence, false, session));
|
||||
}
|
||||
|
||||
let tag = c2[..].try_into().unwrap();
|
||||
if !Crypto::Aead::decrypt_in_place(state.key_ref(false).recv.kek.as_ref().unwrap(), n, &[], &mut [], tag) {
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
}
|
||||
let (_, c) = from_nonce(n);
|
||||
if !session.window.update(c) {
|
||||
return Err(fault!(ExpiredCounter, true));
|
||||
return Err(fault!(ExpiredCounter, true, session));
|
||||
}
|
||||
drop(state);
|
||||
let timeout_timer = {
|
||||
@@ -1111,27 +1112,27 @@ pub(crate) fn received_d_trans<Crypto: CryptoLayer>(
|
||||
kid: NonZeroU32,
|
||||
n: &[u8; AES_GCM_NONCE_SIZE],
|
||||
d: &[u8],
|
||||
) -> Result<(), ReceiveError> {
|
||||
) -> Result<(), ReceiveError<Crypto>> {
|
||||
use FaultType::*;
|
||||
|
||||
if d.len() != SESSION_REJECTED_SIZE {
|
||||
return Err(fault!(InvalidPacket, true));
|
||||
return Err(fault!(InvalidPacket, true, session));
|
||||
}
|
||||
|
||||
let kex_lock = session.state_machine_lock.lock().unwrap();
|
||||
let state = session.state.read().unwrap();
|
||||
|
||||
if Some(kid) != state.key_ref(true).recv.kid || !matches!(&state.beta, ZetaAutomata::A3 { .. }) {
|
||||
return Err(fault!(OutOfSequence, true));
|
||||
return Err(fault!(OutOfSequence, true, session));
|
||||
}
|
||||
|
||||
let tag = d[..].try_into().unwrap();
|
||||
if !Crypto::Aead::decrypt_in_place(state.key_ref(true).recv.kek.as_ref().unwrap(), n, &[], &mut [], tag) {
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
}
|
||||
let (_, c) = from_nonce(n);
|
||||
if !session.window.update(c) {
|
||||
return Err(fault!(ExpiredCounter, true));
|
||||
return Err(fault!(ExpiredCounter, true, session));
|
||||
}
|
||||
|
||||
drop(state);
|
||||
@@ -1329,7 +1330,7 @@ pub(crate) fn received_k1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
n: &[u8; AES_GCM_NONCE_SIZE],
|
||||
k1: &mut [u8],
|
||||
send: impl FnOnce(&mut [u8], Option<&Crypto::PrpEnc>),
|
||||
) -> Result<Option<i64>, ReceiveError> {
|
||||
) -> Result<Option<i64>, ReceiveError<Crypto>> {
|
||||
use FaultType::*;
|
||||
// -> s
|
||||
// <- s
|
||||
@@ -1337,7 +1338,7 @@ pub(crate) fn received_k1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
// -> psk, e, es, ss
|
||||
// <- e, ee, se
|
||||
if k1.len() != REKEY_SIZE {
|
||||
return Err(fault!(InvalidPacket, true));
|
||||
return Err(fault!(InvalidPacket, true, session));
|
||||
}
|
||||
|
||||
let kex_lock = session.state_machine_lock.lock().unwrap();
|
||||
@@ -1345,7 +1346,7 @@ pub(crate) fn received_k1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
|
||||
if Some(kid) != state.key_ref(false).recv.kid {
|
||||
// Some rekey packet may have arrived extremely delayed.
|
||||
return Err(fault!(UnknownLocalKeyId, false));
|
||||
return Err(fault!(UnknownLocalKeyId, false, session));
|
||||
}
|
||||
let should_rekey_as_bob = match &state.beta {
|
||||
ZetaAutomata::S2 { .. } => true,
|
||||
@@ -1354,18 +1355,18 @@ pub(crate) fn received_k1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
};
|
||||
if !should_rekey_as_bob {
|
||||
// Some rekey packet may have arrived extremely delayed.
|
||||
return Err(fault!(OutOfSequence, false));
|
||||
return Err(fault!(OutOfSequence, false, session));
|
||||
}
|
||||
|
||||
let i = k1.len() - AES_GCM_TAG_SIZE;
|
||||
let tag = k1[i..].try_into().unwrap();
|
||||
let kek_recv = state.key_ref(false).recv.kek.as_ref().unwrap();
|
||||
if !Crypto::Aead::decrypt_in_place(kek_recv, n, &[], &mut k1[..i], &tag) {
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
}
|
||||
let (_, c) = from_nonce(n);
|
||||
if !session.window.update(c) {
|
||||
return Err(fault!(ExpiredCounter, true));
|
||||
return Err(fault!(ExpiredCounter, true, session));
|
||||
}
|
||||
|
||||
let result = (move || {
|
||||
@@ -1381,7 +1382,7 @@ pub(crate) fn received_k1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
// Process message pattern 1 e token.
|
||||
let e_remote = noise
|
||||
.read_e_no_init(hash, hmac, &mut i, &k1)
|
||||
.ok_or(fault!(FailedAuth, true))?;
|
||||
.ok_or_else(|| fault!(FailedAuth, true, session))?;
|
||||
// Process message pattern 1 es token.
|
||||
noise.mix_dh_no_init(hmac, &ctx.s_secret, &e_remote);
|
||||
// Process message pattern 1 ss token.
|
||||
@@ -1391,10 +1392,10 @@ pub(crate) fn received_k1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
let k = j + AES_GCM_TAG_SIZE;
|
||||
let tag = k1[j..k].try_into().unwrap();
|
||||
if !noise.decrypt_and_hash_in_place(hash, to_nonce(PACKET_TYPE_REKEY_INIT, 0), &mut k1[i..j], tag) {
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
}
|
||||
let kid_send =
|
||||
NonZeroU32::new(u32::from_ne_bytes(k1[i..j].try_into().unwrap())).ok_or(fault!(FailedAuth, true))?;
|
||||
let kid_send = NonZeroU32::new(u32::from_ne_bytes(k1[i..j].try_into().unwrap()))
|
||||
.ok_or_else(|| fault!(FailedAuth, true, session))?;
|
||||
|
||||
let mut k2 = ArrayVec::<u8, HEADERED_REKEY_SIZE>::new();
|
||||
k2.extend([0u8; HEADER_SIZE]);
|
||||
@@ -1459,8 +1460,8 @@ pub(crate) fn received_k1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
let state = session.state.read().unwrap();
|
||||
match send_control(session, &state, PACKET_TYPE_REKEY_COMPLETE, k2, send) {
|
||||
Ok(()) => Ok(reduced_service_time),
|
||||
Err(false) => Err(fault!(OutOfSequence, true)),
|
||||
Err(true) => Err(fault!(ExpiredCounter, true)),
|
||||
Err(false) => Err(fault!(OutOfSequence, true, session)),
|
||||
Err(true) => Err(fault!(ExpiredCounter, true, session)),
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -1478,11 +1479,11 @@ pub(crate) fn received_k2_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
n: &[u8; AES_GCM_NONCE_SIZE],
|
||||
k2: &mut [u8],
|
||||
send: impl FnOnce(&mut [u8], Option<&Crypto::PrpEnc>),
|
||||
) -> Result<Option<i64>, ReceiveError> {
|
||||
) -> Result<Option<i64>, ReceiveError<Crypto>> {
|
||||
use FaultType::*;
|
||||
// <- e, ee, se
|
||||
if k2.len() != REKEY_SIZE {
|
||||
return Err(fault!(InvalidPacket, true));
|
||||
return Err(fault!(InvalidPacket, true, session));
|
||||
}
|
||||
|
||||
let kex_lock = session.state_machine_lock.lock().unwrap();
|
||||
@@ -1490,22 +1491,22 @@ pub(crate) fn received_k2_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
|
||||
if Some(kid) != state.key_ref(false).recv.kid {
|
||||
// Some rekey packet may have arrived extremely delayed.
|
||||
return Err(fault!(UnknownLocalKeyId, false));
|
||||
return Err(fault!(UnknownLocalKeyId, false, session));
|
||||
}
|
||||
if !matches!(&state.beta, ZetaAutomata::R1 { .. }) {
|
||||
// Some rekey packet may have arrived extremely delayed.
|
||||
return Err(fault!(OutOfSequence, false));
|
||||
return Err(fault!(OutOfSequence, false, session));
|
||||
}
|
||||
|
||||
let i = k2.len() - AES_GCM_TAG_SIZE;
|
||||
let tag = k2[i..].try_into().unwrap();
|
||||
let kek_recv = state.key_ref(false).recv.kek.as_ref().unwrap();
|
||||
if !Crypto::Aead::decrypt_in_place(kek_recv, n, &[], &mut k2[..i], &tag) {
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
}
|
||||
let (_, c) = from_nonce(n);
|
||||
if !session.window.update(c) {
|
||||
return Err(fault!(ExpiredCounter, true));
|
||||
return Err(fault!(ExpiredCounter, true, session));
|
||||
}
|
||||
let result = (move || {
|
||||
if let ZetaAutomata::R1 { noise, e_secret, .. } = &state.beta {
|
||||
@@ -1516,7 +1517,7 @@ pub(crate) fn received_k2_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
// Process message pattern 2 e token.
|
||||
let e_remote = noise
|
||||
.read_e_no_init(hash, hmac, &mut i, &k2)
|
||||
.ok_or(fault!(FailedAuth, true))?;
|
||||
.ok_or_else(|| fault!(FailedAuth, true, session))?;
|
||||
// Process message pattern 2 ee token.
|
||||
noise.mix_dh_no_init(hmac, e_secret, &e_remote);
|
||||
// Process message pattern 2 se token.
|
||||
@@ -1526,10 +1527,10 @@ pub(crate) fn received_k2_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
let k = j + AES_GCM_TAG_SIZE;
|
||||
let tag = k2[j..k].try_into().unwrap();
|
||||
if !noise.decrypt_and_hash_in_place(hash, to_nonce(PACKET_TYPE_REKEY_COMPLETE, 0), &mut k2[i..j], tag) {
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
}
|
||||
let kid_send =
|
||||
NonZeroU32::new(u32::from_ne_bytes(k2[i..j].try_into().unwrap())).ok_or(fault!(InvalidPacket, true))?;
|
||||
let kid_send = NonZeroU32::new(u32::from_ne_bytes(k2[i..j].try_into().unwrap()))
|
||||
.ok_or_else(|| fault!(InvalidPacket, true, session))?;
|
||||
|
||||
let new_ratchet_state = create_ratchet_state(hmac, &noise, state.ratchet_state1.chain_len);
|
||||
app.save_ratchet_state(
|
||||
@@ -1581,8 +1582,8 @@ pub(crate) fn received_k2_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
|
||||
c1.extend([0u8; HEADER_SIZE]);
|
||||
match send_control(&session, &state, PACKET_TYPE_KEY_CONFIRM, c1, send) {
|
||||
Ok(()) => Ok(reduced_service_time),
|
||||
Err(false) => Err(fault!(OutOfSequence, true)),
|
||||
Err(true) => Err(fault!(ExpiredCounter, true)),
|
||||
Err(false) => Err(fault!(OutOfSequence, true, session)),
|
||||
Err(true) => Err(fault!(ExpiredCounter, true, session)),
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
@@ -1707,7 +1708,7 @@ pub(crate) fn receive_payload_in_place<Crypto: CryptoLayer>(
|
||||
nonce: &[u8; AES_GCM_NONCE_SIZE],
|
||||
fragments: &mut [Crypto::IncomingPacketBuffer],
|
||||
mut output_buffer: impl Write,
|
||||
) -> Result<(), ReceiveError> {
|
||||
) -> Result<(), ReceiveError<Crypto>> {
|
||||
use FaultType::*;
|
||||
debug_assert!(!fragments.is_empty());
|
||||
|
||||
@@ -1717,10 +1718,12 @@ pub(crate) fn receive_payload_in_place<Crypto: CryptoLayer>(
|
||||
state.keys[1].nk.as_ref()
|
||||
} else {
|
||||
// Should be unreachable unless we are leaking kids somewhere.
|
||||
return Err(fault!(UnknownLocalKeyId, true));
|
||||
return Err(fault!(UnknownLocalKeyId, true, session));
|
||||
};
|
||||
|
||||
let mut cipher = specified_key.ok_or(fault!(OutOfSequence, true))?.start_dec(nonce);
|
||||
let mut cipher = specified_key
|
||||
.ok_or_else(|| fault!(OutOfSequence, true, session))?
|
||||
.start_dec(nonce);
|
||||
let (_, c) = from_nonce(nonce);
|
||||
|
||||
// NOTE: This only works because we check the size of every received fragment in the receive
|
||||
@@ -1736,25 +1739,25 @@ pub(crate) fn receive_payload_in_place<Crypto: CryptoLayer>(
|
||||
cipher.decrypt_in_place(&mut fragment[..tag_idx]);
|
||||
|
||||
if !cipher.finish((&fragment[tag_idx..]).try_into().unwrap()) {
|
||||
return Err(fault!(FailedAuth, true));
|
||||
return Err(fault!(FailedAuth, true, session));
|
||||
}
|
||||
|
||||
if !session.window.update(c) {
|
||||
// This error is marked as not happening naturally, but it could occur if something about
|
||||
// the transport protocol is duplicating packets.
|
||||
return Err(fault!(ExpiredCounter, true));
|
||||
return Err(fault!(ExpiredCounter, true, session));
|
||||
}
|
||||
|
||||
for i in 0..fragments.len() - 1 {
|
||||
let result = output_buffer.write(&fragments[i].as_ref()[HEADER_SIZE..]);
|
||||
if let Err(e) = result {
|
||||
return Err(ReceiveError::WriteError(e));
|
||||
return Err(ReceiveError::WriteError(e, session.clone()));
|
||||
}
|
||||
}
|
||||
let fragment = &fragments[fragments.len() - 1].as_ref()[HEADER_SIZE..];
|
||||
let result = output_buffer.write(&fragment[..tag_idx]);
|
||||
if let Err(e) = result {
|
||||
return Err(ReceiveError::WriteError(e));
|
||||
return Err(ReceiveError::WriteError(e, session.clone()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
+43
-44
@@ -71,7 +71,9 @@ impl<Crypto: CryptoLayer> ContextInner<Crypto> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_fragment_header(incoming_fragment: &[u8]) -> Result<(usize, usize, [u8; AES_GCM_NONCE_SIZE]), ReceiveError> {
|
||||
fn parse_fragment_header<Crypto: CryptoLayer>(
|
||||
incoming_fragment: &[u8],
|
||||
) -> Result<(usize, usize, [u8; AES_GCM_NONCE_SIZE]), ReceiveError<Crypto>> {
|
||||
let fragment_no = incoming_fragment[FRAGMENT_NO_IDX] as usize;
|
||||
let fragment_count = incoming_fragment[FRAGMENT_COUNT_IDX] as usize;
|
||||
if fragment_no >= fragment_count || fragment_count > MAX_FRAGMENTS {
|
||||
@@ -206,7 +208,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
remote_address: &impl Hash,
|
||||
mut incoming_fragment_buf: Crypto::IncomingPacketBuffer,
|
||||
output_buffer: impl Write,
|
||||
) -> Result<(ReceiveOk<Crypto>, Option<i64>), ReceiveError> {
|
||||
) -> Result<(ReceiveOk<Crypto>, Option<i64>), ReceiveError<Crypto>> {
|
||||
use crate::result::FaultType::*;
|
||||
let ctx = &self.0;
|
||||
send_unassociated_mtu = send_unassociated_mtu.max(MIN_TRANSPORT_MTU);
|
||||
@@ -234,40 +236,38 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
//vrfy
|
||||
if packet_type == PACKET_TYPE_HANDSHAKE_RESPONSE {
|
||||
if !matches!(&state.beta, ZetaAutomata::A1(_)) {
|
||||
// A resent handshake response from Bob may have arrived out of order,
|
||||
// after we already received one.
|
||||
return Err(fault!(OutOfSequence, false));
|
||||
}
|
||||
if incoming_counter >= COUNTER_WINDOW_MAX_SKIP_AHEAD {
|
||||
return Err(fault!(ExpiredCounter, true));
|
||||
}
|
||||
} else if PACKET_TYPE_USES_COUNTER_RANGE.contains(&packet_type) {
|
||||
// For DOS resistant reply-protection we need to check that the given counter is
|
||||
// in the window of valid counters immediately.
|
||||
// But for packets larger than 1 fragment we can't actually record the
|
||||
// counter as received until we've authenticated the packet.
|
||||
// So we check the counter window twice, and only update it the second time
|
||||
// after the packet has been authenticated.
|
||||
if !session.window.check(incoming_counter) {
|
||||
// This can occur naturally if packets arrive way out of order, or
|
||||
// if they are duplicates.
|
||||
// This can also be naturally triggered if Bob has just successfully
|
||||
// received the first session key and is reject all of Alice's resends.
|
||||
// This can also occur if a session was manually expired, but not
|
||||
// dropped, and the remote party is still sending us data.
|
||||
return Err(fault!(ExpiredCounter, false));
|
||||
}
|
||||
} else if packet_type == PACKET_TYPE_HANDSHAKE_COMPLETION {
|
||||
// This can be triggered if Bob successfully received a session key and
|
||||
// needs to reject all of Alice's resends of PACKET_TYPE_NOISE_XK_PATTERN_3.
|
||||
return Err(fault!(InvalidPacket, false));
|
||||
} else {
|
||||
return Err(fault!(InvalidPacket, true));
|
||||
//vrfy
|
||||
if packet_type == PACKET_TYPE_HANDSHAKE_RESPONSE {
|
||||
if !matches!(&state.beta, ZetaAutomata::A1(_)) {
|
||||
// A resent handshake response from Bob may have arrived out of order,
|
||||
// after we already received one.
|
||||
return Err(fault!(OutOfSequence, false, session));
|
||||
}
|
||||
if incoming_counter >= COUNTER_WINDOW_MAX_SKIP_AHEAD {
|
||||
return Err(fault!(ExpiredCounter, true, session));
|
||||
}
|
||||
} else if PACKET_TYPE_USES_COUNTER_RANGE.contains(&packet_type) {
|
||||
// For DOS resistant reply-protection we need to check that the given counter is
|
||||
// in the window of valid counters immediately.
|
||||
// But for packets larger than 1 fragment we can't actually record the
|
||||
// counter as received until we've authenticated the packet.
|
||||
// So we check the counter window twice, and only update it the second time
|
||||
// after the packet has been authenticated.
|
||||
if !session.window.check(incoming_counter) {
|
||||
// This can occur naturally if packets arrive way out of order, or
|
||||
// if they are duplicates.
|
||||
// This can also be naturally triggered if Bob has just successfully
|
||||
// received the first session key and is reject all of Alice's resends.
|
||||
// This can also occur if a session was manually expired, but not
|
||||
// dropped, and the remote party is still sending us data.
|
||||
return Err(fault!(ExpiredCounter, false, session));
|
||||
}
|
||||
} else if packet_type == PACKET_TYPE_HANDSHAKE_COMPLETION {
|
||||
// This can be triggered if Bob successfully received a session key and
|
||||
// needs to reject all of Alice's resends of PACKET_TYPE_NOISE_XK_PATTERN_3.
|
||||
return Err(fault!(InvalidPacket, false, session));
|
||||
} else {
|
||||
return Err(fault!(InvalidPacket, true, session));
|
||||
}
|
||||
|
||||
// Handle defragmentation.
|
||||
@@ -282,7 +282,8 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
&mut fragment_buffer,
|
||||
);
|
||||
if fragment_buffer.is_empty() {
|
||||
return Ok((ReceiveOk::Unassociated, None));
|
||||
drop(state);
|
||||
return Ok((ReceiveOk::Fragment(session), None));
|
||||
} else {
|
||||
// We have not yet authenticated the sender so we do not report
|
||||
// receiving a packet from them.
|
||||
@@ -308,12 +309,12 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
&mut fragment_buffer,
|
||||
);
|
||||
if fragment_buffer.is_empty() {
|
||||
return Ok((ReceiveOk::Unassociated, None));
|
||||
return Ok((ReceiveOk::Fragment(session), None));
|
||||
} else {
|
||||
for fragment in fragment_buffer.as_ref() {
|
||||
buffer
|
||||
.try_extend_from_slice(&fragment.as_ref()[HEADER_SIZE..])
|
||||
.map_err(|_| fault!(InvalidPacket, true))?;
|
||||
.map_err(|_| fault!(InvalidPacket, true, session))?;
|
||||
}
|
||||
// We have not yet authenticated the sender so we do not report
|
||||
// receiving a packet from them.
|
||||
@@ -407,7 +408,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
log!(app, DIsAuthClosedSession(&session));
|
||||
(SessionEvent::Rejected, None)
|
||||
}
|
||||
_ => return Err(fault!(InvalidPacket, true)), // This is unreachable.
|
||||
_ => return Err(fault!(InvalidPacket, true, session)), // This is unreachable.
|
||||
}
|
||||
};
|
||||
Ok((ReceiveOk::Associated(session, ret.0), ret.1))
|
||||
@@ -428,11 +429,9 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
ReceivedRawFragment(packet_type, incoming_counter, fragment_no, fragment_count)
|
||||
);
|
||||
|
||||
{
|
||||
//vrfy
|
||||
if packet_type != PACKET_TYPE_HANDSHAKE_COMPLETION || incoming_counter != 0 {
|
||||
return Err(fault!(InvalidPacket, true));
|
||||
}
|
||||
//vrfy
|
||||
if packet_type != PACKET_TYPE_HANDSHAKE_COMPLETION || incoming_counter != 0 {
|
||||
return Err(fault!(InvalidPacket, true));
|
||||
}
|
||||
|
||||
let mut buffer = ArrayVec::<u8, HANDSHAKE_COMPLETION_MAX_SIZE>::new();
|
||||
|
||||
Reference in New Issue
Block a user