diff --git a/examples/basic_test.rs b/examples/basic_test.rs index 650721b..8770adb 100644 --- a/examples/basic_test.rs +++ b/examples/basic_test.rs @@ -21,7 +21,9 @@ use rand_core::OsRng; use rand_core::RngCore; use sha2::Sha512; -use zssp_proto::applicationlayer::{AcceptAction, ApplicationLayer, RatchetState, RatchetUpdate, Settings, RATCHET_SIZE}; +use zssp_proto::applicationlayer::{ + AcceptAction, ApplicationLayer, RatchetState, RatchetUpdate, Settings, RATCHET_SIZE, +}; use zssp_proto::crypto_impl::PqcKyberSecretKey; use zssp_proto::Session; @@ -81,7 +83,10 @@ impl ApplicationLayer for &TestApplication { } } - fn restore_by_fingerprint(&self, ratchet_fingerprint: &[u8; RATCHET_SIZE]) -> Result, Self::StorageError> { + fn restore_by_fingerprint( + &self, + ratchet_fingerprint: &[u8; RATCHET_SIZE], + ) -> Result, Self::StorageError> { let ratchets = self.ratchets.lock().unwrap(); Ok(ratchets.rf_map.get(ratchet_fingerprint).cloned()) } @@ -156,7 +161,14 @@ fn alice_main( up = false; alice_session = Some( context - .open(alice_app, |b| alice_out.send(b).is_ok(), TEST_MTU, bob_pubkey.clone(), 0, Vec::new()) + .open( + alice_app, + |b| alice_out.send(b).is_ok(), + TEST_MTU, + bob_pubkey.clone(), + 0, + Vec::new(), + ) .unwrap(), ); println!("[alice] opening session"); @@ -225,7 +237,8 @@ fn alice_main( } if current_time >= next_service { - next_service = current_time + context.service(alice_app, |_| Some((|b| alice_out.send(b).is_ok(), TEST_MTU))); + next_service = + current_time + context.service(alice_app, |_| Some((|b| alice_out.send(b).is_ok(), TEST_MTU))); } } } @@ -345,7 +358,17 @@ fn core(time: u64, packet_success_rate: u32) { ) }); } - ts.spawn(move || bob_main(run, packet_success_rate, &bob_app, bob_out, bob_in, alice_out, bob_keypair)); + ts.spawn(move || { + bob_main( + run, + packet_success_rate, + &bob_app, + bob_out, + bob_in, + alice_out, + bob_keypair, + ) + }); thread::sleep(Duration::from_secs(time)); diff --git a/rustfmt.toml b/rustfmt.toml index 3a3929c..9c9fedd 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,4 @@ -max_width = 150 +max_width = 120 edition = "2021" newline_style = "Unix" struct_lit_width = 60 diff --git a/src/applicationlayer.rs b/src/applicationlayer.rs index 9856b4b..53090d5 100644 --- a/src/applicationlayer.rs +++ b/src/applicationlayer.rs @@ -189,7 +189,10 @@ pub trait ApplicationLayer: Sized { /// If `RatchetAction::DowngradeRatchet` is returned we will attempt to convince Alice to downgrade /// to the empty ratchet key, restarting the ratchet chain. /// If `RatchetAction::FailAuthentication` is returned Alice's connection will be silently dropped. - fn restore_by_fingerprint(&self, ratchet_fingerprint: &[u8; RATCHET_SIZE]) -> Result, Self::StorageError>; + fn restore_by_fingerprint( + &self, + ratchet_fingerprint: &[u8; RATCHET_SIZE], + ) -> Result, Self::StorageError>; /// Lookup the specific ratchet states based on the identity of the peer being communicated with. /// This function will be called whenever Alice attempts to open a session, or Bob attempts /// to verify Alice's identity. diff --git a/src/challenge.rs b/src/challenge.rs index 1a4b68b..5249a7d 100644 --- a/src/challenge.rs +++ b/src/challenge.rs @@ -58,7 +58,10 @@ impl ChallengeContext { return Ok(false); } let c = u64::from_be_bytes(response[..COUNTER_SIZE].try_into().unwrap()); - if self.check_window(c) && secure_eq(&response[COUNTER_SIZE..POW_START], &self.create_mac::(c, addr)) && verify_pow::(response) { + if self.check_window(c) + && secure_eq(&response[COUNTER_SIZE..POW_START], &self.create_mac::(c, addr)) + && verify_pow::(response) + { self.update_window(c); Ok(true) } else { diff --git a/src/context.rs b/src/context.rs index 2974c51..ca4b4a9 100644 --- a/src/context.rs +++ b/src/context.rs @@ -149,43 +149,53 @@ impl Context { if let Some(Some(session)) = session { // Process recv fragmentation layer. let mut zeta = session.0.lock().unwrap(); - let result = zeta.defrag.received_fragment::(raw_fragment, app.time(), |n, frag_no, frag_count| { - let (p, c) = from_nonce(n); - if p != PACKET_TYPE_DATA { - log!(app, ReceivedRawFragment(p, c, frag_no, frag_count)); - } - if p == PACKET_TYPE_HANDSHAKE_RESPONSE { - 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)); - } - if c >= COUNTER_WINDOW_MAX_SKIP_AHEAD { - return Err(byzantine_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)); - } - Ok(()) - } else if p == PACKET_TYPE_HANDSHAKE_COMPLETION { - // The handshake completion packet could have been resent. - return Err(byzantine_fault!(InvalidPacket, false)); - } else { - return Err(byzantine_fault!(InvalidPacket, true)); - } - })?; + let result = + zeta.defrag + .received_fragment::(raw_fragment, app.time(), |n, frag_no, frag_count| { + let (p, c) = from_nonce(n); + if p != PACKET_TYPE_DATA { + log!(app, ReceivedRawFragment(p, c, frag_no, frag_count)); + } + if p == PACKET_TYPE_HANDSHAKE_RESPONSE { + 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)); + } + if c >= COUNTER_WINDOW_MAX_SKIP_AHEAD { + return Err(byzantine_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)); + } + Ok(()) + } else if p == PACKET_TYPE_HANDSHAKE_COMPLETION { + // The handshake completion packet could have been resent. + return Err(byzantine_fault!(InvalidPacket, false)); + } else { + return Err(byzantine_fault!(InvalidPacket, true)); + } + })?; if let Some((pn, mut assembled_packet)) = result { // Process recv zeta layer. - let send_associated = |Packet(kid, nonce, payload): &Packet, hk: Option<&[u8; AES_256_KEY_SIZE]>| { - if let Some((send_fragment, mut mtu)) = send_to(&session) { - mtu = mtu.max(MIN_TRANSPORT_MTU); - send_with_fragmentation::(send_fragment, mtu, *kid, to_packet_nonce(&nonce), payload, hk); - } - }; + let send_associated = + |Packet(kid, nonce, payload): &Packet, hk: Option<&[u8; AES_256_KEY_SIZE]>| { + if let Some((send_fragment, mut mtu)) = send_to(&session) { + mtu = mtu.max(MIN_TRANSPORT_MTU); + send_with_fragmentation::( + send_fragment, + mtu, + *kid, + to_packet_nonce(&nonce), + payload, + hk, + ); + } + }; let (p, _) = from_nonce(&pn); let ret = match p { @@ -210,8 +220,15 @@ impl Context { } PACKET_TYPE_KEY_CONFIRM => { log!(app, ReceivedRawKeyConfirm); - let result = - received_c1_trans(&mut zeta, &app, &ctx.rng, kid_recv, to_aes_nonce(&pn), assembled_packet, send_associated)?; + let result = received_c1_trans( + &mut zeta, + &app, + &ctx.rng, + kid_recv, + to_aes_nonce(&pn), + assembled_packet, + send_associated, + )?; log!(app, KeyConfirmIsAuthSentAck(&session)); if result { SessionEvent::Established @@ -221,7 +238,14 @@ impl Context { } PACKET_TYPE_ACK => { log!(app, ReceivedRawAck); - received_c2_trans(&mut zeta, &app, &ctx.rng, kid_recv, to_aes_nonce(&pn), assembled_packet)?; + received_c2_trans( + &mut zeta, + &app, + &ctx.rng, + kid_recv, + to_aes_nonce(&pn), + assembled_packet, + )?; log!(app, AckIsAuth(&session)); SessionEvent::Control } @@ -244,7 +268,14 @@ impl Context { } PACKET_TYPE_REKEY_COMPLETE => { log!(app, ReceivedRawK2); - received_k2_trans(&mut zeta, &app, kid_recv, to_aes_nonce(&pn), assembled_packet, send_associated)?; + received_k2_trans( + &mut zeta, + &app, + kid_recv, + to_aes_nonce(&pn), + assembled_packet, + send_associated, + )?; log!(app, K2IsAuthSentKeyConfirm(&session)); SessionEvent::Control } @@ -266,28 +297,37 @@ impl Context { if let Entry::Occupied(mut entry) = b2_map.entry(kid_recv) { let zeta = entry.get_mut(); // Process recv fragmentation layer. - let result = zeta.defrag.received_fragment::(raw_fragment, app.time(), |n, frag_no, frag_count| { - let (p, c) = from_nonce(n); - log!(app, ReceivedRawFragment(p, c, frag_no, frag_count)); - if p == PACKET_TYPE_HANDSHAKE_COMPLETION && c == 0 { - Ok(()) - } else { - Err(byzantine_fault!(InvalidPacket, true)) - } - })?; + let result = + zeta.defrag + .received_fragment::(raw_fragment, app.time(), |n, frag_no, frag_count| { + let (p, c) = from_nonce(n); + log!(app, ReceivedRawFragment(p, c, frag_no, frag_count)); + if p == PACKET_TYPE_HANDSHAKE_COMPLETION && c == 0 { + Ok(()) + } else { + Err(byzantine_fault!(InvalidPacket, true)) + } + })?; if let Some((_, assembled_packet)) = result { log!(app, ReceivedRawX3); let zeta = entry.remove(); - let session = received_x3_trans(zeta, &app, ctx, kid_recv, assembled_packet, |Packet(kid, nonce, payload), hk| { - send_with_fragmentation::( - send_unassociated_reply, - send_unassociated_mtu, - *kid, - to_packet_nonce(&nonce), - payload, - hk, - ); - })?; + let session = received_x3_trans( + zeta, + &app, + ctx, + kid_recv, + assembled_packet, + |Packet(kid, nonce, payload), hk| { + send_with_fragmentation::( + send_unassociated_reply, + send_unassociated_mtu, + *kid, + to_packet_nonce(&nonce), + payload, + hk, + ); + }, + )?; log!(app, X3IsAuthSentKeyConfirm(&session)); Ok(ReceiveOk::Session(session, SessionEvent::NewSession)) } else { @@ -301,11 +341,10 @@ impl Context { } } else { // Process recv fragmentation layer. - let result = ctx - .hello_defrag - .lock() - .unwrap() - .received_fragment::(raw_fragment, app.time(), |n, frag_no, frag_count| { + let result = ctx.hello_defrag.lock().unwrap().received_fragment::( + raw_fragment, + app.time(), + |n, frag_no, frag_count| { let (p, c) = from_nonce(n); log!(app, ReceivedRawFragment(p, c, frag_no, frag_count)); if p == PACKET_TYPE_HANDSHAKE_HELLO || p == PACKET_TYPE_CHALLENGE { @@ -313,18 +352,18 @@ impl Context { } else { Err(byzantine_fault!(InvalidPacket, true)) } - })?; + }, + )?; if let Some((n, mut assembled_packet)) = result { let (p, _) = from_nonce(&n); if p == PACKET_TYPE_HANDSHAKE_HELLO { log!(app, ReceivedRawX1); // Process recv challenge layer. let challenge_start = assembled_packet.len() - CHALLENGE_SIZE; - let result = ctx - .challenge - .lock() - .unwrap() - .process_hello::(remote_address, (&assembled_packet[challenge_start..]).try_into().unwrap()); + let result = ctx.challenge.lock().unwrap().process_hello::( + remote_address, + (&assembled_packet[challenge_start..]).try_into().unwrap(), + ); if let Err(challenge) = result { log!(app, X1FailedChallengeSentNewChallenge); let mut challenge_packet = Vec::new(); @@ -347,16 +386,22 @@ impl Context { assembled_packet.truncate(challenge_start); // Process recv zeta layer. - received_x1_trans(&app, &ctx, to_aes_nonce(&n), assembled_packet, |Packet(kid, nonce, payload), hk| { - send_with_fragmentation::( - send_unassociated_reply, - send_unassociated_mtu, - *kid, - to_packet_nonce(&nonce), - payload, - Some(hk), - ); - })?; + received_x1_trans( + &app, + &ctx, + to_aes_nonce(&n), + assembled_packet, + |Packet(kid, nonce, payload), hk| { + send_with_fragmentation::( + send_unassociated_reply, + send_unassociated_mtu, + *kid, + to_packet_nonce(&nonce), + payload, + Some(hk), + ); + }, + )?; log!(app, X1IsAuthSentX2); Ok(ReceiveOk::Unassociated) } else if p == PACKET_TYPE_CHALLENGE { @@ -365,10 +410,17 @@ impl Context { if assembled_packet.len() != KID_SIZE + CHALLENGE_SIZE { return Err(byzantine_fault!(InvalidPacket, true)); } - if let Some(kid_recv) = NonZeroU32::new(u32::from_be_bytes(assembled_packet[..KID_SIZE].try_into().unwrap())) { - if let Some(Some(session)) = ctx.session_map.lock().unwrap().get(&kid_recv).map(|r| r.upgrade()) { + if let Some(kid_recv) = + NonZeroU32::new(u32::from_be_bytes(assembled_packet[..KID_SIZE].try_into().unwrap())) + { + if let Some(Some(session)) = ctx.session_map.lock().unwrap().get(&kid_recv).map(|r| r.upgrade()) + { let mut zeta = session.0.lock().unwrap(); - respond_to_challenge(&mut zeta, &ctx.rng, &assembled_packet[KID_SIZE..].try_into().unwrap()); + respond_to_challenge( + &mut zeta, + &ctx.rng, + &assembled_packet[KID_SIZE..].try_into().unwrap(), + ); log!(app, ChallengeIsAuth(&session)); return Ok(ReceiveOk::Unassociated); } @@ -390,7 +442,13 @@ impl Context { /// slice of `data` /// * `mtu` - The MTU of the link, all packets passed to `send` will be at most `mtu` in length /// * `payload` - Data to send - pub fn send(&self, session: &Arc>, send: impl FnMut(Vec) -> bool, mut mtu: usize, payload: Vec) -> Result<(), SendError> { + pub fn send( + &self, + session: &Arc>, + send: impl FnMut(Vec) -> bool, + mut mtu: usize, + payload: Vec, + ) -> Result<(), SendError> { mtu = mtu.max(MIN_TRANSPORT_MTU); let mut zeta = session.0.lock().unwrap(); send_payload(&mut zeta, payload, |Packet(kid, nonce, payload), hk| { @@ -405,7 +463,11 @@ impl Context { /// a problem. /// /// * `send_to` - Function to get a sender and an MTU to send something over an active session - pub fn service) -> bool>(&self, app: App, mut send_to: impl FnMut(&Arc>) -> Option<(SendFn, usize)>) -> i64 { + pub fn service) -> bool>( + &self, + app: App, + mut send_to: impl FnMut(&Arc>) -> Option<(SendFn, usize)>, + ) -> i64 { let ctx = &self.0; let sessions = ctx.sessions.lock().unwrap(); let current_time = app.time(); @@ -422,7 +484,14 @@ impl Context { |Packet(kid, nonce, payload): &Packet, hk| { if let Some((send_fragment, mut mtu)) = send_to(&session) { mtu = mtu.max(MIN_TRANSPORT_MTU); - send_with_fragmentation::(send_fragment, mtu, *kid, to_packet_nonce(&nonce), payload, hk); + send_with_fragmentation::( + send_fragment, + mtu, + *kid, + to_packet_nonce(&nonce), + payload, + hk, + ); } }, ); diff --git a/src/crypto/aes.rs b/src/crypto/aes.rs index d5f1d56..9a16d87 100644 --- a/src/crypto/aes.rs +++ b/src/crypto/aes.rs @@ -27,7 +27,12 @@ pub trait PrpAes256 { pub trait AeadAesGcm { /// Encrypt the given `buffer` of plaintext using AES-GCM-256, with the given `key`, `iv` and `aad`. /// The ciphertext should be written directly back to `buffer`, and the GCM tag should be returned. - fn encrypt_in_place(key: &[u8; AES_256_KEY_SIZE], iv: [u8; AES_GCM_IV_SIZE], aad: Option<&[u8]>, buffer: &mut [u8]) -> [u8; AES_GCM_TAG_SIZE]; + fn encrypt_in_place( + key: &[u8; AES_256_KEY_SIZE], + iv: [u8; AES_GCM_IV_SIZE], + aad: Option<&[u8]>, + buffer: &mut [u8], + ) -> [u8; AES_GCM_TAG_SIZE]; /// Decrypt the given `buffer` of ciphertext using AES-GCM-256, with the given `key`, `iv` and `aad`. /// The ciphertext should be written directly back to `buffer`, and the GCM tag should be returned. fn decrypt_in_place( diff --git a/src/crypto/kyber1024.rs b/src/crypto/kyber1024.rs index 185ab6b..9c5b5c5 100644 --- a/src/crypto/kyber1024.rs +++ b/src/crypto/kyber1024.rs @@ -24,7 +24,10 @@ pub trait PrivateKeyKyber1024: Sized + Send + Sync { /// /// **CRITICAL**: This must return `None` if the given `public_key` is invalid in any way /// according to the Kyber1024 spec. - fn encapsulate(rng: &mut Rng, public_key: &[u8; KYBER_PUBLIC_KEY_SIZE]) -> Option<([u8; KYBER_CIPHERTEXT_SIZE], [u8; KYBER_PLAINTEXT_SIZE])>; + fn encapsulate( + rng: &mut Rng, + public_key: &[u8; KYBER_PUBLIC_KEY_SIZE], + ) -> Option<([u8; KYBER_CIPHERTEXT_SIZE], [u8; KYBER_PLAINTEXT_SIZE])>; /// Decapsulate a Kyber1024 `ciphertext` received from the remote peer, retreiving /// the raw bytes of the original plaintext. This plaintext is immediately hashed and deleted. /// diff --git a/src/crypto_impl/aes_impl.rs b/src/crypto_impl/aes_impl.rs index 9a2edab..c19b26d 100644 --- a/src/crypto_impl/aes_impl.rs +++ b/src/crypto_impl/aes_impl.rs @@ -18,7 +18,12 @@ impl PrpAes256 for Aes256 { } impl AeadAesGcm for Aes256Gcm { - fn encrypt_in_place(key: &[u8; AES_256_KEY_SIZE], iv: [u8; AES_GCM_IV_SIZE], aad: Option<&[u8]>, buffer: &mut [u8]) -> [u8; AES_GCM_TAG_SIZE] { + fn encrypt_in_place( + key: &[u8; AES_256_KEY_SIZE], + iv: [u8; AES_GCM_IV_SIZE], + aad: Option<&[u8]>, + buffer: &mut [u8], + ) -> [u8; AES_GCM_TAG_SIZE] { let key = Key::::from_slice(key); let mut cipher = Aes256Gcm::new(&key); cipher diff --git a/src/crypto_impl/kyber1024.rs b/src/crypto_impl/kyber1024.rs index 4c5ed15..9f56dc3 100644 --- a/src/crypto_impl/kyber1024.rs +++ b/src/crypto_impl/kyber1024.rs @@ -13,7 +13,10 @@ impl PrivateKeyKyber1024 for PqcKyberSecretKey { (Zeroizing::new(keypair.secret), keypair.public) } - fn encapsulate(rng: &mut Rng, public_key: &[u8; KYBER_PUBLIC_KEY_SIZE]) -> Option<([u8; KYBER_CIPHERTEXT_SIZE], [u8; KYBER_PLAINTEXT_SIZE])> { + fn encapsulate( + rng: &mut Rng, + public_key: &[u8; KYBER_PUBLIC_KEY_SIZE], + ) -> Option<([u8; KYBER_CIPHERTEXT_SIZE], [u8; KYBER_PLAINTEXT_SIZE])> { pqc_kyber::encapsulate(public_key, rng).ok() } diff --git a/src/crypto_impl/p384_impl.rs b/src/crypto_impl/p384_impl.rs index ddf6e68..d20705a 100644 --- a/src/crypto_impl/p384_impl.rs +++ b/src/crypto_impl/p384_impl.rs @@ -26,6 +26,12 @@ impl KeyPairP384 for EphemeralSecret { } fn agree(&self, public_key: &Self::PublicKey) -> Option<[u8; P384_ECDH_SHARED_SECRET_SIZE]> { - Some(self.diffie_hellman(public_key).raw_secret_bytes().as_slice().try_into().unwrap()) + Some( + self.diffie_hellman(public_key) + .raw_secret_bytes() + .as_slice() + .try_into() + .unwrap(), + ) } } diff --git a/src/fragmentation.rs b/src/fragmentation.rs index a0ceda1..902f836 100644 --- a/src/fragmentation.rs +++ b/src/fragmentation.rs @@ -10,7 +10,12 @@ use crate::proto::*; use crate::result::{byzantine_fault, ReceiveError}; /// Corresponds to Figure 13 found in Section 6. -fn create_fragment_header(kid_send: u32, fragment_count: usize, fragment_no: usize, n: &[u8; PACKET_NONCE_SIZE]) -> [u8; HEADER_SIZE] { +fn create_fragment_header( + kid_send: u32, + fragment_count: usize, + fragment_no: usize, + n: &[u8; PACKET_NONCE_SIZE], +) -> [u8; HEADER_SIZE] { debug_assert!(fragment_count > 0); debug_assert!(fragment_count <= MAX_FRAGMENTS); debug_assert!(fragment_no < MAX_FRAGMENTS); @@ -47,7 +52,10 @@ pub fn send_with_fragmentation( fragment.extend(&packet[i..j]); if let Some(hk_send) = hk_send { - App::Prp::encrypt_in_place(hk_send, (&mut fragment[HEADER_AUTH_START..HEADER_AUTH_END]).try_into().unwrap()); + App::Prp::encrypt_in_place( + hk_send, + (&mut fragment[HEADER_AUTH_START..HEADER_AUTH_END]).try_into().unwrap(), + ); } if !send(fragment) { return false; @@ -87,7 +95,12 @@ impl DefragBuffer { } if let Some(hk_recv) = self.hk_recv.as_ref() { - App::Prp::decrypt_in_place(hk_recv, (&mut raw_fragment[HEADER_AUTH_START..HEADER_AUTH_END]).try_into().unwrap()) + App::Prp::decrypt_in_place( + hk_recv, + (&mut raw_fragment[HEADER_AUTH_START..HEADER_AUTH_END]) + .try_into() + .unwrap(), + ) } let fragment_no = raw_fragment[FRAGMENT_NO_IDX] as usize; diff --git a/src/proto.rs b/src/proto.rs index d0ee289..68594bb 100644 --- a/src/proto.rs +++ b/src/proto.rs @@ -1,4 +1,6 @@ -use crate::crypto::{AES_GCM_TAG_SIZE, KYBER_CIPHERTEXT_SIZE, KYBER_PUBLIC_KEY_SIZE, P384_PUBLIC_KEY_SIZE, SHA512_HASH_SIZE}; +use crate::crypto::{ + AES_GCM_TAG_SIZE, KYBER_CIPHERTEXT_SIZE, KYBER_PUBLIC_KEY_SIZE, P384_PUBLIC_KEY_SIZE, SHA512_HASH_SIZE, +}; /* Common constants */ @@ -71,7 +73,8 @@ pub(crate) const HASHLEN: usize = SHA512_HASH_SIZE; pub const RATCHET_SIZE: usize = 32; /// Initial value of 'h'. -pub(crate) const PROTOCOL_NAME_NOISE_XK: &[u8; HASHLEN] = b"Noise_XKhfs+psk2_P384+Kyber1024_AESGCM_SHA512\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +pub(crate) const PROTOCOL_NAME_NOISE_XK: &[u8; HASHLEN] = + b"Noise_XKhfs+psk2_P384+Kyber1024_AESGCM_SHA512\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; /// Initial value of 'ck' for rekeying. pub(crate) const PROTOCOL_NAME_NOISE_KK: &[u8; HASHLEN] = b"Noise_KKpsk0_P384_AESGCM_SHA512\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; @@ -112,10 +115,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_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; -pub(crate) const HANDSHAKE_RESPONSE_SIZE: usize = P384_PUBLIC_KEY_SIZE + KYBER_CIPHERTEXT_SIZE + AES_GCM_TAG_SIZE + KID_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; pub(crate) const HANDSHAKE_COMPLETION_MIN_SIZE: usize = P384_PUBLIC_KEY_SIZE + AES_GCM_TAG_SIZE + 0 + AES_GCM_TAG_SIZE; pub(crate) const HANDSHAKE_COMPLETION_MAX_SIZE: usize = HANDSHAKE_COMPLETION_MIN_SIZE + IDENTITY_MAX_SIZE; diff --git a/src/symmetric_state.rs b/src/symmetric_state.rs index 702198a..00b77cc 100644 --- a/src/symmetric_state.rs +++ b/src/symmetric_state.rs @@ -84,7 +84,14 @@ impl SymmetricState { let mut next_ck = [0u8; HASHLEN]; let mut temp_k = [0u8; HASHLEN]; - self.kbkdf(input_key_material, LABEL_KBKDF_CHAIN, 2, &mut next_ck, Some(&mut temp_k), None); + self.kbkdf( + input_key_material, + LABEL_KBKDF_CHAIN, + 2, + &mut next_ck, + Some(&mut temp_k), + None, + ); *self.ck = next_ck; self.k.clone_from_slice(&temp_k[..AES_256_KEY_SIZE]); @@ -116,14 +123,24 @@ impl SymmetricState { self.k.clone_from_slice(&temp_k[..AES_256_KEY_SIZE]); } /// Corresponds to Noise `EncryptAndHash`. - pub fn encrypt_and_hash_in_place(&mut self, iv: [u8; AES_GCM_IV_SIZE], plaintext_start: usize, buffer: &mut Vec) { + pub fn encrypt_and_hash_in_place( + &mut self, + iv: [u8; AES_GCM_IV_SIZE], + plaintext_start: usize, + buffer: &mut Vec, + ) { let tag = App::Aead::encrypt_in_place(&self.k, iv, Some(&self.h), &mut buffer[plaintext_start..]); buffer.extend(&tag); self.mix_hash(&buffer[plaintext_start..]); } /// Corresponds to Noise `DecryptAndHash`. #[must_use] - pub fn decrypt_and_hash_in_place(&mut self, iv: [u8; AES_GCM_IV_SIZE], buffer: &mut [u8], tag: [u8; AES_GCM_TAG_SIZE]) -> bool { + pub fn decrypt_and_hash_in_place( + &mut self, + iv: [u8; AES_GCM_IV_SIZE], + buffer: &mut [u8], + tag: [u8; AES_GCM_TAG_SIZE], + ) -> bool { let mut hash = App::Hash::new(); hash.update(&self.h); hash.update(buffer); diff --git a/src/zeta.rs b/src/zeta.rs index 7113133..e51f2f1 100644 --- a/src/zeta.rs +++ b/src/zeta.rs @@ -276,7 +276,15 @@ pub(crate) fn trans_to_a1( let mut session_map = ctx.session_map.lock().unwrap(); let kid_recv = gen_kid(session_map.deref(), ctx.rng.lock().unwrap().deref_mut()); - let a1 = create_a1_state(&ctx.rng, &s_remote, kid_recv, &ratchet_state1, ratchet_state2.as_ref(), identity).ok_or(OpenError::InvalidPublicKey)?; + let a1 = create_a1_state( + &ctx.rng, + &s_remote, + kid_recv, + &ratchet_state1, + ratchet_state2.as_ref(), + identity, + ) + .ok_or(OpenError::InvalidPublicKey)?; let packet = a1.packet.clone(); let (hk_recv, hk_send) = a1.noise.get_ask(LABEL_HEADER_KEY); @@ -304,14 +312,21 @@ pub(crate) fn trans_to_a1( let session = Arc::new(Session(Mutex::new(zeta))); session_map.insert(kid_recv, Arc::downgrade(&session)); - ctx.sessions.lock().unwrap().insert(Arc::as_ptr(&session), Arc::downgrade(&session)); + ctx.sessions + .lock() + .unwrap() + .insert(Arc::as_ptr(&session), Arc::downgrade(&session)); send(&packet); Ok(session) } /// Corresponds to Algorithm 13 found in Section 5. -pub(crate) fn respond_to_challenge(zeta: &mut Zeta, rng: &Mutex, challenge: &[u8; CHALLENGE_SIZE]) { +pub(crate) fn respond_to_challenge( + zeta: &mut Zeta, + rng: &Mutex, + challenge: &[u8; CHALLENGE_SIZE], +) { if let ZetaAutomata::A1(StateA1 { packet: Packet(_, _, x1), .. }) = &mut zeta.beta { let response_start = x1.len() - CHALLENGE_SIZE; respond_to_challenge_in_place::( @@ -345,13 +360,16 @@ pub(crate) fn received_x1_trans( // 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(byzantine_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))?; // Process message pattern 1 es token. - noise.mix_dh(&ctx.s_secret, &e_remote).ok_or(byzantine_fault!(FailedAuth, true))?; + noise + .mix_dh(&ctx.s_secret, &e_remote) + .ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 1 e1 token. let j = i + KYBER_PUBLIC_KEY_SIZE; let k = j + AES_GCM_TAG_SIZE; @@ -396,12 +414,17 @@ pub(crate) fn received_x1_trans( // Process message pattern 2 e token. let e_secret = noise.write_e(&ctx.rng, &mut x2); // Process message pattern 2 ee token. - noise.mix_dh(&e_secret, &e_remote).ok_or(byzantine_fault!(FailedAuth, true))?; + noise + .mix_dh(&e_secret, &e_remote) + .ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 2 ekem1 token. let i = x2.len(); - let (ekem1, ekem1_secret) = App::Kem::encapsulate(ctx.rng.lock().unwrap().deref_mut(), (&x1[e1_start..e1_end]).try_into().unwrap()) - .map(|(ct, secret)| (ct, Zeroizing::new(secret))) - .ok_or(byzantine_fault!(FailedAuth, true))?; + let (ekem1, ekem1_secret) = App::Kem::encapsulate( + ctx.rng.lock().unwrap().deref_mut(), + (&x1[e1_start..e1_end]).try_into().unwrap(), + ) + .map(|(ct, secret)| (ct, Zeroizing::new(secret))) + .ok_or(byzantine_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()); @@ -436,7 +459,10 @@ pub(crate) fn received_x1_trans( }, ); - send(&Packet(kid_send.get(), to_nonce(PACKET_TYPE_HANDSHAKE_RESPONSE, c), x2), &hk_send); + send( + &Packet(kid_send.get(), to_nonce(PACKET_TYPE_HANDSHAKE_RESPONSE, c), x2), + &hk_send, + ); Ok(()) } /// Corresponds to Transition Algorithm 3 found in Section 4.3. @@ -470,7 +496,9 @@ pub(crate) fn received_x2_trans( // Process message pattern 2 e token. let e_remote = noise.read_e(&mut i, &x2).ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 2 ee token. - noise.mix_dh(e_secret, &e_remote).ok_or(byzantine_fault!(FailedAuth, true))?; + noise + .mix_dh(e_secret, &e_remote) + .ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 2 ekem1 token. let j = i + KYBER_CIPHERTEXT_SIZE; let k = j + AES_GCM_TAG_SIZE; @@ -536,7 +564,9 @@ pub(crate) fn received_x2_trans( x3.extend(&ctx.s_secret.public_key_bytes()); noise.encrypt_and_hash_in_place(to_nonce(PACKET_TYPE_HANDSHAKE_COMPLETION, 1), i, &mut x3); // Process message pattern 3 se token. - noise.mix_dh(&ctx.s_secret, &e_remote).ok_or(byzantine_fault!(FailedAuth, true))?; + noise + .mix_dh(&ctx.s_secret, &e_remote) + .ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 3 payload. let i = x3.len(); x3.extend(identity); @@ -623,10 +653,13 @@ pub(crate) fn received_x3_trans( 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)); } - let s_remote = App::PublicKey::from_bytes((&x3[i..j]).try_into().unwrap()).ok_or(byzantine_fault!(FailedAuth, true))?; + let s_remote = + App::PublicKey::from_bytes((&x3[i..j]).try_into().unwrap()).ok_or(byzantine_fault!(FailedAuth, true))?; i = k; // Process message pattern 3 se token. - noise.mix_dh(&zeta.e_secret, &s_remote).ok_or(byzantine_fault!(FailedAuth, true))?; + noise + .mix_dh(&zeta.e_secret, &s_remote) + .ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 3 payload. let k = x3.len(); let j = k - AES_GCM_TAG_SIZE; @@ -726,7 +759,10 @@ pub(crate) fn received_x3_trans( defrag: zeta.defrag, }))); entry.insert(Arc::downgrade(&session)); - ctx.sessions.lock().unwrap().insert(Arc::as_ptr(&session), Arc::downgrade(&session)); + ctx.sessions + .lock() + .unwrap() + .insert(Arc::as_ptr(&session), Arc::downgrade(&session)); send(&Packet(zeta.kid_send.get(), n, c1), Some(&zeta.hk_send)); Ok(session) @@ -765,7 +801,12 @@ pub(crate) fn received_c1_trans( return Err(byzantine_fault!(OutOfSequence, false)); }; - let specified_key = zeta.key_ref(is_other).recv.kek.as_ref().ok_or(byzantine_fault!(OutOfSequence, true))?; + let specified_key = zeta + .key_ref(is_other) + .recv + .kek + .as_ref() + .ok_or(byzantine_fault!(OutOfSequence, true))?; let tag = c1[..].try_into().unwrap(); if !App::Aead::decrypt_in_place(specified_key, n, None, &mut [], tag) { return Err(byzantine_fault!(FailedAuth, true)); @@ -800,7 +841,8 @@ pub(crate) fn received_c1_trans( zeta.timeout_timer = app.time() + App::SETTINGS .rekey_after_time - .saturating_sub(rng.lock().unwrap().next_u64() % App::SETTINGS.rekey_time_max_jitter) as i64; + .saturating_sub(rng.lock().unwrap().next_u64() % App::SETTINGS.rekey_time_max_jitter) + as i64; zeta.resend_timer = i64::MAX; zeta.beta = ZetaAutomata::S2; } @@ -810,11 +852,19 @@ pub(crate) fn received_c1_trans( let c = zeta.send_counter; zeta.send_counter += 1; let n = to_nonce(PACKET_TYPE_ACK, c); - let latest_confirmed_key = zeta.key_ref(false).send.kek.as_ref().ok_or(byzantine_fault!(OutOfSequence, true))?; + let latest_confirmed_key = zeta + .key_ref(false) + .send + .kek + .as_ref() + .ok_or(byzantine_fault!(OutOfSequence, true))?; let tag = App::Aead::encrypt_in_place(latest_confirmed_key, n, None, &mut []); c2.extend(&tag); - send(&Packet(zeta.key_ref(false).send.kid.unwrap().get(), n, c2), Some(&zeta.hk_send)); + send( + &Packet(zeta.key_ref(false).send.kid.unwrap().get(), n, c2), + Some(&zeta.hk_send), + ); Ok(just_establised) } /// Corresponds to the trivial Transition Algorithm described for processing C_2 packets found in @@ -929,7 +979,12 @@ pub(crate) fn service( let c = zeta.send_counter; zeta.send_counter += 1; let n = to_nonce(p, c); - let tag = App::Aead::encrypt_in_place(zeta.key_ref(false).send.kek.as_ref().unwrap(), n, None, &mut control_payload); + let tag = App::Aead::encrypt_in_place( + zeta.key_ref(false).send.kek.as_ref().unwrap(), + n, + None, + &mut control_payload, + ); control_payload.extend(&tag); send( &Packet(zeta.key_ref(false).send.kid.unwrap().get(), n, control_payload), @@ -937,7 +992,12 @@ pub(crate) fn service( ); } } -fn remap(session: &Arc>, zeta: &Zeta, rng: &Mutex, session_map: &SessionMap) -> NonZeroU32 { +fn remap( + session: &Arc>, + zeta: &Zeta, + rng: &Mutex, + session_map: &SessionMap, +) -> NonZeroU32 { let mut session_map = session_map.lock().unwrap(); let weak = if let Some(Some(weak)) = zeta.key_ref(true).recv.kid.as_ref().map(|kid| session_map.remove(kid)) { weak @@ -1034,7 +1094,10 @@ fn timeout_trans( let tag = App::Aead::encrypt_in_place(zeta.key_ref(false).send.kek.as_ref().unwrap(), n, None, &mut k1); k1.extend(&tag); - send(&Packet(zeta.key_ref(false).send.kid.unwrap().get(), n, k1), Some(&zeta.hk_send)); + send( + &Packet(zeta.key_ref(false).send.kid.unwrap().get(), n, k1), + Some(&zeta.hk_send), + ); } ZetaAutomata::S1 { .. } => { log!(app, TimeoutKeyConfirm(session)); @@ -1088,7 +1151,13 @@ pub(crate) fn received_k1_trans( let i = k1.len() - AES_GCM_TAG_SIZE; let tag = k1[i..].try_into().unwrap(); - if !App::Aead::decrypt_in_place(zeta.key_ref(false).recv.kek.as_ref().unwrap(), n, None, &mut k1[..i], tag) { + if !App::Aead::decrypt_in_place( + zeta.key_ref(false).recv.kek.as_ref().unwrap(), + n, + None, + &mut k1[..i], + tag, + ) { return Err(byzantine_fault!(FailedAuth, true)); } let (_, c) = from_nonce(&n); @@ -1108,9 +1177,13 @@ pub(crate) fn received_k1_trans( // Process message pattern 1 e token. let e_remote = noise.read_e(&mut i, &k1).ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 1 es token. - noise.mix_dh(s_secret, &e_remote).ok_or(byzantine_fault!(FailedAuth, true))?; + noise + .mix_dh(s_secret, &e_remote) + .ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 1 ss token. - noise.mix_dh(s_secret, &zeta.s_remote).ok_or(byzantine_fault!(FailedAuth, true))?; + noise + .mix_dh(s_secret, &zeta.s_remote) + .ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 1 payload. let j = i + KID_SIZE; let k = j + AES_GCM_TAG_SIZE; @@ -1118,15 +1191,20 @@ pub(crate) fn received_k1_trans( 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)); } - 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(byzantine_fault!(FailedAuth, true))?; let mut k2 = Vec::new(); // Process message pattern 2 e token. let e_secret = noise.write_e(rng, &mut k2); // Process message pattern 2 ee token. - noise.mix_dh(&e_secret, &e_remote).ok_or(byzantine_fault!(FailedAuth, true))?; + noise + .mix_dh(&e_secret, &e_remote) + .ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 2 se token. - noise.mix_dh(&s_secret, &e_remote).ok_or(byzantine_fault!(FailedAuth, true))?; + noise + .mix_dh(&s_secret, &e_remote) + .ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 2 payload. let i = k2.len(); let new_kid_recv = remap(session, &zeta, rng, session_map); @@ -1172,7 +1250,10 @@ pub(crate) fn received_k1_trans( let tag = App::Aead::encrypt_in_place(zeta.key_ref(false).send.kek.as_ref().unwrap(), n, None, &mut k2); k2.extend(&tag); - send(&Packet(zeta.key_ref(false).send.kid.unwrap().get(), n, k2), Some(&zeta.hk_send)); + send( + &Packet(zeta.key_ref(false).send.kid.unwrap().get(), n, k2), + Some(&zeta.hk_send), + ); Ok(()) })(); if matches!(result, Err(ReceiveError::ByzantineFault { .. })) { @@ -1205,7 +1286,13 @@ pub(crate) fn received_k2_trans( let i = k2.len() - AES_GCM_TAG_SIZE; let tag = k2[i..].try_into().unwrap(); - if !App::Aead::decrypt_in_place(zeta.key_ref(false).recv.kek.as_ref().unwrap(), n, None, &mut k2[..i], tag) { + if !App::Aead::decrypt_in_place( + zeta.key_ref(false).recv.kek.as_ref().unwrap(), + n, + None, + &mut k2[..i], + tag, + ) { return Err(byzantine_fault!(FailedAuth, true)); } let (_, c) = from_nonce(&n); @@ -1220,9 +1307,13 @@ pub(crate) fn received_k2_trans( // Process message pattern 2 e token. let e_remote = noise.read_e(&mut i, &k2).ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 2 ee token. - noise.mix_dh(e_secret, &e_remote).ok_or(byzantine_fault!(FailedAuth, true))?; + noise + .mix_dh(e_secret, &e_remote) + .ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 2 se token. - noise.mix_dh(e_secret, &zeta.s_remote).ok_or(byzantine_fault!(FailedAuth, true))?; + noise + .mix_dh(e_secret, &zeta.s_remote) + .ok_or(byzantine_fault!(FailedAuth, true))?; // Process message pattern 2 payload. let j = i + KID_SIZE; let k = j + AES_GCM_TAG_SIZE; @@ -1230,7 +1321,8 @@ pub(crate) fn received_k2_trans( 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)); } - 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(byzantine_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); @@ -1271,7 +1363,10 @@ pub(crate) fn received_k2_trans( let tag = App::Aead::encrypt_in_place(zeta.key_ref(false).send.kek.as_ref().unwrap(), n, None, &mut []); c1.extend(&tag); - send(&Packet(zeta.key_ref(false).send.kid.unwrap().get(), n, c1), Some(&zeta.hk_send)); + send( + &Packet(zeta.key_ref(false).send.kid.unwrap().get(), n, c1), + Some(&zeta.hk_send), + ); Ok(()) } else { unreachable!() @@ -1314,7 +1409,10 @@ pub(crate) fn send_payload( let tag = App::Aead::encrypt_in_place(zeta.key_ref(false).send.nk.as_ref().unwrap(), n, None, &mut payload); payload.extend(&tag); - send(&Packet(zeta.key_ref(false).send.kid.unwrap().get(), n, payload), Some(&zeta.hk_send)); + send( + &Packet(zeta.key_ref(false).send.kid.unwrap().get(), n, payload), + Some(&zeta.hk_send), + ); Ok(()) } /// Corresponds to Algorithm 10 found in Section 4.3. @@ -1340,7 +1438,12 @@ pub(crate) fn received_payload_in_place( }; let i = payload.len() - AES_GCM_TAG_SIZE; - let specified_key = zeta.key_ref(is_other).recv.nk.as_ref().ok_or(byzantine_fault!(OutOfSequence, true))?; + let specified_key = zeta + .key_ref(is_other) + .recv + .nk + .as_ref() + .ok_or(byzantine_fault!(OutOfSequence, true))?; let tag = payload[i..].try_into().unwrap(); if !App::Aead::decrypt_in_place(specified_key, n, None, &mut payload[..i], tag) { return Err(byzantine_fault!(FailedAuth, true));