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"
|
||||
log = "0.3"
|
||||
epoll = "0.2"
|
||||
sodiumoxide = {version = "0.0.9", optional = true}
|
||||
|
||||
[build-dependencies]
|
||||
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::udpmessage::{encode, decode, Options, Message};
|
||||
use super::{ethernet, ip};
|
||||
use super::Crypto;
|
||||
|
||||
struct PeerList {
|
||||
timeout: Duration,
|
||||
|
@ -92,7 +93,8 @@ pub struct GenericCloud<P: Protocol> {
|
|||
table: Box<Table>,
|
||||
socket: UdpSocket,
|
||||
device: Device,
|
||||
network_id: Option<NetworkId>,
|
||||
options: Options,
|
||||
crypto: Crypto,
|
||||
next_peerlist: SteadyTime,
|
||||
update_freq: Duration,
|
||||
buffer_out: [u8; 64*1024],
|
||||
|
@ -102,11 +104,14 @@ pub struct GenericCloud<P: Protocol> {
|
|||
|
||||
impl<P: Protocol> GenericCloud<P> {
|
||||
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) {
|
||||
Ok(socket) => socket,
|
||||
_ => panic!("Failed to open socket")
|
||||
};
|
||||
let mut options = Options::default();
|
||||
options.network_id = network_id;
|
||||
GenericCloud{
|
||||
peers: PeerList::new(peer_timeout),
|
||||
addresses: addresses,
|
||||
|
@ -116,7 +121,8 @@ impl<P: Protocol> GenericCloud<P> {
|
|||
table: table,
|
||||
socket: socket,
|
||||
device: device,
|
||||
network_id: network_id,
|
||||
options: options,
|
||||
crypto: crypto,
|
||||
next_peerlist: SteadyTime::now(),
|
||||
update_freq: peer_timeout/2,
|
||||
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> {
|
||||
debug!("Sending {:?} to {}", msg, addr);
|
||||
let mut options = Options::default();
|
||||
options.network_id = self.network_id;
|
||||
let size = encode(&options, msg, &mut self.buffer_out);
|
||||
let size = encode(&mut self.options, msg, &mut self.buffer_out, &mut self.crypto);
|
||||
match self.socket.send_to(&self.buffer_out[..size], addr) {
|
||||
Ok(written) if written == size => Ok(()),
|
||||
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> {
|
||||
if let Some(id) = self.network_id {
|
||||
if let Some(id) = self.options.network_id {
|
||||
if options.network_id != Some(id) {
|
||||
info!("Ignoring message from {} with wrong token {:?}", peer, 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 {
|
||||
&0 => match self.socket.recv_from(&mut buffer) {
|
||||
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(_) => (),
|
||||
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 rustc_serialize;
|
||||
extern crate epoll;
|
||||
#[cfg(feature = "crypto")] extern crate sodiumoxide;
|
||||
|
||||
mod util;
|
||||
mod types;
|
||||
#[cfg(feature = "crypto")] mod crypto;
|
||||
#[cfg(not(feature = "crypto"))] mod no_crypto;
|
||||
mod udpmessage;
|
||||
mod ethernet;
|
||||
mod ip;
|
||||
|
@ -23,10 +26,10 @@ use ethernet::SwitchTable;
|
|||
use ip::RoutingTable;
|
||||
use types::{Error, Mode, Type, Range, Table};
|
||||
use cloud::{TapCloud, TunCloud};
|
||||
|
||||
#[cfg(feature = "crypto")] pub use crypto::Crypto;
|
||||
#[cfg(not(feature = "crypto"))] pub use no_crypto::Crypto;
|
||||
|
||||
//TODO: Implement IPv6
|
||||
//TODO: Encryption
|
||||
//TODO: Call close
|
||||
|
||||
|
||||
|
@ -50,6 +53,7 @@ static USAGE: &'static str = include_str!("usage.txt");
|
|||
struct Args {
|
||||
flag_type: Type,
|
||||
flag_mode: Mode,
|
||||
flag_shared_key: Option<String>,
|
||||
flag_subnet: Vec<String>,
|
||||
flag_device: String,
|
||||
flag_listen: String,
|
||||
|
@ -97,16 +101,20 @@ fn main() {
|
|||
name.hash(&mut s);
|
||||
s.finish()
|
||||
});
|
||||
let crypto = match args.flag_shared_key {
|
||||
Some(key) => Crypto::from_shared_key(&key),
|
||||
None => Crypto::None
|
||||
};
|
||||
match args.flag_type {
|
||||
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 {
|
||||
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);
|
||||
let mut cloud = TunCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto);
|
||||
for addr in args.flag_addr {
|
||||
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>),
|
||||
SocketError(&'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::u16;
|
||||
|
||||
use super::types::{Error, NetworkId, Range, Address};
|
||||
use super::util::{as_obj, as_bytes, to_vec};
|
||||
use super::Crypto;
|
||||
|
||||
const MAGIC: [u8; 3] = [0x76, 0x70, 0x6e];
|
||||
const VERSION: u8 = 0;
|
||||
const VERSION: u8 = 1;
|
||||
|
||||
#[repr(packed)]
|
||||
struct TopHeader {
|
||||
|
@ -25,7 +26,7 @@ impl Default for TopHeader {
|
|||
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
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>() {
|
||||
return Err(Error::ParseError("Empty message"));
|
||||
}
|
||||
|
@ -81,6 +82,21 @@ pub fn decode(data: &[u8]) -> Result<(Options, Message), Error> {
|
|||
options.network_id = Some(id);
|
||||
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 {
|
||||
0 => Message::Data(&data[pos..]),
|
||||
1 => {
|
||||
|
@ -141,7 +157,7 @@ pub fn decode(data: &[u8]) -> Result<(Options, Message), Error> {
|
|||
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>());
|
||||
let mut pos = 0;
|
||||
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() {
|
||||
header.flags |= 0x01;
|
||||
}
|
||||
if crypto.is_secure() {
|
||||
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()) };
|
||||
pos += header_dat.len();
|
||||
|
@ -165,6 +184,16 @@ pub fn encode(options: &Options, msg: &Message, buf: &mut [u8]) -> usize {
|
|||
}
|
||||
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 {
|
||||
&Message::Data(ref data) => {
|
||||
assert!(buf.len() >= pos + data.len());
|
||||
|
@ -218,20 +247,30 @@ pub fn encode(options: &Options, msg: &Message, buf: &mut [u8]) -> usize {
|
|||
&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
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
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 msg = Message::Data(&payload);
|
||||
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!(&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!(msg, msg2);
|
||||
}
|
||||
|
@ -239,13 +278,14 @@ fn encode_message_packet() {
|
|||
#[test]
|
||||
fn encode_message_peers() {
|
||||
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 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!(&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!(msg, msg2);
|
||||
}
|
||||
|
@ -254,39 +294,42 @@ fn encode_message_peers() {
|
|||
fn encode_option_network_id() {
|
||||
let mut options = Options::default();
|
||||
options.network_id = Some(134);
|
||||
let mut crypto = Crypto::None;
|
||||
let msg = Message::Close;
|
||||
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!(&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!(msg, msg2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_message_init() {
|
||||
let options = Options::default();
|
||||
let mut options = Options::default();
|
||||
let mut crypto = Crypto::None;
|
||||
let addrs = vec![];
|
||||
let msg = Message::Init(addrs);
|
||||
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!(&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!(msg, msg2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_message_close() {
|
||||
let options = Options::default();
|
||||
let mut options = Options::default();
|
||||
let mut crypto = Crypto::None;
|
||||
let msg = Message::Close;
|
||||
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!(&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!(msg, msg2);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ Options:
|
|||
-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.
|
||||
[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
|
||||
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>`:
|
||||
|
||||
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
|
||||
side effects.
|
||||
|
||||
- VpnCloud is not designed to be secure. It encapsulates the network data but
|
||||
it (currently) does not encrypt and authenticate it. Attackers with read
|
||||
access to the UDP stream can read the whole traffic including any unencrypted
|
||||
passwords in the payload. Attackers with write access to the UDP stream can
|
||||
manipulate or suppress the whole traffic and even send data on their own.
|
||||
- VpnCloud is not designed for high security use cases. Although the used crypto
|
||||
primitives are expected to be very secure, their application has not been
|
||||
reviewed.
|
||||
The shared key is hashed using *ScryptSalsa208Sha256* to derive a key,
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue