827c13b69e
dont build netstack in CI additional rust 2024 fixes fixes removed temp.rs first round of cleanup removed duplicated NS types moved gateway probe to the monorepo
296 lines
10 KiB
Rust
296 lines
10 KiB
Rust
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
use crate::PacketType;
|
|
#[cfg(feature = "outfox")]
|
|
use nym_sphinx_types::{MIN_PACKET_SIZE, MIX_PARAMS_LEN, OUTFOX_PACKET_OVERHEAD};
|
|
#[cfg(feature = "sphinx")]
|
|
use nym_sphinx_types::{PAYLOAD_OVERHEAD_SIZE, header::HEADER_SIZE};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::cmp::Ordering;
|
|
|
|
use std::fmt::{Debug, Display, Formatter};
|
|
use std::str::FromStr;
|
|
use thiserror::Error;
|
|
|
|
// each sphinx packet contains mandatory header and payload padding + markers
|
|
#[cfg(feature = "sphinx")]
|
|
const SPHINX_PACKET_OVERHEAD: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE;
|
|
|
|
// it's up to the smart people to figure those values out : )
|
|
|
|
// TODO: even though we have 16B IV, is having just 5B (FRAG_ID_LEN) of the ID possibly insecure?
|
|
|
|
// TODO: I'm not entirely sure if we can easily extract `<AckEncryptionAlgorithm as NewStreamCipher>::NonceSize`
|
|
// into a const usize before relevant stuff is stabilised in rust...
|
|
#[cfg(feature = "sphinx")]
|
|
const ACK_IV_SIZE: usize = 16;
|
|
|
|
#[cfg(feature = "sphinx")]
|
|
const ACK_PACKET_SIZE: usize = ACK_IV_SIZE + crate::FRAG_ID_LEN + SPHINX_PACKET_OVERHEAD;
|
|
#[cfg(feature = "sphinx")]
|
|
const REGULAR_PACKET_SIZE: usize = 2 * 1024 + SPHINX_PACKET_OVERHEAD;
|
|
#[cfg(feature = "sphinx")]
|
|
const EXTENDED_PACKET_SIZE_8: usize = 8 * 1024 + SPHINX_PACKET_OVERHEAD;
|
|
#[cfg(feature = "sphinx")]
|
|
const EXTENDED_PACKET_SIZE_16: usize = 16 * 1024 + SPHINX_PACKET_OVERHEAD;
|
|
#[cfg(feature = "sphinx")]
|
|
const EXTENDED_PACKET_SIZE_32: usize = 32 * 1024 + SPHINX_PACKET_OVERHEAD;
|
|
|
|
#[cfg(feature = "outfox")]
|
|
const OUTFOX_ACK_PACKET_SIZE: usize = MIN_PACKET_SIZE + OUTFOX_PACKET_OVERHEAD;
|
|
#[cfg(feature = "outfox")]
|
|
const OUTFOX_REGULAR_PACKET_SIZE: usize = 2 * 1024 + OUTFOX_PACKET_OVERHEAD;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum InvalidPacketSize {
|
|
#[error("{received} is not a valid packet size tag")]
|
|
UnknownPacketTag { received: u8 },
|
|
|
|
#[error("{received} is not a valid extended packet size variant")]
|
|
UnknownExtendedPacketVariant { received: String },
|
|
|
|
#[error("{received} does not correspond with any known packet size")]
|
|
UnknownPacketSize { received: usize },
|
|
}
|
|
|
|
#[repr(u8)]
|
|
#[derive(Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
|
pub enum PacketSize {
|
|
// for example instant messaging use case
|
|
#[default]
|
|
#[serde(rename = "regular")]
|
|
RegularPacket = 1,
|
|
|
|
// for sending SURB-ACKs
|
|
#[serde(rename = "ack")]
|
|
AckPacket = 2,
|
|
|
|
// for example for streaming fast and furious in uncompressed 10bit 4K HDR quality
|
|
#[serde(rename = "extended32")]
|
|
ExtendedPacket32 = 3,
|
|
|
|
// for example for streaming fast and furious in heavily compressed lossy RealPlayer quality
|
|
#[serde(rename = "extended8")]
|
|
ExtendedPacket8 = 4,
|
|
|
|
// for example for streaming fast and furious in compressed XviD quality
|
|
#[serde(rename = "extended16")]
|
|
ExtendedPacket16 = 5,
|
|
|
|
#[serde(rename = "outfox_regular")]
|
|
OutfoxRegularPacket = 6,
|
|
|
|
// for sending SURB-ACKs
|
|
#[serde(rename = "outfox_ack")]
|
|
OutfoxAckPacket = 7,
|
|
}
|
|
|
|
impl PartialOrd for PacketSize {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
// order them by actual packet size
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Ord for PacketSize {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
// order them by actual packet size
|
|
self.size().cmp(&other.size())
|
|
}
|
|
}
|
|
|
|
impl FromStr for PacketSize {
|
|
type Err = InvalidPacketSize;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"regular" => Ok(Self::RegularPacket),
|
|
"ack" => Ok(Self::AckPacket),
|
|
"extended8" => Ok(Self::ExtendedPacket8),
|
|
"extended16" => Ok(Self::ExtendedPacket16),
|
|
"extended32" => Ok(Self::ExtendedPacket32),
|
|
"outfox_regular" => Ok(Self::OutfoxRegularPacket),
|
|
"outfox_ack" => Ok(Self::OutfoxAckPacket),
|
|
s => Err(InvalidPacketSize::UnknownExtendedPacketVariant {
|
|
received: s.to_string(),
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for PacketSize {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
PacketSize::RegularPacket => write!(f, "regular"),
|
|
PacketSize::AckPacket => write!(f, "ack"),
|
|
PacketSize::ExtendedPacket32 => write!(f, "extended32"),
|
|
PacketSize::ExtendedPacket8 => write!(f, "extended8"),
|
|
PacketSize::ExtendedPacket16 => write!(f, "extended16"),
|
|
PacketSize::OutfoxRegularPacket => write!(f, "outfox_regular"),
|
|
PacketSize::OutfoxAckPacket => write!(f, "outfox_ack"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Debug for PacketSize {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
let name = self.to_string();
|
|
let size = self.size();
|
|
let plaintext = self.plaintext_size();
|
|
|
|
write!(f, "{name} ({size} bytes / {plaintext} plaintext)")
|
|
}
|
|
}
|
|
|
|
impl TryFrom<u8> for PacketSize {
|
|
type Error = InvalidPacketSize;
|
|
|
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
match value {
|
|
_ if value == (PacketSize::RegularPacket as u8) => Ok(Self::RegularPacket),
|
|
_ if value == (PacketSize::AckPacket as u8) => Ok(Self::AckPacket),
|
|
_ 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),
|
|
v => Err(InvalidPacketSize::UnknownPacketTag { received: v }),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PacketSize {
|
|
pub const fn size(self) -> usize {
|
|
#[allow(unreachable_patterns)]
|
|
match self {
|
|
#[cfg(feature = "sphinx")]
|
|
PacketSize::RegularPacket => REGULAR_PACKET_SIZE,
|
|
#[cfg(feature = "sphinx")]
|
|
PacketSize::AckPacket => ACK_PACKET_SIZE,
|
|
#[cfg(feature = "sphinx")]
|
|
PacketSize::ExtendedPacket8 => EXTENDED_PACKET_SIZE_8,
|
|
#[cfg(feature = "sphinx")]
|
|
PacketSize::ExtendedPacket16 => EXTENDED_PACKET_SIZE_16,
|
|
#[cfg(feature = "sphinx")]
|
|
PacketSize::ExtendedPacket32 => EXTENDED_PACKET_SIZE_32,
|
|
#[cfg(feature = "outfox")]
|
|
PacketSize::OutfoxRegularPacket => OUTFOX_REGULAR_PACKET_SIZE,
|
|
#[cfg(feature = "outfox")]
|
|
PacketSize::OutfoxAckPacket => OUTFOX_ACK_PACKET_SIZE,
|
|
_ => 0,
|
|
}
|
|
}
|
|
|
|
pub const fn header_size(&self) -> usize {
|
|
#[allow(unreachable_patterns)]
|
|
match self {
|
|
#[cfg(feature = "sphinx")]
|
|
PacketSize::RegularPacket
|
|
| PacketSize::AckPacket
|
|
| PacketSize::ExtendedPacket8
|
|
| PacketSize::ExtendedPacket16
|
|
| PacketSize::ExtendedPacket32 => HEADER_SIZE,
|
|
#[cfg(feature = "outfox")]
|
|
PacketSize::OutfoxRegularPacket | PacketSize::OutfoxAckPacket => MIX_PARAMS_LEN,
|
|
_ => 0,
|
|
}
|
|
}
|
|
|
|
pub const fn payload_overhead(&self) -> usize {
|
|
#[allow(unreachable_patterns)]
|
|
match self {
|
|
#[cfg(feature = "sphinx")]
|
|
PacketSize::RegularPacket
|
|
| PacketSize::AckPacket
|
|
| PacketSize::ExtendedPacket8
|
|
| PacketSize::ExtendedPacket16
|
|
| PacketSize::ExtendedPacket32 => PAYLOAD_OVERHEAD_SIZE,
|
|
#[cfg(feature = "outfox")]
|
|
PacketSize::OutfoxRegularPacket | PacketSize::OutfoxAckPacket => {
|
|
OUTFOX_PACKET_OVERHEAD - MIX_PARAMS_LEN // Mix params are calculated into the total overhead so we take them out here
|
|
}
|
|
_ => 0,
|
|
}
|
|
}
|
|
|
|
pub const fn plaintext_size(self) -> usize {
|
|
self.size() - self.header_size() - self.payload_overhead()
|
|
}
|
|
|
|
pub const fn payload_size(self) -> usize {
|
|
self.size() - self.header_size()
|
|
}
|
|
|
|
pub fn get_type(size: usize) -> Result<Self, InvalidPacketSize> {
|
|
if PacketSize::RegularPacket.size() == size {
|
|
Ok(PacketSize::RegularPacket)
|
|
} else if PacketSize::AckPacket.size() == size {
|
|
Ok(PacketSize::AckPacket)
|
|
} else if PacketSize::ExtendedPacket8.size() == size {
|
|
Ok(PacketSize::ExtendedPacket8)
|
|
} else if PacketSize::ExtendedPacket16.size() == size {
|
|
Ok(PacketSize::ExtendedPacket16)
|
|
} else if PacketSize::ExtendedPacket32.size() == size {
|
|
Ok(PacketSize::ExtendedPacket32)
|
|
} else if PacketSize::OutfoxRegularPacket.size() == size
|
|
|| PacketSize::OutfoxRegularPacket.size() == size + 6
|
|
{
|
|
Ok(PacketSize::OutfoxRegularPacket)
|
|
} else if PacketSize::OutfoxAckPacket.size() == size {
|
|
Ok(PacketSize::OutfoxAckPacket)
|
|
} else {
|
|
Err(InvalidPacketSize::UnknownPacketSize { received: size })
|
|
}
|
|
}
|
|
|
|
pub fn is_extended_size(&self) -> bool {
|
|
match self {
|
|
PacketSize::RegularPacket
|
|
| PacketSize::AckPacket
|
|
| PacketSize::OutfoxAckPacket
|
|
| PacketSize::OutfoxRegularPacket => false,
|
|
PacketSize::ExtendedPacket8
|
|
| PacketSize::ExtendedPacket16
|
|
| PacketSize::ExtendedPacket32 => true,
|
|
}
|
|
}
|
|
|
|
pub fn as_extended_size(self) -> Option<Self> {
|
|
if self.is_extended_size() {
|
|
Some(self)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn get_type_from_plaintext(
|
|
plaintext_size: usize,
|
|
packet_type: PacketType,
|
|
) -> Result<Self, InvalidPacketSize> {
|
|
#[allow(unreachable_patterns)]
|
|
let overhead = match packet_type {
|
|
#[cfg(feature = "sphinx")]
|
|
PacketType::Mix => SPHINX_PACKET_OVERHEAD,
|
|
#[cfg(feature = "outfox")]
|
|
PacketType::Outfox => OUTFOX_PACKET_OVERHEAD,
|
|
_ => 0,
|
|
};
|
|
let packet_size = plaintext_size + overhead;
|
|
Self::get_type(packet_size)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::AckEncryptionAlgorithm;
|
|
use nym_crypto::symmetric::stream_cipher::IvSizeUser;
|
|
|
|
#[test]
|
|
fn ack_iv_size_assertion() {
|
|
let iv_size = AckEncryptionAlgorithm::iv_size();
|
|
assert_eq!(iv_size, ACK_IV_SIZE);
|
|
}
|
|
}
|