wip: choosing correct key for packet processing
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::packet::{FramedNymPacket, Header};
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use nym_sphinx_params::key_rotation::InvalidSphinxKeyRotation;
|
||||
use nym_sphinx_params::packet_sizes::{InvalidPacketSize, PacketSize};
|
||||
use nym_sphinx_params::packet_types::InvalidPacketType;
|
||||
use nym_sphinx_params::packet_version::{InvalidPacketVersion, PacketVersion};
|
||||
@@ -23,6 +24,9 @@ pub enum NymCodecError {
|
||||
#[error("the packet version information was malformed: {0}")]
|
||||
InvalidPacketVersion(#[from] InvalidPacketVersion),
|
||||
|
||||
#[error("the sphinx key rotation information was malformed: {0}")]
|
||||
InvalidSphinxKeyRotation(#[from] InvalidSphinxKeyRotation),
|
||||
|
||||
#[error("received unsupported packet version {received}. max supported is {max_supported}")]
|
||||
UnsupportedPacketVersion {
|
||||
received: PacketVersion,
|
||||
@@ -65,72 +69,74 @@ impl Decoder for NymCodec {
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, 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::SIZE + PacketSize::AckPacket.size());
|
||||
// conservative case, i.e. receiving a legacy ack packet
|
||||
src.reserve(Header::INITIAL_SIZE + PacketSize::AckPacket.size());
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// because header is so small and simple it makes no point in trying to cache
|
||||
// this result. It will be just simpler to re-decode it
|
||||
let header = match Header::decode(src)? {
|
||||
Some(header) => header,
|
||||
None => return Ok(None), // we have some data but not enough to get header back
|
||||
};
|
||||
|
||||
let packet_size = header.packet_size.size();
|
||||
let frame_len = Header::SIZE + packet_size;
|
||||
|
||||
if src.len() < frame_len {
|
||||
// we don't have enough bytes to read the rest of frame
|
||||
src.reserve(packet_size);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// advance buffer past the header - at this point we have enough bytes
|
||||
src.advance(Header::SIZE);
|
||||
let packet_bytes = src.split_to(packet_size);
|
||||
let packet = if let Some(slice) = packet_bytes.get(..) {
|
||||
// here it could be debatable whether stream is corrupt or not,
|
||||
// but let's go with the safer approach and assume it is.
|
||||
match header.packet_type {
|
||||
PacketType::Outfox => NymPacket::outfox_from_bytes(slice)?,
|
||||
PacketType::Mix => NymPacket::sphinx_from_bytes(slice)?,
|
||||
}
|
||||
} else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// let packet = SphinxPacket::from_bytes(&sphinx_packet_bytes)?;
|
||||
let nymsphinx_packet = FramedNymPacket { header, packet };
|
||||
|
||||
// As per docs:
|
||||
// Before returning from the function, implementations should ensure that the buffer
|
||||
// has appropriate capacity in anticipation of future calls to decode.
|
||||
// Failing to do so leads to inefficiency.
|
||||
|
||||
// if we have enough bytes to decode the header of the next packet, we can reserve enough bytes for
|
||||
// the entire next frame, if not, we assume the next frame is an ack packet and
|
||||
// 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 = 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))
|
||||
todo!()
|
||||
//
|
||||
// // because header is so small and simple it makes no point in trying to cache
|
||||
// // this result. It will be just simpler to re-decode it
|
||||
// let header = match Header::decode(src)? {
|
||||
// Some(header) => header,
|
||||
// None => return Ok(None), // we have some data but not enough to get header back
|
||||
// };
|
||||
//
|
||||
// let packet_size = header.packet_size.size();
|
||||
// let frame_len = Header::SIZE + packet_size;
|
||||
//
|
||||
// if src.len() < frame_len {
|
||||
// // we don't have enough bytes to read the rest of frame
|
||||
// src.reserve(packet_size);
|
||||
// return Ok(None);
|
||||
// }
|
||||
//
|
||||
// // advance buffer past the header - at this point we have enough bytes
|
||||
// src.advance(Header::SIZE);
|
||||
// let packet_bytes = src.split_to(packet_size);
|
||||
// let packet = if let Some(slice) = packet_bytes.get(..) {
|
||||
// // here it could be debatable whether stream is corrupt or not,
|
||||
// // but let's go with the safer approach and assume it is.
|
||||
// match header.packet_type {
|
||||
// PacketType::Outfox => NymPacket::outfox_from_bytes(slice)?,
|
||||
// PacketType::Mix => NymPacket::sphinx_from_bytes(slice)?,
|
||||
// }
|
||||
// } else {
|
||||
// return Ok(None);
|
||||
// };
|
||||
//
|
||||
// // let packet = SphinxPacket::from_bytes(&sphinx_packet_bytes)?;
|
||||
// let nymsphinx_packet = FramedNymPacket { header, packet };
|
||||
//
|
||||
// // As per docs:
|
||||
// // Before returning from the function, implementations should ensure that the buffer
|
||||
// // has appropriate capacity in anticipation of future calls to decode.
|
||||
// // Failing to do so leads to inefficiency.
|
||||
//
|
||||
// // if we have enough bytes to decode the header of the next packet, we can reserve enough bytes for
|
||||
// // the entire next frame, if not, we assume the next frame is an ack packet and
|
||||
// // 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 = 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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::codec::NymCodecError;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use nym_sphinx_params::key_rotation::SphinxKeyRotation;
|
||||
use nym_sphinx_params::packet_sizes::PacketSize;
|
||||
use nym_sphinx_params::packet_version::{PacketVersion, CURRENT_PACKET_VERSION};
|
||||
use nym_sphinx_params::PacketType;
|
||||
@@ -18,7 +19,11 @@ pub struct FramedNymPacket {
|
||||
}
|
||||
|
||||
impl FramedNymPacket {
|
||||
pub fn new(packet: NymPacket, packet_type: PacketType) -> Self {
|
||||
pub fn new(
|
||||
packet: NymPacket,
|
||||
packet_type: PacketType,
|
||||
key_rotation: SphinxKeyRotation,
|
||||
) -> 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();
|
||||
@@ -26,6 +31,7 @@ impl FramedNymPacket {
|
||||
let header = Header {
|
||||
packet_version: PacketVersion::new(),
|
||||
packet_size,
|
||||
key_rotation,
|
||||
packet_type,
|
||||
};
|
||||
|
||||
@@ -60,13 +66,16 @@ impl FramedNymPacket {
|
||||
// Contains any metadata that might be useful for sending between mix nodes.
|
||||
// TODO: in theory all those data could be put in a single `u8` by setting appropriate bits,
|
||||
// but would that really be worth it?
|
||||
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub struct Header {
|
||||
/// Represents the wire format version used to construct this packet.
|
||||
pub(crate) packet_version: PacketVersion,
|
||||
pub packet_version: PacketVersion,
|
||||
|
||||
/// Represents type and consequently size of the included SphinxPacket.
|
||||
pub(crate) packet_size: PacketSize,
|
||||
pub packet_size: PacketSize,
|
||||
|
||||
/// Represents information regarding which key rotation has been used for constructing this packet.
|
||||
pub key_rotation: SphinxKeyRotation,
|
||||
|
||||
/// Represents whether this packet is sent in a `vpn_mode` meaning it should not get delayed
|
||||
/// and shared keys might get reused. Mixnodes are capable of inferring this mode from the
|
||||
@@ -77,35 +86,36 @@ pub struct Header {
|
||||
/// (note: this will be behind some encryption, either something implemented by us or some SSL action)
|
||||
// Note: currently packet_type is deprecated but is still left as a concept behind to not break
|
||||
// compatibility with existing network
|
||||
pub(crate) packet_type: PacketType,
|
||||
pub packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub(crate) const SIZE: usize = 3;
|
||||
|
||||
pub fn outfox() -> Header {
|
||||
Header {
|
||||
packet_version: PacketVersion::default(),
|
||||
packet_size: PacketSize::OutfoxRegularPacket,
|
||||
packet_type: PacketType::Outfox,
|
||||
}
|
||||
}
|
||||
pub(crate) const INITIAL_SIZE: usize = 3;
|
||||
pub(crate) const V8_SIZE: usize = 4;
|
||||
|
||||
pub(crate) fn encode(&self, dst: &mut BytesMut) {
|
||||
dst.reserve(Self::SIZE);
|
||||
if self.packet_version.is_initial() {
|
||||
dst.reserve(Self::INITIAL_SIZE);
|
||||
} else {
|
||||
dst.reserve(Self::V8_SIZE)
|
||||
}
|
||||
|
||||
dst.put_u8(self.packet_version.as_u8());
|
||||
dst.put_u8(self.packet_size as u8);
|
||||
dst.put_u8(self.packet_type as u8);
|
||||
|
||||
if !self.packet_version.is_initial() {
|
||||
dst.put_u8(self.key_rotation as u8)
|
||||
}
|
||||
|
||||
// reserve bytes for the actual packet
|
||||
dst.reserve(self.packet_size.size());
|
||||
}
|
||||
|
||||
pub(crate) fn decode(src: &mut BytesMut) -> Result<Option<Self>, NymCodecError> {
|
||||
if src.len() < Self::SIZE {
|
||||
if src.len() < Self::INITIAL_SIZE {
|
||||
// can't do anything if we don't have enough bytes - but reserve enough for the next call
|
||||
src.reserve(Self::SIZE);
|
||||
src.reserve(Self::INITIAL_SIZE);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@@ -119,10 +129,22 @@ impl Header {
|
||||
});
|
||||
}
|
||||
|
||||
// we need to be able to decode the full header
|
||||
if !packet_version.is_initial() && src.len() < Self::V8_SIZE {
|
||||
src.reserve(Self::V8_SIZE)
|
||||
}
|
||||
|
||||
let key_rotation = if packet_version.is_initial() {
|
||||
SphinxKeyRotation::Unknown
|
||||
} else {
|
||||
SphinxKeyRotation::try_from(src[3])?
|
||||
};
|
||||
|
||||
Ok(Some(Header {
|
||||
packet_version,
|
||||
packet_size: PacketSize::try_from(src[1])?,
|
||||
packet_type: PacketType::try_from(src[2])?,
|
||||
key_rotation,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,9 @@ pub enum PacketProcessingError {
|
||||
#[error("attempted to partially process an outfox packet")]
|
||||
PartialOutfoxProcessing,
|
||||
|
||||
#[error("the key needed for unwrapping this packet has already expired")]
|
||||
ExpiredKey,
|
||||
|
||||
#[error("this packet has already been processed before")]
|
||||
PacketReplay,
|
||||
}
|
||||
@@ -119,16 +122,19 @@ impl PartiallyUnwrappedPacket {
|
||||
pub fn new(
|
||||
received_data: FramedNymPacket,
|
||||
sphinx_key: &PrivateKey,
|
||||
) -> Result<Self, PacketProcessingError> {
|
||||
) -> Result<Self, (FramedNymPacket, PacketProcessingError)> {
|
||||
let partial_result = match received_data.packet() {
|
||||
NymPacket::Sphinx(packet) => {
|
||||
let expanded_shared_secret =
|
||||
packet.header.compute_expanded_shared_secret(sphinx_key);
|
||||
|
||||
// don't continue if the header is malformed
|
||||
packet
|
||||
if let Err(err) = packet
|
||||
.header
|
||||
.ensure_header_integrity(&expanded_shared_secret)?;
|
||||
.ensure_header_integrity(&expanded_shared_secret)
|
||||
{
|
||||
return Err((received_data, err.into()));
|
||||
}
|
||||
|
||||
PartialMixProcessingResult::Sphinx {
|
||||
expanded_shared_secret,
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum SphinxKeyRotation {
|
||||
// for legacy packets, where there's no explicit information which key has been used
|
||||
#[default]
|
||||
Unknown = 0,
|
||||
|
||||
OddRotation = 1,
|
||||
|
||||
EvenRotation = 2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("{received} is not a valid encoding of a sphinx key rotation")]
|
||||
pub struct InvalidSphinxKeyRotation {
|
||||
received: u8,
|
||||
}
|
||||
|
||||
impl From<u32> for SphinxKeyRotation {
|
||||
fn from(value: u32) -> Self {
|
||||
if value % 2 == 0 {
|
||||
SphinxKeyRotation::EvenRotation
|
||||
} else {
|
||||
SphinxKeyRotation::OddRotation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if value is actually provided, it MUST be one of the two. otherwise is invalid
|
||||
impl TryFrom<u8> for SphinxKeyRotation {
|
||||
type Error = InvalidSphinxKeyRotation;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
_ if value == (Self::OddRotation as u8) => Ok(Self::OddRotation),
|
||||
_ if value == (Self::EvenRotation as u8) => Ok(Self::EvenRotation),
|
||||
received => Err(InvalidSphinxKeyRotation { received }),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,12 @@ use nym_crypto::Aes256GcmSiv;
|
||||
type Aes128Ctr = ctr::Ctr64BE<Aes128>;
|
||||
|
||||
// Re-export for ease of use
|
||||
pub use key_rotation::SphinxKeyRotation;
|
||||
pub use packet_sizes::PacketSize;
|
||||
pub use packet_types::PacketType;
|
||||
pub use packet_version::PacketVersion;
|
||||
|
||||
pub mod key_rotation;
|
||||
pub mod packet_sizes;
|
||||
pub mod packet_types;
|
||||
pub mod packet_version;
|
||||
|
||||
@@ -10,13 +10,15 @@ use thiserror::Error;
|
||||
// - packet_version (starting with v1.1.0)
|
||||
// - packet_size indicator
|
||||
// - packet_type
|
||||
// - sphinx key rotation (starting with v1.11.0)
|
||||
const TODO: &str = "update the ^ version number when this PR is almost ready";
|
||||
// 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
|
||||
/// Increment it whenever we perform any breaking change in the wire format!
|
||||
pub const INITIAL_PACKET_VERSION_NUMBER: u8 = 7;
|
||||
|
||||
pub const CURRENT_PACKET_VERSION_NUMBER: u8 = INITIAL_PACKET_VERSION_NUMBER;
|
||||
pub const KEY_ROTATION_VERSION_NUMBER: u8 = 8;
|
||||
pub const CURRENT_PACKET_VERSION_NUMBER: u8 = KEY_ROTATION_VERSION_NUMBER;
|
||||
pub const CURRENT_PACKET_VERSION: PacketVersion =
|
||||
PacketVersion::unchecked(CURRENT_PACKET_VERSION_NUMBER);
|
||||
|
||||
@@ -38,6 +40,10 @@ impl PacketVersion {
|
||||
PacketVersion(CURRENT_PACKET_VERSION_NUMBER)
|
||||
}
|
||||
|
||||
pub fn is_initial(&self) -> bool {
|
||||
self.0 == INITIAL_PACKET_VERSION_NUMBER
|
||||
}
|
||||
|
||||
const fn unchecked(version: u8) -> PacketVersion {
|
||||
PacketVersion(version)
|
||||
}
|
||||
|
||||
@@ -28,11 +28,26 @@ impl ActiveSphinxKeys {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn even(&self) -> Option<impl Deref<Target = SphinxPrivateKey>> {
|
||||
let primary = self.inner.primary_key.load();
|
||||
if primary.is_even_rotation() {
|
||||
return Some(primary);
|
||||
}
|
||||
self.secondary()
|
||||
}
|
||||
|
||||
pub(crate) fn odd(&self) -> Option<impl Deref<Target = SphinxPrivateKey>> {
|
||||
let primary = self.inner.primary_key.load();
|
||||
if !primary.is_even_rotation() {
|
||||
return Some(primary);
|
||||
}
|
||||
self.secondary()
|
||||
}
|
||||
|
||||
pub(crate) fn primary(&self) -> impl Deref<Target = SphinxPrivateKey> {
|
||||
self.inner.primary_key.map(|k: &SphinxPrivateKey| k).load()
|
||||
}
|
||||
|
||||
// can't do the same trait bounds due to required borrow on the option
|
||||
pub(crate) fn secondary(&self) -> Option<impl Deref<Target = SphinxPrivateKey>> {
|
||||
let guard = self.inner.secondary_key.load();
|
||||
if guard.is_none() {
|
||||
|
||||
@@ -31,6 +31,10 @@ impl SphinxPrivateKey {
|
||||
pub(crate) fn x25519_pubkey(&self) -> x25519::PublicKey {
|
||||
self.inner.public_key()
|
||||
}
|
||||
|
||||
pub(crate) fn is_even_rotation(&self) -> bool {
|
||||
self.rotation_id & 1 == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SphinxPrivateKey> for SphinxPublicKey {
|
||||
|
||||
@@ -10,6 +10,7 @@ use nym_sphinx_framing::processing::{
|
||||
process_framed_packet, MixProcessingResult, MixProcessingResultData, PacketProcessingError,
|
||||
PartiallyUnwrappedPacket, ProcessedFinalHop,
|
||||
};
|
||||
use nym_sphinx_params::SphinxKeyRotation;
|
||||
use nym_sphinx_types::{Delay, REPLAY_TAG_SIZE};
|
||||
use std::mem;
|
||||
use std::net::SocketAddr;
|
||||
@@ -212,45 +213,76 @@ impl ConnectionHandler {
|
||||
time_threshold && count_threshold
|
||||
}
|
||||
|
||||
fn try_partially_unwrap_packet(
|
||||
&self,
|
||||
packet: FramedNymPacket,
|
||||
) -> Result<PartiallyUnwrappedPacket, PacketProcessingError> {
|
||||
// based on the received sphinx key rotation information,
|
||||
// attempt to choose appropriate key for processing the packet
|
||||
match packet.header().key_rotation {
|
||||
SphinxKeyRotation::Unknown => {
|
||||
// we have to try both keys, start with the primary as it has higher likelihood of being correct
|
||||
// if let Ok(partially_unwrapped) = PartiallyUnwrappedPacket::new()
|
||||
match PartiallyUnwrappedPacket::new(packet, self.shared.sphinx_keys.primary()) {
|
||||
Ok(unwrapped_packet) => Ok(unwrapped_packet),
|
||||
Err((packet, err)) => {
|
||||
if let Some(secondary) = self.shared.sphinx_keys.secondary() {
|
||||
PartiallyUnwrappedPacket::new(packet, secondary).map_err(|(_, err)| err)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SphinxKeyRotation::OddRotation => {
|
||||
let Some(odd_key) = self.shared.sphinx_keys.odd() else {
|
||||
return Err(PacketProcessingError::ExpiredKey);
|
||||
};
|
||||
PartiallyUnwrappedPacket::new(packet, odd_key).map_err(|(_, err)| err)
|
||||
}
|
||||
SphinxKeyRotation::EvenRotation => {
|
||||
let Some(even_key) = self.shared.sphinx_keys.even() else {
|
||||
return Err(PacketProcessingError::ExpiredKey);
|
||||
};
|
||||
PartiallyUnwrappedPacket::new(packet, even_key).map_err(|(_, err)| err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_received_packet_with_replay_detection(
|
||||
&mut self,
|
||||
now: Instant,
|
||||
packet: FramedNymPacket,
|
||||
) {
|
||||
todo!()
|
||||
//
|
||||
// // 1. derive and expand shared secret
|
||||
// // also check the header integrity
|
||||
// let partially_unwrapped = match PartiallyUnwrappedPacket::new(
|
||||
// packet,
|
||||
// self.shared.sphinx_keys.private_key().as_ref(),
|
||||
// ) {
|
||||
// Ok(unwrapped) => unwrapped,
|
||||
// Err(err) => {
|
||||
// trace!("failed to process received mix packet: {err}");
|
||||
// self.shared
|
||||
// .metrics
|
||||
// .mixnet
|
||||
// .ingress_malformed_packet(self.remote_address.ip());
|
||||
// return;
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// self.pending_packets.push(now, partially_unwrapped);
|
||||
//
|
||||
// // 2. check for packet replay
|
||||
// // 2.1 first try it without locking
|
||||
// if self.handle_pending_packets_batch_no_locking(now).await {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 2.2 if we're within deferral threshold, just leave it queued up for another call
|
||||
// if self.within_deferral_threshold(now) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 2.3. otherwise block until we obtain the lock and clear the whole batch
|
||||
// self.handle_pending_packets_batch(now).await;
|
||||
// 1. derive and expand shared secret
|
||||
// also check the header integrity
|
||||
let partially_unwrapped = match self.try_partially_unwrap_packet(packet) {
|
||||
Ok(unwrapped) => unwrapped,
|
||||
Err(err) => {
|
||||
trace!("failed to process received mix packet: {err}");
|
||||
self.shared
|
||||
.metrics
|
||||
.mixnet
|
||||
.ingress_malformed_packet(self.remote_address.ip());
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.pending_packets.push(now, partially_unwrapped);
|
||||
|
||||
// 2. check for packet replay
|
||||
// 2.1 first try it without locking
|
||||
if self.handle_pending_packets_batch_no_locking(now).await {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2.2 if we're within deferral threshold, just leave it queued up for another call
|
||||
if self.within_deferral_threshold(now) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2.3. otherwise block until we obtain the lock and clear the whole batch
|
||||
self.handle_pending_packets_batch(now).await;
|
||||
}
|
||||
|
||||
async fn handle_unwrapped_packet(
|
||||
@@ -342,6 +374,43 @@ impl ConnectionHandler {
|
||||
.await;
|
||||
}
|
||||
|
||||
fn try_full_unwrap_packet(
|
||||
&self,
|
||||
packet: FramedNymPacket,
|
||||
) -> Result<MixProcessingResult, PacketProcessingError> {
|
||||
// based on the received sphinx key rotation information,
|
||||
// attempt to choose appropriate key for processing the packet
|
||||
// NOTE: due to the function signatures, outfox packets will **only** attempt primary key
|
||||
// if no rotation information is available (but that's fine given outfox is not really in use,
|
||||
// and by the time we need it, the rotation info should be present)
|
||||
match packet.header().key_rotation {
|
||||
SphinxKeyRotation::Unknown => {
|
||||
process_framed_packet(packet, self.shared.sphinx_keys.primary())
|
||||
}
|
||||
SphinxKeyRotation::OddRotation => {
|
||||
let Some(odd_key) = self.shared.sphinx_keys.odd() else {
|
||||
return Err(PacketProcessingError::ExpiredKey);
|
||||
};
|
||||
process_framed_packet(packet, odd_key)
|
||||
}
|
||||
SphinxKeyRotation::EvenRotation => {
|
||||
let Some(even_key) = self.shared.sphinx_keys.even() else {
|
||||
return Err(PacketProcessingError::ExpiredKey);
|
||||
};
|
||||
process_framed_packet(packet, even_key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_received_packet_with_no_replay_detection(
|
||||
&mut self,
|
||||
now: Instant,
|
||||
packet: FramedNymPacket,
|
||||
) {
|
||||
let unwrapped_packet = self.try_full_unwrap_packet(packet);
|
||||
self.handle_unwrapped_packet(now, unwrapped_packet).await;
|
||||
}
|
||||
|
||||
#[instrument(skip(self, packet), level = "debug")]
|
||||
async fn handle_received_nym_packet(&mut self, packet: FramedNymPacket) {
|
||||
let now = Instant::now();
|
||||
@@ -352,13 +421,10 @@ impl ConnectionHandler {
|
||||
self.handle_received_packet_with_replay_detection(now, packet)
|
||||
.await;
|
||||
} else {
|
||||
todo!()
|
||||
//
|
||||
// // otherwise just skip that whole procedure and go straight to payload unwrapping
|
||||
// // (assuming the basic framing is valid)
|
||||
// let unwrapped_packet =
|
||||
// process_framed_packet(packet, self.shared.sphinx_keys.private_key().as_ref());
|
||||
// self.handle_unwrapped_packet(now, unwrapped_packet).await;
|
||||
// otherwise just skip that whole procedure and go straight to payload unwrapping
|
||||
// (assuming the basic framing is valid)
|
||||
self.handle_received_packet_with_no_replay_detection(now, packet)
|
||||
.await;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user