diff --git a/nym-outfox/Cargo.toml b/nym-outfox/Cargo.toml index dcff212623..455acc326e 100644 --- a/nym-outfox/Cargo.toml +++ b/nym-outfox/Cargo.toml @@ -14,15 +14,14 @@ rayon = { workspace = true } blake3 = { workspace = true } zeroize = { workspace = true } chacha20 = { workspace = true, features = ["std"] } -x25519-dalek = { version = "2.0.1", features = ["static_secrets", "getrandom", "zeroize"] } chacha20poly1305 = { workspace = true } getrandom = { workspace = true, features = ["js"] } thiserror = { workspace = true } -rand = { workspace = true } log = { workspace = true } +rand = "0.9.1" bs58 = "0.5.1" -# libcrux-kem = "0.0.3" +libcrux-kem = "0.0.3" [dev-dependencies] criterion = { workspace = true } diff --git a/nym-outfox/src/constants.rs b/nym-outfox/src/constants.rs index 05b68b1608..f23ef96ba2 100644 --- a/nym-outfox/src/constants.rs +++ b/nym-outfox/src/constants.rs @@ -1,3 +1,5 @@ +use libcrux_kem::Algorithm; + pub const GROUPELEMENTBYTES: u8 = 32; pub const TAGBYTES: u8 = 16; pub const MIX_PARAMS_LEN: usize = DEFAULT_HOPS + 2; @@ -11,12 +13,17 @@ pub const ROUTING_INFORMATION_LENGTH_BY_STAGE: [u8; DEFAULT_HOPS] = pub const MIN_PACKET_SIZE: usize = 48; pub const MAGIC_SLICE: &[u8] = &[111, 102, 120]; -pub const OUTFOX_PACKET_OVERHEAD: usize = MIX_PARAMS_LEN - + (groupelementbytes() + tagbytes() + DEFAULT_ROUTING_INFO_SIZE as usize) * DEFAULT_HOPS - + MAGIC_SLICE.len(); +// pub const OUTFOX_PACKET_OVERHEAD: usize = MIX_PARAMS_LEN +// + (groupelementbytes() + tagbytes() + DEFAULT_ROUTING_INFO_SIZE as usize) * DEFAULT_HOPS +// + MAGIC_SLICE.len(); -pub const fn groupelementbytes() -> usize { - GROUPELEMENTBYTES as usize +pub const fn groupelementbytes(kem: Algorithm) -> usize { + match kem { + Algorithm::XWingKemDraft06 => 1120, + Algorithm::X25519 => 32, + Algorithm::MlKem768 => 1088, + _ => unreachable!(), + } } pub const fn tagbytes() -> usize { diff --git a/nym-outfox/src/format.rs b/nym-outfox/src/format.rs index 2a6f542565..b912d3de02 100644 --- a/nym-outfox/src/format.rs +++ b/nym-outfox/src/format.rs @@ -58,23 +58,30 @@ use crate::constants::groupelementbytes; use crate::constants::tagbytes; use crate::constants::DEFAULT_HOPS; use crate::constants::DEFAULT_ROUTING_INFO_SIZE; -use crate::constants::GROUPELEMENTBYTES; use crate::constants::MIX_PARAMS_LEN; use crate::constants::ROUTING_INFORMATION_LENGTH_BY_STAGE; use crate::constants::TAGBYTES; use crate::error::OutfoxError; use crate::lion::*; -use crate::route::PrivateKey; + +use rand::CryptoRng; + use chacha20poly1305::AeadInPlace; use chacha20poly1305::ChaCha20Poly1305; use chacha20poly1305::KeyInit; use chacha20poly1305::Tag; +use libcrux_kem::Algorithm; +use libcrux_kem::Ct; +use libcrux_kem::PrivateKey; +use libcrux_kem::PublicKey; +use libcrux_kem::Ss; use std::ops::Range; /// A structure that holds mix packet construction parameters. These incluse the length /// of the routing information at each hop, the number of hops, and the payload length. -#[derive(Eq, PartialEq, Debug)] +#[derive(PartialEq, Debug)] pub struct MixCreationParameters { + pub kem: Algorithm, /// The routing length is inner first, so \[0\] is the innermost routing length, etc (in bytes) /// In our stratified topology this will always be 4 pub routing_information_length_by_stage: [u8; DEFAULT_HOPS], @@ -82,15 +89,16 @@ pub struct MixCreationParameters { pub payload_length_bytes: u16, } -impl TryFrom<&[u8]> for MixCreationParameters { +impl TryFrom<(Algorithm, &[u8])> for MixCreationParameters { type Error = OutfoxError; - fn try_from(v: &[u8]) -> Result { - if v.len() != MIX_PARAMS_LEN { - return Err(OutfoxError::InvalidHeaderLength(v.len())); + fn try_from(v: (Algorithm, &[u8])) -> Result { + if v.1.len() != MIX_PARAMS_LEN { + return Err(OutfoxError::InvalidHeaderLength(v.1.len())); } - let (routing, payload) = v.split_at(DEFAULT_HOPS); + let (routing, payload) = v.1.split_at(DEFAULT_HOPS); Ok(MixCreationParameters { + kem: v.0, routing_information_length_by_stage: routing.try_into()?, payload_length_bytes: u16::from_le_bytes(payload.try_into()?), }) @@ -110,8 +118,9 @@ impl MixCreationParameters { } /// Create a set of parameters for a mix packet format. - pub fn new(payload_length_bytes: u16) -> MixCreationParameters { + pub fn new(kem: Algorithm, payload_length_bytes: u16) -> MixCreationParameters { MixCreationParameters { + kem: kem, routing_information_length_by_stage: [DEFAULT_ROUTING_INFO_SIZE; DEFAULT_HOPS], payload_length_bytes, } @@ -121,7 +130,7 @@ impl MixCreationParameters { pub fn total_packet_length(&self) -> usize { let mut len = self.payload_length_bytes(); for stage_len in ROUTING_INFORMATION_LENGTH_BY_STAGE.iter() { - len += *stage_len as usize + groupelementbytes() + tagbytes() + len += *stage_len as usize + groupelementbytes(self.kem) + tagbytes() } len } @@ -134,6 +143,7 @@ impl MixCreationParameters { for (i, stage_len) in ROUTING_INFORMATION_LENGTH_BY_STAGE.iter().enumerate() { if i == layer_number { let params = MixStageParameters { + kem: self.kem, routing_information_length_bytes: *stage_len, remaining_header_length_bytes, payload_length_bytes: self.payload_length_bytes, @@ -144,7 +154,8 @@ impl MixCreationParameters { return (total_size - inner_size..total_size, params); } else { - remaining_header_length_bytes += (stage_len + GROUPELEMENTBYTES + TAGBYTES) as u16; + remaining_header_length_bytes += + groupelementbytes(self.kem) as u16 + (stage_len + TAGBYTES) as u16; } } @@ -154,6 +165,7 @@ impl MixCreationParameters { /// A structure representing the parameters of a single stage of mixing. pub struct MixStageParameters { + pub kem: Algorithm, /// The routing information length for this stage of mixing pub routing_information_length_bytes: u8, /// The reamining header length for this stage of mixing @@ -176,7 +188,7 @@ impl MixStageParameters { } pub fn incoming_packet_length(&self) -> usize { - groupelementbytes() + tagbytes() + self.outgoing_packet_length() + groupelementbytes(self.kem) + tagbytes() + self.outgoing_packet_length() } pub fn outgoing_packet_length(&self) -> usize { @@ -185,22 +197,22 @@ impl MixStageParameters { + self.payload_length_bytes() } - pub fn pub_element_range(&self) -> Range { - 0..groupelementbytes() + pub fn encaps_element_range(&self) -> Range { + 0..groupelementbytes(self.kem) } pub fn tag_range(&self) -> Range { - groupelementbytes()..groupelementbytes() + tagbytes() + groupelementbytes(self.kem)..groupelementbytes(self.kem) + tagbytes() } pub fn routing_data_range(&self) -> Range { - groupelementbytes() + tagbytes() - ..groupelementbytes() + tagbytes() + self.routing_information_length_bytes() + groupelementbytes(self.kem) + tagbytes() + ..groupelementbytes(self.kem) + tagbytes() + self.routing_information_length_bytes() } pub fn header_range(&self) -> Range { - groupelementbytes() + tagbytes() - ..groupelementbytes() + groupelementbytes(self.kem) + tagbytes() + ..groupelementbytes(self.kem) + tagbytes() + self.routing_information_length_bytes() + self.remaining_header_length_bytes() @@ -210,13 +222,16 @@ impl MixStageParameters { self.incoming_packet_length() - self.payload_length_bytes()..self.incoming_packet_length() } - pub fn encode_mix_layer( + pub fn encode_mix_layer( &self, + rng: &mut R, buffer: &mut [u8], - user_secret_key: &PrivateKey, - mix_public_key: x25519_dalek::PublicKey, + mix_encapsulation_key: &PublicKey, destination: &[u8; 32], - ) -> Result { + ) -> Result + where + R: CryptoRng, + { let routing_data = destination; if buffer.len() != self.incoming_packet_length() { @@ -233,14 +248,15 @@ impl MixStageParameters { }); } - let user_public_key = x25519_dalek::PublicKey::from(user_secret_key); - let shared_key = user_secret_key.diffie_hellman(&mix_public_key); + let (ss, ct) = mix_encapsulation_key.encapsulate(rng).unwrap(); + + let shared_key = ss.encode(); // Copy rounting data into buffer buffer[self.routing_data_range()].copy_from_slice(routing_data); // Perform the AEAD - let header_aead_key = ChaCha20Poly1305::new_from_slice(shared_key.as_bytes())?; + let header_aead_key = ChaCha20Poly1305::new_from_slice(&shared_key)?; let nonce = [0u8; 12]; let tag = header_aead_key @@ -251,18 +267,18 @@ impl MixStageParameters { buffer[self.tag_range()].copy_from_slice(&tag[..]); // Copy own public key into buffer - buffer[self.pub_element_range()].copy_from_slice(user_public_key.as_bytes()); + buffer[self.encaps_element_range()].copy_from_slice(&ct.encode()); // Do a round of LION on the payload - lion_transform_encrypt(&mut buffer[self.payload_range()], shared_key.as_bytes())?; + lion_transform_encrypt(&mut buffer[self.payload_range()], &shared_key)?; - Ok(shared_key) + Ok(ss) } pub fn decode_mix_layer( &self, buffer: &mut [u8], - mix_secret_key: &PrivateKey, + mix_decapsulation_key: &PrivateKey, ) -> Result, OutfoxError> { // Check the length of the incoming buffer is correct. if buffer.len() != self.incoming_packet_length() { @@ -272,13 +288,12 @@ impl MixStageParameters { }); } - // Derive the shared key for this packet - let user_public_key_bytes: [u8; 32] = buffer[self.pub_element_range()].try_into()?; - let user_public_key = x25519_dalek::PublicKey::from(user_public_key_bytes); - let shared_key = mix_secret_key.diffie_hellman(&user_public_key); + let ct = Ct::decode(self.kem, &buffer[self.encaps_element_range()]).unwrap(); + let shared_key = ct.decapsulate(&mix_decapsulation_key).unwrap(); + let shared_key = shared_key.encode(); // Compute the AEAD and check the Tag, if wrong return Err - let header_aead_key = ChaCha20Poly1305::new_from_slice(shared_key.as_bytes())?; + let header_aead_key = ChaCha20Poly1305::new_from_slice(&shared_key)?; let nonce = [0; 12]; let tag_bytes = buffer[self.tag_range()].to_vec(); @@ -295,7 +310,7 @@ impl MixStageParameters { let routing_data = buffer[self.routing_data_range()].to_vec(); // Do a round of LION on the payload - lion_transform_decrypt(&mut buffer[self.payload_range()], shared_key.as_bytes())?; + lion_transform_decrypt(&mut buffer[self.payload_range()], &shared_key)?; Ok(routing_data) } @@ -307,17 +322,29 @@ mod test { #[test] fn test_to_bytes() { - let mix_params = MixCreationParameters::new(1024); - assert_eq!(mix_params.to_bytes(), vec![32, 32, 32, 32, 0, 4]) + for kem in [ + libcrux_kem::Algorithm::X25519, + libcrux_kem::Algorithm::XWingKemDraft06, + libcrux_kem::Algorithm::MlKem768, + ] { + let mix_params = MixCreationParameters::new(kem, 1024); + assert_eq!(mix_params.to_bytes(), vec![32, 32, 32, 32, 0, 4]); + } } #[test] fn test_from_bytes() { let params_bytes = vec![32, 32, 32, 32, 0, 4]; - let mix_params = MixCreationParameters::new(1024); - assert_eq!( - mix_params, - MixCreationParameters::try_from(params_bytes.as_slice()).unwrap() - ) + for kem in [ + libcrux_kem::Algorithm::X25519, + libcrux_kem::Algorithm::XWingKemDraft06, + libcrux_kem::Algorithm::MlKem768, + ] { + let mix_params = MixCreationParameters::new(kem, 1024); + assert_eq!( + mix_params, + MixCreationParameters::try_from((kem, params_bytes.as_slice())).unwrap() + ) + } } } diff --git a/nym-outfox/src/lib.rs b/nym-outfox/src/lib.rs index ab0c2e4aae..df70cdde8f 100644 --- a/nym-outfox/src/lib.rs +++ b/nym-outfox/src/lib.rs @@ -4,3 +4,33 @@ pub mod format; pub mod lion; pub mod packet; pub mod route; + +#[cfg(test)] +mod test { + use libcrux_kem::*; + use rand::rngs::OsRng; + use rand::TryRngCore; + + #[test] + fn test_kem() { + let mut os_rng = OsRng; + let mut rng = os_rng.unwrap_mut(); + + let (sk_a, pk_a) = key_gen(Algorithm::MlKem768, &mut rng).unwrap(); + + let received_sk = sk_a.encode(); + let received_pk = pk_a.encode(); + + let pk = PublicKey::decode(Algorithm::MlKem768, &received_pk).unwrap(); + let (ss_b, ct_b) = pk.encapsulate(&mut rng).unwrap(); + let received_ct = ct_b.encode(); + + println!("pk: {}", received_pk.len()); + println!("sk: {}", received_sk.len()); + println!("kem encaps: {}", received_ct.len()); + + let ct_a = Ct::decode(Algorithm::MlKem768, &received_ct).unwrap(); + let ss_a = ct_a.decapsulate(&sk_a).unwrap(); + assert_eq!(ss_b.encode(), ss_a.encode()); + } +} diff --git a/nym-outfox/src/packet.rs b/nym-outfox/src/packet.rs index ba6a8098ac..c4217cf0d2 100644 --- a/nym-outfox/src/packet.rs +++ b/nym-outfox/src/packet.rs @@ -1,26 +1,32 @@ +use libcrux_kem::{Algorithm, PrivateKey}; +use rand::CryptoRng; + use crate::{ constants::{DEFAULT_HOPS, MAGIC_SLICE, MIN_PACKET_SIZE, MIX_PARAMS_LEN}, error::OutfoxError, format::{MixCreationParameters, MixStageParameters}, - route::{Destination, Node, PrivateKey, DEFAULT_PAYLOAD_SIZE}, + route::{Destination, Node, DEFAULT_PAYLOAD_SIZE}, }; use std::{array::TryFromSliceError, collections::VecDeque, ops::Range}; #[derive(Debug)] pub struct OutfoxPacket { + kem: Algorithm, mix_params: MixCreationParameters, payload: Vec, } pub struct OutfoxProcessedPacket { + kem: Algorithm, packet: OutfoxPacket, next_address: [u8; 32], } impl OutfoxProcessedPacket { - pub fn new(packet: OutfoxPacket, next_address: [u8; 32]) -> Self { + pub fn new(kem: Algorithm, packet: OutfoxPacket, next_address: [u8; 32]) -> Self { OutfoxProcessedPacket { + kem, packet, next_address, } @@ -35,13 +41,14 @@ impl OutfoxProcessedPacket { } } -impl TryFrom<&[u8]> for OutfoxPacket { +impl TryFrom<(Algorithm, &[u8])> for OutfoxPacket { type Error = OutfoxError; - fn try_from(v: &[u8]) -> Result { - let (header, payload) = v.split_at(MIX_PARAMS_LEN); + fn try_from(v: (Algorithm, &[u8])) -> Result { + let (header, payload) = v.1.split_at(MIX_PARAMS_LEN); Ok(OutfoxPacket { - mix_params: MixCreationParameters::try_from(header)?, + kem: v.0, + mix_params: MixCreationParameters::try_from((v.0, header))?, payload: payload.to_vec(), }) } @@ -78,20 +85,24 @@ impl OutfoxPacket { Ok(bytes) } - pub fn build>( + pub fn build>( + rng: &mut R, + kem: Algorithm, payload: M, route: &[Node; 4], destination: &Destination, packet_size: Option, - ) -> Result { - let secret_key = x25519_dalek::StaticSecret::random(); + ) -> Result + where + R: CryptoRng, + { let packet_size = packet_size.unwrap_or(DEFAULT_PAYLOAD_SIZE); let packet_size = if packet_size < MIN_PACKET_SIZE { MIN_PACKET_SIZE } else { packet_size } + MAGIC_SLICE.len(); - let mix_params = MixCreationParameters::new(packet_size as u16); + let mix_params = MixCreationParameters::new(kem, packet_size as u16); let padding = mix_params.total_packet_length() - payload.as_ref().len() - MAGIC_SLICE.len(); let mut buffer = vec![0; padding]; @@ -101,9 +112,9 @@ impl OutfoxPacket { // Last node in the route is a gateway, it will decrypt last, and get the final destination address let (range, stage_params) = mix_params.get_stage_params(0); stage_params.encode_mix_layer( + rng, &mut buffer[range], - &secret_key, - route.last().unwrap().pub_key, + &route.last().unwrap().pub_key, destination.address.as_bytes_ref(), )?; @@ -123,16 +134,17 @@ impl OutfoxPacket { // We know that we'll always get 4 nodes, so we can unwrap here let processing_node = nodes.last().unwrap(); let destination_node = nodes.first().unwrap(); - let secret_key = x25519_dalek::StaticSecret::random(); + stage_params.encode_mix_layer( + rng, &mut buffer[range], - &secret_key, - processing_node.pub_key, + &processing_node.pub_key, destination_node.address.as_bytes(), )?; } Ok(OutfoxPacket { + kem, mix_params, payload: buffer, }) @@ -161,7 +173,7 @@ impl OutfoxPacket { pub fn decode_mix_layer( &mut self, layer: usize, - mix_secret_key: &x25519_dalek::StaticSecret, + mix_secret_key: &PrivateKey, ) -> Result, OutfoxError> { let (range, params) = self.stage_params(layer); let routing_data = diff --git a/nym-outfox/src/route.rs b/nym-outfox/src/route.rs index 21a370759a..9bf0581245 100644 --- a/nym-outfox/src/route.rs +++ b/nym-outfox/src/route.rs @@ -2,6 +2,8 @@ use std::fmt::{self, Display, Formatter}; +use libcrux_kem::{Algorithm, PublicKey}; + use crate::error::OutfoxError; pub const SECURITY_PARAMETER: usize = 16; // k in the Sphinx paper. Measured in bytes; 128 bits. @@ -10,9 +12,6 @@ pub const IDENTIFIER_LENGTH: usize = SECURITY_PARAMETER; pub const NODE_ADDRESS_LENGTH: usize = 2 * SECURITY_PARAMETER; pub const DEFAULT_PAYLOAD_SIZE: usize = 1024; -pub type PrivateKey = x25519_dalek::StaticSecret; -pub type PublicKey = x25519_dalek::PublicKey; - // in paper I pub type SURBIdentifier = [u8; IDENTIFIER_LENGTH]; @@ -99,15 +98,38 @@ impl Display for NodeAddressBytes { } } -#[derive(Clone, Debug)] pub struct Node { + pub kem: Algorithm, pub address: NodeAddressBytes, pub pub_key: PublicKey, } +impl Clone for Node { + fn clone(&self) -> Self { + Self { + kem: self.kem, + address: self.address.clone(), + pub_key: PublicKey::decode(self.kem, &self.pub_key.encode()).unwrap(), + } + } +} +impl std::fmt::Debug for Node { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Node") + .field("kem", &self.kem) + .field("address", &self.address) + .field("pub_key", &self.pub_key.encode()) + .finish() + } +} + impl Node { - pub fn new(address: NodeAddressBytes, pub_key: PublicKey) -> Self { - Self { address, pub_key } + pub fn new(kem: Algorithm, address: NodeAddressBytes, pub_key: PublicKey) -> Self { + Self { + kem, + address, + pub_key, + } } } diff --git a/nym-outfox/tests/unittests.rs b/nym-outfox/tests/unittests.rs index 08054d1dcd..3cea852852 100644 --- a/nym-outfox/tests/unittests.rs +++ b/nym-outfox/tests/unittests.rs @@ -9,70 +9,64 @@ mod tests { repeat_with(|| fastrand::u8(..)).take(n).collect() } + use libcrux_kem::key_gen; use nym_outfox::packet::OutfoxPacket; use nym_outfox::route::{ Destination, DestinationAddressBytes, Node, NodeAddressBytes, NODE_ADDRESS_LENGTH, - {PrivateKey, PublicKey}, }; use nym_outfox::format::*; use nym_outfox::lion::*; - pub fn keygen() -> (PrivateKey, PublicKey) { - let private_key = PrivateKey::random(); - let public_key = PublicKey::from(&private_key); - (private_key, public_key) - } - #[test] fn test_encode_decode() { - let mix_params = MixStageParameters { - routing_information_length_bytes: 32, - remaining_header_length_bytes: (32 + 16 + 32) * 4, - payload_length_bytes: 1024, // 1kb - }; + for kem in [ + libcrux_kem::Algorithm::X25519, + libcrux_kem::Algorithm::XWingKemDraft06, + libcrux_kem::Algorithm::MlKem768, + ] { + let mix_params = MixStageParameters { + kem: kem, + routing_information_length_bytes: 32, + remaining_header_length_bytes: (32 + 16 + 32) * 4, + payload_length_bytes: 1024, // 1kb + }; - let user_secret = x25519_dalek::StaticSecret::random(); - let mix_secret = x25519_dalek::StaticSecret::random(); - let mix_public_key = x25519_dalek::PublicKey::from(&mix_secret); + let mut rng = rand::rng(); + let (mix_decapsulation_key, mix_encapsulation_key) = key_gen(kem, &mut rng).unwrap(); - let routing = [0; 32]; - let destination = [0; 32]; + let routing = [0; 32]; + let destination = [0; 32]; - let buffer = randombytes(mix_params.incoming_packet_length()); + let buffer = randombytes(mix_params.incoming_packet_length()); - let mut new_buffer = buffer.clone(); + let mut new_buffer = buffer.clone(); - let node_address_bytes = NodeAddressBytes::from_bytes(routing); - let mix_public_key = PublicKey::from(*mix_public_key.as_bytes()); + let node_address_bytes = NodeAddressBytes::from_bytes(routing); - let node = Node::new(node_address_bytes, mix_public_key); + let node = Node::new(kem, node_address_bytes, mix_encapsulation_key); - let _ = mix_params - .encode_mix_layer( - &mut new_buffer[..], - &user_secret, - node.pub_key, - &destination, - ) - .unwrap(); + let _ = mix_params + .encode_mix_layer(&mut rng, &mut new_buffer[..], &node.pub_key, &destination) + .unwrap(); - assert_ne!( - new_buffer[mix_params.payload_range()], - buffer[mix_params.payload_range()] - ); - assert_ne!(new_buffer[mix_params.routing_data_range()], routing[..]); + assert_ne!( + new_buffer[mix_params.payload_range()], + buffer[mix_params.payload_range()] + ); + assert_ne!(new_buffer[mix_params.routing_data_range()], routing[..]); - let _ = mix_params - .decode_mix_layer(&mut new_buffer[..], &mix_secret) - .unwrap(); + let _ = mix_params + .decode_mix_layer(&mut new_buffer[..], &mix_decapsulation_key) + .unwrap(); - assert_eq!( - new_buffer[mix_params.payload_range()], - buffer[mix_params.payload_range()] - ); - assert_eq!(new_buffer[mix_params.routing_data_range()], routing[..]); + assert_eq!( + new_buffer[mix_params.payload_range()], + buffer[mix_params.payload_range()] + ); + assert_eq!(new_buffer[mix_params.routing_data_range()], routing[..]); + } } #[test] @@ -94,113 +88,149 @@ mod tests { #[test] fn test_packet_params_short() { - let (node1_pk, node1_pub) = keygen(); - let node1 = Node::new( - NodeAddressBytes::from_bytes([0u8; NODE_ADDRESS_LENGTH]), - node1_pub, - ); - let (node2_pk, node2_pub) = keygen(); - let node2 = Node::new( - NodeAddressBytes::from_bytes([1u8; NODE_ADDRESS_LENGTH]), - node2_pub, - ); - let (node3_pk, node3_pub) = keygen(); - let node3 = Node::new( - NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]), - node3_pub, - ); + let mut rng = rand::rng(); + for kem in [ + libcrux_kem::Algorithm::X25519, + libcrux_kem::Algorithm::XWingKemDraft06, + libcrux_kem::Algorithm::MlKem768, + ] { + let (node1_pk, node1_pub) = key_gen(kem, &mut rng).unwrap(); + let node1 = Node::new( + kem, + NodeAddressBytes::from_bytes([0u8; NODE_ADDRESS_LENGTH]), + node1_pub, + ); + let (node2_pk, node2_pub) = key_gen(kem, &mut rng).unwrap(); + let node2 = Node::new( + kem, + NodeAddressBytes::from_bytes([1u8; NODE_ADDRESS_LENGTH]), + node2_pub, + ); + let (node3_pk, node3_pub) = key_gen(kem, &mut rng).unwrap(); + let node3 = Node::new( + kem, + NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]), + node3_pub, + ); - let (gateway_pk, gateway_pub) = keygen(); - let gateway = Node::new( - NodeAddressBytes::from_bytes([3u8; NODE_ADDRESS_LENGTH]), - gateway_pub, - ); + let (gateway_pk, gateway_pub) = key_gen(kem, &mut rng).unwrap(); + let gateway = Node::new( + kem, + NodeAddressBytes::from_bytes([3u8; NODE_ADDRESS_LENGTH]), + gateway_pub, + ); - let destination = Destination::new( - DestinationAddressBytes::from_bytes([9u8; NODE_ADDRESS_LENGTH]), - [0u8; 16], - ); + let destination = Destination::new( + DestinationAddressBytes::from_bytes([9u8; NODE_ADDRESS_LENGTH]), + [0u8; 16], + ); - let route = [node1, node2.clone(), node3.clone(), gateway.clone()]; + let route = [node1, node2.clone(), node3.clone(), gateway.clone()]; - let payload = vec![0, 0, 1, 1, 1, 0, 0]; + let payload = vec![0, 0, 1, 1, 1, 0, 0]; - let packet = - OutfoxPacket::build(&payload, &route, &destination, Some(payload.len())).unwrap(); - let packet_bytes = packet.to_bytes().unwrap(); - println!( - "packet bytes length, {}, declared {}", - packet_bytes.len(), - packet.len() - ); + let packet = OutfoxPacket::build( + &mut rng, + kem, + &payload, + &route, + &destination, + Some(payload.len()), + ) + .unwrap(); + let packet_bytes = packet.to_bytes().unwrap(); + println!( + "packet bytes length, {}, declared {}", + packet_bytes.len(), + packet.len() + ); - let mut packet = OutfoxPacket::try_from(packet_bytes.as_slice()).unwrap(); + let mut packet = OutfoxPacket::try_from((kem, packet_bytes.as_slice())).unwrap(); - let next_address = packet.decode_next_layer(&node1_pk).unwrap(); - assert_eq!(&next_address, node2.address.as_bytes()); - let next_address = packet.decode_next_layer(&node2_pk).unwrap(); - assert_eq!(&next_address, node3.address.as_bytes()); - let next_address = packet.decode_next_layer(&node3_pk).unwrap(); - assert_eq!(&next_address, gateway.address.as_bytes()); - let destination_address = packet.decode_next_layer(&gateway_pk).unwrap(); - assert_eq!(destination_address, destination.address.as_bytes()); + let next_address = packet.decode_next_layer(&node1_pk).unwrap(); + assert_eq!(&next_address, node2.address.as_bytes()); + let next_address = packet.decode_next_layer(&node2_pk).unwrap(); + assert_eq!(&next_address, node3.address.as_bytes()); + let next_address = packet.decode_next_layer(&node3_pk).unwrap(); + assert_eq!(&next_address, gateway.address.as_bytes()); + let destination_address = packet.decode_next_layer(&gateway_pk).unwrap(); + assert_eq!(destination_address, destination.address.as_bytes()); - assert_eq!(payload, packet.recover_plaintext().unwrap()); + assert_eq!(payload, packet.recover_plaintext().unwrap()); + } } #[test] fn test_packet_params_long() { - let (node1_pk, node1_pub) = keygen(); - let node1 = Node::new( - NodeAddressBytes::from_bytes([0u8; NODE_ADDRESS_LENGTH]), - node1_pub, - ); - let (node2_pk, node2_pub) = keygen(); - let node2 = Node::new( - NodeAddressBytes::from_bytes([1u8; NODE_ADDRESS_LENGTH]), - node2_pub, - ); - let (node3_pk, node3_pub) = keygen(); - let node3 = Node::new( - NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]), - node3_pub, - ); + let mut rng = rand::rng(); + for kem in [ + libcrux_kem::Algorithm::X25519, + libcrux_kem::Algorithm::XWingKemDraft06, + libcrux_kem::Algorithm::MlKem768, + ] { + let (node1_pk, node1_pub) = key_gen(kem, &mut rng).unwrap(); + let node1 = Node::new( + kem, + NodeAddressBytes::from_bytes([0u8; NODE_ADDRESS_LENGTH]), + node1_pub, + ); + let (node2_pk, node2_pub) = key_gen(kem, &mut rng).unwrap(); + let node2 = Node::new( + kem, + NodeAddressBytes::from_bytes([1u8; NODE_ADDRESS_LENGTH]), + node2_pub, + ); + let (node3_pk, node3_pub) = key_gen(kem, &mut rng).unwrap(); + let node3 = Node::new( + kem, + NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]), + node3_pub, + ); - let (gateway_pk, gateway_pub) = keygen(); - let gateway = Node::new( - NodeAddressBytes::from_bytes([3u8; NODE_ADDRESS_LENGTH]), - gateway_pub, - ); + let (gateway_pk, gateway_pub) = key_gen(kem, &mut rng).unwrap(); + let gateway = Node::new( + kem, + NodeAddressBytes::from_bytes([3u8; NODE_ADDRESS_LENGTH]), + gateway_pub, + ); - let destination = Destination::new( - DestinationAddressBytes::from_bytes([9u8; NODE_ADDRESS_LENGTH]), - [0u8; 16], - ); + let destination = Destination::new( + DestinationAddressBytes::from_bytes([9u8; NODE_ADDRESS_LENGTH]), + [0u8; 16], + ); - let route = [node1, node2.clone(), node3.clone(), gateway.clone()]; + let route = [node1, node2.clone(), node3.clone(), gateway.clone()]; - let payload = randombytes(2048); + let payload = randombytes(2048); - let packet = - OutfoxPacket::build(&payload, &route, &destination, Some(payload.len())).unwrap(); - let packet_bytes = packet.to_bytes().unwrap(); - println!( - "packet bytes length, {}, declared {}", - packet_bytes.len(), - packet.len() - ); + let packet = OutfoxPacket::build( + &mut rng, + kem, + &payload, + &route, + &destination, + Some(payload.len()), + ) + .unwrap(); + let packet_bytes = packet.to_bytes().unwrap(); + println!( + "packet bytes length, {}, declared {}", + packet_bytes.len(), + packet.len() + ); - let mut packet = OutfoxPacket::try_from(packet_bytes.as_slice()).unwrap(); + let mut packet = OutfoxPacket::try_from((kem, packet_bytes.as_slice())).unwrap(); - let next_address = packet.decode_next_layer(&node1_pk).unwrap(); - assert_eq!(&next_address, node2.address.as_bytes()); - let next_address = packet.decode_next_layer(&node2_pk).unwrap(); - assert_eq!(&next_address, node3.address.as_bytes()); - let next_address = packet.decode_next_layer(&node3_pk).unwrap(); - assert_eq!(&next_address, gateway.address.as_bytes()); - let destination_address = packet.decode_next_layer(&gateway_pk).unwrap(); - assert_eq!(destination_address, destination.address.as_bytes()); + let next_address = packet.decode_next_layer(&node1_pk).unwrap(); + assert_eq!(&next_address, node2.address.as_bytes()); + let next_address = packet.decode_next_layer(&node2_pk).unwrap(); + assert_eq!(&next_address, node3.address.as_bytes()); + let next_address = packet.decode_next_layer(&node3_pk).unwrap(); + assert_eq!(&next_address, gateway.address.as_bytes()); + let destination_address = packet.decode_next_layer(&gateway_pk).unwrap(); + assert_eq!(destination_address, destination.address.as_bytes()); - assert_eq!(payload, packet.recover_plaintext().unwrap()); + assert_eq!(payload, packet.recover_plaintext().unwrap()); + } } }