Crypto feature

This commit is contained in:
Dennis Schwerdel 2015-11-23 15:40:04 +01:00
parent 83574b9b47
commit 46728ce362
9 changed files with 205 additions and 36 deletions

View File

@ -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"]

View File

@ -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)
} }

73
src/crypto.rs Normal file
View File

@ -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())
}
}
}
}

View File

@ -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");
} }

23
src/no_crypto.rs Normal file
View File

@ -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())
}
}

View File

@ -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)
} }

View File

@ -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);
} }

View File

@ -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]

View File

@ -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