More work on the reciever end, and outfox format

This commit is contained in:
durch
2023-05-04 16:08:46 +02:00
parent 4d837a0402
commit 19c4b3255c
37 changed files with 1234 additions and 1622 deletions
+2 -2
View File
@@ -53,7 +53,7 @@ where
}
async fn on_messages(&mut self, mut mix_packets: Vec<MixPacket>) {
debug_assert!(!mix_packets.is_empty());
assert!(!mix_packets.is_empty());
let result = if mix_packets.len() == 1 {
let mix_packet = mix_packets.pop().unwrap();
@@ -103,7 +103,7 @@ where
}
}
shutdown.recv_timeout().await;
log::debug!("MixTrafficController: Exiting");
log::error!("MixTrafficController: Exiting");
})
}
}
@@ -71,7 +71,8 @@ impl AcknowledgementListener {
while !shutdown.is_shutdown() {
tokio::select! {
acks = self.ack_receiver.next() => match acks {
Some(acks) => self.handle_ack_receiver_item(acks).await,
Some(acks) => {self.handle_ack_receiver_item(acks).await}
None => {
log::trace!("AcknowledgementListener: Stopping since channel closed");
break;
@@ -49,11 +49,11 @@ where
packet_recipient: Recipient,
chunk_data: Fragment,
) -> Result<PreparedFragment, PreparationError> {
info!("retransmitting normal packet...");
debug!("retransmitting normal packet...");
// TODO: Figure out retransmission packet type signaling
self.message_handler
.try_prepare_single_chunk_for_sending(packet_recipient, chunk_data, PacketType::Mix)
.try_prepare_single_chunk_for_sending(packet_recipient, chunk_data, PacketType::Outfox)
.await
}
@@ -431,7 +431,7 @@ where
lane: TransmissionLane,
packet_type: PacketType,
) -> Result<(), PreparationError> {
info!("Sending non-reply message with packet type {packet_type}");
debug!("Sending non-reply message with packet type {packet_type}");
// TODO: I really dislike existence of this assertion, it implies code has to be re-organised
debug_assert!(!matches!(message, NymMessage::Reply(_)));
@@ -439,7 +439,11 @@ where
let topology_permit = self.topology_access.get_read_permit().await;
let topology = self.get_topology(&topology_permit)?;
let packet_size = self.optimal_packet_size(&message);
let packet_size = if packet_type == PacketType::Outfox {
PacketSize::OutfoxRegularPacket
} else {
self.optimal_packet_size(&message)
};
debug!("Using {packet_size} packets for {message}");
let fragments = self
.message_preparer
@@ -482,7 +486,7 @@ where
amount: u32,
packet_type: PacketType,
) -> Result<(), PreparationError> {
info!("Sending additional reply SURBs with packet type {packet_type}");
debug!("Sending additional reply SURBs with packet type {packet_type}");
let sender_tag = self.get_or_create_sender_tag(&recipient);
let (reply_surbs, reply_keys) =
self.generate_reply_surbs_with_keys(amount as usize).await?;
@@ -514,7 +518,7 @@ where
lane: TransmissionLane,
packet_type: PacketType,
) -> Result<(), SurbWrappedPreparationError> {
info!("Sending message with reply SURBs with packet type {packet_type}");
debug!("Sending message with reply SURBs with packet type {packet_type}");
let sender_tag = self.get_or_create_sender_tag(&recipient);
let (reply_surbs, reply_keys) = self
.generate_reply_surbs_with_keys(num_reply_surbs as usize)
@@ -538,7 +542,7 @@ where
chunk: Fragment,
packet_type: PacketType,
) -> Result<PreparedFragment, PreparationError> {
info!("Sending single chunk with packet type {packet_type}");
debug!("Sending single chunk with packet type {packet_type}");
let topology_permit = self.topology_access.get_read_permit().await;
let topology = self.get_topology(&topology_permit)?;
@@ -353,7 +353,7 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
};
if let Some(completed) = completed_message {
info!("received {completed}");
debug!("received {completed}");
completed_messages.push(completed)
}
}
@@ -579,6 +579,8 @@ impl<C, St> GatewayClient<C, St> {
&mut self,
packets: Vec<MixPacket>,
) -> Result<(), GatewayClientError> {
warn!("Sending {} mix packets", packets.len());
if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated);
}
@@ -623,9 +625,10 @@ impl<C, St> GatewayClient<C, St> {
) -> Result<(), GatewayClientError> {
if let Err(err) = self.send_websocket_message_without_response(msg).await {
if err.is_closed_connection() && self.should_reconnect_on_failure {
info!("Going to attempt a reconnection");
error!("Going to attempt a reconnection");
self.attempt_reconnection().await
} else {
error!("{err}");
Err(err)
}
} else {
@@ -50,10 +50,15 @@ impl PacketRouter {
let ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
for received_packet in unwrapped_packets {
if received_packet.len() == PacketSize::AckPacket.plaintext_size() {
if received_packet.len() == PacketSize::AckPacket.plaintext_size()
|| received_packet.len() == PacketSize::OutfoxAckPacket.plaintext_size()
{
received_acks.push(received_packet);
} else if received_packet.len()
== PacketSize::RegularPacket.plaintext_size() - ack_overhead
|| received_packet.len()
== PacketSize::OutfoxRegularPacket.plaintext_size() - ack_overhead
|| received_packet.len() == PacketSize::OutfoxRegularPacket.size() - 6
{
trace!("routing regular packet");
received_messages.push(received_packet);
@@ -10,7 +10,7 @@ use nym_sphinx_forwarding::packet::MixPacket;
use nym_sphinx_framing::packet::FramedNymPacket;
use nym_sphinx_params::{PacketSize, PacketType};
use nym_sphinx_types::{
Delay as SphinxDelay, DestinationAddressBytes, NodeAddressBytes, NymPacket, Payload,
Delay as SphinxDelay, DestinationAddressBytes, NodeAddressBytes, NymPacket, NymProcessedPacket,
PrivateKey, ProcessedPacket,
};
use std::convert::TryFrom;
@@ -56,10 +56,10 @@ impl SphinxPacketProcessor {
fn perform_initial_packet_processing(
&self,
packet: NymPacket,
) -> Result<ProcessedPacket, MixProcessingError> {
) -> Result<NymProcessedPacket, MixProcessingError> {
measure!({
packet.process(&self.sphinx_key).map_err(|err| {
debug!("Failed to unwrap Sphinx packet: {err}");
info!("Failed to unwrap NymPacket packet: {err}");
MixProcessingError::NymPacketProcessingError(err)
})
})
@@ -73,7 +73,7 @@ impl SphinxPacketProcessor {
fn perform_initial_unwrapping(
&self,
received: FramedNymPacket,
) -> Result<ProcessedPacket, MixProcessingError> {
) -> Result<NymProcessedPacket, MixProcessingError> {
measure!({
let packet = received.into_inner();
@@ -101,14 +101,17 @@ impl SphinxPacketProcessor {
fn split_hop_data_into_ack_and_message(
&self,
mut extracted_data: Vec<u8>,
packet_type: PacketType,
) -> Result<(Vec<u8>, Vec<u8>), MixProcessingError> {
let ack_len = SurbAck::len(Some(packet_type));
// in theory it's impossible for this to fail since it managed to go into correct `match`
// branch at the caller
if extracted_data.len() < SurbAck::len() {
if extracted_data.len() < ack_len {
return Err(MixProcessingError::NoSurbAckInFinalHop);
}
let message = extracted_data.split_off(SurbAck::len());
let message = extracted_data.split_off(ack_len);
let ack_data = extracted_data;
Ok((ack_data, message))
}
@@ -130,13 +133,18 @@ impl SphinxPacketProcessor {
| PacketSize::ExtendedPacket8
| PacketSize::ExtendedPacket16
| PacketSize::ExtendedPacket32
| PacketSize::OutfoxRegularPacket
| PacketSize::OutfoxExtendedPacket8
| PacketSize::OutfoxExtendedPacket16
| PacketSize::OutfoxExtendedPacket32 => {
| PacketSize::OutfoxRegularPacket => {
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 (ack_data, message) =
self.split_hop_data_into_ack_and_message(data, packet_type)?;
let (ack_first_hop, ack_packet) =
match SurbAck::try_recover_first_hop_packet(&ack_data, packet_type) {
Ok((first_hop, packet)) => (first_hop, packet),
Err(err) => {
error!("Failed to recover first hop from ack data: {err}");
return Err(err.into());
}
};
let forward_ack = MixPacket::new(ack_first_hop, ack_packet, packet_type);
Ok((Some(forward_ack), message))
}
@@ -149,14 +157,12 @@ impl SphinxPacketProcessor {
fn process_final_hop(
&self,
destination: DestinationAddressBytes,
payload: Payload,
payload: Vec<u8>,
packet_size: PacketSize,
packet_type: PacketType,
) -> Result<MixProcessingResult, MixProcessingError> {
let packet_message = payload.recover_plaintext()?;
let (forward_ack, message) =
self.split_into_ack_and_message(packet_message, packet_size, packet_type)?;
self.split_into_ack_and_message(payload, packet_size, packet_type)?;
Ok(MixProcessingResult::FinalHop(ProcessedFinalHop {
destination,
@@ -169,18 +175,48 @@ impl SphinxPacketProcessor {
/// or a final hop.
fn perform_final_processing(
&self,
packet: ProcessedPacket,
packet: NymProcessedPacket,
packet_size: PacketSize,
packet_type: PacketType,
) -> Result<MixProcessingResult, MixProcessingError> {
match packet {
ProcessedPacket::ForwardHop(packet, address, delay) => {
self.process_forward_hop(NymPacket::Sphinx(*packet), address, delay, packet_type)
NymProcessedPacket::Sphinx(packet) => {
match packet {
ProcessedPacket::ForwardHop(packet, address, delay) => 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.recover_plaintext()?,
packet_size,
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_type)
NymProcessedPacket::Outfox(packet) => {
let next_address = *packet.next_address();
let packet = packet.into_packet();
if packet.is_final_hop() {
self.process_final_hop(
DestinationAddressBytes::from_bytes(next_address),
packet.recover_plaintext().to_vec(),
packet_size,
packet_type,
)
} else {
let mix_packet = MixPacket::new(
NymNodeRoutingAddress::try_from_bytes(&next_address)?,
NymPacket::Outfox(packet),
PacketType::Outfox,
);
Ok(MixProcessingResult::ForwardHop(mix_packet, None))
}
}
}
}
@@ -225,31 +261,71 @@ mod tests {
let short_data = vec![42u8];
assert!(processor
.split_hop_data_into_ack_and_message(short_data)
.split_hop_data_into_ack_and_message(short_data, PacketType::Mix)
.is_err());
let sufficient_data = vec![42u8; SurbAck::len()];
let sufficient_data = vec![42u8; SurbAck::len(Some(PacketType::Mix))];
let (ack, data) = processor
.split_hop_data_into_ack_and_message(sufficient_data.clone())
.split_hop_data_into_ack_and_message(sufficient_data.clone(), PacketType::Mix)
.unwrap();
assert_eq!(sufficient_data, ack);
assert!(data.is_empty());
let long_data = vec![42u8; SurbAck::len() * 5];
let long_data = vec![42u8; SurbAck::len(Some(PacketType::Mix)) * 5];
let (ack, data) = processor
.split_hop_data_into_ack_and_message(long_data)
.split_hop_data_into_ack_and_message(long_data, PacketType::Mix)
.unwrap();
assert_eq!(ack.len(), SurbAck::len());
assert_eq!(data.len(), SurbAck::len() * 4)
assert_eq!(ack.len(), SurbAck::len(Some(PacketType::Mix)));
assert_eq!(data.len(), SurbAck::len(Some(PacketType::Mix)) * 4)
}
#[tokio::test]
async fn splitting_hop_data_works_for_sufficiently_long_payload_outfox() {
let processor = fixture();
let short_data = vec![42u8];
assert!(processor
.split_hop_data_into_ack_and_message(short_data, PacketType::Outfox)
.is_err());
let sufficient_data = vec![42u8; SurbAck::len(Some(PacketType::Outfox))];
let (ack, data) = processor
.split_hop_data_into_ack_and_message(sufficient_data.clone(), PacketType::Outfox)
.unwrap();
assert_eq!(sufficient_data, ack);
assert!(data.is_empty());
let long_data = vec![42u8; SurbAck::len(Some(PacketType::Outfox)) * 5];
let (ack, data) = processor
.split_hop_data_into_ack_and_message(long_data, PacketType::Outfox)
.unwrap();
assert_eq!(ack.len(), SurbAck::len(Some(PacketType::Outfox)));
assert_eq!(data.len(), SurbAck::len(Some(PacketType::Outfox)) * 4)
}
#[tokio::test]
async fn splitting_into_ack_and_message_returns_whole_data_for_ack() {
let processor = fixture();
let data = vec![42u8; SurbAck::len() + 10];
let data = vec![42u8; SurbAck::len(Some(PacketType::Mix)) + 10];
let (ack, message) = processor
.split_into_ack_and_message(data.clone(), PacketSize::AckPacket, Default::default())
.split_into_ack_and_message(data.clone(), PacketSize::AckPacket, PacketType::Mix)
.unwrap();
assert!(ack.is_none());
assert_eq!(data, message)
}
#[tokio::test]
async fn splitting_into_ack_and_message_returns_whole_data_for_ack_outfox() {
let processor = fixture();
let data = vec![42u8; SurbAck::len(Some(PacketType::Outfox)) + 10];
let (ack, message) = processor
.split_into_ack_and_message(
data.clone(),
PacketSize::OutfoxAckPacket,
PacketType::Outfox,
)
.unwrap();
assert!(ack.is_none());
assert_eq!(data, message)
@@ -3,9 +3,7 @@
use crate::AckKey;
use nym_crypto::symmetric::stream_cipher::{self, encrypt, iv_from_slice, random_iv, IvSizeUser};
use nym_sphinx_params::{
packet_sizes::PacketSize, AckEncryptionAlgorithm, SerializedFragmentIdentifier, FRAG_ID_LEN,
};
use nym_sphinx_params::{AckEncryptionAlgorithm, SerializedFragmentIdentifier, FRAG_ID_LEN};
use rand::{CryptoRng, RngCore};
// TODO: should those functions even exist in this file?
@@ -28,9 +26,9 @@ pub fn recover_identifier(
) -> Option<SerializedFragmentIdentifier> {
// The content of an 'ACK' packet consists of AckEncryptionAlgorithm::IV followed by
// serialized FragmentIdentifier
if iv_id_ciphertext.len() != PacketSize::AckPacket.plaintext_size() {
return None;
}
// if iv_id_ciphertext.len() != PacketSize::AckPacket.plaintext_size() {
// return None;
// }
let iv_size = AckEncryptionAlgorithm::iv_size();
let iv = iv_from_slice::<AckEncryptionAlgorithm>(&iv_id_ciphertext[..iv_size]);
@@ -17,6 +17,7 @@ use std::convert::TryFrom;
use std::time;
use thiserror::Error;
#[derive(Debug)]
pub struct SurbAck {
surb_ack_packet: NymPacket,
first_hop_address: NymNodeRoutingAddress,
@@ -55,15 +56,24 @@ impl SurbAck {
let surb_ack_payload = prepare_identifier(rng, ack_key, marshaled_fragment_id);
let packet_size = match packet_type {
PacketType::Outfox => PacketSize::OutfoxAckPacket.payload_size(),
PacketType::Outfox => {
if surb_ack_payload.len() >= 48 {
surb_ack_payload.len()
} else {
48
}
}
PacketType::Mix => PacketSize::AckPacket.payload_size(),
PacketType::Vpn => PacketSize::AckPacket.payload_size(),
};
let surb_ack_packet = match packet_type {
PacketType::Outfox => {
NymPacket::outfox_build(surb_ack_payload, route.as_slice(), Some(packet_size))?
}
PacketType::Outfox => NymPacket::outfox_build(
surb_ack_payload,
route.as_slice(),
&destination,
Some(packet_size),
)?,
PacketType::Mix => NymPacket::sphinx_build(
packet_size,
surb_ack_payload,
@@ -92,10 +102,17 @@ impl SurbAck {
})
}
pub fn len() -> usize {
pub fn len(packet_type: Option<PacketType>) -> usize {
// TODO: this will be variable once/if we decide to introduce optimization described
// in common/nymsphinx/chunking/src/lib.rs:available_plaintext_size()
PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN
let packet_type = packet_type.unwrap_or(PacketType::Mix);
match packet_type {
PacketType::Outfox => {
PacketSize::OutfoxAckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN
}
PacketType::Mix => PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN,
PacketType::Vpn => PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN,
}
}
pub fn expected_total_delay(&self) -> Delay {
@@ -116,21 +133,19 @@ impl SurbAck {
// partial reciprocal of `prepare_for_sending` performed by the gateway
pub fn try_recover_first_hop_packet(
b: &[u8],
packet_type: PacketType,
) -> Result<(NymNodeRoutingAddress, NymPacket), SurbAckRecoveryError> {
if b.len() != Self::len() {
Err(SurbAckRecoveryError::InvalidPacketSize {
received: b.len(),
expected: Self::len(),
})
} else {
let address = NymNodeRoutingAddress::try_from_bytes(b)?;
let address = NymNodeRoutingAddress::try_from_bytes(b)?;
// 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 = NymPacket::sphinx_from_bytes(&b[address_offset..])?;
// 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 = match packet_type {
PacketType::Outfox => NymPacket::outfox_from_bytes(&b[address_offset..])?,
PacketType::Mix => NymPacket::sphinx_from_bytes(&b[address_offset..])?,
PacketType::Vpn => NymPacket::sphinx_from_bytes(&b[address_offset..])?,
};
Ok((address, packet))
}
Ok((address, packet))
}
}
+24 -17
View File
@@ -42,7 +42,8 @@ impl Encoder<FramedNymPacket> for NymCodec {
fn encode(&mut self, item: FramedNymPacket, dst: &mut BytesMut) -> Result<(), Self::Error> {
item.header.encode(dst);
let packet_bytes = item.packet.to_bytes()?;
dst.put(packet_bytes.as_slice());
let encoded = packet_bytes.as_slice();
dst.put(encoded);
Ok(())
}
}
@@ -103,22 +104,23 @@ impl Decoder for NymCodec {
// reserve for that.
// we also assume the next packet coming from the same client will use exactly the same versioning
// as the current packet
let mut allocate_for_next_packet = header.size() + PacketSize::AckPacket.size();
if !src.is_empty() {
match Header::decode(src) {
Ok(Some(next_header)) => {
allocate_for_next_packet = next_header.size() + next_header.packet_size.size();
}
Ok(None) => {
// we don't have enough information to know how much to reserve, fallback to the ack case
}
// the next frame will be malformed but let's leave handling the error to the next
// call to 'decode', as presumably, the current sphinx packet is still valid
Err(_) => return Ok(Some(nymsphinx_packet)),
};
}
src.reserve(allocate_for_next_packet);
// let mut allocate_for_next_packet = header.size() + PacketSize::AckPacket.size();
// if !src.is_empty() {
// match Header::decode(src) {
// Ok(Some(next_header)) => {
// allocate_for_next_packet = next_header.size() + next_header.packet_size.size();
// }
// Ok(None) => {
// // we don't have enough information to know how much to reserve, fallback to the ack case
// }
// // the next frame will be malformed but let's leave handling the error to the next
// // call to 'decode', as presumably, the current sphinx packet is still valid
// Err(_) => return Ok(Some(nymsphinx_packet)),
// };
// }
// src.reserve(allocate_for_next_packet);
Ok(Some(nymsphinx_packet))
}
}
@@ -154,11 +156,16 @@ mod packet_encoding {
node4_pk,
);
let destination = Destination::new(
DestinationAddressBytes::from_bytes([3u8; DESTINATION_ADDRESS_LENGTH]),
[4u8; IDENTIFIER_LENGTH],
);
let route = &[node1, node2, node3, node4];
let payload = vec![1; 48];
NymPacket::outfox_build(payload, route, Some(size.plaintext_size())).unwrap()
NymPacket::outfox_build(payload, route, &destination, Some(size.plaintext_size())).unwrap()
}
fn make_valid_sphinx_packet(size: PacketSize) -> NymPacket {
+16 -11
View File
@@ -24,14 +24,19 @@ impl FramedNymPacket {
// already managed to somehow create a sphinx packet
let packet_size = PacketSize::get_type(packet.len()).unwrap();
FramedNymPacket {
header: Header {
packet_version: PacketVersion::new(use_legacy_version),
packet_size,
packet_type,
},
packet,
}
let use_legacy = if packet_type == PacketType::Outfox {
false
} else {
use_legacy_version
};
let header = Header {
packet_version: PacketVersion::new(use_legacy),
packet_size,
packet_type,
};
FramedNymPacket { header, packet }
}
pub fn header(&self) -> Header {
@@ -96,16 +101,16 @@ impl Header {
pub(crate) fn encode(&self, dst: &mut BytesMut) {
// we reserve one byte for `packet_size` and the other for `mode`
dst.reserve(Self::LEGACY_SIZE);
// dst.reserve(Self::LEGACY_SIZE);
if let Some(version) = self.packet_version.as_u8() {
dst.reserve(Self::VERSIONED_SIZE);
// dst.reserve(Self::VERSIONED_SIZE);
dst.put_u8(version)
}
dst.put_u8(self.packet_size as u8);
dst.put_u8(self.packet_type as u8);
// reserve bytes for the actual packet
dst.reserve(self.packet_size.size());
// dst.reserve(self.packet_size.size());
}
pub(crate) fn decode(src: &mut BytesMut) -> Result<Option<Self>, NymCodecError> {
+10 -55
View File
@@ -28,11 +28,9 @@ const EXTENDED_PACKET_SIZE_8: usize = 8 * 1024 + SPHINX_PACKET_OVERHEAD;
const EXTENDED_PACKET_SIZE_16: usize = 16 * 1024 + SPHINX_PACKET_OVERHEAD;
const EXTENDED_PACKET_SIZE_32: usize = 32 * 1024 + SPHINX_PACKET_OVERHEAD;
const OUTFOX_ACK_PACKET_SIZE: usize = ACK_IV_SIZE + FRAG_ID_LEN + OUTFOX_PACKET_OVERHEAD;
// 48 is min outfox payload size
const OUTFOX_ACK_PACKET_SIZE: usize = 48 + OUTFOX_PACKET_OVERHEAD;
const OUTFOX_REGULAR_PACKET_SIZE: usize = 2 * 1024 + OUTFOX_PACKET_OVERHEAD;
const OUTFOX_EXTENDED_PACKET_SIZE_8: usize = 8 * 1024 + OUTFOX_PACKET_OVERHEAD;
const OUTFOX_EXTENDED_PACKET_SIZE_16: usize = 16 * 1024 + OUTFOX_PACKET_OVERHEAD;
const OUTFOX_EXTENDED_PACKET_SIZE_32: usize = 32 * 1024 + OUTFOX_PACKET_OVERHEAD;
#[derive(Debug, Error)]
pub enum InvalidPacketSize {
@@ -76,18 +74,6 @@ pub enum PacketSize {
// for sending SURB-ACKs
#[serde(rename = "outfox_ack")]
OutfoxAckPacket = 7,
// for example for streaming fast and furious in uncompressed 10bit 4K HDR quality
#[serde(rename = "outfox_extended32")]
OutfoxExtendedPacket32 = 8,
// for example for streaming fast and furious in heavily compressed lossy RealPlayer quality
#[serde(rename = "outfox_extended8")]
OutfoxExtendedPacket8 = 9,
// for example for streaming fast and furious in compressed XviD quality
#[serde(rename = "outfox_extended16")]
OutfoxExtendedPacket16 = 10,
}
impl PartialOrd for PacketSize {
@@ -116,9 +102,6 @@ impl FromStr for PacketSize {
"extended32" => Ok(Self::ExtendedPacket32),
"outfox_regular" => Ok(Self::OutfoxRegularPacket),
"outfox_ack" => Ok(Self::OutfoxAckPacket),
"outfox_extended8" => Ok(Self::OutfoxExtendedPacket8),
"outfox_extended16" => Ok(Self::OutfoxExtendedPacket16),
"outfox_extended32" => Ok(Self::OutfoxExtendedPacket32),
s => Err(InvalidPacketSize::UnknownExtendedPacketVariant {
received: s.to_string(),
}),
@@ -136,9 +119,6 @@ impl Display for PacketSize {
PacketSize::ExtendedPacket16 => write!(f, "extended16"),
PacketSize::OutfoxRegularPacket => write!(f, "outfox_regular"),
PacketSize::OutfoxAckPacket => write!(f, "outfox_ack"),
PacketSize::OutfoxExtendedPacket32 => write!(f, "outfox_extended32"),
PacketSize::OutfoxExtendedPacket8 => write!(f, "outfox_extended8"),
PacketSize::OutfoxExtendedPacket16 => write!(f, "outfox_extended16"),
}
}
}
@@ -165,15 +145,6 @@ impl TryFrom<u8> for PacketSize {
_ if value == (PacketSize::ExtendedPacket32 as u8) => Ok(Self::ExtendedPacket32),
_ if value == (PacketSize::OutfoxRegularPacket as u8) => Ok(Self::OutfoxRegularPacket),
_ if value == (PacketSize::OutfoxAckPacket as u8) => Ok(Self::OutfoxAckPacket),
_ if value == (PacketSize::OutfoxExtendedPacket8 as u8) => {
Ok(Self::OutfoxExtendedPacket8)
}
_ if value == (PacketSize::OutfoxExtendedPacket16 as u8) => {
Ok(Self::OutfoxExtendedPacket16)
}
_ if value == (PacketSize::OutfoxExtendedPacket32 as u8) => {
Ok(Self::OutfoxExtendedPacket32)
}
v => Err(InvalidPacketSize::UnknownPacketTag { received: v }),
}
}
@@ -189,9 +160,6 @@ impl PacketSize {
PacketSize::ExtendedPacket32 => EXTENDED_PACKET_SIZE_32,
PacketSize::OutfoxRegularPacket => OUTFOX_REGULAR_PACKET_SIZE,
PacketSize::OutfoxAckPacket => OUTFOX_ACK_PACKET_SIZE,
PacketSize::OutfoxExtendedPacket8 => OUTFOX_EXTENDED_PACKET_SIZE_8,
PacketSize::OutfoxExtendedPacket16 => OUTFOX_EXTENDED_PACKET_SIZE_16,
PacketSize::OutfoxExtendedPacket32 => OUTFOX_EXTENDED_PACKET_SIZE_32,
}
}
@@ -202,11 +170,7 @@ impl PacketSize {
| PacketSize::ExtendedPacket8
| PacketSize::ExtendedPacket16
| PacketSize::ExtendedPacket32 => HEADER_SIZE,
PacketSize::OutfoxRegularPacket
| PacketSize::OutfoxAckPacket
| PacketSize::OutfoxExtendedPacket8
| PacketSize::OutfoxExtendedPacket16
| PacketSize::OutfoxExtendedPacket32 => MIX_PARAMS_LEN,
PacketSize::OutfoxRegularPacket | PacketSize::OutfoxAckPacket => MIX_PARAMS_LEN,
}
}
@@ -217,11 +181,9 @@ impl PacketSize {
| 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,
PacketSize::OutfoxRegularPacket | PacketSize::OutfoxAckPacket => {
OUTFOX_PACKET_OVERHEAD - MIX_PARAMS_LEN // Mix params are calculated into the total overhead so we take them out here
}
}
}
@@ -244,16 +206,12 @@ impl PacketSize {
Ok(PacketSize::ExtendedPacket16)
} else if PacketSize::ExtendedPacket32.size() == size {
Ok(PacketSize::ExtendedPacket32)
} else if PacketSize::OutfoxRegularPacket.size() == size {
} else if PacketSize::OutfoxRegularPacket.size() == size
|| PacketSize::OutfoxRegularPacket.size() == size + 6
{
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 })
}
@@ -267,10 +225,7 @@ impl PacketSize {
| PacketSize::OutfoxRegularPacket => false,
PacketSize::ExtendedPacket8
| PacketSize::ExtendedPacket16
| PacketSize::ExtendedPacket32
| PacketSize::OutfoxExtendedPacket8
| PacketSize::OutfoxExtendedPacket16
| PacketSize::OutfoxExtendedPacket32 => true,
| PacketSize::ExtendedPacket32 => true,
}
}
@@ -6,6 +6,8 @@ use std::convert::TryFrom;
use std::fmt;
use thiserror::Error;
use crate::PacketSize;
#[derive(Error, Debug)]
#[error("{received} is not a valid packet mode tag")]
pub struct InvalidPacketType {
@@ -58,3 +60,17 @@ impl TryFrom<u8> for PacketType {
}
}
}
impl From<PacketSize> for PacketType {
fn from(s: PacketSize) -> Self {
match s {
PacketSize::RegularPacket => PacketType::Mix,
PacketSize::AckPacket => PacketType::Mix,
PacketSize::ExtendedPacket32 => PacketType::Mix,
PacketSize::ExtendedPacket8 => PacketType::Mix,
PacketSize::ExtendedPacket16 => PacketType::Mix,
PacketSize::OutfoxRegularPacket => PacketType::Outfox,
PacketSize::OutfoxAckPacket => PacketType::Outfox,
}
}
}
+11 -2
View File
@@ -11,12 +11,14 @@ use nym_sphinx_anonymous_replies::requests::{
ReplyMessageContent,
};
use nym_sphinx_chunking::fragment::Fragment;
use nym_sphinx_params::{PacketSize, ReplySurbKeyDigestAlgorithm};
use nym_sphinx_params::{PacketSize, PacketType, ReplySurbKeyDigestAlgorithm};
use rand::Rng;
use std::fmt::{Display, Formatter};
use thiserror::Error;
pub(crate) const ACK_OVERHEAD: usize = MAX_NODE_ADDRESS_UNPADDED_LEN + PacketSize::AckPacket.size();
pub(crate) const OUTFOX_ACK_OVERHEAD: usize =
MAX_NODE_ADDRESS_UNPADDED_LEN + PacketSize::AckPacket.size();
#[derive(Debug, Error)]
pub enum NymMessageError {
@@ -187,8 +189,15 @@ impl NymMessage {
NymMessage::Reply(_) => ReplySurbKeyDigestAlgorithm::output_size(),
};
let packet_type = PacketType::from(packet_size);
// each packet will contain an ack + variant specific data (as described above)
packet_size.plaintext_size() - ACK_OVERHEAD - variant_overhead
match packet_type {
PacketType::Outfox => {
packet_size.plaintext_size() - OUTFOX_ACK_OVERHEAD - variant_overhead
}
_ => packet_size.plaintext_size() - ACK_OVERHEAD - variant_overhead,
}
}
/// Length of the actual (from the **message** point of view) data that is available in each packet.
+15 -8
View File
@@ -1,9 +1,8 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::message::{NymMessage, ACK_OVERHEAD};
use crate::message::{NymMessage, ACK_OVERHEAD, OUTFOX_ACK_OVERHEAD};
use crate::NymPayloadBuilder;
use log::info;
use nym_crypto::asymmetric::encryption;
use nym_crypto::Digest;
use nym_sphinx_acknowledgements::surb_ack::SurbAck;
@@ -114,7 +113,10 @@ pub trait FragmentPreparer {
// each reply attaches the digest of the encryption key so that the recipient could
// lookup correct key for decryption,
let reply_overhead = ReplySurbKeyDigestAlgorithm::output_size();
let expected_plaintext = fragment.serialized_size() + ACK_OVERHEAD + reply_overhead;
let expected_plaintext = match packet_type {
PacketType::Outfox => fragment.serialized_size() + OUTFOX_ACK_OVERHEAD + reply_overhead,
_ => fragment.serialized_size() + ACK_OVERHEAD + reply_overhead,
};
// the reason we're unwrapping (or rather 'expecting') here rather than handling the error
// more gracefully is that this error should never be reached as it implies incorrect chunking
@@ -157,7 +159,7 @@ pub trait FragmentPreparer {
// well as the total delay of the ack packet.
// we don't know the delays inside the reply surbs so we use best-effort estimation from our poisson distribution
total_delay: expected_forward_delay + ack_delay,
mix_packet: MixPacket::new(first_hop_address, sphinx_packet, Default::default()),
mix_packet: MixPacket::new(first_hop_address, sphinx_packet, packet_type),
fragment_identifier,
})
}
@@ -188,12 +190,16 @@ pub trait FragmentPreparer {
packet_recipient: &Recipient,
packet_type: PacketType,
) -> Result<PreparedFragment, NymTopologyError> {
info!("Preparing {packet_type} packet for sending");
// each plain or repliable packet (i.e. not a reply) attaches an ephemeral public key so that the recipient
// could perform diffie-hellman with its own keys followed by a kdf to re-derive
// the packet encryption key
let non_reply_overhead = encryption::PUBLIC_KEY_SIZE;
let expected_plaintext = fragment.serialized_size() + ACK_OVERHEAD + non_reply_overhead;
let expected_plaintext = match packet_type {
PacketType::Outfox => {
fragment.serialized_size() + OUTFOX_ACK_OVERHEAD + non_reply_overhead
}
_ => fragment.serialized_size() + ACK_OVERHEAD + non_reply_overhead,
};
// the reason we're unwrapping (or rather 'expecting') here rather than handling the error
// more gracefully is that this error should never be reached as it implies incorrect chunking
@@ -235,7 +241,8 @@ pub trait FragmentPreparer {
PacketType::Outfox => NymPacket::outfox_build(
packet_payload,
route.as_slice(),
Some(packet_size.payload_size()),
&destination,
Some(packet_size.plaintext_size()),
)?,
PacketType::Mix => NymPacket::sphinx_build(
packet_size.payload_size(),
@@ -262,7 +269,7 @@ pub trait FragmentPreparer {
// well as the total delay of the ack packet.
// note that the last hop of the packet is a gateway that does not do any delays
total_delay: delays.iter().take(delays.len() - 1).sum::<Delay>() + ack_delay,
mix_packet: MixPacket::new(first_hop_address, packet, Default::default()),
mix_packet: MixPacket::new(first_hop_address, packet, packet_type),
fragment_identifier,
})
}
+1
View File
@@ -46,6 +46,7 @@ impl NymPayloadBuilder {
// 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
Ok(NymPayload(
surb_ack_bytes
.into_iter()
+22 -4
View File
@@ -5,7 +5,7 @@ pub use nym_outfox::{
constants::MIX_PARAMS_LEN, constants::OUTFOX_PACKET_OVERHEAD, error::OutfoxError,
};
// re-exporting types and constants available in sphinx
use nym_outfox::packet::OutfoxPacket;
use nym_outfox::packet::{OutfoxPacket, OutfoxProcessedPacket};
pub use sphinx_packet::{
constants::{
self, DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, MAX_PATH_LENGTH, NODE_ADDRESS_LENGTH,
@@ -41,6 +41,11 @@ pub enum NymPacket {
Outfox(OutfoxPacket),
}
pub enum NymProcessedPacket {
Sphinx(ProcessedPacket),
Outfox(OutfoxProcessedPacket),
}
impl fmt::Debug for NymPacket {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
@@ -77,11 +82,13 @@ impl NymPacket {
pub fn outfox_build<M: AsRef<[u8]>>(
payload: M,
route: &[Node],
destination: &Destination,
size: Option<usize>,
) -> Result<NymPacket, NymPacketError> {
Ok(NymPacket::Outfox(OutfoxPacket::build(
payload,
route.try_into()?,
destination,
size,
)?))
}
@@ -108,10 +115,21 @@ impl NymPacket {
}
}
pub fn process(self, node_secret_key: &PrivateKey) -> Result<ProcessedPacket, NymPacketError> {
pub fn process(
self,
node_secret_key: &PrivateKey,
) -> Result<NymProcessedPacket, NymPacketError> {
match self {
NymPacket::Sphinx(packet) => Ok(packet.process(node_secret_key)?),
NymPacket::Outfox(_packet) => todo!(),
NymPacket::Sphinx(packet) => {
Ok(NymProcessedPacket::Sphinx(packet.process(node_secret_key)?))
}
NymPacket::Outfox(mut packet) => {
let next_address = packet.decode_next_layer(node_secret_key)?;
Ok(NymProcessedPacket::Outfox(OutfoxProcessedPacket::new(
packet,
next_address,
)))
}
}
}
}
@@ -499,13 +499,13 @@ impl SocksClient {
})
.unwrap();
info!(
debug!(
"Starting proxy for {} (id: {})",
remote_address.clone(),
self.connection_id
);
self.run_proxy(mix_receiver, remote_address.clone()).await;
info!(
debug!(
"Proxy for {} is finished (id: {})",
remote_address, self.connection_id
);
@@ -78,7 +78,7 @@ impl SocksRequest {
where
R: AsyncRead + Unpin,
{
log::info!("read from stream socks5");
log::trace!("read from stream socks5");
let mut packet = [0u8; 4];
// Read a byte from the stream and determine the version being requested
-1
View File
@@ -9,7 +9,6 @@ use nym_sphinx_addressing::nodes::NodeIdentity;
use rand::prelude::SliceRandom;
use nym_sphinx_types::{Node as SphinxNode, NymPacketError};
use rand::{CryptoRng, Rng};
use std::array::TryFromSliceError;
use std::collections::BTreeMap;
use std::convert::TryInto;
use std::fmt::{self, Display, Formatter};