add bloom filter based replay detection

This commit is contained in:
Simon Wicky
2024-02-14 14:32:34 +01:00
parent b2c545770b
commit 6dddeaff1d
5 changed files with 130 additions and 9 deletions
Generated
+49 -9
View File
@@ -1703,6 +1703,17 @@ dependencies = [
"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]]
name = "cupid"
version = "0.6.1"
@@ -2598,6 +2609,23 @@ dependencies = [
"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]]
name = "fastrand"
version = "1.9.0"
@@ -5764,6 +5792,7 @@ dependencies = [
"bytes",
"cfg-if",
"cpu-cycles",
"fastbloom-rs",
"futures",
"humantime-serde",
"log",
@@ -5928,7 +5957,7 @@ dependencies = [
"tracing",
"utoipa",
"utoipa-swagger-ui",
"x25519-dalek 2.0.0",
"x25519-dalek 2.0.1",
]
[[package]]
@@ -6496,7 +6525,7 @@ dependencies = [
"thiserror",
"ts-rs",
"url",
"x25519-dalek 2.0.0",
"x25519-dalek 2.0.1",
]
[[package]]
@@ -6628,7 +6657,7 @@ dependencies = [
"nym-task",
"nym-wireguard-types",
"tokio",
"x25519-dalek 2.0.0",
"x25519-dalek 2.0.1",
]
[[package]]
@@ -6646,7 +6675,7 @@ dependencies = [
"sha2 0.10.8",
"thiserror",
"utoipa",
"x25519-dalek 2.0.0",
"x25519-dalek 2.0.1",
]
[[package]]
@@ -9032,8 +9061,7 @@ dependencies = [
[[package]]
name = "sphinx-packet"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc43eda802856ee82a7555c7b75ceb9e07451741c7a2f5f23d036020e01189d4"
source = "git+https://github.com/nymtech/sphinx.git?branch=simon/replay_tag#91a64ab1aaf3a7bb358af0a5b1a89f0564a03d38"
dependencies = [
"aes 0.7.5",
"arrayref",
@@ -10917,7 +10945,7 @@ dependencies = [
"tokio",
"webpki 0.21.4",
"webrtc-util",
"x25519-dalek 2.0.0",
"x25519-dalek 2.0.1",
"x509-parser 0.13.2",
]
@@ -11304,9 +11332,9 @@ dependencies = [
[[package]]
name = "x25519-dalek"
version = "2.0.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
dependencies = [
"curve25519-dalek 4.1.1",
"rand_core 0.6.4",
@@ -11360,6 +11388,18 @@ dependencies = [
"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]]
name = "yamux"
version = "0.10.2"
+1
View File
@@ -24,6 +24,7 @@ tokio = { version = "1.24.1", features = [
tokio-util = { workspace = true, features = ["codec"] }
url = { workspace = true }
thiserror = { workspace = true }
fastbloom-rs = { git = "https://github.com/simonwicky/fastbloom"}
## tracing
tracing = { version = "0.1.37", optional = true }
@@ -28,4 +28,7 @@ pub enum MixProcessingError {
#[error("failed to process received outfox packet: {0}")]
OutfoxProcessingError(#[from] OutfoxError),
#[error("this packet was already processed, it's a replay")]
ReplayedPacketDetected,
}
@@ -3,3 +3,4 @@
pub mod error;
pub mod processor;
pub mod replay_detection;
@@ -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
}
}