From a985136dfaf72e4609e7d474bb213b532900eccd Mon Sep 17 00:00:00 2001 From: Drazen Urch Date: Thu, 20 Apr 2023 23:59:40 +0200 Subject: [PATCH] Outfox rest compat (#3333) * Outfox forwarding compat * Tidy up interface * PacketSize compat --- Cargo.lock | 1 + .../real_traffic_stream.rs | 2 +- .../client-libs/gateway-client/src/client.rs | 6 +- .../client-libs/mixnet-client/src/client.rs | 8 +- .../mixnet-client/src/forwarder.rs | 7 +- .../src/packet_processor/processor.rs | 37 +++--- .../acknowledgements/src/surb_ack.rs | 33 +++-- .../anonymous-replies/src/reply_surb.rs | 6 +- common/nymsphinx/cover/src/lib.rs | 24 ++-- common/nymsphinx/forwarding/Cargo.toml | 1 + common/nymsphinx/forwarding/src/packet.rs | 99 +++++++-------- common/nymsphinx/framing/src/codec.rs | 113 +++++------------- common/nymsphinx/framing/src/packet.rs | 32 ++--- common/nymsphinx/params/src/lib.rs | 6 +- common/nymsphinx/params/src/packet_sizes.rs | 54 ++++++++- .../src/{packet_modes.rs => packet_types.rs} | 29 ++--- common/nymsphinx/src/preparer/mod.rs | 20 ++-- common/nymsphinx/src/preparer/payload.rs | 17 +-- common/nymsphinx/src/receiver.rs | 45 ------- common/nymsphinx/types/src/lib.rs | 44 ++++++- common/topology/src/error.rs | 3 + gateway/gateway-requests/src/types.rs | 9 +- .../connection_handler/authenticated.rs | 2 +- mixnode/src/node/packet_delayforwarder.rs | 35 +++--- nym-connect/desktop/Cargo.lock | 1 + nym-outfox/Cargo.toml | 5 - nym-outfox/src/packet.rs | 8 +- 27 files changed, 305 insertions(+), 342 deletions(-) rename common/nymsphinx/params/src/{packet_modes.rs => packet_types.rs} (51%) diff --git a/Cargo.lock b/Cargo.lock index 70a5dcfb19..dba9f79a9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4336,6 +4336,7 @@ dependencies = [ "nym-sphinx-addressing", "nym-sphinx-params", "nym-sphinx-types", + "thiserror", ] [[package]] diff --git a/common/client-core/src/client/real_messages_control/real_traffic_stream.rs b/common/client-core/src/client/real_messages_control/real_traffic_stream.rs index c7e5c80dfe..4ef8ae4539 100644 --- a/common/client-core/src/client/real_messages_control/real_traffic_stream.rs +++ b/common/client-core/src/client/real_messages_control/real_traffic_stream.rs @@ -136,7 +136,7 @@ impl From for RealMessage { impl RealMessage { pub(crate) fn packet_size(&self) -> usize { - self.mix_packet.sphinx_packet().len() + self.mix_packet.packet().len() } pub(crate) fn new(mix_packet: MixPacket, fragment_id: Option) -> Self { diff --git a/common/client-libs/gateway-client/src/client.rs b/common/client-libs/gateway-client/src/client.rs index c964dbe2d2..ccee80d659 100644 --- a/common/client-libs/gateway-client/src/client.rs +++ b/common/client-libs/gateway-client/src/client.rs @@ -571,7 +571,7 @@ impl GatewayClient { fn estimate_required_bandwidth(&self, packets: &[MixPacket]) -> i64 { packets .iter() - .map(|packet| packet.sphinx_packet().len()) + .map(|packet| packet.packet().len()) .sum::() as i64 } @@ -652,9 +652,9 @@ impl GatewayClient { if !self.authenticated { return Err(GatewayClientError::NotAuthenticated); } - if (mix_packet.sphinx_packet().len() as i64) > self.bandwidth_remaining { + if (mix_packet.packet().len() as i64) > self.bandwidth_remaining { return Err(GatewayClientError::NotEnoughBandwidth( - mix_packet.sphinx_packet().len() as i64, + mix_packet.packet().len() as i64, self.bandwidth_remaining, )); } diff --git a/common/client-libs/mixnet-client/src/client.rs b/common/client-libs/mixnet-client/src/client.rs index 6d43a5f436..4da308144f 100644 --- a/common/client-libs/mixnet-client/src/client.rs +++ b/common/client-libs/mixnet-client/src/client.rs @@ -7,7 +7,7 @@ use log::*; use nym_sphinx::addressing::nodes::NymNodeRoutingAddress; use nym_sphinx::framing::codec::NymCodec; use nym_sphinx::framing::packet::FramedNymPacket; -use nym_sphinx::params::PacketMode; +use nym_sphinx::params::PacketType; use nym_sphinx::NymPacket; use std::collections::HashMap; use std::io; @@ -52,7 +52,7 @@ pub trait SendWithoutResponse { &mut self, address: NymNodeRoutingAddress, packet: NymPacket, - packet_mode: PacketMode, + packet_type: PacketType, ) -> io::Result<()>; } @@ -198,11 +198,11 @@ impl SendWithoutResponse for Client { &mut self, address: NymNodeRoutingAddress, packet: NymPacket, - packet_mode: PacketMode, + packet_type: PacketType, ) -> io::Result<()> { trace!("Sending packet to {:?}", address); let framed_packet = - FramedNymPacket::new(packet, packet_mode, self.config.use_legacy_version); + FramedNymPacket::new(packet, packet_type, self.config.use_legacy_version); if let Some(sender) = self.conn_new.get_mut(&address) { if let Err(err) = sender.channel.try_send(framed_packet) { diff --git a/common/client-libs/mixnet-client/src/forwarder.rs b/common/client-libs/mixnet-client/src/forwarder.rs index 4defb056d7..8bbf5a47f6 100644 --- a/common/client-libs/mixnet-client/src/forwarder.rs +++ b/common/client-libs/mixnet-client/src/forwarder.rs @@ -6,7 +6,6 @@ use futures::channel::mpsc; use futures::StreamExt; use log::*; use nym_sphinx::forwarding::packet::MixPacket; -use nym_sphinx::NymPacket; use std::time::Duration; pub type MixForwardingSender = mpsc::UnboundedSender; @@ -60,14 +59,14 @@ impl PacketForwarder { trace!("Going to forward packet to {:?}", mix_packet.next_hop()); let next_hop = mix_packet.next_hop(); - let packet_mode = mix_packet.packet_mode(); - let sphinx_packet = mix_packet.into_sphinx_packet(); + let packet_type = mix_packet.packet_type(); + let packet = mix_packet.into_packet(); // we don't care about responses, we just want to fire packets // as quickly as possible if let Err(err) = self.mixnet_client - .send_without_response(next_hop, NymPacket::Sphinx(sphinx_packet), packet_mode) + .send_without_response(next_hop, packet, packet_type) { debug!("failed to forward the packet - {err}") } diff --git a/common/mixnode-common/src/packet_processor/processor.rs b/common/mixnode-common/src/packet_processor/processor.rs index c0295e2894..aa216cfb5f 100644 --- a/common/mixnode-common/src/packet_processor/processor.rs +++ b/common/mixnode-common/src/packet_processor/processor.rs @@ -8,10 +8,10 @@ use nym_sphinx_acknowledgements::surb_ack::SurbAck; use nym_sphinx_addressing::nodes::NymNodeRoutingAddress; use nym_sphinx_forwarding::packet::MixPacket; use nym_sphinx_framing::packet::FramedNymPacket; -use nym_sphinx_params::{PacketMode, PacketSize}; +use nym_sphinx_params::{PacketSize, PacketType}; use nym_sphinx_types::{ Delay as SphinxDelay, DestinationAddressBytes, NodeAddressBytes, NymPacket, Payload, - PrivateKey, ProcessedPacket, SphinxPacket, + PrivateKey, ProcessedPacket, }; use std::convert::TryFrom; use std::sync::Arc; @@ -53,7 +53,7 @@ impl SphinxPacketProcessor { feature = "cpucycles", instrument(skip(self, packet), fields(cpucycles)) )] - fn perform_initial_sphinx_packet_processing( + fn perform_initial_packet_processing( &self, packet: NymPacket, ) -> Result { @@ -75,14 +75,9 @@ impl SphinxPacketProcessor { received: FramedNymPacket, ) -> Result { measure!({ - let packet_mode = received.packet_mode(); let packet = received.into_inner(); - if packet_mode.is_old_vpn() { - return Err(MixProcessingError::ReceivedOldTypeVpnPacket); - } - - self.perform_initial_sphinx_packet_processing(packet) + self.perform_initial_packet_processing(packet) }) } @@ -90,14 +85,14 @@ impl SphinxPacketProcessor { /// and packs all the data in a way that can be easily sent to the next hop. fn process_forward_hop( &self, - packet: SphinxPacket, + packet: NymPacket, forward_address: NodeAddressBytes, delay: SphinxDelay, - packet_mode: PacketMode, + packet_type: PacketType, ) -> Result { let next_hop_address = NymNodeRoutingAddress::try_from(forward_address)?; - let mix_packet = MixPacket::new(next_hop_address, packet, packet_mode); + let mix_packet = MixPacket::new(next_hop_address, packet, packet_type); Ok(MixProcessingResult::ForwardHop(mix_packet, Some(delay))) } @@ -124,7 +119,7 @@ impl SphinxPacketProcessor { &self, data: Vec, packet_size: PacketSize, - packet_mode: PacketMode, + packet_type: PacketType, ) -> Result<(Option, Vec), MixProcessingError> { match packet_size { PacketSize::AckPacket | PacketSize::OutfoxAckPacket => { @@ -142,7 +137,7 @@ impl SphinxPacketProcessor { trace!("received a normal packet!"); let (ack_data, message) = self.split_hop_data_into_ack_and_message(data)?; let (ack_first_hop, ack_packet) = SurbAck::try_recover_first_hop_packet(&ack_data)?; - let forward_ack = MixPacket::new(ack_first_hop, ack_packet, packet_mode); + let forward_ack = MixPacket::new(ack_first_hop, ack_packet, packet_type); Ok((Some(forward_ack), message)) } } @@ -156,12 +151,12 @@ impl SphinxPacketProcessor { destination: DestinationAddressBytes, payload: Payload, packet_size: PacketSize, - packet_mode: PacketMode, + packet_type: PacketType, ) -> Result { let packet_message = payload.recover_plaintext()?; let (forward_ack, message) = - self.split_into_ack_and_message(packet_message, packet_size, packet_mode)?; + self.split_into_ack_and_message(packet_message, packet_size, packet_type)?; Ok(MixProcessingResult::FinalHop(ProcessedFinalHop { destination, @@ -176,16 +171,16 @@ impl SphinxPacketProcessor { &self, packet: ProcessedPacket, packet_size: PacketSize, - packet_mode: PacketMode, + packet_type: PacketType, ) -> Result { match packet { ProcessedPacket::ForwardHop(packet, address, delay) => { - self.process_forward_hop(*packet, address, delay, packet_mode) + self.process_forward_hop(NymPacket::Sphinx(*packet), address, delay, packet_type) } // right now there's no use for the surb_id included in the header - probably it should get removed from the // sphinx all together? ProcessedPacket::FinalHop(destination, _, payload) => { - self.process_final_hop(destination, payload, packet_size, packet_mode) + self.process_final_hop(destination, payload, packet_size, packet_type) } } } @@ -201,14 +196,14 @@ impl SphinxPacketProcessor { // explicit packet size will help to correctly parse final hop measure!({ let packet_size = received.packet_size(); - let packet_mode = received.packet_mode(); + let packet_type = received.packet_type(); // unwrap the sphinx packet and if possible and appropriate, cache keys let processed_packet = self.perform_initial_unwrapping(received)?; // for forward packets, extract next hop and set delay (but do NOT delay here) // for final packets, extract SURBAck - self.perform_final_processing(processed_packet, packet_size, packet_mode) + self.perform_final_processing(processed_packet, packet_size, packet_type) }) } } diff --git a/common/nymsphinx/acknowledgements/src/surb_ack.rs b/common/nymsphinx/acknowledgements/src/surb_ack.rs index 9331fbb26f..39d4fc47dd 100644 --- a/common/nymsphinx/acknowledgements/src/surb_ack.rs +++ b/common/nymsphinx/acknowledgements/src/surb_ack.rs @@ -10,11 +10,8 @@ use nym_sphinx_addressing::nodes::{ use nym_sphinx_params::packet_sizes::PacketSize; use nym_sphinx_params::DEFAULT_NUM_MIX_HOPS; use nym_sphinx_types::builder::SphinxPacketBuilder; -use nym_sphinx_types::SphinxError; -use nym_sphinx_types::{ - delays::{self, Delay}, - SphinxPacket, -}; +use nym_sphinx_types::delays::{self, Delay}; +use nym_sphinx_types::{NymPacket, NymPacketError}; use nym_topology::{NymTopology, NymTopologyError}; use rand::{CryptoRng, RngCore}; use std::convert::TryFrom; @@ -22,7 +19,7 @@ use std::time; use thiserror::Error; pub struct SurbAck { - surb_ack_packet: SphinxPacket, + surb_ack_packet: NymPacket, first_hop_address: NymNodeRoutingAddress, expected_total_delay: Delay, } @@ -35,8 +32,8 @@ pub enum SurbAckRecoveryError { #[error("could not extract first hop address information - {0}")] InvalidAddress(#[from] NymNodeRoutingAddressError), - #[error("the contained sphinx packet was not correctly formed - {0}")] - InvalidSphinxPacket(#[from] SphinxError), + #[error("packet: {0}")] + NymPacket(#[from] NymPacketError), } impl SurbAck { @@ -58,10 +55,12 @@ impl SurbAck { let surb_ack_payload = prepare_identifier(rng, ack_key, marshaled_fragment_id); - let surb_ack_packet = SphinxPacketBuilder::new() - .with_payload_size(PacketSize::AckPacket.payload_size()) - .build_packet(surb_ack_payload, &route, &destination, &delays) - .unwrap(); + let surb_ack_packet = NymPacket::Sphinx( + SphinxPacketBuilder::new() + .with_payload_size(PacketSize::AckPacket.payload_size()) + .build_packet(surb_ack_payload, &route, &destination, &delays) + .unwrap(), + ); // in our case, the last hop is a gateway that does NOT do any delays let expected_total_delay = delays.iter().take(delays.len() - 1).sum(); @@ -85,21 +84,21 @@ impl SurbAck { self.expected_total_delay } - pub fn prepare_for_sending(self) -> (Delay, Vec) { + pub fn prepare_for_sending(self) -> Result<(Delay, Vec), SurbAckRecoveryError> { // SURB_FIRST_HOP || SURB_ACK let surb_bytes: Vec<_> = self .first_hop_address .as_zero_padded_bytes(MAX_NODE_ADDRESS_UNPADDED_LEN) .into_iter() - .chain(self.surb_ack_packet.to_bytes().into_iter()) + .chain(self.surb_ack_packet.to_bytes()?.into_iter()) .collect(); - (self.expected_total_delay, surb_bytes) + Ok((self.expected_total_delay, surb_bytes)) } // partial reciprocal of `prepare_for_sending` performed by the gateway pub fn try_recover_first_hop_packet( b: &[u8], - ) -> Result<(NymNodeRoutingAddress, SphinxPacket), SurbAckRecoveryError> { + ) -> Result<(NymNodeRoutingAddress, NymPacket), SurbAckRecoveryError> { if b.len() != Self::len() { Err(SurbAckRecoveryError::InvalidPacketSize { received: b.len(), @@ -111,7 +110,7 @@ impl SurbAck { // TODO: this will be variable once/if we decide to introduce optimization described // in common/nymsphinx/chunking/src/lib.rs:available_plaintext_size() let address_offset = MAX_NODE_ADDRESS_UNPADDED_LEN; - let packet = SphinxPacket::from_bytes(&b[address_offset..])?; + let packet = NymPacket::sphinx_from_bytes(&b[address_offset..])?; Ok((address, packet)) } diff --git a/common/nymsphinx/anonymous-replies/src/reply_surb.rs b/common/nymsphinx/anonymous-replies/src/reply_surb.rs index e501623a81..e9b1f4ca14 100644 --- a/common/nymsphinx/anonymous-replies/src/reply_surb.rs +++ b/common/nymsphinx/anonymous-replies/src/reply_surb.rs @@ -7,7 +7,7 @@ use nym_sphinx_addressing::clients::Recipient; use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN}; use nym_sphinx_params::packet_sizes::PacketSize; use nym_sphinx_params::{ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS}; -use nym_sphinx_types::{delays, SURBMaterial, SphinxError, SphinxPacket, SURB}; +use nym_sphinx_types::{delays, NymPacket, SURBMaterial, SphinxError, SURB}; use nym_topology::{NymTopology, NymTopologyError}; use rand::{CryptoRng, RngCore}; use serde::de::{Error as SerdeError, Visitor}; @@ -173,7 +173,7 @@ impl ReplySurb { self, message: M, packet_size: PacketSize, - ) -> Result<(SphinxPacket, NymNodeRoutingAddress), ReplySurbError> { + ) -> Result<(NymPacket, NymNodeRoutingAddress), ReplySurbError> { let message_bytes = message.as_ref(); if message_bytes.len() != packet_size.plaintext_size() { return Err(ReplySurbError::UnpaddedMessageError); @@ -187,6 +187,6 @@ impl ReplySurb { let first_hop_address = NymNodeRoutingAddress::try_from(first_hop).unwrap(); - Ok((packet, first_hop_address)) + Ok((NymPacket::Sphinx(packet), first_hop_address)) } } diff --git a/common/nymsphinx/cover/src/lib.rs b/common/nymsphinx/cover/src/lib.rs index 608ef1a1ae..1ec35fe30f 100644 --- a/common/nymsphinx/cover/src/lib.rs +++ b/common/nymsphinx/cover/src/lib.rs @@ -3,7 +3,7 @@ use nym_crypto::shared_key::new_ephemeral_shared_key; use nym_crypto::symmetric::stream_cipher; -use nym_sphinx_acknowledgements::surb_ack::SurbAck; +use nym_sphinx_acknowledgements::surb_ack::{SurbAck, SurbAckRecoveryError}; use nym_sphinx_acknowledgements::AckKey; use nym_sphinx_addressing::clients::Recipient; use nym_sphinx_addressing::nodes::NymNodeRoutingAddress; @@ -11,10 +11,10 @@ use nym_sphinx_chunking::fragment::COVER_FRAG_ID; use nym_sphinx_forwarding::packet::MixPacket; use nym_sphinx_params::packet_sizes::PacketSize; use nym_sphinx_params::{ - PacketEncryptionAlgorithm, PacketHkdfAlgorithm, PacketMode, DEFAULT_NUM_MIX_HOPS, + PacketEncryptionAlgorithm, PacketHkdfAlgorithm, PacketType, DEFAULT_NUM_MIX_HOPS, }; use nym_sphinx_types::builder::SphinxPacketBuilder; -use nym_sphinx_types::{delays, SphinxError}; +use nym_sphinx_types::{delays, NymPacket}; use nym_topology::{NymTopology, NymTopologyError}; use rand::{CryptoRng, RngCore}; use std::convert::TryFrom; @@ -28,8 +28,8 @@ pub enum CoverMessageError { #[error("Could not construct cover message due to invalid topology - {0}")] InvalidTopologyError(#[from] NymTopologyError), - #[error("Could not construct a valid sphinx packet - {0}")] - SphinxError(#[from] SphinxError), + #[error("SurbAck: {0}")] + SurbAck(#[from] SurbAckRecoveryError), } pub fn generate_loop_cover_surb_ack( @@ -67,7 +67,7 @@ where // we don't care about total ack delay - we will not be retransmitting it anyway let (_, ack_bytes) = generate_loop_cover_surb_ack(rng, topology, ack_key, full_address, average_ack_delay)? - .prepare_for_sending(); + .prepare_for_sending()?; // cover message can't be distinguishable from a normal traffic so we have to go through // all the effort of key generation, encryption, etc. Note here we are generating shared key @@ -111,15 +111,17 @@ where let destination = full_address.as_sphinx_destination(); // once merged, that's an easy rng injection point for sphinx packets : ) - let packet = SphinxPacketBuilder::new() - .with_payload_size(packet_size.payload_size()) - .build_packet(packet_payload, &route, &destination, &delays) - .unwrap(); + let packet = NymPacket::Sphinx( + SphinxPacketBuilder::new() + .with_payload_size(packet_size.payload_size()) + .build_packet(packet_payload, &route, &destination, &delays) + .unwrap(), + ); let first_hop_address = NymNodeRoutingAddress::try_from(route.first().unwrap().address).unwrap(); - Ok(MixPacket::new(first_hop_address, packet, PacketMode::Mix)) + Ok(MixPacket::new(first_hop_address, packet, PacketType::Mix)) } /// Helper function used to determine if given message represents a loop cover message. diff --git a/common/nymsphinx/forwarding/Cargo.toml b/common/nymsphinx/forwarding/Cargo.toml index df071403a6..6509098398 100644 --- a/common/nymsphinx/forwarding/Cargo.toml +++ b/common/nymsphinx/forwarding/Cargo.toml @@ -12,3 +12,4 @@ nym-sphinx-addressing = { path = "../addressing" } nym-sphinx-params = { path = "../params" } nym-sphinx-types = { path = "../types" } nym-outfox = { path = "../../../nym-outfox" } +thiserror = "1" diff --git a/common/nymsphinx/forwarding/src/packet.rs b/common/nymsphinx/forwarding/src/packet.rs index 024b80b005..2276e9a5cd 100644 --- a/common/nymsphinx/forwarding/src/packet.rs +++ b/common/nymsphinx/forwarding/src/packet.rs @@ -2,42 +2,28 @@ // SPDX-License-Identifier: Apache-2.0 use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, NymNodeRoutingAddressError}; -use nym_sphinx_params::{PacketMode, PacketSize}; -use nym_sphinx_types::SphinxPacket; +use nym_sphinx_params::{PacketSize, PacketType}; +use nym_sphinx_types::{NymPacket, NymPacketError}; use std::convert::TryFrom; -use std::fmt::{self, Debug, Display, Formatter}; +use std::fmt::{self, Debug, Formatter}; +use thiserror::Error; -#[derive(Debug)] +#[derive(Debug, Error)] pub enum MixPacketFormattingError { + #[error("too few bytes provided to recover from bytes")] TooFewBytesProvided, - InvalidPacketMode, + #[error("provided packet mode is invalid")] + InvalidPacketType, + #[error("received request had invalid size - received {0}")] InvalidPacketSize(usize), + #[error("address field was incorrectly encoded")] InvalidAddress, + #[error("received sphinx packet was malformed")] MalformedSphinxPacket, + #[error("Packet: {0}")] + Packet(#[from] NymPacketError), } -impl Display for MixPacketFormattingError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - use MixPacketFormattingError::*; - match self { - TooFewBytesProvided => write!(f, "Too few bytes provided to recover from bytes"), - InvalidAddress => write!(f, "address field was incorrectly encoded"), - InvalidPacketSize(actual) => - write!( - f, - "received request had invalid size. (actual: {}, but expected one of: {} (ACK), {} (REGULAR), {}, {}, {} (EXTENDED))", - actual, PacketSize::AckPacket.size(), PacketSize::RegularPacket.size(), - PacketSize::ExtendedPacket8.size(), PacketSize::ExtendedPacket16.size(), - PacketSize::ExtendedPacket32.size() - ), - MalformedSphinxPacket => write!(f, "received sphinx packet was malformed"), - InvalidPacketMode => write!(f, "provided packet mode is invalid") - } - } -} - -impl std::error::Error for MixPacketFormattingError {} - impl From for MixPacketFormattingError { fn from(_: NymNodeRoutingAddressError) -> Self { MixPacketFormattingError::InvalidAddress @@ -46,19 +32,16 @@ impl From for MixPacketFormattingError { pub struct MixPacket { next_hop: NymNodeRoutingAddress, - sphinx_packet: SphinxPacket, - packet_mode: PacketMode, + packet: NymPacket, + packet_type: PacketType, } impl Debug for MixPacket { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!( f, - "MixPacket to {:?} with packet_mode {:?}. Sphinx header: {:?}, payload length: {}", - self.next_hop, - self.packet_mode, - self.sphinx_packet.header, - self.sphinx_packet.payload.len() + "MixPacket to {:?} with packet_type {:?}. Packet {:?}", + self.next_hop, self.packet_type, self.packet ) } } @@ -66,13 +49,13 @@ impl Debug for MixPacket { impl MixPacket { pub fn new( next_hop: NymNodeRoutingAddress, - sphinx_packet: SphinxPacket, - packet_mode: PacketMode, + packet: NymPacket, + packet_type: PacketType, ) -> Self { MixPacket { next_hop, - sphinx_packet, - packet_mode, + packet, + packet_type, } } @@ -80,52 +63,52 @@ impl MixPacket { self.next_hop } - pub fn sphinx_packet(&self) -> &SphinxPacket { - &self.sphinx_packet + pub fn packet(&self) -> &NymPacket { + &self.packet } - pub fn into_sphinx_packet(self) -> SphinxPacket { - self.sphinx_packet + pub fn into_packet(self) -> NymPacket { + self.packet } - pub fn packet_mode(&self) -> PacketMode { - self.packet_mode + pub fn packet_type(&self) -> PacketType { + self.packet_type } // the message is formatted as follows: - // PACKET_MODE || FIRST_HOP || SPHINX_PACKET + // packet_type || FIRST_HOP || packet pub fn try_from_bytes(b: &[u8]) -> Result { - let packet_mode = match PacketMode::try_from(b[0]) { + let packet_type = match PacketType::try_from(b[0]) { Ok(mode) => mode, - Err(_) => return Err(MixPacketFormattingError::InvalidPacketMode), + Err(_) => return Err(MixPacketFormattingError::InvalidPacketType), }; let next_hop = NymNodeRoutingAddress::try_from_bytes(&b[1..])?; let addr_offset = next_hop.bytes_min_len(); - let sphinx_packet_data = &b[addr_offset + 1..]; - let packet_size = sphinx_packet_data.len(); + let packet_data = &b[addr_offset + 1..]; + let packet_size = packet_data.len(); if PacketSize::get_type(packet_size).is_err() { Err(MixPacketFormattingError::InvalidPacketSize(packet_size)) } else { - let sphinx_packet = match SphinxPacket::from_bytes(sphinx_packet_data) { - Ok(packet) => packet, - Err(_) => return Err(MixPacketFormattingError::MalformedSphinxPacket), + let packet = match packet_type { + PacketType::Outfox => NymPacket::outfox_from_bytes(packet_data)?, + _ => NymPacket::sphinx_from_bytes(packet_data)?, }; Ok(MixPacket { next_hop, - sphinx_packet, - packet_mode, + packet, + packet_type, }) } } - pub fn into_bytes(self) -> Vec { - std::iter::once(self.packet_mode as u8) + pub fn into_bytes(self) -> Result, MixPacketFormattingError> { + Ok(std::iter::once(self.packet_type as u8) .chain(self.next_hop.as_bytes().into_iter()) - .chain(self.sphinx_packet.to_bytes().into_iter()) - .collect() + .chain(self.packet.to_bytes()?.into_iter()) + .collect()) } } diff --git a/common/nymsphinx/framing/src/codec.rs b/common/nymsphinx/framing/src/codec.rs index d29f9c43f3..07ac62ec44 100644 --- a/common/nymsphinx/framing/src/codec.rs +++ b/common/nymsphinx/framing/src/codec.rs @@ -3,11 +3,10 @@ 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_params::PacketMode; -use nym_sphinx_types::{NymPacket, NymPacketError, SphinxError}; -use nym_sphinx_types::{OutfoxError, OutfoxPacket, SphinxPacket}; +use nym_sphinx_params::packet_types::InvalidPacketType; +use nym_sphinx_params::PacketType; +use nym_sphinx_types::{NymPacket, NymPacketError}; use std::io; use thiserror::Error; use tokio_util::codec::{Decoder, Encoder}; @@ -18,13 +17,7 @@ pub enum NymCodecError { InvalidPacketSize(#[from] InvalidPacketSize), #[error("the packet mode information was malformed - {0}")] - InvalidPacketMode(#[from] InvalidPacketMode), - - #[error("the actual sphinx packet was malformed - {0}")] - MalformedSphinxPacket(#[from] SphinxError), - - #[error("the actual outfox packet was malformed - {0}")] - MalformedOutfoxPacket(#[from] OutfoxError), + InvalidPacketType(#[from] InvalidPacketType), #[error("encountered an IO error - {0}")] IoError(#[from] io::Error), @@ -46,17 +39,6 @@ pub struct NymCodec; impl Encoder for NymCodec { type Error = NymCodecError; - // fn encode_serde(&mut self, item: FramedNymPacket, dst: &mut BytesMut) -> Result<(), Self::Error> { - // let encoded_size = bincode::serialized_size(&item).unwrap() as u32; - // dst.put_u32(encoded_size); - // dst.put( - // bincode::serialize(&item) - // .map_err(|_| NymCodecError::ToBytes)? - // .as_slice(), - // ); - // Ok(()) - // } - fn encode(&mut self, item: FramedNymPacket, dst: &mut BytesMut) -> Result<(), Self::Error> { item.header.encode(dst); let packet_bytes = item.packet.to_bytes()?; @@ -69,39 +51,6 @@ impl Decoder for NymCodec { type Item = FramedNymPacket; type Error = NymCodecError; - // fn decode_serde(&mut self, src: &mut BytesMut) -> Result, Self::Error> { - // if src.is_empty() { - // // can't do anything if we have no bytes, but let's reserve enough for the most - // // conservative case, i.e. receiving an ack packet - // src.reserve(Header::LEGACY_SIZE + PacketSize::AckPacket.size()); - // return Ok(None); - // } - - // let next_size = if let Some(size) = src.get(..4) { - // u32::from_be_bytes(size.try_into().unwrap()) as usize - // } else { - // 0 - // }; - - // if next_size == 0 { - // return Ok(None); - // } - - // if let Some(next_packet) = src.get(4..next_size + 4) { - // match bincode::deserialize::(next_packet) { - // Ok(packet) => { - // src.advance(next_size + 4); - // return Ok(Some(packet)); - // } - // Err(_) => { - // println!("Could not decode packet"); - // return Ok(None); - // } - // } - // } - // Ok(None) - // } - fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { if src.is_empty() { // can't do anything if we have no bytes, but let's reserve enough for the most @@ -132,9 +81,9 @@ impl Decoder for NymCodec { 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)?), + match header.packet_type { + PacketType::Outfox => NymPacket::outfox_from_bytes(slice)?, + PacketType::Mix => NymPacket::sphinx_from_bytes(slice)?, } } else { return Ok(None); @@ -176,13 +125,12 @@ impl Decoder for NymCodec { #[cfg(test)] mod packet_encoding { use super::*; - 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, OUTFOX_PACKET_OVERHEAD, + DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, NODE_ADDRESS_LENGTH, }; - fn make_valid_outfox_packet(size: PacketSize) -> OutfoxPacket { + fn make_valid_outfox_packet(size: PacketSize) -> NymPacket { let (_, node1_pk) = crypto::keygen(); let node1 = Node::new( NodeAddressBytes::from_bytes([5u8; NODE_ADDRESS_LENGTH]), @@ -199,14 +147,14 @@ mod packet_encoding { node3_pk, ); - let route = [node1, node2, node3]; + let route = &[node1, node2, node3]; let payload = vec![1; 48]; - OutfoxPacket::build(&payload, &route, Some(size.size() - OUTFOX_PACKET_OVERHEAD)).unwrap() + NymPacket::outfox_build(payload, route, Some(size.plaintext_size())).unwrap() } - fn make_valid_sphinx_packet(size: PacketSize) -> SphinxPacket { + fn make_valid_sphinx_packet(size: PacketSize) -> NymPacket { let (_, node1_pk) = crypto::keygen(); let node1 = Node::new( NodeAddressBytes::from_bytes([5u8; NODE_ADDRESS_LENGTH]), @@ -233,9 +181,7 @@ mod packet_encoding { SphinxDelay::new_from_nanos(42), SphinxDelay::new_from_nanos(42), ]; - SphinxPacketBuilder::new() - .with_payload_size(size.payload_size()) - .build_packet(b"foomp", &route, &destination, &delays) + NymPacket::sphinx_build(size.payload_size(), b"foomp", &route, &destination, &delays) .unwrap() } @@ -243,11 +189,11 @@ mod packet_encoding { fn whole_packet_can_be_decoded_from_a_valid_encoded_instance() { let header = Default::default(); let sphinx_packet = make_valid_sphinx_packet(Default::default()); - let sphinx_bytes = sphinx_packet.to_bytes(); + let sphinx_bytes = sphinx_packet.to_bytes().unwrap(); let packet = FramedNymPacket { header, - packet: NymPacket::Sphinx(sphinx_packet), + packet: sphinx_packet, }; let mut bytes = BytesMut::new(); @@ -264,12 +210,9 @@ mod packet_encoding { let packet = make_valid_outfox_packet(PacketSize::OutfoxRegularPacket); let packet_bytes = packet.to_bytes().unwrap(); - OutfoxPacket::try_from(packet_bytes.as_slice()).unwrap(); + NymPacket::outfox_from_bytes(packet_bytes.as_slice()).unwrap(); - let packet = FramedNymPacket { - header, - packet: NymPacket::Outfox(packet), - }; + let packet = FramedNymPacket { header, packet }; let mut bytes = BytesMut::new(); NymCodec.encode(packet, &mut bytes).unwrap(); @@ -283,7 +226,7 @@ mod packet_encoding { mod decode_will_allocate_enough_bytes_for_next_call { use super::*; use nym_sphinx_params::packet_version::PacketVersion; - use nym_sphinx_params::PacketMode; + use nym_sphinx_params::PacketType; #[test] fn for_empty_bytes() { @@ -355,7 +298,7 @@ mod packet_encoding { packet_version: PacketVersion::Legacy, ..Default::default() }, - packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())), + packet: make_valid_sphinx_packet(Default::default()), }; let mut bytes = BytesMut::new(); @@ -372,7 +315,7 @@ mod packet_encoding { // if full frame is used exactly, there should be enough space for header + ack packet let packet = FramedNymPacket { header: Header::default(), - packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())), + packet: make_valid_sphinx_packet(Default::default()), }; let mut bytes = BytesMut::new(); @@ -401,13 +344,13 @@ mod packet_encoding { packet_version: PacketVersion::Legacy, ..Default::default() }, - packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())), + packet: make_valid_sphinx_packet(Default::default()), }; let mut bytes = BytesMut::new(); NymCodec.encode(first_packet, &mut bytes).unwrap(); bytes.put_u8(packet_size as u8); - bytes.put_u8(PacketMode::default() as u8); + bytes.put_u8(PacketType::default() as u8); assert!(NymCodec.decode(&mut bytes).unwrap().is_some()); assert!(bytes.capacity() >= Header::LEGACY_SIZE + packet_size.size()) @@ -428,14 +371,14 @@ mod packet_encoding { for packet_size in packet_sizes { let first_packet = FramedNymPacket { header: Header::default(), - packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())), + packet: make_valid_sphinx_packet(Default::default()), }; let mut bytes = BytesMut::new(); NymCodec.encode(first_packet, &mut bytes).unwrap(); bytes.put_u8(PacketVersion::new_versioned(123).as_u8().unwrap()); bytes.put_u8(packet_size as u8); - bytes.put_u8(PacketMode::default() as u8); + bytes.put_u8(PacketType::default() as u8); assert!(NymCodec.decode(&mut bytes).unwrap().is_some()); // assert!(bytes.capacity() >= Header::VERSIONED_SIZE + packet_size.size()) @@ -447,12 +390,12 @@ mod packet_encoding { fn can_decode_two_packets_immediately() { let packet1 = FramedNymPacket { header: Header::default(), - packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())), + packet: make_valid_sphinx_packet(Default::default()), }; let packet2 = FramedNymPacket { header: Header::default(), - packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())), + packet: make_valid_sphinx_packet(Default::default()), }; let mut bytes = BytesMut::new(); @@ -469,12 +412,12 @@ mod packet_encoding { fn can_decode_two_packets_in_separate_calls() { let packet1 = FramedNymPacket { header: Header::default(), - packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())), + packet: make_valid_sphinx_packet(Default::default()), }; let packet2 = FramedNymPacket { header: Header::default(), - packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())), + packet: make_valid_sphinx_packet(Default::default()), }; let mut bytes = BytesMut::new(); diff --git a/common/nymsphinx/framing/src/packet.rs b/common/nymsphinx/framing/src/packet.rs index 3d6d865b01..0ccef02b67 100644 --- a/common/nymsphinx/framing/src/packet.rs +++ b/common/nymsphinx/framing/src/packet.rs @@ -5,7 +5,7 @@ use crate::codec::NymCodecError; use bytes::{BufMut, BytesMut}; use nym_sphinx_params::packet_sizes::PacketSize; use nym_sphinx_params::packet_version::PacketVersion; -use nym_sphinx_params::PacketMode; +use nym_sphinx_params::PacketType; use nym_sphinx_types::NymPacket; use std::convert::TryFrom; @@ -19,7 +19,7 @@ pub struct FramedNymPacket { } impl FramedNymPacket { - pub fn new(packet: NymPacket, packet_mode: PacketMode, use_legacy_version: bool) -> Self { + pub fn new(packet: NymPacket, packet_type: PacketType, use_legacy_version: bool) -> Self { // If this fails somebody is using the library in a super incorrect way, because they // already managed to somehow create a sphinx packet let packet_size = PacketSize::get_type(packet.len()).unwrap(); @@ -28,7 +28,7 @@ impl FramedNymPacket { header: Header { packet_version: PacketVersion::new(use_legacy_version), packet_size, - packet_mode, + packet_type, }, packet, } @@ -42,8 +42,8 @@ impl FramedNymPacket { self.header.packet_size } - pub fn packet_mode(&self) -> PacketMode { - self.header.packet_mode + pub fn packet_type(&self) -> PacketType { + self.header.packet_type } pub fn into_inner(self) -> NymPacket { @@ -69,9 +69,9 @@ pub struct Header { /// /// TODO: ask @AP whether this can be sent like this - could it introduce some anonymity issues? /// (note: this will be behind some encryption, either something implemented by us or some SSL action) - // Note: currently packet_mode is deprecated but is still left as a concept behind to not break + // Note: currently packet_type is deprecated but is still left as a concept behind to not break // compatibility with existing network - pub(crate) packet_mode: PacketMode, + pub(crate) packet_type: PacketType, } impl Header { @@ -82,7 +82,7 @@ impl Header { Header { packet_version: PacketVersion::default(), packet_size: PacketSize::OutfoxRegularPacket, - packet_mode: PacketMode::Outfox, + packet_type: PacketType::Outfox, } } @@ -103,7 +103,7 @@ impl Header { } dst.put_u8(self.packet_size as u8); - dst.put_u8(self.packet_mode as u8); + dst.put_u8(self.packet_type as u8); // reserve bytes for the actual packet dst.reserve(self.packet_size.size()); } @@ -120,7 +120,7 @@ impl Header { Ok(Some(Header { packet_version, packet_size: PacketSize::try_from(src[0])?, - packet_mode: PacketMode::try_from(src[1])?, + packet_type: PacketType::try_from(src[1])?, })) } else if src.len() < Self::VERSIONED_SIZE { // we're missing that 1 byte to read the full header... @@ -130,7 +130,7 @@ impl Header { Ok(Some(Header { packet_version, packet_size: PacketSize::try_from(src[1])?, - packet_mode: PacketMode::try_from(src[2])?, + packet_type: PacketType::try_from(src[2])?, })) } } @@ -161,7 +161,7 @@ mod header_encoding { [ PacketVersion::new_versioned(123).as_u8().unwrap(), unknown_packet_size, - PacketMode::default() as u8, + PacketType::default() as u8, ] .as_ref(), ); @@ -169,12 +169,12 @@ mod header_encoding { } #[test] - fn decoding_will_fail_for_unknown_packet_mode() { - let unknown_packet_mode: u8 = 255; + fn decoding_will_fail_for_unknown_packet_type() { + let unknown_packet_type: u8 = 255; // make sure this is still 'unknown' for if we make changes in the future - assert!(PacketMode::try_from(unknown_packet_mode).is_err()); + assert!(PacketType::try_from(unknown_packet_type).is_err()); - let mut bytes = BytesMut::from([PacketSize::default() as u8, unknown_packet_mode].as_ref()); + let mut bytes = BytesMut::from([PacketSize::default() as u8, unknown_packet_type].as_ref()); assert!(Header::decode(&mut bytes).is_err()) } diff --git a/common/nymsphinx/params/src/lib.rs b/common/nymsphinx/params/src/lib.rs index 5df4db1a2a..7ad247dfa5 100644 --- a/common/nymsphinx/params/src/lib.rs +++ b/common/nymsphinx/params/src/lib.rs @@ -8,11 +8,11 @@ use nym_crypto::ctr; type Aes128Ctr = ctr::Ctr64BE; // Re-export for ease of use -pub use packet_modes::PacketMode; pub use packet_sizes::PacketSize; +pub use packet_types::PacketType; -pub mod packet_modes; pub mod packet_sizes; +pub mod packet_types; pub mod packet_version; // If somebody can provide an argument why it might be reasonable to have more than 255 mix hops, @@ -29,7 +29,7 @@ pub type SerializedFragmentIdentifier = [u8; FRAG_ID_LEN]; // when packet header gets serialized, the following bytes (in that order) are put onto the wire: // - packet_version (starting with v1.1.0) // - packet_size indicator -// - packet_mode +// - packet_type // it also just so happens that the only valid values for packet_size indicator include values 1-6 // therefore if we receive byte `7` (or larger than that) we'll know we received a versioned packet, // otherwise we should treat it as legacy diff --git a/common/nymsphinx/params/src/packet_sizes.rs b/common/nymsphinx/params/src/packet_sizes.rs index 7c468dba63..a55532301b 100644 --- a/common/nymsphinx/params/src/packet_sizes.rs +++ b/common/nymsphinx/params/src/packet_sizes.rs @@ -3,7 +3,7 @@ use crate::FRAG_ID_LEN; use nym_sphinx_types::header::HEADER_SIZE; -use nym_sphinx_types::{OUTFOX_PACKET_OVERHEAD, PAYLOAD_OVERHEAD_SIZE}; +use nym_sphinx_types::{MIX_PARAMS_LEN, OUTFOX_PACKET_OVERHEAD, PAYLOAD_OVERHEAD_SIZE}; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::convert::TryFrom; @@ -195,12 +195,42 @@ impl PacketSize { } } + pub const fn header_size(&self) -> usize { + match self { + PacketSize::RegularPacket + | PacketSize::AckPacket + | PacketSize::ExtendedPacket8 + | PacketSize::ExtendedPacket16 + | PacketSize::ExtendedPacket32 => HEADER_SIZE, + PacketSize::OutfoxRegularPacket + | PacketSize::OutfoxAckPacket + | PacketSize::OutfoxExtendedPacket8 + | PacketSize::OutfoxExtendedPacket16 + | PacketSize::OutfoxExtendedPacket32 => MIX_PARAMS_LEN, + } + } + + pub const fn payload_overhead(&self) -> usize { + match self { + PacketSize::RegularPacket + | PacketSize::AckPacket + | PacketSize::ExtendedPacket8 + | PacketSize::ExtendedPacket16 + | PacketSize::ExtendedPacket32 => PAYLOAD_OVERHEAD_SIZE, + PacketSize::OutfoxRegularPacket + | PacketSize::OutfoxAckPacket + | PacketSize::OutfoxExtendedPacket8 + | PacketSize::OutfoxExtendedPacket16 + | PacketSize::OutfoxExtendedPacket32 => OUTFOX_PACKET_OVERHEAD - MIX_PARAMS_LEN, + } + } + pub const fn plaintext_size(self) -> usize { - self.size() - HEADER_SIZE - PAYLOAD_OVERHEAD_SIZE + self.size() - self.header_size() - self.payload_overhead() } pub const fn payload_size(self) -> usize { - self.size() - HEADER_SIZE + self.size() - self.header_size() } pub fn get_type(size: usize) -> Result { @@ -214,6 +244,16 @@ impl PacketSize { Ok(PacketSize::ExtendedPacket16) } else if PacketSize::ExtendedPacket32.size() == size { Ok(PacketSize::ExtendedPacket32) + } else if PacketSize::OutfoxRegularPacket.size() == size { + Ok(PacketSize::OutfoxRegularPacket) + } else if PacketSize::OutfoxAckPacket.size() == size { + Ok(PacketSize::OutfoxAckPacket) + } else if PacketSize::OutfoxExtendedPacket8.size() == size { + Ok(PacketSize::OutfoxExtendedPacket8) + } else if PacketSize::OutfoxExtendedPacket16.size() == size { + Ok(PacketSize::OutfoxExtendedPacket16) + } else if PacketSize::OutfoxExtendedPacket32.size() == size { + Ok(PacketSize::OutfoxExtendedPacket32) } else { Err(InvalidPacketSize::UnknownPacketSize { received: size }) } @@ -243,8 +283,12 @@ impl PacketSize { } pub fn get_type_from_plaintext(plaintext_size: usize) -> Result { - let packet_size = plaintext_size + SPHINX_PACKET_OVERHEAD; - Self::get_type(packet_size) + let sphinx_packet_size = plaintext_size + SPHINX_PACKET_OVERHEAD; + let outfox_packet_size = plaintext_size + OUTFOX_PACKET_OVERHEAD; + match Self::get_type(sphinx_packet_size) { + Ok(t) => Ok(t), + Err(_) => Self::get_type(outfox_packet_size), + } } } diff --git a/common/nymsphinx/params/src/packet_modes.rs b/common/nymsphinx/params/src/packet_types.rs similarity index 51% rename from common/nymsphinx/params/src/packet_modes.rs rename to common/nymsphinx/params/src/packet_types.rs index a5963ae241..869173735e 100644 --- a/common/nymsphinx/params/src/packet_modes.rs +++ b/common/nymsphinx/params/src/packet_types.rs @@ -7,49 +7,40 @@ use thiserror::Error; #[derive(Error, Debug)] #[error("{received} is not a valid packet mode tag")] -pub struct InvalidPacketMode { +pub struct InvalidPacketType { received: u8, } #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub enum PacketMode { +pub enum PacketType { /// Represents 'normal' packet sent through the network that should be delayed by an appropriate /// value at each hop. #[default] Mix = 0, - /// 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 { +impl PacketType { pub fn is_mix(self) -> bool { - self == PacketMode::Mix - } - - pub fn is_old_vpn(self) -> bool { - self == PacketMode::Vpn + self == PacketType::Mix } pub fn is_outfox(self) -> bool { - self == PacketMode::Outfox + self == PacketType::Outfox } } -impl TryFrom for PacketMode { - type Error = InvalidPacketMode; +impl TryFrom for PacketType { + type Error = InvalidPacketType; fn try_from(value: u8) -> Result { 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 }), + _ if value == (PacketType::Mix as u8) => Ok(Self::Mix), + _ if value == (PacketType::Outfox as u8) => Ok(Self::Outfox), + v => Err(InvalidPacketType { received: v }), } } } diff --git a/common/nymsphinx/src/preparer/mod.rs b/common/nymsphinx/src/preparer/mod.rs index e06a9f44b9..74916cc7a5 100644 --- a/common/nymsphinx/src/preparer/mod.rs +++ b/common/nymsphinx/src/preparer/mod.rs @@ -15,7 +15,7 @@ use nym_sphinx_forwarding::packet::MixPacket; use nym_sphinx_params::packet_sizes::PacketSize; use nym_sphinx_params::{ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS}; use nym_sphinx_types::builder::SphinxPacketBuilder; -use nym_sphinx_types::{delays, Delay}; +use nym_sphinx_types::{delays, Delay, NymPacket}; use nym_topology::{NymTopology, NymTopologyError}; use rand::{CryptoRng, Rng}; use std::convert::TryFrom; @@ -130,8 +130,11 @@ pub trait FragmentPreparer { self.generate_surb_ack(packet_sender, fragment_identifier, topology, ack_key)?; let ack_delay = surb_ack.expected_total_delay(); - let packet_payload = NymsphinxPayloadBuilder::new(fragment, surb_ack) - .build_reply(reply_surb.encryption_key()); + let packet_payload = match NymsphinxPayloadBuilder::new(fragment, surb_ack) + .build_reply(reply_surb.encryption_key()) { + Ok(payload) => payload, + Err(_e) => return Err(NymTopologyError::PayloadBuilder) + }; // the unwrap here is fine as the failures can only originate from attempting to use invalid payload lengths // and we just very carefully constructed a (presumably) valid one @@ -191,8 +194,11 @@ pub trait FragmentPreparer { self.generate_surb_ack(packet_sender, fragment_identifier, topology, ack_key)?; let ack_delay = surb_ack.expected_total_delay(); - let packet_payload = NymsphinxPayloadBuilder::new(fragment, surb_ack) - .build_regular(self.rng(), packet_recipient.encryption_key()); + let packet_payload = match NymsphinxPayloadBuilder::new(fragment, surb_ack) + .build_regular(self.rng(), packet_recipient.encryption_key()) { + Ok(payload) => payload, + Err(_e) => return Err(NymTopologyError::PayloadBuilder) + }; // generate pseudorandom route for the packet let hops = self.num_mix_hops(); @@ -206,10 +212,10 @@ pub trait FragmentPreparer { // create the actual sphinx packet here. With valid route and correct payload size, // there's absolutely no reason for this call to fail. - let sphinx_packet = SphinxPacketBuilder::new() + let sphinx_packet = NymPacket::Sphinx(SphinxPacketBuilder::new() .with_payload_size(packet_size.payload_size()) .build_packet(packet_payload, &route, &destination, &delays) - .unwrap(); + .unwrap()); // from the previously constructed route extract the first hop let first_hop_address = diff --git a/common/nymsphinx/src/preparer/payload.rs b/common/nymsphinx/src/preparer/payload.rs index 60786d3f11..73f0c4889c 100644 --- a/common/nymsphinx/src/preparer/payload.rs +++ b/common/nymsphinx/src/preparer/payload.rs @@ -6,7 +6,7 @@ use nym_crypto::asymmetric::encryption; use nym_crypto::shared_key::new_ephemeral_shared_key; use nym_crypto::symmetric::stream_cipher; use nym_crypto::symmetric::stream_cipher::CipherKey; -use nym_sphinx_acknowledgements::surb_ack::SurbAck; +use nym_sphinx_acknowledgements::surb_ack::{SurbAck, SurbAckRecoveryError}; use nym_sphinx_anonymous_replies::SurbEncryptionKey; use nym_sphinx_chunking::fragment::Fragment; use nym_sphinx_params::{ @@ -28,11 +28,11 @@ impl NymsphinxPayloadBuilder { self, packet_encryption_key: &CipherKey, variant_data: impl IntoIterator, - ) -> NymsphinxPayload + ) -> Result where C: StreamCipher + KeyIvInit, { - let (_, surb_ack_bytes) = self.surb_ack.prepare_for_sending(); + let (_, surb_ack_bytes) = self.surb_ack.prepare_for_sending()?; let mut fragment_data = self.fragment.into_bytes(); stream_cipher::encrypt_in_place::( @@ -46,16 +46,19 @@ impl NymsphinxPayloadBuilder { // where variant-specific data is as follows: // for replies it would be the digest of the encryption key used // for 'regular' messages it would be the public component used in DH later used in the KDF - NymsphinxPayload( + Ok(NymsphinxPayload( surb_ack_bytes .into_iter() .chain(variant_data.into_iter()) .chain(fragment_data.into_iter()) .collect(), - ) + )) } - pub fn build_reply(self, packet_encryption_key: &SurbEncryptionKey) -> NymsphinxPayload { + pub fn build_reply( + self, + packet_encryption_key: &SurbEncryptionKey, + ) -> Result { let key_digest = packet_encryption_key.compute_digest(); self.build::( packet_encryption_key.inner(), @@ -67,7 +70,7 @@ impl NymsphinxPayloadBuilder { self, rng: &mut R, recipient_encryption_key: &encryption::PublicKey, - ) -> NymsphinxPayload + ) -> Result where R: RngCore + CryptoRng, { diff --git a/common/nymsphinx/src/receiver.rs b/common/nymsphinx/src/receiver.rs index 49af877502..631ee5eef0 100644 --- a/common/nymsphinx/src/receiver.rs +++ b/common/nymsphinx/src/receiver.rs @@ -7,8 +7,6 @@ use nym_crypto::asymmetric::encryption; use nym_crypto::shared_key::recompute_shared_key; use nym_crypto::symmetric::stream_cipher; use nym_crypto::symmetric::stream_cipher::CipherKey; -use nym_outfox::error::OutfoxError; -use nym_outfox::lion::lion_transform_decrypt; use nym_sphinx_anonymous_replies::requests::AnonymousSenderTag; use nym_sphinx_anonymous_replies::SurbEncryptionKey; use nym_sphinx_chunking::fragment::Fragment; @@ -76,49 +74,6 @@ pub enum MessageRecoveryError { #[error("Failed to recover message fragment - {0}")] FragmentRecoveryError(#[from] ChunkingError), - - #[error("Outfox: {source}")] - OutfoxRecoveryError { - #[from] - source: OutfoxError, - }, -} - -#[derive(Default)] -pub struct OutfoxMessageReceiver { - reconstructor: MessageReconstructor, -} - -impl OutfoxMessageReceiver { - pub fn new() -> Self { - Default::default() - } -} - -impl MessageReceiver for OutfoxMessageReceiver { - fn new() -> Self { - Self::default() - } - - fn reconstructor(&mut self) -> &mut MessageReconstructor { - &mut self.reconstructor - } - - fn num_mix_hops(&self) -> u8 { - DEFAULT_NUM_MIX_HOPS - } - - fn decrypt_raw_message( - &self, - message: &mut [u8], - key: &CipherKey, - ) -> Result<(), MessageRecoveryError> - where - C: StreamCipher + KeyIvInit, - { - lion_transform_decrypt(message, key)?; - Ok(()) - } } pub trait MessageReceiver { diff --git a/common/nymsphinx/types/src/lib.rs b/common/nymsphinx/types/src/lib.rs index 38ca61a6ce..b9f15693a8 100644 --- a/common/nymsphinx/types/src/lib.rs +++ b/common/nymsphinx/types/src/lib.rs @@ -1,8 +1,9 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -pub use nym_outfox::{error::OutfoxError, packet::OutfoxPacket, packet::OUTFOX_PACKET_OVERHEAD}; +pub use nym_outfox::{format::MIX_PARAMS_LEN, packet::OUTFOX_PACKET_OVERHEAD}; // re-exporting types and constants available in sphinx +use nym_outfox::packet::OutfoxPacket; pub use sphinx_packet::{ constants::{ self, DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, MAX_PATH_LENGTH, NODE_ADDRESS_LENGTH, @@ -14,9 +15,10 @@ pub use sphinx_packet::{ payload::{Payload, PAYLOAD_OVERHEAD_SIZE}, route::{Destination, DestinationAddressBytes, Node, NodeAddressBytes, SURBIdentifier}, surb::{SURBMaterial, SURB}, - Error as SphinxError, ProcessedPacket, SphinxPacket, + Error as SphinxError, ProcessedPacket, }; -use std::fmt; +use sphinx_packet::{SphinxPacket, SphinxPacketBuilder}; +use std::{array::TryFromSliceError, fmt}; use thiserror::Error; #[derive(Error, Debug)] @@ -26,6 +28,9 @@ pub enum NymPacketError { #[error("Outfox error: {0}")] Outfox(#[from] nym_outfox::error::OutfoxError), + + #[error("{0}")] + FromSlice(#[from] TryFromSliceError), } #[allow(clippy::large_enum_variant)] @@ -50,6 +55,39 @@ impl fmt::Debug for NymPacket { } impl NymPacket { + pub fn sphinx_build>( + size: usize, + message: M, + route: &[Node], + destination: &Destination, + delays: &[Delay], + ) -> Result { + Ok(NymPacket::Sphinx( + SphinxPacketBuilder::new() + .with_payload_size(size) + .build_packet(message, route, destination, delays)?, + )) + } + pub fn sphinx_from_bytes(bytes: &[u8]) -> Result { + Ok(NymPacket::Sphinx(SphinxPacket::from_bytes(bytes)?)) + } + + pub fn outfox_build>( + payload: M, + route: &[Node], + size: Option, + ) -> Result { + Ok(NymPacket::Outfox(OutfoxPacket::build( + payload, + route.try_into()?, + size, + )?)) + } + + pub fn outfox_from_bytes(bytes: &[u8]) -> Result { + Ok(NymPacket::Outfox(OutfoxPacket::try_from(bytes)?)) + } + pub fn len(&self) -> usize { match self { NymPacket::Sphinx(packet) => packet.len(), diff --git a/common/topology/src/error.rs b/common/topology/src/error.rs index 170bb1889c..b8826c7191 100644 --- a/common/topology/src/error.rs +++ b/common/topology/src/error.rs @@ -33,4 +33,7 @@ pub enum NymTopologyError { total_nodes: usize, layer_distribution: Vec<(MixLayer, usize)>, }, + // We can't import SurbAckRecoveryError due to cyclic dependency, this is a bit dirty + #[error("Could not build payload")] + PayloadBuilder, } diff --git a/gateway/gateway-requests/src/types.rs b/gateway/gateway-requests/src/types.rs index 0685c0d473..3683144980 100644 --- a/gateway/gateway-requests/src/types.rs +++ b/gateway/gateway-requests/src/types.rs @@ -5,6 +5,7 @@ use crate::authentication::encrypted_address::EncryptedAddressBytes; use crate::iv::IV; use crate::registration::handshake::SharedKeys; use crate::{GatewayMacSize, PROTOCOL_VERSION}; +use log::error; use nym_coconut_interface::Credential; use nym_crypto::generic_array::typenum::Unsigned; use nym_crypto::hmac::recompute_keyed_hmac_and_verify_tag; @@ -290,7 +291,13 @@ impl BinaryRequest { pub fn into_encrypted_tagged_bytes(self, shared_key: &SharedKeys) -> Vec { match self { BinaryRequest::ForwardSphinx(mix_packet) => { - let forwarding_data = mix_packet.into_bytes(); + let forwarding_data = match mix_packet.into_bytes() { + Ok(mix_packet) => mix_packet, + Err(e) => { + error!("Could not convert packet to bytes: {e}"); + return vec![]; + } + }; // TODO: it could be theoretically slightly more efficient if the data wasn't taken // by reference because then it makes a copy for encryption rather than do it in place diff --git a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs index dfc4ae377a..193c5dd1fc 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs @@ -283,7 +283,7 @@ where &self, mix_packet: MixPacket, ) -> Result { - let consumed_bandwidth = mix_packet.sphinx_packet().len() as i64; + let consumed_bandwidth = mix_packet.packet().len() as i64; let available_bandwidth = self.get_available_bandwidth().await?; diff --git a/mixnode/src/node/packet_delayforwarder.rs b/mixnode/src/node/packet_delayforwarder.rs index bcf2a6b669..e2d96c0dfb 100644 --- a/mixnode/src/node/packet_delayforwarder.rs +++ b/mixnode/src/node/packet_delayforwarder.rs @@ -5,7 +5,7 @@ use crate::node::node_statistics::UpdateSender; use futures::channel::mpsc; use futures::StreamExt; use nym_nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue}; -use nym_sphinx::{forwarding::packet::MixPacket, NymPacket}; +use nym_sphinx::forwarding::packet::MixPacket; use std::io; use tokio::time::Instant; @@ -58,14 +58,13 @@ where fn forward_packet(&mut self, packet: MixPacket) { let next_hop = packet.next_hop(); - let packet_mode = packet.packet_mode(); - let sphinx_packet = packet.into_sphinx_packet(); + let packet_type = packet.packet_type(); + let packet = packet.into_packet(); - if let Err(err) = self.mixnet_client.send_without_response( - next_hop, - NymPacket::Sphinx(sphinx_packet), - packet_mode, - ) { + if let Err(err) = self + .mixnet_client + .send_without_response(next_hop, packet, packet_type) + { if err.kind() == io::ErrorKind::WouldBlock { // we only know for sure if we dropped a packet if our sending queue was full // in any other case the connection might still be re-established (or created for the first time) @@ -135,20 +134,20 @@ mod tests { use std::sync::{Arc, Mutex}; use std::time::Duration; + use nym_sphinx::NymPacket; use nym_task::TaskManager; use nym_sphinx::addressing::nodes::NymNodeRoutingAddress; use nym_sphinx_params::packet_sizes::PacketSize; - use nym_sphinx_params::PacketMode; - use nym_sphinx_types::builder::SphinxPacketBuilder; + use nym_sphinx_params::PacketType; use nym_sphinx_types::{ crypto, Delay as SphinxDelay, Destination, DestinationAddressBytes, Node, NodeAddressBytes, - SphinxPacket, DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, NODE_ADDRESS_LENGTH, + DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, NODE_ADDRESS_LENGTH, }; #[derive(Default)] struct TestClient { - pub packets_sent: Arc>>, + pub packets_sent: Arc>>, } impl nym_mixnet_client::SendWithoutResponse for TestClient { @@ -156,17 +155,17 @@ mod tests { &mut self, address: NymNodeRoutingAddress, packet: NymPacket, - packet_mode: PacketMode, + packet_type: PacketType, ) -> io::Result<()> { self.packets_sent .lock() .unwrap() - .push((address, packet, packet_mode)); + .push((address, packet, packet_type)); Ok(()) } } - fn make_valid_sphinx_packet(size: PacketSize) -> SphinxPacket { + fn make_valid_sphinx_packet(size: PacketSize) -> NymPacket { let (_, node1_pk) = crypto::keygen(); let node1 = Node::new( NodeAddressBytes::from_bytes([5u8; NODE_ADDRESS_LENGTH]), @@ -193,9 +192,7 @@ mod tests { SphinxDelay::new_from_nanos(42), SphinxDelay::new_from_nanos(42), ]; - SphinxPacketBuilder::new() - .with_payload_size(size.payload_size()) - .build_packet(b"foomp", &route, &destination, &delays) + NymPacket::sphinx_build(size.payload_size(), b"foomp", &route, &destination, &delays) .unwrap() } @@ -220,7 +217,7 @@ mod tests { let mix_packet = MixPacket::new( next_hop, make_valid_sphinx_packet(PacketSize::default()), - PacketMode::default(), + PacketType::default(), ); let forward_instant = None; packet_sender diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index ef83c79ddb..d6bfc07560 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -3751,6 +3751,7 @@ dependencies = [ "nym-sphinx-addressing", "nym-sphinx-params", "nym-sphinx-types", + "thiserror", ] [[package]] diff --git a/nym-outfox/Cargo.toml b/nym-outfox/Cargo.toml index 44df9b3487..36714d1eab 100644 --- a/nym-outfox/Cargo.toml +++ b/nym-outfox/Cargo.toml @@ -16,12 +16,7 @@ chacha20poly1305 = "0.10.1" getrandom = { version = "*", features = ["js"] } thiserror = "1" fastrand = "1.8" - sphinx-packet = "0.1.0" -[patch.crates-io] -sphinx-packet = { path = "../../../../sphinx" } - - [dev-dependencies] criterion = "0.4" diff --git a/nym-outfox/src/packet.rs b/nym-outfox/src/packet.rs index c5296ce850..cdb92fd6f3 100644 --- a/nym-outfox/src/packet.rs +++ b/nym-outfox/src/packet.rs @@ -49,8 +49,8 @@ impl OutfoxPacket { Ok(bytes) } - pub fn build( - payload: &[u8], + pub fn build>( + payload: M, route: &[Node; 3], packet_size: Option, ) -> Result { @@ -58,9 +58,9 @@ impl OutfoxPacket { 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 padding = mix_params.total_packet_length() - payload.as_ref().len(); let mut buffer = vec![0; padding]; - buffer.extend_from_slice(payload); + buffer.extend_from_slice(payload.as_ref()); for (idx, node) in route.iter().rev().enumerate() { let (range, stage_params) = mix_params.get_stage_params(idx);