Fixed crypto

This commit is contained in:
Dennis Schwerdel 2015-11-23 17:10:42 +01:00
parent 7ce4c21613
commit ff38beb6e3
5 changed files with 51 additions and 30 deletions

View File

@ -1,5 +1,5 @@
use sodiumoxide::crypto::stream::chacha20::{Key as CryptoKey, Nonce, stream_xor_inplace, gen_nonce,
NONCEBYTES, KEYBYTES};
KEYBYTES};
use sodiumoxide::crypto::auth::hmacsha512256::{Key as AuthKey, Tag, authenticate, verify};
use sodiumoxide::crypto::pwhash::{derive_key, SALTBYTES, Salt, HASHEDPASSWORDBYTES,
OPSLIMIT_INTERACTIVE, MEMLIMIT_INTERACTIVE};
@ -8,14 +8,15 @@ use super::types::Error;
pub enum Crypto {
None,
ChaCha20HmacSha512256{key: Vec<u8>, nonce: Nonce}
ChaCha20HmacSha512256{key: Vec<u8>, nonce: Vec<u8>}
}
fn inc_nonce(nonce: &mut Nonce) {
for i in 1..NONCEBYTES+1 {
let mut val = nonce.0[NONCEBYTES-i];
fn inc_nonce(nonce: &mut [u8]) {
let len = nonce.len();
for i in 1..len+1 {
let mut val = nonce[len-i];
val = val.wrapping_add(1);
nonce.0[NONCEBYTES-i] = val;
nonce[len-i] = val;
if val != 0 {
break;
}
@ -31,13 +32,14 @@ impl Crypto {
}
pub fn from_shared_key(password: &str) -> Self {
let salt = "vpn cloud----vpn cloud----vpn cloud";
let salt = "vpncloudVPNCLOUDvpncl0udVpnCloud";
assert_eq!(salt.len(), SALTBYTES);
let mut key = [0; HASHEDPASSWORDBYTES];
derive_key(&mut key, password.as_bytes(), &Salt::from_slice(salt.as_bytes()).unwrap(),
OPSLIMIT_INTERACTIVE, MEMLIMIT_INTERACTIVE).unwrap();
let key = key[..KEYBYTES].iter().map(|b| *b).collect();
Crypto::ChaCha20HmacSha512256{key: key, nonce: gen_nonce()}
let nonce = gen_nonce().0.iter().map(|b| *b).collect();
Crypto::ChaCha20HmacSha512256{key: key, nonce: nonce}
}
pub fn decrypt(&self, mut buf: &mut [u8], nonce: &[u8], hash: &[u8]) -> Result<(), Error> {
@ -60,14 +62,31 @@ impl Crypto {
pub fn encrypt(&mut self, mut buf: &mut [u8]) -> (Vec<u8>, Vec<u8>) {
match self {
&mut Crypto::None => (Vec::new(), Vec::new()),
&mut Crypto::ChaCha20HmacSha512256{ref key, mut nonce} => {
&mut Crypto::ChaCha20HmacSha512256{ref key, ref mut nonce} => {
let crypto_key = CryptoKey::from_slice(key).unwrap();
let auth_key = AuthKey::from_slice(key).unwrap();
inc_nonce(&mut nonce);
stream_xor_inplace(&mut buf, &nonce, &crypto_key);
inc_nonce(nonce);
let hash = authenticate(&buf, &auth_key);
(nonce.0.iter().map(|v| *v).collect(), hash.0.iter().map(|v| *v).collect())
stream_xor_inplace(&mut buf, &Nonce::from_slice(&nonce).unwrap(), &crypto_key);
(nonce.clone(), hash.0.iter().map(|v| *v).collect())
}
}
}
}
#[test]
fn encrypt_decrypt() {
let mut sender = Crypto::from_shared_key("test");
let receiver = Crypto::from_shared_key("test");
let msg = "HelloWorld0123456789";
let mut buffer: Vec<u8> = msg.bytes().collect();
let (nonce1, hash1) = sender.encrypt(&mut buffer);
assert!(msg.as_bytes() != &buffer as &[u8]);
receiver.decrypt(&mut buffer, &nonce1, &hash1).unwrap();
assert_eq!(msg.as_bytes(), &buffer as &[u8]);
let (nonce2, hash2) = sender.encrypt(&mut buffer);
assert!(nonce1 != nonce2);
assert!(hash1 == hash2);
receiver.decrypt(&mut buffer, &nonce2, &hash2).unwrap();
assert_eq!(msg.as_bytes(), &buffer as &[u8]);
}

View File

@ -58,7 +58,7 @@ struct Args {
flag_device: String,
flag_listen: String,
flag_network_id: Option<String>,
flag_addr: Vec<String>,
flag_connect: Vec<String>,
flag_peer_timeout: usize,
flag_dst_timeout: usize,
flag_verbose: bool,
@ -108,14 +108,14 @@ fn main() {
match args.flag_type {
Type::Tap => {
let mut cloud = TapCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto);
for addr in args.flag_addr {
for addr in &args.flag_connect {
cloud.connect(&addr as &str, true).expect("Failed to send");
}
cloud.run()
},
Type::Tun => {
let mut cloud = TunCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto);
for addr in args.flag_addr {
for addr in &args.flag_connect {
cloud.connect(&addr as &str, true).expect("Failed to send");
}
cloud.run()

View File

@ -93,6 +93,8 @@ pub fn decode<'a>(data: &'a mut [u8], crypto: &mut Crypto) -> Result<(Options, M
pos += 8;
let hash = &data[pos..pos+32];
pos += 32;
debug!("{:?}", nonce);
debug!("{:?}", hash);
// Cheat data mutable to make the borrow checker happy
let data = unsafe { slice::from_raw_parts_mut(mem::transmute(data[pos..].as_ptr()), data.len()-pos) };
try!(crypto.decrypt(data, nonce, hash));
@ -171,7 +173,7 @@ pub fn encode(options: &Options, msg: &Message, buf: &mut [u8], crypto: &mut Cry
header.flags |= 0x01;
}
if crypto.is_secure() {
header.flags |= 0x02
header.flags |= 0x02;
}
let header_dat = unsafe { as_bytes(&header) };
unsafe { ptr::copy_nonoverlapping(header_dat.as_ptr(), buf[pos..].as_mut_ptr(), header_dat.len()) };
@ -269,7 +271,7 @@ fn encode_message_packet() {
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 13);
assert_eq!(&buf[..8], &[118,112,110,0,0,0,0,0]);
assert_eq!(&buf[..8], &[118,112,110,1,0,0,0,0]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
@ -284,7 +286,7 @@ fn encode_message_peers() {
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 22);
assert_eq!(&buf[..size], &[118,112,110,0,0,0,0,1,2,1,2,3,4,0,123,5,6,7,8,48,57,0]);
assert_eq!(&buf[..size], &[118,112,110,1,0,0,0,1,2,1,2,3,4,0,123,5,6,7,8,48,57,0]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
@ -299,7 +301,7 @@ fn encode_option_network_id() {
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 16);
assert_eq!(&buf[..size], &[118,112,110,0,0,0,1,3,0,0,0,0,0,0,0,134]);
assert_eq!(&buf[..size], &[118,112,110,1,0,0,1,3,0,0,0,0,0,0,0,134]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
@ -314,7 +316,7 @@ fn encode_message_init() {
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 9);
assert_eq!(&buf[..size], &[118,112,110,0,0,0,0,2,0]);
assert_eq!(&buf[..size], &[118,112,110,1,0,0,0,2,0]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
@ -328,7 +330,7 @@ fn encode_message_close() {
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 8);
assert_eq!(&buf[..size], &[118,112,110,0,0,0,0,3]);
assert_eq!(&buf[..size], &[118,112,110,1,0,0,0,3]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);

View File

@ -1,21 +1,21 @@
Usage:
vpncloud [options] [-t <type>] [-d <device>] [-l <listen>] [-c <connect>...]
vpncloud [options] [-t <type>] [-d <name>] [-l <listen>] [-c <addr>...]
Options:
-t <type>, --type <type> Set the type of network ("tap" or "tun").
[default: tap]
-d <device>, --device <device> Name of the virtual device.
-d <name>, --device <name> Name of the virtual device.
[default: vpncloud%d]
-m <mode>, --mode <mode> The mode of the VPN ("hub", "switch",
"router", or "normal"). [default: normal]
-l <listen>, --listen <listen> The address to listen for data.
-l <addr>, --listen <addr> The address to listen for data.
[default: 0.0.0.0:3210]
-c <addr>, --connect <addr> Address of a peer to connect to.
--subnet <subnet> The local subnets to use.
--network-id <network_id> Optional token that identifies the network.
--shared-key <shared_key> The shared key to encrypt all traffic.
--peer-timeout <peer_timeout> Peer timeout in seconds. [default: 1800]
--dst-timeout <dst_timeout> Switch table entry timeout in seconds.
--network-id <id> Optional token that identifies the network.
--shared-key <key> The shared key to encrypt all traffic.
--peer-timeout <timeout> Peer timeout in seconds. [default: 1800]
--dst-timeout <timeout> Switch table entry timeout in seconds.
[default: 300]
-v, --verbose Print debug information.
-q, --quiet Only print errors and warnings.

View File

@ -3,7 +3,7 @@ vpncloud(1) -- Peer-to-peer VPN
## SYNOPSIS
`vpncloud [options] [-t <type>] [-d <device>] [-l <listen>] [-c <connect>...]`
`vpncloud [options] [-t <type>] [-d <device>] [-l <addr>] [-c <addr>...]`
## OPTIONS
@ -27,7 +27,7 @@ vpncloud(1) -- Peer-to-peer VPN
peers and ignore them otherwise. The **normal** mode is switch for tap
devices and router for tun devices. [default: normal]
* `-l <listen>`, `--listen <listen>`:
* `-l <addr>`, `--listen <addr>`:
The address to listen for data. [default: 0.0.0.0:3210]