Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 00aa88b578 | |||
| 6dddeaff1d | |||
| b2c545770b |
Generated
+49
-9
@@ -1703,6 +1703,17 @@ dependencies = [
|
|||||||
"cipher 0.4.4",
|
"cipher 0.4.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cuckoofilter"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b810a8449931679f64cd7eef1bbd0fa315801b6d5d9cdc1ace2804d6529eee18"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"fnv",
|
||||||
|
"rand 0.7.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cupid"
|
name = "cupid"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
@@ -2598,6 +2609,23 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastbloom-rs"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "git+https://github.com/simonwicky/fastbloom#9733fce76e68f193ce749cc455557b09620abf32"
|
||||||
|
dependencies = [
|
||||||
|
"cuckoofilter",
|
||||||
|
"fastmurmur3",
|
||||||
|
"xorfilter-rs",
|
||||||
|
"xxhash-rust",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastmurmur3"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d922f481ae01f2a3f1fff7b9e0e789f18f0c755a38ec983a3e6f37762cdcc2a2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
@@ -5764,6 +5792,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpu-cycles",
|
"cpu-cycles",
|
||||||
|
"fastbloom-rs",
|
||||||
"futures",
|
"futures",
|
||||||
"humantime-serde",
|
"humantime-serde",
|
||||||
"log",
|
"log",
|
||||||
@@ -5928,7 +5957,7 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
"utoipa",
|
"utoipa",
|
||||||
"utoipa-swagger-ui",
|
"utoipa-swagger-ui",
|
||||||
"x25519-dalek 2.0.0",
|
"x25519-dalek 2.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6496,7 +6525,7 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
"url",
|
"url",
|
||||||
"x25519-dalek 2.0.0",
|
"x25519-dalek 2.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6628,7 +6657,7 @@ dependencies = [
|
|||||||
"nym-task",
|
"nym-task",
|
||||||
"nym-wireguard-types",
|
"nym-wireguard-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
"x25519-dalek 2.0.0",
|
"x25519-dalek 2.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6646,7 +6675,7 @@ dependencies = [
|
|||||||
"sha2 0.10.8",
|
"sha2 0.10.8",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"utoipa",
|
"utoipa",
|
||||||
"x25519-dalek 2.0.0",
|
"x25519-dalek 2.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -9032,8 +9061,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "sphinx-packet"
|
name = "sphinx-packet"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/nymtech/sphinx.git?branch=simon/replay_tag#91a64ab1aaf3a7bb358af0a5b1a89f0564a03d38"
|
||||||
checksum = "cc43eda802856ee82a7555c7b75ceb9e07451741c7a2f5f23d036020e01189d4"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes 0.7.5",
|
"aes 0.7.5",
|
||||||
"arrayref",
|
"arrayref",
|
||||||
@@ -10917,7 +10945,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"webpki 0.21.4",
|
"webpki 0.21.4",
|
||||||
"webrtc-util",
|
"webrtc-util",
|
||||||
"x25519-dalek 2.0.0",
|
"x25519-dalek 2.0.1",
|
||||||
"x509-parser 0.13.2",
|
"x509-parser 0.13.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -11304,9 +11332,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x25519-dalek"
|
name = "x25519-dalek"
|
||||||
version = "2.0.0"
|
version = "2.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
|
checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek 4.1.1",
|
"curve25519-dalek 4.1.1",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
@@ -11360,6 +11388,18 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xorfilter-rs"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47f9da296a88b6bc150b896d17770a62d4dc6f63ecf0ed10a9c08a1cb3d12f24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xxhash-rust"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "735a71d46c4d68d71d4b24d03fdc2b98e38cea81730595801db779c04fe80d70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yamux"
|
name = "yamux"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ tokio = { version = "1.24.1", features = [
|
|||||||
tokio-util = { workspace = true, features = ["codec"] }
|
tokio-util = { workspace = true, features = ["codec"] }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
fastbloom-rs = { git = "https://github.com/simonwicky/fastbloom"}
|
||||||
|
|
||||||
## tracing
|
## tracing
|
||||||
tracing = { version = "0.1.37", optional = true }
|
tracing = { version = "0.1.37", optional = true }
|
||||||
|
|||||||
@@ -28,4 +28,7 @@ pub enum MixProcessingError {
|
|||||||
|
|
||||||
#[error("failed to process received outfox packet: {0}")]
|
#[error("failed to process received outfox packet: {0}")]
|
||||||
OutfoxProcessingError(#[from] OutfoxError),
|
OutfoxProcessingError(#[from] OutfoxError),
|
||||||
|
|
||||||
|
#[error("this packet was already processed, it's a replay")]
|
||||||
|
ReplayedPacketDetected,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,3 +3,4 @@
|
|||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod processor;
|
pub mod processor;
|
||||||
|
pub mod replay_detection;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
use crate::measure;
|
use crate::measure;
|
||||||
use crate::packet_processor::error::MixProcessingError;
|
use crate::packet_processor::error::MixProcessingError;
|
||||||
|
use crate::packet_processor::replay_detection::ReplayDetector;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nym_sphinx_acknowledgements::surb_ack::SurbAck;
|
use nym_sphinx_acknowledgements::surb_ack::SurbAck;
|
||||||
use nym_sphinx_addressing::nodes::NymNodeRoutingAddress;
|
use nym_sphinx_addressing::nodes::NymNodeRoutingAddress;
|
||||||
@@ -40,6 +41,9 @@ pub enum MixProcessingResult {
|
|||||||
pub struct SphinxPacketProcessor {
|
pub struct SphinxPacketProcessor {
|
||||||
/// Private sphinx key of this node required to unwrap received sphinx packet.
|
/// Private sphinx key of this node required to unwrap received sphinx packet.
|
||||||
sphinx_key: Arc<PrivateKey>,
|
sphinx_key: Arc<PrivateKey>,
|
||||||
|
|
||||||
|
/// Detector of replay attack
|
||||||
|
replay_detector: ReplayDetector,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SphinxPacketProcessor {
|
impl SphinxPacketProcessor {
|
||||||
@@ -47,6 +51,7 @@ impl SphinxPacketProcessor {
|
|||||||
pub fn new(sphinx_key: PrivateKey) -> Self {
|
pub fn new(sphinx_key: PrivateKey) -> Self {
|
||||||
SphinxPacketProcessor {
|
SphinxPacketProcessor {
|
||||||
sphinx_key: Arc::new(sphinx_key),
|
sphinx_key: Arc::new(sphinx_key),
|
||||||
|
replay_detector: ReplayDetector::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +189,7 @@ impl SphinxPacketProcessor {
|
|||||||
match packet {
|
match packet {
|
||||||
NymProcessedPacket::Sphinx(packet) => {
|
NymProcessedPacket::Sphinx(packet) => {
|
||||||
match packet {
|
match packet {
|
||||||
ProcessedPacket::ForwardHop(packet, address, delay) => self
|
ProcessedPacket::ForwardHop(packet, address, delay, _) => self
|
||||||
.process_forward_hop(
|
.process_forward_hop(
|
||||||
NymPacket::Sphinx(*packet),
|
NymPacket::Sphinx(*packet),
|
||||||
address,
|
address,
|
||||||
@@ -193,12 +198,13 @@ impl SphinxPacketProcessor {
|
|||||||
),
|
),
|
||||||
// right now there's no use for the surb_id included in the header - probably it should get removed from the
|
// right now there's no use for the surb_id included in the header - probably it should get removed from the
|
||||||
// sphinx all together?
|
// sphinx all together?
|
||||||
ProcessedPacket::FinalHop(destination, _, payload) => self.process_final_hop(
|
ProcessedPacket::FinalHop(destination, _, payload, _) => self
|
||||||
destination,
|
.process_final_hop(
|
||||||
payload.recover_plaintext()?,
|
destination,
|
||||||
packet_size,
|
payload.recover_plaintext()?,
|
||||||
packet_type,
|
packet_size,
|
||||||
),
|
packet_type,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NymProcessedPacket::Outfox(packet) => {
|
NymProcessedPacket::Outfox(packet) => {
|
||||||
@@ -239,6 +245,10 @@ impl SphinxPacketProcessor {
|
|||||||
// unwrap the sphinx packet and if possible and appropriate, cache keys
|
// unwrap the sphinx packet and if possible and appropriate, cache keys
|
||||||
let processed_packet = self.perform_initial_unwrapping(received)?;
|
let processed_packet = self.perform_initial_unwrapping(received)?;
|
||||||
|
|
||||||
|
//check for replay attack
|
||||||
|
self.replay_detector
|
||||||
|
.handle_replay_tag(&processed_packet.replay_tag())?;
|
||||||
|
|
||||||
// for forward packets, extract next hop and set delay (but do NOT delay here)
|
// for forward packets, extract next hop and set delay (but do NOT delay here)
|
||||||
// for final packets, extract SURBAck
|
// for final packets, extract SURBAck
|
||||||
let final_processing_result =
|
let final_processing_result =
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::packet_processor::error::MixProcessingError;
|
||||||
|
use fastbloom_rs::{BloomFilter, FilterBuilder, Membership};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
const BLOOM_FILTER_SIZE: u64 = 10_000_000;
|
||||||
|
const FP_RATE: f64 = 1e-4;
|
||||||
|
|
||||||
|
//alias for convenience
|
||||||
|
type ReplayTag = [u8];
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ReplayDetector(Arc<Mutex<ReplayDetectorInner>>);
|
||||||
|
|
||||||
|
impl ReplayDetector {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ReplayDetector(Arc::new(Mutex::new(ReplayDetectorInner::new())))
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if secret has been seen already
|
||||||
|
//if no, return Ok
|
||||||
|
//if yes, add the secret to the list, then return an error
|
||||||
|
pub fn handle_replay_tag(&self, replay_tag: &ReplayTag) -> Result<(), MixProcessingError> {
|
||||||
|
match self.0.lock() {
|
||||||
|
Ok(mut inner) => {
|
||||||
|
if !inner.lookup_then_insert(replay_tag) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(MixProcessingError::ReplayedPacketDetected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to handle replay_tag : {err}");
|
||||||
|
Ok(()) //what is the sensible thing to do, if the lock is poisoned? Reset the filter ?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ReplayDetector {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ReplayDetectorInner {
|
||||||
|
filter: BloomFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReplayDetectorInner {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ReplayDetectorInner {
|
||||||
|
filter: FilterBuilder::new(BLOOM_FILTER_SIZE, FP_RATE).build_bloom_filter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_then_insert(&mut self, replay_tag: &ReplayTag) -> bool {
|
||||||
|
self.filter.contains_then_add(replay_tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod replay_detector_test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handle_replay_tag_correctly_detects_replay() {
|
||||||
|
let replay_detector = ReplayDetector::new();
|
||||||
|
let replay_tag = b"Hello World!";
|
||||||
|
assert!(replay_detector.handle_replay_tag(replay_tag).is_ok()); //first insert is fine
|
||||||
|
assert!(replay_detector.handle_replay_tag(replay_tag).is_err()); //second is not
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,9 +8,10 @@ license = { workspace = true }
|
|||||||
repository = { workspace = true }
|
repository = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sphinx-packet = { version = "0.1.0", optional = true }
|
#sphinx-packet = { version = "0.1.0", optional = true }
|
||||||
nym-outfox = { path = "../../../nym-outfox", optional = true }
|
nym-outfox = { path = "../../../nym-outfox", optional = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
sphinx-packet = { git = "https://github.com/nymtech/sphinx.git", branch = "simon/replay_tag", optional = true}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["sphinx"]
|
default = ["sphinx"]
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pub use nym_outfox::{
|
|||||||
// re-exporting types and constants available in sphinx
|
// re-exporting types and constants available in sphinx
|
||||||
#[cfg(feature = "outfox")]
|
#[cfg(feature = "outfox")]
|
||||||
use nym_outfox::packet::{OutfoxPacket, OutfoxProcessedPacket};
|
use nym_outfox::packet::{OutfoxPacket, OutfoxProcessedPacket};
|
||||||
|
use sphinx_packet::header::keys::ReplayTag;
|
||||||
#[cfg(feature = "sphinx")]
|
#[cfg(feature = "sphinx")]
|
||||||
pub use sphinx_packet::{
|
pub use sphinx_packet::{
|
||||||
constants::{
|
constants::{
|
||||||
@@ -57,6 +58,15 @@ pub enum NymProcessedPacket {
|
|||||||
Outfox(OutfoxProcessedPacket),
|
Outfox(OutfoxProcessedPacket),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NymProcessedPacket {
|
||||||
|
pub fn replay_tag(&self) -> ReplayTag {
|
||||||
|
match self {
|
||||||
|
NymProcessedPacket::Sphinx(sphinx) => sphinx.replay_tag(),
|
||||||
|
NymProcessedPacket::Outfox(_) => todo!(), //SW temporary while I add a replay tag to outfox
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Debug for NymPacket {
|
impl fmt::Debug for NymPacket {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ curve25519-dalek = "3.2"
|
|||||||
chacha20poly1305 = "0.10.1"
|
chacha20poly1305 = "0.10.1"
|
||||||
getrandom = { workspace = true, features = ["js"] }
|
getrandom = { workspace = true, features = ["js"] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
sphinx-packet = "0.1.0"
|
#sphinx-packet = "0.1.0"
|
||||||
|
sphinx-packet = { git = "https://github.com/nymtech/sphinx.git", branch = "simon/replay_tag"}
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user