switch to libcrux + add support to pq primitives

This commit is contained in:
Georgio Nicolas
2025-07-14 21:49:03 +02:00
parent 19661bc172
commit 77d1f2b845
7 changed files with 331 additions and 204 deletions
+2 -3
View File
@@ -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 }
+12 -5
View File
@@ -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 {
+70 -43
View File
@@ -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<Self, Self::Error> {
if v.len() != MIX_PARAMS_LEN {
return Err(OutfoxError::InvalidHeaderLength(v.len()));
fn try_from(v: (Algorithm, &[u8])) -> Result<Self, Self::Error> {
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<usize> {
0..groupelementbytes()
pub fn encaps_element_range(&self) -> Range<usize> {
0..groupelementbytes(self.kem)
}
pub fn tag_range(&self) -> Range<usize> {
groupelementbytes()..groupelementbytes() + tagbytes()
groupelementbytes(self.kem)..groupelementbytes(self.kem) + tagbytes()
}
pub fn routing_data_range(&self) -> Range<usize> {
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<usize> {
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<R>(
&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<x25519_dalek::SharedSecret, OutfoxError> {
) -> Result<Ss, OutfoxError>
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<Vec<u8>, 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()
)
}
}
}
+30
View File
@@ -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());
}
}
+28 -16
View File
@@ -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<u8>,
}
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<Self, Self::Error> {
let (header, payload) = v.split_at(MIX_PARAMS_LEN);
fn try_from(v: (Algorithm, &[u8])) -> Result<Self, Self::Error> {
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<M: AsRef<[u8]>>(
pub fn build<R, M: AsRef<[u8]>>(
rng: &mut R,
kem: Algorithm,
payload: M,
route: &[Node; 4],
destination: &Destination,
packet_size: Option<usize>,
) -> Result<OutfoxPacket, OutfoxError> {
let secret_key = x25519_dalek::StaticSecret::random();
) -> Result<OutfoxPacket, OutfoxError>
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<Vec<u8>, OutfoxError> {
let (range, params) = self.stage_params(layer);
let routing_data =
+28 -6
View File
@@ -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,
}
}
}
+161 -131
View File
@@ -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());
}
}
}