mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-04-22 16:17:23 +08: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",
|
||||
"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"
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
peers::encrypt::{xor_cipher::XorCipher, Encryptor},
|
||||
peers::encrypt::{xor::XorCipher, Encryptor},
|
||||
tunnel::packet_def::ZCPacket,
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user