mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-02 09:09:17 +00:00
Introduce secure mode (part 1) (#1808)
Use noise protocol on handshake. Check peer's public key if needed. Also support rekey and replay attack prevention. E2EE and temporary password will be implemented based on this.
This commit is contained in:
@@ -84,6 +84,14 @@ impl Encryptor for AesGcmCipher {
|
||||
}
|
||||
|
||||
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");
|
||||
@@ -91,16 +99,28 @@ impl Encryptor for AesGcmCipher {
|
||||
}
|
||||
|
||||
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 {
|
||||
AesGcmEnum::AES128GCM(aes_gcm) => {
|
||||
let nonce = Aes128Gcm::generate_nonce(&mut OsRng);
|
||||
tail.nonce.copy_from_slice(nonce.as_slice());
|
||||
aes_gcm.encrypt_in_place_detached(&nonce, &[], zc_packet.mut_payload())
|
||||
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())
|
||||
}
|
||||
AesGcmEnum::AES256GCM(aes_gcm) => {
|
||||
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
|
||||
tail.nonce.copy_from_slice(nonce.as_slice());
|
||||
aes_gcm.encrypt_in_place_detached(&nonce, &[], zc_packet.mut_payload())
|
||||
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())
|
||||
}
|
||||
};
|
||||
|
||||
@@ -122,8 +142,9 @@ impl Encryptor for AesGcmCipher {
|
||||
mod tests {
|
||||
use crate::{
|
||||
peers::encrypt::{aes_gcm::AesGcmCipher, Encryptor},
|
||||
tunnel::packet_def::{ZCPacket, AES_GCM_ENCRYPTION_RESERVED},
|
||||
tunnel::packet_def::{AesGcmTail, ZCPacket, AES_GCM_ENCRYPTION_RESERVED},
|
||||
};
|
||||
use zerocopy::FromBytes;
|
||||
|
||||
#[test]
|
||||
fn test_aes_gcm_cipher() {
|
||||
@@ -143,4 +164,32 @@ mod tests {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,13 @@ pub enum Error {
|
||||
|
||||
pub trait Encryptor: Send + Sync + 'static {
|
||||
fn encrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error>;
|
||||
fn encrypt_with_nonce(
|
||||
&self,
|
||||
zc_packet: &mut ZCPacket,
|
||||
_nonce: Option<&[u8]>,
|
||||
) -> Result<(), Error> {
|
||||
self.encrypt(zc_packet)
|
||||
}
|
||||
fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
|
||||
@@ -142,6 +142,14 @@ impl Encryptor for OpenSslCipher {
|
||||
}
|
||||
|
||||
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");
|
||||
@@ -153,7 +161,14 @@ impl Encryptor for OpenSslCipher {
|
||||
let nonce_size = self.get_nonce_size();
|
||||
|
||||
let mut tail = OpenSslTail::default();
|
||||
rand::thread_rng().fill_bytes(&mut tail.nonce[..nonce_size]);
|
||||
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]))
|
||||
@@ -198,6 +213,7 @@ mod tests {
|
||||
peers::encrypt::{openssl_cipher::OpenSslCipher, Encryptor},
|
||||
tunnel::packet_def::ZCPacket,
|
||||
};
|
||||
use zerocopy::FromBytes;
|
||||
|
||||
use super::OPENSSL_ENCRYPTION_RESERVED;
|
||||
|
||||
@@ -220,6 +236,37 @@ mod tests {
|
||||
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];
|
||||
|
||||
@@ -93,6 +93,14 @@ impl Encryptor for AesGcmCipher {
|
||||
}
|
||||
|
||||
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");
|
||||
@@ -100,7 +108,14 @@ impl Encryptor for AesGcmCipher {
|
||||
}
|
||||
|
||||
let mut tail = AesGcmTail::default();
|
||||
rand::thread_rng().fill_bytes(&mut tail.nonce);
|
||||
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 {
|
||||
@@ -137,8 +152,9 @@ impl Encryptor for AesGcmCipher {
|
||||
mod tests {
|
||||
use crate::{
|
||||
peers::encrypt::{ring_aes_gcm::AesGcmCipher, Encryptor},
|
||||
tunnel::packet_def::{ZCPacket, AES_GCM_ENCRYPTION_RESERVED},
|
||||
tunnel::packet_def::{AesGcmTail, ZCPacket, AES_GCM_ENCRYPTION_RESERVED},
|
||||
};
|
||||
use zerocopy::FromBytes;
|
||||
|
||||
#[test]
|
||||
fn test_aes_gcm_cipher() {
|
||||
@@ -158,4 +174,32 @@ mod tests {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,14 @@ impl Encryptor for RingChaCha20Cipher {
|
||||
}
|
||||
|
||||
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");
|
||||
@@ -74,7 +82,14 @@ impl Encryptor for RingChaCha20Cipher {
|
||||
}
|
||||
|
||||
let mut tail = ChaCha20Poly1305Tail::default();
|
||||
rand::thread_rng().fill_bytes(&mut tail.nonce);
|
||||
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 =
|
||||
@@ -100,6 +115,7 @@ mod tests {
|
||||
peers::encrypt::{ring_chacha20::RingChaCha20Cipher, Encryptor},
|
||||
tunnel::packet_def::ZCPacket,
|
||||
};
|
||||
use zerocopy::FromBytes;
|
||||
|
||||
use super::CHACHA20_POLY1305_ENCRYPTION_RESERVED;
|
||||
|
||||
@@ -122,4 +138,33 @@ mod tests {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user