Added support for AES128

pull/61/head
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).
### UNRELEASED
- [added] Added crypto option AES128
### v1.4.0 (2020-06-03)
- [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;
}
#[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;

View File

@ -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<Self, Self::Err> {
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<u8> = 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<usize, Error> {
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]);
}

View File

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

View File

@ -59,9 +59,9 @@ vpncloud - Peer-to-peer VPN
the traffic will be sent unencrypted.
*--crypto <method>*::
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 <id>*::
@ -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).