mirror of https://github.com/dswd/vpncloud.git
Crypto feature
This commit is contained in:
parent
83574b9b47
commit
46728ce362
|
@ -10,6 +10,11 @@ docopt = "0.6"
|
||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
epoll = "0.2"
|
epoll = "0.2"
|
||||||
|
sodiumoxide = {version = "0.0.9", optional = true}
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gcc = "0.3"
|
gcc = "0.3"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
crypto = ["sodiumoxide"]
|
||||||
|
|
20
src/cloud.rs
20
src/cloud.rs
|
@ -14,6 +14,7 @@ use super::types::{Table, Protocol, VirtualInterface, Range, Error, NetworkId};
|
||||||
use super::device::Device;
|
use super::device::Device;
|
||||||
use super::udpmessage::{encode, decode, Options, Message};
|
use super::udpmessage::{encode, decode, Options, Message};
|
||||||
use super::{ethernet, ip};
|
use super::{ethernet, ip};
|
||||||
|
use super::Crypto;
|
||||||
|
|
||||||
struct PeerList {
|
struct PeerList {
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
|
@ -92,7 +93,8 @@ pub struct GenericCloud<P: Protocol> {
|
||||||
table: Box<Table>,
|
table: Box<Table>,
|
||||||
socket: UdpSocket,
|
socket: UdpSocket,
|
||||||
device: Device,
|
device: Device,
|
||||||
network_id: Option<NetworkId>,
|
options: Options,
|
||||||
|
crypto: Crypto,
|
||||||
next_peerlist: SteadyTime,
|
next_peerlist: SteadyTime,
|
||||||
update_freq: Duration,
|
update_freq: Duration,
|
||||||
buffer_out: [u8; 64*1024],
|
buffer_out: [u8; 64*1024],
|
||||||
|
@ -102,11 +104,14 @@ pub struct GenericCloud<P: Protocol> {
|
||||||
|
|
||||||
impl<P: Protocol> GenericCloud<P> {
|
impl<P: Protocol> GenericCloud<P> {
|
||||||
pub fn new(device: Device, listen: String, network_id: Option<NetworkId>, table: Box<Table>,
|
pub fn new(device: Device, listen: String, network_id: Option<NetworkId>, table: Box<Table>,
|
||||||
peer_timeout: Duration, learning: bool, broadcast: bool, addresses: Vec<Range>) -> Self {
|
peer_timeout: Duration, learning: bool, broadcast: bool, addresses: Vec<Range>,
|
||||||
|
crypto: Crypto) -> Self {
|
||||||
let socket = match UdpSocket::bind(&listen as &str) {
|
let socket = match UdpSocket::bind(&listen as &str) {
|
||||||
Ok(socket) => socket,
|
Ok(socket) => socket,
|
||||||
_ => panic!("Failed to open socket")
|
_ => panic!("Failed to open socket")
|
||||||
};
|
};
|
||||||
|
let mut options = Options::default();
|
||||||
|
options.network_id = network_id;
|
||||||
GenericCloud{
|
GenericCloud{
|
||||||
peers: PeerList::new(peer_timeout),
|
peers: PeerList::new(peer_timeout),
|
||||||
addresses: addresses,
|
addresses: addresses,
|
||||||
|
@ -116,7 +121,8 @@ impl<P: Protocol> GenericCloud<P> {
|
||||||
table: table,
|
table: table,
|
||||||
socket: socket,
|
socket: socket,
|
||||||
device: device,
|
device: device,
|
||||||
network_id: network_id,
|
options: options,
|
||||||
|
crypto: crypto,
|
||||||
next_peerlist: SteadyTime::now(),
|
next_peerlist: SteadyTime::now(),
|
||||||
update_freq: peer_timeout/2,
|
update_freq: peer_timeout/2,
|
||||||
buffer_out: [0; 64*1024],
|
buffer_out: [0; 64*1024],
|
||||||
|
@ -127,9 +133,7 @@ impl<P: Protocol> GenericCloud<P> {
|
||||||
|
|
||||||
fn send_msg<Addr: ToSocketAddrs+fmt::Display>(&mut self, addr: Addr, msg: &Message) -> Result<(), Error> {
|
fn send_msg<Addr: ToSocketAddrs+fmt::Display>(&mut self, addr: Addr, msg: &Message) -> Result<(), Error> {
|
||||||
debug!("Sending {:?} to {}", msg, addr);
|
debug!("Sending {:?} to {}", msg, addr);
|
||||||
let mut options = Options::default();
|
let size = encode(&mut self.options, msg, &mut self.buffer_out, &mut self.crypto);
|
||||||
options.network_id = self.network_id;
|
|
||||||
let size = encode(&options, msg, &mut self.buffer_out);
|
|
||||||
match self.socket.send_to(&self.buffer_out[..size], addr) {
|
match self.socket.send_to(&self.buffer_out[..size], addr) {
|
||||||
Ok(written) if written == size => Ok(()),
|
Ok(written) if written == size => Ok(()),
|
||||||
Ok(_) => Err(Error::SocketError("Sent out truncated packet")),
|
Ok(_) => Err(Error::SocketError("Sent out truncated packet")),
|
||||||
|
@ -210,7 +214,7 @@ impl<P: Protocol> GenericCloud<P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_net_message(&mut self, peer: SocketAddr, options: Options, msg: Message) -> Result<(), Error> {
|
fn handle_net_message(&mut self, peer: SocketAddr, options: Options, msg: Message) -> Result<(), Error> {
|
||||||
if let Some(id) = self.network_id {
|
if let Some(id) = self.options.network_id {
|
||||||
if options.network_id != Some(id) {
|
if options.network_id != Some(id) {
|
||||||
info!("Ignoring message from {} with wrong token {:?}", peer, options.network_id);
|
info!("Ignoring message from {} with wrong token {:?}", peer, options.network_id);
|
||||||
return Err(Error::WrongNetwork(options.network_id));
|
return Err(Error::WrongNetwork(options.network_id));
|
||||||
|
@ -279,7 +283,7 @@ impl<P: Protocol> GenericCloud<P> {
|
||||||
match &events[i as usize].data {
|
match &events[i as usize].data {
|
||||||
&0 => match self.socket.recv_from(&mut buffer) {
|
&0 => match self.socket.recv_from(&mut buffer) {
|
||||||
Ok((size, src)) => {
|
Ok((size, src)) => {
|
||||||
match decode(&buffer[..size]).and_then(|(options, msg)| self.handle_net_message(src, options, msg)) {
|
match decode(&mut buffer[..size], &mut self.crypto).and_then(|(options, msg)| self.handle_net_message(src, options, msg)) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => error!("Error: {:?}", e)
|
Err(e) => error!("Error: {:?}", e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
use sodiumoxide::crypto::stream::chacha20::{Key as CryptoKey, Nonce, stream_xor_inplace, gen_nonce,
|
||||||
|
NONCEBYTES, 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};
|
||||||
|
|
||||||
|
use super::types::Error;
|
||||||
|
|
||||||
|
pub enum Crypto {
|
||||||
|
None,
|
||||||
|
ChaCha20HmacSha512256{key: Vec<u8>, nonce: Nonce}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inc_nonce(nonce: &mut Nonce) {
|
||||||
|
for i in 1..NONCEBYTES+1 {
|
||||||
|
let mut val = nonce.0[NONCEBYTES-i];
|
||||||
|
val = val.wrapping_add(1);
|
||||||
|
nonce.0[NONCEBYTES-i] = val;
|
||||||
|
if val != 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Crypto {
|
||||||
|
pub fn is_secure(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
&Crypto::None => false,
|
||||||
|
_ => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_shared_key(password: &str) -> Self {
|
||||||
|
let salt = "vpn cloud----vpn cloud----vpn cloud";
|
||||||
|
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()}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decrypt(&self, mut buf: &mut [u8], nonce: &[u8], hash: &[u8]) -> Result<(), Error> {
|
||||||
|
match self {
|
||||||
|
&Crypto::None => Ok(()),
|
||||||
|
&Crypto::ChaCha20HmacSha512256{ref key, nonce: _} => {
|
||||||
|
let crypto_key = CryptoKey::from_slice(key).unwrap();
|
||||||
|
let nonce = Nonce::from_slice(nonce).unwrap();
|
||||||
|
let auth_key = AuthKey::from_slice(key).unwrap();
|
||||||
|
let hash = Tag::from_slice(hash).unwrap();
|
||||||
|
stream_xor_inplace(&mut buf, &nonce, &crypto_key);
|
||||||
|
match verify(&hash, &buf, &auth_key) {
|
||||||
|
true => Ok(()),
|
||||||
|
false => Err(Error::CryptoError("Decryption failed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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} => {
|
||||||
|
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);
|
||||||
|
let hash = authenticate(&buf, &auth_key);
|
||||||
|
(nonce.0.iter().map(|v| *v).collect(), hash.0.iter().map(|v| *v).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/main.rs
16
src/main.rs
|
@ -3,9 +3,12 @@ extern crate time;
|
||||||
extern crate docopt;
|
extern crate docopt;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate epoll;
|
extern crate epoll;
|
||||||
|
#[cfg(feature = "crypto")] extern crate sodiumoxide;
|
||||||
|
|
||||||
mod util;
|
mod util;
|
||||||
mod types;
|
mod types;
|
||||||
|
#[cfg(feature = "crypto")] mod crypto;
|
||||||
|
#[cfg(not(feature = "crypto"))] mod no_crypto;
|
||||||
mod udpmessage;
|
mod udpmessage;
|
||||||
mod ethernet;
|
mod ethernet;
|
||||||
mod ip;
|
mod ip;
|
||||||
|
@ -23,10 +26,10 @@ use ethernet::SwitchTable;
|
||||||
use ip::RoutingTable;
|
use ip::RoutingTable;
|
||||||
use types::{Error, Mode, Type, Range, Table};
|
use types::{Error, Mode, Type, Range, Table};
|
||||||
use cloud::{TapCloud, TunCloud};
|
use cloud::{TapCloud, TunCloud};
|
||||||
|
#[cfg(feature = "crypto")] pub use crypto::Crypto;
|
||||||
|
#[cfg(not(feature = "crypto"))] pub use no_crypto::Crypto;
|
||||||
|
|
||||||
//TODO: Implement IPv6
|
//TODO: Implement IPv6
|
||||||
//TODO: Encryption
|
|
||||||
//TODO: Call close
|
//TODO: Call close
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,6 +53,7 @@ static USAGE: &'static str = include_str!("usage.txt");
|
||||||
struct Args {
|
struct Args {
|
||||||
flag_type: Type,
|
flag_type: Type,
|
||||||
flag_mode: Mode,
|
flag_mode: Mode,
|
||||||
|
flag_shared_key: Option<String>,
|
||||||
flag_subnet: Vec<String>,
|
flag_subnet: Vec<String>,
|
||||||
flag_device: String,
|
flag_device: String,
|
||||||
flag_listen: String,
|
flag_listen: String,
|
||||||
|
@ -97,16 +101,20 @@ fn main() {
|
||||||
name.hash(&mut s);
|
name.hash(&mut s);
|
||||||
s.finish()
|
s.finish()
|
||||||
});
|
});
|
||||||
|
let crypto = match args.flag_shared_key {
|
||||||
|
Some(key) => Crypto::from_shared_key(&key),
|
||||||
|
None => Crypto::None
|
||||||
|
};
|
||||||
match args.flag_type {
|
match args.flag_type {
|
||||||
Type::Tap => {
|
Type::Tap => {
|
||||||
let mut cloud = TapCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges);
|
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_addr {
|
||||||
cloud.connect(&addr as &str, true).expect("Failed to send");
|
cloud.connect(&addr as &str, true).expect("Failed to send");
|
||||||
}
|
}
|
||||||
cloud.run()
|
cloud.run()
|
||||||
},
|
},
|
||||||
Type::Tun => {
|
Type::Tun => {
|
||||||
let mut cloud = TunCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges);
|
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_addr {
|
||||||
cloud.connect(&addr as &str, true).expect("Failed to send");
|
cloud.connect(&addr as &str, true).expect("Failed to send");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
use super::types::Error;
|
||||||
|
|
||||||
|
pub enum Crypto {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Crypto {
|
||||||
|
pub fn is_secure(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
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<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encrypt(&mut self, mut _buf: &mut [u8]) -> (Vec<u8>, Vec<u8>) {
|
||||||
|
(Vec::new(), Vec::new())
|
||||||
|
}
|
||||||
|
}
|
|
@ -125,4 +125,5 @@ pub enum Error {
|
||||||
WrongNetwork(Option<NetworkId>),
|
WrongNetwork(Option<NetworkId>),
|
||||||
SocketError(&'static str),
|
SocketError(&'static str),
|
||||||
TunTapDevError(&'static str),
|
TunTapDevError(&'static str),
|
||||||
|
CryptoError(&'static str)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use std::{mem, ptr, fmt};
|
use std::{mem, ptr, fmt, slice};
|
||||||
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr};
|
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr};
|
||||||
use std::u16;
|
use std::u16;
|
||||||
|
|
||||||
use super::types::{Error, NetworkId, Range, Address};
|
use super::types::{Error, NetworkId, Range, Address};
|
||||||
use super::util::{as_obj, as_bytes, to_vec};
|
use super::util::{as_obj, as_bytes, to_vec};
|
||||||
|
use super::Crypto;
|
||||||
|
|
||||||
const MAGIC: [u8; 3] = [0x76, 0x70, 0x6e];
|
const MAGIC: [u8; 3] = [0x76, 0x70, 0x6e];
|
||||||
const VERSION: u8 = 0;
|
const VERSION: u8 = 1;
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct TopHeader {
|
struct TopHeader {
|
||||||
|
@ -25,7 +26,7 @@ impl Default for TopHeader {
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Eq)]
|
#[derive(Default, Debug, PartialEq, Eq)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
pub network_id: Option<NetworkId>
|
pub network_id: Option<NetworkId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ impl<'a> fmt::Debug for Message<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode(data: &[u8]) -> Result<(Options, Message), Error> {
|
pub fn decode<'a>(data: &'a mut [u8], crypto: &mut Crypto) -> Result<(Options, Message<'a>), Error> {
|
||||||
if data.len() < mem::size_of::<TopHeader>() {
|
if data.len() < mem::size_of::<TopHeader>() {
|
||||||
return Err(Error::ParseError("Empty message"));
|
return Err(Error::ParseError("Empty message"));
|
||||||
}
|
}
|
||||||
|
@ -81,6 +82,21 @@ pub fn decode(data: &[u8]) -> Result<(Options, Message), Error> {
|
||||||
options.network_id = Some(id);
|
options.network_id = Some(id);
|
||||||
pos += 8;
|
pos += 8;
|
||||||
}
|
}
|
||||||
|
if header.flags & 0x02 > 0 {
|
||||||
|
if data.len() < pos + 40 {
|
||||||
|
return Err(Error::ParseError("Truncated options"));
|
||||||
|
}
|
||||||
|
if !crypto.is_secure() {
|
||||||
|
return Err(Error::CryptoError("Unexpected encrypted data"));
|
||||||
|
}
|
||||||
|
let nonce = &data[pos..pos+8];
|
||||||
|
pos += 8;
|
||||||
|
let hash = &data[pos..pos+32];
|
||||||
|
pos += 32;
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
let msg = match header.msgtype {
|
let msg = match header.msgtype {
|
||||||
0 => Message::Data(&data[pos..]),
|
0 => Message::Data(&data[pos..]),
|
||||||
1 => {
|
1 => {
|
||||||
|
@ -141,7 +157,7 @@ pub fn decode(data: &[u8]) -> Result<(Options, Message), Error> {
|
||||||
Ok((options, msg))
|
Ok((options, msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode(options: &Options, msg: &Message, buf: &mut [u8]) -> usize {
|
pub fn encode(options: &Options, msg: &Message, buf: &mut [u8], crypto: &mut Crypto) -> usize {
|
||||||
assert!(buf.len() >= mem::size_of::<TopHeader>());
|
assert!(buf.len() >= mem::size_of::<TopHeader>());
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
let mut header = TopHeader::default();
|
let mut header = TopHeader::default();
|
||||||
|
@ -154,6 +170,9 @@ pub fn encode(options: &Options, msg: &Message, buf: &mut [u8]) -> usize {
|
||||||
if options.network_id.is_some() {
|
if options.network_id.is_some() {
|
||||||
header.flags |= 0x01;
|
header.flags |= 0x01;
|
||||||
}
|
}
|
||||||
|
if crypto.is_secure() {
|
||||||
|
header.flags |= 0x02
|
||||||
|
}
|
||||||
let header_dat = unsafe { as_bytes(&header) };
|
let header_dat = unsafe { as_bytes(&header) };
|
||||||
unsafe { ptr::copy_nonoverlapping(header_dat.as_ptr(), buf[pos..].as_mut_ptr(), header_dat.len()) };
|
unsafe { ptr::copy_nonoverlapping(header_dat.as_ptr(), buf[pos..].as_mut_ptr(), header_dat.len()) };
|
||||||
pos += header_dat.len();
|
pos += header_dat.len();
|
||||||
|
@ -165,6 +184,16 @@ pub fn encode(options: &Options, msg: &Message, buf: &mut [u8]) -> usize {
|
||||||
}
|
}
|
||||||
pos += 8;
|
pos += 8;
|
||||||
}
|
}
|
||||||
|
let (nonce_pos, hash_pos) = if crypto.is_secure() {
|
||||||
|
let nonce_pos = pos;
|
||||||
|
pos += 8;
|
||||||
|
let hash_pos = pos;
|
||||||
|
pos += 32;
|
||||||
|
(nonce_pos, hash_pos)
|
||||||
|
} else {
|
||||||
|
(0, 0)
|
||||||
|
};
|
||||||
|
let crypto_pos = pos;
|
||||||
match msg {
|
match msg {
|
||||||
&Message::Data(ref data) => {
|
&Message::Data(ref data) => {
|
||||||
assert!(buf.len() >= pos + data.len());
|
assert!(buf.len() >= pos + data.len());
|
||||||
|
@ -218,20 +247,30 @@ pub fn encode(options: &Options, msg: &Message, buf: &mut [u8]) -> usize {
|
||||||
&Message::Close => {
|
&Message::Close => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if crypto.is_secure() {
|
||||||
|
let (nonce, hash) = crypto.encrypt(&mut buf[crypto_pos..pos]);
|
||||||
|
assert_eq!(nonce.len(), 8);
|
||||||
|
assert_eq!(hash.len(), 32);
|
||||||
|
unsafe {
|
||||||
|
ptr::copy_nonoverlapping(nonce.as_ptr(), buf[nonce_pos..].as_mut_ptr(), 8);
|
||||||
|
ptr::copy_nonoverlapping(hash.as_ptr(), buf[hash_pos..].as_mut_ptr(), 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
pos
|
pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encode_message_packet() {
|
fn encode_message_packet() {
|
||||||
let options = Options::default();
|
let mut options = Options::default();
|
||||||
|
let mut crypto = Crypto::None;
|
||||||
let payload = [1,2,3,4,5];
|
let payload = [1,2,3,4,5];
|
||||||
let msg = Message::Data(&payload);
|
let msg = Message::Data(&payload);
|
||||||
let mut buf = [0; 1024];
|
let mut buf = [0; 1024];
|
||||||
let size = encode(&options, &msg, &mut buf[..]);
|
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
|
||||||
assert_eq!(size, 13);
|
assert_eq!(size, 13);
|
||||||
assert_eq!(&buf[..8], &[118,112,110,0,0,0,0,0]);
|
assert_eq!(&buf[..8], &[118,112,110,0,0,0,0,0]);
|
||||||
let (options2, msg2) = decode(&buf[..size]).unwrap();
|
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
|
||||||
assert_eq!(options, options2);
|
assert_eq!(options, options2);
|
||||||
assert_eq!(msg, msg2);
|
assert_eq!(msg, msg2);
|
||||||
}
|
}
|
||||||
|
@ -239,13 +278,14 @@ fn encode_message_packet() {
|
||||||
#[test]
|
#[test]
|
||||||
fn encode_message_peers() {
|
fn encode_message_peers() {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
let options = Options::default();
|
let mut options = Options::default();
|
||||||
|
let mut crypto = Crypto::None;
|
||||||
let msg = Message::Peers(vec![SocketAddr::from_str("1.2.3.4:123").unwrap(), SocketAddr::from_str("5.6.7.8:12345").unwrap()]);
|
let msg = Message::Peers(vec![SocketAddr::from_str("1.2.3.4:123").unwrap(), SocketAddr::from_str("5.6.7.8:12345").unwrap()]);
|
||||||
let mut buf = [0; 1024];
|
let mut buf = [0; 1024];
|
||||||
let size = encode(&options, &msg, &mut buf[..]);
|
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
|
||||||
assert_eq!(size, 22);
|
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,0,0,0,0,1,2,1,2,3,4,0,123,5,6,7,8,48,57,0]);
|
||||||
let (options2, msg2) = decode(&buf[..size]).unwrap();
|
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
|
||||||
assert_eq!(options, options2);
|
assert_eq!(options, options2);
|
||||||
assert_eq!(msg, msg2);
|
assert_eq!(msg, msg2);
|
||||||
}
|
}
|
||||||
|
@ -254,39 +294,42 @@ fn encode_message_peers() {
|
||||||
fn encode_option_network_id() {
|
fn encode_option_network_id() {
|
||||||
let mut options = Options::default();
|
let mut options = Options::default();
|
||||||
options.network_id = Some(134);
|
options.network_id = Some(134);
|
||||||
|
let mut crypto = Crypto::None;
|
||||||
let msg = Message::Close;
|
let msg = Message::Close;
|
||||||
let mut buf = [0; 1024];
|
let mut buf = [0; 1024];
|
||||||
let size = encode(&options, &msg, &mut buf[..]);
|
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
|
||||||
assert_eq!(size, 16);
|
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,0,0,0,1,3,0,0,0,0,0,0,0,134]);
|
||||||
let (options2, msg2) = decode(&buf[..size]).unwrap();
|
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
|
||||||
assert_eq!(options, options2);
|
assert_eq!(options, options2);
|
||||||
assert_eq!(msg, msg2);
|
assert_eq!(msg, msg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encode_message_init() {
|
fn encode_message_init() {
|
||||||
let options = Options::default();
|
let mut options = Options::default();
|
||||||
|
let mut crypto = Crypto::None;
|
||||||
let addrs = vec![];
|
let addrs = vec![];
|
||||||
let msg = Message::Init(addrs);
|
let msg = Message::Init(addrs);
|
||||||
let mut buf = [0; 1024];
|
let mut buf = [0; 1024];
|
||||||
let size = encode(&options, &msg, &mut buf[..]);
|
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
|
||||||
assert_eq!(size, 9);
|
assert_eq!(size, 9);
|
||||||
assert_eq!(&buf[..size], &[118,112,110,0,0,0,0,2,0]);
|
assert_eq!(&buf[..size], &[118,112,110,0,0,0,0,2,0]);
|
||||||
let (options2, msg2) = decode(&buf[..size]).unwrap();
|
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
|
||||||
assert_eq!(options, options2);
|
assert_eq!(options, options2);
|
||||||
assert_eq!(msg, msg2);
|
assert_eq!(msg, msg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encode_message_close() {
|
fn encode_message_close() {
|
||||||
let options = Options::default();
|
let mut options = Options::default();
|
||||||
|
let mut crypto = Crypto::None;
|
||||||
let msg = Message::Close;
|
let msg = Message::Close;
|
||||||
let mut buf = [0; 1024];
|
let mut buf = [0; 1024];
|
||||||
let size = encode(&options, &msg, &mut buf[..]);
|
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
|
||||||
assert_eq!(size, 8);
|
assert_eq!(size, 8);
|
||||||
assert_eq!(&buf[..size], &[118,112,110,0,0,0,0,3]);
|
assert_eq!(&buf[..size], &[118,112,110,0,0,0,0,3]);
|
||||||
let (options2, msg2) = decode(&buf[..size]).unwrap();
|
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
|
||||||
assert_eq!(options, options2);
|
assert_eq!(options, options2);
|
||||||
assert_eq!(msg, msg2);
|
assert_eq!(msg, msg2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ Options:
|
||||||
-c <addr>, --connect <addr> Address of a peer to connect to.
|
-c <addr>, --connect <addr> Address of a peer to connect to.
|
||||||
--subnet <subnet> The local subnets to use.
|
--subnet <subnet> The local subnets to use.
|
||||||
--network-id <network_id> Optional token that identifies the network.
|
--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]
|
--peer-timeout <peer_timeout> Peer timeout in seconds. [default: 1800]
|
||||||
--dst-timeout <dst_timeout> Switch table entry timeout in seconds.
|
--dst-timeout <dst_timeout> Switch table entry timeout in seconds.
|
||||||
[default: 300]
|
[default: 300]
|
||||||
|
|
21
vpncloud.md
21
vpncloud.md
|
@ -44,6 +44,11 @@ vpncloud(1) -- Peer-to-peer VPN
|
||||||
MAC address. The prefix length is the number of significant front bits that
|
MAC address. The prefix length is the number of significant front bits that
|
||||||
distinguish the subnet from other subnets. Example: `10.1.1.0/24`.
|
distinguish the subnet from other subnets. Example: `10.1.1.0/24`.
|
||||||
|
|
||||||
|
* `--shared-key <shared_key>`:
|
||||||
|
|
||||||
|
An optional shared key to encrypt the VPN data. If this option is not set,
|
||||||
|
the traffic will be sent unencrypted.
|
||||||
|
|
||||||
* `--network-id <network_id>`:
|
* `--network-id <network_id>`:
|
||||||
|
|
||||||
An optional token that identifies the network and helps to distinguish it
|
An optional token that identifies the network and helps to distinguish it
|
||||||
|
@ -177,11 +182,17 @@ example.
|
||||||
it can conflict with DHCP servers of the local network and can have severe
|
it can conflict with DHCP servers of the local network and can have severe
|
||||||
side effects.
|
side effects.
|
||||||
|
|
||||||
- VpnCloud is not designed to be secure. It encapsulates the network data but
|
- VpnCloud is not designed for high security use cases. Although the used crypto
|
||||||
it (currently) does not encrypt and authenticate it. Attackers with read
|
primitives are expected to be very secure, their application has not been
|
||||||
access to the UDP stream can read the whole traffic including any unencrypted
|
reviewed.
|
||||||
passwords in the payload. Attackers with write access to the UDP stream can
|
The shared key is hashed using *ScryptSalsa208Sha256* to derive a key,
|
||||||
manipulate or suppress the whole traffic and even send data on their own.
|
which is used to encrypt the payload of messages using *ChaCha20*. The
|
||||||
|
authenticity of messages is verified using *HmacSha512256* hashes.
|
||||||
|
This method only protects the contents of the message (payload, peer list,
|
||||||
|
etc.) but not the header of each message.
|
||||||
|
Also, this method does only protect against attacks on single messages but not
|
||||||
|
on attacks that manipulate the message series itself (i.e. suppress messages,
|
||||||
|
reorder them, and duplicate them).
|
||||||
|
|
||||||
|
|
||||||
## NETWORK PROTOCOL
|
## NETWORK PROTOCOL
|
||||||
|
|
Loading…
Reference in New Issue