Include libsodium in builds

pull/9/head
Dennis Schwerdel 2015-11-30 17:27:50 +01:00
parent 03793052a3
commit ec6d1b544f
24 changed files with 394 additions and 324 deletions

View File

@ -11,9 +11,10 @@ before_script:
- ! ' set -e ;
pip install ''travis-cargo<0.2'' --user ;
export PATH=$HOME/.local/bin:$PATH ;
wget https://github.com/jedisct1/libsodium/releases/download/1.0.3/libsodium-1.0.3.tar.gz ;
tar xvfz libsodium-1.0.3.tar.gz ;
pushd libsodium-1.0.3 ;
export VERSION=1.0.6 ;
wget https://github.com/jedisct1/libsodium/releases/download/$VERSION/libsodium-$VERSION.tar.gz ;
tar xvfz libsodium-$VERSION.tar.gz ;
pushd libsodium-$VERSION ;
./configure --prefix=/usr ;
make ;
sudo make install ;
@ -22,9 +23,8 @@ before_script:
script:
- ! ' set -e ;
travis-cargo build ;
travis-cargo build -- --features "crypto" ;
travis-cargo test -- --features "crypto" ;
travis-cargo bench -- --features "crypto" ;
travis-cargo test ;
travis-cargo bench ;
travis-cargo coverage ;
'
addons:
@ -37,7 +37,7 @@ after_success:
- ! ' set -e ;
rm -rf target/kcov ;
rm target/debug/vpncloud-* ;
cargo test --features "crypto" ;
cargo test ;
kcov/build/src/kcov --exclude-pattern=/libsodium/,/x86_64-linux-gnu/,/.cargo --coveralls-id=$TRAVIS_JOB_ID target/kcov target/debug/vpncloud-* ;
'
notifications:

11
Cargo.lock generated
View File

@ -6,9 +6,9 @@ dependencies = [
"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)",
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libsodium-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"signal 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
@ -93,15 +93,6 @@ name = "libc"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libsodium-sys"
version = "0.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.3.4"

View File

@ -10,7 +10,6 @@ repository = "https://github.com/dswd/vpncloud.rs"
keywords = ["vpn", "p2p", "tun", "tap", "network"]
readme = "README.md"
[dependencies]
time = "0.1"
docopt = "0.6"
@ -20,12 +19,11 @@ epoll = "0.2"
signal = "0.1"
nix = "0.4"
libc = "0.2"
libsodium-sys = {version = "0.0.9", optional = true}
[build-dependencies]
gcc = "0.3"
pkg-config = "0.3.6"
[features]
default = []
crypto = ["libsodium-sys"] #not default as it requires external library
bench = []

View File

@ -1,5 +1,7 @@
extern crate gcc;
extern crate pkg_config;
fn main() {
pkg_config::find_library("libsodium").unwrap();
gcc::Config::new().file("src/c/tuntap.c").include("src").compile("libtuntap.a");
}

View File

@ -5,14 +5,11 @@ DEPENDENCIES=debhelper devscripts
default: clean build
.PHONY: build
build: $(PACKAGE)_*.deb $(PACKAGE)-nocrypto_*.deb
build: $(PACKAGE)_*.deb
$(PACKAGE)_*.deb:
(cd $(PACKAGE); make clean; debuild -b -us -uc; cd ..)
$(PACKAGE)-nocrypto_*.deb:
(cd $(PACKAGE)-nocrypto; make clean; debuild -b -us -uc; cd ..)
.PHONY: clean
clean:
(cd $(PACKAGE); debuild clean; cd ..)
(cd $(PACKAGE)-nocrypto; debuild clean; cd ..)
rm -rf $(PACKAGE)_* $(PACKAGE)-nocrypto_*
rm -rf $(PACKAGE)_*

View File

@ -1,20 +0,0 @@
build: vpncloud.1 vpncloud
vpncloud.1: vpncloud.1.ronn
ronn -r vpncloud.1.ronn
vpncloud.1.ronn: ../../vpncloud.md
cp ../../vpncloud.md vpncloud.1.ronn
clean:
rm -f vpncloud.1* vpncloud ../../target/release/vpncloud
vpncloud: ../../target/release/vpncloud
cp ../../target/release/vpncloud vpncloud
../../target/release/vpncloud: ../../src/*.rs ../../Cargo.toml ../../src/usage.txt ../../src/c/*
(cd ../..; cargo build --release)
install:
install -d $(DESTDIR)/usr/bin
install -m 755 vpncloud $(DESTDIR)/usr/bin/vpncloud

View File

@ -1,11 +0,0 @@
vpncloud-nocrypto (0.2.0) stable; urgency=medium
* More stable release
-- Dennis Schwerdel <schwerdel@informatik.uni-kl.de> Thu, 26 Nov 2015 17:41:40 +0100
vpncloud-nocrypto (0.1.0) stable; urgency=medium
* Initial release
-- Dennis Schwerdel <schwerdel@informatik.uni-kl.de> Tue, 24 Nov 2015 09:31:47 +0100

View File

@ -1 +0,0 @@
7

View File

@ -1,12 +0,0 @@
Source: vpncloud-nocrypto
Section: misc
Priority: extra
Maintainer: Dennis Schwerdel <schwerdel@informatik.uni-kl.de>
Build-Depends: debhelper (>= 7), ruby-ronn, rust-stable
Standards-Version: 3.8.3
Package: vpncloud-nocrypto
Architecture: amd64
Depends: ${shlibs:Depends}, ${misc:Depends}
Conflicts: vpncloud
Description: Peer-to-peer VPN (built without crypto support)

View File

@ -1,31 +0,0 @@
Upstream Author:
Dennis Schwerdel <schwerdel@informatik.uni-kl.de>
Copyright:
Copyright (C) 2015 Dennis Schwerdel, University of Kaiserslautern
License:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
The Debian packaging is:
Copyright (C) 2015 Dennis Schwerdel, University of Kaiserslautern
and is licensed under the GPL version 3, see above.

View File

@ -1 +0,0 @@
misc:Depends=

View File

@ -1 +0,0 @@
vpncloud.1

View File

@ -1,3 +0,0 @@
#!/usr/bin/make -f
%:
dh $@

View File

@ -13,7 +13,7 @@ vpncloud: ../../target/release/vpncloud
cp ../../target/release/vpncloud vpncloud
../../target/release/vpncloud: ../../src/*.rs ../../Cargo.toml ../../src/usage.txt ../../src/c/*
(cd ../..; cargo build --release --features "crypto")
(cd ../..; cargo build --release)
install:
install -d $(DESTDIR)/usr/bin

View File

@ -2,11 +2,10 @@ Source: vpncloud
Section: misc
Priority: extra
Maintainer: Dennis Schwerdel <schwerdel@informatik.uni-kl.de>
Build-Depends: debhelper (>= 7), ruby-ronn, rust-stable, libsodium-dev
Build-Depends: debhelper (>= 7), ruby-ronn, rust-stable
Standards-Version: 3.8.3
Package: vpncloud
Architecture: amd64
Depends: ${shlibs:Depends}, ${misc:Depends}, libsodium13
Conflicts: vpncloud-nocrypto
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Peer-to-peer VPN

View File

@ -52,11 +52,12 @@ The test is run in 3 steps:
* Encrypted throughput via VpnCloud (`DST` is `10.2.1.2`)
| Throughput test | Bandwidth | CPU usage (one core) |
| -------------------- | ------------- | -------------------- |
| Without VpnCloud | 926 Mbits/sec | - |
| Unencrypted VpnCloud | 873 Mbits/sec | 80% / 95% |
| Encrypted VpnCloud | 770 Mbits/sec | 100% |
| Throughput test | Bandwidth | CPU usage (one core) |
| ----------------------------- | ------------- | -------------------- |
| 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% |
### Latency
@ -70,13 +71,15 @@ For all the test, the best average RTT out of 5 runs is selected. The latency is
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 | 218 µs | 230 µs | 257 µs |
| Difference | +8 µs | +14 µs | +20 µs |
| 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 |
### Conclusion

293
src/crypto.rs Normal file
View File

@ -0,0 +1,293 @@
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
}
}
}
}

View File

@ -1,35 +0,0 @@
use super::super::types::Error;
pub enum Crypto {
None
}
impl Crypto {
pub fn init() {
}
pub fn method(&self) -> u8 {
0
}
pub fn nonce_bytes(&self) -> usize {
0
}
pub fn additional_bytes(&self) -> usize {
0
}
pub fn from_shared_key(_password: &str) -> Self {
panic!("This binary has no crypto support");
}
pub fn decrypt(&self, mut _buf: &mut [u8], _nonce: &[u8], _hash: &[u8]) -> Result<usize, Error> {
unreachable!("This should never be called")
}
pub fn encrypt(&mut self, mut _buf: &mut [u8], _mlen: usize, _nonce_bytes: &mut [u8], _header: &[u8]) -> usize {
unreachable!("This should never be called")
}
}

View File

@ -1,5 +0,0 @@
#[cfg(feature = "crypto")] mod sodium;
#[cfg(not(feature = "crypto"))] mod dummy;
#[cfg(feature = "crypto")] pub use self::sodium::Crypto;
#[cfg(not(feature = "crypto"))] pub use self::dummy::Crypto;

View File

@ -1,151 +0,0 @@
use std::{mem, ptr};
use libsodium_sys::*;
use super::super::types::Error;
pub enum Crypto {
None,
ChaCha20Poly1305{key: [u8; 32], nonce: [u8; 8]}
}
fn inc_nonce(nonce: [u8; 8]) -> [u8; 8] {
unsafe {
let mut num: u64 = mem::transmute(nonce);
num = num.wrapping_add(1);
mem::transmute(num)
}
}
impl Crypto {
pub fn init() {
unsafe { sodium_init() };
}
pub fn method(&self) -> u8 {
match self {
&Crypto::None => 0,
&Crypto::ChaCha20Poly1305{key: _, nonce: _} => 1
}
}
pub fn nonce_bytes(&self) -> usize {
match self {
&Crypto::None => 0,
&Crypto::ChaCha20Poly1305{key: _, ref nonce} => nonce.len()
}
}
pub fn additional_bytes(&self) -> usize {
match self {
&Crypto::None => 0,
&Crypto::ChaCha20Poly1305{key: _, nonce: _} => crypto_aead_chacha20poly1305_ABYTES
}
}
pub fn from_shared_key(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");
}
let mut crypto_key = [0; 32];
for i in 0..crypto_key.len() {
crypto_key[i] = key[i];
}
let mut nonce = [0u8; 8];
unsafe { randombytes_buf(nonce.as_mut_ptr(), nonce.len()) };
Crypto::ChaCha20Poly1305{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"))
}
}
}
}
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} => {
*nonce = inc_nonce(*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
}
}
}
}
#[test]
fn encrypt_decrypt() {
let mut sender = Crypto::from_shared_key("test");
let receiver = Crypto::from_shared_key("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; 8];
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; 8];
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

@ -7,7 +7,6 @@ extern crate epoll;
extern crate signal;
extern crate nix;
extern crate libc;
#[cfg(feature = "crypto")] extern crate libsodium_sys;
#[cfg(feature = "bench")] extern crate test;
#[macro_use] mod util;
@ -33,7 +32,7 @@ use ip::RoutingTable;
use types::{Error, Mode, Type, Range, Table, Protocol};
use cloud::GenericCloud;
use udpmessage::VERSION;
use crypto::Crypto;
use crypto::{Crypto, CryptoMethod};
use util::Duration;
@ -60,6 +59,7 @@ struct Args {
flag_type: Type,
flag_mode: Mode,
flag_shared_key: Option<String>,
flag_crypto: CryptoMethod,
flag_subnet: Vec<String>,
flag_device: String,
flag_listen: String,
@ -113,7 +113,7 @@ fn run<T: Protocol> (args: Args) {
});
Crypto::init();
let crypto = match args.flag_shared_key {
Some(key) => Crypto::from_shared_key(&key),
Some(key) => Crypto::from_shared_key(args.flag_crypto, &key),
None => Crypto::None
};
let mut cloud = GenericCloud::<T>::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto);
@ -132,9 +132,8 @@ fn run<T: Protocol> (args: Args) {
fn main() {
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
if args.flag_version {
println!("VpnCloud v{} ({}, protocol version {})", env!("CARGO_PKG_VERSION"),
if cfg!(feature = "crypto") { "with crypto support" } else { "without crypto support" },
VERSION
println!("VpnCloud v{} (protocol version {}, libsodium {})", env!("CARGO_PKG_VERSION"),
VERSION, Crypto::sodium_version()
);
return;
}

View File

@ -5,7 +5,7 @@ use super::ethernet::{Frame, SwitchTable};
use super::ip::{RoutingTable, Packet};
use super::types::{Protocol, Address, Range, Table};
use super::udpmessage::{Options, Message, decode, encode};
use super::crypto::Crypto;
use super::crypto::{Crypto, CryptoMethod};
#[test]
@ -23,11 +23,10 @@ fn udpmessage_packet() {
assert_eq!(msg, msg2);
}
#[cfg(feature = "crypto")]
#[test]
fn udpmessage_encrypted() {
let mut options = Options::default();
let mut crypto = Crypto::from_shared_key("test");
let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
let payload = [1,2,3,4,5];
let msg = Message::Data(&payload);
let mut buf = [0; 1024];
@ -122,11 +121,9 @@ fn udpmessage_invalid() {
assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,1,0], &mut crypto).is_err());
}
#[cfg(feature = "crypto")]
#[test]
fn udpmessage_invalid_crypto() {
let mut options = Options::default();
let mut crypto = Crypto::from_shared_key("test");
let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
// truncated crypto
assert!(decode(&mut [0x76,0x70,0x6e,1,1,0,0,0], &mut crypto).is_err());
}
@ -252,3 +249,51 @@ fn message_fmt() {
])), "Init(stage=0, [0.1.2.3/24, 00:01:02:03:04:05/16])");
assert_eq!(format!("{:?}", Message::Close), "Close");
}
#[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; 8];
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; 8];
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; 8];
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; 8];
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

@ -14,6 +14,8 @@ Options:
--subnet <subnet> The local subnets to use.
--network-id <id> Optional token that identifies the network.
--shared-key <key> The shared key to encrypt all traffic.
--crypto <method> The encryption method to use ("aes256", or
"chacha20"). [default: aes256]
--peer-timeout <secs> Peer timeout in seconds. [default: 1800]
--dst-timeout <secs> Switch table entry timeout in seconds.
[default: 300]

View File

@ -49,6 +49,13 @@ vpncloud(1) -- Peer-to-peer VPN
An optional shared key to encrypt the VPN data. If this option is not set,
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, CHACHA20 should be faster.
[default: `aes256`]
* `--network-id <id>`:
An optional token that identifies the network and helps to distinguish it
@ -233,6 +240,11 @@ Every packet sent over UDP contains the following header (in order):
`libsodium::crypto_aead_chacha20poly1305` method, using the 8 byte header
as additional data.
- Method `2`, **AES256GCM**: The header is followed by a 12 byte *nonce*.
The rest of the data is encrypted with the
`libsodium::crypto_aead_aes256gcm` method, using the 8 byte header
as additional data.
* 1 `reserved byte` that is currently unused
* 1 byte for `flags`