Skip to content

Commit

Permalink
Outfox framing
Browse files Browse the repository at this point in the history
  • Loading branch information
durch committed Apr 19, 2023
1 parent 6464da5 commit 582e7d5
Show file tree
Hide file tree
Showing 17 changed files with 249 additions and 114 deletions.
3 changes: 0 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 66 additions & 12 deletions common/nymsphinx/framing/src/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use crate::packet::{FramedNymPacket, Header};
use bytes::{Buf, BufMut, BytesMut};
use nym_sphinx_params::packet_modes::InvalidPacketMode;
use nym_sphinx_params::packet_sizes::{InvalidPacketSize, PacketSize};
use nym_sphinx_types::SphinxPacket;
use nym_sphinx_params::PacketMode;
use nym_sphinx_types::{NymPacket, NymPacketError, SphinxError};
use nym_sphinx_types::{OutfoxError, OutfoxPacket, SphinxPacket};
use std::io;
use thiserror::Error;
use tokio_util::codec::{Decoder, Encoder};
Expand All @@ -22,6 +23,9 @@ pub enum NymCodecError {
#[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),

Expand Down Expand Up @@ -55,7 +59,8 @@ impl Encoder<FramedNymPacket> for NymCodec {

fn encode(&mut self, item: FramedNymPacket, dst: &mut BytesMut) -> Result<(), Self::Error> {
item.header.encode(dst);
dst.put(item.packet.to_bytes()?.as_slice());
let packet_bytes = item.packet.to_bytes()?;
dst.put(packet_bytes.as_slice());
Ok(())
}
}
Expand Down Expand Up @@ -123,16 +128,21 @@ impl Decoder for NymCodec {

// advance buffer past the header - at this point we have enough bytes
src.advance(header.size());
let sphinx_packet_bytes = src.split_to(packet_size);

// here it could be debatable whether stream is corrupt or not,
// but let's go with the safer approach and assume it is.
let packet = SphinxPacket::from_bytes(&sphinx_packet_bytes)?;
let nymsphinx_packet = FramedNymPacket {
header,
packet: NymPacket::Sphinx(packet),
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_mode {
PacketMode::Outfox => NymPacket::Outfox(OutfoxPacket::try_from(slice)?),
_ => NymPacket::Sphinx(SphinxPacket::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.
Expand All @@ -159,7 +169,6 @@ impl Decoder for NymCodec {
};
}
src.reserve(allocate_for_next_packet);

Ok(Some(nymsphinx_packet))
}
}
Expand All @@ -170,9 +179,33 @@ mod packet_encoding {
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,
DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, NODE_ADDRESS_LENGTH, OUTFOX_PACKET_OVERHEAD,
};

fn make_valid_outfox_packet(size: PacketSize) -> OutfoxPacket {
let (_, node1_pk) = crypto::keygen();
let node1 = Node::new(
NodeAddressBytes::from_bytes([5u8; NODE_ADDRESS_LENGTH]),
node1_pk,
);
let (_, node2_pk) = crypto::keygen();
let node2 = Node::new(
NodeAddressBytes::from_bytes([4u8; NODE_ADDRESS_LENGTH]),
node2_pk,
);
let (_, node3_pk) = crypto::keygen();
let node3 = Node::new(
NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]),
node3_pk,
);

let route = [node1, node2, node3];

let payload = vec![1; 48];

OutfoxPacket::build(&payload, &route, Some(size.size() - OUTFOX_PACKET_OVERHEAD)).unwrap()
}

fn make_valid_sphinx_packet(size: PacketSize) -> SphinxPacket {
let (_, node1_pk) = crypto::keygen();
let node1 = Node::new(
Expand Down Expand Up @@ -225,6 +258,27 @@ mod packet_encoding {
assert_eq!(decoded.packet.to_bytes().unwrap(), sphinx_bytes)
}

#[test]
fn whole_outfox_can_be_decoded_from_a_valid_encoded_instance() {
let header = Header::outfox();
let packet = make_valid_outfox_packet(PacketSize::OutfoxRegularPacket);
let packet_bytes = packet.to_bytes().unwrap();

OutfoxPacket::try_from(packet_bytes.as_slice()).unwrap();

let packet = FramedNymPacket {
header,
packet: NymPacket::Outfox(packet),
};

let mut bytes = BytesMut::new();
NymCodec.encode(packet, &mut bytes).unwrap();
let decoded = NymCodec.decode(&mut bytes).unwrap().unwrap();

assert_eq!(decoded.header, header);
assert_eq!(decoded.packet.to_bytes().unwrap(), packet_bytes)
}

#[cfg(test)]
mod decode_will_allocate_enough_bytes_for_next_call {
use super::*;
Expand Down
8 changes: 8 additions & 0 deletions common/nymsphinx/framing/src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ impl Header {
pub(crate) const LEGACY_SIZE: usize = 2;
pub(crate) const VERSIONED_SIZE: usize = 3;

pub fn outfox() -> Header {
Header {
packet_version: PacketVersion::default(),
packet_size: PacketSize::OutfoxRegularPacket,
packet_mode: PacketMode::Outfox,
}
}

pub(crate) fn size(&self) -> usize {
if self.packet_version.is_legacy() {
Self::LEGACY_SIZE
Expand Down
1 change: 0 additions & 1 deletion common/nymsphinx/params/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ serde = { workspace = true, features = ["derive"] }

nym-crypto = { path = "../../crypto", features = ["hashing", "symmetric"] }
nym-sphinx-types = { path = "../types" }
nym-outfox = { "path" = "../../../nym-outfox" }
8 changes: 8 additions & 0 deletions common/nymsphinx/params/src/packet_modes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub enum PacketMode {
/// Represents a VPN packet that should not be delayed and ideally cached pre-computed keys
/// should be used for unwrapping data. Note that it does not offer the same level of anonymity.
Vpn = 1,

/// Abusing this to add Outfox support
Outfox = 2,
}

impl PacketMode {
Expand All @@ -32,6 +35,10 @@ impl PacketMode {
pub fn is_old_vpn(self) -> bool {
self == PacketMode::Vpn
}

pub fn is_outfox(self) -> bool {
self == PacketMode::Outfox
}
}

impl TryFrom<u8> for PacketMode {
Expand All @@ -41,6 +48,7 @@ impl TryFrom<u8> for PacketMode {
match value {
_ if value == (PacketMode::Mix as u8) => Ok(Self::Mix),
_ if value == (PacketMode::Vpn as u8) => Ok(Self::Vpn),
_ if value == (PacketMode::Outfox as u8) => Ok(Self::Outfox),
v => Err(InvalidPacketMode { received: v }),
}
}
Expand Down
14 changes: 12 additions & 2 deletions common/nymsphinx/params/src/packet_sizes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
// SPDX-License-Identifier: Apache-2.0

use crate::FRAG_ID_LEN;
use nym_outfox::packet::OUTFOX_PACKET_OVERHEAD;
use nym_sphinx_types::header::HEADER_SIZE;
use nym_sphinx_types::PAYLOAD_OVERHEAD_SIZE;
use nym_sphinx_types::{OUTFOX_PACKET_OVERHEAD, PAYLOAD_OVERHEAD_SIZE};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::convert::TryFrom;
Expand Down Expand Up @@ -164,6 +163,17 @@ impl TryFrom<u8> for PacketSize {
_ if value == (PacketSize::ExtendedPacket8 as u8) => Ok(Self::ExtendedPacket8),
_ if value == (PacketSize::ExtendedPacket16 as u8) => Ok(Self::ExtendedPacket16),
_ 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 }),
}
}
Expand Down
3 changes: 0 additions & 3 deletions common/nymsphinx/types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,3 @@ sphinx-packet = { version = "0.1.0" }
serde = "1"
nym-outfox = { path = "../../../nym-outfox" }
thiserror = "1"

[patch.crates-io]
sphinx-packet = { path = "../../../../sphinx" }
4 changes: 2 additions & 2 deletions common/nymsphinx/types/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0

pub use nym_outfox::{error::OutfoxError, packet::OutfoxPacket};
pub use nym_outfox::{error::OutfoxError, packet::OutfoxPacket, packet::OUTFOX_PACKET_OVERHEAD};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
// re-exporting types and constants available in sphinx
pub use sphinx_packet::{
Expand Down Expand Up @@ -104,7 +104,7 @@ impl<'de> Visitor<'de> for NymPacketVisitor {
{
match SphinxPacket::from_bytes(v) {
Ok(packet) => Ok(NymPacket::Sphinx(packet)),
Err(_) => match OutfoxPacket::from_bytes(v) {
Err(_) => match OutfoxPacket::try_from(v) {
Ok(packet) => Ok(NymPacket::Outfox(packet)),
Err(_) => Err(E::custom(
"Could not deserialize Outfox nor Sphinx packet from bytes",
Expand Down
12 changes: 1 addition & 11 deletions contracts/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions nym-connect/desktop/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions nym-outfox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ chacha20poly1305 = "0.10.1"
# Need this star over here to pull in js into getrandom
getrandom = { version = "*", features = ["js"] }
thiserror = "1"
serde = { version = "1", features = ["derive"] }
bincode = "1"
fastrand = "1.8"

sphinx-packet = "0.1.0"

Expand All @@ -26,4 +25,3 @@ sphinx-packet = { path = "../../../../sphinx" }

[dev-dependencies]
criterion = "0.4"
fastrand = "1.8"
5 changes: 3 additions & 2 deletions nym-outfox/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::array::TryFromSliceError;

use crate::format::MIX_PARAMS_LEN;
use crate::lion::MIN_MESSAGE_LEN;
use chacha20::cipher::InvalidLength;
use thiserror::Error;
Expand All @@ -24,6 +25,6 @@ pub enum OutfoxError {
#[from]
source: TryFromSliceError,
},
#[error("Could not serialize OutfoxPacket!")]
Bincode,
#[error("Header length must be {MIX_PARAMS_LEN}, got {0}")]
InvalidHeaderLength(usize),
}
Loading

0 comments on commit 582e7d5

Please sign in to comment.