vpncloud/src/crypto.rs

211 lines
7.3 KiB
Rust
Raw Normal View History

// VpnCloud - Peer-to-Peer VPN
2019-02-14 21:51:10 +00:00
// Copyright (C) 2015-2019 Dennis Schwerdel
// This software is licensed under GPL-3 or newer (see LICENSE.md)
2019-02-14 21:51:10 +00:00
use std::num::NonZeroU32;
2019-01-04 12:53:12 +00:00
use ring::aead::*;
2019-01-08 19:32:47 +00:00
use ring::pbkdf2;
use ring::rand::*;
use ring::digest::*;
2015-11-30 16:27:50 +00:00
use super::types::Error;
2019-02-15 21:42:13 +00:00
const SALT: &[u8; 32] = b"vpncloudVPNCLOUDvpncl0udVpnCloud";
const HEX_PREFIX: &str = "hex:";
const HASH_PREFIX: &str = "hash:";
2019-01-10 18:36:50 +00:00
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
2015-11-30 16:27:50 +00:00
pub enum CryptoMethod {
#[serde(rename = "chacha20")]
ChaCha20,
#[serde(rename = "aes256")]
AES256
2015-11-30 16:27:50 +00:00
}
2019-01-04 12:53:12 +00:00
pub struct CryptoData {
sealing_key: SealingKey,
opening_key: OpeningKey,
2019-02-19 17:42:50 +00:00
nonce: Vec<u8>,
key: Vec<u8>
2019-01-04 12:53:12 +00:00
}
2019-01-08 19:32:47 +00:00
#[allow(unknown_lints, clippy::large_enum_variant)]
2015-11-30 16:27:50 +00:00
pub enum Crypto {
None,
2019-01-04 12:53:12 +00:00
ChaCha20Poly1305(CryptoData),
AES256GCM(CryptoData)
2015-11-30 16:27:50 +00:00
}
2019-01-04 12:53:12 +00:00
fn inc_nonce(nonce: &mut [u8]) {
let l = nonce.len();
for i in (0..l).rev() {
let mut num = nonce[i];
2015-11-30 16:27:50 +00:00
num = num.wrapping_add(1);
2019-01-04 12:53:12 +00:00
nonce[i] = num;
2015-11-30 16:27:50 +00:00
if num > 0 {
2019-01-08 19:32:47 +00:00
return
2015-11-30 16:27:50 +00:00
}
}
2019-01-08 19:32:47 +00:00
warn!("Nonce overflowed");
2015-11-30 16:27:50 +00:00
}
impl Crypto {
2016-06-29 06:43:39 +00:00
#[inline]
2015-11-30 16:27:50 +00:00
pub fn method(&self) -> u8 {
2016-06-11 14:08:57 +00:00
match *self {
Crypto::None => 0,
Crypto::ChaCha20Poly1305{..} => 1,
Crypto::AES256GCM{..} => 2
2015-11-30 16:27:50 +00:00
}
}
2016-06-29 06:43:39 +00:00
#[inline]
2015-11-30 16:27:50 +00:00
pub fn nonce_bytes(&self) -> usize {
2016-06-11 14:08:57 +00:00
match *self {
Crypto::None => 0,
2019-01-04 12:53:12 +00:00
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => data.sealing_key.algorithm().nonce_len()
2015-11-30 16:27:50 +00:00
}
}
2019-02-19 17:42:50 +00:00
#[inline]
pub fn get_key(&self) -> &[u8] {
match *self {
Crypto::None => &[],
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => &data.key
}
}
2016-06-29 06:43:39 +00:00
#[inline]
2019-01-01 23:35:14 +00:00
#[allow(unknown_lints,clippy::match_same_arms)]
2015-11-30 16:27:50 +00:00
pub fn additional_bytes(&self) -> usize {
2016-06-11 14:08:57 +00:00
match *self {
Crypto::None => 0,
2019-01-08 19:32:47 +00:00
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => data.sealing_key.algorithm().tag_len()
2015-11-30 16:27:50 +00:00
}
}
pub fn from_shared_key(method: CryptoMethod, password: &str) -> Self {
2019-01-04 12:53:12 +00:00
let algo = match method {
CryptoMethod::ChaCha20 => &CHACHA20_POLY1305,
CryptoMethod::AES256 => &AES_256_GCM
};
2019-01-08 19:32:47 +00:00
let mut key: Vec<u8> = Vec::with_capacity(algo.key_len());
for _ in 0..algo.key_len() {
key.push(0);
}
2019-02-15 21:42:13 +00:00
if password.starts_with(HEX_PREFIX) {
let password = &password[HEX_PREFIX.len()..];
if password.len() != 2 * algo.key_len() {
fail!("Raw secret key must be exactly {} bytes long", algo.key_len());
}
for i in 0..algo.key_len() {
key[i] = try_fail!(u8::from_str_radix(&password[2*i..=2*i+1], 16), "Failed to parse raw secret key: {}");
2019-02-15 21:42:13 +00:00
}
} else {
let password = if password.starts_with(HASH_PREFIX) {
&password[HASH_PREFIX.len()..]
} else {
password
};
pbkdf2::derive(&SHA256, NonZeroU32::new(4096).unwrap(), SALT, password.as_bytes(), &mut key);
}
2019-01-04 12:53:12 +00:00
let sealing_key = SealingKey::new(algo, &key[..algo.key_len()]).expect("Failed to create key");
let opening_key = OpeningKey::new(algo, &key[..algo.key_len()]).expect("Failed to create key");
let mut nonce: Vec<u8> = Vec::with_capacity(algo.nonce_len());
for _ in 0..algo.nonce_len() {
nonce.push(0);
}
2019-02-15 21:42:13 +00:00
// leave the highest byte of the nonce 0 so it will not overflow
if SystemRandom::new().fill(&mut nonce[1..]).is_err() {
2019-01-08 19:32:47 +00:00
fail!("Randomizing nonce failed");
}
2019-02-19 17:42:50 +00:00
let data = CryptoData { sealing_key, opening_key, nonce, key };
2015-11-30 16:27:50 +00:00
match method {
2019-01-04 12:53:12 +00:00
CryptoMethod::ChaCha20 => Crypto::ChaCha20Poly1305(data),
CryptoMethod::AES256 => Crypto::AES256GCM(data)
2015-11-30 16:27:50 +00:00
}
}
2019-01-01 21:55:15 +00:00
pub fn decrypt(&self, buf: &mut [u8], nonce: &[u8], header: &[u8]) -> Result<usize, Error> {
2016-06-11 14:08:57 +00:00
match *self {
Crypto::None => Ok(buf.len()),
2019-01-04 12:53:12 +00:00
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => {
2019-02-14 21:51:10 +00:00
let nonce = Nonce::try_assume_unique_for_key(nonce).unwrap();
match open_in_place(&data.opening_key, nonce, Aad::from(header), 0, buf) {
2019-01-08 19:32:47 +00:00
Ok(plaintext) => Ok(plaintext.len()),
Err(_) => Err(Error::Crypto("Failed to decrypt"))
}
2015-11-30 16:27:50 +00:00
}
}
}
2019-01-01 21:55:15 +00:00
pub fn encrypt(&mut self, buf: &mut [u8], mlen: usize, nonce_bytes: &mut [u8], header: &[u8]) -> usize {
2019-01-08 19:32:47 +00:00
let tag_len = self.additional_bytes();
2016-06-11 14:08:57 +00:00
match *self {
Crypto::None => mlen,
2019-01-04 12:53:12 +00:00
Crypto::ChaCha20Poly1305(ref mut data) | Crypto::AES256GCM(ref mut data) => {
inc_nonce(&mut data.nonce);
assert!(buf.len() - mlen >= tag_len);
let buf = &mut buf[.. mlen + tag_len];
2019-02-14 21:51:10 +00:00
let nonce = Nonce::try_assume_unique_for_key(&data.nonce).unwrap();
let new_len = seal_in_place(&data.sealing_key, nonce, Aad::from(header), buf, tag_len).expect("Failed to encrypt");
2019-01-08 19:32:47 +00:00
nonce_bytes.clone_from_slice(&data.nonce);
2019-01-04 12:53:12 +00:00
new_len
2015-11-30 16:27:50 +00:00
}
}
}
}
2019-02-19 17:42:50 +00:00
#[test]
fn encrypt_decrypt_chacha20poly1305() {
let mut sender = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
let receiver = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
let msg = "HelloWorld0123456789";
let msg_bytes = msg.as_bytes();
let mut buffer = [0u8; 1024];
let header = [0u8; 8];
for i in 0..msg_bytes.len() {
buffer[i] = msg_bytes[i];
}
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]);
}
#[test]
fn encrypt_decrypt_aes256() {
let mut sender = Crypto::from_shared_key(CryptoMethod::AES256, "test");
let receiver = Crypto::from_shared_key(CryptoMethod::AES256, "test");
let msg = "HelloWorld0123456789";
let msg_bytes = msg.as_bytes();
let mut buffer = [0u8; 1024];
let header = [0u8; 8];
for i in 0..msg_bytes.len() {
buffer[i] = msg_bytes[i];
}
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]);
}