mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-06 17:59:11 +00:00
refactor: use strum on EncryptionAlgorithm, use Xor as default when AesGcm not available (#1923)
This commit is contained in:
Generated
+23
-1
@@ -2257,6 +2257,7 @@ dependencies = [
|
|||||||
"smoltcp",
|
"smoltcp",
|
||||||
"snow",
|
"snow",
|
||||||
"socket2 0.5.10",
|
"socket2 0.5.10",
|
||||||
|
"strum 0.27.2",
|
||||||
"stun_codec",
|
"stun_codec",
|
||||||
"sys-locale",
|
"sys-locale",
|
||||||
"tabled",
|
"tabled",
|
||||||
@@ -7644,7 +7645,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"strum",
|
"strum 0.26.3",
|
||||||
"thiserror 1.0.63",
|
"thiserror 1.0.63",
|
||||||
"time",
|
"time",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -8679,6 +8680,27 @@ version = "0.26.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
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]]
|
[[package]]
|
||||||
name = "stun_codec"
|
name = "stun_codec"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ time = "0.3"
|
|||||||
toml = "0.8.12"
|
toml = "0.8.12"
|
||||||
chrono = { version = "0.4.37", features = ["serde"] }
|
chrono = { version = "0.4.37", features = ["serde"] }
|
||||||
|
|
||||||
|
strum = { version = "0.27.2", features = ["derive"] }
|
||||||
|
|
||||||
gethostname = "0.5.0"
|
gethostname = "0.5.0"
|
||||||
|
|
||||||
futures = { version = "0.3", features = ["bilock", "unstable"] }
|
futures = { version = "0.3", features = ["bilock", "unstable"] }
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ use std::{
|
|||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use base64::{prelude::BASE64_STANDARD, Engine as _};
|
use base64::{prelude::BASE64_STANDARD, Engine as _};
|
||||||
|
use cfg_if::cfg_if;
|
||||||
|
use clap::builder::PossibleValue;
|
||||||
|
use clap::ValueEnum;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use strum::{Display, EnumString, VariantArray};
|
||||||
use tokio::io::AsyncReadExt as _;
|
use tokio::io::AsyncReadExt as _;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -59,7 +63,7 @@ pub fn gen_default_flags() -> Flags {
|
|||||||
enable_relay_foreign_network_quic: false,
|
enable_relay_foreign_network_quic: false,
|
||||||
foreign_relay_bps_limit: u64::MAX,
|
foreign_relay_bps_limit: u64::MAX,
|
||||||
multi_thread_count: 2,
|
multi_thread_count: 2,
|
||||||
encryption_algorithm: "aes-gcm".to_string(),
|
encryption_algorithm: EncryptionAlgorithm::default().to_string(),
|
||||||
disable_sym_hole_punching: false,
|
disable_sym_hole_punching: false,
|
||||||
tld_dns_zone: DEFAULT_ET_DNS_ZONE.to_string(),
|
tld_dns_zone: DEFAULT_ET_DNS_ZONE.to_string(),
|
||||||
|
|
||||||
@@ -68,73 +72,51 @@ pub fn gen_default_flags() -> Flags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Display, EnumString, VariantArray)]
|
||||||
|
#[strum(ascii_case_insensitive)]
|
||||||
pub enum EncryptionAlgorithm {
|
pub enum EncryptionAlgorithm {
|
||||||
AesGcm,
|
#[strum(serialize = "xor")]
|
||||||
Aes256Gcm,
|
|
||||||
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,
|
ChaCha20,
|
||||||
|
|
||||||
#[cfg(feature = "openssl-crypto")]
|
|
||||||
OpensslAesGcm,
|
|
||||||
#[cfg(feature = "openssl-crypto")]
|
|
||||||
OpensslChacha20,
|
|
||||||
#[cfg(feature = "openssl-crypto")]
|
|
||||||
OpensslAes256Gcm,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for EncryptionAlgorithm {
|
impl ValueEnum for EncryptionAlgorithm {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn value_variants<'a>() -> &'a [Self] {
|
||||||
match self {
|
Self::VARIANTS
|
||||||
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"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for EncryptionAlgorithm {
|
#[allow(clippy::derivable_impls)]
|
||||||
type Error = anyhow::Error;
|
impl Default for EncryptionAlgorithm {
|
||||||
|
fn default() -> Self {
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
cfg_if! {
|
||||||
match value {
|
if #[cfg(any(feature = "aes-gcm", feature = "wireguard", feature = "openssl-crypto"))] {
|
||||||
"aes-gcm" => Ok(Self::AesGcm),
|
EncryptionAlgorithm::AesGcm
|
||||||
"aes-256-gcm" => Ok(Self::Aes256Gcm),
|
} else {
|
||||||
"xor" => Ok(Self::Xor),
|
crate::common::log::warn!("no AEAD encryption algorithm is available, using INSECURE XOR");
|
||||||
#[cfg(feature = "wireguard")]
|
EncryptionAlgorithm::Xor
|
||||||
"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, &)]
|
#[auto_impl::auto_impl(Box, &)]
|
||||||
|
|||||||
@@ -10,10 +10,9 @@ use std::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
common::{
|
common::{
|
||||||
config::{
|
config::{
|
||||||
get_avaliable_encrypt_methods, load_config_from_file, process_secure_mode_cfg,
|
load_config_from_file, process_secure_mode_cfg, ConfigFileControl, ConfigLoader,
|
||||||
ConfigFileControl, ConfigLoader, ConsoleLoggerConfig, FileLoggerConfig,
|
ConsoleLoggerConfig, EncryptionAlgorithm, FileLoggerConfig, LoggingConfigLoader,
|
||||||
LoggingConfigLoader, NetworkIdentity, PeerConfig, PortForwardConfig, TomlConfigLoader,
|
NetworkIdentity, PeerConfig, PortForwardConfig, TomlConfigLoader, VpnPortalConfig,
|
||||||
VpnPortalConfig,
|
|
||||||
},
|
},
|
||||||
constants::EASYTIER_VERSION,
|
constants::EASYTIER_VERSION,
|
||||||
log,
|
log,
|
||||||
@@ -277,9 +276,9 @@ struct NetworkOptions {
|
|||||||
long,
|
long,
|
||||||
env = "ET_ENCRYPTION_ALGORITHM",
|
env = "ET_ENCRYPTION_ALGORITHM",
|
||||||
help = t!("core_clap.encryption_algorithm").to_string(),
|
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(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
@@ -1007,7 +1006,7 @@ impl NetworkOptions {
|
|||||||
f.enable_encryption = !v;
|
f.enable_encryption = !v;
|
||||||
}
|
}
|
||||||
if let Some(algorithm) = &self.encryption_algorithm {
|
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 {
|
if let Some(v) = self.disable_ipv6 {
|
||||||
f.enable_ipv6 = !v;
|
f.enable_ipv6 = !v;
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
use aes_gcm::aead::consts::{U12, U16};
|
use aes_gcm::{AeadCore, AeadInPlace, Aes128Gcm, Aes256Gcm, Key, KeyInit};
|
||||||
use aes_gcm::aead::generic_array::GenericArray;
|
|
||||||
use aes_gcm::{AeadCore, AeadInPlace, Aes128Gcm, Aes256Gcm, Key, KeyInit, Nonce, Tag};
|
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use zerocopy::{AsBytes, FromBytes};
|
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};
|
use super::{Encryptor, Error};
|
||||||
|
|
||||||
@@ -42,27 +40,28 @@ impl Encryptor for AesGcmCipher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let payload_len = zc_packet.payload().len();
|
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()));
|
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()
|
.unwrap()
|
||||||
.clone();
|
.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 {
|
let rs = match &self.cipher {
|
||||||
AesGcmEnum::AES128GCM(aes_gcm) => aes_gcm.decrypt_in_place_detached(
|
AesGcmEnum::AES128GCM(aes_gcm) => aes_gcm.decrypt_in_place_detached(
|
||||||
nonce,
|
&nonce,
|
||||||
&[],
|
&[],
|
||||||
&mut zc_packet.mut_payload()[..text_len],
|
&mut zc_packet.mut_payload()[..text_len],
|
||||||
&tag,
|
&tag,
|
||||||
),
|
),
|
||||||
AesGcmEnum::AES256GCM(aes_gcm) => aes_gcm.decrypt_in_place_detached(
|
AesGcmEnum::AES256GCM(aes_gcm) => aes_gcm.decrypt_in_place_detached(
|
||||||
nonce,
|
&nonce,
|
||||||
&[],
|
&[],
|
||||||
&mut zc_packet.mut_payload()[..text_len],
|
&mut zc_packet.mut_payload()[..text_len],
|
||||||
&tag,
|
&tag,
|
||||||
@@ -79,7 +78,7 @@ impl Encryptor for AesGcmCipher {
|
|||||||
let old_len = zc_packet.buf_len();
|
let old_len = zc_packet.buf_len();
|
||||||
zc_packet
|
zc_packet
|
||||||
.mut_inner()
|
.mut_inner()
|
||||||
.truncate(old_len - AES_GCM_ENCRYPTION_RESERVED);
|
.truncate(old_len - StandardAeadTail::SIZE);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,51 +97,48 @@ impl Encryptor for AesGcmCipher {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut tail = AesGcmTail::default();
|
let nonce = nonce
|
||||||
if let Some(nonce) = nonce {
|
.map(|n| {
|
||||||
if nonce.len() != tail.nonce.len() {
|
<[u8; StandardAeadTail::NONCE_SIZE]>::try_from(n)
|
||||||
return Err(Error::EncryptionFailed);
|
.map(Into::into)
|
||||||
}
|
.map_err(|_| Error::EncryptionFailed)
|
||||||
tail.nonce.copy_from_slice(nonce);
|
})
|
||||||
}
|
.transpose()?;
|
||||||
let rs = match &self.cipher {
|
|
||||||
|
let (tag, nonce) = match &self.cipher {
|
||||||
AesGcmEnum::AES128GCM(aes_gcm) => {
|
AesGcmEnum::AES128GCM(aes_gcm) => {
|
||||||
if nonce.is_none() {
|
let nonce = nonce.unwrap_or_else(|| Aes128Gcm::generate_nonce(&mut OsRng));
|
||||||
let nonce = Aes128Gcm::generate_nonce(&mut OsRng);
|
(
|
||||||
tail.nonce.copy_from_slice(nonce.as_slice());
|
aes_gcm.encrypt_in_place_detached(&nonce, &[], zc_packet.mut_payload()),
|
||||||
}
|
nonce,
|
||||||
let nonce = Nonce::from_slice(&tail.nonce);
|
)
|
||||||
aes_gcm.encrypt_in_place_detached(nonce, &[], zc_packet.mut_payload())
|
|
||||||
}
|
}
|
||||||
AesGcmEnum::AES256GCM(aes_gcm) => {
|
AesGcmEnum::AES256GCM(aes_gcm) => {
|
||||||
if nonce.is_none() {
|
let nonce = nonce.unwrap_or_else(|| Aes256Gcm::generate_nonce(&mut OsRng));
|
||||||
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
|
(
|
||||||
tail.nonce.copy_from_slice(nonce.as_slice());
|
aes_gcm.encrypt_in_place_detached(&nonce, &[], zc_packet.mut_payload()),
|
||||||
}
|
nonce,
|
||||||
let nonce = Nonce::from_slice(&tail.nonce);
|
)
|
||||||
aes_gcm.encrypt_in_place_detached(nonce, &[], zc_packet.mut_payload())
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match rs {
|
let tail = StandardAeadTail {
|
||||||
Ok(tag) => {
|
tag: tag.map_err(|_| Error::EncryptionFailed)?.into(),
|
||||||
tail.tag.copy_from_slice(tag.as_slice());
|
nonce: nonce.into(),
|
||||||
|
};
|
||||||
|
|
||||||
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
|
let pm_header = zc_packet.mut_peer_manager_header().unwrap();
|
||||||
pm_header.set_encrypted(true);
|
pm_header.set_encrypted(true);
|
||||||
zc_packet.mut_inner().extend_from_slice(tail.as_bytes());
|
zc_packet.mut_inner().extend_from_slice(tail.as_bytes());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(_) => Err(Error::EncryptionFailed),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
peers::encrypt::{aes_gcm::AesGcmCipher, Encryptor},
|
peers::encrypt::{aes_gcm::AesGcmCipher, Encryptor},
|
||||||
tunnel::packet_def::{AesGcmTail, ZCPacket, AES_GCM_ENCRYPTION_RESERVED},
|
tunnel::packet_def::{StandardAeadTail, ZCPacket},
|
||||||
};
|
};
|
||||||
use zerocopy::FromBytes;
|
use zerocopy::FromBytes;
|
||||||
|
|
||||||
@@ -154,10 +150,7 @@ mod tests {
|
|||||||
let mut packet = ZCPacket::new_with_payload(text);
|
let mut packet = ZCPacket::new_with_payload(text);
|
||||||
packet.fill_peer_manager_hdr(0, 0, 0);
|
packet.fill_peer_manager_hdr(0, 0, 0);
|
||||||
cipher.encrypt(&mut packet).unwrap();
|
cipher.encrypt(&mut packet).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(packet.payload().len(), text.len() + StandardAeadTail::SIZE);
|
||||||
packet.payload().len(),
|
|
||||||
text.len() + AES_GCM_ENCRYPTION_RESERVED
|
|
||||||
);
|
|
||||||
assert!(packet.peer_manager_header().unwrap().is_encrypted());
|
assert!(packet.peer_manager_header().unwrap().is_encrypted());
|
||||||
|
|
||||||
cipher.decrypt(&mut packet).unwrap();
|
cipher.decrypt(&mut packet).unwrap();
|
||||||
@@ -186,7 +179,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(packet1.payload(), packet2.payload());
|
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);
|
assert_eq!(tail.nonce, nonce);
|
||||||
|
|
||||||
cipher.decrypt(&mut packet1).unwrap();
|
cipher.decrypt(&mut packet1).unwrap();
|
||||||
|
|||||||
@@ -1,23 +1,20 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{config::EncryptionAlgorithm, log},
|
common::{config::EncryptionAlgorithm, log},
|
||||||
tunnel::packet_def::ZCPacket,
|
tunnel::packet_def::ZCPacket,
|
||||||
};
|
};
|
||||||
|
use cfg_if::cfg_if;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[cfg(feature = "wireguard")]
|
#[cfg(feature = "wireguard")]
|
||||||
pub mod ring_aes_gcm;
|
pub mod ring;
|
||||||
|
|
||||||
#[cfg(feature = "wireguard")]
|
|
||||||
pub mod ring_chacha20;
|
|
||||||
|
|
||||||
#[cfg(feature = "aes-gcm")]
|
#[cfg(feature = "aes-gcm")]
|
||||||
pub mod aes_gcm;
|
pub mod aes_gcm;
|
||||||
|
|
||||||
#[cfg(feature = "openssl-crypto")]
|
#[cfg(feature = "openssl-crypto")]
|
||||||
pub mod openssl_cipher;
|
pub mod openssl;
|
||||||
|
|
||||||
pub mod xor_cipher;
|
pub mod xor;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -32,6 +29,7 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Encryptor: Send + Sync + 'static {
|
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(&self, zc_packet: &mut ZCPacket) -> Result<(), Error>;
|
||||||
fn encrypt_with_nonce(
|
fn encrypt_with_nonce(
|
||||||
&self,
|
&self,
|
||||||
@@ -40,16 +38,11 @@ pub trait Encryptor: Send + Sync + 'static {
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.encrypt(zc_packet)
|
self.encrypt(zc_packet)
|
||||||
}
|
}
|
||||||
fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NullCipher;
|
pub struct NullCipher;
|
||||||
|
|
||||||
impl Encryptor for NullCipher {
|
impl Encryptor for NullCipher {
|
||||||
fn encrypt(&self, _zc_packet: &mut ZCPacket) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
|
fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> {
|
||||||
let pm_header = zc_packet.peer_manager_header().unwrap();
|
let pm_header = zc_packet.peer_manager_header().unwrap();
|
||||||
if pm_header.is_encrypted() {
|
if pm_header.is_encrypted() {
|
||||||
@@ -58,6 +51,10 @@ impl Encryptor for NullCipher {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn encrypt(&self, _zc_packet: &mut ZCPacket) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an encryptor based on the algorithm name
|
/// Create an encryptor based on the algorithm name
|
||||||
@@ -69,60 +66,59 @@ pub fn create_encryptor(
|
|||||||
let algorithm = match EncryptionAlgorithm::try_from(algorithm) {
|
let algorithm = match EncryptionAlgorithm::try_from(algorithm) {
|
||||||
Ok(algorithm) => algorithm,
|
Ok(algorithm) => algorithm,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
let default = EncryptionAlgorithm::default();
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"Unknown encryption algorithm: {}, falling back to default AES-GCM",
|
"Unknown encryption algorithm: {}, falling back to default {}",
|
||||||
algorithm
|
algorithm,
|
||||||
|
default
|
||||||
);
|
);
|
||||||
EncryptionAlgorithm::AesGcm
|
default
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match algorithm {
|
match algorithm {
|
||||||
|
EncryptionAlgorithm::Xor => Arc::new(xor::XorCipher::new(&key_128)),
|
||||||
|
|
||||||
|
#[cfg(any(feature = "aes-gcm", feature = "wireguard", feature = "openssl-crypto"))]
|
||||||
EncryptionAlgorithm::AesGcm => {
|
EncryptionAlgorithm::AesGcm => {
|
||||||
#[cfg(feature = "wireguard")]
|
cfg_if! {
|
||||||
{
|
if #[cfg(feature = "openssl-crypto")] {
|
||||||
Arc::new(ring_aes_gcm::AesGcmCipher::new_128(key_128))
|
Arc::new(openssl::OpenSslCipher::new_aes128_gcm(key_128))
|
||||||
}
|
} else if #[cfg(feature = "wireguard")] {
|
||||||
#[cfg(all(feature = "aes-gcm", not(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))
|
Arc::new(aes_gcm::AesGcmCipher::new_128(key_128))
|
||||||
|
} else {
|
||||||
|
compile_error!("unreachable!");
|
||||||
}
|
}
|
||||||
#[cfg(all(not(feature = "wireguard"), not(feature = "aes-gcm")))]
|
|
||||||
{
|
|
||||||
compile_error!(
|
|
||||||
"wireguard or aes-gcm feature must be enabled for default encryption"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "aes-gcm", feature = "wireguard", feature = "openssl-crypto"))]
|
||||||
EncryptionAlgorithm::Aes256Gcm => {
|
EncryptionAlgorithm::Aes256Gcm => {
|
||||||
#[cfg(feature = "wireguard")]
|
cfg_if! {
|
||||||
{
|
if #[cfg(feature = "openssl-crypto")] {
|
||||||
Arc::new(ring_aes_gcm::AesGcmCipher::new_256(key_256))
|
Arc::new(openssl::OpenSslCipher::new_aes256_gcm(key_256))
|
||||||
}
|
} else if #[cfg(feature = "wireguard")] {
|
||||||
#[cfg(all(feature = "aes-gcm", not(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))
|
Arc::new(aes_gcm::AesGcmCipher::new_256(key_256))
|
||||||
|
} else {
|
||||||
|
compile_error!("unreachable!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptionAlgorithm::Xor => Arc::new(xor_cipher::XorCipher::new(&key_128)),
|
#[cfg(any(feature = "wireguard", feature = "openssl-crypto"))]
|
||||||
|
EncryptionAlgorithm::ChaCha20 => {
|
||||||
#[cfg(feature = "wireguard")]
|
cfg_if! {
|
||||||
EncryptionAlgorithm::ChaCha20 => Arc::new(ring_chacha20::RingChaCha20Cipher::new(key_256)),
|
if #[cfg(feature = "openssl-crypto")] {
|
||||||
|
Arc::new(openssl::OpenSslCipher::new_chacha20(key_256))
|
||||||
#[cfg(feature = "openssl-crypto")]
|
} else if #[cfg(feature = "wireguard")] {
|
||||||
EncryptionAlgorithm::OpensslAesGcm => {
|
Arc::new(ring::RingCipher::new_chacha20(key_256))
|
||||||
Arc::new(openssl_cipher::OpenSslCipher::new_aes128_gcm(key_128))
|
} else {
|
||||||
}
|
compile_error!("unreachable!");
|
||||||
|
}
|
||||||
#[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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
peers::encrypt::{xor_cipher::XorCipher, Encryptor},
|
peers::encrypt::{xor::XorCipher, Encryptor},
|
||||||
tunnel::packet_def::ZCPacket,
|
tunnel::packet_def::ZCPacket,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -8,17 +8,17 @@ use std::{
|
|||||||
|
|
||||||
use atomic_shim::AtomicU64;
|
use atomic_shim::AtomicU64;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
common::PeerId,
|
||||||
|
peers::encrypt::{create_encryptor, Encryptor},
|
||||||
|
tunnel::packet_def::{StandardAeadTail, ZCPacket},
|
||||||
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use hmac::{Hmac, Mac as _};
|
use hmac::{Hmac, Mac as _};
|
||||||
use rand::RngCore as _;
|
use rand::RngCore as _;
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
|
use zerocopy::FromBytes;
|
||||||
use crate::{
|
|
||||||
common::PeerId,
|
|
||||||
peers::encrypt::{create_encryptor, Encryptor},
|
|
||||||
tunnel::packet_def::{AesGcmTail, ZCPacket},
|
|
||||||
};
|
|
||||||
|
|
||||||
type HmacSha256 = Hmac<Sha256>;
|
type HmacSha256 = Hmac<Sha256>;
|
||||||
pub struct UpsertResponderSessionReturn {
|
pub struct UpsertResponderSessionReturn {
|
||||||
@@ -733,14 +733,8 @@ impl PeerSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_tail(payload: &[u8]) -> Option<[u8; 12]> {
|
fn parse_tail(payload: &[u8]) -> Option<[u8; 12]> {
|
||||||
if payload.len() < std::mem::size_of::<AesGcmTail>() {
|
let tail = StandardAeadTail::ref_from_suffix(payload)?;
|
||||||
return None;
|
Some(tail.nonce)
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evict_old_rx_slots(rx: &mut [[EpochRxSlot; 2]; 2], now_ms: u64) {
|
fn evict_old_rx_slots(rx: &mut [[EpochRxSlot; 2]; 2], now_ms: u64) {
|
||||||
|
|||||||
@@ -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)]
|
#[repr(C, packed)]
|
||||||
#[derive(AsBytes, FromBytes, FromZeroes, Clone, Debug, Default)]
|
#[derive(AsBytes, FromBytes, FromZeroes, Clone, Debug)]
|
||||||
pub struct AesGcmTail {
|
pub struct AeadTail<const TAG_SIZE: usize, const NONCE_SIZE: usize> {
|
||||||
pub tag: [u8; 16],
|
pub tag: [u8; TAG_SIZE],
|
||||||
pub nonce: [u8; 12],
|
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)]
|
#[derive(AsBytes, FromZeroes, Clone, Debug, Copy, PartialEq, Hash, Eq)]
|
||||||
#[repr(u8)]
|
#[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)]
|
#[derive(Default, Debug)]
|
||||||
pub struct ZCPacketOffsets {
|
pub struct ZCPacketOffsets {
|
||||||
|
|||||||
Reference in New Issue
Block a user