mirror of https://github.com/dswd/vpncloud.git
Include libsodium in builds
This commit is contained in:
parent
03793052a3
commit
ec6d1b544f
14
.travis.yml
14
.travis.yml
|
@ -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:
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 = []
|
||||
|
|
2
build.rs
2
build.rs
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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)_*
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
7
|
|
@ -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)
|
|
@ -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.
|
|
@ -1 +0,0 @@
|
|||
misc:Depends=
|
|
@ -1 +0,0 @@
|
|||
vpncloud.1
|
|
@ -1,3 +0,0 @@
|
|||
#!/usr/bin/make -f
|
||||
%:
|
||||
dh $@
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -53,10 +53,11 @@ The test is run in 3 steps:
|
|||
|
||||
|
||||
| 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% |
|
||||
| Encrypted VpnCloud (ChaCha20) | 770 Mbits/sec | 100% |
|
||||
| Encrypted VpnCloud (AES256) | 813 Mbits/sec | 90% / 100% |
|
||||
|
||||
|
||||
### Latency
|
||||
|
@ -71,12 +72,14 @@ 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 |
|
||||
| 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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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]);
|
||||
}
|
11
src/main.rs
11
src/main.rs
|
@ -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;
|
||||
}
|
||||
|
|
57
src/tests.rs
57
src/tests.rs
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
12
vpncloud.md
12
vpncloud.md
|
@ -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`
|
||||
|
|
Loading…
Reference in New Issue