diff --git a/src/crypto.rs b/src/crypto.rs index 42fae38..c1a057d 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -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, nonce: Nonce} + ChaCha20HmacSha512256{key: Vec, nonce: Vec} } -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, Vec) { 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 = 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]); +} diff --git a/src/main.rs b/src/main.rs index 2327853..3a8d303 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,7 +58,7 @@ struct Args { flag_device: String, flag_listen: String, flag_network_id: Option, - flag_addr: Vec, + flag_connect: Vec, 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() diff --git a/src/udpmessage.rs b/src/udpmessage.rs index 72d8d47..574fe89 100644 --- a/src/udpmessage.rs +++ b/src/udpmessage.rs @@ -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); diff --git a/src/usage.txt b/src/usage.txt index f621ee3..38853b1 100644 --- a/src/usage.txt +++ b/src/usage.txt @@ -1,21 +1,21 @@ Usage: - vpncloud [options] [-t ] [-d ] [-l ] [-c ...] + vpncloud [options] [-t ] [-d ] [-l ] [-c ...] Options: -t , --type Set the type of network ("tap" or "tun"). [default: tap] - -d , --device Name of the virtual device. + -d , --device Name of the virtual device. [default: vpncloud%d] -m , --mode The mode of the VPN ("hub", "switch", "router", or "normal"). [default: normal] - -l , --listen The address to listen for data. + -l , --listen The address to listen for data. [default: 0.0.0.0:3210] -c , --connect Address of a peer to connect to. --subnet The local subnets to use. - --network-id Optional token that identifies the network. - --shared-key The shared key to encrypt all traffic. - --peer-timeout Peer timeout in seconds. [default: 1800] - --dst-timeout Switch table entry timeout in seconds. + --network-id Optional token that identifies the network. + --shared-key The shared key to encrypt all traffic. + --peer-timeout Peer timeout in seconds. [default: 1800] + --dst-timeout Switch table entry timeout in seconds. [default: 300] -v, --verbose Print debug information. -q, --quiet Only print errors and warnings. diff --git a/vpncloud.md b/vpncloud.md index 98cab77..b661c0d 100644 --- a/vpncloud.md +++ b/vpncloud.md @@ -3,7 +3,7 @@ vpncloud(1) -- Peer-to-peer VPN ## SYNOPSIS -`vpncloud [options] [-t ] [-d ] [-l ] [-c ...]` +`vpncloud [options] [-t ] [-d ] [-l ] [-c ...]` ## 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 `: + * `-l `, `--listen `: The address to listen for data. [default: 0.0.0.0:3210]