Outfox rest compat (#3333)

* Outfox forwarding compat

* Tidy up interface

* PacketSize compat
This commit is contained in:
Drazen Urch
2023-04-20 23:59:40 +02:00
committed by GitHub
parent e7be91a94c
commit cbd654d6fd
23 changed files with 239 additions and 214 deletions
Generated
+1
View File
@@ -4061,6 +4061,7 @@ dependencies = [
"nym-sphinx-addressing",
"nym-sphinx-params",
"nym-sphinx-types",
"thiserror",
]
[[package]]
@@ -136,7 +136,7 @@ impl From<PreparedFragment> 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: FragmentIdentifier) -> Self {
@@ -605,7 +605,7 @@ where
fn estimate_required_bandwidth(&self, packets: &[MixPacket]) -> i64 {
packets
.iter()
.map(|packet| packet.sphinx_packet().len())
.map(|packet| packet.packet().len())
.sum::<usize>() as i64
}
@@ -686,9 +686,9 @@ where
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,
));
}
@@ -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<MixPacket>;
@@ -61,13 +60,13 @@ impl PacketForwarder {
let next_hop = mix_packet.next_hop();
let packet_mode = mix_packet.packet_mode();
let sphinx_packet = mix_packet.into_sphinx_packet();
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_mode)
{
debug!("failed to forward the packet - {err}")
}
@@ -11,7 +11,7 @@ use nym_sphinx_framing::packet::FramedNymPacket;
use nym_sphinx_params::{PacketMode, PacketSize};
use nym_sphinx_types::{
Delay as SphinxDelay, DestinationAddressBytes, NodeAddressBytes, NymPacket, Payload,
PrivateKey, ProcessedPacket, SphinxPacket,
PrivateKey, ProcessedPacket,
};
use std::convert::TryFrom;
use std::sync::Arc;
@@ -90,7 +90,7 @@ 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,
@@ -180,7 +180,7 @@ impl SphinxPacketProcessor {
) -> Result<MixProcessingResult, MixProcessingError> {
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_mode)
}
// right now there's no use for the surb_id included in the header - probably it should get removed from the
// sphinx all together?
@@ -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<u8>) {
pub fn prepare_for_sending(self) -> Result<(Delay, Vec<u8>), 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))
}
@@ -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))
}
}
+11 -9
View File
@@ -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;
@@ -14,7 +14,7 @@ use nym_sphinx_params::{
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, PacketMode, 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<R>(
@@ -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,10 +111,12 @@ 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();
+1
View File
@@ -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"
+31 -48
View File
@@ -3,41 +3,27 @@
use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, NymNodeRoutingAddressError};
use nym_sphinx_params::{PacketMode, PacketSize};
use nym_sphinx_types::SphinxPacket;
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,
#[error("provided packet mode is invalid")]
InvalidPacketMode,
#[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<NymNodeRoutingAddressError> for MixPacketFormattingError {
fn from(_: NymNodeRoutingAddressError) -> Self {
MixPacketFormattingError::InvalidAddress
@@ -46,7 +32,7 @@ impl From<NymNodeRoutingAddressError> for MixPacketFormattingError {
pub struct MixPacket {
next_hop: NymNodeRoutingAddress,
sphinx_packet: SphinxPacket,
packet: NymPacket,
packet_mode: PacketMode,
}
@@ -54,11 +40,8 @@ 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_mode {:?}. Packet {:?}",
self.next_hop, self.packet_mode, self.packet
)
}
}
@@ -66,12 +49,12 @@ impl Debug for MixPacket {
impl MixPacket {
pub fn new(
next_hop: NymNodeRoutingAddress,
sphinx_packet: SphinxPacket,
packet: NymPacket,
packet_mode: PacketMode,
) -> Self {
MixPacket {
next_hop,
sphinx_packet,
packet,
packet_mode,
}
}
@@ -80,12 +63,12 @@ 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 {
@@ -93,7 +76,7 @@ impl MixPacket {
}
// the message is formatted as follows:
// PACKET_MODE || FIRST_HOP || SPHINX_PACKET
// PACKET_MODE || FIRST_HOP || packet
pub fn try_from_bytes(b: &[u8]) -> Result<Self, MixPacketFormattingError> {
let packet_mode = match PacketMode::try_from(b[0]) {
Ok(mode) => mode,
@@ -103,29 +86,29 @@ impl MixPacket {
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_mode {
PacketMode::Outfox => NymPacket::outfox_from_bytes(packet_data)?,
_ => NymPacket::sphinx_from_bytes(packet_data)?,
};
Ok(MixPacket {
next_hop,
sphinx_packet,
packet,
packet_mode,
})
}
}
pub fn into_bytes(self) -> Vec<u8> {
std::iter::once(self.packet_mode as u8)
pub fn into_bytes(self) -> Result<Vec<u8>, MixPacketFormattingError> {
Ok(std::iter::once(self.packet_mode 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())
}
}
+21 -34
View File
@@ -6,8 +6,7 @@ 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_types::{NymPacket, NymPacketError};
use std::io;
use thiserror::Error;
use tokio_util::codec::{Decoder, Encoder};
@@ -20,12 +19,6 @@ pub enum NymCodecError {
#[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),
#[error("encountered an IO error - {0}")]
IoError(#[from] io::Error),
@@ -133,8 +126,8 @@ impl Decoder for NymCodec {
// 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)?),
PacketMode::Outfox => NymPacket::outfox_from_bytes(slice)?,
_ => NymPacket::sphinx_from_bytes(slice)?,
}
} else {
return Ok(None);
@@ -176,13 +169,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 +191,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 +225,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 +233,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 +254,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();
@@ -355,7 +342,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 +359,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,7 +388,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();
@@ -428,7 +415,7 @@ 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();
@@ -447,12 +434,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 +456,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();
+49 -5
View File
@@ -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<Self, InvalidPacketSize> {
@@ -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<Self, InvalidPacketSize> {
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),
}
}
}
+19 -9
View File
@@ -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;
@@ -149,8 +149,12 @@ where
let surb_ack = self.generate_surb_ack(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
@@ -208,8 +212,12 @@ where
let surb_ack = self.generate_surb_ack(fragment_identifier, topology, ack_key)?;
let ack_delay = surb_ack.expected_total_delay();
let packet_payload = NymsphinxPayloadBuilder::new(fragment, surb_ack)
.build_regular(&mut self.rng, packet_recipient.encryption_key());
let packet_payload = match NymsphinxPayloadBuilder::new(fragment, surb_ack)
.build_regular(&mut self.rng, packet_recipient.encryption_key())
{
Ok(payload) => payload,
Err(_e) => return Err(NymTopologyError::PayloadBuilder),
};
// generate pseudorandom route for the packet
let route = topology.random_route_to_gateway(
@@ -224,10 +232,12 @@ where
// 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()
.with_payload_size(packet_size.payload_size())
.build_packet(packet_payload, &route, &destination, &delays)
.unwrap();
let sphinx_packet = NymPacket::Sphinx(
SphinxPacketBuilder::new()
.with_payload_size(packet_size.payload_size())
.build_packet(packet_payload, &route, &destination, &delays)
.unwrap(),
);
// from the previously constructed route extract the first hop
let first_hop_address =
+10 -7
View File
@@ -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<C>,
variant_data: impl IntoIterator<Item = u8>,
) -> NymsphinxPayload
) -> Result<NymsphinxPayload, SurbAckRecoveryError>
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::<C>(
@@ -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<NymsphinxPayload, SurbAckRecoveryError> {
let key_digest = packet_encryption_key.compute_digest();
self.build::<ReplySurbEncryptionAlgorithm>(
packet_encryption_key.inner(),
@@ -67,7 +70,7 @@ impl NymsphinxPayloadBuilder {
self,
rng: &mut R,
recipient_encryption_key: &encryption::PublicKey,
) -> NymsphinxPayload
) -> Result<NymsphinxPayload, SurbAckRecoveryError>
where
R: RngCore + CryptoRng,
{
-45
View File
@@ -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<C>(
&self,
message: &mut [u8],
key: &CipherKey<C>,
) -> Result<(), MessageRecoveryError>
where
C: StreamCipher + KeyIvInit,
{
lion_transform_decrypt(message, key)?;
Ok(())
}
}
pub trait MessageReceiver {
+41 -3
View File
@@ -1,8 +1,9 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// 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<M: AsRef<[u8]>>(
size: usize,
message: M,
route: &[Node],
destination: &Destination,
delays: &[Delay],
) -> Result<NymPacket, NymPacketError> {
Ok(NymPacket::Sphinx(
SphinxPacketBuilder::new()
.with_payload_size(size)
.build_packet(message, route, destination, delays)?,
))
}
pub fn sphinx_from_bytes(bytes: &[u8]) -> Result<NymPacket, NymPacketError> {
Ok(NymPacket::Sphinx(SphinxPacket::from_bytes(bytes)?))
}
pub fn outfox_build<M: AsRef<[u8]>>(
payload: M,
route: &[Node],
size: Option<usize>,
) -> Result<NymPacket, NymPacketError> {
Ok(NymPacket::Outfox(OutfoxPacket::build(
payload,
route.try_into()?,
size,
)?))
}
pub fn outfox_from_bytes(bytes: &[u8]) -> Result<NymPacket, NymPacketError> {
Ok(NymPacket::Outfox(OutfoxPacket::try_from(bytes)?))
}
pub fn len(&self) -> usize {
match self {
NymPacket::Sphinx(packet) => packet.len(),
+3
View File
@@ -52,6 +52,9 @@ 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,
}
#[derive(Debug, Clone)]
+8 -1
View File
@@ -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<u8> {
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
@@ -283,7 +283,7 @@ where
&self,
mix_packet: MixPacket,
) -> Result<ServerResponse, RequestHandlingError> {
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?;
+10 -13
View File
@@ -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;
@@ -59,13 +59,12 @@ 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 = 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_mode)
{
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,15 +134,15 @@ 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_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)]
@@ -166,7 +165,7 @@ mod tests {
}
}
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()
}
+1
View File
@@ -3742,6 +3742,7 @@ dependencies = [
"nym-sphinx-addressing",
"nym-sphinx-params",
"nym-sphinx-types",
"thiserror",
]
[[package]]
-5
View File
@@ -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"
+4 -4
View File
@@ -49,8 +49,8 @@ impl OutfoxPacket {
Ok(bytes)
}
pub fn build(
payload: &[u8],
pub fn build<M: AsRef<[u8]>>(
payload: M,
route: &[Node; 3],
packet_size: Option<usize>,
) -> Result<OutfoxPacket, OutfoxError> {
@@ -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);