vpncloud/src/crypto.rs

294 lines
12 KiB
Rust

use std::{mem, ptr};
use std::ffi::CStr;
use libc::{size_t, c_char, c_ulonglong, c_int};
use super::types::Error;
#[allow(non_upper_case_globals)]
const crypto_aead_chacha20poly1305_KEYBYTES: usize = 32;
#[allow(non_upper_case_globals)]
const crypto_aead_chacha20poly1305_NSECBYTES: usize = 0;
#[allow(non_upper_case_globals)]
const crypto_aead_chacha20poly1305_NPUBBYTES: usize = 8;
#[allow(non_upper_case_globals)]
const crypto_aead_chacha20poly1305_ABYTES: usize = 16;
#[allow(non_upper_case_globals)]
const crypto_aead_aes256gcm_KEYBYTES: usize = 32;
#[allow(non_upper_case_globals)]
const crypto_aead_aes256gcm_NSECBYTES: usize = 0;
#[allow(non_upper_case_globals)]
const crypto_aead_aes256gcm_NPUBBYTES: usize = 12;
#[allow(non_upper_case_globals)]
const crypto_aead_aes256gcm_ABYTES: usize = 16;
#[allow(non_upper_case_globals)]
const crypto_pwhash_scryptsalsa208sha256_SALTBYTES: usize = 32;
#[allow(non_upper_case_globals)]
const crypto_pwhash_scryptsalsa208sha256_STRBYTES: usize = 102;
#[allow(non_upper_case_globals)]
const crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE: usize = 524288;
#[allow(non_upper_case_globals)]
const crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE: usize = 16777216;
#[link(name="sodium", kind="static")]
extern {
pub fn sodium_init() -> c_int;
pub fn randombytes_buf(buf: *mut u8, size: size_t);
pub fn sodium_version_string() -> *const c_char;
pub fn crypto_pwhash_scryptsalsa208sha256(
out: *mut u8,
outlen: c_ulonglong,
passwd: *const u8,
passwdlen: c_ulonglong,
salt: *const [u8; crypto_pwhash_scryptsalsa208sha256_SALTBYTES],
opslimit: c_ulonglong,
memlimit: size_t) -> c_int;
pub fn crypto_aead_chacha20poly1305_encrypt(
c: *mut u8,
clen: *mut c_ulonglong,
m: *const u8,
mlen: c_ulonglong,
ad: *const u8,
adlen: c_ulonglong,
nsec: *const [u8; crypto_aead_chacha20poly1305_NSECBYTES],
npub: *const [u8; crypto_aead_chacha20poly1305_NPUBBYTES],
k: *const [u8; crypto_aead_chacha20poly1305_KEYBYTES]) -> c_int;
pub fn crypto_aead_chacha20poly1305_decrypt(
m: *mut u8,
mlen: *mut c_ulonglong,
nsec: *mut [u8; crypto_aead_chacha20poly1305_NSECBYTES],
c: *const u8,
clen: c_ulonglong,
ad: *const u8,
adlen: c_ulonglong,
npub: *const [u8; crypto_aead_chacha20poly1305_NPUBBYTES],
k: *const [u8; crypto_aead_chacha20poly1305_KEYBYTES]) -> c_int;
pub fn crypto_aead_aes256gcm_encrypt(
c: *mut u8,
clen: *mut c_ulonglong,
m: *const u8,
mlen: c_ulonglong,
ad: *const u8,
adlen: c_ulonglong,
nsec: *const [u8; crypto_aead_aes256gcm_NSECBYTES],
npub: *const [u8; crypto_aead_aes256gcm_NPUBBYTES],
k: *const [u8; crypto_aead_aes256gcm_KEYBYTES]) -> c_int;
pub fn crypto_aead_aes256gcm_decrypt(
m: *mut u8,
mlen: *mut c_ulonglong,
nsec: *mut [u8; crypto_aead_aes256gcm_NSECBYTES],
c: *const u8,
clen: c_ulonglong,
ad: *const u8,
adlen: c_ulonglong,
npub: *const [u8; crypto_aead_aes256gcm_NPUBBYTES],
k: *const [u8; crypto_aead_aes256gcm_KEYBYTES]) -> c_int;
}
#[derive(RustcDecodable, Debug)]
pub enum CryptoMethod {
ChaCha20, AES256
}
pub enum Crypto {
None,
ChaCha20Poly1305{key: [u8; 32], nonce: [u8; 8]},
AES256GCM{key: [u8; 32], nonce: [u8; 12]}
}
fn inc_nonce_8(nonce: &mut [u8; 8]) {
unsafe {
let num = mem::transmute::<&mut [u8; 8], &mut u64>(nonce);
*num = num.wrapping_add(1)
}
}
fn inc_nonce_12(nonce: &mut [u8; 12]) {
for i in 0..12 {
let mut num = nonce[11-i];
num = num.wrapping_add(1);
nonce[11-i] = num;
if num > 0 {
break
}
}
}
impl Crypto {
pub fn init() {
unsafe { sodium_init() };
}
pub fn sodium_version() -> String {
unsafe {
CStr::from_ptr(sodium_version_string()).to_string_lossy().to_string()
}
}
pub fn method(&self) -> u8 {
match self {
&Crypto::None => 0,
&Crypto::ChaCha20Poly1305{key: _, nonce: _} => 1,
&Crypto::AES256GCM{key: _, nonce: _} => 2
}
}
pub fn nonce_bytes(&self) -> usize {
match self {
&Crypto::None => 0,
&Crypto::ChaCha20Poly1305{key: _, ref nonce} => nonce.len(),
&Crypto::AES256GCM{key: _, ref nonce} => nonce.len()
}
}
pub fn additional_bytes(&self) -> usize {
match self {
&Crypto::None => 0,
&Crypto::ChaCha20Poly1305{key: _, nonce: _} => crypto_aead_chacha20poly1305_ABYTES,
&Crypto::AES256GCM{key: _, nonce: _} => crypto_aead_aes256gcm_ABYTES
}
}
pub fn from_shared_key(method: CryptoMethod, password: &str) -> Self {
let salt = "vpncloudVPNCLOUDvpncl0udVpnCloud".as_bytes();
assert_eq!(salt.len(), crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
let mut key = [0; crypto_pwhash_scryptsalsa208sha256_STRBYTES];
let res = unsafe { crypto_pwhash_scryptsalsa208sha256(
key.as_mut_ptr(),
key.len() as u64,
password.as_bytes().as_ptr(),
password.as_bytes().len() as u64,
salt.as_ptr() as *const [u8; crypto_pwhash_scryptsalsa208sha256_SALTBYTES],
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE as u64,
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE
) };
if res != 0 {
panic!("Key derivation failed");
}
match method {
CryptoMethod::ChaCha20 => {
let mut crypto_key = [0; crypto_aead_chacha20poly1305_KEYBYTES];
for i in 0..crypto_key.len() {
crypto_key[i] = key[i];
}
let mut nonce = [0u8; crypto_aead_chacha20poly1305_NPUBBYTES];
unsafe { randombytes_buf(nonce.as_mut_ptr(), nonce.len()) };
Crypto::ChaCha20Poly1305{key: crypto_key, nonce: nonce}
},
CryptoMethod::AES256 => {
let mut crypto_key = [0; crypto_aead_aes256gcm_KEYBYTES];
for i in 0..crypto_key.len() {
crypto_key[i] = key[i];
}
let mut nonce = [0u8; crypto_aead_aes256gcm_NPUBBYTES];
unsafe { randombytes_buf(nonce.as_mut_ptr(), nonce.len()) };
Crypto::AES256GCM{key: crypto_key, nonce: nonce}
}
}
}
pub fn decrypt(&self, mut buf: &mut [u8], nonce: &[u8], header: &[u8]) -> Result<usize, Error> {
match self {
&Crypto::None => Ok(buf.len()),
&Crypto::ChaCha20Poly1305{ref key, nonce: _} => {
let mut mlen: u64 = buf.len() as u64;
let res = unsafe { crypto_aead_chacha20poly1305_decrypt(
buf.as_mut_ptr(), // Base pointer to buffer
&mut mlen, // Mutable size of buffer (will be set to used size)
ptr::null_mut::<[u8; 0]>(), // Mutable base pointer to secret nonce (always NULL)
buf.as_ptr(), // Base pointer to message
buf.len() as u64, // Size of message
header.as_ptr(), // Base pointer to additional data
header.len() as u64, // Size of additional data
nonce.as_ptr() as *const [u8; 8], // Base pointer to public nonce
key.as_ptr() as *const [u8; 32] // Base pointer to key
) };
match res {
0 => Ok(mlen as usize),
_ => Err(Error::CryptoError("Failed to decrypt"))
}
},
&Crypto::AES256GCM{ref key, nonce: _} => {
let mut mlen: u64 = buf.len() as u64;
let res = unsafe { crypto_aead_aes256gcm_decrypt(
buf.as_mut_ptr(), // Base pointer to buffer
&mut mlen, // Mutable size of buffer (will be set to used size)
ptr::null_mut::<[u8; 0]>(), // Mutable base pointer to secret nonce (always NULL)
buf.as_ptr(), // Base pointer to message
buf.len() as u64, // Size of message
header.as_ptr(), // Base pointer to additional data
header.len() as u64, // Size of additional data
nonce.as_ptr() as *const [u8; 12], // Base pointer to public nonce
key.as_ptr() as *const [u8; 32] // Base pointer to key
) };
match res {
0 => Ok(mlen as usize),
_ => Err(Error::CryptoError("Failed to decrypt"))
}
}
}
}
pub fn encrypt(&mut self, mut buf: &mut [u8], mlen: usize, nonce_bytes: &mut [u8], header: &[u8]) -> usize {
match self {
&mut Crypto::None => mlen,
&mut Crypto::ChaCha20Poly1305{ref key, ref mut nonce} => {
inc_nonce_8(nonce);
let mut clen: u64 = buf.len() as u64;
assert_eq!(nonce_bytes.len(), nonce.len());
assert_eq!(nonce.len(), crypto_aead_chacha20poly1305_NPUBBYTES);
assert_eq!(key.len(), crypto_aead_chacha20poly1305_KEYBYTES);
assert_eq!(0, crypto_aead_chacha20poly1305_NSECBYTES);
assert!(clen as usize >= mlen + crypto_aead_chacha20poly1305_ABYTES);
let res = unsafe { crypto_aead_chacha20poly1305_encrypt(
buf.as_mut_ptr(), // Base pointer to buffer
&mut clen, // Mutable size of buffer (will be set to used size)
buf.as_ptr(), // Base pointer to message
mlen as u64, // Size of message
header.as_ptr(), // Base pointer to additional data
header.len() as u64, // Size of additional data
ptr::null::<[u8; 0]>(), // Base pointer to secret nonce (always NULL)
nonce.as_ptr() as *const [u8; 8], // Base pointer to public nonce
key.as_ptr() as *const [u8; 32] // Base pointer to key
) };
assert_eq!(res, 0);
assert_eq!(clen as usize, mlen + crypto_aead_chacha20poly1305_ABYTES);
unsafe {
ptr::copy_nonoverlapping(nonce.as_ptr(), nonce_bytes.as_mut_ptr(), nonce.len());
}
clen as usize
},
&mut Crypto::AES256GCM{ref key, ref mut nonce} => {
inc_nonce_12(nonce);
let mut clen: u64 = buf.len() as u64;
assert_eq!(nonce_bytes.len(), nonce.len());
assert_eq!(nonce.len(), crypto_aead_aes256gcm_NPUBBYTES);
assert_eq!(key.len(), crypto_aead_aes256gcm_KEYBYTES);
assert_eq!(0, crypto_aead_aes256gcm_NSECBYTES);
assert!(clen as usize >= mlen + crypto_aead_aes256gcm_ABYTES);
let res = unsafe { crypto_aead_aes256gcm_encrypt(
buf.as_mut_ptr(), // Base pointer to buffer
&mut clen, // Mutable size of buffer (will be set to used size)
buf.as_ptr(), // Base pointer to message
mlen as u64, // Size of message
header.as_ptr(), // Base pointer to additional data
header.len() as u64, // Size of additional data
ptr::null::<[u8; 0]>(), // Base pointer to secret nonce (always NULL)
nonce.as_ptr() as *const [u8; 12], // Base pointer to public nonce
key.as_ptr() as *const [u8; 32] // Base pointer to key
) };
assert_eq!(res, 0);
assert_eq!(clen as usize, mlen + crypto_aead_aes256gcm_ABYTES);
unsafe {
ptr::copy_nonoverlapping(nonce.as_ptr(), nonce_bytes.as_mut_ptr(), nonce.len());
}
clen as usize
}
}
}
}