switch to libcrux + add support to pq primitives
This commit is contained in:
@@ -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 }
|
||||
|
||||
@@ -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
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user