From 7dfcd92ab09ff7e33c768c8edf2fbab1fb678e54 Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Mon, 30 Nov 2015 22:11:37 +0100 Subject: [PATCH] Improved crypto --- Cargo.lock | 22 +++++++++++++++- Cargo.toml | 3 ++- performance.md | 26 +++++++++---------- src/crypto.rs | 70 +++++++++++++++++++++++++++++--------------------- src/main.rs | 1 + 5 files changed, 78 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8c8611..224d07d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,7 +1,8 @@ [root] name = "vpncloud" -version = "0.2.0" +version = "0.3.0" dependencies = [ + "aligned_alloc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.6.76 (registry+https://github.com/rust-lang/crates.io-index)", "epoll 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -31,6 +32,16 @@ dependencies = [ "memchr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "aligned_alloc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bitflags" version = "0.3.3" @@ -74,6 +85,15 @@ dependencies = [ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "kernel32-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kernel32-sys" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index d9e8639..b92b3c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vpncloud" -version = "0.2.0" +version = "0.3.0" authors = ["Dennis Schwerdel "] build = "build.rs" license = "GPL-3.0" @@ -19,6 +19,7 @@ epoll = "0.2" signal = "0.1" nix = "0.4" libc = "0.2" +aligned_alloc = "0.1.1" [build-dependencies] gcc = "0.3" diff --git a/performance.md b/performance.md index 993e9ec..1113be6 100644 --- a/performance.md +++ b/performance.md @@ -17,7 +17,7 @@ Receiver node: * Ubuntu 14.04 (Kernel 3.13.0-63-generic) * Libsodium 1.0.7 -VpnCloud version: `VpnCloud v0.2.0 (with crypto support, protocol version 1)` +VpnCloud version: `VpnCloud v0.3.0 (protocol version 1, libsodium 1.0.7)` The sender runs the following command: @@ -35,7 +35,7 @@ $> iperf -s & $> top ``` -For encrypted tests, `--shared-key test` is appended. +For encrypted tests, `--shared-key test --crypto METHOD` is appended. ### Throughput @@ -57,14 +57,14 @@ The test is run in 3 steps: | Without VpnCloud | 926 Mbits/sec | - | | Unencrypted VpnCloud | 873 Mbits/sec | 80% / 95% | | Encrypted VpnCloud (ChaCha20) | 770 Mbits/sec | 100% | -| Encrypted VpnCloud (AES256) | 813 Mbits/sec | 90% / 100% | +| Encrypted VpnCloud (AES256) | 835 Mbits/sec | 90% / 100% | ### Latency The latency is measured with the following command: ``` -$> ping DST -c 10000 -i 0.001 -s SIZE -U -q +$> ping DST -c 100000 -i 0.001 -s SIZE -U -q ``` For all the test, the best average RTT out of 5 runs is selected. The latency is @@ -73,18 +73,18 @@ assumed to be half of the RTT. | Payload size | 100 bytes | 500 bytes | 1000 bytes | | ----------------------------- | --------- | --------- | ---------- | -| Without VpnCloud | 158 µs | 165 µs | 178 µs | -| Unencrypted VpnCloud | 210 µs | 216 µs | 237 µs | -| Difference | +52 µs | +51 µs | +59 µs | -| Encrypted VpnCloud (ChaCha20) | 218 µs | 230 µs | 257 µs | -| Difference | +8 µs | +14 µs | +20 µs | -| Encrypted VpnCloud (AES256) | 224 µs | 230 µs | 255 µs | -| Difference | +14 µs | +14 µs | +18 µs | +| Without VpnCloud | 158 µs | 164 µs | 171 µs | +| Unencrypted VpnCloud | 217 µs | 227 µs | 242 µs | +| Difference | +59 µs | +63 µs | +71 µs | +| Encrypted VpnCloud (ChaCha20) | 231 µs | 245 µs | 259 µs | +| Difference | +14 µs | +18 µs | +17 µs | +| Encrypted VpnCloud (AES256) | 223 µs | 237 µs | 251 µs | +| Difference | +6 µs | +10 µs | +9 µs | ### Conclusion * VpnCloud achieves over 850 MBit/s with default MTU settings. -* In encrypted mode, VpnCloud reaches over 750 MBit/s with default MTU settings. -* VpnCloud adds about 60µs to the latency. +* In encrypted mode, VpnCloud reaches over 800 MBit/s with default MTU settings. +* VpnCloud adds about 70µs to the latency. * Encryption adds an additional latency up to 20µs depending on the packet size. diff --git a/src/crypto.rs b/src/crypto.rs index 4139837..472bf17 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -2,6 +2,7 @@ use std::{mem, ptr}; use std::ffi::CStr; use libc::{size_t, c_char, c_ulonglong, c_int}; +use aligned_alloc::{aligned_alloc, aligned_free}; use super::types::Error; @@ -32,6 +33,22 @@ const crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE: usize = 524288; #[allow(non_upper_case_globals)] const crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE: usize = 16777216; +struct Aes256State(*mut [u8; 512]); + +impl Aes256State { + fn new() -> Aes256State { + let ptr = aligned_alloc(512, 16) as *mut [u8; 512]; + Aes256State(ptr) + } +} + +impl Drop for Aes256State { + fn drop(&mut self) { + unsafe { aligned_free(self.0 as *mut ()) } + } +} + + #[link(name="sodium", kind="static")] extern { pub fn sodium_init() -> c_int; @@ -65,7 +82,10 @@ extern { 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( + pub fn crypto_aead_aes256gcm_beforenm( + state: *mut [u8; 512], + k: *const [u8; crypto_aead_aes256gcm_KEYBYTES]) -> c_int; + pub fn crypto_aead_aes256gcm_encrypt_afternm( c: *mut u8, clen: *mut c_ulonglong, m: *const u8, @@ -74,8 +94,8 @@ extern { 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( + state: *const [u8; 512]) -> c_int; + pub fn crypto_aead_aes256gcm_decrypt_afternm( m: *mut u8, mlen: *mut c_ulonglong, nsec: *mut [u8; crypto_aead_aes256gcm_NSECBYTES], @@ -84,7 +104,7 @@ extern { ad: *const u8, adlen: c_ulonglong, npub: *const [u8; crypto_aead_aes256gcm_NPUBBYTES], - k: *const [u8; crypto_aead_aes256gcm_KEYBYTES]) -> c_int; + state: *const [u8; 512]) -> c_int; } @@ -96,7 +116,7 @@ pub enum CryptoMethod { pub enum Crypto { None, ChaCha20Poly1305{key: [u8; 32], nonce: [u8; 8]}, - AES256GCM{key: [u8; 32], nonce: [u8; 12]} + AES256GCM{state: Aes256State, nonce: [u8; 12]} } fn inc_nonce_8(nonce: &mut [u8; 8]) { @@ -133,7 +153,7 @@ impl Crypto { match self { &Crypto::None => 0, &Crypto::ChaCha20Poly1305{key: _, nonce: _} => 1, - &Crypto::AES256GCM{key: _, nonce: _} => 2 + &Crypto::AES256GCM{state: _, nonce: _} => 2 } } @@ -141,7 +161,7 @@ impl Crypto { match self { &Crypto::None => 0, &Crypto::ChaCha20Poly1305{key: _, ref nonce} => nonce.len(), - &Crypto::AES256GCM{key: _, ref nonce} => nonce.len() + &Crypto::AES256GCM{state: _, ref nonce} => nonce.len() } } @@ -149,7 +169,7 @@ impl Crypto { match self { &Crypto::None => 0, &Crypto::ChaCha20Poly1305{key: _, nonce: _} => crypto_aead_chacha20poly1305_ABYTES, - &Crypto::AES256GCM{key: _, nonce: _} => crypto_aead_aes256gcm_ABYTES + &Crypto::AES256GCM{state: _, nonce: _} => crypto_aead_aes256gcm_ABYTES } } @@ -180,13 +200,15 @@ impl Crypto { 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} + let state = Aes256State::new(); + let res = unsafe { crypto_aead_aes256gcm_beforenm( + state.0, + key[..crypto_aead_aes256gcm_KEYBYTES].as_ptr() as *const [u8; crypto_aead_aes256gcm_KEYBYTES] + ) }; + assert_eq!(res, 0); + Crypto::AES256GCM{state: state, nonce: nonce} } } } @@ -212,9 +234,9 @@ impl Crypto { _ => Err(Error::CryptoError("Failed to decrypt")) } }, - &Crypto::AES256GCM{ref key, nonce: _} => { + &Crypto::AES256GCM{ref state, nonce: _} => { let mut mlen: u64 = buf.len() as u64; - let res = unsafe { crypto_aead_aes256gcm_decrypt( + let res = unsafe { crypto_aead_aes256gcm_decrypt_afternm( 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) @@ -223,7 +245,7 @@ impl Crypto { 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 + state.0 // Base pointer to state ) }; match res { 0 => Ok(mlen as usize), @@ -239,10 +261,6 @@ impl Crypto { &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 @@ -256,21 +274,16 @@ impl Crypto { 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} => { + &mut Crypto::AES256GCM{ref state, 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( + let res = unsafe { crypto_aead_aes256gcm_encrypt_afternm( 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 @@ -279,10 +292,9 @@ impl Crypto { 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 + state.0 // Base pointer to state ) }; 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()); } diff --git a/src/main.rs b/src/main.rs index 215909a..67441c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ extern crate epoll; extern crate signal; extern crate nix; extern crate libc; +extern crate aligned_alloc; #[cfg(feature = "bench")] extern crate test; #[macro_use] mod util;