Generated
+26
-1
@@ -3810,6 +3810,7 @@ dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.2.8",
|
||||
"rayon",
|
||||
"sphinx-packet 0.1.0 (git+https://github.com/nymtech/sphinx.git)",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -4056,6 +4057,7 @@ dependencies = [
|
||||
name = "nym-sphinx-forwarding"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nym-outfox",
|
||||
"nym-sphinx-addressing",
|
||||
"nym-sphinx-params",
|
||||
"nym-sphinx-types",
|
||||
@@ -4086,7 +4088,7 @@ dependencies = [
|
||||
name = "nym-sphinx-types"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"sphinx-packet",
|
||||
"sphinx-packet 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5887,6 +5889,29 @@ dependencies = [
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sphinx-packet"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/sphinx.git#ca107d94360cdf8bbfbdb12fe5320ed74f80e40c"
|
||||
dependencies = [
|
||||
"aes 0.7.5",
|
||||
"arrayref",
|
||||
"blake2",
|
||||
"bs58",
|
||||
"byteorder",
|
||||
"chacha",
|
||||
"curve25519-dalek",
|
||||
"digest 0.9.0",
|
||||
"hkdf 0.11.0",
|
||||
"hmac 0.11.0",
|
||||
"lioness",
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"rand_distr",
|
||||
"sha2 0.9.9",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
|
||||
@@ -11,3 +11,4 @@ repository = { workspace = true }
|
||||
nym-sphinx-addressing = { path = "../addressing" }
|
||||
nym-sphinx-params = { path = "../params" }
|
||||
nym-sphinx-types = { path = "../types" }
|
||||
nym-outfox = { path = "../../../nym-outfox" }
|
||||
|
||||
Generated
+26
-1
@@ -3566,6 +3566,7 @@ dependencies = [
|
||||
"curve25519-dalek",
|
||||
"getrandom 0.2.8",
|
||||
"rayon",
|
||||
"sphinx-packet 0.1.0 (git+https://github.com/nymtech/sphinx.git)",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -3736,6 +3737,7 @@ dependencies = [
|
||||
name = "nym-sphinx-forwarding"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nym-outfox",
|
||||
"nym-sphinx-addressing",
|
||||
"nym-sphinx-params",
|
||||
"nym-sphinx-types",
|
||||
@@ -3766,7 +3768,7 @@ dependencies = [
|
||||
name = "nym-sphinx-types"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"sphinx-packet",
|
||||
"sphinx-packet 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5375,6 +5377,29 @@ dependencies = [
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sphinx-packet"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/sphinx.git#ca107d94360cdf8bbfbdb12fe5320ed74f80e40c"
|
||||
dependencies = [
|
||||
"aes 0.7.5",
|
||||
"arrayref",
|
||||
"blake2",
|
||||
"bs58",
|
||||
"byteorder",
|
||||
"chacha",
|
||||
"curve25519-dalek",
|
||||
"digest 0.9.0",
|
||||
"hkdf 0.11.0",
|
||||
"hmac 0.11.0",
|
||||
"lioness",
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"rand_distr",
|
||||
"sha2 0.9.9",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
|
||||
@@ -16,6 +16,9 @@ chacha20poly1305 = "0.10.1"
|
||||
getrandom = { version = "*", features = ["js"] }
|
||||
thiserror = "1"
|
||||
|
||||
sphinx-packet = { git = "https://github.com/nymtech/sphinx.git" }
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.4"
|
||||
fastrand = "1.8"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::array::TryFromSliceError;
|
||||
|
||||
use crate::lion::MIN_MESSAGE_LEN;
|
||||
use chacha20::cipher::InvalidLength;
|
||||
use thiserror::Error;
|
||||
@@ -11,10 +13,15 @@ pub enum OutfoxError {
|
||||
#[from]
|
||||
source: InvalidLength,
|
||||
},
|
||||
#[error("ChaCha20Poly1305 - Opaque error")]
|
||||
ChaCha20Poly1305Error,
|
||||
#[error("ChaCha20Poly1305 - {0}")]
|
||||
ChaCha20Poly1305Error(String),
|
||||
#[error("Key length must be 32 bytes")]
|
||||
InvalidKeyLength,
|
||||
#[error("Message length must be greater then {MIN_MESSAGE_LEN} bytes")]
|
||||
InvalidMessageLength,
|
||||
#[error("{source}")]
|
||||
TryFromSluce {
|
||||
#[from]
|
||||
source: TryFromSliceError,
|
||||
},
|
||||
}
|
||||
|
||||
+21
-14
@@ -19,7 +19,7 @@
|
||||
//! traffic or dropping messages can be empued at each mix to frustrate traffic analysis.
|
||||
//!
|
||||
//!
|
||||
//! A layer of mix processing is defined by three parameters, includes in the structure [MixStageParameters]:
|
||||
//! A layer of mix processing is defined by three parameters, included in the structure [MixStageParameters]:
|
||||
//! * The `routing_information_length_bytes` (`R`) states the number of bytes representing
|
||||
//! routing information at this layer.
|
||||
//! * The `remaining_header_length_bytes` (`H`) represents the remaining bytes of the packet header.
|
||||
@@ -62,6 +62,7 @@ use chacha20poly1305::Tag;
|
||||
use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE;
|
||||
use curve25519_dalek::montgomery::MontgomeryPoint;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use sphinx_packet::route::Node;
|
||||
|
||||
use std::convert::TryInto;
|
||||
|
||||
@@ -69,6 +70,7 @@ const GROUPELEMENTBYTES: usize = 32;
|
||||
const TAGBYTES: usize = 16;
|
||||
|
||||
use std::ops::Range;
|
||||
use std::u8;
|
||||
|
||||
use crate::error::OutfoxError;
|
||||
use crate::lion::*;
|
||||
@@ -181,10 +183,13 @@ impl MixStageParameters {
|
||||
pub fn encode_mix_layer(
|
||||
&self,
|
||||
buffer: &mut [u8],
|
||||
user_secret_key: &Scalar,
|
||||
mix_public_key: &MontgomeryPoint,
|
||||
routing_data: &[u8],
|
||||
user_secret_key: &[u8],
|
||||
node: &Node,
|
||||
) -> Result<MontgomeryPoint, OutfoxError> {
|
||||
let routing_data = node.address.as_bytes().to_vec();
|
||||
let mix_public_key = MontgomeryPoint(*node.pub_key.as_bytes());
|
||||
let user_secret_key = Scalar::from_bytes_mod_order(user_secret_key.try_into()?);
|
||||
|
||||
if buffer.len() != self.incoming_packet_length() {
|
||||
return Err(OutfoxError::LenMismatch {
|
||||
expected: buffer.len(),
|
||||
@@ -199,20 +204,19 @@ impl MixStageParameters {
|
||||
});
|
||||
}
|
||||
|
||||
let user_public_key = (&ED25519_BASEPOINT_TABLE * user_secret_key).to_montgomery();
|
||||
let user_public_key = (&ED25519_BASEPOINT_TABLE * &user_secret_key).to_montgomery();
|
||||
let shared_key = user_secret_key * mix_public_key;
|
||||
|
||||
// Copy rounting data into buffer
|
||||
buffer[self.routing_data_range()].copy_from_slice(routing_data);
|
||||
buffer[self.routing_data_range()].copy_from_slice(&routing_data);
|
||||
|
||||
// Perform the AEAD
|
||||
let header_aead_key = ChaCha20Poly1305::new_from_slice(&shared_key.0[..])?;
|
||||
// TODO: Should this be all 0s?
|
||||
let nonce = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
let nonce = [0u8; 12];
|
||||
|
||||
let tag = header_aead_key
|
||||
.encrypt_in_place_detached(&nonce.into(), &[], &mut buffer[self.header_range()])
|
||||
.map_err(|_| OutfoxError::ChaCha20Poly1305Error)?;
|
||||
.map_err(|e| OutfoxError::ChaCha20Poly1305Error(e.to_string()))?;
|
||||
|
||||
// Copy Tag into buffer
|
||||
buffer[self.tag_range()].copy_from_slice(&tag[..]);
|
||||
@@ -229,9 +233,12 @@ impl MixStageParameters {
|
||||
pub fn decode_mix_layer(
|
||||
&self,
|
||||
buffer: &mut [u8],
|
||||
mix_secret_key: &Scalar,
|
||||
mix_secret_key: &[u8],
|
||||
) -> Result<MontgomeryPoint, OutfoxError> {
|
||||
// Check the length of the incoming buffer is correct.
|
||||
|
||||
let mix_secret_key = Scalar::from_bytes_mod_order(mix_secret_key.try_into()?);
|
||||
|
||||
if buffer.len() != self.incoming_packet_length() {
|
||||
return Err(OutfoxError::LenMismatch {
|
||||
expected: buffer.len(),
|
||||
@@ -240,12 +247,12 @@ impl MixStageParameters {
|
||||
}
|
||||
|
||||
// Derive the shared key for this packet
|
||||
let user_public_key = MontgomeryPoint(buffer[self.pub_element_range()].try_into().unwrap());
|
||||
let user_public_key = MontgomeryPoint(buffer[self.pub_element_range()].try_into()?);
|
||||
let shared_key = mix_secret_key * user_public_key;
|
||||
|
||||
// Compute the AEAD and check the Tag, if wrong return Err
|
||||
let header_aead_key = ChaCha20Poly1305::new_from_slice(&shared_key.0[..]).unwrap();
|
||||
let nonce = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
let header_aead_key = ChaCha20Poly1305::new_from_slice(&shared_key.0[..])?;
|
||||
let nonce = [0; 12];
|
||||
|
||||
let tag_bytes = buffer[self.tag_range()].to_vec();
|
||||
let tag = Tag::from_slice(&tag_bytes);
|
||||
@@ -257,7 +264,7 @@ impl MixStageParameters {
|
||||
&mut buffer[self.header_range()],
|
||||
tag.as_slice().try_into().unwrap(),
|
||||
)
|
||||
.map_err(|_| OutfoxError::ChaCha20Poly1305Error)?;
|
||||
.map_err(|e| OutfoxError::ChaCha20Poly1305Error(e.to_string()))?;
|
||||
|
||||
// Do a round of LION on the payload
|
||||
lion_transform_decrypt(&mut buffer[self.payload_range()], &shared_key.0)?;
|
||||
|
||||
@@ -1,7 +1,73 @@
|
||||
use crate::format::MixCreationParameters;
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::{
|
||||
error::OutfoxError,
|
||||
format::{MixCreationParameters, MixStageParameters},
|
||||
};
|
||||
|
||||
use sphinx_packet::{packet::builder::DEFAULT_PAYLOAD_SIZE, route::Node};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct OutfoxPacket {
|
||||
params: MixCreationParameters,
|
||||
mix_params: MixCreationParameters,
|
||||
payload: Vec<u8>,
|
||||
}
|
||||
|
||||
pub const DEFAULT_ROUTING_INFO_SIZE: usize = 32;
|
||||
|
||||
impl OutfoxPacket {
|
||||
pub fn build(
|
||||
payload: &[u8],
|
||||
route: &[Node; 3],
|
||||
user_secret_key: &[u8],
|
||||
) -> Result<OutfoxPacket, OutfoxError> {
|
||||
let mut mix_params = MixCreationParameters::new(DEFAULT_PAYLOAD_SIZE);
|
||||
|
||||
for node in route.iter() {
|
||||
mix_params.add_outer_layer(node.address.as_bytes_ref().len());
|
||||
}
|
||||
|
||||
let padding = mix_params.total_packet_length() - payload.len();
|
||||
let mut buffer = vec![0; padding];
|
||||
buffer.extend_from_slice(payload);
|
||||
|
||||
for (idx, node) in route.iter().rev().enumerate() {
|
||||
let (range, stage_params) = mix_params.get_stage_params(idx);
|
||||
stage_params.encode_mix_layer(&mut buffer[range], user_secret_key, node)?;
|
||||
}
|
||||
|
||||
Ok(OutfoxPacket {
|
||||
mix_params,
|
||||
payload: buffer,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn stage_params(&self, layer_number: usize) -> (Range<usize>, MixStageParameters) {
|
||||
self.mix_params().get_stage_params(layer_number)
|
||||
}
|
||||
|
||||
pub fn mix_params(&self) -> &MixCreationParameters {
|
||||
&self.mix_params
|
||||
}
|
||||
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.payload
|
||||
}
|
||||
|
||||
pub fn payload_range(&self) -> Range<usize> {
|
||||
self.stage_params(2).1.payload_range()
|
||||
}
|
||||
|
||||
pub fn payload_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.payload
|
||||
}
|
||||
|
||||
pub fn decode_mix_layer(
|
||||
&mut self,
|
||||
layer: usize,
|
||||
mix_secret_key: &[u8; 32],
|
||||
) -> Result<(), OutfoxError> {
|
||||
let (range, params) = self.stage_params(layer);
|
||||
params.decode_mix_layer(&mut self.payload_mut()[range], mix_secret_key)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,12 @@ mod tests {
|
||||
|
||||
use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use nym_outfox::packet::OutfoxPacket;
|
||||
use sphinx_packet::constants::NODE_ADDRESS_LENGTH;
|
||||
use sphinx_packet::crypto::PublicKey;
|
||||
use sphinx_packet::packet::builder::DEFAULT_PAYLOAD_SIZE;
|
||||
use sphinx_packet::route::Node;
|
||||
use sphinx_packet::route::NodeAddressBytes;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use nym_outfox::format::*;
|
||||
@@ -24,12 +30,11 @@ mod tests {
|
||||
payload_length_bytes: 1024, // 1kb
|
||||
};
|
||||
|
||||
let user_secret_bytes = randombytes(32);
|
||||
let mix_secret_bytes = randombytes(32);
|
||||
|
||||
let user_secret = Scalar::from_bytes_mod_order(user_secret_bytes.try_into().unwrap());
|
||||
let mix_secret = Scalar::from_bytes_mod_order(mix_secret_bytes.try_into().unwrap());
|
||||
let mix_public_key = (&ED25519_BASEPOINT_TABLE * &mix_secret).to_montgomery();
|
||||
let user_secret = randombytes(32);
|
||||
let mix_secret = randombytes(32);
|
||||
let mix_secret_scalar =
|
||||
Scalar::from_bytes_mod_order(mix_secret.clone().try_into().unwrap());
|
||||
let mix_public_key = (&ED25519_BASEPOINT_TABLE * &mix_secret_scalar).to_montgomery();
|
||||
|
||||
let routing = [0; 32];
|
||||
|
||||
@@ -37,13 +42,13 @@ mod tests {
|
||||
|
||||
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 = Node::new(node_address_bytes, mix_public_key);
|
||||
|
||||
let _ = mix_params
|
||||
.encode_mix_layer(
|
||||
&mut new_buffer[..],
|
||||
&user_secret,
|
||||
&mix_public_key,
|
||||
&routing[..],
|
||||
)
|
||||
.encode_mix_layer(&mut new_buffer[..], &user_secret, &node)
|
||||
.unwrap();
|
||||
|
||||
assert!(new_buffer[mix_params.payload_range()] != buffer[mix_params.payload_range()]);
|
||||
@@ -76,67 +81,34 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_packet_params() {
|
||||
// Dummy keys -- we will use the same key for each layer
|
||||
let user_secret_bytes = randombytes(32);
|
||||
let mix_secret_bytes = randombytes(32);
|
||||
let user_secret = randombytes(32);
|
||||
|
||||
let user_secret = Scalar::from_bytes_mod_order(user_secret_bytes.try_into().unwrap());
|
||||
let mix_secret = Scalar::from_bytes_mod_order(mix_secret_bytes.try_into().unwrap());
|
||||
let mix_public_key = (&ED25519_BASEPOINT_TABLE * &mix_secret).to_montgomery();
|
||||
|
||||
let routing = [0; 32];
|
||||
|
||||
let mut params = MixCreationParameters::new(1025);
|
||||
params.add_outer_layer(32);
|
||||
params.add_outer_layer(32);
|
||||
params.add_outer_layer(32);
|
||||
|
||||
let mut buf = vec![0; params.total_packet_length()];
|
||||
|
||||
let (range0, layer_params0) = params.get_stage_params(0);
|
||||
let _ = layer_params0
|
||||
.encode_mix_layer(
|
||||
&mut buf[range0.clone()],
|
||||
&user_secret,
|
||||
&mix_public_key,
|
||||
&routing[..],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (range1, layer_params1) = params.get_stage_params(1);
|
||||
let _ = layer_params1
|
||||
.encode_mix_layer(
|
||||
&mut buf[range1.clone()],
|
||||
&user_secret,
|
||||
&mix_public_key,
|
||||
&routing[..],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (range2, layer_params2) = params.get_stage_params(2);
|
||||
let _ = layer_params2
|
||||
.encode_mix_layer(
|
||||
&mut buf[range2.clone()],
|
||||
&user_secret,
|
||||
&mix_public_key,
|
||||
&routing[..],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
buf[params.total_packet_length() - 1025..params.total_packet_length()] != [0; 1025]
|
||||
let (node1_pk, node1_pub) = sphinx_packet::crypto::keygen();
|
||||
let node1 = Node::new(
|
||||
NodeAddressBytes::from_bytes([0u8; NODE_ADDRESS_LENGTH]),
|
||||
node1_pub,
|
||||
);
|
||||
let (node2_pk, node2_pub) = sphinx_packet::crypto::keygen();
|
||||
let node2 = Node::new(
|
||||
NodeAddressBytes::from_bytes([1u8; NODE_ADDRESS_LENGTH]),
|
||||
node2_pub,
|
||||
);
|
||||
let (node3_pk, node3_pub) = sphinx_packet::crypto::keygen();
|
||||
let node3 = Node::new(
|
||||
NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]),
|
||||
node3_pub,
|
||||
);
|
||||
|
||||
let _ = layer_params2
|
||||
.decode_mix_layer(&mut buf[range2], &mix_secret)
|
||||
.unwrap();
|
||||
let route = [node1, node2, node3];
|
||||
|
||||
let _ = layer_params1
|
||||
.decode_mix_layer(&mut buf[range1], &mix_secret)
|
||||
.unwrap();
|
||||
let payload = randombytes(DEFAULT_PAYLOAD_SIZE);
|
||||
|
||||
let _ = layer_params0
|
||||
.decode_mix_layer(&mut buf[range0], &mix_secret)
|
||||
.unwrap();
|
||||
let mut packet = OutfoxPacket::build(&payload, &route, &user_secret).unwrap();
|
||||
|
||||
packet.decode_mix_layer(2, &node1_pk.to_bytes()).unwrap();
|
||||
packet.decode_mix_layer(1, &node2_pk.to_bytes()).unwrap();
|
||||
packet.decode_mix_layer(0, &node3_pk.to_bytes()).unwrap();
|
||||
|
||||
assert_eq!(payload, &packet.payload()[packet.payload_range()]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user