feat: make aead-cipher an optional feature

This commit is contained in:
zonyitoo
2024-11-02 13:11:31 +08:00
parent ac7b30530d
commit 466309b970
10 changed files with 123 additions and 50 deletions

49
Cargo.lock generated
View File

@@ -136,9 +136,9 @@ dependencies = [
[[package]]
name = "anstyle"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
@@ -170,9 +170,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.91"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13"
[[package]]
name = "arc-swap"
@@ -465,9 +465,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.1.31"
version = "1.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
checksum = "538b056773ee67775e422d15c33169f5415706855814b96505c84ee942f2bae2"
dependencies = [
"jobserver",
"libc",
@@ -1097,9 +1097,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-lite"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
checksum = "3f1fa2f9765705486b33fd2acf1577f8ec449c2ba1f318ae5447697b7c08d210"
dependencies = [
"futures-core",
"pin-project-lite",
@@ -1503,7 +1503,7 @@ dependencies = [
"http 1.1.0",
"hyper",
"hyper-util",
"rustls 0.23.15",
"rustls 0.23.16",
"rustls-native-certs 0.8.0",
"rustls-pki-types",
"tokio",
@@ -1530,9 +1530,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.9"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b"
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
dependencies = [
"bytes",
"futures-channel",
@@ -2569,9 +2569,9 @@ dependencies = [
"bytes",
"pin-project-lite",
"quinn-proto 0.11.8",
"quinn-udp 0.5.5",
"quinn-udp 0.5.6",
"rustc-hash 2.0.0",
"rustls 0.23.15",
"rustls 0.23.16",
"socket2",
"thiserror",
"tokio",
@@ -2605,7 +2605,7 @@ dependencies = [
"rand",
"ring 0.17.8",
"rustc-hash 2.0.0",
"rustls 0.23.15",
"rustls 0.23.16",
"slab",
"thiserror",
"tinyvec",
@@ -2627,10 +2627,11 @@ dependencies = [
[[package]]
name = "quinn-udp"
version = "0.5.5"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b"
checksum = "e346e016eacfff12233c243718197ca12f148c84e1e84268a896699b41c71780"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
@@ -2785,7 +2786,7 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"quinn 0.11.5",
"rustls 0.23.15",
"rustls 0.23.16",
"rustls-native-certs 0.8.0",
"rustls-pemfile 2.2.0",
"rustls-pki-types",
@@ -2926,9 +2927,9 @@ checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
[[package]]
name = "rustix"
version = "0.38.37"
version = "0.38.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a"
dependencies = [
"bitflags 2.6.0",
"errno",
@@ -2951,9 +2952,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.15"
version = "0.23.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993"
checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e"
dependencies = [
"log",
"once_cell",
@@ -3259,9 +3260,9 @@ dependencies = [
[[package]]
name = "shadowsocks-crypto"
version = "0.5.5"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e49ecfad8b27f3df28848af11f08aa10df0c6b74b45748131753913be23373"
checksum = "8e4c5a437e27929c991daa2073b130fd94731ebdc2ba771be95aed5029defcae"
dependencies = [
"aead",
"aes",
@@ -3809,7 +3810,7 @@ version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [
"rustls 0.23.15",
"rustls 0.23.16",
"rustls-pki-types",
"tokio",
]

View File

@@ -33,7 +33,7 @@ aead-cipher = ["shadowsocks-crypto/v1-aead"]
# Enable extra AEAD ciphers
# WARN: These non-standard AEAD ciphers are not officially supported by shadowsocks community
aead-cipher-extra = ["shadowsocks-crypto/v1-aead-extra"]
aead-cipher-extra = ["aead-cipher", "shadowsocks-crypto/v1-aead-extra"]
# Enable AEAD 2022
aead-cipher-2022 = [

View File

@@ -20,11 +20,9 @@ use log::error;
use thiserror::Error;
use url::{self, Url};
use crate::{
crypto::{v1::openssl_bytes_to_key, CipherKind},
plugin::PluginConfig,
relay::socks5::Address,
};
#[cfg(any(feature = "stream-cipher", feature = "aead-cipher"))]
use crate::crypto::v1::openssl_bytes_to_key;
use crate::{crypto::CipherKind, plugin::PluginConfig, relay::socks5::Address};
const USER_KEY_BASE64_ENGINE: base64::engine::GeneralPurpose = base64::engine::GeneralPurpose::new(
&base64::alphabet::STANDARD,
@@ -427,9 +425,9 @@ pub struct ServerConfig {
source: ServerSource,
}
#[cfg(feature = "aead-cipher-2022")]
#[inline]
fn make_derived_key(method: CipherKind, password: &str, enc_key: &mut [u8]) {
#[cfg(feature = "aead-cipher-2022")]
if method.is_aead_2022() {
// AEAD 2022 password is a base64 form of enc_key
match AEAD2022_PASSWORD_BASE64_ENGINE.decode(password) {
@@ -449,15 +447,21 @@ fn make_derived_key(method: CipherKind, password: &str, enc_key: &mut [u8]) {
panic!("{method} password {password} is not base64 encoded, error: {err}");
}
}
} else {
openssl_bytes_to_key(password.as_bytes(), enc_key);
}
}
#[cfg(not(feature = "aead-cipher-2022"))]
#[inline]
fn make_derived_key(_method: CipherKind, password: &str, enc_key: &mut [u8]) {
openssl_bytes_to_key(password.as_bytes(), enc_key);
return;
}
cfg_if! {
if #[cfg(any(feature = "stream-cipher", feature = "aead-cipher"))] {
let _ = method;
openssl_bytes_to_key(password.as_bytes(), enc_key);
} else {
// No default implementation.
let _ = password;
let _ = enc_key;
unreachable!("{method} don't know how to make a derived key");
}
}
}
/// Check if method supports Extended Identity Header

View File

@@ -7,7 +7,7 @@ use log::warn;
use crate::{
config::{ReplayAttackPolicy, ServerType},
crypto::{v1::random_iv_or_salt, CipherKind},
crypto::CipherKind,
dns_resolver::DnsResolver,
security::replay::ReplayProtector,
};
@@ -50,6 +50,7 @@ impl Context {
/// Check if nonce exist or not
///
/// If not, set into the current bloom filter
#[cfg(any(feature = "stream-cipher", feature = "aead-cipher", feature = "aead-cipher-2022"))]
#[inline(always)]
fn check_nonce_and_set(&self, method: CipherKind, nonce: &[u8]) -> bool {
match self.replay_policy {
@@ -64,7 +65,10 @@ impl Context {
return;
}
#[cfg(any(feature = "stream-cipher", feature = "aead-cipher", feature = "aead-cipher-2022"))]
loop {
use crate::crypto::v1::random_iv_or_salt;
random_iv_or_salt(nonce);
// Salt already exists, generate a new one.
@@ -74,6 +78,12 @@ impl Context {
break;
}
#[cfg(not(any(feature = "stream-cipher", feature = "aead-cipher", feature = "aead-cipher-2022")))]
if !nonce.is_empty() {
let _ = unique;
panic!("{method} don't know how to generate nonce");
}
}
/// Check nonce replay

View File

@@ -8,9 +8,11 @@ use std::{
task::{self, Poll},
};
#[cfg(any(feature = "stream-cipher", feature = "aead-cipher", feature = "aead-cipher-2022"))]
use byte_string::ByteStr;
use bytes::Bytes;
use futures::ready;
#[cfg(any(feature = "stream-cipher", feature = "aead-cipher", feature = "aead-cipher-2022"))]
use log::trace;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
@@ -20,6 +22,7 @@ use crate::{
crypto::{CipherCategory, CipherKind},
};
#[cfg(feature = "aead-cipher")]
use super::aead::{DecryptedReader as AeadDecryptedReader, EncryptedWriter as AeadEncryptedWriter};
#[cfg(feature = "aead-cipher-2022")]
use super::aead_2022::{DecryptedReader as Aead2022DecryptedReader, EncryptedWriter as Aead2022EncryptedWriter};
@@ -34,6 +37,7 @@ pub enum ProtocolError {
#[cfg(feature = "stream-cipher")]
#[error(transparent)]
StreamError(#[from] super::stream::ProtocolError),
#[cfg(feature = "aead-cipher")]
#[error(transparent)]
AeadError(#[from] super::aead::ProtocolError),
#[cfg(feature = "aead-cipher-2022")]
@@ -50,6 +54,7 @@ impl From<ProtocolError> for io::Error {
ProtocolError::IoError(err) => err,
#[cfg(feature = "stream-cipher")]
ProtocolError::StreamError(err) => err.into(),
#[cfg(feature = "aead-cipher")]
ProtocolError::AeadError(err) => err.into(),
#[cfg(feature = "aead-cipher-2022")]
ProtocolError::Aead2022Error(err) => err.into(),
@@ -70,6 +75,7 @@ pub enum StreamType {
#[allow(clippy::large_enum_variant)]
pub enum DecryptedReader {
None,
#[cfg(feature = "aead-cipher")]
Aead(AeadDecryptedReader),
#[cfg(feature = "stream-cipher")]
Stream(StreamDecryptedReader),
@@ -98,8 +104,13 @@ impl DecryptedReader {
match method.category() {
#[cfg(feature = "stream-cipher")]
CipherCategory::Stream => DecryptedReader::Stream(StreamDecryptedReader::new(method, key)),
#[cfg(feature = "aead-cipher")]
CipherCategory::Aead => DecryptedReader::Aead(AeadDecryptedReader::new(method, key)),
CipherCategory::None => DecryptedReader::None,
CipherCategory::None => {
let _ = method;
let _ = key;
DecryptedReader::None
}
#[cfg(feature = "aead-cipher-2022")]
CipherCategory::Aead2022 => DecryptedReader::Aead2022(Aead2022DecryptedReader::with_user_manager(
stream_ty,
@@ -127,10 +138,14 @@ impl DecryptedReader {
DecryptedReader::Stream(ref mut reader) => {
reader.poll_read_decrypted(cx, context, stream, buf).map_err(Into::into)
}
#[cfg(feature = "aead-cipher")]
DecryptedReader::Aead(ref mut reader) => {
reader.poll_read_decrypted(cx, context, stream, buf).map_err(Into::into)
}
DecryptedReader::None => Pin::new(stream).poll_read(cx, buf).map_err(Into::into),
DecryptedReader::None => {
let _ = context;
Pin::new(stream).poll_read(cx, buf).map_err(Into::into)
}
#[cfg(feature = "aead-cipher-2022")]
DecryptedReader::Aead2022(ref mut reader) => {
reader.poll_read_decrypted(cx, context, stream, buf).map_err(Into::into)
@@ -143,6 +158,7 @@ impl DecryptedReader {
match *self {
#[cfg(feature = "stream-cipher")]
DecryptedReader::Stream(ref reader) => reader.iv(),
#[cfg(feature = "aead-cipher")]
DecryptedReader::Aead(ref reader) => reader.salt(),
DecryptedReader::None => None,
#[cfg(feature = "aead-cipher-2022")]
@@ -155,6 +171,7 @@ impl DecryptedReader {
match *self {
#[cfg(feature = "stream-cipher")]
DecryptedReader::Stream(..) => None,
#[cfg(feature = "aead-cipher")]
DecryptedReader::Aead(..) => None,
DecryptedReader::None => None,
#[cfg(feature = "aead-cipher-2022")]
@@ -167,6 +184,7 @@ impl DecryptedReader {
match *self {
#[cfg(feature = "stream-cipher")]
DecryptedReader::Stream(..) => None,
#[cfg(feature = "aead-cipher")]
DecryptedReader::Aead(..) => None,
DecryptedReader::None => None,
#[cfg(feature = "aead-cipher-2022")]
@@ -178,6 +196,7 @@ impl DecryptedReader {
match *self {
#[cfg(feature = "stream-cipher")]
DecryptedReader::Stream(ref reader) => reader.handshaked(),
#[cfg(feature = "aead-cipher")]
DecryptedReader::Aead(ref reader) => reader.handshaked(),
DecryptedReader::None => true,
#[cfg(feature = "aead-cipher-2022")]
@@ -189,6 +208,7 @@ impl DecryptedReader {
/// Writer for writing encrypted data stream into shadowsocks' tunnel
pub enum EncryptedWriter {
None,
#[cfg(feature = "aead-cipher")]
Aead(AeadEncryptedWriter),
#[cfg(feature = "stream-cipher")]
Stream(StreamEncryptedWriter),
@@ -206,8 +226,13 @@ impl EncryptedWriter {
match method.category() {
#[cfg(feature = "stream-cipher")]
CipherCategory::Stream => EncryptedWriter::Stream(StreamEncryptedWriter::new(method, key, nonce)),
#[cfg(feature = "aead-cipher")]
CipherCategory::Aead => EncryptedWriter::Aead(AeadEncryptedWriter::new(method, key, nonce)),
CipherCategory::None => EncryptedWriter::None,
CipherCategory::None => {
let _ = key;
let _ = nonce;
EncryptedWriter::None
}
#[cfg(feature = "aead-cipher-2022")]
CipherCategory::Aead2022 => {
EncryptedWriter::Aead2022(Aead2022EncryptedWriter::new(stream_ty, method, key, nonce))
@@ -231,8 +256,13 @@ impl EncryptedWriter {
match method.category() {
#[cfg(feature = "stream-cipher")]
CipherCategory::Stream => EncryptedWriter::Stream(StreamEncryptedWriter::new(method, key, nonce)),
#[cfg(feature = "aead-cipher")]
CipherCategory::Aead => EncryptedWriter::Aead(AeadEncryptedWriter::new(method, key, nonce)),
CipherCategory::None => EncryptedWriter::None,
CipherCategory::None => {
let _ = key;
let _ = nonce;
EncryptedWriter::None
}
#[cfg(feature = "aead-cipher-2022")]
CipherCategory::Aead2022 => EncryptedWriter::Aead2022(Aead2022EncryptedWriter::with_identity(
stream_ty,
@@ -258,6 +288,7 @@ impl EncryptedWriter {
match *self {
#[cfg(feature = "stream-cipher")]
EncryptedWriter::Stream(ref mut writer) => writer.poll_write_encrypted(cx, stream, buf).map_err(Into::into),
#[cfg(feature = "aead-cipher")]
EncryptedWriter::Aead(ref mut writer) => writer.poll_write_encrypted(cx, stream, buf).map_err(Into::into),
EncryptedWriter::None => Pin::new(stream).poll_write(cx, buf).map_err(Into::into),
#[cfg(feature = "aead-cipher-2022")]
@@ -272,6 +303,7 @@ impl EncryptedWriter {
match *self {
#[cfg(feature = "stream-cipher")]
EncryptedWriter::Stream(ref writer) => writer.iv(),
#[cfg(feature = "aead-cipher")]
EncryptedWriter::Aead(ref writer) => writer.salt(),
EncryptedWriter::None => &[],
#[cfg(feature = "aead-cipher-2022")]
@@ -355,6 +387,7 @@ impl<S> CryptoStream<S> {
let prev_len = match category {
#[cfg(feature = "stream-cipher")]
CipherCategory::Stream => method.iv_len(),
#[cfg(feature = "aead-cipher")]
CipherCategory::Aead => method.salt_len(),
CipherCategory::None => 0,
#[cfg(feature = "aead-cipher-2022")]
@@ -369,13 +402,18 @@ impl<S> CryptoStream<S> {
trace!("generated Stream cipher IV {:?}", ByteStr::new(&local_iv));
local_iv
}
#[cfg(feature = "aead-cipher")]
CipherCategory::Aead => {
let mut local_salt = vec![0u8; prev_len];
context.generate_nonce(method, &mut local_salt, true);
trace!("generated AEAD cipher salt {:?}", ByteStr::new(&local_salt));
local_salt
}
CipherCategory::None => Vec::new(),
CipherCategory::None => {
debug_assert_eq!(prev_len, 0);
let _ = context;
Vec::new()
}
#[cfg(feature = "aead-cipher-2022")]
CipherCategory::Aead2022 => {
// AEAD-2022 has a request-salt in respond header, so the generated salt doesn't need to be remembered.

View File

@@ -5,6 +5,7 @@ pub use self::{
proxy_stream::{ProxyClientStream, ProxyServerStream},
};
#[cfg(feature = "aead-cipher")]
mod aead;
#[cfg(feature = "aead-cipher-2022")]
mod aead_2022;

View File

@@ -28,7 +28,11 @@ pub enum TcpRequestHeader {
impl TcpRequestHeader {
pub async fn read_from<R: AsyncRead + Unpin>(method: CipherKind, reader: &mut R) -> io::Result<TcpRequestHeader> {
match method.category() {
CipherCategory::None | CipherCategory::Aead => Ok(TcpRequestHeader::Stream(
CipherCategory::None => Ok(TcpRequestHeader::Stream(
StreamTcpRequestHeader::read_from(reader).await?,
)),
#[cfg(feature = "aead-cipher")]
CipherCategory::Aead => Ok(TcpRequestHeader::Stream(
StreamTcpRequestHeader::read_from(reader).await?,
)),
#[cfg(feature = "stream-cipher")]

View File

@@ -155,6 +155,7 @@ where
fn plain_read_buffer_size(method: CipherKind) -> usize {
match method.category() {
#[cfg(feature = "aead-cipher")]
CipherCategory::Aead => super::aead::MAX_PACKET_SIZE,
#[cfg(feature = "stream-cipher")]
CipherCategory::Stream => 1 << 14,

View File

@@ -30,17 +30,16 @@ use crate::{
relay::socks5::{Address, Error as Socks5Error},
};
#[cfg(feature = "aead-cipher")]
use super::aead::{decrypt_payload_aead, encrypt_payload_aead};
#[cfg(feature = "aead-cipher-2022")]
use super::aead_2022::{
decrypt_client_payload_aead_2022, decrypt_server_payload_aead_2022, encrypt_client_payload_aead_2022,
encrypt_server_payload_aead_2022,
};
use super::options::UdpSocketControlData;
#[cfg(feature = "stream-cipher")]
use super::stream::{decrypt_payload_stream, encrypt_payload_stream};
use super::{
aead::{decrypt_payload_aead, encrypt_payload_aead},
options::UdpSocketControlData,
};
/// UDP shadowsocks protocol errors
#[derive(thiserror::Error, Debug)]
@@ -50,6 +49,7 @@ pub enum ProtocolError {
#[cfg(feature = "stream-cipher")]
#[error(transparent)]
StreamError(#[from] super::stream::ProtocolError),
#[cfg(feature = "aead-cipher")]
#[error(transparent)]
AeadError(#[from] super::aead::ProtocolError),
#[cfg(feature = "aead-cipher-2022")]
@@ -74,6 +74,8 @@ pub fn encrypt_client_payload(
) {
match method.category() {
CipherCategory::None => {
let _ = context;
let _ = key;
let _ = control;
let _ = identity_keys;
dst.reserve(addr.serialized_len() + payload.len());
@@ -86,6 +88,7 @@ pub fn encrypt_client_payload(
let _ = identity_keys;
encrypt_payload_stream(context, method, key, addr, payload, dst)
}
#[cfg(feature = "aead-cipher")]
CipherCategory::Aead => {
let _ = control;
let _ = identity_keys;
@@ -110,6 +113,8 @@ pub fn encrypt_server_payload(
) {
match method.category() {
CipherCategory::None => {
let _ = context;
let _ = key;
let _ = control;
dst.reserve(addr.serialized_len() + payload.len());
addr.write_to_buf(dst);
@@ -120,6 +125,7 @@ pub fn encrypt_server_payload(
let _ = control;
encrypt_payload_stream(context, method, key, addr, payload, dst)
}
#[cfg(feature = "aead-cipher")]
CipherCategory::Aead => {
let _ = control;
encrypt_payload_aead(context, method, key, addr, payload, dst)
@@ -139,6 +145,8 @@ pub fn decrypt_client_payload(
) -> ProtocolResult<(usize, Address, Option<UdpSocketControlData>)> {
match method.category() {
CipherCategory::None => {
let _ = context;
let _ = key;
let _ = user_manager;
let mut cur = Cursor::new(payload);
match Address::read_cursor(&mut cur) {
@@ -158,6 +166,7 @@ pub fn decrypt_client_payload(
.map(|(n, a)| (n, a, None))
.map_err(Into::into)
}
#[cfg(feature = "aead-cipher")]
CipherCategory::Aead => {
let _ = user_manager;
decrypt_payload_aead(context, method, key, payload)
@@ -180,6 +189,9 @@ pub fn decrypt_server_payload(
) -> ProtocolResult<(usize, Address, Option<UdpSocketControlData>)> {
match method.category() {
CipherCategory::None => {
let _ = context;
let _ = key;
let mut cur = Cursor::new(payload);
match Address::read_cursor(&mut cur) {
Ok(address) => {
@@ -195,6 +207,7 @@ pub fn decrypt_server_payload(
CipherCategory::Stream => decrypt_payload_stream(context, method, key, payload)
.map(|(n, a)| (n, a, None))
.map_err(Into::into),
#[cfg(feature = "aead-cipher")]
CipherCategory::Aead => decrypt_payload_aead(context, method, key, payload)
.map(|(n, a)| (n, a, None))
.map_err(Into::into),

View File

@@ -52,6 +52,7 @@ use std::time::Duration;
pub use self::proxy_socket::ProxySocket;
pub use compat::{DatagramReceive, DatagramReceiveExt, DatagramSend, DatagramSendExt, DatagramSocket};
#[cfg(feature = "aead-cipher")]
mod aead;
#[cfg(feature = "aead-cipher-2022")]
mod aead_2022;