mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-02-09 01:59:16 +08:00
replay filter for TCP & UDP in AEAD-2022
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@
|
||||
/udp_echo_server.py
|
||||
/.idea
|
||||
/.devcontainer
|
||||
.DS_Store
|
||||
|
||||
@@ -32,7 +32,7 @@ stream-cipher = ["shadowsocks-crypto/v1-stream"]
|
||||
aead-cipher-extra = ["shadowsocks-crypto/v1-aead-extra"]
|
||||
|
||||
# Enable AEAD 2022
|
||||
aead-cipher-2022 = ["shadowsocks-crypto/v2", "rand/small_rng", "aes", "lru_time_cache"]
|
||||
aead-cipher-2022 = ["shadowsocks-crypto/v2", "rand/small_rng", "aes", "lru_time_cache", "spin"]
|
||||
|
||||
# Enable detection against replay attack
|
||||
security-replay-attack-detect = ["bloomfilter", "spin"]
|
||||
|
||||
@@ -7,7 +7,7 @@ use log::warn;
|
||||
|
||||
use crate::{
|
||||
config::{ReplayAttackPolicy, ServerType},
|
||||
crypto::v1::random_iv_or_salt,
|
||||
crypto::{v1::random_iv_or_salt, CipherKind},
|
||||
dns_resolver::DnsResolver,
|
||||
security::replay::ReplayProtector,
|
||||
};
|
||||
@@ -50,15 +50,15 @@ impl Context {
|
||||
///
|
||||
/// If not, set into the current bloom filter
|
||||
#[inline(always)]
|
||||
fn check_nonce_and_set(&self, nonce: &[u8]) -> bool {
|
||||
fn check_nonce_and_set(&self, method: CipherKind, nonce: &[u8]) -> bool {
|
||||
match self.replay_policy {
|
||||
ReplayAttackPolicy::Ignore => false,
|
||||
_ => self.replay_protector.check_nonce_and_set(nonce),
|
||||
_ => self.replay_protector.check_nonce_and_set(method, nonce),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate nonce (IV or SALT)
|
||||
pub fn generate_nonce(&self, nonce: &mut [u8], unique: bool) {
|
||||
pub fn generate_nonce(&self, method: CipherKind, nonce: &mut [u8], unique: bool) {
|
||||
if nonce.is_empty() {
|
||||
return;
|
||||
}
|
||||
@@ -86,7 +86,7 @@ impl Context {
|
||||
}
|
||||
|
||||
// Salt already exists, generate a new one.
|
||||
if unique && self.check_nonce_and_set(nonce) {
|
||||
if unique && self.check_nonce_and_set(method, nonce) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ impl Context {
|
||||
}
|
||||
|
||||
/// Check nonce replay
|
||||
pub fn check_nonce_replay(&self, nonce: &[u8]) -> io::Result<()> {
|
||||
pub fn check_nonce_replay(&self, method: CipherKind, nonce: &[u8]) -> io::Result<()> {
|
||||
if nonce.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -103,13 +103,13 @@ impl Context {
|
||||
match self.replay_policy {
|
||||
ReplayAttackPolicy::Ignore => Ok(()),
|
||||
ReplayAttackPolicy::Detect => {
|
||||
if self.replay_protector.check_nonce_and_set(nonce) {
|
||||
if self.replay_protector.check_nonce_and_set(method, nonce) {
|
||||
warn!("detected repeated nonce (iv/salt) {:?}", ByteStr::new(nonce));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ReplayAttackPolicy::Reject => {
|
||||
if self.replay_protector.check_nonce_and_set(nonce) {
|
||||
if self.replay_protector.check_nonce_and_set(method, nonce) {
|
||||
let err = io::Error::new(io::ErrorKind::Other, "detected repeated nonce (iv/salt)");
|
||||
Err(err)
|
||||
} else {
|
||||
@@ -151,4 +151,9 @@ impl Context {
|
||||
pub fn set_replay_attack_policy(&mut self, replay_policy: ReplayAttackPolicy) {
|
||||
self.replay_policy = replay_policy;
|
||||
}
|
||||
|
||||
/// Get policy against replay attach
|
||||
pub fn replay_attack_policy(&self) -> ReplayAttackPolicy {
|
||||
self.replay_policy
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,25 @@ pub use self::socks5::Address;
|
||||
pub mod socks5;
|
||||
pub mod tcprelay;
|
||||
pub mod udprelay;
|
||||
|
||||
/// AEAD 2022 maximum padding length
|
||||
#[cfg(feature = "aead-cipher-2022")]
|
||||
const AEAD2022_MAX_PADDING_SIZE: usize = 900;
|
||||
|
||||
/// Get a properly AEAD 2022 padding size according to payload's length
|
||||
#[cfg(feature = "aead-cipher-2022")]
|
||||
fn get_aead_2022_padding_size(payload: &[u8]) -> usize {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||
|
||||
thread_local! {
|
||||
static PADDING_RNG: RefCell<SmallRng> = RefCell::new(SmallRng::from_entropy());
|
||||
}
|
||||
|
||||
if payload.is_empty() {
|
||||
PADDING_RNG.with(|rng| rng.borrow_mut().gen::<usize>() % AEAD2022_MAX_PADDING_SIZE)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ impl DecryptedReader {
|
||||
|
||||
// Check repeated salt after first successful decryption #442
|
||||
if let Some(ref salt) = self.salt {
|
||||
context.check_nonce_replay(salt)?;
|
||||
context.check_nonce_replay(self.method, salt)?;
|
||||
}
|
||||
|
||||
// Remote TAG
|
||||
|
||||
@@ -222,7 +222,7 @@ impl DecryptedReader {
|
||||
|
||||
// Check repeated salt after first successful decryption #442
|
||||
if let Some(ref salt) = self.salt {
|
||||
context.check_nonce_replay(salt)?;
|
||||
context.check_nonce_replay(self.method, salt)?;
|
||||
}
|
||||
|
||||
// Remote TAG
|
||||
|
||||
@@ -169,13 +169,13 @@ impl<S> CryptoStream<S> {
|
||||
#[cfg(feature = "stream-cipher")]
|
||||
CipherCategory::Stream => {
|
||||
let mut local_iv = vec![0u8; prev_len];
|
||||
context.generate_nonce(&mut local_iv, true);
|
||||
context.generate_nonce(method, &mut local_iv, true);
|
||||
trace!("generated Stream cipher IV {:?}", ByteStr::new(&local_iv));
|
||||
local_iv
|
||||
}
|
||||
CipherCategory::Aead => {
|
||||
let mut local_salt = vec![0u8; prev_len];
|
||||
context.generate_nonce(&mut local_salt, true);
|
||||
context.generate_nonce(method, &mut local_salt, true);
|
||||
trace!("generated AEAD cipher salt {:?}", ByteStr::new(&local_salt));
|
||||
local_salt
|
||||
}
|
||||
@@ -183,7 +183,7 @@ impl<S> CryptoStream<S> {
|
||||
#[cfg(feature = "aead-cipher-2022")]
|
||||
CipherCategory::Aead2022 => {
|
||||
let mut local_salt = vec![0u8; prev_len];
|
||||
context.generate_nonce(&mut local_salt, true);
|
||||
context.generate_nonce(method, &mut local_salt, true);
|
||||
trace!("generated AEAD cipher salt {:?}", ByteStr::new(&local_salt));
|
||||
local_salt
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ use tokio::{
|
||||
time,
|
||||
};
|
||||
|
||||
#[cfg(feature = "aead-cipher-2022")]
|
||||
use crate::context::Context;
|
||||
use crate::{
|
||||
config::ServerConfig,
|
||||
context::SharedContext,
|
||||
@@ -28,6 +26,8 @@ use crate::{
|
||||
tcprelay::crypto_io::{CryptoRead, CryptoStream, CryptoWrite},
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "aead-cipher-2022")]
|
||||
use crate::{context::Context, relay::get_aead_2022_padding_size};
|
||||
|
||||
enum ProxyClientStreamWriteState {
|
||||
Connect(Address),
|
||||
@@ -197,8 +197,8 @@ fn poll_read_aead_2022_header<S>(
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
use super::protocol::v2::{get_now_timestamp, Aead2022TcpStreamType, SERVER_STREAM_TIMESTAMP_MAX_DIFF};
|
||||
use bytes::Buf;
|
||||
use std::time::SystemTime;
|
||||
|
||||
// AEAD 2022 TCP Response Header
|
||||
//
|
||||
@@ -208,9 +208,6 @@ where
|
||||
// | Request SALT (Variable ...)
|
||||
// +-------+-------+-------+-------+-------+-------+-------+-------+-------+
|
||||
|
||||
const SERVER_STREAM_TYPE: u8 = 1;
|
||||
const SERVER_STREAM_TIMESTAMP_MAX_DIFF: u64 = 30;
|
||||
|
||||
// Initialize buffer
|
||||
let method = stream.method();
|
||||
if header_buf.is_empty() {
|
||||
@@ -230,7 +227,7 @@ where
|
||||
// Done reading TCP header, check all the fields
|
||||
|
||||
let stream_type = header_buf.get_u8();
|
||||
if stream_type != SERVER_STREAM_TYPE {
|
||||
if stream_type != Aead2022TcpStreamType::Server as u8 {
|
||||
return Err(io::Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("received TCP response header with wrong type {}", stream_type),
|
||||
@@ -239,10 +236,7 @@ where
|
||||
}
|
||||
|
||||
let timestamp = header_buf.get_u64();
|
||||
let now = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
|
||||
Ok(n) => n.as_secs(),
|
||||
Err(_) => panic!("SystemTime::now() is before UNIX Epoch!"),
|
||||
};
|
||||
let now = get_now_timestamp();
|
||||
|
||||
if now.abs_diff(timestamp) > SERVER_STREAM_TIMESTAMP_MAX_DIFF {
|
||||
return Err(io::Error::new(
|
||||
@@ -317,30 +311,14 @@ fn make_first_packet_buffer(method: CipherKind, addr: &Address, buf: &[u8]) -> B
|
||||
//
|
||||
// Client -> Server TYPE=0
|
||||
|
||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||
use std::{cell::RefCell, time::SystemTime};
|
||||
use super::protocol::v2::{get_now_timestamp, Aead2022TcpStreamType};
|
||||
|
||||
const CLIENT_STREAM_TYPE: u8 = 0;
|
||||
const MAX_PADDING_SIZE: usize = 900;
|
||||
|
||||
thread_local! {
|
||||
static PADDING_RNG: RefCell<SmallRng> = RefCell::new(SmallRng::from_entropy());
|
||||
}
|
||||
|
||||
let padding_size = if buf.is_empty() {
|
||||
PADDING_RNG.with(|rng| rng.borrow_mut().gen::<usize>() % MAX_PADDING_SIZE)
|
||||
} else {
|
||||
// If handshake with data buffer, then padding is not required and should be 0 for letting TFO work properly.
|
||||
0
|
||||
};
|
||||
let padding_size = get_aead_2022_padding_size(buf);
|
||||
|
||||
buffer.reserve(1 + 8 + addr_length + 2 + padding_size);
|
||||
buffer.put_u8(CLIENT_STREAM_TYPE);
|
||||
buffer.put_u8(Aead2022TcpStreamType::Client as u8);
|
||||
|
||||
let timestamp = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
|
||||
Ok(n) => n.as_secs(),
|
||||
Err(_) => panic!("SystemTime::now() is before UNIX Epoch!"),
|
||||
};
|
||||
let timestamp = get_now_timestamp();
|
||||
buffer.put_u64(timestamp);
|
||||
|
||||
addr.write_to_buf(&mut buffer);
|
||||
|
||||
@@ -119,7 +119,7 @@ impl DecryptedReader {
|
||||
}
|
||||
|
||||
let iv = &self.buffer[..iv_len];
|
||||
context.check_nonce_replay(iv)?;
|
||||
context.check_nonce_replay(self.method, iv)?;
|
||||
|
||||
trace!("got stream iv {:?}", ByteStr::new(iv));
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ pub fn encrypt_payload_aead(
|
||||
let salt = &mut dst[..salt_len];
|
||||
|
||||
if salt_len > 0 {
|
||||
context.generate_nonce(salt, false);
|
||||
context.generate_nonce(method, salt, false);
|
||||
trace!("UDP packet generated aead salt {:?}", ByteStr::new(salt));
|
||||
}
|
||||
|
||||
|
||||
@@ -63,24 +63,25 @@ use aes::{
|
||||
};
|
||||
use byte_string::ByteStr;
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use log::{error, trace};
|
||||
use log::{error, trace, warn};
|
||||
use lru_time_cache::LruCache;
|
||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||
use once_cell::sync::Lazy;
|
||||
use spin::Mutex as SpinMutex;
|
||||
|
||||
use crate::{
|
||||
config::ReplayAttackPolicy,
|
||||
context::Context,
|
||||
crypto::{
|
||||
v2::udp::{ChaCha20Poly1305Cipher, UdpCipher},
|
||||
CipherKind,
|
||||
},
|
||||
relay::socks5::Address,
|
||||
relay::{get_aead_2022_padding_size, socks5::Address},
|
||||
};
|
||||
|
||||
use super::options::UdpSocketControlData;
|
||||
|
||||
const CLIENT_SOCKET_TYPE: u8 = 0;
|
||||
const SERVER_SOCKET_TYPE: u8 = 1;
|
||||
const MAX_PADDING_SIZE: usize = 900;
|
||||
const SERVER_PACKET_TIMESTAMP_MAX_DIFF: u64 = 30;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
@@ -124,19 +125,12 @@ impl Ord for CipherKey {
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static PADDING_RNG: RefCell<SmallRng> = RefCell::new(SmallRng::from_entropy());
|
||||
static CIPHER_CACHE: RefCell<LruCache<CipherKey, Rc<UdpCipher>>> =
|
||||
RefCell::new(LruCache::with_expiry_duration_and_capacity(Duration::from_secs(60), 102400));
|
||||
}
|
||||
const CIPHER_CACHE_DURATION: Duration = Duration::from_secs(30);
|
||||
const CIPHER_CACHE_LIMIT: usize = 102400;
|
||||
|
||||
#[inline]
|
||||
fn get_padding_size(payload: &[u8]) -> usize {
|
||||
if payload.is_empty() {
|
||||
PADDING_RNG.with(|rng| rng.borrow_mut().gen::<usize>() % MAX_PADDING_SIZE)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
thread_local! {
|
||||
static CIPHER_CACHE: RefCell<LruCache<CipherKey, Rc<UdpCipher>>> =
|
||||
RefCell::new(LruCache::with_expiry_duration_and_capacity(CIPHER_CACHE_DURATION, CIPHER_CACHE_LIMIT));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -165,6 +159,57 @@ fn get_cipher(method: CipherKind, key: &[u8], session_id: u64) -> Rc<UdpCipher>
|
||||
})
|
||||
}
|
||||
|
||||
fn check_and_record_nonce(method: CipherKind, key: &[u8], session_id: u64, nonce: &[u8]) -> bool {
|
||||
static REPLAY_FILTER_RECORDER: Lazy<SpinMutex<LruCache<CipherKey, LruCache<Vec<u8>, ()>>>> = Lazy::new(|| {
|
||||
SpinMutex::new(LruCache::with_expiry_duration_and_capacity(
|
||||
CIPHER_CACHE_DURATION,
|
||||
CIPHER_CACHE_LIMIT,
|
||||
))
|
||||
});
|
||||
|
||||
let cache_key = CipherKey {
|
||||
method,
|
||||
// The key is stored in ServerConfig structure, so the address of it won't change.
|
||||
key: key.as_ptr() as usize,
|
||||
session_id,
|
||||
};
|
||||
|
||||
const REPLAY_DETECT_NONCE_EXPIRE_DURATION: Duration = Duration::from_secs(SERVER_PACKET_TIMESTAMP_MAX_DIFF);
|
||||
|
||||
let mut session_map = REPLAY_FILTER_RECORDER.lock();
|
||||
|
||||
let session_nonce_map = session_map
|
||||
.entry(cache_key)
|
||||
.or_insert_with(|| LruCache::with_expiry_duration(REPLAY_DETECT_NONCE_EXPIRE_DURATION));
|
||||
|
||||
if session_nonce_map.get(nonce).is_some() {
|
||||
return true;
|
||||
}
|
||||
|
||||
session_nonce_map.insert(nonce.to_vec(), ());
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_nonce_replay(context: &Context, method: CipherKind, key: &[u8], session_id: u64, nonce: &[u8]) -> bool {
|
||||
match context.replay_attack_policy() {
|
||||
ReplayAttackPolicy::Ignore => false,
|
||||
ReplayAttackPolicy::Detect => {
|
||||
if check_and_record_nonce(method, key, session_id, nonce) {
|
||||
warn!("detected repeated nonce salt {:?}", ByteStr::new(nonce));
|
||||
}
|
||||
false
|
||||
}
|
||||
ReplayAttackPolicy::Reject => {
|
||||
let replayed = check_and_record_nonce(method, key, session_id, nonce);
|
||||
if replayed {
|
||||
error!("detected repeated nonce salt {:?}", ByteStr::new(nonce));
|
||||
}
|
||||
replayed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encrypt_message(_context: &Context, method: CipherKind, key: &[u8], packet: &mut BytesMut, session_id: u64) {
|
||||
unsafe {
|
||||
packet.advance_mut(method.tag_len());
|
||||
@@ -217,10 +262,6 @@ fn decrypt_message(context: &Context, method: CipherKind, key: &[u8], packet: &m
|
||||
let nonce_size = ChaCha20Poly1305Cipher::nonce_size();
|
||||
|
||||
let (nonce, message) = packet.split_at_mut(nonce_size);
|
||||
if let Err(..) = context.check_nonce_replay(nonce) {
|
||||
error!("detected replayed nonce: {:?}", ByteStr::new(nonce));
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: ChaCha20-Poly1305's session_id is not required because it uses PSK directly
|
||||
//
|
||||
@@ -231,6 +272,11 @@ fn decrypt_message(context: &Context, method: CipherKind, key: &[u8], packet: &m
|
||||
u64::from_be(session_id_slice[0])
|
||||
};
|
||||
|
||||
if check_nonce_replay(context, method, key, session_id, nonce) {
|
||||
error!("detected replayed nonce: {:?}", ByteStr::new(nonce));
|
||||
return false;
|
||||
}
|
||||
|
||||
let cipher = get_cipher(method, key, session_id);
|
||||
|
||||
if !cipher.decrypt_packet(nonce, message) {
|
||||
@@ -271,7 +317,7 @@ fn decrypt_message(context: &Context, method: CipherKind, key: &[u8], packet: &m
|
||||
let nonce = &packet_header[4..16];
|
||||
|
||||
let cipher = {
|
||||
if let Err(..) = context.check_nonce_replay(nonce) {
|
||||
if check_nonce_replay(context, method, key, session_id, nonce) {
|
||||
error!("detected replayed nonce: {:?}", ByteStr::new(nonce));
|
||||
return false;
|
||||
}
|
||||
@@ -308,7 +354,7 @@ pub fn encrypt_client_payload_aead_2022(
|
||||
payload: &[u8],
|
||||
dst: &mut BytesMut,
|
||||
) {
|
||||
let padding_size = get_padding_size(payload);
|
||||
let padding_size = get_aead_2022_padding_size(payload);
|
||||
let nonce_size = get_nonce_len(method);
|
||||
|
||||
dst.reserve(
|
||||
@@ -322,7 +368,7 @@ pub fn encrypt_client_payload_aead_2022(
|
||||
}
|
||||
let nonce = &mut dst[..nonce_size];
|
||||
|
||||
context.generate_nonce(nonce, false);
|
||||
context.generate_nonce(method, nonce, false);
|
||||
trace!("UDP packet generated aead nonce {:?}", ByteStr::new(nonce));
|
||||
}
|
||||
|
||||
@@ -414,7 +460,7 @@ pub fn encrypt_server_payload_aead_2022(
|
||||
payload: &[u8],
|
||||
dst: &mut BytesMut,
|
||||
) {
|
||||
let padding_size = get_padding_size(payload);
|
||||
let padding_size = get_aead_2022_padding_size(payload);
|
||||
let nonce_size = get_nonce_len(method);
|
||||
|
||||
dst.reserve(
|
||||
@@ -428,7 +474,7 @@ pub fn encrypt_server_payload_aead_2022(
|
||||
}
|
||||
let nonce = &mut dst[..nonce_size];
|
||||
|
||||
context.generate_nonce(nonce, false);
|
||||
context.generate_nonce(method, nonce, false);
|
||||
trace!("UDP packet generated aead nonce {:?}", ByteStr::new(nonce));
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ pub fn encrypt_payload_stream(
|
||||
let iv = &mut dst[..iv_len];
|
||||
|
||||
if iv_len > 0 {
|
||||
context.generate_nonce(iv, false);
|
||||
context.generate_nonce(method, iv, false);
|
||||
trace!("UDP packet generated stream iv {:?}", ByteStr::new(iv));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::config::ServerType;
|
||||
use crate::{config::ServerType, crypto::CipherKind};
|
||||
|
||||
/// A dummy protector against replay attack
|
||||
///
|
||||
@@ -14,7 +14,7 @@ impl ReplayProtector {
|
||||
|
||||
/// Check if nonce exist or not
|
||||
#[inline(always)]
|
||||
pub fn check_nonce_and_set(&self, _nonce: &[u8]) -> bool {
|
||||
pub fn check_nonce_and_set(&self, _method: CipherKind, _nonce: &[u8]) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
#[cfg(feature = "aead-cipher-2022")]
|
||||
use std::time::Duration;
|
||||
|
||||
use bloomfilter::Bloom;
|
||||
use log::debug;
|
||||
#[cfg(feature = "aead-cipher-2022")]
|
||||
use lru_time_cache::LruCache;
|
||||
use spin::Mutex as SpinMutex;
|
||||
|
||||
use crate::config::ServerType;
|
||||
#[cfg(feature = "aead-cipher-2022")]
|
||||
use crate::relay::tcprelay::proxy_stream::protocol::v2::SERVER_STREAM_TIMESTAMP_MAX_DIFF;
|
||||
use crate::{config::ServerType, crypto::CipherKind};
|
||||
|
||||
// Entries for server's bloom filter
|
||||
//
|
||||
@@ -98,6 +105,12 @@ pub struct ReplayProtector {
|
||||
// Check for duplicated IV/Nonce, for prevent replay attack
|
||||
// https://github.com/shadowsocks/shadowsocks-org/issues/44
|
||||
nonce_ppbloom: SpinMutex<PingPongBloom>,
|
||||
|
||||
// AEAD 2022 specific filter.
|
||||
// AEAD 2022 TCP protocol has a timestamp, which can already reject most of the replay requests,
|
||||
// so we only need to remember nonce that are in the valid time range
|
||||
#[cfg(feature = "aead-cipher-2022")]
|
||||
nonce_set: SpinMutex<LruCache<Vec<u8>, ()>>,
|
||||
}
|
||||
|
||||
impl ReplayProtector {
|
||||
@@ -105,18 +118,32 @@ impl ReplayProtector {
|
||||
pub fn new(config_type: ServerType) -> ReplayProtector {
|
||||
ReplayProtector {
|
||||
nonce_ppbloom: SpinMutex::new(PingPongBloom::new(config_type)),
|
||||
#[cfg(feature = "aead-cipher-2022")]
|
||||
nonce_set: SpinMutex::new(LruCache::with_expiry_duration(Duration::from_secs(
|
||||
SERVER_STREAM_TIMESTAMP_MAX_DIFF,
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if nonce exist or not
|
||||
#[inline(always)]
|
||||
pub fn check_nonce_and_set(&self, nonce: &[u8]) -> bool {
|
||||
pub fn check_nonce_and_set(&self, method: CipherKind, nonce: &[u8]) -> bool {
|
||||
// Plain cipher doesn't have a nonce
|
||||
// Always treated as non-duplicated
|
||||
if nonce.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
#[cfg(feature = "aead-cipher-2022")]
|
||||
if method.is_aead_2022() {
|
||||
let mut set = self.nonce_set.lock();
|
||||
if set.get(nonce).is_some() {
|
||||
return true;
|
||||
}
|
||||
set.insert(nonce.to_vec(), ());
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut ppbloom = self.nonce_ppbloom.lock();
|
||||
ppbloom.check_and_set(nonce)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user