2021-02-08 09:11:20 +00:00
|
|
|
// VpnCloud - Peer-to-Peer VPN
|
|
|
|
// Copyright (C) 2015-2021 Dennis Schwerdel
|
|
|
|
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
|
|
|
|
2021-01-28 21:54:29 +00:00
|
|
|
// This module implements a 3-way handshake to initialize an authenticated and encrypted connection.
|
|
|
|
//
|
|
|
|
// The handshake assumes that each node has a asymmetric Curve 25519 key pair as well as a list of trusted public keys
|
|
|
|
// and a set of supported crypto algorithms as well as the expected speed when using them. If successful, the handshake
|
|
|
|
// will negotiate a crypto algorithm to use and a common ephemeral symmetric key and exchange a given payload between
|
|
|
|
// the nodes.
|
|
|
|
//
|
|
|
|
// The handshake consists of 3 stages, "ping", "pong" and "peng". In the following description, the node that initiates
|
|
|
|
// the connection is named "A" and the other node is named "B". Since a lot of things are going on in parallel in the
|
|
|
|
// handshake, those aspects are described separately in the following paragraphs.
|
|
|
|
//
|
|
|
|
// Every message contains the node id of the sender. If a node receives a message with its own node id, it just ignores
|
|
|
|
// it and closes the connection. This is the way nodes avoid to connect to themselves as it is not trivial for a node
|
|
|
|
// to know its own addresses (especially in the case of NAT).
|
|
|
|
//
|
|
|
|
// All initialization messages are signed by the asymmetric key of the sender. Also the messages indicate the public
|
|
|
|
// key being used, so the receiver can use the correct public key to verify the signature. The public key itself is not
|
|
|
|
// attached to the message for privacy reasons (the public key is stable over multiple restarts while the node id is
|
|
|
|
// only valid for a single run). Instead, a 2 byte salt value as well as the last 2 bytes of the salted sha 2 hash of
|
|
|
|
// the public key are used to identify the public key. This way, a receiver that trusts this public key can identify
|
|
|
|
// it but a random observer can't. If the public key is unknown or the signature can't be verified, the message is
|
|
|
|
// ignored.
|
|
|
|
//
|
|
|
|
// Every message contains a byte that specifies the stage (ping = 1, pong = 2, peng = 3). If a message with an
|
|
|
|
// unexpected stage is received, it is ignored and the last message that has been sent is repeated. There is only one
|
|
|
|
// exception to this rule: if a "pong" message is expected, but a "ping" message is received instead AND the node id of
|
|
|
|
// the sender is greater than the node id of the receiver, the receiving node will reset its state and assume the role
|
|
|
|
// of a receiver of the initialization (i.e. "B"). This is used to "negotiate" the roles A and B when both nodes
|
|
|
|
// initiate the connection in parallel and think they are A.
|
|
|
|
//
|
|
|
|
// Upon connection creation, both nodes create a random ephemeral ECDH key pair and exchange the public keys in the
|
|
|
|
// ping and pong messages. A sends the ping message to B containing A's public key and B replies with a pong message
|
|
|
|
// containing B's public key. That means, that after receiving the ping message B can calculate the shared key material
|
|
|
|
// and after receiving the pong message A can calculate the shared key material.
|
|
|
|
//
|
|
|
|
// The ping message and the pong message contain a set of supported crypto algorithms together with the estimated
|
|
|
|
// speeds of the algorithms. When B receives a ping message, or A receives a pong message, it can combine this
|
|
|
|
// information with its own algorithm list and select the algorithm with the best expected speed for the crypto core.
|
|
|
|
//
|
|
|
|
// The pong and peng message contain the payload that the nodes want to exchange in the initialization phase apart from
|
|
|
|
// the cryptographic initialization. This payload is encoded according to the application and encrypted using the key
|
|
|
|
// material and the crypto algorithm that have been negotiated via the ping and pong messages. The pong message,
|
|
|
|
// therefore contains information to set up symmetric encryption as well as a part that is already encrypted.
|
|
|
|
//
|
|
|
|
// The handshake ends for A after sending the peng message and for B after receiving this message. At this time both
|
|
|
|
// nodes initialize the connection using the payload and enter normal operation. The negotiated crypto core is used for
|
|
|
|
// future communication and the key rotation is started. Since the peng message can be lost, A needs to keep the
|
|
|
|
// initialization state in order to repeat a lost peng message. After one second, A removes that state.
|
|
|
|
//
|
|
|
|
// Once every second, both nodes check whether they have already finished the initialization. If not, they repeat their
|
|
|
|
// last message. After 5 seconds, the initialization is aborted as failed.
|
2020-09-24 17:48:13 +00:00
|
|
|
|
|
|
|
use super::{
|
|
|
|
core::{CryptoCore, EXTRA_LEN},
|
2021-04-06 10:28:31 +00:00
|
|
|
Algorithms, EcdhPrivateKey, EcdhPublicKey, Ed25519PublicKey, Payload,
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
2021-02-08 16:30:58 +00:00
|
|
|
use crate::{error::Error, types::NodeId, util::MsgBuffer};
|
2020-09-24 17:48:13 +00:00
|
|
|
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
|
|
|
|
use ring::{
|
|
|
|
aead::{Algorithm, LessSafeKey, UnboundKey, AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305},
|
|
|
|
agreement::{agree_ephemeral, X25519},
|
|
|
|
digest,
|
|
|
|
rand::{SecureRandom, SystemRandom},
|
2021-04-06 10:28:31 +00:00
|
|
|
signature::{self, Ed25519KeyPair, KeyPair, ED25519, ED25519_PUBLIC_KEY_LEN},
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
|
|
|
use smallvec::{smallvec, SmallVec};
|
|
|
|
use std::{
|
|
|
|
cmp, f32,
|
|
|
|
fmt::Debug,
|
|
|
|
io::{self, Cursor, Read, Write},
|
2021-04-06 10:28:31 +00:00
|
|
|
sync::Arc,
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
pub const STAGE_PING: u8 = 1;
|
|
|
|
pub const STAGE_PONG: u8 = 2;
|
|
|
|
pub const STAGE_PENG: u8 = 3;
|
|
|
|
pub const WAITING_TO_CLOSE: u8 = 4;
|
|
|
|
pub const CLOSING: u8 = 5;
|
|
|
|
|
2020-11-02 19:44:05 +00:00
|
|
|
pub const MAX_FAILED_RETRIES: usize = 120;
|
|
|
|
|
2020-09-28 10:50:08 +00:00
|
|
|
pub const SALTED_NODE_ID_HASH_LEN: usize = 20;
|
|
|
|
pub type SaltedNodeIdHash = [u8; SALTED_NODE_ID_HASH_LEN];
|
2020-09-24 17:48:13 +00:00
|
|
|
|
|
|
|
#[allow(clippy::large_enum_variant)]
|
|
|
|
pub enum InitMsg {
|
2020-09-28 10:50:08 +00:00
|
|
|
Ping {
|
|
|
|
salted_node_id_hash: SaltedNodeIdHash,
|
|
|
|
ecdh_public_key: EcdhPublicKey,
|
2021-04-06 10:28:31 +00:00
|
|
|
algorithms: Algorithms,
|
2020-09-28 10:50:08 +00:00
|
|
|
},
|
|
|
|
Pong {
|
|
|
|
salted_node_id_hash: SaltedNodeIdHash,
|
|
|
|
ecdh_public_key: EcdhPublicKey,
|
|
|
|
algorithms: Algorithms,
|
2021-04-06 10:28:31 +00:00
|
|
|
encrypted_payload: MsgBuffer,
|
2020-09-28 10:50:08 +00:00
|
|
|
},
|
|
|
|
Peng {
|
|
|
|
salted_node_id_hash: SaltedNodeIdHash,
|
2021-04-06 10:28:31 +00:00
|
|
|
encrypted_payload: MsgBuffer,
|
|
|
|
},
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl InitMsg {
|
|
|
|
const PART_ALGORITHMS: u8 = 4;
|
|
|
|
const PART_ECDH_PUBLIC_KEY: u8 = 3;
|
|
|
|
const PART_END: u8 = 0;
|
|
|
|
const PART_PAYLOAD: u8 = 5;
|
2020-09-28 10:50:08 +00:00
|
|
|
const PART_SALTED_NODE_ID_HASH: u8 = 2;
|
2020-09-24 17:48:13 +00:00
|
|
|
const PART_STAGE: u8 = 1;
|
|
|
|
|
|
|
|
fn stage(&self) -> u8 {
|
|
|
|
match self {
|
|
|
|
InitMsg::Ping { .. } => STAGE_PING,
|
|
|
|
InitMsg::Pong { .. } => STAGE_PONG,
|
2021-04-06 10:28:31 +00:00
|
|
|
InitMsg::Peng { .. } => STAGE_PENG,
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-28 10:50:08 +00:00
|
|
|
fn salted_node_id_hash(&self) -> &SaltedNodeIdHash {
|
2020-09-24 17:48:13 +00:00
|
|
|
match self {
|
2020-09-28 10:50:08 +00:00
|
|
|
InitMsg::Ping { salted_node_id_hash, .. }
|
|
|
|
| InitMsg::Pong { salted_node_id_hash, .. }
|
2021-04-06 10:28:31 +00:00
|
|
|
| InitMsg::Peng { salted_node_id_hash, .. } => salted_node_id_hash,
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn calculate_hash(key: &Ed25519PublicKey, salt: &[u8; 4]) -> [u8; 4] {
|
|
|
|
let mut data = [0; ED25519_PUBLIC_KEY_LEN + 4];
|
|
|
|
data[..ED25519_PUBLIC_KEY_LEN].clone_from_slice(key);
|
|
|
|
data[ED25519_PUBLIC_KEY_LEN..].clone_from_slice(salt);
|
|
|
|
let hash = digest::digest(&digest::SHA256, &data);
|
|
|
|
let mut short_hash = [0; 4];
|
|
|
|
short_hash.clone_from_slice(&hash.as_ref()[..4]);
|
|
|
|
short_hash
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_from(buffer: &[u8], trusted_keys: &[Ed25519PublicKey]) -> Result<(Self, Ed25519PublicKey), Error> {
|
|
|
|
let mut r = Cursor::new(buffer);
|
|
|
|
|
|
|
|
let mut public_key_salt = [0; 4];
|
|
|
|
r.read_exact(&mut public_key_salt).map_err(|_| Error::Parse("Init message too short"))?;
|
|
|
|
let mut public_key_hash = [0; 4];
|
|
|
|
r.read_exact(&mut public_key_hash).map_err(|_| Error::Parse("Init message too short"))?;
|
|
|
|
let mut public_key_data = [0; ED25519_PUBLIC_KEY_LEN];
|
|
|
|
let mut found_key = false;
|
|
|
|
for tk in trusted_keys {
|
|
|
|
if Self::calculate_hash(tk, &public_key_salt) == public_key_hash {
|
|
|
|
public_key_data.clone_from_slice(tk);
|
|
|
|
found_key = true;
|
2021-04-06 10:28:31 +00:00
|
|
|
break;
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found_key {
|
2021-04-06 10:28:31 +00:00
|
|
|
return Err(Error::Crypto("untrusted peer"));
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut stage = None;
|
2020-09-28 10:50:08 +00:00
|
|
|
let mut salted_node_id_hash = None;
|
2020-09-24 17:48:13 +00:00
|
|
|
let mut ecdh_public_key = None;
|
|
|
|
let mut encrypted_payload = None;
|
|
|
|
let mut algorithms = None;
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let field = r.read_u8().map_err(|_| Error::Parse("Init message too short"))?;
|
|
|
|
if field == Self::PART_END {
|
2021-04-06 10:28:31 +00:00
|
|
|
break;
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
let field_len = r.read_u16::<NetworkEndian>().map_err(|_| Error::Parse("Init message too short"))? as usize;
|
|
|
|
match field {
|
|
|
|
Self::PART_STAGE => {
|
|
|
|
if field_len != 1 {
|
2021-04-06 10:28:31 +00:00
|
|
|
return Err(Error::CryptoInit("Invalid size for stage field"));
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
stage = Some(r.read_u8().map_err(|_| Error::Parse("Init message too short"))?)
|
|
|
|
}
|
2020-09-28 10:50:08 +00:00
|
|
|
Self::PART_SALTED_NODE_ID_HASH => {
|
|
|
|
if field_len != SALTED_NODE_ID_HASH_LEN {
|
2021-04-06 10:28:31 +00:00
|
|
|
return Err(Error::CryptoInit("Invalid size for salted node id hash field"));
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
2020-09-28 10:50:08 +00:00
|
|
|
let mut id = [0; SALTED_NODE_ID_HASH_LEN];
|
2020-09-24 17:48:13 +00:00
|
|
|
r.read_exact(&mut id).map_err(|_| Error::Parse("Init message too short"))?;
|
2020-09-28 10:50:08 +00:00
|
|
|
salted_node_id_hash = Some(id)
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
Self::PART_ECDH_PUBLIC_KEY => {
|
|
|
|
let mut pub_key_data = smallvec![0; field_len];
|
|
|
|
r.read_exact(&mut pub_key_data).map_err(|_| Error::Parse("Init message too short"))?;
|
|
|
|
ecdh_public_key = Some(EcdhPublicKey::new(&X25519, pub_key_data));
|
|
|
|
}
|
|
|
|
Self::PART_PAYLOAD => {
|
|
|
|
let mut payload = MsgBuffer::new(0);
|
|
|
|
payload.set_length(field_len);
|
|
|
|
r.read_exact(payload.message_mut()).map_err(|_| Error::Parse("Init message too short"))?;
|
|
|
|
encrypted_payload = Some(payload);
|
|
|
|
}
|
|
|
|
Self::PART_ALGORITHMS => {
|
|
|
|
let count = field_len / 5;
|
|
|
|
let mut algos = SmallVec::with_capacity(count);
|
|
|
|
let mut allow_unencrypted = false;
|
|
|
|
for _ in 0..count {
|
|
|
|
let algo = match r.read_u8().map_err(|_| Error::Parse("Init message too short"))? {
|
|
|
|
0 => {
|
|
|
|
allow_unencrypted = true;
|
|
|
|
None
|
|
|
|
}
|
|
|
|
1 => Some(&AES_128_GCM),
|
|
|
|
2 => Some(&AES_256_GCM),
|
|
|
|
3 => Some(&CHACHA20_POLY1305),
|
2021-04-06 10:28:31 +00:00
|
|
|
_ => None,
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
|
|
|
let speed =
|
|
|
|
r.read_f32::<NetworkEndian>().map_err(|_| Error::Parse("Init message too short"))?;
|
|
|
|
if let Some(algo) = algo {
|
|
|
|
algos.push((algo, speed));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
algorithms = Some(Algorithms { algorithm_speeds: algos, allow_unencrypted });
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
let mut data = vec![0; field_len];
|
|
|
|
r.read_exact(&mut data).map_err(|_| Error::Parse("Init message too short"))?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let pos = r.position() as usize;
|
|
|
|
|
|
|
|
let signature_len = r.read_u8().map_err(|_| Error::Parse("Init message too short"))? as usize;
|
2020-11-03 17:49:35 +00:00
|
|
|
let mut signature: SmallVec<[u8; 32]> = smallvec![0; signature_len];
|
2020-09-24 17:48:13 +00:00
|
|
|
r.read_exact(&mut signature).map_err(|_| Error::Parse("Init message too short"))?;
|
|
|
|
|
|
|
|
let signed_data = &r.into_inner()[0..pos];
|
|
|
|
let public_key = signature::UnparsedPublicKey::new(&ED25519, &public_key_data);
|
|
|
|
if public_key.verify(&signed_data, &signature).is_err() {
|
2021-04-06 10:28:31 +00:00
|
|
|
return Err(Error::Crypto("invalid signature"));
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let stage = match stage {
|
|
|
|
Some(val) => val,
|
2021-04-06 10:28:31 +00:00
|
|
|
None => return Err(Error::CryptoInit("Init message without stage")),
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
2020-09-28 10:50:08 +00:00
|
|
|
let salted_node_id_hash = match salted_node_id_hash {
|
2020-09-24 17:48:13 +00:00
|
|
|
Some(val) => val,
|
2021-04-06 10:28:31 +00:00
|
|
|
None => return Err(Error::CryptoInit("Init message without node id")),
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let msg = match stage {
|
|
|
|
STAGE_PING => {
|
|
|
|
let ecdh_public_key = match ecdh_public_key {
|
|
|
|
Some(val) => val,
|
2021-04-06 10:28:31 +00:00
|
|
|
None => return Err(Error::CryptoInit("Init message without ecdh public key")),
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
|
|
|
let algorithms = match algorithms {
|
|
|
|
Some(val) => val,
|
2021-04-06 10:28:31 +00:00
|
|
|
None => return Err(Error::CryptoInit("Init message without algorithms")),
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
2020-09-28 10:50:08 +00:00
|
|
|
Self::Ping { salted_node_id_hash, ecdh_public_key, algorithms }
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
STAGE_PONG => {
|
|
|
|
let ecdh_public_key = match ecdh_public_key {
|
|
|
|
Some(val) => val,
|
2021-04-06 10:28:31 +00:00
|
|
|
None => return Err(Error::CryptoInit("Init message without ecdh public key")),
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
|
|
|
let algorithms = match algorithms {
|
|
|
|
Some(val) => val,
|
2021-04-06 10:28:31 +00:00
|
|
|
None => return Err(Error::CryptoInit("Init message without algorithms")),
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
|
|
|
let encrypted_payload = match encrypted_payload {
|
|
|
|
Some(val) => val,
|
2021-04-06 10:28:31 +00:00
|
|
|
None => return Err(Error::CryptoInit("Init message without payload")),
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
2020-09-28 10:50:08 +00:00
|
|
|
Self::Pong { salted_node_id_hash, ecdh_public_key, algorithms, encrypted_payload }
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
STAGE_PENG => {
|
|
|
|
let encrypted_payload = match encrypted_payload {
|
|
|
|
Some(val) => val,
|
2021-04-06 10:28:31 +00:00
|
|
|
None => return Err(Error::CryptoInit("Init message without payload")),
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
2020-09-28 10:50:08 +00:00
|
|
|
Self::Peng { salted_node_id_hash, encrypted_payload }
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
2021-04-06 10:28:31 +00:00
|
|
|
_ => return Err(Error::CryptoInit("Invalid stage")),
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Ok((msg, public_key_data))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_to(&self, buffer: &mut [u8], key: &Ed25519KeyPair) -> Result<usize, io::Error> {
|
|
|
|
let mut w = Cursor::new(buffer);
|
|
|
|
|
|
|
|
let rand = SystemRandom::new();
|
|
|
|
let mut salt = [0; 4];
|
|
|
|
rand.fill(&mut salt).unwrap();
|
|
|
|
let mut public_key = [0; ED25519_PUBLIC_KEY_LEN];
|
|
|
|
public_key.clone_from_slice(key.public_key().as_ref());
|
|
|
|
let hash = Self::calculate_hash(&public_key, &salt);
|
|
|
|
w.write_all(&salt)?;
|
|
|
|
w.write_all(&hash)?;
|
|
|
|
|
|
|
|
w.write_u8(Self::PART_STAGE)?;
|
|
|
|
w.write_u16::<NetworkEndian>(1)?;
|
|
|
|
w.write_u8(self.stage())?;
|
|
|
|
|
|
|
|
match &self {
|
2020-09-28 10:50:08 +00:00
|
|
|
Self::Ping { salted_node_id_hash, .. }
|
|
|
|
| Self::Pong { salted_node_id_hash, .. }
|
|
|
|
| Self::Peng { salted_node_id_hash, .. } => {
|
|
|
|
w.write_u8(Self::PART_SALTED_NODE_ID_HASH)?;
|
|
|
|
w.write_u16::<NetworkEndian>(SALTED_NODE_ID_HASH_LEN as u16)?;
|
|
|
|
w.write_all(salted_node_id_hash)?;
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match &self {
|
|
|
|
Self::Ping { ecdh_public_key, .. } | Self::Pong { ecdh_public_key, .. } => {
|
|
|
|
w.write_u8(Self::PART_ECDH_PUBLIC_KEY)?;
|
|
|
|
let key_bytes = ecdh_public_key.bytes();
|
|
|
|
w.write_u16::<NetworkEndian>(key_bytes.len() as u16)?;
|
|
|
|
w.write_all(&key_bytes)?;
|
|
|
|
}
|
2021-04-06 10:28:31 +00:00
|
|
|
_ => (),
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
match &self {
|
|
|
|
Self::Ping { algorithms, .. } | Self::Pong { algorithms, .. } => {
|
|
|
|
w.write_u8(Self::PART_ALGORITHMS)?;
|
|
|
|
let mut len = algorithms.algorithm_speeds.len() * 5;
|
|
|
|
if algorithms.allow_unencrypted {
|
|
|
|
len += 5;
|
|
|
|
}
|
|
|
|
w.write_u16::<NetworkEndian>(len as u16)?;
|
|
|
|
if algorithms.allow_unencrypted {
|
|
|
|
w.write_u8(0)?;
|
|
|
|
w.write_f32::<NetworkEndian>(f32::INFINITY)?;
|
|
|
|
}
|
|
|
|
for (algo, speed) in &algorithms.algorithm_speeds {
|
|
|
|
if *algo == &AES_128_GCM {
|
|
|
|
w.write_u8(1)?;
|
|
|
|
} else if *algo == &AES_256_GCM {
|
|
|
|
w.write_u8(2)?;
|
|
|
|
} else if *algo == &CHACHA20_POLY1305 {
|
|
|
|
w.write_u8(3)?;
|
|
|
|
} else {
|
|
|
|
unreachable!();
|
|
|
|
}
|
|
|
|
w.write_f32::<NetworkEndian>(*speed)?;
|
|
|
|
}
|
|
|
|
}
|
2021-04-06 10:28:31 +00:00
|
|
|
_ => (),
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
match &self {
|
|
|
|
Self::Pong { encrypted_payload, .. } | Self::Peng { encrypted_payload, .. } => {
|
|
|
|
w.write_u8(Self::PART_PAYLOAD)?;
|
|
|
|
w.write_u16::<NetworkEndian>(encrypted_payload.len() as u16)?;
|
|
|
|
w.write_all(encrypted_payload.message())?;
|
|
|
|
}
|
2021-04-06 10:28:31 +00:00
|
|
|
_ => (),
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
w.write_u8(Self::PART_END)?;
|
|
|
|
|
|
|
|
let pos = w.position() as usize;
|
|
|
|
let signature = key.sign(&w.get_ref()[0..pos]);
|
|
|
|
w.write_u8(signature.as_ref().len() as u8)?;
|
|
|
|
w.write_all(signature.as_ref())?;
|
|
|
|
|
|
|
|
Ok(w.position() as usize)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
|
|
pub enum InitResult<P: Payload> {
|
|
|
|
Continue,
|
2021-04-06 10:28:31 +00:00
|
|
|
Success { peer_payload: P, is_initiator: bool },
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct InitState<P: Payload> {
|
|
|
|
node_id: NodeId,
|
2020-09-28 10:50:08 +00:00
|
|
|
salted_node_id_hash: SaltedNodeIdHash,
|
2020-09-24 17:48:13 +00:00
|
|
|
payload: P,
|
|
|
|
key_pair: Arc<Ed25519KeyPair>,
|
2020-10-11 19:45:28 +00:00
|
|
|
trusted_keys: Arc<[Ed25519PublicKey]>,
|
2020-09-24 17:48:13 +00:00
|
|
|
ecdh_private_key: Option<EcdhPrivateKey>,
|
|
|
|
next_stage: u8,
|
|
|
|
close_time: usize,
|
|
|
|
last_message: Option<Vec<u8>>,
|
|
|
|
crypto: Option<CryptoCore>,
|
|
|
|
algorithms: Algorithms,
|
2021-02-06 12:09:11 +00:00
|
|
|
#[allow(dead_code)] // Used in tests
|
2020-10-19 20:38:32 +00:00
|
|
|
selected_algorithm: Option<&'static Algorithm>,
|
2021-04-06 10:28:31 +00:00
|
|
|
failed_retries: usize,
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<P: Payload> InitState<P> {
|
|
|
|
pub fn new(
|
2020-10-11 19:45:28 +00:00
|
|
|
node_id: NodeId, payload: P, key_pair: Arc<Ed25519KeyPair>, trusted_keys: Arc<[Ed25519PublicKey]>,
|
2021-04-06 10:28:31 +00:00
|
|
|
algorithms: Algorithms,
|
2021-02-08 16:30:58 +00:00
|
|
|
) -> Self {
|
2020-09-28 10:50:08 +00:00
|
|
|
let mut hash = [0; SALTED_NODE_ID_HASH_LEN];
|
|
|
|
let rng = SystemRandom::new();
|
|
|
|
rng.fill(&mut hash[0..4]).unwrap();
|
|
|
|
hash[4..].clone_from_slice(&node_id);
|
|
|
|
let d = digest::digest(&digest::SHA256, &hash);
|
|
|
|
hash[4..].clone_from_slice(&d.as_ref()[..16]);
|
2020-09-24 17:48:13 +00:00
|
|
|
Self {
|
|
|
|
node_id,
|
2020-09-28 10:50:08 +00:00
|
|
|
salted_node_id_hash: hash,
|
2020-09-24 17:48:13 +00:00
|
|
|
payload,
|
|
|
|
key_pair,
|
|
|
|
trusted_keys,
|
|
|
|
next_stage: STAGE_PING,
|
|
|
|
last_message: None,
|
|
|
|
crypto: None,
|
|
|
|
ecdh_private_key: None,
|
2020-10-19 20:38:32 +00:00
|
|
|
selected_algorithm: None,
|
2020-09-24 17:48:13 +00:00
|
|
|
algorithms,
|
|
|
|
failed_retries: 0,
|
2021-04-06 10:28:31 +00:00
|
|
|
close_time: 60,
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-06 20:52:14 +00:00
|
|
|
pub fn send_ping(&mut self, out: &mut MsgBuffer) {
|
2020-09-24 17:48:13 +00:00
|
|
|
// create ecdh ephemeral key
|
|
|
|
let (ecdh_private_key, ecdh_public_key) = self.create_ecdh_keypair();
|
|
|
|
self.ecdh_private_key = Some(ecdh_private_key);
|
|
|
|
|
|
|
|
// create stage 1 msg
|
2020-10-06 20:52:14 +00:00
|
|
|
self.send_message(STAGE_PING, Some(ecdh_public_key), out);
|
2020-09-24 17:48:13 +00:00
|
|
|
|
|
|
|
self.next_stage = STAGE_PONG;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stage(&self) -> u8 {
|
|
|
|
self.next_stage
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn every_second(&mut self, out: &mut MsgBuffer) -> Result<(), Error> {
|
|
|
|
if self.next_stage == WAITING_TO_CLOSE {
|
|
|
|
if self.close_time == 0 {
|
|
|
|
self.next_stage = CLOSING;
|
|
|
|
} else {
|
|
|
|
self.close_time -= 1;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
} else if self.next_stage == CLOSING {
|
|
|
|
Ok(())
|
2020-11-02 19:44:05 +00:00
|
|
|
} else if self.failed_retries < MAX_FAILED_RETRIES {
|
2020-09-24 17:48:13 +00:00
|
|
|
self.failed_retries += 1;
|
2020-10-06 20:52:14 +00:00
|
|
|
self.repeat_last_message(out);
|
2020-09-24 17:48:13 +00:00
|
|
|
Ok(())
|
|
|
|
} else {
|
2020-10-11 20:47:55 +00:00
|
|
|
self.next_stage = CLOSING;
|
2020-10-28 17:54:17 +00:00
|
|
|
Err(Error::CryptoInitFatal("Initialization timeout"))
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn derive_master_key(&self, algo: &'static Algorithm, privk: EcdhPrivateKey, pubk: &EcdhPublicKey) -> LessSafeKey {
|
|
|
|
agree_ephemeral(privk, pubk, (), |k| {
|
|
|
|
UnboundKey::new(algo, &k[..algo.key_len()]).map(LessSafeKey::new).map_err(|_| ())
|
|
|
|
})
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_ecdh_keypair(&self) -> (EcdhPrivateKey, EcdhPublicKey) {
|
|
|
|
let rand = SystemRandom::new();
|
|
|
|
let ecdh_private_key = EcdhPrivateKey::generate(&X25519, &rand).unwrap();
|
|
|
|
let public_key = ecdh_private_key.compute_public_key().unwrap();
|
|
|
|
let mut vec = SmallVec::<[u8; 96]>::new();
|
|
|
|
vec.extend_from_slice(public_key.as_ref());
|
|
|
|
let ecdh_public_key = EcdhPublicKey::new(&X25519, vec);
|
|
|
|
(ecdh_private_key, ecdh_public_key)
|
|
|
|
}
|
|
|
|
|
2020-10-06 20:52:14 +00:00
|
|
|
fn encrypt_payload(&mut self) -> MsgBuffer {
|
2020-09-24 17:48:13 +00:00
|
|
|
let mut buffer = MsgBuffer::new(EXTRA_LEN);
|
|
|
|
self.payload.write_to(&mut buffer);
|
|
|
|
if let Some(crypto) = &mut self.crypto {
|
|
|
|
crypto.encrypt(&mut buffer);
|
|
|
|
}
|
2020-10-06 20:52:14 +00:00
|
|
|
buffer
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn decrypt(&mut self, data: &mut MsgBuffer) -> Result<P, Error> {
|
|
|
|
if let Some(crypto) = &mut self.crypto {
|
|
|
|
crypto.decrypt(data)?;
|
|
|
|
}
|
2021-02-06 12:09:11 +00:00
|
|
|
P::read_from(Cursor::new(data.message()))
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
|
2020-09-28 10:50:08 +00:00
|
|
|
fn check_salted_node_id_hash(&self, hash: &SaltedNodeIdHash, node_id: NodeId) -> bool {
|
|
|
|
let mut h2 = [0; SALTED_NODE_ID_HASH_LEN];
|
|
|
|
h2[0..4].clone_from_slice(&hash[0..4]);
|
|
|
|
h2[4..].clone_from_slice(&node_id);
|
|
|
|
let d = digest::digest(&digest::SHA256, &h2);
|
|
|
|
hash == d.as_ref()
|
|
|
|
}
|
|
|
|
|
2020-10-11 19:45:28 +00:00
|
|
|
fn send_message(&mut self, stage: u8, ecdh_public_key: Option<EcdhPublicKey>, out: &mut MsgBuffer) {
|
2020-09-24 17:48:13 +00:00
|
|
|
debug!("Sending init with stage={}", stage);
|
|
|
|
assert!(out.is_empty());
|
|
|
|
let mut public_key = [0; ED25519_PUBLIC_KEY_LEN];
|
|
|
|
public_key.clone_from_slice(self.key_pair.as_ref().public_key().as_ref());
|
|
|
|
let msg = match stage {
|
2021-04-06 10:28:31 +00:00
|
|
|
STAGE_PING => InitMsg::Ping {
|
|
|
|
salted_node_id_hash: self.salted_node_id_hash,
|
|
|
|
ecdh_public_key: ecdh_public_key.unwrap(),
|
|
|
|
algorithms: self.algorithms.clone(),
|
|
|
|
},
|
|
|
|
STAGE_PONG => InitMsg::Pong {
|
|
|
|
salted_node_id_hash: self.salted_node_id_hash,
|
|
|
|
ecdh_public_key: ecdh_public_key.unwrap(),
|
|
|
|
algorithms: self.algorithms.clone(),
|
|
|
|
encrypted_payload: self.encrypt_payload(),
|
|
|
|
},
|
|
|
|
STAGE_PENG => InitMsg::Peng {
|
|
|
|
salted_node_id_hash: self.salted_node_id_hash,
|
|
|
|
encrypted_payload: self.encrypt_payload(),
|
|
|
|
},
|
|
|
|
_ => unreachable!(),
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
|
|
|
let mut bytes = out.buffer();
|
|
|
|
let len = msg.write_to(&mut bytes, &self.key_pair).expect("Buffer too small");
|
|
|
|
self.last_message = Some(bytes[0..len].to_vec());
|
|
|
|
out.set_length(len);
|
|
|
|
}
|
|
|
|
|
2020-10-06 20:52:14 +00:00
|
|
|
fn repeat_last_message(&self, out: &mut MsgBuffer) {
|
2020-09-24 17:48:13 +00:00
|
|
|
if let Some(ref bytes) = self.last_message {
|
|
|
|
debug!("Repeating last init message");
|
|
|
|
let buffer = out.buffer();
|
|
|
|
buffer[0..bytes.len()].copy_from_slice(bytes);
|
|
|
|
out.set_length(bytes.len());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn select_algorithm(&self, peer_algos: &Algorithms) -> Result<Option<(&'static Algorithm, f32)>, Error> {
|
|
|
|
if self.algorithms.allow_unencrypted && peer_algos.allow_unencrypted {
|
2021-04-06 10:28:31 +00:00
|
|
|
return Ok(None);
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
// For each supported algorithm, find the algorithm in the list of the peer (ignore algorithm if not found).
|
|
|
|
// Take the minimal speed reported by either us or the peer.
|
|
|
|
// Select the algorithm with the greatest minimal speed.
|
|
|
|
let algo = self
|
|
|
|
.algorithms
|
|
|
|
.algorithm_speeds
|
|
|
|
.iter()
|
|
|
|
.filter_map(|(a1, s1)| {
|
|
|
|
peer_algos
|
|
|
|
.algorithm_speeds
|
|
|
|
.iter()
|
|
|
|
.find(|(a2, _)| a1 == a2)
|
|
|
|
.map(|(_, s2)| (*a1, if s1 < s2 { *s1 } else { *s2 }))
|
|
|
|
})
|
|
|
|
.max_by(|(_, s1), (_, s2)| if s1 < s2 { cmp::Ordering::Less } else { cmp::Ordering::Greater });
|
|
|
|
if let Some(algo) = algo {
|
2020-10-24 22:24:11 +00:00
|
|
|
debug!("Init: best algorithm is {:?} with speed {}", algo.0, algo.1);
|
2020-09-24 17:48:13 +00:00
|
|
|
Ok(Some(algo))
|
|
|
|
} else {
|
2020-10-28 17:54:17 +00:00
|
|
|
Err(Error::CryptoInitFatal("No common algorithms"))
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn handle_init(&mut self, out: &mut MsgBuffer) -> Result<InitResult<P>, Error> {
|
|
|
|
let (msg, _peer_key) = InitMsg::read_from(out.buffer(), &self.trusted_keys)?;
|
|
|
|
out.clear();
|
|
|
|
let stage = msg.stage();
|
2020-09-28 10:50:08 +00:00
|
|
|
let salted_node_id_hash = *msg.salted_node_id_hash();
|
2020-09-24 17:48:13 +00:00
|
|
|
debug!("Received init with stage={}, expected stage={}", stage, self.next_stage);
|
2020-09-28 10:50:08 +00:00
|
|
|
if self.salted_node_id_hash == salted_node_id_hash
|
|
|
|
|| self.check_salted_node_id_hash(&salted_node_id_hash, self.node_id)
|
|
|
|
{
|
2021-04-06 10:28:31 +00:00
|
|
|
return Err(Error::CryptoInitFatal("Connected to self"));
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
if stage != self.next_stage {
|
|
|
|
if self.next_stage == STAGE_PONG && stage == STAGE_PING {
|
|
|
|
// special case for concurrent init messages in both directions
|
|
|
|
// the node with the higher node_id "wins" and gets to initialize the connection
|
2020-09-28 10:50:08 +00:00
|
|
|
if salted_node_id_hash > self.salted_node_id_hash {
|
2020-09-24 17:48:13 +00:00
|
|
|
// reset to initial state
|
|
|
|
self.next_stage = STAGE_PING;
|
|
|
|
self.last_message = None;
|
|
|
|
self.ecdh_private_key = None;
|
|
|
|
} else {
|
2021-04-06 10:28:31 +00:00
|
|
|
return Ok(InitResult::Continue);
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
} else if self.next_stage == CLOSING {
|
2021-04-06 10:28:31 +00:00
|
|
|
return Ok(InitResult::Continue);
|
2020-09-24 17:48:13 +00:00
|
|
|
} else if self.last_message.is_some() {
|
2020-10-06 20:52:14 +00:00
|
|
|
self.repeat_last_message(out);
|
2021-04-06 10:28:31 +00:00
|
|
|
return Ok(InitResult::Continue);
|
2020-09-24 17:48:13 +00:00
|
|
|
} else {
|
2021-04-06 10:28:31 +00:00
|
|
|
return Err(Error::CryptoInitFatal("Received invalid stage as first message"));
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
self.failed_retries = 0;
|
|
|
|
match msg {
|
|
|
|
InitMsg::Ping { ecdh_public_key, algorithms, .. } => {
|
|
|
|
// create ecdh ephemeral key
|
|
|
|
let (my_ecdh_private_key, my_ecdh_public_key) = self.create_ecdh_keypair();
|
|
|
|
|
|
|
|
// do ecdh agreement and derive master key
|
|
|
|
let algorithm = self.select_algorithm(&algorithms)?;
|
2020-10-19 20:38:32 +00:00
|
|
|
self.selected_algorithm = algorithm.map(|a| a.0);
|
2020-09-24 17:48:13 +00:00
|
|
|
if let Some((algorithm, _speed)) = algorithm {
|
|
|
|
let master_key = self.derive_master_key(algorithm, my_ecdh_private_key, &ecdh_public_key);
|
2020-09-28 10:50:08 +00:00
|
|
|
self.crypto = Some(CryptoCore::new(master_key, self.salted_node_id_hash > salted_node_id_hash));
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// create and send stage 2 reply
|
2020-10-06 20:52:14 +00:00
|
|
|
self.send_message(STAGE_PONG, Some(my_ecdh_public_key), out);
|
2020-09-24 17:48:13 +00:00
|
|
|
|
|
|
|
self.next_stage = STAGE_PENG;
|
|
|
|
Ok(InitResult::Continue)
|
|
|
|
}
|
|
|
|
InitMsg::Pong { ecdh_public_key, algorithms, mut encrypted_payload, .. } => {
|
|
|
|
// do ecdh agreement and derive master key
|
|
|
|
let ecdh_private_key = self.ecdh_private_key.take().unwrap();
|
|
|
|
let algorithm = self.select_algorithm(&algorithms)?;
|
2020-10-19 20:38:32 +00:00
|
|
|
self.selected_algorithm = algorithm.map(|a| a.0);
|
2020-09-24 17:48:13 +00:00
|
|
|
if let Some((algorithm, _speed)) = algorithm {
|
|
|
|
let master_key = self.derive_master_key(algorithm, ecdh_private_key, &ecdh_public_key);
|
2020-09-28 10:50:08 +00:00
|
|
|
self.crypto = Some(CryptoCore::new(master_key, self.salted_node_id_hash > salted_node_id_hash));
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// decrypt the payload
|
2020-11-02 19:44:05 +00:00
|
|
|
let peer_payload = self
|
|
|
|
.decrypt(&mut encrypted_payload)
|
|
|
|
.map_err(|_| Error::CryptoInitFatal("Failed to decrypt payload"))?;
|
2020-09-24 17:48:13 +00:00
|
|
|
|
|
|
|
// create and send stage 3 reply
|
2020-10-06 20:52:14 +00:00
|
|
|
self.send_message(STAGE_PENG, None, out);
|
2020-09-24 17:48:13 +00:00
|
|
|
|
|
|
|
self.next_stage = WAITING_TO_CLOSE;
|
|
|
|
self.close_time = 60;
|
2020-09-28 10:50:08 +00:00
|
|
|
Ok(InitResult::Success { peer_payload, is_initiator: true })
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
InitMsg::Peng { mut encrypted_payload, .. } => {
|
|
|
|
// decrypt the payload
|
2020-11-02 19:44:05 +00:00
|
|
|
let peer_payload = self
|
|
|
|
.decrypt(&mut encrypted_payload)
|
|
|
|
.map_err(|_| Error::CryptoInitFatal("Failed to decrypt payload"))?;
|
2020-09-24 17:48:13 +00:00
|
|
|
|
|
|
|
self.next_stage = CLOSING; // force resend when receiving any message
|
2020-09-28 10:50:08 +00:00
|
|
|
Ok(InitResult::Success { peer_payload, is_initiator: false })
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn take_core(&mut self) -> Option<CryptoCore> {
|
|
|
|
self.crypto.take()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-09-28 10:50:08 +00:00
|
|
|
use crate::types::NODE_ID_BYTES;
|
2020-09-24 17:48:13 +00:00
|
|
|
|
|
|
|
impl Payload for Vec<u8> {
|
|
|
|
fn write_to(&self, buffer: &mut MsgBuffer) {
|
|
|
|
buffer.buffer().write_all(&self).expect("Buffer too small");
|
|
|
|
buffer.set_length(self.len())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_from<R: Read>(mut r: R) -> Result<Self, Error> {
|
|
|
|
let mut data = Vec::new();
|
|
|
|
r.read_to_end(&mut data).map_err(|_| Error::Parse("Buffer too small"))?;
|
|
|
|
Ok(data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_pair() -> (InitState<Vec<u8>>, InitState<Vec<u8>>) {
|
|
|
|
let rng = SystemRandom::new();
|
|
|
|
let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
|
|
|
|
let key_pair = Arc::new(Ed25519KeyPair::from_pkcs8(pkcs8_bytes.as_ref()).unwrap());
|
|
|
|
let mut public_key = [0; ED25519_PUBLIC_KEY_LEN];
|
|
|
|
public_key.clone_from_slice(key_pair.public_key().as_ref());
|
2020-10-11 19:45:28 +00:00
|
|
|
let trusted_nodes = Arc::new([public_key]);
|
2020-09-24 17:48:13 +00:00
|
|
|
let mut node1 = [0; NODE_ID_BYTES];
|
|
|
|
rng.fill(&mut node1).unwrap();
|
|
|
|
let mut node2 = [0; NODE_ID_BYTES];
|
|
|
|
rng.fill(&mut node2).unwrap();
|
|
|
|
let algorithms = Algorithms {
|
|
|
|
algorithm_speeds: smallvec![(&AES_128_GCM, 600.0), (&AES_256_GCM, 500.0), (&CHACHA20_POLY1305, 400.0)],
|
2021-04-06 10:28:31 +00:00
|
|
|
allow_unencrypted: false,
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
|
|
|
let sender = InitState::new(node1, vec![1], key_pair.clone(), trusted_nodes.clone(), algorithms.clone());
|
|
|
|
let receiver = InitState::new(node2, vec![2], key_pair, trusted_nodes, algorithms);
|
|
|
|
(sender, receiver)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn normal_init() {
|
|
|
|
let (mut sender, mut receiver) = create_pair();
|
|
|
|
let mut out = MsgBuffer::new(8);
|
2020-10-06 20:52:14 +00:00
|
|
|
sender.send_ping(&mut out);
|
2020-09-24 17:48:13 +00:00
|
|
|
assert_eq!(sender.stage(), STAGE_PONG);
|
|
|
|
let result = receiver.handle_init(&mut out).unwrap();
|
|
|
|
assert_eq!(receiver.stage(), STAGE_PENG);
|
|
|
|
assert_eq!(result, InitResult::Continue);
|
|
|
|
let result = sender.handle_init(&mut out).unwrap();
|
|
|
|
assert_eq!(sender.stage(), WAITING_TO_CLOSE);
|
|
|
|
let result = match result {
|
|
|
|
InitResult::Success { .. } => receiver.handle_init(&mut out).unwrap(),
|
2021-04-06 10:28:31 +00:00
|
|
|
InitResult::Continue => unreachable!(),
|
2020-09-24 17:48:13 +00:00
|
|
|
};
|
|
|
|
assert_eq!(receiver.stage(), CLOSING);
|
|
|
|
match result {
|
|
|
|
InitResult::Success { .. } => assert!(out.is_empty()),
|
2021-04-06 10:28:31 +00:00
|
|
|
InitResult::Continue => unreachable!(),
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-11 19:45:28 +00:00
|
|
|
#[test]
|
|
|
|
fn lost_init_sender_recovers() {
|
|
|
|
let (mut sender, mut receiver) = create_pair();
|
|
|
|
let mut out = MsgBuffer::new(8);
|
|
|
|
sender.send_ping(&mut out);
|
|
|
|
assert_eq!(sender.stage(), STAGE_PONG);
|
|
|
|
// lost ping, sender recovers
|
|
|
|
out.clear();
|
|
|
|
sender.every_second(&mut out).unwrap();
|
|
|
|
let result = receiver.handle_init(&mut out).unwrap();
|
|
|
|
assert_eq!(receiver.stage(), STAGE_PENG);
|
|
|
|
assert_eq!(result, InitResult::Continue);
|
|
|
|
// lost pong, sender recovers
|
|
|
|
out.clear();
|
|
|
|
receiver.every_second(&mut out).unwrap();
|
|
|
|
let result = sender.handle_init(&mut out).unwrap();
|
|
|
|
assert_eq!(sender.stage(), WAITING_TO_CLOSE);
|
|
|
|
match result {
|
|
|
|
InitResult::Success { .. } => {
|
|
|
|
// lost peng, sender recovers
|
|
|
|
out.clear();
|
|
|
|
}
|
2021-04-06 10:28:31 +00:00
|
|
|
InitResult::Continue => unreachable!(),
|
2020-10-11 19:45:28 +00:00
|
|
|
};
|
|
|
|
sender.every_second(&mut out).unwrap();
|
|
|
|
let result = receiver.handle_init(&mut out).unwrap();
|
|
|
|
assert_eq!(receiver.stage(), CLOSING);
|
|
|
|
match result {
|
|
|
|
InitResult::Success { .. } => assert!(out.is_empty()),
|
2021-04-06 10:28:31 +00:00
|
|
|
InitResult::Continue => unreachable!(),
|
2020-10-11 19:45:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn lost_init_receiver_recovers() {
|
|
|
|
let (mut sender, mut receiver) = create_pair();
|
|
|
|
let mut out = MsgBuffer::new(8);
|
|
|
|
sender.send_ping(&mut out);
|
|
|
|
assert_eq!(sender.stage(), STAGE_PONG);
|
|
|
|
let result = receiver.handle_init(&mut out).unwrap();
|
|
|
|
assert_eq!(receiver.stage(), STAGE_PENG);
|
|
|
|
assert_eq!(result, InitResult::Continue);
|
|
|
|
// lost pong, receiver recovers
|
|
|
|
out.clear();
|
|
|
|
sender.every_second(&mut out).unwrap();
|
|
|
|
receiver.handle_init(&mut out).unwrap();
|
|
|
|
let result = sender.handle_init(&mut out).unwrap();
|
|
|
|
assert_eq!(sender.stage(), WAITING_TO_CLOSE);
|
|
|
|
match result {
|
|
|
|
InitResult::Success { .. } => {
|
|
|
|
// lost peng, sender recovers
|
|
|
|
out.clear();
|
|
|
|
}
|
2021-04-06 10:28:31 +00:00
|
|
|
InitResult::Continue => unreachable!(),
|
2020-10-11 19:45:28 +00:00
|
|
|
};
|
|
|
|
receiver.every_second(&mut out).unwrap();
|
|
|
|
sender.handle_init(&mut out).unwrap();
|
|
|
|
let result = receiver.handle_init(&mut out).unwrap();
|
|
|
|
assert_eq!(receiver.stage(), CLOSING);
|
|
|
|
match result {
|
|
|
|
InitResult::Success { .. } => assert!(out.is_empty()),
|
2021-04-06 10:28:31 +00:00
|
|
|
InitResult::Continue => unreachable!(),
|
2020-10-11 19:45:28 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-24 17:48:13 +00:00
|
|
|
|
2020-10-11 20:47:55 +00:00
|
|
|
#[test]
|
|
|
|
fn timeout() {
|
|
|
|
let (mut sender, _receiver) = create_pair();
|
|
|
|
let mut out = MsgBuffer::new(8);
|
|
|
|
sender.send_ping(&mut out);
|
|
|
|
assert_eq!(sender.stage(), STAGE_PONG);
|
2020-11-02 21:32:13 +00:00
|
|
|
for _ in 0..120 {
|
2020-10-11 20:47:55 +00:00
|
|
|
out.clear();
|
|
|
|
sender.every_second(&mut out).unwrap();
|
|
|
|
}
|
|
|
|
out.clear();
|
|
|
|
assert!(sender.every_second(&mut out).is_err());
|
|
|
|
assert_eq!(sender.stage(), CLOSING);
|
|
|
|
}
|
2020-09-24 17:48:13 +00:00
|
|
|
|
2020-10-19 20:38:32 +00:00
|
|
|
#[test]
|
|
|
|
fn untrusted_peer() {
|
|
|
|
let (mut sender, _) = create_pair();
|
|
|
|
let (_, mut receiver) = create_pair();
|
|
|
|
let mut out = MsgBuffer::new(8);
|
|
|
|
sender.send_ping(&mut out);
|
|
|
|
assert_eq!(sender.stage(), STAGE_PONG);
|
|
|
|
assert!(receiver.handle_init(&mut out).is_err());
|
|
|
|
}
|
2020-09-24 17:48:13 +00:00
|
|
|
|
2020-10-19 20:38:32 +00:00
|
|
|
#[test]
|
|
|
|
fn manipulated_message() {
|
|
|
|
let (mut sender, mut receiver) = create_pair();
|
|
|
|
let mut out = MsgBuffer::new(8);
|
|
|
|
sender.send_ping(&mut out);
|
|
|
|
assert_eq!(sender.stage(), STAGE_PONG);
|
|
|
|
out.message_mut()[10] ^= 0x01;
|
|
|
|
assert!(receiver.handle_init(&mut out).is_err());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn connect_to_self() {
|
|
|
|
let (mut sender, _) = create_pair();
|
|
|
|
let mut out = MsgBuffer::new(8);
|
|
|
|
sender.send_ping(&mut out);
|
|
|
|
assert_eq!(sender.stage(), STAGE_PONG);
|
|
|
|
assert!(sender.handle_init(&mut out).is_err());
|
|
|
|
}
|
2020-09-24 17:48:13 +00:00
|
|
|
|
2020-10-19 20:38:32 +00:00
|
|
|
fn test_algorithm_negotiation(
|
2021-04-06 10:28:31 +00:00
|
|
|
algos1: Algorithms, algos2: Algorithms, success: bool, selected: Option<&'static Algorithm>,
|
2020-10-19 20:38:32 +00:00
|
|
|
) {
|
|
|
|
let (mut sender, mut receiver) = create_pair();
|
|
|
|
sender.algorithms = algos1;
|
|
|
|
receiver.algorithms = algos2;
|
|
|
|
let mut out = MsgBuffer::new(8);
|
|
|
|
sender.send_ping(&mut out);
|
|
|
|
let res = receiver.handle_init(&mut out);
|
|
|
|
assert_eq!(res.is_ok(), success);
|
|
|
|
if !success {
|
2021-04-06 10:28:31 +00:00
|
|
|
return;
|
2020-10-19 20:38:32 +00:00
|
|
|
}
|
|
|
|
sender.handle_init(&mut out).unwrap();
|
|
|
|
receiver.handle_init(&mut out).unwrap();
|
|
|
|
assert_eq!(sender.selected_algorithm, selected);
|
|
|
|
assert_eq!(sender.selected_algorithm, receiver.selected_algorithm);
|
|
|
|
}
|
2020-09-24 17:48:13 +00:00
|
|
|
|
2020-10-19 20:38:32 +00:00
|
|
|
#[test]
|
|
|
|
fn algorithm_negotiation() {
|
|
|
|
// Equal algorithms
|
|
|
|
test_algorithm_negotiation(
|
|
|
|
Algorithms {
|
|
|
|
algorithm_speeds: smallvec![(&AES_128_GCM, 600.0), (&AES_256_GCM, 500.0), (&CHACHA20_POLY1305, 400.0)],
|
2021-04-06 10:28:31 +00:00
|
|
|
allow_unencrypted: false,
|
2020-10-19 20:38:32 +00:00
|
|
|
},
|
|
|
|
Algorithms {
|
|
|
|
algorithm_speeds: smallvec![(&AES_128_GCM, 600.0), (&AES_256_GCM, 500.0), (&CHACHA20_POLY1305, 400.0)],
|
2021-04-06 10:28:31 +00:00
|
|
|
allow_unencrypted: false,
|
2020-10-19 20:38:32 +00:00
|
|
|
},
|
|
|
|
true,
|
2021-04-06 10:28:31 +00:00
|
|
|
Some(&AES_128_GCM),
|
2020-10-19 20:38:32 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Overlapping but different
|
|
|
|
test_algorithm_negotiation(
|
|
|
|
Algorithms {
|
|
|
|
algorithm_speeds: smallvec![(&AES_256_GCM, 500.0), (&CHACHA20_POLY1305, 400.0)],
|
2021-04-06 10:28:31 +00:00
|
|
|
allow_unencrypted: false,
|
2020-10-19 20:38:32 +00:00
|
|
|
},
|
|
|
|
Algorithms {
|
|
|
|
algorithm_speeds: smallvec![(&AES_128_GCM, 600.0), (&AES_256_GCM, 500.0)],
|
2021-04-06 10:28:31 +00:00
|
|
|
allow_unencrypted: false,
|
2020-10-19 20:38:32 +00:00
|
|
|
},
|
|
|
|
true,
|
2021-04-06 10:28:31 +00:00
|
|
|
Some(&AES_256_GCM),
|
2020-10-19 20:38:32 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Select fastest pair
|
|
|
|
test_algorithm_negotiation(
|
|
|
|
Algorithms {
|
|
|
|
algorithm_speeds: smallvec![(&AES_128_GCM, 600.0), (&AES_256_GCM, 500.0), (&CHACHA20_POLY1305, 400.0)],
|
2021-04-06 10:28:31 +00:00
|
|
|
allow_unencrypted: false,
|
2020-10-19 20:38:32 +00:00
|
|
|
},
|
|
|
|
Algorithms {
|
|
|
|
algorithm_speeds: smallvec![(&AES_128_GCM, 40.0), (&AES_256_GCM, 50.0), (&CHACHA20_POLY1305, 60.0)],
|
2021-04-06 10:28:31 +00:00
|
|
|
allow_unencrypted: false,
|
2020-10-19 20:38:32 +00:00
|
|
|
},
|
|
|
|
true,
|
2021-04-06 10:28:31 +00:00
|
|
|
Some(&CHACHA20_POLY1305),
|
2020-10-19 20:38:32 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Select unencrypted if supported by both
|
|
|
|
test_algorithm_negotiation(
|
|
|
|
Algorithms {
|
|
|
|
algorithm_speeds: smallvec![(&AES_128_GCM, 600.0), (&AES_256_GCM, 500.0), (&CHACHA20_POLY1305, 400.0)],
|
2021-04-06 10:28:31 +00:00
|
|
|
allow_unencrypted: true,
|
2020-10-19 20:38:32 +00:00
|
|
|
},
|
|
|
|
Algorithms {
|
|
|
|
algorithm_speeds: smallvec![(&AES_128_GCM, 600.0), (&AES_256_GCM, 500.0), (&CHACHA20_POLY1305, 400.0)],
|
2021-04-06 10:28:31 +00:00
|
|
|
allow_unencrypted: true,
|
2020-10-19 20:38:32 +00:00
|
|
|
},
|
|
|
|
true,
|
2021-04-06 10:28:31 +00:00
|
|
|
None,
|
2020-10-19 20:38:32 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Do not select unencrypted if only supported by one
|
|
|
|
test_algorithm_negotiation(
|
|
|
|
Algorithms {
|
|
|
|
algorithm_speeds: smallvec![(&AES_128_GCM, 600.0), (&AES_256_GCM, 500.0), (&CHACHA20_POLY1305, 400.0)],
|
2021-04-06 10:28:31 +00:00
|
|
|
allow_unencrypted: true,
|
2020-10-19 20:38:32 +00:00
|
|
|
},
|
|
|
|
Algorithms {
|
|
|
|
algorithm_speeds: smallvec![(&AES_128_GCM, 600.0), (&AES_256_GCM, 500.0), (&CHACHA20_POLY1305, 400.0)],
|
2021-04-06 10:28:31 +00:00
|
|
|
allow_unencrypted: false,
|
2020-10-19 20:38:32 +00:00
|
|
|
},
|
|
|
|
true,
|
2021-04-06 10:28:31 +00:00
|
|
|
Some(&AES_128_GCM),
|
2020-10-19 20:38:32 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Fail if no match
|
|
|
|
test_algorithm_negotiation(
|
2020-11-02 19:44:05 +00:00
|
|
|
Algorithms { algorithm_speeds: smallvec![(&AES_128_GCM, 600.0)], allow_unencrypted: true },
|
2020-10-19 20:38:32 +00:00
|
|
|
Algorithms {
|
|
|
|
algorithm_speeds: smallvec![(&AES_256_GCM, 500.0), (&CHACHA20_POLY1305, 400.0)],
|
2021-04-06 10:28:31 +00:00
|
|
|
allow_unencrypted: false,
|
2020-10-19 20:38:32 +00:00
|
|
|
},
|
|
|
|
false,
|
2021-04-06 10:28:31 +00:00
|
|
|
Some(&AES_128_GCM),
|
2020-10-19 20:38:32 +00:00
|
|
|
);
|
|
|
|
}
|
2020-09-24 17:48:13 +00:00
|
|
|
}
|