diff --git a/CHANGELOG.md b/CHANGELOG.md index 411d0d8..fdcc067 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ This project follows [semantic versioning](http://semver.org). + +### UNRELEASED + +- [added] Added crypto option AES128 + + ### v1.4.0 (2020-06-03) - [added] Added option to listen on specified IP diff --git a/breaking_changes.md b/breaking_changes.md new file mode 100644 index 0000000..1d168e2 --- /dev/null +++ b/breaking_changes.md @@ -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 \ No newline at end of file diff --git a/src/benches.rs b/src/benches.rs index 179ab2a..7d3c961 100644 --- a/src/benches.rs +++ b/src/benches.rs @@ -50,6 +50,19 @@ fn crypto_aes256(b: &mut Bencher) { 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] fn message_encode(b: &mut Bencher) { let mut crypto = Crypto::None; diff --git a/src/crypto.rs b/src/crypto.rs index b6cb6fb..65ea595 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -17,7 +17,9 @@ pub enum CryptoMethod { #[serde(rename = "chacha20")] ChaCha20, #[serde(rename = "aes256")] - AES256 + AES256, + #[serde(rename = "aes128")] + AES128 } impl FromStr for CryptoMethod { type Err = &'static str; @@ -25,7 +27,8 @@ impl FromStr for CryptoMethod { fn from_str(text: &str) -> Result { Ok(match &text.to_lowercase() as &str { "chacha20" | "chacha" => Self::ChaCha20, - "aes256" | "aes" => Self::AES256, + "aes256" => Self::AES256, + "aes128" | "aes" => Self::AES128, _ => return Err("Unknown method") }) } @@ -41,7 +44,8 @@ pub struct CryptoData { pub enum Crypto { None, ChaCha20Poly1305(CryptoData), - AES256GCM(CryptoData) + AES256GCM(CryptoData), + AES128GCM(CryptoData) } fn inc_nonce(nonce: &mut [u8]) { @@ -63,7 +67,8 @@ impl Crypto { match *self { Crypto::None => 0, Crypto::ChaCha20Poly1305 { .. } => 1, - Crypto::AES256GCM { .. } => 2 + Crypto::AES256GCM { .. } => 2, + Crypto::AES128GCM { .. } => 3 } } @@ -71,7 +76,9 @@ impl Crypto { pub fn nonce_bytes(&self) -> usize { match *self { 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] { match *self { 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 { match *self { 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 { let algo = match method { CryptoMethod::ChaCha20 => &CHACHA20_POLY1305, - CryptoMethod::AES256 => &AES_256_GCM + CryptoMethod::AES256 => &AES_256_GCM, + CryptoMethod::AES128 => &AES_128_GCM }; let mut key: Vec = Vec::with_capacity(algo.key_len()); for _ in 0..algo.key_len() { @@ -137,14 +147,15 @@ impl Crypto { let data = CryptoData { crypto_key, nonce, key }; match method { 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 { match *self { 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(); match data.crypto_key.open_in_place(nonce, Aad::from(header), buf) { Ok(plaintext) => Ok(plaintext.len()), @@ -158,7 +169,9 @@ impl Crypto { let tag_len = self.additional_bytes(); match *self { 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); assert!(buf.len() - mlen >= tag_len); 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(); 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]); +} diff --git a/src/main.rs b/src/main.rs index 022d638..4163170 100644 --- a/src/main.rs +++ b/src/main.rs @@ -83,7 +83,7 @@ pub struct Args { #[structopt(short, long, aliases=&["shared-key", "secret-key", "secret"])] key: Option, - /// The encryption method to use ("aes256", or "chacha20") + /// The encryption method to use ("aes128", "aes256", or "chacha20") #[structopt(long)] crypto: Option, diff --git a/vpncloud.adoc b/vpncloud.adoc index da3bba8..b5c10ac 100644 --- a/vpncloud.adoc +++ b/vpncloud.adoc @@ -59,9 +59,9 @@ vpncloud - Peer-to-peer VPN the traffic will be sent unencrypted. *--crypto *:: - The encryption method to use ("aes256", or "chacha20"). Most current CPUs - have special support for AES256 so this should be faster. For older - computers lacking this support, only CHACHA20 is supported. + The encryption method to use ("aes128", "aes256", or "chacha20"). Most + current CPUs have special support for AES256 so this should be faster. For + older computers lacking this support, CHACHA20 is the fastest option. [default: *chacha20*] *--magic *:: @@ -258,10 +258,10 @@ side effects. . 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 reviewed. -The shared key is hashed using _ScryptSalsa208Sha256_ to derive a key, -which is used to encrypt the payload of messages using _ChaCha20Poly1305_ or -_AES256-GCM_. The encryption includes an authentication that also protects the -header. +The shared key is hashed using _PBKDF2_HMAC_SHA256_ to derive a key, +which is used to encrypt the payload of messages using _ChaCha20Poly1305_, +_AES128-GCM_, or _AES256-GCM_. The encryption includes an authentication that +also protects the header. This method does only protect against attacks on single messages but not against attacks that manipulate the message series itself (i.e. suppress messages, reorder them, or duplicate them).