use crate::NymPayloadBuilder;
use crate::message::{ACK_OVERHEAD, NymMessage, OUTFOX_ACK_OVERHEAD};
use nym_crypto::Digest;
use nym_crypto::asymmetric::x25519;
use nym_sphinx_acknowledgements::AckKey;
use nym_sphinx_acknowledgements::surb_ack::SurbAck;
use nym_sphinx_addressing::clients::Recipient;
use nym_sphinx_addressing::nodes::NymNodeRoutingAddress;
use nym_sphinx_anonymous_replies::reply_surb::ReplySurb;
use nym_sphinx_chunking::fragment::{Fragment, FragmentIdentifier};
use nym_sphinx_forwarding::packet::MixPacket;
use nym_sphinx_params::packet_sizes::PacketSize;
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, SphinxKeyRotation};
use nym_sphinx_types::{Delay, NymPacket};
use nym_topology::{NymRouteProvider, NymTopologyError};
use rand::{CryptoRng, Rng, SeedableRng};
use rand_chacha::ChaCha8Rng;
use tracing::*;
use nym_sphinx_anonymous_replies::ReplySurbWithKeyRotation;
use nym_sphinx_chunking::monitoring;
use std::time::Duration;
pub(crate) mod payload;
pub struct PreparedFragment {
pub total_delay: Delay,
pub mix_packet: MixPacket,
pub fragment_identifier: FragmentIdentifier,
}
impl From<PreparedFragment> for MixPacket {
fn from(value: PreparedFragment) -> Self {
value.mix_packet
}
}
pub trait FragmentPreparer {
type Rng: CryptoRng + Rng;
fn use_legacy_sphinx_format(&self) -> bool;
fn mix_hops_disabled(&self) -> bool {
false
}
fn deterministic_route_selection(&self) -> bool;
fn rng(&mut self) -> &mut Self::Rng;
fn nonce(&self) -> i32;
fn average_packet_delay(&self) -> Duration;
fn average_ack_delay(&self) -> Duration;
fn generate_surb_ack(
&mut self,
recipient: &Recipient,
fragment_id: FragmentIdentifier,
topology: &NymRouteProvider,
ack_key: &AckKey,
packet_type: PacketType,
) -> Result<SurbAck, NymTopologyError> {
let ack_delay = self.average_ack_delay();
let use_legacy_sphinx_format = self.use_legacy_sphinx_format();
let disable_mix_hops = self.mix_hops_disabled();
SurbAck::construct(
self.rng(),
use_legacy_sphinx_format,
recipient,
ack_key,
fragment_id.to_bytes(),
ack_delay,
topology,
packet_type,
disable_mix_hops,
)
}
fn prepare_reply_chunk_for_sending(
&mut self,
fragment: Fragment,
topology: &NymRouteProvider,
ack_key: &AckKey,
reply_surb: ReplySurbWithKeyRotation,
packet_sender: &Recipient,
packet_type: PacketType,
) -> Result<PreparedFragment, NymTopologyError> {
debug!("Preparing reply chunk for sending");
let reply_overhead = ReplySurbKeyDigestAlgorithm::output_size();
let expected_plaintext = match packet_type {
PacketType::Outfox => fragment.serialized_size() + OUTFOX_ACK_OVERHEAD + reply_overhead,
_ => fragment.serialized_size() + ACK_OVERHEAD + reply_overhead,
};
let packet_size = PacketSize::get_type_from_plaintext(expected_plaintext, PacketType::Mix)
.expect("the message has been incorrectly fragmented");
let expected_forward_delay =
Delay::new_from_millis((self.average_packet_delay().as_millis() * 3) as u64);
let fragment_identifier = fragment.fragment_identifier();
let surb_ack = self.generate_surb_ack(
packet_sender,
fragment_identifier,
topology,
ack_key,
packet_type,
)?;
let ack_delay = surb_ack.expected_total_delay();
let packet_payload = match NymPayloadBuilder::new(fragment, surb_ack)
.build_reply(reply_surb.encryption_key())
{
Ok(payload) => payload,
Err(_e) => return Err(NymTopologyError::PayloadBuilder),
};
let applied_surb = reply_surb
.apply_surb(packet_payload, packet_size, packet_type)
.unwrap();
Ok(PreparedFragment {
total_delay: expected_forward_delay + ack_delay,
mix_packet: MixPacket::from_applied_surb(applied_surb, packet_type),
fragment_identifier,
})
}
#[allow(clippy::too_many_arguments)]
fn prepare_chunk_for_sending(
&mut self,
fragment: Fragment,
topology: &NymRouteProvider,
ack_key: &AckKey,
packet_sender: &Recipient,
packet_recipient: &Recipient,
packet_type: PacketType,
) -> Result<PreparedFragment, NymTopologyError> {
debug!("Preparing chunk for sending");
let fragment_header = fragment.header();
let destination = packet_recipient.gateway();
monitoring::fragment_sent(&fragment, self.nonce(), destination);
let non_reply_overhead = x25519::PUBLIC_KEY_SIZE;
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,
};
let packet_size = PacketSize::get_type_from_plaintext(expected_plaintext, packet_type)
.expect("the message has been incorrectly fragmented");
let rotation_id = topology.current_key_rotation();
let sphinx_key_rotation = SphinxKeyRotation::from(rotation_id);
let fragment_identifier = fragment.fragment_identifier();
let surb_ack = self.generate_surb_ack(
packet_sender,
fragment_identifier,
topology,
ack_key,
packet_type,
)?;
let ack_delay = surb_ack.expected_total_delay();
let packet_payload = match NymPayloadBuilder::new(fragment, surb_ack)
.build_regular(self.rng(), packet_recipient.encryption_key())
{
Ok(payload) => payload,
Err(_e) => return Err(NymTopologyError::PayloadBuilder),
};
trace!("Preparing chunk for sending");
let route = if self.mix_hops_disabled() {
topology.empty_route_to_egress(destination)?
} else if self.deterministic_route_selection() {
trace!("using deterministic route selection");
let seed = fragment_header.seed().wrapping_mul(self.nonce());
let mut rng = ChaCha8Rng::seed_from_u64(seed as u64);
topology.random_route_to_egress(&mut rng, destination)?
} else {
trace!("using pseudorandom route selection");
let mut rng = self.rng();
topology.random_route_to_egress(&mut rng, destination)?
};
let destination = packet_recipient.as_sphinx_destination();
let delays =
nym_sphinx_routing::generate_hop_delays(self.average_packet_delay(), route.len());
let packet = match packet_type {
PacketType::Outfox => NymPacket::outfox_build(
packet_payload,
route.as_slice(),
&destination,
Some(packet_size.plaintext_size()),
)?,
PacketType::Mix => NymPacket::sphinx_build(
self.use_legacy_sphinx_format(),
packet_size.payload_size(),
packet_payload,
&route,
&destination,
&delays,
)?,
};
let first_hop_address =
NymNodeRoutingAddress::try_from(route.first().unwrap().address).unwrap();
Ok(PreparedFragment {
total_delay: delays.iter().take(delays.len() - 1).sum::<Delay>() + ack_delay,
mix_packet: MixPacket::new(first_hop_address, packet, packet_type, sphinx_key_rotation),
fragment_identifier,
})
}
fn pad_and_split_message(
&mut self,
message: NymMessage,
packet_size: PacketSize,
) -> Vec<Fragment> {
let plaintext_per_packet = message.available_sphinx_plaintext_per_packet(packet_size);
message
.pad_to_full_packet_lengths(plaintext_per_packet)
.split_into_fragments(self.rng(), plaintext_per_packet)
}
}
#[derive(Clone)]
#[must_use]
pub struct MessagePreparer<R> {
rng: R,
deterministic_route_selection: bool,
sender_address: Recipient,
average_packet_delay: Duration,
average_ack_delay: Duration,
use_legacy_sphinx_format: bool,
nonce: i32,
pub disable_mix_hops: bool,
}
impl<R> MessagePreparer<R>
where
R: CryptoRng + Rng,
{
pub fn new(
rng: R,
deterministic_route_selection: bool,
sender_address: Recipient,
average_packet_delay: Duration,
average_ack_delay: Duration,
use_legacy_sphinx_format: bool,
disable_mix_hops: bool,
) -> Self {
let mut rng = rng;
let nonce = rng.r#gen();
MessagePreparer {
rng,
deterministic_route_selection,
sender_address,
average_packet_delay,
average_ack_delay,
use_legacy_sphinx_format,
nonce,
disable_mix_hops,
}
}
pub fn set_sender_address(&mut self, sender_address: Recipient) {
self.sender_address = sender_address;
}
pub fn generate_reply_surbs(
&mut self,
use_legacy_reply_surb_format: bool,
amount: usize,
topology: &NymRouteProvider,
) -> Result<Vec<ReplySurbWithKeyRotation>, NymTopologyError> {
let mut reply_surbs = Vec::with_capacity(amount);
let disabled_mix_hops = self.mix_hops_disabled();
let key_rotation = SphinxKeyRotation::from(topology.current_key_rotation());
for _ in 0..amount {
let reply_surb = ReplySurb::construct(
&mut self.rng,
&self.sender_address,
self.average_packet_delay,
use_legacy_reply_surb_format,
topology,
disabled_mix_hops,
)?
.with_key_rotation(key_rotation);
reply_surbs.push(reply_surb)
}
Ok(reply_surbs)
}
pub fn prepare_reply_chunk_for_sending(
&mut self,
fragment: Fragment,
topology: &NymRouteProvider,
ack_key: &AckKey,
reply_surb: ReplySurbWithKeyRotation,
packet_type: PacketType,
) -> Result<PreparedFragment, NymTopologyError> {
let sender = self.sender_address;
<Self as FragmentPreparer>::prepare_reply_chunk_for_sending(
self,
fragment,
topology,
ack_key,
reply_surb,
&sender,
packet_type,
)
}
pub fn prepare_chunk_for_sending(
&mut self,
fragment: Fragment,
topology: &NymRouteProvider,
ack_key: &AckKey,
packet_recipient: &Recipient,
packet_type: PacketType,
) -> Result<PreparedFragment, NymTopologyError> {
let sender = self.sender_address;
<Self as FragmentPreparer>::prepare_chunk_for_sending(
self,
fragment,
topology,
ack_key,
&sender,
packet_recipient,
packet_type,
)
}
pub fn generate_surb_ack(
&mut self,
fragment_id: FragmentIdentifier,
topology: &NymRouteProvider,
ack_key: &AckKey,
packet_type: PacketType,
) -> Result<SurbAck, NymTopologyError> {
let sender = self.sender_address;
<Self as FragmentPreparer>::generate_surb_ack(
self,
&sender,
fragment_id,
topology,
ack_key,
packet_type,
)
}
pub fn pad_and_split_message(
&mut self,
message: NymMessage,
packet_size: PacketSize,
) -> Vec<Fragment> {
<Self as FragmentPreparer>::pad_and_split_message(self, message, packet_size)
}
}
impl<R: CryptoRng + Rng> FragmentPreparer for MessagePreparer<R> {
type Rng = R;
fn mix_hops_disabled(&self) -> bool {
self.disable_mix_hops
}
fn use_legacy_sphinx_format(&self) -> bool {
self.use_legacy_sphinx_format
}
fn deterministic_route_selection(&self) -> bool {
self.deterministic_route_selection
}
fn rng(&mut self) -> &mut Self::Rng {
&mut self.rng
}
fn nonce(&self) -> i32 {
self.nonce
}
fn average_packet_delay(&self) -> Duration {
self.average_packet_delay
}
fn average_ack_delay(&self) -> Duration {
self.average_ack_delay
}
}