diff --git a/Cargo.lock b/Cargo.lock index 1bdd0c2207..dc4b682eeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3811,7 +3811,6 @@ dependencies = [ name = "nym-outfox" version = "0.1.0" dependencies = [ - "bincode", "blake3", "chacha20", "chacha20poly1305", @@ -3820,7 +3819,6 @@ dependencies = [ "fastrand", "getrandom 0.2.8", "rayon", - "serde", "sphinx-packet", "thiserror", "zeroize", @@ -4092,7 +4090,6 @@ name = "nym-sphinx-params" version = "0.1.0" dependencies = [ "nym-crypto", - "nym-outfox", "nym-sphinx-types", "serde", "thiserror", diff --git a/common/nymsphinx/framing/src/codec.rs b/common/nymsphinx/framing/src/codec.rs index 9cfc47103b..d29f9c43f3 100644 --- a/common/nymsphinx/framing/src/codec.rs +++ b/common/nymsphinx/framing/src/codec.rs @@ -5,8 +5,9 @@ use crate::packet::{FramedNymPacket, Header}; use bytes::{Buf, BufMut, BytesMut}; use nym_sphinx_params::packet_modes::InvalidPacketMode; use nym_sphinx_params::packet_sizes::{InvalidPacketSize, PacketSize}; -use nym_sphinx_types::SphinxPacket; +use nym_sphinx_params::PacketMode; use nym_sphinx_types::{NymPacket, NymPacketError, SphinxError}; +use nym_sphinx_types::{OutfoxError, OutfoxPacket, SphinxPacket}; use std::io; use thiserror::Error; use tokio_util::codec::{Decoder, Encoder}; @@ -22,6 +23,9 @@ pub enum NymCodecError { #[error("the actual sphinx packet was malformed - {0}")] MalformedSphinxPacket(#[from] SphinxError), + #[error("the actual outfox packet was malformed - {0}")] + MalformedOutfoxPacket(#[from] OutfoxError), + #[error("encountered an IO error - {0}")] IoError(#[from] io::Error), @@ -55,7 +59,8 @@ impl Encoder for NymCodec { fn encode(&mut self, item: FramedNymPacket, dst: &mut BytesMut) -> Result<(), Self::Error> { item.header.encode(dst); - dst.put(item.packet.to_bytes()?.as_slice()); + let packet_bytes = item.packet.to_bytes()?; + dst.put(packet_bytes.as_slice()); Ok(()) } } @@ -123,16 +128,21 @@ impl Decoder for NymCodec { // advance buffer past the header - at this point we have enough bytes src.advance(header.size()); - let sphinx_packet_bytes = src.split_to(packet_size); - - // here it could be debatable whether stream is corrupt or not, - // but let's go with the safer approach and assume it is. - let packet = SphinxPacket::from_bytes(&sphinx_packet_bytes)?; - let nymsphinx_packet = FramedNymPacket { - header, - packet: NymPacket::Sphinx(packet), + let packet_bytes = src.split_to(packet_size); + let packet = if let Some(slice) = packet_bytes.get(..) { + // here it could be debatable whether stream is corrupt or not, + // but let's go with the safer approach and assume it is. + match header.packet_mode { + PacketMode::Outfox => NymPacket::Outfox(OutfoxPacket::try_from(slice)?), + _ => NymPacket::Sphinx(SphinxPacket::from_bytes(slice)?), + } + } else { + return Ok(None); }; + // let packet = SphinxPacket::from_bytes(&sphinx_packet_bytes)?; + let nymsphinx_packet = FramedNymPacket { header, packet }; + // As per docs: // Before returning from the function, implementations should ensure that the buffer // has appropriate capacity in anticipation of future calls to decode. @@ -159,7 +169,6 @@ impl Decoder for NymCodec { }; } src.reserve(allocate_for_next_packet); - Ok(Some(nymsphinx_packet)) } } @@ -170,9 +179,33 @@ mod packet_encoding { use nym_sphinx_types::builder::SphinxPacketBuilder; use nym_sphinx_types::{ crypto, Delay as SphinxDelay, Destination, DestinationAddressBytes, Node, NodeAddressBytes, - DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, NODE_ADDRESS_LENGTH, + DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, NODE_ADDRESS_LENGTH, OUTFOX_PACKET_OVERHEAD, }; + fn make_valid_outfox_packet(size: PacketSize) -> OutfoxPacket { + let (_, node1_pk) = crypto::keygen(); + let node1 = Node::new( + NodeAddressBytes::from_bytes([5u8; NODE_ADDRESS_LENGTH]), + node1_pk, + ); + let (_, node2_pk) = crypto::keygen(); + let node2 = Node::new( + NodeAddressBytes::from_bytes([4u8; NODE_ADDRESS_LENGTH]), + node2_pk, + ); + let (_, node3_pk) = crypto::keygen(); + let node3 = Node::new( + NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]), + node3_pk, + ); + + let route = [node1, node2, node3]; + + let payload = vec![1; 48]; + + OutfoxPacket::build(&payload, &route, Some(size.size() - OUTFOX_PACKET_OVERHEAD)).unwrap() + } + fn make_valid_sphinx_packet(size: PacketSize) -> SphinxPacket { let (_, node1_pk) = crypto::keygen(); let node1 = Node::new( @@ -225,6 +258,27 @@ mod packet_encoding { assert_eq!(decoded.packet.to_bytes().unwrap(), sphinx_bytes) } + #[test] + fn whole_outfox_can_be_decoded_from_a_valid_encoded_instance() { + let header = Header::outfox(); + let packet = make_valid_outfox_packet(PacketSize::OutfoxRegularPacket); + let packet_bytes = packet.to_bytes().unwrap(); + + OutfoxPacket::try_from(packet_bytes.as_slice()).unwrap(); + + let packet = FramedNymPacket { + header, + packet: NymPacket::Outfox(packet), + }; + + let mut bytes = BytesMut::new(); + NymCodec.encode(packet, &mut bytes).unwrap(); + let decoded = NymCodec.decode(&mut bytes).unwrap().unwrap(); + + assert_eq!(decoded.header, header); + assert_eq!(decoded.packet.to_bytes().unwrap(), packet_bytes) + } + #[cfg(test)] mod decode_will_allocate_enough_bytes_for_next_call { use super::*; diff --git a/common/nymsphinx/framing/src/packet.rs b/common/nymsphinx/framing/src/packet.rs index 2ed3f16e1b..513131a44f 100644 --- a/common/nymsphinx/framing/src/packet.rs +++ b/common/nymsphinx/framing/src/packet.rs @@ -79,6 +79,14 @@ impl Header { pub(crate) const LEGACY_SIZE: usize = 2; pub(crate) const VERSIONED_SIZE: usize = 3; + pub fn outfox() -> Header { + Header { + packet_version: PacketVersion::default(), + packet_size: PacketSize::OutfoxRegularPacket, + packet_mode: PacketMode::Outfox, + } + } + pub(crate) fn size(&self) -> usize { if self.packet_version.is_legacy() { Self::LEGACY_SIZE diff --git a/common/nymsphinx/params/Cargo.toml b/common/nymsphinx/params/Cargo.toml index 7242935eac..bc9b40b9a8 100644 --- a/common/nymsphinx/params/Cargo.toml +++ b/common/nymsphinx/params/Cargo.toml @@ -13,4 +13,3 @@ serde = { workspace = true, features = ["derive"] } nym-crypto = { path = "../../crypto", features = ["hashing", "symmetric"] } nym-sphinx-types = { path = "../types" } -nym-outfox = { "path" = "../../../nym-outfox" } diff --git a/common/nymsphinx/params/src/packet_modes.rs b/common/nymsphinx/params/src/packet_modes.rs index 83c1ac405b..a5963ae241 100644 --- a/common/nymsphinx/params/src/packet_modes.rs +++ b/common/nymsphinx/params/src/packet_modes.rs @@ -22,6 +22,9 @@ pub enum PacketMode { /// Represents a VPN packet that should not be delayed and ideally cached pre-computed keys /// should be used for unwrapping data. Note that it does not offer the same level of anonymity. Vpn = 1, + + /// Abusing this to add Outfox support + Outfox = 2, } impl PacketMode { @@ -32,6 +35,10 @@ impl PacketMode { pub fn is_old_vpn(self) -> bool { self == PacketMode::Vpn } + + pub fn is_outfox(self) -> bool { + self == PacketMode::Outfox + } } impl TryFrom for PacketMode { @@ -41,6 +48,7 @@ impl TryFrom for PacketMode { match value { _ if value == (PacketMode::Mix as u8) => Ok(Self::Mix), _ if value == (PacketMode::Vpn as u8) => Ok(Self::Vpn), + _ if value == (PacketMode::Outfox as u8) => Ok(Self::Outfox), v => Err(InvalidPacketMode { received: v }), } } diff --git a/common/nymsphinx/params/src/packet_sizes.rs b/common/nymsphinx/params/src/packet_sizes.rs index 40f30af9e0..7c468dba63 100644 --- a/common/nymsphinx/params/src/packet_sizes.rs +++ b/common/nymsphinx/params/src/packet_sizes.rs @@ -2,9 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::FRAG_ID_LEN; -use nym_outfox::packet::OUTFOX_PACKET_OVERHEAD; use nym_sphinx_types::header::HEADER_SIZE; -use nym_sphinx_types::PAYLOAD_OVERHEAD_SIZE; +use nym_sphinx_types::{OUTFOX_PACKET_OVERHEAD, PAYLOAD_OVERHEAD_SIZE}; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::convert::TryFrom; @@ -164,6 +163,17 @@ impl TryFrom for PacketSize { _ if value == (PacketSize::ExtendedPacket8 as u8) => Ok(Self::ExtendedPacket8), _ if value == (PacketSize::ExtendedPacket16 as u8) => Ok(Self::ExtendedPacket16), _ if value == (PacketSize::ExtendedPacket32 as u8) => Ok(Self::ExtendedPacket32), + _ if value == (PacketSize::OutfoxRegularPacket as u8) => Ok(Self::OutfoxRegularPacket), + _ if value == (PacketSize::OutfoxAckPacket as u8) => Ok(Self::OutfoxAckPacket), + _ if value == (PacketSize::OutfoxExtendedPacket8 as u8) => { + Ok(Self::OutfoxExtendedPacket8) + } + _ if value == (PacketSize::OutfoxExtendedPacket16 as u8) => { + Ok(Self::OutfoxExtendedPacket16) + } + _ if value == (PacketSize::OutfoxExtendedPacket32 as u8) => { + Ok(Self::OutfoxExtendedPacket32) + } v => Err(InvalidPacketSize::UnknownPacketTag { received: v }), } } diff --git a/common/nymsphinx/types/Cargo.toml b/common/nymsphinx/types/Cargo.toml index cc9e9aab10..0fb12f3fd1 100644 --- a/common/nymsphinx/types/Cargo.toml +++ b/common/nymsphinx/types/Cargo.toml @@ -12,6 +12,3 @@ sphinx-packet = { version = "0.1.0" } serde = "1" nym-outfox = { path = "../../../nym-outfox" } thiserror = "1" - -[patch.crates-io] -sphinx-packet = { path = "../../../../sphinx" } diff --git a/common/nymsphinx/types/src/lib.rs b/common/nymsphinx/types/src/lib.rs index fa4bb95f85..000982e0ae 100644 --- a/common/nymsphinx/types/src/lib.rs +++ b/common/nymsphinx/types/src/lib.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -pub use nym_outfox::{error::OutfoxError, packet::OutfoxPacket}; +pub use nym_outfox::{error::OutfoxError, packet::OutfoxPacket, packet::OUTFOX_PACKET_OVERHEAD}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; // re-exporting types and constants available in sphinx pub use sphinx_packet::{ @@ -104,7 +104,7 @@ impl<'de> Visitor<'de> for NymPacketVisitor { { match SphinxPacket::from_bytes(v) { Ok(packet) => Ok(NymPacket::Sphinx(packet)), - Err(_) => match OutfoxPacket::from_bytes(v) { + Err(_) => match OutfoxPacket::try_from(v) { Ok(packet) => Ok(NymPacket::Outfox(packet)), Err(_) => Err(E::custom( "Could not deserialize Outfox nor Sphinx packet from bytes", diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index 2a6be45d4a..6bf76ee003 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -84,15 +84,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -1302,14 +1293,13 @@ dependencies = [ name = "nym-outfox" version = "0.1.0" dependencies = [ - "bincode", "blake3", "chacha20", "chacha20poly1305", "curve25519-dalek", + "fastrand", "getrandom 0.2.8", "rayon", - "serde", "sphinx-packet", "thiserror", "zeroize", diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index dccdd85593..d4ff7fca09 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -3569,14 +3569,13 @@ dependencies = [ name = "nym-outfox" version = "0.1.0" dependencies = [ - "bincode", "blake3", "chacha20", "chacha20poly1305", "curve25519-dalek", + "fastrand", "getrandom 0.2.8", "rayon", - "serde", "sphinx-packet", "thiserror", "zeroize", @@ -3772,7 +3771,6 @@ name = "nym-sphinx-params" version = "0.1.0" dependencies = [ "nym-crypto", - "nym-outfox", "nym-sphinx-types", "serde", "thiserror", diff --git a/nym-outfox/Cargo.toml b/nym-outfox/Cargo.toml index 5966282f7b..44df9b3487 100644 --- a/nym-outfox/Cargo.toml +++ b/nym-outfox/Cargo.toml @@ -15,8 +15,7 @@ chacha20poly1305 = "0.10.1" # Need this star over here to pull in js into getrandom getrandom = { version = "*", features = ["js"] } thiserror = "1" -serde = { version = "1", features = ["derive"] } -bincode = "1" +fastrand = "1.8" sphinx-packet = "0.1.0" @@ -26,4 +25,3 @@ sphinx-packet = { path = "../../../../sphinx" } [dev-dependencies] criterion = "0.4" -fastrand = "1.8" diff --git a/nym-outfox/src/error.rs b/nym-outfox/src/error.rs index 87fa1ca8f8..36f869bf87 100644 --- a/nym-outfox/src/error.rs +++ b/nym-outfox/src/error.rs @@ -1,5 +1,6 @@ use std::array::TryFromSliceError; +use crate::format::MIX_PARAMS_LEN; use crate::lion::MIN_MESSAGE_LEN; use chacha20::cipher::InvalidLength; use thiserror::Error; @@ -24,6 +25,6 @@ pub enum OutfoxError { #[from] source: TryFromSliceError, }, - #[error("Could not serialize OutfoxPacket!")] - Bincode, + #[error("Header length must be {MIX_PARAMS_LEN}, got {0}")] + InvalidHeaderLength(usize), } diff --git a/nym-outfox/src/format.rs b/nym-outfox/src/format.rs index 89778b9eb3..4615124de6 100644 --- a/nym-outfox/src/format.rs +++ b/nym-outfox/src/format.rs @@ -62,14 +62,21 @@ use chacha20poly1305::Tag; use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; use curve25519_dalek::montgomery::MontgomeryPoint; use curve25519_dalek::scalar::Scalar; -use serde::Deserialize; -use serde::Serialize; use sphinx_packet::route::Node; use std::convert::TryInto; -pub const GROUPELEMENTBYTES: usize = 32; -pub const TAGBYTES: usize = 16; +pub const GROUPELEMENTBYTES: u8 = 32; +pub const TAGBYTES: u8 = 16; +pub const MIX_PARAMS_LEN: usize = 5; + +pub const fn groupelementbytes() -> usize { + GROUPELEMENTBYTES as usize +} + +pub const fn tagbytes() -> usize { + TAGBYTES as usize +} use std::ops::Range; use std::u8; @@ -77,21 +84,48 @@ use std::u8; use crate::error::OutfoxError; use crate::lion::*; use crate::packet::DEFAULT_ROUTING_INFO_SIZE; +use std::convert::TryFrom; /// 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(Serialize, Deserialize)] +#[derive(Eq, PartialEq, Debug)] pub struct MixCreationParameters { /// The routing length is inner first, so \[0\] is the innermost routing length, etc (in bytes) /// In our stratified topology this will always be 3 - pub routing_information_length_by_stage: [usize; 3], + pub routing_information_length_by_stage: [u8; 3], /// The payload length (in bytes) - pub payload_length_bytes: usize, + pub payload_length_bytes: u16, +} + +impl TryFrom<&[u8]> for MixCreationParameters { + type Error = OutfoxError; + + fn try_from(v: &[u8]) -> Result { + if v.len() != MIX_PARAMS_LEN { + return Err(OutfoxError::InvalidHeaderLength(v.len())); + } + let (routing, payload) = v.split_at(3); + Ok(MixCreationParameters { + routing_information_length_by_stage: routing.try_into()?, + payload_length_bytes: u16::from_le_bytes(payload.try_into()?), + }) + } } impl MixCreationParameters { + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::with_capacity(5); + bytes.extend_from_slice(self.routing_information_length_by_stage.as_slice()); + bytes.extend_from_slice(&self.payload_length_bytes.to_le_bytes()); + bytes + } + + pub fn payload_length_bytes(&self) -> usize { + self.payload_length_bytes as usize + } + /// Create a set of parameters for a mix packet format. - pub fn new(payload_length_bytes: usize) -> MixCreationParameters { + pub fn new(payload_length_bytes: u16) -> MixCreationParameters { MixCreationParameters { routing_information_length_by_stage: [DEFAULT_ROUTING_INFO_SIZE; 3], payload_length_bytes, @@ -106,9 +140,9 @@ impl MixCreationParameters { /// The length of the buffer needed to build a packet. pub fn total_packet_length(&self) -> usize { - let mut len = self.payload_length_bytes; + let mut len = self.payload_length_bytes(); for stage_len in &self.routing_information_length_by_stage { - len += stage_len + GROUPELEMENTBYTES + TAGBYTES + len += *stage_len as usize + groupelementbytes() + tagbytes() } len } @@ -131,7 +165,7 @@ impl MixCreationParameters { return (total_size - inner_size..total_size, params); } else { - remaining_header_length_bytes += stage_len + GROUPELEMENTBYTES + TAGBYTES; + remaining_header_length_bytes += (stage_len + GROUPELEMENTBYTES + TAGBYTES) as u16; } } @@ -142,47 +176,59 @@ impl MixCreationParameters { /// A structure representing the parameters of a single stage of mixing. pub struct MixStageParameters { /// The routing information length for this stage of mixing - pub routing_information_length_bytes: usize, + pub routing_information_length_bytes: u8, /// The reamining header length for this stage of mixing - pub remaining_header_length_bytes: usize, + pub remaining_header_length_bytes: u16, /// The payload length - pub payload_length_bytes: usize, + pub payload_length_bytes: u16, } impl MixStageParameters { + pub fn routing_information_length_bytes(&self) -> usize { + self.routing_information_length_bytes as usize + } + + pub fn remaining_header_length_bytes(&self) -> usize { + self.remaining_header_length_bytes as usize + } + + pub fn payload_length_bytes(&self) -> usize { + self.payload_length_bytes as usize + } + pub fn incoming_packet_length(&self) -> usize { - GROUPELEMENTBYTES + TAGBYTES + self.outgoing_packet_length() + groupelementbytes() + tagbytes() + self.outgoing_packet_length() } pub fn outgoing_packet_length(&self) -> usize { - self.routing_information_length_bytes - + self.remaining_header_length_bytes - + self.payload_length_bytes + self.routing_information_length_bytes() + + self.remaining_header_length_bytes() + + self.payload_length_bytes() } pub fn pub_element_range(&self) -> Range { - 0..GROUPELEMENTBYTES + 0..groupelementbytes() } pub fn tag_range(&self) -> Range { - GROUPELEMENTBYTES..GROUPELEMENTBYTES + TAGBYTES + groupelementbytes()..groupelementbytes() + tagbytes() } pub fn routing_data_range(&self) -> Range { - GROUPELEMENTBYTES + TAGBYTES - ..GROUPELEMENTBYTES + TAGBYTES + self.routing_information_length_bytes + groupelementbytes() + tagbytes() + ..groupelementbytes() + tagbytes() + self.routing_information_length_bytes() } pub fn header_range(&self) -> Range { - GROUPELEMENTBYTES + TAGBYTES - ..GROUPELEMENTBYTES - + TAGBYTES - + self.routing_information_length_bytes - + self.remaining_header_length_bytes + groupelementbytes() + tagbytes() + ..groupelementbytes() + + tagbytes() + + self.routing_information_length_bytes() + + self.remaining_header_length_bytes() } pub fn payload_range(&self) -> Range { - self.incoming_packet_length() - self.payload_length_bytes..self.incoming_packet_length() + self.incoming_packet_length() - self.payload_length_bytes()..self.incoming_packet_length() } pub fn encode_mix_layer( @@ -202,10 +248,10 @@ impl MixStageParameters { }); } - if routing_data.len() != self.routing_information_length_bytes { + if routing_data.len() != self.routing_information_length_bytes() { return Err(OutfoxError::LenMismatch { expected: routing_data.len(), - got: self.routing_information_length_bytes, + got: self.routing_information_length_bytes(), }); } @@ -277,3 +323,25 @@ impl MixStageParameters { Ok(shared_key) } } + +#[cfg(test)] +mod test { + use super::MixCreationParameters; + use std::convert::TryFrom; + + #[test] + fn test_to_bytes() { + let mix_params = MixCreationParameters::new(1024); + assert_eq!(mix_params.to_bytes(), vec![32, 32, 32, 0, 4]) + } + + #[test] + fn test_from_bytes() { + let params_bytes = vec![32, 32, 32, 0, 4]; + let mix_params = MixCreationParameters::new(1024); + assert_eq!( + mix_params, + MixCreationParameters::try_from(params_bytes.as_slice()).unwrap() + ) + } +} diff --git a/nym-outfox/src/lib.rs b/nym-outfox/src/lib.rs index 8ff0a79fcc..b33e1fe036 100644 --- a/nym-outfox/src/lib.rs +++ b/nym-outfox/src/lib.rs @@ -1,4 +1,10 @@ +use std::iter::repeat_with; + pub mod error; pub mod format; pub mod lion; pub mod packet; + +pub fn randombytes(n: usize) -> Vec { + repeat_with(|| fastrand::u8(..)).take(n).collect() +} diff --git a/nym-outfox/src/packet.rs b/nym-outfox/src/packet.rs index aed90bf7bf..c5296ce850 100644 --- a/nym-outfox/src/packet.rs +++ b/nym-outfox/src/packet.rs @@ -1,52 +1,62 @@ -use serde::{Deserialize, Serialize}; -use std::ops::Range; +use std::{convert::TryFrom, ops::Range}; use crate::{ error::OutfoxError, - format::{MixCreationParameters, MixStageParameters, GROUPELEMENTBYTES, TAGBYTES}, + format::{ + groupelementbytes, tagbytes, MixCreationParameters, MixStageParameters, MIX_PARAMS_LEN, + }, + randombytes, }; use sphinx_packet::{packet::builder::DEFAULT_PAYLOAD_SIZE, route::Node}; pub const OUTFOX_PACKET_OVERHEAD: usize = - 3 * DEFAULT_ROUTING_INFO_SIZE + GROUPELEMENTBYTES + TAGBYTES; + MIX_PARAMS_LEN + (groupelementbytes() + tagbytes() + DEFAULT_ROUTING_INFO_SIZE as usize) * 3; -#[derive(Serialize, Deserialize)] +#[derive(Debug)] pub struct OutfoxPacket { mix_params: MixCreationParameters, payload: Vec, } -pub const DEFAULT_ROUTING_INFO_SIZE: usize = 32; +pub const DEFAULT_ROUTING_INFO_SIZE: u8 = 32; + +impl TryFrom<&[u8]> for OutfoxPacket { + type Error = OutfoxError; + + fn try_from(v: &[u8]) -> Result { + let (header, payload) = v.split_at(MIX_PARAMS_LEN); + Ok(OutfoxPacket { + mix_params: MixCreationParameters::try_from(header)?, + payload: payload.to_vec(), + }) + } +} impl OutfoxPacket { pub fn len(&self) -> usize { - self.mix_params().total_packet_length() + self.payload.len() + self.mix_params().total_packet_length() } pub fn is_empty(&self) -> bool { self.len() == 0 } - // TODO: Replace with lightweight methods pub fn to_bytes(&self) -> Result, OutfoxError> { - bincode::serialize(self).map_err(|_e| OutfoxError::Bincode) - } - - pub fn from_bytes(v: &[u8]) -> Result { - bincode::deserialize(v).map_err(|_| OutfoxError::Bincode) + let mut bytes = vec![]; + bytes.extend(self.mix_params.to_bytes()); + bytes.extend(self.payload.as_slice()); + Ok(bytes) } pub fn build( payload: &[u8], route: &[Node; 3], - user_secret_key: &[u8], + packet_size: Option, ) -> Result { - let mix_params = MixCreationParameters::new(DEFAULT_PAYLOAD_SIZE); - - // for node in route.iter() { - // mix_params.add_outer_layer(node.address.as_bytes_ref().len()); - // } + let secret_key = randombytes(32); + let packet_size = packet_size.unwrap_or(DEFAULT_PAYLOAD_SIZE); + let mix_params = MixCreationParameters::new(packet_size as u16); let padding = mix_params.total_packet_length() - payload.len(); let mut buffer = vec![0; padding]; @@ -54,7 +64,7 @@ impl OutfoxPacket { 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)?; + stage_params.encode_mix_layer(&mut buffer[range], &secret_key, node)?; } Ok(OutfoxPacket { diff --git a/nym-outfox/tests/unittests.rs b/nym-outfox/tests/unittests.rs index b085210756..099cd2cea1 100644 --- a/nym-outfox/tests/unittests.rs +++ b/nym-outfox/tests/unittests.rs @@ -6,22 +6,17 @@ mod tests { use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; use curve25519_dalek::scalar::Scalar; use nym_outfox::packet::OutfoxPacket; + use nym_outfox::randombytes; 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::TryFrom; use std::convert::TryInto; use nym_outfox::format::*; use nym_outfox::lion::*; - use std::iter::repeat_with; - - pub fn randombytes(n: usize) -> Vec { - repeat_with(|| fastrand::u8(..)).take(n).collect() - } - #[test] fn test_encode_decode() { let mix_params = MixStageParameters { @@ -81,8 +76,6 @@ mod tests { #[test] fn test_packet_params() { - let user_secret = randombytes(32); - let (node1_pk, node1_pub) = sphinx_packet::crypto::keygen(); let node1 = Node::new( NodeAddressBytes::from_bytes([0u8; NODE_ADDRESS_LENGTH]), @@ -101,9 +94,17 @@ mod tests { let route = [node1, node2, node3]; - let payload = randombytes(DEFAULT_PAYLOAD_SIZE); + let payload = randombytes(2048); - let mut packet = OutfoxPacket::build(&payload, &route, &user_secret).unwrap(); + let packet = OutfoxPacket::build(&payload, &route, Some(2048)).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(); packet.decode_mix_layer(2, &node1_pk.to_bytes()).unwrap(); packet.decode_mix_layer(1, &node2_pk.to_bytes()).unwrap(); diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index e79a3c4184..3565ae37e7 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -227,15 +227,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bip32" version = "0.3.0" @@ -3086,14 +3077,13 @@ dependencies = [ name = "nym-outfox" version = "0.1.0" dependencies = [ - "bincode", "blake3", "chacha20", "chacha20poly1305", "curve25519-dalek", + "fastrand", "getrandom 0.2.8", "rayon", - "serde", "sphinx-packet", "thiserror", "zeroize",