refactor: use strum on EncryptionAlgorithm, use Xor as default when AesGcm not available (#1923)

This commit is contained in:
Luna Yao
2026-03-25 11:42:34 +01:00
committed by GitHub
parent 1d89ddbb16
commit e2684a93de
14 changed files with 642 additions and 856 deletions
Generated
+23 -1
View File
@@ -2257,6 +2257,7 @@ dependencies = [
"smoltcp",
"snow",
"socket2 0.5.10",
"strum 0.27.2",
"stun_codec",
"sys-locale",
"tabled",
@@ -7644,7 +7645,7 @@ dependencies = [
"serde",
"serde_json",
"sqlx",
"strum",
"strum 0.26.3",
"thiserror 1.0.63",
"time",
"tracing",
@@ -8679,6 +8680,27 @@ version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
[[package]]
name = "strum"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.87",
]
[[package]]
name = "stun_codec"
version = "0.3.5"
+2
View File
@@ -50,6 +50,8 @@ time = "0.3"
toml = "0.8.12"
chrono = { version = "0.4.37", features = ["serde"] }
strum = { version = "0.27.2", features = ["derive"] }
gethostname = "0.5.0"
futures = { version = "0.3", features = ["bilock", "unstable"] }
+43 -61
View File
@@ -7,7 +7,11 @@ use std::{
use anyhow::Context;
use base64::{prelude::BASE64_STANDARD, Engine as _};
use cfg_if::cfg_if;
use clap::builder::PossibleValue;
use clap::ValueEnum;
use serde::{Deserialize, Serialize};
use strum::{Display, EnumString, VariantArray};
use tokio::io::AsyncReadExt as _;
use crate::{
@@ -59,7 +63,7 @@ pub fn gen_default_flags() -> Flags {
enable_relay_foreign_network_quic: false,
foreign_relay_bps_limit: u64::MAX,
multi_thread_count: 2,
encryption_algorithm: "aes-gcm".to_string(),
encryption_algorithm: EncryptionAlgorithm::default().to_string(),
disable_sym_hole_punching: false,
tld_dns_zone: DEFAULT_ET_DNS_ZONE.to_string(),
@@ -68,75 +72,53 @@ pub fn gen_default_flags() -> Flags {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Display, EnumString, VariantArray)]
#[strum(ascii_case_insensitive)]
pub enum EncryptionAlgorithm {
AesGcm,
Aes256Gcm,
#[strum(serialize = "xor")]
Xor,
#[cfg(feature = "wireguard")]
#[cfg(any(feature = "aes-gcm", feature = "wireguard", feature = "openssl-crypto"))]
#[strum(serialize = "aes-gcm")]
AesGcm,
#[cfg(any(feature = "aes-gcm", feature = "wireguard", feature = "openssl-crypto"))]
#[strum(serialize = "aes-256-gcm")]
Aes256Gcm,
#[cfg(any(feature = "wireguard", feature = "openssl-crypto"))]
#[strum(serialize = "chacha20")]
ChaCha20,
#[cfg(feature = "openssl-crypto")]
OpensslAesGcm,
#[cfg(feature = "openssl-crypto")]
OpensslChacha20,
#[cfg(feature = "openssl-crypto")]
OpensslAes256Gcm,
}
impl std::fmt::Display for EncryptionAlgorithm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::AesGcm => write!(f, "aes-gcm"),
Self::Aes256Gcm => write!(f, "aes-256-gcm"),
Self::Xor => write!(f, "xor"),
#[cfg(feature = "wireguard")]
Self::ChaCha20 => write!(f, "chacha20"),
#[cfg(feature = "openssl-crypto")]
Self::OpensslAesGcm => write!(f, "openssl-aes-gcm"),
#[cfg(feature = "openssl-crypto")]
Self::OpensslChacha20 => write!(f, "openssl-chacha20"),
#[cfg(feature = "openssl-crypto")]
Self::OpensslAes256Gcm => write!(f, "openssl-aes-256-gcm"),
impl ValueEnum for EncryptionAlgorithm {
fn value_variants<'a>() -> &'a [Self] {
Self::VARIANTS
}
fn from_str(input: &str, _ignore_case: bool) -> Result<Self, String> {
input
.parse()
.map_err(|_| format!("'{}' is not a valid encryption algorithm", input))
}
fn to_possible_value(&self) -> Option<PossibleValue> {
Some(PossibleValue::new(self.to_string()))
}
}
#[allow(clippy::derivable_impls)]
impl Default for EncryptionAlgorithm {
fn default() -> Self {
cfg_if! {
if #[cfg(any(feature = "aes-gcm", feature = "wireguard", feature = "openssl-crypto"))] {
EncryptionAlgorithm::AesGcm
} else {
crate::common::log::warn!("no AEAD encryption algorithm is available, using INSECURE XOR");
EncryptionAlgorithm::Xor
}
}
}
}
impl TryFrom<&str> for EncryptionAlgorithm {
type Error = anyhow::Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"aes-gcm" => Ok(Self::AesGcm),
"aes-256-gcm" => Ok(Self::Aes256Gcm),
"xor" => Ok(Self::Xor),
#[cfg(feature = "wireguard")]
"chacha20" => Ok(Self::ChaCha20),
#[cfg(feature = "openssl-crypto")]
"openssl-aes-gcm" => Ok(Self::OpensslAesGcm),
#[cfg(feature = "openssl-crypto")]
"openssl-chacha20" => Ok(Self::OpensslChacha20),
#[cfg(feature = "openssl-crypto")]
"openssl-aes-256-gcm" => Ok(Self::OpensslAes256Gcm),
_ => Err(anyhow::anyhow!("invalid encryption algorithm")),
}
}
}
pub fn get_avaliable_encrypt_methods() -> Vec<&'static str> {
let mut r = vec!["aes-gcm", "aes-256-gcm", "xor"];
if cfg!(feature = "wireguard") {
r.push("chacha20");
}
if cfg!(feature = "openssl-crypto") {
r.extend(vec![
"openssl-aes-gcm",
"openssl-chacha20",
"openssl-aes-256-gcm",
]);
}
r
}
#[auto_impl::auto_impl(Box, &)]
pub trait ConfigLoader: Send + Sync {
fn get_id(&self) -> uuid::Uuid;
+6 -7
View File
@@ -10,10 +10,9 @@ use std::{
use crate::{
common::{
config::{
get_avaliable_encrypt_methods, load_config_from_file, process_secure_mode_cfg,
ConfigFileControl, ConfigLoader, ConsoleLoggerConfig, FileLoggerConfig,
LoggingConfigLoader, NetworkIdentity, PeerConfig, PortForwardConfig, TomlConfigLoader,
VpnPortalConfig,
load_config_from_file, process_secure_mode_cfg, ConfigFileControl, ConfigLoader,
ConsoleLoggerConfig, EncryptionAlgorithm, FileLoggerConfig, LoggingConfigLoader,
NetworkIdentity, PeerConfig, PortForwardConfig, TomlConfigLoader, VpnPortalConfig,
},
constants::EASYTIER_VERSION,
log,
@@ -277,9 +276,9 @@ struct NetworkOptions {
long,
env = "ET_ENCRYPTION_ALGORITHM",
help = t!("core_clap.encryption_algorithm").to_string(),
value_parser = get_avaliable_encrypt_methods()
value_enum,
)]
encryption_algorithm: Option<String>,
encryption_algorithm: Option<EncryptionAlgorithm>,
#[arg(
long,
@@ -1007,7 +1006,7 @@ impl NetworkOptions {
f.enable_encryption = !v;
}
if let Some(algorithm) = &self.encryption_algorithm {
f.encryption_algorithm = algorithm.clone();
f.encryption_algorithm = algorithm.to_string();
}
if let Some(v) = self.disable_ipv6 {
f.enable_ipv6 = !v;
+41 -48
View File
@@ -1,10 +1,8 @@
use aes_gcm::aead::consts::{U12, U16};
use aes_gcm::aead::generic_array::GenericArray;
use aes_gcm::{AeadCore, AeadInPlace, Aes128Gcm, Aes256Gcm, Key, KeyInit, Nonce, Tag};
use aes_gcm::{AeadCore, AeadInPlace, Aes128Gcm, Aes256Gcm, Key, KeyInit};
use rand::rngs::OsRng;
use zerocopy::{AsBytes, FromBytes};
use crate::tunnel::packet_def::{AesGcmTail, ZCPacket, AES_GCM_ENCRYPTION_RESERVED};
use crate::tunnel::packet_def::{StandardAeadTail, ZCPacket};
use super::{Encryptor, Error};
@@ -42,27 +40,28 @@ impl Encryptor for AesGcmCipher {
}
let payload_len = zc_packet.payload().len();
if payload_len < AES_GCM_ENCRYPTION_RESERVED {
if payload_len < StandardAeadTail::SIZE {
return Err(Error::PacketTooShort(zc_packet.payload().len()));
}
let text_len = payload_len - AES_GCM_ENCRYPTION_RESERVED;
let text_len = payload_len - StandardAeadTail::SIZE;
let aes_tail = AesGcmTail::ref_from_suffix(zc_packet.payload())
let aes_tail = StandardAeadTail::ref_from_suffix(zc_packet.payload())
.unwrap()
.clone();
let nonce: &GenericArray<u8, U12> = Nonce::from_slice(&aes_tail.nonce);
let tag: GenericArray<u8, U16> = Tag::clone_from_slice(aes_tail.tag.as_slice());
let nonce = aes_tail.nonce.into();
let tag = aes_tail.tag.into();
let rs = match &self.cipher {
AesGcmEnum::AES128GCM(aes_gcm) => aes_gcm.decrypt_in_place_detached(
nonce,
&nonce,
&[],
&mut zc_packet.mut_payload()[..text_len],
&tag,
),
AesGcmEnum::AES256GCM(aes_gcm) => aes_gcm.decrypt_in_place_detached(
nonce,
&nonce,
&[],
&mut zc_packet.mut_payload()[..text_len],
&tag,
@@ -79,7 +78,7 @@ impl Encryptor for AesGcmCipher {
let old_len = zc_packet.buf_len();
zc_packet
.mut_inner()
.truncate(old_len - AES_GCM_ENCRYPTION_RESERVED);
.truncate(old_len - StandardAeadTail::SIZE);
Ok(())
}
@@ -98,43 +97,40 @@ impl Encryptor for AesGcmCipher {
return Ok(());
}
let mut tail = AesGcmTail::default();
if let Some(nonce) = nonce {
if nonce.len() != tail.nonce.len() {
return Err(Error::EncryptionFailed);
}
tail.nonce.copy_from_slice(nonce);
}
let rs = match &self.cipher {
let nonce = nonce
.map(|n| {
<[u8; StandardAeadTail::NONCE_SIZE]>::try_from(n)
.map(Into::into)
.map_err(|_| Error::EncryptionFailed)
})
.transpose()?;
let (tag, nonce) = match &self.cipher {
AesGcmEnum::AES128GCM(aes_gcm) => {
if nonce.is_none() {
let nonce = Aes128Gcm::generate_nonce(&mut OsRng);
tail.nonce.copy_from_slice(nonce.as_slice());
}
let nonce = Nonce::from_slice(&tail.nonce);
aes_gcm.encrypt_in_place_detached(nonce, &[], zc_packet.mut_payload())
let nonce = nonce.unwrap_or_else(|| Aes128Gcm::generate_nonce(&mut OsRng));
(
aes_gcm.encrypt_in_place_detached(&nonce, &[], zc_packet.mut_payload()),
nonce,
)
}
AesGcmEnum::AES256GCM(aes_gcm) => {
if nonce.is_none() {
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
tail.nonce.copy_from_slice(nonce.as_slice());
}
let nonce = Nonce::from_slice(&tail.nonce);
aes_gcm.encrypt_in_place_detached(nonce, &[], zc_packet.mut_payload())
let nonce = nonce.unwrap_or_else(|| Aes256Gcm::generate_nonce(&mut OsRng));
(
aes_gcm.encrypt_in_place_detached(&nonce, &[], zc_packet.mut_payload()),
nonce,
)
}
};
match rs {
Ok(tag) => {
tail.tag.copy_from_slice(tag.as_slice());
let tail = StandardAeadTail {
tag: tag.map_err(|_| Error::EncryptionFailed)?.into(),
nonce: nonce.into(),
};
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
pm_header.set_encrypted(true);
zc_packet.mut_inner().extend_from_slice(tail.as_bytes());
Ok(())
}
Err(_) => Err(Error::EncryptionFailed),
}
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
pm_header.set_encrypted(true);
zc_packet.mut_inner().extend_from_slice(tail.as_bytes());
Ok(())
}
}
@@ -142,7 +138,7 @@ impl Encryptor for AesGcmCipher {
mod tests {
use crate::{
peers::encrypt::{aes_gcm::AesGcmCipher, Encryptor},
tunnel::packet_def::{AesGcmTail, ZCPacket, AES_GCM_ENCRYPTION_RESERVED},
tunnel::packet_def::{StandardAeadTail, ZCPacket},
};
use zerocopy::FromBytes;
@@ -154,10 +150,7 @@ mod tests {
let mut packet = ZCPacket::new_with_payload(text);
packet.fill_peer_manager_hdr(0, 0, 0);
cipher.encrypt(&mut packet).unwrap();
assert_eq!(
packet.payload().len(),
text.len() + AES_GCM_ENCRYPTION_RESERVED
);
assert_eq!(packet.payload().len(), text.len() + StandardAeadTail::SIZE);
assert!(packet.peer_manager_header().unwrap().is_encrypted());
cipher.decrypt(&mut packet).unwrap();
@@ -186,7 +179,7 @@ mod tests {
assert_eq!(packet1.payload(), packet2.payload());
let tail = AesGcmTail::ref_from_suffix(packet1.payload()).unwrap();
let tail = StandardAeadTail::ref_from_suffix(packet1.payload()).unwrap();
assert_eq!(tail.nonce, nonce);
cipher.decrypt(&mut packet1).unwrap();
+50 -54
View File
@@ -1,23 +1,20 @@
use std::sync::Arc;
use crate::{
common::{config::EncryptionAlgorithm, log},
tunnel::packet_def::ZCPacket,
};
use cfg_if::cfg_if;
use std::sync::Arc;
#[cfg(feature = "wireguard")]
pub mod ring_aes_gcm;
#[cfg(feature = "wireguard")]
pub mod ring_chacha20;
pub mod ring;
#[cfg(feature = "aes-gcm")]
pub mod aes_gcm;
#[cfg(feature = "openssl-crypto")]
pub mod openssl_cipher;
pub mod openssl;
pub mod xor_cipher;
pub mod xor;
#[derive(thiserror::Error, Debug)]
pub enum Error {
@@ -32,6 +29,7 @@ pub enum Error {
}
pub trait Encryptor: Send + Sync + 'static {
fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error>;
fn encrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error>;
fn encrypt_with_nonce(
&self,
@@ -40,16 +38,11 @@ pub trait Encryptor: Send + Sync + 'static {
) -> Result<(), Error> {
self.encrypt(zc_packet)
}
fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error>;
}
pub struct NullCipher;
impl Encryptor for NullCipher {
fn encrypt(&self, _zc_packet: &mut ZCPacket) -> Result<(), Error> {
Ok(())
}
fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
let pm_header = zc_packet.peer_manager_header().unwrap();
if pm_header.is_encrypted() {
@@ -58,6 +51,10 @@ impl Encryptor for NullCipher {
Ok(())
}
}
fn encrypt(&self, _zc_packet: &mut ZCPacket) -> Result<(), Error> {
Ok(())
}
}
/// Create an encryptor based on the algorithm name
@@ -69,60 +66,59 @@ pub fn create_encryptor(
let algorithm = match EncryptionAlgorithm::try_from(algorithm) {
Ok(algorithm) => algorithm,
Err(_) => {
let default = EncryptionAlgorithm::default();
log::warn!(
"Unknown encryption algorithm: {}, falling back to default AES-GCM",
algorithm
"Unknown encryption algorithm: {}, falling back to default {}",
algorithm,
default
);
EncryptionAlgorithm::AesGcm
default
}
};
match algorithm {
EncryptionAlgorithm::Xor => Arc::new(xor::XorCipher::new(&key_128)),
#[cfg(any(feature = "aes-gcm", feature = "wireguard", feature = "openssl-crypto"))]
EncryptionAlgorithm::AesGcm => {
#[cfg(feature = "wireguard")]
{
Arc::new(ring_aes_gcm::AesGcmCipher::new_128(key_128))
}
#[cfg(all(feature = "aes-gcm", not(feature = "wireguard")))]
{
Arc::new(aes_gcm::AesGcmCipher::new_128(key_128))
}
#[cfg(all(not(feature = "wireguard"), not(feature = "aes-gcm")))]
{
compile_error!(
"wireguard or aes-gcm feature must be enabled for default encryption"
);
cfg_if! {
if #[cfg(feature = "openssl-crypto")] {
Arc::new(openssl::OpenSslCipher::new_aes128_gcm(key_128))
} else if #[cfg(feature = "wireguard")] {
Arc::new(ring::RingCipher::new_aes128_gcm(key_128))
} else if #[cfg(feature = "aes-gcm")] {
Arc::new(aes_gcm::AesGcmCipher::new_128(key_128))
} else {
compile_error!("unreachable!");
}
}
}
#[cfg(any(feature = "aes-gcm", feature = "wireguard", feature = "openssl-crypto"))]
EncryptionAlgorithm::Aes256Gcm => {
#[cfg(feature = "wireguard")]
{
Arc::new(ring_aes_gcm::AesGcmCipher::new_256(key_256))
}
#[cfg(all(feature = "aes-gcm", not(feature = "wireguard")))]
{
Arc::new(aes_gcm::AesGcmCipher::new_256(key_256))
cfg_if! {
if #[cfg(feature = "openssl-crypto")] {
Arc::new(openssl::OpenSslCipher::new_aes256_gcm(key_256))
} else if #[cfg(feature = "wireguard")] {
Arc::new(ring::RingCipher::new_aes256_gcm(key_256))
} else if #[cfg(feature = "aes-gcm")] {
Arc::new(aes_gcm::AesGcmCipher::new_256(key_256))
} else {
compile_error!("unreachable!");
}
}
}
EncryptionAlgorithm::Xor => Arc::new(xor_cipher::XorCipher::new(&key_128)),
#[cfg(feature = "wireguard")]
EncryptionAlgorithm::ChaCha20 => Arc::new(ring_chacha20::RingChaCha20Cipher::new(key_256)),
#[cfg(feature = "openssl-crypto")]
EncryptionAlgorithm::OpensslAesGcm => {
Arc::new(openssl_cipher::OpenSslCipher::new_aes128_gcm(key_128))
}
#[cfg(feature = "openssl-crypto")]
EncryptionAlgorithm::OpensslAes256Gcm => {
Arc::new(openssl_cipher::OpenSslCipher::new_aes256_gcm(key_256))
}
#[cfg(feature = "openssl-crypto")]
EncryptionAlgorithm::OpensslChacha20 => {
Arc::new(openssl_cipher::OpenSslCipher::new_chacha20(key_256))
#[cfg(any(feature = "wireguard", feature = "openssl-crypto"))]
EncryptionAlgorithm::ChaCha20 => {
cfg_if! {
if #[cfg(feature = "openssl-crypto")] {
Arc::new(openssl::OpenSslCipher::new_chacha20(key_256))
} else if #[cfg(feature = "wireguard")] {
Arc::new(ring::RingCipher::new_chacha20(key_256))
} else {
compile_error!("unreachable!");
}
}
}
}
}
+201
View File
@@ -0,0 +1,201 @@
use crate::tunnel::packet_def::{StandardAeadTail, ZCPacket};
use openssl::symm::{Cipher, Crypter, Mode};
use rand::RngCore;
use zerocopy::{AsBytes, FromBytes, FromZeroes};
use crate::peers::encrypt::{Encryptor, Error};
#[derive(Clone)]
pub struct OpenSslCipher {
pub(crate) cipher: OpenSslEnum,
}
#[derive(Clone, Copy)]
pub enum OpenSslEnum {
Aes128Gcm([u8; 16]),
Aes256Gcm([u8; 32]),
ChaCha20([u8; 32]),
}
impl OpenSslCipher {
pub fn new_aes128_gcm(key: [u8; 16]) -> Self {
Self {
cipher: OpenSslEnum::Aes128Gcm(key),
}
}
pub fn new_aes256_gcm(key: [u8; 32]) -> Self {
Self {
cipher: OpenSslEnum::Aes256Gcm(key),
}
}
pub fn new_chacha20(key: [u8; 32]) -> Self {
Self {
cipher: OpenSslEnum::ChaCha20(key),
}
}
fn get_cipher_and_key(&self) -> (Cipher, &[u8]) {
match &self.cipher {
OpenSslEnum::Aes128Gcm(key) => (Cipher::aes_128_gcm(), key.as_slice()),
OpenSslEnum::Aes256Gcm(key) => (Cipher::aes_256_gcm(), key.as_slice()),
OpenSslEnum::ChaCha20(key) => (Cipher::chacha20_poly1305(), key.as_slice()),
}
}
}
impl Encryptor for OpenSslCipher {
fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
let pm_header = zc_packet.peer_manager_header().unwrap();
if !pm_header.is_encrypted() {
return Ok(());
}
let payload = zc_packet.payload();
let len = payload.len();
if len < StandardAeadTail::SIZE {
return Err(Error::PacketTooShort(len));
}
let (cipher, key) = self.get_cipher_and_key();
// 提取 nonce/IV 和 tag
let tail = StandardAeadTail::ref_from_suffix(payload).unwrap();
let mut decrypter = Crypter::new(cipher, Mode::Decrypt, key, Some(&tail.nonce))
.map_err(|_| Error::DecryptionFailed)?;
decrypter
.set_tag(&tail.tag)
.map_err(|_| Error::DecryptionFailed)?;
let text_len = len - StandardAeadTail::SIZE;
let mut output = vec![0u8; text_len + cipher.block_size()];
let mut count = decrypter
.update(&payload[..text_len], &mut output)
.map_err(|_| Error::DecryptionFailed)?;
count += decrypter
.finalize(&mut output[count..])
.map_err(|_| Error::DecryptionFailed)?;
// 更新数据包
zc_packet.mut_payload()[..count].copy_from_slice(&output[..count]);
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
pm_header.set_encrypted(false);
let len = zc_packet.buf_len() - (len - count);
zc_packet.mut_inner().truncate(len);
Ok(())
}
fn encrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
self.encrypt_with_nonce(zc_packet, None)
}
fn encrypt_with_nonce(
&self,
zc_packet: &mut ZCPacket,
nonce: Option<&[u8]>,
) -> Result<(), Error> {
let pm_header = zc_packet.peer_manager_header().unwrap();
if pm_header.is_encrypted() {
tracing::warn!(?zc_packet, "packet is already encrypted");
return Ok(());
}
let (cipher, key) = self.get_cipher_and_key();
let mut tail = StandardAeadTail::new_zeroed();
if let Some(nonce) = nonce {
if nonce.len() != StandardAeadTail::NONCE_SIZE {
return Err(Error::EncryptionFailed);
}
tail.nonce.copy_from_slice(nonce);
} else {
rand::thread_rng().fill_bytes(&mut tail.nonce);
}
let mut encrypter = Crypter::new(cipher, Mode::Encrypt, key, Some(&tail.nonce))
.map_err(|_| Error::EncryptionFailed)?;
let payload_len = zc_packet.payload().len();
let mut output = vec![0u8; payload_len + cipher.block_size()];
let mut count = encrypter
.update(zc_packet.payload(), &mut output)
.map_err(|_| Error::EncryptionFailed)?;
count += encrypter
.finalize(&mut output[count..])
.map_err(|_| Error::EncryptionFailed)?;
// 更新数据包内容
zc_packet.mut_payload()[..count].copy_from_slice(&output[..count]);
encrypter
.get_tag(&mut tail.tag)
.map_err(|_| Error::EncryptionFailed)?;
// 添加 nonce/IV & tag 的结构
zc_packet.mut_inner().extend_from_slice(tail.as_bytes());
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
pm_header.set_encrypted(true);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn run_cipher_test_with_nonce(cipher: OpenSslCipher) {
let text = b"Hello, World! This is a standardized test message.";
let nonce: [u8; 12] = [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112];
let mut packet = ZCPacket::new_with_payload(text);
packet.fill_peer_manager_hdr(0, 0, 0);
cipher
.encrypt_with_nonce(&mut packet, Some(&nonce))
.unwrap();
let payload = packet.payload();
let len = payload.len();
assert!(len > text.len() + StandardAeadTail::SIZE - 1);
assert!(packet.peer_manager_header().unwrap().is_encrypted());
let tail = StandardAeadTail::ref_from_suffix(payload).unwrap().clone();
assert_eq!(tail.nonce, nonce);
cipher.decrypt(&mut packet).unwrap();
assert_eq!(packet.payload(), text);
assert!(!packet.peer_manager_header().unwrap().is_encrypted());
}
#[test]
fn test_openssl_aes128_gcm() {
let key = [1u8; 16];
let cipher = OpenSslCipher::new_aes128_gcm(key);
run_cipher_test_with_nonce(cipher);
}
#[test]
fn test_openssl_aes256_gcm() {
let key = [2u8; 32];
let cipher = OpenSslCipher::new_aes256_gcm(key);
run_cipher_test_with_nonce(cipher);
}
#[test]
fn test_openssl_chacha20() {
let key = [3u8; 32];
let cipher = OpenSslCipher::new_chacha20(key);
run_cipher_test_with_nonce(cipher);
}
}
@@ -1,288 +0,0 @@
use openssl::symm::{Cipher, Crypter, Mode};
use rand::RngCore;
use zerocopy::{AsBytes, FromBytes, FromZeroes};
use crate::tunnel::packet_def::ZCPacket;
use crate::peers::encrypt::{Encryptor, Error};
// OpenSSL 加密尾部结构
#[repr(C, packed)]
#[derive(AsBytes, FromBytes, FromZeroes, Clone, Debug, Default)]
pub struct OpenSslTail {
pub nonce: [u8; 16], // 使用 16 字节的 nonce/IV
}
pub const OPENSSL_ENCRYPTION_RESERVED: usize = std::mem::size_of::<OpenSslTail>();
#[derive(Clone)]
pub struct OpenSslCipher {
pub(crate) cipher: OpenSslEnum,
}
pub enum OpenSslEnum {
Aes128Gcm([u8; 16]),
Aes256Gcm([u8; 32]),
Chacha20([u8; 32]),
}
impl Clone for OpenSslEnum {
fn clone(&self) -> Self {
match &self {
OpenSslEnum::Aes128Gcm(key) => OpenSslEnum::Aes128Gcm(*key),
OpenSslEnum::Aes256Gcm(key) => OpenSslEnum::Aes256Gcm(*key),
OpenSslEnum::Chacha20(key) => OpenSslEnum::Chacha20(*key),
}
}
}
impl OpenSslCipher {
pub fn new_aes128_gcm(key: [u8; 16]) -> Self {
Self {
cipher: OpenSslEnum::Aes128Gcm(key),
}
}
pub fn new_aes256_gcm(key: [u8; 32]) -> Self {
Self {
cipher: OpenSslEnum::Aes256Gcm(key),
}
}
pub fn new_chacha20(key: [u8; 32]) -> Self {
Self {
cipher: OpenSslEnum::Chacha20(key),
}
}
fn get_cipher_and_key(&self) -> (Cipher, &[u8]) {
match &self.cipher {
OpenSslEnum::Aes128Gcm(key) => (Cipher::aes_128_gcm(), key.as_slice()),
OpenSslEnum::Aes256Gcm(key) => (Cipher::aes_256_gcm(), key.as_slice()),
OpenSslEnum::Chacha20(key) => (Cipher::chacha20_poly1305(), key.as_slice()),
}
}
fn is_aead_cipher(&self) -> bool {
matches!(
self.cipher,
OpenSslEnum::Aes128Gcm(_) | OpenSslEnum::Aes256Gcm(_) | OpenSslEnum::Chacha20(_)
)
}
fn get_nonce_size(&self) -> usize {
match &self.cipher {
OpenSslEnum::Aes128Gcm(_) | OpenSslEnum::Aes256Gcm(_) | OpenSslEnum::Chacha20(_) => 12, // GCM and ChaCha20-Poly1305 use 12-byte nonce
}
}
}
impl Encryptor for OpenSslCipher {
fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
let pm_header = zc_packet.peer_manager_header().unwrap();
if !pm_header.is_encrypted() {
return Ok(());
}
let payload_len = zc_packet.payload().len();
if payload_len < OPENSSL_ENCRYPTION_RESERVED {
return Err(Error::PacketTooShort(zc_packet.payload().len()));
}
let (cipher, key) = self.get_cipher_and_key();
let is_aead = self.is_aead_cipher();
let nonce_size = self.get_nonce_size();
// 提取 nonce/IV
let openssl_tail = OpenSslTail::ref_from_suffix(zc_packet.payload())
.unwrap()
.clone();
let text_len = if is_aead {
payload_len - OPENSSL_ENCRYPTION_RESERVED - 16 // AEAD 需要减去 tag 长度
} else {
payload_len - OPENSSL_ENCRYPTION_RESERVED
};
let mut decrypter = Crypter::new(
cipher,
Mode::Decrypt,
key,
Some(&openssl_tail.nonce[..nonce_size]),
)
.map_err(|_| Error::DecryptionFailed)?;
if is_aead {
// 对于 AEAD 模式,需要设置 tag
let tag_start = text_len;
let tag = &zc_packet.payload()[tag_start..tag_start + 16];
decrypter
.set_tag(tag)
.map_err(|_| Error::DecryptionFailed)?;
}
let mut output = vec![0u8; text_len + cipher.block_size()];
let mut count = decrypter
.update(&zc_packet.payload()[..text_len], &mut output)
.map_err(|_| Error::DecryptionFailed)?;
count += decrypter
.finalize(&mut output[count..])
.map_err(|_| Error::DecryptionFailed)?;
// 更新数据包
zc_packet.mut_payload()[..count].copy_from_slice(&output[..count]);
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
pm_header.set_encrypted(false);
let old_len = zc_packet.buf_len();
let new_len = old_len - (payload_len - count);
zc_packet.mut_inner().truncate(new_len);
Ok(())
}
fn encrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
self.encrypt_with_nonce(zc_packet, None)
}
fn encrypt_with_nonce(
&self,
zc_packet: &mut ZCPacket,
nonce: Option<&[u8]>,
) -> Result<(), Error> {
let pm_header = zc_packet.peer_manager_header().unwrap();
if pm_header.is_encrypted() {
tracing::warn!(?zc_packet, "packet is already encrypted");
return Ok(());
}
let (cipher, key) = self.get_cipher_and_key();
let is_aead = self.is_aead_cipher();
let nonce_size = self.get_nonce_size();
let mut tail = OpenSslTail::default();
if let Some(nonce) = nonce {
if nonce.len() != nonce_size {
return Err(Error::EncryptionFailed);
}
tail.nonce[..nonce_size].copy_from_slice(nonce);
} else {
rand::thread_rng().fill_bytes(&mut tail.nonce[..nonce_size]);
}
let mut encrypter =
Crypter::new(cipher, Mode::Encrypt, key, Some(&tail.nonce[..nonce_size]))
.map_err(|_| Error::EncryptionFailed)?;
let payload_len = zc_packet.payload().len();
let mut output = vec![0u8; payload_len + cipher.block_size()];
let mut count = encrypter
.update(zc_packet.payload(), &mut output)
.map_err(|_| Error::EncryptionFailed)?;
count += encrypter
.finalize(&mut output[count..])
.map_err(|_| Error::EncryptionFailed)?;
// 更新数据包内容
zc_packet.mut_payload()[..count].copy_from_slice(&output[..count]);
// 对于 AEAD 模式,添加 tag
if is_aead {
let mut tag = vec![0u8; 16]; // GCM 标签通常是 16 字节
encrypter
.get_tag(&mut tag)
.map_err(|_| Error::EncryptionFailed)?;
zc_packet.mut_inner().extend_from_slice(&tag);
}
// 添加 nonce/IV
zc_packet.mut_inner().extend_from_slice(tail.as_bytes());
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
pm_header.set_encrypted(true);
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::{
peers::encrypt::{openssl_cipher::OpenSslCipher, Encryptor},
tunnel::packet_def::ZCPacket,
};
use zerocopy::FromBytes;
use super::OPENSSL_ENCRYPTION_RESERVED;
#[test]
fn test_openssl_aes128_gcm() {
let key = [0u8; 16];
let cipher = OpenSslCipher::new_aes128_gcm(key);
let text = b"Hello, World! This is a test message for OpenSSL AES-128-GCM.";
let mut packet = ZCPacket::new_with_payload(text);
packet.fill_peer_manager_hdr(0, 0, 0);
// 加密
cipher.encrypt(&mut packet).unwrap();
assert!(packet.payload().len() > text.len() + OPENSSL_ENCRYPTION_RESERVED);
assert!(packet.peer_manager_header().unwrap().is_encrypted());
// 解密
cipher.decrypt(&mut packet).unwrap();
assert_eq!(packet.payload(), text);
assert!(!packet.peer_manager_header().unwrap().is_encrypted());
}
#[test]
fn test_openssl_aes128_gcm_with_nonce() {
let key = [7u8; 16];
let cipher = OpenSslCipher::new_aes128_gcm(key);
let text = b"Hello";
let nonce = [3u8; 12];
let mut packet1 = ZCPacket::new_with_payload(text);
packet1.fill_peer_manager_hdr(0, 0, 0);
cipher
.encrypt_with_nonce(&mut packet1, Some(&nonce))
.unwrap();
let mut packet2 = ZCPacket::new_with_payload(text);
packet2.fill_peer_manager_hdr(0, 0, 0);
cipher
.encrypt_with_nonce(&mut packet2, Some(&nonce))
.unwrap();
assert_eq!(packet1.payload(), packet2.payload());
assert!(packet1.payload().len() > text.len() + OPENSSL_ENCRYPTION_RESERVED);
let tail = super::OpenSslTail::ref_from_suffix(packet1.payload())
.unwrap()
.clone();
assert_eq!(&tail.nonce[..nonce.len()], nonce);
cipher.decrypt(&mut packet1).unwrap();
assert_eq!(packet1.payload(), text);
}
#[test]
fn test_openssl_chacha20() {
let key = [0u8; 32];
let cipher = OpenSslCipher::new_chacha20(key);
let text = b"Hello, World! This is a test message for OpenSSL ChaCha20.";
let mut packet = ZCPacket::new_with_payload(text);
packet.fill_peer_manager_hdr(0, 0, 0);
// 加密
cipher.encrypt(&mut packet).unwrap();
assert!(packet.payload().len() > text.len());
assert!(packet.peer_manager_header().unwrap().is_encrypted());
// 解密
cipher.decrypt(&mut packet).unwrap();
assert_eq!(packet.payload(), text);
assert!(!packet.peer_manager_header().unwrap().is_encrypted());
}
}
+252
View File
@@ -0,0 +1,252 @@
use rand::RngCore;
use ring::aead::{self};
use ring::aead::{LessSafeKey, UnboundKey};
use zerocopy::{AsBytes, FromBytes, FromZeroes};
use crate::tunnel::packet_def::{StandardAeadTail, ZCPacket};
use super::{Encryptor, Error};
#[derive(Clone)]
pub struct RingCipher {
pub(crate) cipher: RingEnum,
}
pub enum RingEnum {
Aes128Gcm(LessSafeKey, [u8; 16]),
Aes256Gcm(LessSafeKey, [u8; 32]),
ChaCha20(LessSafeKey, [u8; 32]),
}
impl RingEnum {
fn get_cipher(&self) -> &LessSafeKey {
match &self {
RingEnum::Aes128Gcm(cipher, _) => cipher,
RingEnum::Aes256Gcm(cipher, _) => cipher,
RingEnum::ChaCha20(cipher, _) => cipher,
}
}
}
impl Clone for RingEnum {
fn clone(&self) -> Self {
match &self {
RingEnum::Aes128Gcm(_, key) => {
let c =
LessSafeKey::new(UnboundKey::new(&aead::AES_128_GCM, key.as_slice()).unwrap());
RingEnum::Aes128Gcm(c, *key)
}
RingEnum::Aes256Gcm(_, key) => {
let c =
LessSafeKey::new(UnboundKey::new(&aead::AES_256_GCM, key.as_slice()).unwrap());
RingEnum::Aes256Gcm(c, *key)
}
RingEnum::ChaCha20(_, key) => {
let c = LessSafeKey::new(
UnboundKey::new(&aead::CHACHA20_POLY1305, key.as_slice()).unwrap(),
);
RingEnum::ChaCha20(c, *key)
}
}
}
}
impl RingCipher {
pub fn new_aes128_gcm(key: [u8; 16]) -> Self {
let cipher = LessSafeKey::new(UnboundKey::new(&aead::AES_128_GCM, &key).unwrap());
Self {
cipher: RingEnum::Aes128Gcm(cipher, key),
}
}
pub fn new_aes256_gcm(key: [u8; 32]) -> Self {
let cipher = LessSafeKey::new(UnboundKey::new(&aead::AES_256_GCM, &key).unwrap());
Self {
cipher: RingEnum::Aes256Gcm(cipher, key),
}
}
pub fn new_chacha20(key: [u8; 32]) -> Self {
let unbound_key = UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
let cipher = LessSafeKey::new(unbound_key);
Self {
cipher: RingEnum::ChaCha20(cipher, key),
}
}
}
impl Encryptor for RingCipher {
fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
let pm_header = zc_packet.peer_manager_header().unwrap();
if !pm_header.is_encrypted() {
return Ok(());
}
let payload_len = zc_packet.payload().len();
if payload_len < StandardAeadTail::SIZE {
return Err(Error::PacketTooShort(zc_packet.payload().len()));
}
let text_and_tag_len = payload_len - StandardAeadTail::SIZE + StandardAeadTail::TAG_SIZE;
let aes_tail = StandardAeadTail::ref_from_suffix(zc_packet.payload()).unwrap();
let nonce = aead::Nonce::assume_unique_for_key(aes_tail.nonce);
self.cipher
.get_cipher()
.open_in_place(
nonce,
aead::Aad::empty(),
&mut zc_packet.mut_payload()[..text_and_tag_len],
)
.map_err(|_| Error::DecryptionFailed)?;
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
pm_header.set_encrypted(false);
let old_len = zc_packet.buf_len();
zc_packet
.mut_inner()
.truncate(old_len - StandardAeadTail::SIZE);
Ok(())
}
fn encrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
self.encrypt_with_nonce(zc_packet, None)
}
fn encrypt_with_nonce(
&self,
zc_packet: &mut ZCPacket,
nonce: Option<&[u8]>,
) -> Result<(), Error> {
let pm_header = zc_packet.peer_manager_header().unwrap();
if pm_header.is_encrypted() {
tracing::warn!(?zc_packet, "packet is already encrypted");
return Ok(());
}
let mut tail = StandardAeadTail::new_zeroed();
match nonce {
Some(n) => tail.nonce = n.try_into().map_err(|_| Error::EncryptionFailed)?,
None => rand::thread_rng().fill_bytes(&mut tail.nonce),
}
let nonce = aead::Nonce::assume_unique_for_key(tail.nonce);
let tag = self
.cipher
.get_cipher()
.seal_in_place_separate_tag(nonce, aead::Aad::empty(), zc_packet.mut_payload())
.map_err(|_| Error::EncryptionFailed)?;
let tag = tag.as_ref();
if tag.len() != StandardAeadTail::TAG_SIZE {
return Err(Error::InvalidTag(tag.to_vec()));
}
tail.tag.copy_from_slice(tag);
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
pm_header.set_encrypted(true);
zc_packet.mut_inner().extend_from_slice(tail.as_bytes());
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::{
peers::encrypt::{ring::RingCipher, Encryptor},
tunnel::packet_def::{StandardAeadTail, ZCPacket},
};
use zerocopy::FromBytes;
#[test]
fn test_aes_gcm_cipher() {
let key = [0u8; 16];
let cipher = RingCipher::new_aes128_gcm(key);
let text = b"1234567";
let mut packet = ZCPacket::new_with_payload(text);
packet.fill_peer_manager_hdr(0, 0, 0);
cipher.encrypt(&mut packet).unwrap();
assert_eq!(packet.payload().len(), text.len() + StandardAeadTail::SIZE);
assert!(packet.peer_manager_header().unwrap().is_encrypted());
cipher.decrypt(&mut packet).unwrap();
assert_eq!(packet.payload(), text);
assert!(!packet.peer_manager_header().unwrap().is_encrypted());
}
#[test]
fn test_aes_gcm_cipher_with_nonce() {
let key = [7u8; 16];
let cipher = RingCipher::new_aes128_gcm(key);
let text = b"Hello";
let nonce = [3u8; 12];
let mut packet1 = ZCPacket::new_with_payload(text);
packet1.fill_peer_manager_hdr(0, 0, 0);
cipher
.encrypt_with_nonce(&mut packet1, Some(&nonce))
.unwrap();
let mut packet2 = ZCPacket::new_with_payload(text);
packet2.fill_peer_manager_hdr(0, 0, 0);
cipher
.encrypt_with_nonce(&mut packet2, Some(&nonce))
.unwrap();
assert_eq!(packet1.payload(), packet2.payload());
let tail = StandardAeadTail::ref_from_suffix(packet1.payload()).unwrap();
assert_eq!(tail.nonce, nonce);
cipher.decrypt(&mut packet1).unwrap();
assert_eq!(packet1.payload(), text);
}
#[test]
fn test_ring_chacha20_cipher() {
let key = [0u8; 32];
let cipher = RingCipher::new_chacha20(key);
let text = b"Hello, World! This is a test message for Ring ChaCha20-Poly1305.";
let mut packet = ZCPacket::new_with_payload(text);
packet.fill_peer_manager_hdr(0, 0, 0);
cipher.encrypt(&mut packet).unwrap();
assert_eq!(packet.payload().len(), text.len() + StandardAeadTail::SIZE);
assert!(packet.peer_manager_header().unwrap().is_encrypted());
cipher.decrypt(&mut packet).unwrap();
assert_eq!(packet.payload(), text);
assert!(!packet.peer_manager_header().unwrap().is_encrypted());
}
#[test]
fn test_ring_chacha20_cipher_with_nonce() {
let key = [9u8; 32];
let cipher = RingCipher::new_chacha20(key);
let text = b"Hello";
let nonce = [5u8; 12];
let mut packet1 = ZCPacket::new_with_payload(text);
packet1.fill_peer_manager_hdr(0, 0, 0);
cipher
.encrypt_with_nonce(&mut packet1, Some(&nonce))
.unwrap();
let mut packet2 = ZCPacket::new_with_payload(text);
packet2.fill_peer_manager_hdr(0, 0, 0);
cipher
.encrypt_with_nonce(&mut packet2, Some(&nonce))
.unwrap();
assert_eq!(packet1.payload(), packet2.payload());
let tail = StandardAeadTail::ref_from_suffix(packet1.payload()).unwrap();
assert_eq!(tail.nonce, nonce);
cipher.decrypt(&mut packet1).unwrap();
assert_eq!(packet1.payload(), text);
assert!(!packet1.peer_manager_header().unwrap().is_encrypted());
}
}
-205
View File
@@ -1,205 +0,0 @@
use rand::RngCore;
use ring::aead::{self};
use ring::aead::{LessSafeKey, UnboundKey};
use zerocopy::{AsBytes, FromBytes};
use crate::tunnel::packet_def::{AesGcmTail, ZCPacket, AES_GCM_ENCRYPTION_RESERVED};
use super::{Encryptor, Error};
#[derive(Clone)]
pub struct AesGcmCipher {
pub(crate) cipher: AesGcmEnum,
}
pub enum AesGcmEnum {
AesGCM128(LessSafeKey, [u8; 16]),
AesGCM256(LessSafeKey, [u8; 32]),
}
impl Clone for AesGcmEnum {
fn clone(&self) -> Self {
match &self {
AesGcmEnum::AesGCM128(_, key) => {
let c =
LessSafeKey::new(UnboundKey::new(&aead::AES_128_GCM, key.as_slice()).unwrap());
AesGcmEnum::AesGCM128(c, *key)
}
AesGcmEnum::AesGCM256(_, key) => {
let c =
LessSafeKey::new(UnboundKey::new(&aead::AES_256_GCM, key.as_slice()).unwrap());
AesGcmEnum::AesGCM256(c, *key)
}
}
}
}
impl AesGcmCipher {
pub fn new_128(key: [u8; 16]) -> Self {
let cipher = LessSafeKey::new(UnboundKey::new(&aead::AES_128_GCM, &key).unwrap());
Self {
cipher: AesGcmEnum::AesGCM128(cipher, key),
}
}
pub fn new_256(key: [u8; 32]) -> Self {
let cipher = LessSafeKey::new(UnboundKey::new(&aead::AES_256_GCM, &key).unwrap());
Self {
cipher: AesGcmEnum::AesGCM256(cipher, key),
}
}
}
impl Encryptor for AesGcmCipher {
fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
let pm_header = zc_packet.peer_manager_header().unwrap();
if !pm_header.is_encrypted() {
return Ok(());
}
let payload_len = zc_packet.payload().len();
if payload_len < AES_GCM_ENCRYPTION_RESERVED {
return Err(Error::PacketTooShort(zc_packet.payload().len()));
}
let text_and_tag_len = payload_len - AES_GCM_ENCRYPTION_RESERVED + 16;
let aes_tail = AesGcmTail::ref_from_suffix(zc_packet.payload()).unwrap();
let nonce = aead::Nonce::assume_unique_for_key(aes_tail.nonce);
let rs = match &self.cipher {
AesGcmEnum::AesGCM128(cipher, _) => cipher.open_in_place(
nonce,
aead::Aad::empty(),
&mut zc_packet.mut_payload()[..text_and_tag_len],
),
AesGcmEnum::AesGCM256(cipher, _) => cipher.open_in_place(
nonce,
aead::Aad::empty(),
&mut zc_packet.mut_payload()[..text_and_tag_len],
),
};
if rs.is_err() {
return Err(Error::DecryptionFailed);
}
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
pm_header.set_encrypted(false);
let old_len = zc_packet.buf_len();
zc_packet
.mut_inner()
.truncate(old_len - AES_GCM_ENCRYPTION_RESERVED);
Ok(())
}
fn encrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
self.encrypt_with_nonce(zc_packet, None)
}
fn encrypt_with_nonce(
&self,
zc_packet: &mut ZCPacket,
nonce: Option<&[u8]>,
) -> Result<(), Error> {
let pm_header = zc_packet.peer_manager_header().unwrap();
if pm_header.is_encrypted() {
tracing::warn!(?zc_packet, "packet is already encrypted");
return Ok(());
}
let mut tail = AesGcmTail::default();
if let Some(nonce) = nonce {
if nonce.len() != tail.nonce.len() {
return Err(Error::EncryptionFailed);
}
tail.nonce.copy_from_slice(nonce);
} else {
rand::thread_rng().fill_bytes(&mut tail.nonce);
}
let nonce = aead::Nonce::assume_unique_for_key(tail.nonce);
let rs = match &self.cipher {
AesGcmEnum::AesGCM128(cipher, _) => cipher.seal_in_place_separate_tag(
nonce,
aead::Aad::empty(),
zc_packet.mut_payload(),
),
AesGcmEnum::AesGCM256(cipher, _) => cipher.seal_in_place_separate_tag(
nonce,
aead::Aad::empty(),
zc_packet.mut_payload(),
),
};
match rs {
Ok(tag) => {
let tag = tag.as_ref();
if tag.len() != 16 {
return Err(Error::InvalidTag(tag.to_vec()));
}
tail.tag.copy_from_slice(tag);
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
pm_header.set_encrypted(true);
zc_packet.mut_inner().extend_from_slice(tail.as_bytes());
Ok(())
}
Err(_) => Err(Error::EncryptionFailed),
}
}
}
#[cfg(test)]
mod tests {
use crate::{
peers::encrypt::{ring_aes_gcm::AesGcmCipher, Encryptor},
tunnel::packet_def::{AesGcmTail, ZCPacket, AES_GCM_ENCRYPTION_RESERVED},
};
use zerocopy::FromBytes;
#[test]
fn test_aes_gcm_cipher() {
let key = [0u8; 16];
let cipher = AesGcmCipher::new_128(key);
let text = b"1234567";
let mut packet = ZCPacket::new_with_payload(text);
packet.fill_peer_manager_hdr(0, 0, 0);
cipher.encrypt(&mut packet).unwrap();
assert_eq!(
packet.payload().len(),
text.len() + AES_GCM_ENCRYPTION_RESERVED
);
assert!(packet.peer_manager_header().unwrap().is_encrypted());
cipher.decrypt(&mut packet).unwrap();
assert_eq!(packet.payload(), text);
assert!(!packet.peer_manager_header().unwrap().is_encrypted());
}
#[test]
fn test_aes_gcm_cipher_with_nonce() {
let key = [7u8; 16];
let cipher = AesGcmCipher::new_128(key);
let text = b"Hello";
let nonce = [3u8; 12];
let mut packet1 = ZCPacket::new_with_payload(text);
packet1.fill_peer_manager_hdr(0, 0, 0);
cipher
.encrypt_with_nonce(&mut packet1, Some(&nonce))
.unwrap();
let mut packet2 = ZCPacket::new_with_payload(text);
packet2.fill_peer_manager_hdr(0, 0, 0);
cipher
.encrypt_with_nonce(&mut packet2, Some(&nonce))
.unwrap();
assert_eq!(packet1.payload(), packet2.payload());
let tail = AesGcmTail::ref_from_suffix(packet1.payload()).unwrap();
assert_eq!(tail.nonce, nonce);
cipher.decrypt(&mut packet1).unwrap();
assert_eq!(packet1.payload(), text);
}
}
-170
View File
@@ -1,170 +0,0 @@
use rand::RngCore;
use ring::aead::{self, Aad, LessSafeKey, Nonce, UnboundKey};
use zerocopy::{AsBytes, FromBytes, FromZeroes};
use super::{Encryptor, Error};
use crate::tunnel::packet_def::ZCPacket;
#[repr(C, packed)]
#[derive(AsBytes, FromBytes, FromZeroes, Clone, Debug, Default)]
pub struct ChaCha20Poly1305Tail {
pub tag: [u8; 16],
pub nonce: [u8; 12],
}
pub const CHACHA20_POLY1305_ENCRYPTION_RESERVED: usize =
std::mem::size_of::<ChaCha20Poly1305Tail>();
#[derive(Clone)]
pub struct RingChaCha20Cipher {
cipher: LessSafeKey,
key: [u8; 32],
}
impl RingChaCha20Cipher {
pub fn new(key: [u8; 32]) -> Self {
let unbound_key = UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
let cipher = LessSafeKey::new(unbound_key);
Self { cipher, key }
}
}
impl Encryptor for RingChaCha20Cipher {
fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
let pm_header = zc_packet.peer_manager_header().unwrap();
if !pm_header.is_encrypted() {
return Ok(());
}
let payload_len = zc_packet.payload().len();
if payload_len < CHACHA20_POLY1305_ENCRYPTION_RESERVED {
return Err(Error::PacketTooShort(zc_packet.payload().len()));
}
let text_and_tag_len = payload_len - CHACHA20_POLY1305_ENCRYPTION_RESERVED + 16;
let chacha20_tail = ChaCha20Poly1305Tail::ref_from_suffix(zc_packet.payload()).unwrap();
let nonce = Nonce::assume_unique_for_key(chacha20_tail.nonce);
let rs = self.cipher.open_in_place(
nonce,
Aad::empty(),
&mut zc_packet.mut_payload()[..text_and_tag_len],
);
if rs.is_err() {
return Err(Error::DecryptionFailed);
}
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
pm_header.set_encrypted(false);
let old_len = zc_packet.buf_len();
zc_packet
.mut_inner()
.truncate(old_len - CHACHA20_POLY1305_ENCRYPTION_RESERVED);
Ok(())
}
fn encrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
self.encrypt_with_nonce(zc_packet, None)
}
fn encrypt_with_nonce(
&self,
zc_packet: &mut ZCPacket,
nonce: Option<&[u8]>,
) -> Result<(), Error> {
let pm_header = zc_packet.peer_manager_header().unwrap();
if pm_header.is_encrypted() {
tracing::warn!(?zc_packet, "packet is already encrypted");
return Ok(());
}
let mut tail = ChaCha20Poly1305Tail::default();
if let Some(nonce) = nonce {
if nonce.len() != tail.nonce.len() {
return Err(Error::EncryptionFailed);
}
tail.nonce.copy_from_slice(nonce);
} else {
rand::thread_rng().fill_bytes(&mut tail.nonce);
}
let nonce = Nonce::assume_unique_for_key(tail.nonce);
let rs =
self.cipher
.seal_in_place_separate_tag(nonce, Aad::empty(), zc_packet.mut_payload());
match rs {
Ok(tag) => {
tail.tag.copy_from_slice(tag.as_ref());
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
pm_header.set_encrypted(true);
zc_packet.mut_inner().extend_from_slice(tail.as_bytes());
Ok(())
}
Err(_) => Err(Error::EncryptionFailed),
}
}
}
#[cfg(test)]
mod tests {
use crate::{
peers::encrypt::{ring_chacha20::RingChaCha20Cipher, Encryptor},
tunnel::packet_def::ZCPacket,
};
use zerocopy::FromBytes;
use super::CHACHA20_POLY1305_ENCRYPTION_RESERVED;
#[test]
fn test_ring_chacha20_cipher() {
let key = [0u8; 32];
let cipher = RingChaCha20Cipher::new(key);
let text = b"Hello, World! This is a test message for Ring ChaCha20-Poly1305.";
let mut packet = ZCPacket::new_with_payload(text);
packet.fill_peer_manager_hdr(0, 0, 0);
cipher.encrypt(&mut packet).unwrap();
assert_eq!(
packet.payload().len(),
text.len() + CHACHA20_POLY1305_ENCRYPTION_RESERVED
);
assert!(packet.peer_manager_header().unwrap().is_encrypted());
cipher.decrypt(&mut packet).unwrap();
assert_eq!(packet.payload(), text);
assert!(!packet.peer_manager_header().unwrap().is_encrypted());
}
#[test]
fn test_ring_chacha20_cipher_with_nonce() {
let key = [9u8; 32];
let cipher = RingChaCha20Cipher::new(key);
let text = b"Hello";
let nonce = [5u8; 12];
let mut packet1 = ZCPacket::new_with_payload(text);
packet1.fill_peer_manager_hdr(0, 0, 0);
cipher
.encrypt_with_nonce(&mut packet1, Some(&nonce))
.unwrap();
let mut packet2 = ZCPacket::new_with_payload(text);
packet2.fill_peer_manager_hdr(0, 0, 0);
cipher
.encrypt_with_nonce(&mut packet2, Some(&nonce))
.unwrap();
assert_eq!(packet1.payload(), packet2.payload());
let tail = super::ChaCha20Poly1305Tail::ref_from_suffix(packet1.payload()).unwrap();
assert_eq!(tail.nonce, nonce);
cipher.decrypt(&mut packet1).unwrap();
assert_eq!(packet1.payload(), text);
assert!(!packet1.peer_manager_header().unwrap().is_encrypted());
}
}
@@ -61,7 +61,7 @@ impl Encryptor for XorCipher {
#[cfg(test)]
mod tests {
use crate::{
peers::encrypt::{xor_cipher::XorCipher, Encryptor},
peers::encrypt::{xor::XorCipher, Encryptor},
tunnel::packet_def::ZCPacket,
};
+8 -14
View File
@@ -8,17 +8,17 @@ use std::{
use atomic_shim::AtomicU64;
use crate::{
common::PeerId,
peers::encrypt::{create_encryptor, Encryptor},
tunnel::packet_def::{StandardAeadTail, ZCPacket},
};
use anyhow::anyhow;
use dashmap::DashMap;
use hmac::{Hmac, Mac as _};
use rand::RngCore as _;
use sha2::Sha256;
use crate::{
common::PeerId,
peers::encrypt::{create_encryptor, Encryptor},
tunnel::packet_def::{AesGcmTail, ZCPacket},
};
use zerocopy::FromBytes;
type HmacSha256 = Hmac<Sha256>;
pub struct UpsertResponderSessionReturn {
@@ -733,14 +733,8 @@ impl PeerSession {
}
fn parse_tail(payload: &[u8]) -> Option<[u8; 12]> {
if payload.len() < std::mem::size_of::<AesGcmTail>() {
return None;
}
let tail_off = payload.len() - std::mem::size_of::<AesGcmTail>();
let tail = &payload[tail_off..];
let mut nonce = [0u8; 12];
nonce.copy_from_slice(&tail[16..]);
Some(nonce)
let tail = StandardAeadTail::ref_from_suffix(payload)?;
Some(tail.nonce)
}
fn evict_old_rx_slots(rx: &mut [[EpochRxSlot; 2]; 2], now_ms: u64) {
+15 -7
View File
@@ -277,14 +277,22 @@ impl ForeignNetworkPacketHeader {
}
}
// reserve the space for aes tag and nonce
// reserve space for AEAD authentication tag and nonce
#[repr(C, packed)]
#[derive(AsBytes, FromBytes, FromZeroes, Clone, Debug, Default)]
pub struct AesGcmTail {
pub tag: [u8; 16],
pub nonce: [u8; 12],
#[derive(AsBytes, FromBytes, FromZeroes, Clone, Debug)]
pub struct AeadTail<const TAG_SIZE: usize, const NONCE_SIZE: usize> {
pub tag: [u8; TAG_SIZE],
pub nonce: [u8; NONCE_SIZE],
}
pub const AES_GCM_ENCRYPTION_RESERVED: usize = std::mem::size_of::<AesGcmTail>();
impl<const TAG_SIZE: usize, const NONCE_SIZE: usize> AeadTail<TAG_SIZE, NONCE_SIZE> {
pub const TAG_SIZE: usize = TAG_SIZE;
pub const NONCE_SIZE: usize = NONCE_SIZE;
pub const SIZE: usize = std::mem::size_of::<Self>();
}
pub type StandardAeadTail = AeadTail<16, 12>;
#[derive(AsBytes, FromZeroes, Clone, Debug, Copy, PartialEq, Hash, Eq)]
#[repr(u8)]
@@ -315,7 +323,7 @@ impl CompressorTail {
}
}
pub const TAIL_RESERVED_SIZE: usize = max(AES_GCM_ENCRYPTION_RESERVED, COMPRESSOR_TAIL_SIZE);
pub const TAIL_RESERVED_SIZE: usize = max(StandardAeadTail::SIZE, COMPRESSOR_TAIL_SIZE);
#[derive(Default, Debug)]
pub struct ZCPacketOffsets {