Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c0f8a7dc8 | |||
| f94acf95b2 | |||
| ae607d300a | |||
| f895cbd91d | |||
| ff253aed1f | |||
| d36bcad11b | |||
| 77d1f2b845 | |||
| 19661bc172 |
Generated
+220
-30
@@ -1446,6 +1446,17 @@ version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "core-models"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94950e87ea550d6d68f1993f3e7bebc8cb7235157bff84337d46195c3aa0b3f0"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
"pastey",
|
||||
"rand 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmos-sdk-proto"
|
||||
version = "0.26.1"
|
||||
@@ -3150,6 +3161,43 @@ dependencies = [
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hax-lib"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89999d4446ba7d86c777bc6a30c106b9bf60d12eb1952242af5d1d7cb33943d"
|
||||
dependencies = [
|
||||
"hax-lib-macros",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hax-lib-macros"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "704a31fb12a8c50243e8d4c88d006f64852822b0e24488a0de9986803a661792"
|
||||
dependencies = [
|
||||
"hax-lib-macros-types",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hax-lib-macros-types"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f302d2a43ba5888c61454ffeb8719dc43e7c0e83082d0fe302ac82d8d898b075"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hdrhistogram"
|
||||
version = "7.5.4"
|
||||
@@ -3257,7 +3305,7 @@ dependencies = [
|
||||
"idna",
|
||||
"ipnet",
|
||||
"once_cell",
|
||||
"rand 0.9.0",
|
||||
"rand 0.9.1",
|
||||
"ring",
|
||||
"rustls 0.23.25",
|
||||
"thiserror 2.0.12",
|
||||
@@ -3282,7 +3330,7 @@ dependencies = [
|
||||
"moka",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"rand 0.9.0",
|
||||
"rand 0.9.1",
|
||||
"resolv-conf",
|
||||
"rustls 0.23.25",
|
||||
"smallvec",
|
||||
@@ -4157,6 +4205,143 @@ version = "0.2.171"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-curve25519"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5514645ba1ee6c55dd71d62a50cc37ad8aab3f956826001aa8dad17482655c46"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-ecdh"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c4fa67cad871d7be9175141b23a174b77536b039945c91b6a5a6d697acd6371"
|
||||
dependencies = [
|
||||
"libcrux-curve25519",
|
||||
"libcrux-p256",
|
||||
"rand 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-hacl-rs"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1134af11da3f24ae8d1a7e2b60ee871c9e3ffd3d8857deaeebab8088b005addd"
|
||||
dependencies = [
|
||||
"libcrux-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-intrinsics"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d3b41dcbc21a5fb7efbbb5af7405b2e79c4bfe443924e90b13afc0080318d31"
|
||||
dependencies = [
|
||||
"core-models",
|
||||
"hax-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-kem"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eefe0e9579f058b99995cbaf918de3cbab90c4d2dde544fe75247fb027ff5af9"
|
||||
dependencies = [
|
||||
"libcrux-ecdh",
|
||||
"libcrux-ml-kem",
|
||||
"libcrux-sha3",
|
||||
"libcrux-traits",
|
||||
"rand 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-macros"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffd6aa2dcd5be681662001b81d493f1569c6d49a32361f470b0c955465cd0338"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-ml-kem"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d368d3e8d6a74e277178d54921eca112a1e6b7837d7d8bc555091acb5d817f5"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
"libcrux-intrinsics",
|
||||
"libcrux-platform",
|
||||
"libcrux-secrets",
|
||||
"libcrux-sha3",
|
||||
"rand 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-p256"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b00d21690ebcc7ce1f242e6c4bdadfd8529f9cf2d7b961c0344c9bcb2c82f78f"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
"libcrux-sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-platform"
|
||||
version = "0.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db82d058aa76ea315a3b2092f69dfbd67ddb0e462038a206e1dcd73f058c0778"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-secrets"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "332737e629fe6ba7547f5c0f90559eac865d5dbecf98138ffae8f16ab8cbe33f"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-sha2"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91eed3bb0ae073f46ae03c83318013fba6e3302bf3292639417b68e908fec4bf"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
"libcrux-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-sha3"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29d95de4257eafdfaf3bffecadb615219b0ca920c553722b3646d32dde76c797"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
"libcrux-intrinsics",
|
||||
"libcrux-platform",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-traits"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cdbf9591a39f04d6da6b9bad51ac58378604a80708c2173dadf92029891b9e2"
|
||||
dependencies = [
|
||||
"rand 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.11"
|
||||
@@ -6856,25 +7041,6 @@ dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-outfox"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"blake3",
|
||||
"chacha20",
|
||||
"chacha20poly1305",
|
||||
"criterion",
|
||||
"fastrand 2.3.0",
|
||||
"getrandom 0.2.15",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"rayon",
|
||||
"sphinx-packet",
|
||||
"thiserror 2.0.12",
|
||||
"x25519-dalek",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-outfox"
|
||||
version = "0.1.0"
|
||||
@@ -6893,6 +7059,25 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-outfox"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"blake3",
|
||||
"bs58",
|
||||
"chacha20",
|
||||
"chacha20poly1305",
|
||||
"criterion",
|
||||
"fastrand 2.3.0",
|
||||
"getrandom 0.2.15",
|
||||
"libcrux-kem",
|
||||
"log",
|
||||
"rand 0.9.1",
|
||||
"rayon",
|
||||
"thiserror 2.0.12",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-pemstore"
|
||||
version = "0.3.0"
|
||||
@@ -7371,7 +7556,7 @@ name = "nym-sphinx-forwarding"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym.git?branch=release/2025.11-cheddar#e9bb9792ab723a1ad5fe40cb292dc08d4eb40c2f"
|
||||
dependencies = [
|
||||
"nym-outfox 0.1.0 (git+https://github.com/nymtech/nym.git?branch=release/2025.11-cheddar)",
|
||||
"nym-outfox 0.1.0",
|
||||
"nym-sphinx-addressing 0.1.0 (git+https://github.com/nymtech/nym.git?branch=release/2025.11-cheddar)",
|
||||
"nym-sphinx-params 0.1.0 (git+https://github.com/nymtech/nym.git?branch=release/2025.11-cheddar)",
|
||||
"nym-sphinx-types 0.2.0 (git+https://github.com/nymtech/nym.git?branch=release/2025.11-cheddar)",
|
||||
@@ -7454,7 +7639,7 @@ dependencies = [
|
||||
name = "nym-sphinx-types"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"nym-outfox 0.1.0",
|
||||
"nym-outfox 0.2.0",
|
||||
"sphinx-packet",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
@@ -7464,7 +7649,7 @@ name = "nym-sphinx-types"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/nymtech/nym.git?branch=release/2025.11-cheddar#e9bb9792ab723a1ad5fe40cb292dc08d4eb40c2f"
|
||||
dependencies = [
|
||||
"nym-outfox 0.1.0 (git+https://github.com/nymtech/nym.git?branch=release/2025.11-cheddar)",
|
||||
"nym-outfox 0.1.0",
|
||||
"sphinx-packet",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
@@ -8318,6 +8503,12 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pastey"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3a8cb46bdc156b1c90460339ae6bfd45ba0394e5effbaa640badb4987fdc261"
|
||||
|
||||
[[package]]
|
||||
name = "peg"
|
||||
version = "0.8.4"
|
||||
@@ -8638,7 +8829,7 @@ dependencies = [
|
||||
"hmac",
|
||||
"md-5",
|
||||
"memchr",
|
||||
"rand 0.9.0",
|
||||
"rand 0.9.1",
|
||||
"sha2 0.10.9",
|
||||
"stringprep",
|
||||
]
|
||||
@@ -8889,7 +9080,7 @@ checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.3.1",
|
||||
"rand 0.9.0",
|
||||
"rand 0.9.1",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustls 0.23.25",
|
||||
@@ -8943,13 +9134,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.1",
|
||||
"zerocopy 0.8.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11016,7 +11206,7 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"postgres-protocol",
|
||||
"postgres-types",
|
||||
"rand 0.9.0",
|
||||
"rand 0.9.1",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
|
||||
+10
-5
@@ -1,27 +1,32 @@
|
||||
[package]
|
||||
name = "nym-outfox"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
description = "Outfox package format"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rayon = { workspace = true }
|
||||
blake3 = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
chacha20 = { workspace = true, features = ["std"] }
|
||||
x25519-dalek = { workspace = true }
|
||||
chacha20poly1305 = { workspace = true }
|
||||
getrandom = { workspace = true, features = ["js"] }
|
||||
thiserror = { workspace = true }
|
||||
sphinx-packet = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
||||
rand = "0.9.1"
|
||||
bs58 = "0.5.1"
|
||||
libcrux-kem = "0.0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { workspace = true }
|
||||
fastrand = { workspace = true }
|
||||
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
harness = false
|
||||
@@ -0,0 +1,13 @@
|
||||
# Outfox
|
||||
|
||||
## Notes from Georgio:
|
||||
|
||||
### DO NOT INTEGRATE BEFORE:
|
||||
- Checking the situation with empty Nonces/Additional Data for `ChaCha20Poly1305` calls marked by HAZARD.
|
||||
- Programmatically checking which KEM is used during packet handling, and handling related errors.
|
||||
- Crypto audit of `src/lion.rs`.
|
||||
- Updating documentation to replace discrete-log operations with KEM operations.
|
||||
|
||||
### Things to optimize:
|
||||
- Copying and allocations.
|
||||
- Using libcrux for `ChaCha20`/`ChaCha20Poly1305`.
|
||||
@@ -0,0 +1,285 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use libcrux_kem::key_gen;
|
||||
use nym_outfox::{
|
||||
format::MixStageParameters,
|
||||
packet::OutfoxPacket,
|
||||
route::{Destination, DestinationAddressBytes, Node, NodeAddressBytes, NODE_ADDRESS_LENGTH},
|
||||
};
|
||||
use std::iter::repeat_with;
|
||||
|
||||
pub fn randombytes(n: usize) -> Vec<u8> {
|
||||
repeat_with(|| fastrand::u8(..)).take(n).collect()
|
||||
}
|
||||
|
||||
fn test_encode_decode(c: &mut Criterion) {
|
||||
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 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 buffer = randombytes(mix_params.incoming_packet_length());
|
||||
|
||||
let mut new_buffer = buffer.clone();
|
||||
|
||||
let node_address_bytes = NodeAddressBytes::from_bytes(routing);
|
||||
|
||||
let node = Node::new(kem, node_address_bytes, mix_encapsulation_key);
|
||||
|
||||
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[..]);
|
||||
|
||||
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[..]);
|
||||
}
|
||||
}
|
||||
|
||||
fn kem_str(kem: libcrux_kem::Algorithm) -> &'static str {
|
||||
match kem {
|
||||
libcrux_kem::Algorithm::X25519 => "KEM: x25519",
|
||||
libcrux_kem::Algorithm::XWingKemDraft06 => "KEM: XWing",
|
||||
libcrux_kem::Algorithm::MlKem768 => "KEM: MlKem768",
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn test_packet(c: &mut Criterion) {
|
||||
let mut rng = rand::rng();
|
||||
for kem in [
|
||||
libcrux_kem::Algorithm::X25519,
|
||||
libcrux_kem::Algorithm::XWingKemDraft06,
|
||||
libcrux_kem::Algorithm::MlKem768,
|
||||
] {
|
||||
let (entry_sk, entry_pk) = key_gen(kem, &mut rng).unwrap();
|
||||
let entry_node = Node::new(
|
||||
kem,
|
||||
NodeAddressBytes::from_bytes([5u8; NODE_ADDRESS_LENGTH]),
|
||||
entry_pk,
|
||||
);
|
||||
let (node1_sk, node1_pk) = key_gen(kem, &mut rng).unwrap();
|
||||
let node1 = Node::new(
|
||||
kem,
|
||||
NodeAddressBytes::from_bytes([0u8; NODE_ADDRESS_LENGTH]),
|
||||
node1_pk,
|
||||
);
|
||||
let (node2_sk, node2_pk) = key_gen(kem, &mut rng).unwrap();
|
||||
let node2 = Node::new(
|
||||
kem,
|
||||
NodeAddressBytes::from_bytes([1u8; NODE_ADDRESS_LENGTH]),
|
||||
node2_pk,
|
||||
);
|
||||
let (node3_sk, node3_pk) = key_gen(kem, &mut rng).unwrap();
|
||||
let node3 = Node::new(
|
||||
kem,
|
||||
NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]),
|
||||
node3_pk,
|
||||
);
|
||||
|
||||
let (exit_sk, exit_pk) = key_gen(kem, &mut rng).unwrap();
|
||||
let exit = Node::new(
|
||||
kem,
|
||||
NodeAddressBytes::from_bytes([3u8; NODE_ADDRESS_LENGTH]),
|
||||
exit_pk,
|
||||
);
|
||||
|
||||
c.bench_function(&format!("{} | Key Generation", kem_str(kem)), |b| {
|
||||
b.iter(|| key_gen(kem, &mut rng).unwrap())
|
||||
});
|
||||
|
||||
let destination = Destination::new(
|
||||
DestinationAddressBytes::from_bytes([9u8; NODE_ADDRESS_LENGTH]),
|
||||
[0u8; 16],
|
||||
);
|
||||
|
||||
let route = [
|
||||
entry_node,
|
||||
node1.clone(),
|
||||
node2.clone(),
|
||||
node3.clone(),
|
||||
exit.clone(),
|
||||
];
|
||||
|
||||
for payload_size in [512, 1000, 1024, 2048, 4096] {
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{} | Packet Construction | Payload: {} bytes",
|
||||
kem_str(kem),
|
||||
payload_size
|
||||
),
|
||||
|b| {
|
||||
b.iter_batched(
|
||||
|| (rand::rng(), randombytes(payload_size)),
|
||||
|(mut rng, payload)| {
|
||||
OutfoxPacket::build(
|
||||
&mut rng,
|
||||
kem,
|
||||
&payload,
|
||||
&route,
|
||||
&destination,
|
||||
Some(payload.len()),
|
||||
)
|
||||
},
|
||||
criterion::BatchSize::PerIteration,
|
||||
)
|
||||
},
|
||||
);
|
||||
let payload = randombytes(payload_size);
|
||||
|
||||
let packet = OutfoxPacket::build(
|
||||
&mut rng,
|
||||
kem,
|
||||
&payload,
|
||||
&route,
|
||||
&destination,
|
||||
Some(payload.len()),
|
||||
)
|
||||
.unwrap();
|
||||
let packet_bytes = packet.to_bytes().unwrap();
|
||||
|
||||
let mut packet = OutfoxPacket::try_from((kem, packet_bytes.as_slice())).unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{} | Packet Decoding (Entry) | Payload: {} bytes",
|
||||
kem_str(kem),
|
||||
payload_size
|
||||
),
|
||||
|b| {
|
||||
b.iter_batched(
|
||||
|| OutfoxPacket::try_from((kem, packet_bytes.as_slice())).unwrap(),
|
||||
|mut packet| packet.decode_next_layer(&entry_sk).unwrap(),
|
||||
criterion::BatchSize::PerIteration,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{} | Packet Decoding (Layer 1) | Payload: {} bytes",
|
||||
kem_str(kem),
|
||||
payload_size
|
||||
),
|
||||
|b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
let mut packet =
|
||||
OutfoxPacket::try_from((kem, packet_bytes.as_slice())).unwrap();
|
||||
packet.decode_next_layer(&entry_sk).unwrap();
|
||||
packet
|
||||
},
|
||||
|mut packet| packet.decode_next_layer(&node1_sk).unwrap(),
|
||||
criterion::BatchSize::PerIteration,
|
||||
)
|
||||
},
|
||||
);
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{} | Packet Decoding (Layer 2) | Payload: {} bytes",
|
||||
kem_str(kem),
|
||||
payload_size
|
||||
),
|
||||
|b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
let mut packet =
|
||||
OutfoxPacket::try_from((kem, packet_bytes.as_slice())).unwrap();
|
||||
packet.decode_next_layer(&entry_sk).unwrap();
|
||||
packet.decode_next_layer(&node1_sk).unwrap();
|
||||
packet
|
||||
},
|
||||
|mut packet| packet.decode_next_layer(&node2_sk).unwrap(),
|
||||
criterion::BatchSize::PerIteration,
|
||||
)
|
||||
},
|
||||
);
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{} | Packet Decoding (Layer 3) | Payload: {} bytes",
|
||||
kem_str(kem),
|
||||
payload_size
|
||||
),
|
||||
|b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
let mut packet =
|
||||
OutfoxPacket::try_from((kem, packet_bytes.as_slice())).unwrap();
|
||||
packet.decode_next_layer(&entry_sk).unwrap();
|
||||
packet.decode_next_layer(&node1_sk).unwrap();
|
||||
packet.decode_next_layer(&node2_sk).unwrap();
|
||||
packet
|
||||
},
|
||||
|mut packet| packet.decode_next_layer(&node3_sk).unwrap(),
|
||||
criterion::BatchSize::PerIteration,
|
||||
)
|
||||
},
|
||||
);
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{} | Packet Decoding + Plaintext Recovery (exit) | Payload: {} bytes",
|
||||
kem_str(kem),
|
||||
payload_size
|
||||
),
|
||||
|b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
let mut packet =
|
||||
OutfoxPacket::try_from((kem, packet_bytes.as_slice())).unwrap();
|
||||
packet.decode_next_layer(&entry_sk).unwrap();
|
||||
packet.decode_next_layer(&node1_sk).unwrap();
|
||||
packet.decode_next_layer(&node2_sk).unwrap();
|
||||
packet.decode_next_layer(&node3_sk).unwrap();
|
||||
packet
|
||||
},
|
||||
|mut packet| {
|
||||
packet.decode_next_layer(&exit_sk).unwrap();
|
||||
packet.recover_plaintext()
|
||||
},
|
||||
criterion::BatchSize::PerIteration,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
let next_address = packet.decode_next_layer(&entry_sk).unwrap();
|
||||
assert_eq!(&next_address, node1.address.as_bytes());
|
||||
let next_address = packet.decode_next_layer(&node1_sk).unwrap();
|
||||
assert_eq!(&next_address, node2.address.as_bytes());
|
||||
let next_address = packet.decode_next_layer(&node2_sk).unwrap();
|
||||
assert_eq!(&next_address, node3.address.as_bytes());
|
||||
let next_address = packet.decode_next_layer(&node3_sk).unwrap();
|
||||
assert_eq!(&next_address, exit.address.as_bytes());
|
||||
let destination_address = packet.decode_next_layer(&exit_sk).unwrap();
|
||||
assert_eq!(destination_address, destination.address.as_bytes());
|
||||
|
||||
assert_eq!(payload, packet.recover_plaintext().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(benches, test_packet);
|
||||
criterion_main!(benches);
|
||||
@@ -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;
|
||||
@@ -5,18 +7,25 @@ pub const MIN_MESSAGE_LEN: usize = 24 * 2;
|
||||
pub(crate) const CONTEXT: &str = "LIONKEYS";
|
||||
pub(crate) const TAG_LEN: usize = 24;
|
||||
pub const DEFAULT_ROUTING_INFO_SIZE: u8 = 32;
|
||||
pub const DEFAULT_HOPS: usize = 4;
|
||||
pub const DEFAULT_HOPS: usize = 5;
|
||||
pub const ROUTING_INFORMATION_LENGTH_BY_STAGE: [u8; DEFAULT_HOPS] =
|
||||
[DEFAULT_ROUTING_INFO_SIZE; 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 fn outfox_packet_overhead(kem: Algorithm) -> usize {
|
||||
MIX_PARAMS_LEN
|
||||
+ (groupelementbytes(kem) + 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 {
|
||||
|
||||
@@ -28,4 +28,7 @@ pub enum OutfoxError {
|
||||
InvalidHeaderLength(usize),
|
||||
#[error("Invalid magic bytes, expected: {:?}, got: {:?}", MAGIC_SLICE, 0)]
|
||||
InvalidMagicBytes(Vec<u8>),
|
||||
|
||||
#[error("routing information processing failure {0}")]
|
||||
InvalidRouting(String),
|
||||
}
|
||||
|
||||
+77
-44
@@ -58,22 +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 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],
|
||||
@@ -81,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()?),
|
||||
})
|
||||
@@ -98,7 +107,7 @@ impl TryFrom<&[u8]> for MixCreationParameters {
|
||||
|
||||
impl MixCreationParameters {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(5);
|
||||
let mut bytes = Vec::with_capacity(DEFAULT_HOPS + 1);
|
||||
bytes.extend_from_slice(self.routing_information_length_by_stage.as_slice());
|
||||
bytes.extend_from_slice(&self.payload_length_bytes.to_le_bytes());
|
||||
bytes
|
||||
@@ -109,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,
|
||||
}
|
||||
@@ -120,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
|
||||
}
|
||||
@@ -133,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,
|
||||
@@ -143,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,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
|
||||
@@ -175,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 {
|
||||
@@ -184,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()
|
||||
@@ -209,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: &x25519_dalek::StaticSecret,
|
||||
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() {
|
||||
@@ -232,14 +248,18 @@ 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())?;
|
||||
|
||||
// HAZARD: ENCRYPTION WITH EMPTY NONCES AND NO AD!!
|
||||
|
||||
let header_aead_key = ChaCha20Poly1305::new_from_slice(&shared_key)?;
|
||||
let nonce = [0u8; 12];
|
||||
|
||||
let tag = header_aead_key
|
||||
@@ -250,18 +270,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: &x25519_dalek::StaticSecret,
|
||||
mix_decapsulation_key: &PrivateKey,
|
||||
) -> Result<Vec<u8>, OutfoxError> {
|
||||
// Check the length of the incoming buffer is correct.
|
||||
if buffer.len() != self.incoming_packet_length() {
|
||||
@@ -271,13 +291,14 @@ 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)?;
|
||||
|
||||
// HAZARD: ENCRYPTION WITH EMPTY NONCES AND NO AD!!
|
||||
let nonce = [0; 12];
|
||||
|
||||
let tag_bytes = buffer[self.tag_range()].to_vec();
|
||||
@@ -294,7 +315,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)
|
||||
}
|
||||
@@ -306,17 +327,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, 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()
|
||||
)
|
||||
let params_bytes = vec![32, 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,
|
||||
MixCreationParameters::try_from((kem, params_bytes.as_slice())).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,3 +3,34 @@ pub mod error;
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
+35
-26
@@ -1,29 +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, DEFAULT_PAYLOAD_SIZE},
|
||||
};
|
||||
use sphinx_packet::{
|
||||
crypto::PrivateKey,
|
||||
packet::builder::DEFAULT_PAYLOAD_SIZE,
|
||||
route::{Destination, Node},
|
||||
};
|
||||
|
||||
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,
|
||||
}
|
||||
@@ -38,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(),
|
||||
})
|
||||
}
|
||||
@@ -81,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],
|
||||
route: &[Node; DEFAULT_HOPS],
|
||||
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];
|
||||
@@ -104,38 +112,39 @@ 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(),
|
||||
)?;
|
||||
|
||||
let route = route.iter().rev().collect::<Vec<&Node>>();
|
||||
|
||||
// We've reversed the route, and we iterate pairs of node, first node in the pair is the destination, and the second(last) is the processing node
|
||||
// Route: [N1, N2, N3, G]
|
||||
// Reverse: [G, N3, N2, N1]
|
||||
// Pairs: [(G, N3), (N3, N2), (N2, N1)]
|
||||
// Route: [Entry, N1, N2, N3, Exit]
|
||||
// Reverse: [Exit, N3, N2, N1, Entry]
|
||||
// Pairs: [(Exit, N3), (N3, N2), (N2, N1), (N1, Entry)]
|
||||
// We iterate over pairs, and encode the mix layer for each pair
|
||||
// For the first pair, we encode the mix layer for N3, and the destination is G
|
||||
// For the first pair, we encode the mix layer for N3, and the destination is Exit
|
||||
// For the second pair, we encode the mix layer for N2, and the destination is N3
|
||||
// For the third pair, we encode the mix layer for N1, and the destination is N2
|
||||
// Entry gateway will simply forward the packet to N1 and processing will continue from there
|
||||
for (idx, nodes) in route.windows(2).enumerate() {
|
||||
let (range, stage_params) = mix_params.get_stage_params(idx + 1);
|
||||
// We know that we'll always get 4 nodes, so we can unwrap here
|
||||
// We know that we'll always get DEFAULT_HOPS 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,
|
||||
})
|
||||
@@ -164,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 =
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
// I took everything from sphinx to decouple the crates
|
||||
|
||||
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.
|
||||
pub const DESTINATION_ADDRESS_LENGTH: usize = 2 * SECURITY_PARAMETER;
|
||||
pub const IDENTIFIER_LENGTH: usize = SECURITY_PARAMETER;
|
||||
pub const NODE_ADDRESS_LENGTH: usize = 2 * SECURITY_PARAMETER;
|
||||
pub const DEFAULT_PAYLOAD_SIZE: usize = 1024;
|
||||
|
||||
// in paper I
|
||||
pub type SURBIdentifier = [u8; IDENTIFIER_LENGTH];
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Destination {
|
||||
// address in theory could be changed to a vec<u8> as it does not need to be strictly DESTINATION_ADDRESS_LENGTH long
|
||||
// but cannot be longer than that (assuming longest possible route)
|
||||
pub address: DestinationAddressBytes,
|
||||
pub identifier: SURBIdentifier,
|
||||
}
|
||||
|
||||
impl Destination {
|
||||
pub fn new(address: DestinationAddressBytes, identifier: SURBIdentifier) -> Self {
|
||||
Self {
|
||||
address,
|
||||
identifier,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in paper nu
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Hash)]
|
||||
pub struct NodeAddressBytes([u8; NODE_ADDRESS_LENGTH]);
|
||||
|
||||
impl NodeAddressBytes {
|
||||
pub fn as_base58_string(&self) -> String {
|
||||
bs58::encode(&self.0).into_string()
|
||||
}
|
||||
|
||||
pub fn try_from_base58_string<S: Into<String>>(val: S) -> Result<Self, OutfoxError> {
|
||||
let decoded = match bs58::decode(val.into()).into_vec() {
|
||||
Ok(decoded) => decoded,
|
||||
Err(e) => {
|
||||
return Err(OutfoxError::InvalidRouting(format!(
|
||||
"failed to decode node address from b58 string: {:?}",
|
||||
e
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
if decoded.len() != NODE_ADDRESS_LENGTH {
|
||||
return Err(OutfoxError::InvalidRouting(
|
||||
format!("decoded node address has invalid length").into(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut address_bytes = [0; NODE_ADDRESS_LENGTH];
|
||||
address_bytes.copy_from_slice(&decoded[..]);
|
||||
|
||||
Ok(NodeAddressBytes(address_bytes))
|
||||
}
|
||||
|
||||
pub fn try_from_byte_slice(b: &[u8]) -> Result<Self, OutfoxError> {
|
||||
if b.len() != NODE_ADDRESS_LENGTH {
|
||||
return Err(OutfoxError::InvalidRouting(
|
||||
format!("received bytes got invalid length").into(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut address_bytes = [0; NODE_ADDRESS_LENGTH];
|
||||
address_bytes.copy_from_slice(b);
|
||||
|
||||
Ok(NodeAddressBytes(address_bytes))
|
||||
}
|
||||
|
||||
pub fn from_bytes(b: [u8; NODE_ADDRESS_LENGTH]) -> Self {
|
||||
NodeAddressBytes(b)
|
||||
}
|
||||
|
||||
/// View this `NodeAddressBytes` as an array of bytes.
|
||||
pub fn as_bytes(&self) -> &[u8; NODE_ADDRESS_LENGTH] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Convert this `NodeAddressBytes` to an array of bytes.
|
||||
pub fn to_bytes(&self) -> [u8; NODE_ADDRESS_LENGTH] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NodeAddressBytes {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
self.as_base58_string().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
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(kem: Algorithm, address: NodeAddressBytes, pub_key: PublicKey) -> Self {
|
||||
Self {
|
||||
kem,
|
||||
address,
|
||||
pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in paper delta
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Hash)]
|
||||
pub struct DestinationAddressBytes([u8; DESTINATION_ADDRESS_LENGTH]);
|
||||
|
||||
impl DestinationAddressBytes {
|
||||
pub fn as_base58_string(&self) -> String {
|
||||
bs58::encode(&self.0).into_string()
|
||||
}
|
||||
|
||||
pub fn try_from_base58_string<S: Into<String>>(val: S) -> Result<Self, OutfoxError> {
|
||||
let decoded = match bs58::decode(val.into()).into_vec() {
|
||||
Ok(decoded) => decoded,
|
||||
Err(e) => {
|
||||
return Err(OutfoxError::InvalidRouting(format!(
|
||||
"failed to decode destination from b58 string: {:?}",
|
||||
e
|
||||
))
|
||||
.into())
|
||||
}
|
||||
};
|
||||
|
||||
if decoded.len() != DESTINATION_ADDRESS_LENGTH {
|
||||
return Err(OutfoxError::InvalidRouting(
|
||||
format!("decoded destination address has invalid length",).into(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut address_bytes = [0; DESTINATION_ADDRESS_LENGTH];
|
||||
address_bytes.copy_from_slice(&decoded[..]);
|
||||
|
||||
Ok(DestinationAddressBytes(address_bytes))
|
||||
}
|
||||
|
||||
pub fn from_bytes(b: [u8; DESTINATION_ADDRESS_LENGTH]) -> Self {
|
||||
DestinationAddressBytes(b)
|
||||
}
|
||||
|
||||
pub fn try_from_byte_slice(b: &[u8]) -> Result<Self, OutfoxError> {
|
||||
if b.len() != DESTINATION_ADDRESS_LENGTH {
|
||||
return Err(
|
||||
OutfoxError::InvalidRouting(format!("received bytes got invalid length")).into(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut address_bytes = [0; DESTINATION_ADDRESS_LENGTH];
|
||||
address_bytes.copy_from_slice(b);
|
||||
|
||||
Ok(DestinationAddressBytes(address_bytes))
|
||||
}
|
||||
|
||||
/// View this `DestinationAddressBytes` as an array of bytes.
|
||||
pub fn as_bytes_ref(&self) -> &[u8; DESTINATION_ADDRESS_LENGTH] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Convert this `DestinationAddressBytes` to an array of bytes.
|
||||
pub fn as_bytes(&self) -> [u8; DESTINATION_ADDRESS_LENGTH] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DestinationAddressBytes {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
self.as_base58_string().fmt(f)
|
||||
}
|
||||
}
|
||||
+193
-136
@@ -9,71 +9,64 @@ mod tests {
|
||||
repeat_with(|| fastrand::u8(..)).take(n).collect()
|
||||
}
|
||||
|
||||
use libcrux_kem::key_gen;
|
||||
use nym_outfox::packet::OutfoxPacket;
|
||||
use sphinx_packet::constants::NODE_ADDRESS_LENGTH;
|
||||
use sphinx_packet::crypto::{PrivateKey, PublicKey};
|
||||
use sphinx_packet::route::Destination;
|
||||
use sphinx_packet::route::DestinationAddressBytes;
|
||||
use sphinx_packet::route::Node;
|
||||
use sphinx_packet::route::NodeAddressBytes;
|
||||
|
||||
use nym_outfox::route::{
|
||||
Destination, DestinationAddressBytes, Node, NodeAddressBytes, NODE_ADDRESS_LENGTH,
|
||||
};
|
||||
|
||||
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]
|
||||
@@ -95,113 +88,177 @@ 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 (entry_pk, entry_pub) = key_gen(kem, &mut rng).unwrap();
|
||||
let entry = Node::new(
|
||||
kem,
|
||||
NodeAddressBytes::from_bytes([8u8; NODE_ADDRESS_LENGTH]),
|
||||
entry_pub,
|
||||
);
|
||||
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 (exit_pk, exit_pub) = key_gen(kem, &mut rng).unwrap();
|
||||
let exit = Node::new(
|
||||
kem,
|
||||
NodeAddressBytes::from_bytes([3u8; NODE_ADDRESS_LENGTH]),
|
||||
exit_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 = [
|
||||
entry,
|
||||
node1.clone(),
|
||||
node2.clone(),
|
||||
node3.clone(),
|
||||
exit.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(&entry_pk).unwrap();
|
||||
assert_eq!(&next_address, node1.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, exit.address.as_bytes());
|
||||
let destination_address = packet.decode_next_layer(&exit_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 (entry_pk, entry_pub) = key_gen(kem, &mut rng).unwrap();
|
||||
let entry = Node::new(
|
||||
kem,
|
||||
NodeAddressBytes::from_bytes([8u8; NODE_ADDRESS_LENGTH]),
|
||||
entry_pub,
|
||||
);
|
||||
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 (exit_pk, exit_pub) = key_gen(kem, &mut rng).unwrap();
|
||||
let exit = Node::new(
|
||||
kem,
|
||||
NodeAddressBytes::from_bytes([3u8; NODE_ADDRESS_LENGTH]),
|
||||
exit_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 = [
|
||||
entry,
|
||||
node1.clone(),
|
||||
node2.clone(),
|
||||
node3.clone(),
|
||||
exit.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(&entry_pk).unwrap();
|
||||
assert_eq!(&next_address, node1.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, exit.address.as_bytes());
|
||||
let destination_address = packet.decode_next_layer(&exit_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