mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-07 02:09:06 +00:00
refactor: use strum on EncryptionAlgorithm, use Xor as default when AesGcm not available (#1923)
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user