From f3903144e4e6fc93bbfbc93ca4b4d68651655c5e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 17 Aug 2022 08:51:06 -0400 Subject: [PATCH] Move Noise to the crypto package, and tests. --- rustfmt.toml | 3 +- zerotier-core-crypto/Cargo.toml | 2 + zerotier-core-crypto/src/aes.rs | 67 +++-- zerotier-core-crypto/src/lib.rs | 3 + .../vl1 => zerotier-core-crypto/src}/noise.rs | 253 +++++++++++++++--- zerotier-network-hypervisor/Cargo.toml | 1 - zerotier-network-hypervisor/src/vl1/mod.rs | 1 - 7 files changed, 262 insertions(+), 68 deletions(-) rename {zerotier-network-hypervisor/src/vl1 => zerotier-core-crypto/src}/noise.rs (81%) diff --git a/rustfmt.toml b/rustfmt.toml index 27a1c5432..68e3c5287 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,6 +1,7 @@ #unstable_features = true max_width = 256 -use_small_heuristics = "Max" +#use_small_heuristics = "Max" +use_small_heuristics = "Default" tab_spaces = 4 newline_style = "Unix" edition = "2021" diff --git a/zerotier-core-crypto/Cargo.toml b/zerotier-core-crypto/Cargo.toml index a2902ecf8..c4817566e 100644 --- a/zerotier-core-crypto/Cargo.toml +++ b/zerotier-core-crypto/Cargo.toml @@ -15,6 +15,8 @@ openssl = { version = "^0", features = [], default-features = false } lazy_static = "^1" foreign-types = "0.3.1" poly1305 = { version = "0.7.2", features = [], default-features = false } +parking_lot = { version = "^0", features = [], default-features = false } +pqc_kyber = { path = "../third_party/kyber", features = ["kyber512", "reference"], default-features = false } [target."cfg(not(any(target_os = \"macos\", target_os = \"ios\")))".dependencies] openssl = "^0" diff --git a/zerotier-core-crypto/src/aes.rs b/zerotier-core-crypto/src/aes.rs index 572c74ee7..641b064b6 100644 --- a/zerotier-core-crypto/src/aes.rs +++ b/zerotier-core-crypto/src/aes.rs @@ -22,26 +22,15 @@ mod fruit_flavored { const kCCOptionECBMode: i32 = 2; extern "C" { - fn CCCryptorCreateWithMode( - op: i32, - mode: i32, - alg: i32, - padding: i32, - iv: *const c_void, - key: *const c_void, - key_len: usize, - tweak: *const c_void, - tweak_len: usize, - num_rounds: c_int, - options: i32, - cryyptor_ref: *mut *mut c_void, - ) -> i32; + fn CCCryptorCreateWithMode(op: i32, mode: i32, alg: i32, padding: i32, iv: *const c_void, key: *const c_void, key_len: usize, tweak: *const c_void, tweak_len: usize, num_rounds: c_int, options: i32, cryyptor_ref: *mut *mut c_void) -> i32; fn CCCryptorUpdate(cryptor_ref: *mut c_void, data_in: *const c_void, data_in_len: usize, data_out: *mut c_void, data_out_len: usize, data_out_written: *mut usize) -> i32; //fn CCCryptorReset(cryptor_ref: *mut c_void, iv: *const c_void) -> i32; fn CCCryptorRelease(cryptor_ref: *mut c_void) -> i32; fn CCCryptorGCMSetIV(cryptor_ref: *mut c_void, iv: *const c_void, iv_len: usize) -> i32; fn CCCryptorGCMAddAAD(cryptor_ref: *mut c_void, aad: *const c_void, len: usize) -> i32; - fn CCCryptorGCMFinalize(cryptor_ref: *mut c_void, tag: *mut c_void, tag_len: usize) -> i32; + fn CCCryptorGCMEncrypt(cryptor_ref: *mut c_void, data_in: *const c_void, data_in_len: usize, data_out: *mut c_void) -> i32; + fn CCCryptorGCMDecrypt(cryptor_ref: *mut c_void, data_in: *const c_void, data_in_len: usize, data_out: *mut c_void) -> i32; + fn CCCryptorGCMFinal(cryptor_ref: *mut c_void, tag: *mut c_void, tag_len: *mut usize) -> i32; fn CCCryptorGCMReset(cryptor_ref: *mut c_void) -> i32; } @@ -112,7 +101,7 @@ mod fruit_flavored { unsafe impl Send for Aes {} unsafe impl Sync for Aes {} - pub struct AesGcm(*mut c_void); + pub struct AesGcm(*mut c_void, bool); impl Drop for AesGcm { #[inline(always)] @@ -130,11 +119,15 @@ mod fruit_flavored { let mut ptr: *mut c_void = null_mut(); assert_eq!( CCCryptorCreateWithMode( - if encrypt { kCCEncrypt } else { kCCDecrypt }, + if encrypt { + kCCEncrypt + } else { + kCCDecrypt + }, kCCModeGCM, kCCAlgorithmAES, 0, - [0_u64; 2].as_ptr().cast(), + null(), k.as_ptr().cast(), k.len(), null(), @@ -145,7 +138,7 @@ mod fruit_flavored { ), 0 ); - AesGcm(ptr) + AesGcm(ptr, encrypt) } } @@ -168,7 +161,7 @@ mod fruit_flavored { #[inline(always)] pub fn aad(&mut self, aad: &[u8]) { unsafe { - CCCryptorGCMAddAAD(self.0, aad.as_ptr().cast(), aad.len()); + assert_eq!(CCCryptorGCMAddAAD(self.0, aad.as_ptr().cast(), aad.len()), 0); } } @@ -176,26 +169,32 @@ mod fruit_flavored { pub fn crypt(&mut self, input: &[u8], output: &mut [u8]) { unsafe { assert_eq!(input.len(), output.len()); - let mut data_out_written: usize = 0; - CCCryptorUpdate(self.0, input.as_ptr().cast(), input.len(), output.as_mut_ptr().cast(), output.len(), &mut data_out_written); - assert_eq!(data_out_written, input.len()); + if self.1 { + assert_eq!(CCCryptorGCMEncrypt(self.0, input.as_ptr().cast(), input.len(), output.as_mut_ptr().cast()), 0); + } else { + assert_eq!(CCCryptorGCMDecrypt(self.0, input.as_ptr().cast(), input.len(), output.as_mut_ptr().cast()), 0); + } } } #[inline(always)] pub fn crypt_in_place(&mut self, data: &mut [u8]) { unsafe { - let mut data_out_written: usize = 0; - CCCryptorUpdate(self.0, data.as_ptr().cast(), data.len(), data.as_mut_ptr().cast(), data.len(), &mut data_out_written); - assert_eq!(data_out_written, data.len()); + if self.1 { + assert_eq!(CCCryptorGCMEncrypt(self.0, data.as_ptr().cast(), data.len(), data.as_mut_ptr().cast()), 0); + } else { + assert_eq!(CCCryptorGCMDecrypt(self.0, data.as_ptr().cast(), data.len(), data.as_mut_ptr().cast()), 0); + } } } #[inline(always)] pub fn finish(&mut self) -> [u8; 16] { - let mut tag = [0_u8; 16]; + let mut tag = 0_u128.to_ne_bytes(); unsafe { - if CCCryptorGCMFinalize(self.0, tag.as_mut_ptr().cast(), 16) != 0 { + let mut tag_len = 16; + if CCCryptorGCMFinal(self.0, tag.as_mut_ptr().cast(), &mut tag_len) != 0 { + debug_assert!(false); tag.fill(0); } } @@ -327,7 +326,17 @@ mod openssl { #[inline(always)] pub fn init(&mut self, iv: &[u8]) { assert_eq!(iv.len(), 12); - let mut c = Crypter::new(aes_gcm_by_key_size(self.1), if self.3 { Mode::Encrypt } else { Mode::Decrypt }, &self.0 .0[..self.1], Some(iv)).unwrap(); + let mut c = Crypter::new( + aes_gcm_by_key_size(self.1), + if self.3 { + Mode::Encrypt + } else { + Mode::Decrypt + }, + &self.0 .0[..self.1], + Some(iv), + ) + .unwrap(); c.pad(false); let _ = self.2.replace(c); } diff --git a/zerotier-core-crypto/src/lib.rs b/zerotier-core-crypto/src/lib.rs index 0aa6ee58b..19000753d 100644 --- a/zerotier-core-crypto/src/lib.rs +++ b/zerotier-core-crypto/src/lib.rs @@ -5,6 +5,7 @@ pub mod aes_gmac_siv; pub mod hash; pub mod hex; pub mod kbkdf; +pub mod noise; pub mod p384; pub mod poly1305; pub mod random; @@ -13,4 +14,6 @@ pub mod secret; pub mod varint; pub mod x25519; +pub use pqc_kyber; + pub const ZEROES: [u8; 16] = [0_u8; 16]; diff --git a/zerotier-network-hypervisor/src/vl1/noise.rs b/zerotier-core-crypto/src/noise.rs similarity index 81% rename from zerotier-network-hypervisor/src/vl1/noise.rs rename to zerotier-core-crypto/src/noise.rs index ddfb344d1..9b10bed64 100644 --- a/zerotier-network-hypervisor/src/vl1/noise.rs +++ b/zerotier-core-crypto/src/noise.rs @@ -3,11 +3,11 @@ use std::num::NonZeroU64; use std::sync::atomic::{AtomicU64, Ordering}; -use zerotier_core_crypto::aes::{Aes, AesGcm}; -use zerotier_core_crypto::hash::{hmac_sha384, hmac_sha512, SHA384, SHA512}; -use zerotier_core_crypto::p384::{P384KeyPair, P384PublicKey, P384_PUBLIC_KEY_SIZE}; -use zerotier_core_crypto::random; -use zerotier_core_crypto::secret::Secret; +use crate::aes::{Aes, AesGcm}; +use crate::hash::{hmac_sha384, hmac_sha512, SHA384, SHA512}; +use crate::p384::{P384KeyPair, P384PublicKey, P384_PUBLIC_KEY_SIZE}; +use crate::random; +use crate::secret::Secret; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; @@ -163,7 +163,6 @@ impl Obfuscator { } } -#[allow(unused)] pub enum ReceiveResult<'a, O> { /// Packet is valid and contained a data payload. OkData(&'a [u8]), @@ -242,7 +241,7 @@ pub struct Session { remote_s_public_hash: [u8; 48], psk: Secret<64>, ss: Secret<48>, - outgoing_obfuscator: Obfuscator, + pub(self) outgoing_obfuscator: Obfuscator, state: RwLock, remote_s_public_p384: [u8; P384_PUBLIC_KEY_SIZE], @@ -262,13 +261,23 @@ impl Session { /// /// This must be checked often enough to ensure that the hard key usage limit is not reached, which in the /// usual UDP use case means once every ~3TiB of traffic. - #[allow(unused)] pub fn rekey_check<'a, const MAX_PACKET_SIZE: usize, const STATIC_PUBLIC_SIZE: usize>(&self, buffer: &'a mut [u8; MAX_PACKET_SIZE], local_s_public: &[u8; STATIC_PUBLIC_SIZE], current_time: i64, force: bool, jedi: bool) -> Option<&'a [u8]> { let state = self.state.upgradable_read(); if let Some(key) = state.keys[0].as_ref() { if force || (key.lifetime.should_rekey(self.send_counter.current(), current_time) && state.offer.as_ref().map_or(true, |o| (current_time - o.creation_time) > OFFER_RATE_LIMIT_MS)) { if let Some(remote_s_public_p384) = P384PublicKey::from_bytes(&self.remote_s_public_p384) { - if let Some((offer, psize)) = EphemeralOffer::create_alice_offer(buffer, self.send_counter.next(), self.id, state.remote_session_id, local_s_public, &remote_s_public_p384, &self.ss, &self.outgoing_obfuscator, current_time, jedi) { + if let Some((offer, psize)) = EphemeralOffer::create_alice_offer( + buffer, + self.send_counter.next(), + self.id, + state.remote_session_id, + local_s_public, + &remote_s_public_p384, + &self.ss, + &self.outgoing_obfuscator, + current_time, + jedi, + ) { let mut state = RwLockUpgradableReadGuard::upgrade(state); let _ = state.offer.replace(offer); return Some(&buffer[..psize]); @@ -281,7 +290,6 @@ impl Session { } /// Create a new session and return this plus an outgoing packet to send to the other end. -#[allow(unused)] pub fn new_session<'a, O, const MAX_PACKET_SIZE: usize, const STATIC_PUBLIC_SIZE: usize>( buffer: &'a mut [u8; MAX_PACKET_SIZE], local_session_id: SessionId, @@ -325,7 +333,6 @@ pub fn new_session<'a, O, const MAX_PACKET_SIZE: usize, const STATIC_PUBLIC_SIZE /// Receive a packet from the network and take the appropriate action. /// /// Check ReceiveResult to see if it includes data or a reply packet. -#[allow(unused)] pub fn receive< 'a, ExtractP384PublicKeyFunction: FnOnce(&[u8; STATIC_PUBLIC_SIZE]) -> Option, @@ -419,19 +426,24 @@ pub fn receive< return Err(Error::InvalidPacket); } let payload_end = incoming_packet.len() - (AES_GCM_TAG_SIZE + HMAC_SIZE); + let aes_gcm_tag_end = incoming_packet.len() - HMAC_SIZE; match packet_type { PACKET_TYPE_KEY_OFFER => { // alice (remote) -> bob (local) - let (alice_e0_public, e0s) = P384PublicKey::from_bytes(&buffer[HEADER_SIZE..HEADER_SIZE + P384_PUBLIC_KEY_SIZE]).and_then(|pk| local_s_keypair_p384.agree(&pk).map(move |s| (pk, s))).ok_or(Error::FailedAuthentication)?; + let (alice_e0_public, e0s) = P384PublicKey::from_bytes(&buffer[HEADER_SIZE..HEADER_SIZE + P384_PUBLIC_KEY_SIZE]) + .and_then(|pk| local_s_keypair_p384.agree(&pk).map(move |s| (pk, s))) + .ok_or(Error::FailedAuthentication)?; let key = Secret(hmac_sha512(&hmac_sha512(&KEY_DERIVATION_CHAIN_STARTING_SALT, alice_e0_public.as_bytes()), e0s.as_bytes())); + let original_ciphertext = buffer.clone(); let mut c = AesGcm::new(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB).first_n::<32>(), false); c.init(&get_aes_gcm_nonce(buffer)); c.crypt_in_place(&mut buffer[(HEADER_SIZE + P384_PUBLIC_KEY_SIZE)..payload_end]); - if !c.finish().eq(&buffer[payload_end..(payload_end + AES_GCM_TAG_SIZE)]) { + let c = c.finish(); + if !c.eq(&buffer[payload_end..aes_gcm_tag_end]) { return Err(Error::FailedAuthentication); } @@ -449,7 +461,7 @@ pub fn receive< let key = Secret(hmac_sha512(key.as_bytes(), ss.as_bytes())); - if !hmac_sha384(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), &buffer[..(payload_end + AES_GCM_TAG_SIZE)]).eq(&buffer[(payload_end + AES_GCM_TAG_SIZE)..(payload_end + AES_GCM_TAG_SIZE + HMAC_SIZE)]) { + if !hmac_sha384(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), &original_ciphertext[..aes_gcm_tag_end]).eq(&buffer[aes_gcm_tag_end..incoming_packet.len()]) { return Err(Error::FailedAuthentication); } @@ -459,7 +471,7 @@ pub fn receive< let e0e0 = bob_e0_keypair.agree(&alice_e0_public).ok_or(Error::FailedAuthentication)?; let se0 = bob_e0_keypair.agree(&alice_s_public_p384).ok_or(Error::FailedAuthentication)?; - let new_session = if let Some(session) = session.as_ref() { + let new_session = if session.is_some() { None } else { if let Some((local_session_id, psk, associated_object)) = new_session_auth(&alice_s_public) { @@ -489,7 +501,10 @@ pub fn receive< // NIST/FIPS allows HKDF with HMAC(salt, key) and salt is allowed to be anything. This way if the PSK is not // FIPS compliant the compliance of the entire key derivation is not invalidated. Both inputs are secrets of // fixed size so this shouldn't matter cryptographically. - let key = Secret(hmac_sha512(session.psk.as_bytes(), &hmac_sha512(&hmac_sha512(&hmac_sha512(key.as_bytes(), bob_e0_keypair.public_key_bytes()), e0e0.as_bytes()), se0.as_bytes()))); + let key = Secret(hmac_sha512( + session.psk.as_bytes(), + &hmac_sha512(&hmac_sha512(&hmac_sha512(key.as_bytes(), bob_e0_keypair.public_key_bytes()), e0e0.as_bytes()), se0.as_bytes()), + )); // At this point we've completed Noise_IK key derivation with NIST P-384 ECDH, but see final step below... @@ -509,7 +524,8 @@ pub fn receive< let mut c = AesGcm::new(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE).first_n::<32>(), true); c.init(&get_aes_gcm_nonce(buffer)); c.crypt_in_place(&mut buffer[(HEADER_SIZE + P384_PUBLIC_KEY_SIZE)..reply_size]); - buffer[reply_size..(reply_size + AES_GCM_TAG_SIZE)].copy_from_slice(&c.finish()); + let c = c.finish(); + buffer[reply_size..(reply_size + AES_GCM_TAG_SIZE)].copy_from_slice(&c); reply_size += AES_GCM_TAG_SIZE; // Normal Noise_IK is done, but we have one more step: mix in the Kyber shared secret (or all zeroes if Kyber is @@ -543,19 +559,26 @@ pub fn receive< if let Some(session) = session { let state = session.state.upgradable_read(); if let Some(offer) = state.offer.as_ref() { - let (bob_e0_public, e0e0) = P384PublicKey::from_bytes(&buffer[HEADER_SIZE..(HEADER_SIZE + P384_PUBLIC_KEY_SIZE)]).and_then(|pk| offer.alice_e0_keypair.agree(&pk).map(move |s| (pk, s))).ok_or(Error::FailedAuthentication)?; + let (bob_e0_public, e0e0) = P384PublicKey::from_bytes(&buffer[HEADER_SIZE..(HEADER_SIZE + P384_PUBLIC_KEY_SIZE)]) + .and_then(|pk| offer.alice_e0_keypair.agree(&pk).map(move |s| (pk, s))) + .ok_or(Error::FailedAuthentication)?; let se0 = local_s_keypair_p384.agree(&bob_e0_public).ok_or(Error::FailedAuthentication)?; - let key = Secret(hmac_sha512(session.psk.as_bytes(), &hmac_sha512(&hmac_sha512(&hmac_sha512(offer.key.as_bytes(), bob_e0_public.as_bytes()), e0e0.as_bytes()), se0.as_bytes()))); + let key = Secret(hmac_sha512( + session.psk.as_bytes(), + &hmac_sha512(&hmac_sha512(&hmac_sha512(offer.key.as_bytes(), bob_e0_public.as_bytes()), e0e0.as_bytes()), se0.as_bytes()), + )); + let original_ciphertext = buffer.clone(); let mut c = AesGcm::new(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE).first_n::<32>(), false); c.init(&get_aes_gcm_nonce(buffer)); c.crypt_in_place(&mut buffer[(HEADER_SIZE + P384_PUBLIC_KEY_SIZE)..payload_end]); - if !c.finish().eq(&buffer[payload_end..(payload_end + AES_GCM_TAG_SIZE)]) { + let c = c.finish(); + if !c.eq(&buffer[payload_end..aes_gcm_tag_end]) { return Err(Error::FailedAuthentication); } - // Alice has now completed Noise_IK for P-384, now for the hybrid part. + // Alice has now completed Noise_IK for P-384 and verified with GCM auth, now for the hybrid add-on. let (bob_session_id, bob_e1_public) = parse_KEY_COUNTER_OFFER_after_header(&buffer[(HEADER_SIZE + P384_PUBLIC_KEY_SIZE)..payload_end])?; @@ -571,7 +594,7 @@ pub fn receive< let key = Secret(hmac_sha512(e1e1.as_bytes(), key.as_bytes())); - if !hmac_sha384(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), &buffer[..(payload_end + AES_GCM_TAG_SIZE)]).eq(&buffer[(payload_end + AES_GCM_TAG_SIZE)..(payload_end + AES_GCM_TAG_SIZE + HMAC_SIZE)]) { + if !hmac_sha384(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), &original_ciphertext[..aes_gcm_tag_end]).eq(&buffer[aes_gcm_tag_end..incoming_packet.len()]) { return Err(Error::FailedAuthentication); } @@ -669,12 +692,11 @@ impl KeyLifetime { #[inline(always)] fn expired(&self, counter: CounterValue) -> bool { - counter.0 < self.hard_expire_at_counter + counter.0 >= self.hard_expire_at_counter } } /// Ephemeral offer sent with KEY_OFFER and rememebered so state can be reconstructed on COUNTER_OFFER. -#[allow(unused)] struct EphemeralOffer { creation_time: i64, key: Secret<64>, @@ -709,10 +731,12 @@ impl EphemeralOffer { let mut packet_size = assemble_KEY_OFFER(buffer, counter, bob_session_id, alice_e0_keypair.public_key(), alice_session_id, alice_s_public, alice_e1_keypair.as_ref().map(|s| &s.public)); + debug_assert!(packet_size <= MAX_PACKET_SIZE); let mut c = AesGcm::new(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB).first_n::<32>(), true); c.init(&get_aes_gcm_nonce(buffer)); - c.crypt_in_place(&mut buffer[HEADER_SIZE + P384_PUBLIC_KEY_SIZE..packet_size]); - buffer[packet_size..packet_size + AES_GCM_TAG_SIZE].copy_from_slice(&c.finish()); + c.crypt_in_place(&mut buffer[(HEADER_SIZE + P384_PUBLIC_KEY_SIZE)..packet_size]); + let c = c.finish(); + buffer[packet_size..packet_size + AES_GCM_TAG_SIZE].copy_from_slice(&c); packet_size += AES_GCM_TAG_SIZE; let key = Secret(hmac_sha512(key.as_bytes(), ss.as_bytes())); @@ -733,7 +757,7 @@ impl EphemeralOffer { alice_e0_keypair, alice_e1_keypair, }, - packet_size + AES_GCM_TAG_SIZE + HMAC_SIZE, + packet_size, )) } } @@ -823,6 +847,16 @@ fn assemble_and_armor_DATA(buffer: &mut [u8; MAX_P Ok(tag_end) } +fn append_random_padding(b: &mut [u8]) -> &mut [u8] { + if b.len() > AES_GCM_TAG_SIZE + HMAC_SIZE { + let random_padding_len = (random::next_u32_secure() as usize) % (b.len() - (AES_GCM_TAG_SIZE + HMAC_SIZE)); + b[..random_padding_len].fill(0); + &mut b[random_padding_len..] + } else { + b + } +} + #[allow(non_snake_case)] fn assemble_KEY_OFFER( buffer: &mut [u8; MAX_PACKET_SIZE], @@ -860,11 +894,9 @@ fn assemble_KEY_OFFER( b[1] = 0; // reserved for future use b = &mut b[2..]; - let random_padding_len = (random::next_u32_secure() as usize) % (MAX_PACKET_SIZE - (b.len() + AES_GCM_TAG_SIZE + HMAC_SIZE)); - random::fill_bytes_secure(&mut b[..random_padding_len]); - b = &mut b[random_padding_len..]; + b = append_random_padding(b); - b.len() + MAX_PACKET_SIZE - b.len() } #[allow(non_snake_case)] @@ -972,10 +1002,161 @@ fn kbkdf512(key: &[u8], label: u8) -> Secret<64> { #[inline(always)] fn get_aes_gcm_nonce(deobfuscated_packet: &[u8]) -> [u8; 16] { let mut tmp = 0_u128.to_ne_bytes(); - tmp[..HEADER_SIZE].copy_from_slice(deobfuscated_packet); + tmp[..HEADER_SIZE].copy_from_slice(&deobfuscated_packet[..HEADER_SIZE]); tmp } #[cold] #[inline(never)] extern "C" fn unlikely_branch() {} + +#[cfg(test)] +mod tests { + use std::rc::Rc; + + #[allow(unused_imports)] + use super::*; + + #[test] + fn alice_bob() { + let psk: Secret<64> = Secret::default(); + let mut a_buffer = [0_u8; 1500]; + let mut b_buffer = [0_u8; 1500]; + let alice_static_keypair = P384KeyPair::generate(); + let bob_static_keypair = P384KeyPair::generate(); + let outgoing_obfuscator_to_alice = Obfuscator::new(alice_static_keypair.public_key_bytes()); + let outgoing_obfuscator_to_bob = Obfuscator::new(bob_static_keypair.public_key_bytes()); + + let mut from_alice: Vec> = Vec::new(); + let mut from_bob: Vec> = Vec::new(); + + // Session TO Bob, on Alice's side. + let (alice, packet) = new_session( + &mut a_buffer, + SessionId::new_random(), + alice_static_keypair.public_key_bytes(), + &alice_static_keypair, + bob_static_keypair.public_key_bytes(), + bob_static_keypair.public_key(), + &psk, + 0, + 0, + true, + ) + .unwrap(); + let alice = Rc::new(alice); + from_alice.push(packet.to_vec()); + + // Session FROM Alice, on Bob's side. + let mut bob: Option>> = None; + + while !from_alice.is_empty() || !from_bob.is_empty() { + if let Some(packet) = from_alice.pop() { + let r = receive( + packet.as_slice(), + &mut b_buffer, + &bob_static_keypair, + &outgoing_obfuscator_to_bob, + |p: &[u8; P384_PUBLIC_KEY_SIZE]| P384PublicKey::from_bytes(p), + |sid| { + if let Some(bob) = bob.as_ref() { + if sid == bob.id { + Some(bob.clone()) + } else { + None + } + } else { + None + } + }, + |_: &[u8; P384_PUBLIC_KEY_SIZE]| { + if bob.is_none() { + Some((SessionId::new_random(), psk.clone(), 0)) + } else { + panic!("Bob received a second new session request from Alice"); + } + }, + 0, + true, + ); + if let Ok(r) = r { + match r { + ReceiveResult::OkData(_) => { + println!("alice->bob DATA"); + } + ReceiveResult::OkSendReply(p) => { + from_bob.push(p.to_vec()); + } + ReceiveResult::OkNewSession(ns, p) => { + if bob.is_some() { + panic!("attempt to create new session on Bob's side when he already has one"); + } + let _ = bob.replace(Rc::new(ns)); + from_bob.push(p.to_vec()); + println!("alice->bob NEW SESSION established!"); + } + ReceiveResult::Ok => { + println!("alice->bob OK"); + } + ReceiveResult::Duplicate => { + println!("alice->bob duplicate packet"); + } + ReceiveResult::Ignored => { + println!("alice->bob ignored packet"); + } + } + } else { + println!("ERROR (alice->bob): {}", r.err().unwrap().to_string()); + panic!(); + } + } + + if let Some(packet) = from_bob.pop() { + let r = receive( + packet.as_slice(), + &mut b_buffer, + &alice_static_keypair, + &outgoing_obfuscator_to_alice, + |p: &[u8; P384_PUBLIC_KEY_SIZE]| P384PublicKey::from_bytes(p), + |sid| { + if sid == alice.id { + Some(alice.clone()) + } else { + panic!("received from Bob addressed to unknown session ID, not Alice"); + } + }, + |_: &[u8; P384_PUBLIC_KEY_SIZE]| { + panic!("Alice received an unexpected new session request from Bob"); + }, + 0, + true, + ); + if let Ok(r) = r { + match r { + ReceiveResult::OkData(_) => { + println!("bob->alice DATA"); + } + ReceiveResult::OkSendReply(p) => { + from_alice.push(p.to_vec()); + } + ReceiveResult::OkNewSession(_, _) => { + panic!("attempt to create new session on Alice's side; Bob should not initiate"); + } + ReceiveResult::Ok => { + println!("bob->alice OK"); + } + ReceiveResult::Duplicate => { + println!("bob->alice duplicate packet"); + } + ReceiveResult::Ignored => { + println!("bob->alice ignored packet"); + } + } + } else { + println!("ERROR (bob->alice): {}", r.err().unwrap().to_string()); + panic!(); + } + } + } + } +} diff --git a/zerotier-network-hypervisor/Cargo.toml b/zerotier-network-hypervisor/Cargo.toml index 6a5e6e201..51e1642c8 100644 --- a/zerotier-network-hypervisor/Cargo.toml +++ b/zerotier-network-hypervisor/Cargo.toml @@ -11,7 +11,6 @@ debug_events = [] [dependencies] zerotier-core-crypto = { path = "../zerotier-core-crypto" } -pqc_kyber = { path = "../third_party/kyber", features = ["kyber512", "reference"], default-features = false } async-trait = "^0" base64 = "^0" lz4_flex = { version = "^0", features = ["safe-encode", "safe-decode", "checked-decode"] } diff --git a/zerotier-network-hypervisor/src/vl1/mod.rs b/zerotier-network-hypervisor/src/vl1/mod.rs index c357baa55..f17a10e5c 100644 --- a/zerotier-network-hypervisor/src/vl1/mod.rs +++ b/zerotier-network-hypervisor/src/vl1/mod.rs @@ -8,7 +8,6 @@ mod fragmentedpacket; mod identity; mod inetaddress; mod mac; -mod noise; mod path; mod peer; mod rootset;