Added support for AES128

This commit is contained in:
Dennis Schwerdel 2020-06-09 09:03:52 +02:00
parent b6dafdaeee
commit dfad73065c
6 changed files with 80 additions and 19 deletions

View File

@ -2,6 +2,12 @@
This project follows [semantic versioning](http://semver.org). This project follows [semantic versioning](http://semver.org).
### UNRELEASED
- [added] Added crypto option AES128
### v1.4.0 (2020-06-03) ### v1.4.0 (2020-06-03)
- [added] Added option to listen on specified IP - [added] Added option to listen on specified IP

7
breaking_changes.md Normal file
View File

@ -0,0 +1,7 @@
# Planned breaking changes
Due to semantic versioning, any breaking change after 1.0 requires a new major version number.
This is a list of breaking changes to do in such a case:
- Change default crypto to AES128
- Remove network-id parameter
- Remove port config option

View File

@ -50,6 +50,19 @@ fn crypto_aes256(b: &mut Bencher) {
b.bytes = 1400; b.bytes = 1400;
} }
#[bench]
fn crypto_aes128(b: &mut Bencher) {
let mut crypto = Crypto::from_shared_key(CryptoMethod::AES128, "test");
let mut payload = [0; 1500];
let header = [0; 8];
let mut nonce_bytes = [0; 12];
b.iter(|| {
let len = crypto.encrypt(&mut payload, 1400, &mut nonce_bytes, &header);
assert!(crypto.decrypt(&mut payload[..len], &nonce_bytes, &header).is_ok());
});
b.bytes = 1400;
}
#[bench] #[bench]
fn message_encode(b: &mut Bencher) { fn message_encode(b: &mut Bencher) {
let mut crypto = Crypto::None; let mut crypto = Crypto::None;

View File

@ -17,7 +17,9 @@ pub enum CryptoMethod {
#[serde(rename = "chacha20")] #[serde(rename = "chacha20")]
ChaCha20, ChaCha20,
#[serde(rename = "aes256")] #[serde(rename = "aes256")]
AES256 AES256,
#[serde(rename = "aes128")]
AES128
} }
impl FromStr for CryptoMethod { impl FromStr for CryptoMethod {
type Err = &'static str; type Err = &'static str;
@ -25,7 +27,8 @@ impl FromStr for CryptoMethod {
fn from_str(text: &str) -> Result<Self, Self::Err> { fn from_str(text: &str) -> Result<Self, Self::Err> {
Ok(match &text.to_lowercase() as &str { Ok(match &text.to_lowercase() as &str {
"chacha20" | "chacha" => Self::ChaCha20, "chacha20" | "chacha" => Self::ChaCha20,
"aes256" | "aes" => Self::AES256, "aes256" => Self::AES256,
"aes128" | "aes" => Self::AES128,
_ => return Err("Unknown method") _ => return Err("Unknown method")
}) })
} }
@ -41,7 +44,8 @@ pub struct CryptoData {
pub enum Crypto { pub enum Crypto {
None, None,
ChaCha20Poly1305(CryptoData), ChaCha20Poly1305(CryptoData),
AES256GCM(CryptoData) AES256GCM(CryptoData),
AES128GCM(CryptoData)
} }
fn inc_nonce(nonce: &mut [u8]) { fn inc_nonce(nonce: &mut [u8]) {
@ -63,7 +67,8 @@ impl Crypto {
match *self { match *self {
Crypto::None => 0, Crypto::None => 0,
Crypto::ChaCha20Poly1305 { .. } => 1, Crypto::ChaCha20Poly1305 { .. } => 1,
Crypto::AES256GCM { .. } => 2 Crypto::AES256GCM { .. } => 2,
Crypto::AES128GCM { .. } => 3
} }
} }
@ -71,7 +76,9 @@ impl Crypto {
pub fn nonce_bytes(&self) -> usize { pub fn nonce_bytes(&self) -> usize {
match *self { match *self {
Crypto::None => 0, Crypto::None => 0,
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => data.crypto_key.algorithm().nonce_len() Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) | Crypto::AES128GCM(ref data) => {
data.crypto_key.algorithm().nonce_len()
}
} }
} }
@ -79,7 +86,7 @@ impl Crypto {
pub fn get_key(&self) -> &[u8] { pub fn get_key(&self) -> &[u8] {
match *self { match *self {
Crypto::None => &[], Crypto::None => &[],
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => &data.key Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) | Crypto::AES128GCM(ref data) => &data.key
} }
} }
@ -88,14 +95,17 @@ impl Crypto {
pub fn additional_bytes(&self) -> usize { pub fn additional_bytes(&self) -> usize {
match *self { match *self {
Crypto::None => 0, Crypto::None => 0,
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => data.crypto_key.algorithm().tag_len() Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) | Crypto::AES128GCM(ref data) => {
data.crypto_key.algorithm().tag_len()
}
} }
} }
pub fn from_shared_key(method: CryptoMethod, password: &str) -> Self { pub fn from_shared_key(method: CryptoMethod, password: &str) -> Self {
let algo = match method { let algo = match method {
CryptoMethod::ChaCha20 => &CHACHA20_POLY1305, CryptoMethod::ChaCha20 => &CHACHA20_POLY1305,
CryptoMethod::AES256 => &AES_256_GCM CryptoMethod::AES256 => &AES_256_GCM,
CryptoMethod::AES128 => &AES_128_GCM
}; };
let mut key: Vec<u8> = Vec::with_capacity(algo.key_len()); let mut key: Vec<u8> = Vec::with_capacity(algo.key_len());
for _ in 0..algo.key_len() { for _ in 0..algo.key_len() {
@ -137,14 +147,15 @@ impl Crypto {
let data = CryptoData { crypto_key, nonce, key }; let data = CryptoData { crypto_key, nonce, key };
match method { match method {
CryptoMethod::ChaCha20 => Crypto::ChaCha20Poly1305(data), CryptoMethod::ChaCha20 => Crypto::ChaCha20Poly1305(data),
CryptoMethod::AES256 => Crypto::AES256GCM(data) CryptoMethod::AES256 => Crypto::AES256GCM(data),
CryptoMethod::AES128 => Crypto::AES128GCM(data)
} }
} }
pub fn decrypt(&self, buf: &mut [u8], nonce: &[u8], header: &[u8]) -> Result<usize, Error> { pub fn decrypt(&self, buf: &mut [u8], nonce: &[u8], header: &[u8]) -> Result<usize, Error> {
match *self { match *self {
Crypto::None => Ok(buf.len()), Crypto::None => Ok(buf.len()),
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => { Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) | Crypto::AES128GCM(ref data) => {
let nonce = Nonce::try_assume_unique_for_key(nonce).unwrap(); let nonce = Nonce::try_assume_unique_for_key(nonce).unwrap();
match data.crypto_key.open_in_place(nonce, Aad::from(header), buf) { match data.crypto_key.open_in_place(nonce, Aad::from(header), buf) {
Ok(plaintext) => Ok(plaintext.len()), Ok(plaintext) => Ok(plaintext.len()),
@ -158,7 +169,9 @@ impl Crypto {
let tag_len = self.additional_bytes(); let tag_len = self.additional_bytes();
match *self { match *self {
Crypto::None => mlen, Crypto::None => mlen,
Crypto::ChaCha20Poly1305(ref mut data) | Crypto::AES256GCM(ref mut data) => { Crypto::ChaCha20Poly1305(ref mut data)
| Crypto::AES256GCM(ref mut data)
| Crypto::AES128GCM(ref mut data) => {
inc_nonce(&mut data.nonce); inc_nonce(&mut data.nonce);
assert!(buf.len() - mlen >= tag_len); assert!(buf.len() - mlen >= tag_len);
let nonce = Nonce::try_assume_unique_for_key(&data.nonce).unwrap(); let nonce = Nonce::try_assume_unique_for_key(&data.nonce).unwrap();
@ -217,3 +230,25 @@ fn encrypt_decrypt_aes256() {
receiver.decrypt(&mut buffer[..size], &nonce2, &header).unwrap(); receiver.decrypt(&mut buffer[..size], &nonce2, &header).unwrap();
assert_eq!(msg_bytes, &buffer[..msg_bytes.len()] as &[u8]); assert_eq!(msg_bytes, &buffer[..msg_bytes.len()] as &[u8]);
} }
#[test]
fn encrypt_decrypt_aes128() {
let mut sender = Crypto::from_shared_key(CryptoMethod::AES128, "test");
let receiver = Crypto::from_shared_key(CryptoMethod::AES128, "test");
let msg = "HelloWorld0123456789";
let msg_bytes = msg.as_bytes();
let mut buffer = [0u8; 1024];
let header = [0u8; 8];
buffer[..msg_bytes.len()].clone_from_slice(&msg_bytes);
let mut nonce1 = [0u8; 12];
let size = sender.encrypt(&mut buffer, msg_bytes.len(), &mut nonce1, &header);
assert_eq!(size, msg_bytes.len() + sender.additional_bytes());
assert!(msg_bytes != &buffer[..msg_bytes.len()] as &[u8]);
receiver.decrypt(&mut buffer[..size], &nonce1, &header).unwrap();
assert_eq!(msg_bytes, &buffer[..msg_bytes.len()] as &[u8]);
let mut nonce2 = [0u8; 12];
let size = sender.encrypt(&mut buffer, msg_bytes.len(), &mut nonce2, &header);
assert!(nonce1 != nonce2);
receiver.decrypt(&mut buffer[..size], &nonce2, &header).unwrap();
assert_eq!(msg_bytes, &buffer[..msg_bytes.len()] as &[u8]);
}

View File

@ -83,7 +83,7 @@ pub struct Args {
#[structopt(short, long, aliases=&["shared-key", "secret-key", "secret"])] #[structopt(short, long, aliases=&["shared-key", "secret-key", "secret"])]
key: Option<String>, key: Option<String>,
/// The encryption method to use ("aes256", or "chacha20") /// The encryption method to use ("aes128", "aes256", or "chacha20")
#[structopt(long)] #[structopt(long)]
crypto: Option<CryptoMethod>, crypto: Option<CryptoMethod>,

View File

@ -59,9 +59,9 @@ vpncloud - Peer-to-peer VPN
the traffic will be sent unencrypted. the traffic will be sent unencrypted.
*--crypto <method>*:: *--crypto <method>*::
The encryption method to use ("aes256", or "chacha20"). Most current CPUs The encryption method to use ("aes128", "aes256", or "chacha20"). Most
have special support for AES256 so this should be faster. For older current CPUs have special support for AES256 so this should be faster. For
computers lacking this support, only CHACHA20 is supported. older computers lacking this support, CHACHA20 is the fastest option.
[default: *chacha20*] [default: *chacha20*]
*--magic <id>*:: *--magic <id>*::
@ -258,10 +258,10 @@ side effects.
. VpnCloud is not designed for high security use cases. Although the used crypto . VpnCloud is not designed for high security use cases. Although the used crypto
primitives are expected to be very secure, their application has not been primitives are expected to be very secure, their application has not been
reviewed. reviewed.
The shared key is hashed using _ScryptSalsa208Sha256_ to derive a key, The shared key is hashed using _PBKDF2_HMAC_SHA256_ to derive a key,
which is used to encrypt the payload of messages using _ChaCha20Poly1305_ or which is used to encrypt the payload of messages using _ChaCha20Poly1305_,
_AES256-GCM_. The encryption includes an authentication that also protects the _AES128-GCM_, or _AES256-GCM_. The encryption includes an authentication that
header. also protects the header.
This method does only protect against attacks on single messages but not This method does only protect against attacks on single messages but not
against attacks that manipulate the message series itself (i.e. suppress against attacks that manipulate the message series itself (i.e. suppress
messages, reorder them, or duplicate them). messages, reorder them, or duplicate them).