From 21d58f25a3b86bc83b846253fbb35f12af2a7838 Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Tue, 9 Feb 2021 23:50:00 +0100 Subject: [PATCH] Changed crypto --- src/crypto/common.rs | 270 +++++++++++++------------------------------ src/crypto/init.rs | 30 +++-- src/engine/mod.rs | 2 +- src/engine/shared.rs | 2 +- src/tests/common.rs | 8 +- 5 files changed, 106 insertions(+), 206 deletions(-) diff --git a/src/crypto/common.rs b/src/crypto/common.rs index db31179..cb2402d 100644 --- a/src/crypto/common.rs +++ b/src/crypto/common.rs @@ -1,7 +1,7 @@ -use super::{ - core::{test_speed, CryptoCore}, - init::{self, InitResult, InitState, CLOSING}, - rotate::RotationState +use super::{core::test_speed, rotate::RotationState}; +pub use super::{ + core::{CryptoCore, EXTRA_LEN, TAG_LEN}, + init::{is_init_message, INIT_MESSAGE_FIRST_BYTE, InitState, InitResult} }; use crate::{ error::Error, @@ -20,8 +20,8 @@ use std::{fmt::Debug, io::Read, num::NonZeroU32, sync::Arc, time::Duration}; const SALT: &[u8; 32] = b"vpncloudVPNCLOUDvpncl0udVpnCloud"; -const INIT_MESSAGE_FIRST_BYTE: u8 = 0xff; -const MESSAGE_TYPE_ROTATION: u8 = 0x10; + +pub const MESSAGE_TYPE_ROTATION: u8 = 0x10; pub type Ed25519PublicKey = [u8; ED25519_PUBLIC_KEY_LEN]; pub type EcdhPublicKey = UnparsedPublicKey>; @@ -180,175 +180,84 @@ impl Crypto { Ok(result) } - pub fn peer_instance(&self, payload: P) -> PeerCrypto

{ - PeerCrypto::new( - self.node_id, - payload, - self.key_pair.clone(), - self.trusted_keys.clone(), - self.algorithms.clone() - ) + pub fn peer_instance(&self, payload: P) -> InitState

{ + InitState::new(self.node_id, payload, self.key_pair.clone(), self.trusted_keys.clone(), self.algorithms.clone()) } } #[derive(Debug, PartialEq)] -pub enum MessageResult { +pub enum MessageResult { Message(u8), - Initialized(P), - InitializedWithReply(P), Reply, None } -pub struct PeerCrypto { - #[allow(dead_code)] - node_id: NodeId, - init: Option>, - rotation: Option, - unencrypted: bool, - core: Option, - rotate_counter: usize +pub enum PeerCrypto { + Encrypted { + last_init_message: Vec, + algorithm: &'static Algorithm, + rotation: RotationState, + core: Arc, + rotate_counter: usize + }, + Unencrypted { + last_init_message: Vec + } } -impl PeerCrypto

{ - pub fn new( - node_id: NodeId, init_payload: P, key_pair: Arc, trusted_keys: Arc<[Ed25519PublicKey]>, - algorithms: Algorithms - ) -> Self - { - Self { - node_id, - init: Some(InitState::new(node_id, init_payload, key_pair, trusted_keys, algorithms)), - rotation: None, - unencrypted: false, - core: None, - rotate_counter: 0 - } - } - - fn get_init(&mut self) -> Result<&mut InitState

, Error> { - if let Some(init) = &mut self.init { - Ok(init) - } else { - Err(Error::InvalidCryptoState("Initialization already finished")) - } - } - - fn get_core(&mut self) -> Result<&mut CryptoCore, Error> { - if let Some(core) = &mut self.core { - Ok(core) - } else { - Err(Error::InvalidCryptoState("Crypto core not ready yet")) - } - } - - fn get_rotation(&mut self) -> Result<&mut RotationState, Error> { - if let Some(rotation) = &mut self.rotation { - Ok(rotation) - } else { - Err(Error::InvalidCryptoState("Key rotation not initialized")) - } - } - - pub fn initialize(&mut self, out: &mut MsgBuffer) -> Result<(), Error> { - let init = self.get_init()?; - if init.stage() != init::STAGE_PING { - Err(Error::InvalidCryptoState("Initialization already ongoing")) - } else { - init.send_ping(out); - out.prepend_byte(INIT_MESSAGE_FIRST_BYTE); - Ok(()) - } - } - - pub fn has_init(&self) -> bool { - self.init.is_some() - } - - pub fn is_ready(&self) -> bool { - self.core.is_some() - } - +impl PeerCrypto { pub fn algorithm_name(&self) -> &'static str { - if let Some(ref core) = self.core { - let algo = core.algorithm(); - if algo == &aead::CHACHA20_POLY1305 { - "CHACHA20" - } else if algo == &aead::AES_128_GCM { - "AES128" - } else if algo == &aead::AES_256_GCM { - "AES256" - } else { - unreachable!() + match self { + PeerCrypto::Encrypted { algorithm, .. } => { + match *algorithm { + x if x == &aead::CHACHA20_POLY1305 => "CHACHA20", + x if x == &aead::AES_128_GCM => "AES128", + x if x == &aead::AES_256_GCM => "AES256", + _ => unreachable!() + } } - } else { - "PLAIN" + PeerCrypto::Unencrypted { .. } => "PLAIN" } } - fn handle_init_message(&mut self, buffer: &mut MsgBuffer) -> Result, Error> { - let result = self.get_init()?.handle_init(buffer)?; - if !buffer.is_empty() { - buffer.prepend_byte(INIT_MESSAGE_FIRST_BYTE); - } - match result { - InitResult::Continue => Ok(MessageResult::Reply), - InitResult::Success { peer_payload, is_initiator } => { - self.core = self.get_init()?.take_core(); - if self.core.is_none() { - self.unencrypted = true; - } - if self.get_init()?.stage() == init::CLOSING { - self.init = None - } - if self.core.is_some() { - self.rotation = Some(RotationState::new(!is_initiator, buffer)); - } - if !is_initiator { - if self.unencrypted { - return Ok(MessageResult::Initialized(peer_payload)) - } - assert!(!buffer.is_empty()); - buffer.prepend_byte(MESSAGE_TYPE_ROTATION); - self.encrypt_message(buffer)?; - } - Ok(MessageResult::InitializedWithReply(peer_payload)) - } - } + fn handle_init_message(&mut self, buffer: &mut MsgBuffer) -> Result { + // TODO: parse message stage + // TODO: depending on stage resend last message + Ok(MessageResult::None) } fn handle_rotate_message(&mut self, data: &[u8]) -> Result<(), Error> { - if self.unencrypted { - return Ok(()) + match self { + PeerCrypto::Encrypted { rotation, core, algorithm, .. } => { + if let Some(rot) = rotation.handle_message(data)? { + let key = LessSafeKey::new(UnboundKey::new(algorithm, &rot.key[..algorithm.key_len()]).unwrap()); + core.rotate_key(key, rot.id, rot.use_for_sending); + } + Ok(()) + } + PeerCrypto::Unencrypted { .. } => Err(Error::Crypto("Rotation when unencrypted")) } - if let Some(rot) = self.get_rotation()?.handle_message(data)? { - let core = self.get_core()?; - let algo = core.algorithm(); - let key = LessSafeKey::new(UnboundKey::new(algo, &rot.key[..algo.key_len()]).unwrap()); - core.rotate_key(key, rot.id, rot.use_for_sending); - } - Ok(()) } - fn encrypt_message(&mut self, buffer: &mut MsgBuffer) -> Result<(), Error> { - if self.unencrypted { - return Ok(()) + fn encrypt_message(&mut self, buffer: &mut MsgBuffer) { + // HOT PATH + if let PeerCrypto::Encrypted { core, .. } = self { + core.encrypt(buffer) } - self.get_core()?.encrypt(buffer); - Ok(()) } fn decrypt_message(&mut self, buffer: &mut MsgBuffer) -> Result<(), Error> { // HOT PATH - if self.unencrypted { - return Ok(()) + if let PeerCrypto::Encrypted { core, .. } = self { + core.decrypt(buffer) + } else { + Ok(()) } - self.get_core()?.decrypt(buffer) } - pub fn handle_message(&mut self, buffer: &mut MsgBuffer) -> Result, Error> { + pub fn handle_message(&mut self, buffer: &mut MsgBuffer) -> Result { // HOT PATH if buffer.is_empty() { return Err(Error::InvalidCryptoState("No message in buffer")) @@ -356,7 +265,6 @@ impl PeerCrypto

{ if is_init_message(buffer.buffer()) { // COLD PATH debug!("Received init message"); - buffer.take_prefix(); self.handle_init_message(buffer) } else { // HOT PATH @@ -375,54 +283,35 @@ impl PeerCrypto

{ } } - pub fn send_message(&mut self, type_: u8, buffer: &mut MsgBuffer) -> Result<(), Error> { + pub fn send_message(&mut self, type_: u8, buffer: &mut MsgBuffer) { // HOT PATH assert_ne!(type_, MESSAGE_TYPE_ROTATION); buffer.prepend_byte(type_); - self.encrypt_message(buffer) + self.encrypt_message(buffer); } - pub fn every_second(&mut self, out: &mut MsgBuffer) -> Result, Error> { + pub fn every_second(&mut self, out: &mut MsgBuffer) -> MessageResult { out.clear(); - if let Some(ref mut core) = self.core { - core.every_second() - } - if let Some(ref mut init) = self.init { - init.every_second(out)?; - } - if self.init.as_ref().map(|i| i.stage()).unwrap_or(CLOSING) == CLOSING { - self.init = None - } - if !out.is_empty() { - out.prepend_byte(INIT_MESSAGE_FIRST_BYTE); - return Ok(MessageResult::Reply) - } - if let Some(ref mut rotate) = self.rotation { - self.rotate_counter += 1; - if self.rotate_counter >= ROTATE_INTERVAL { - self.rotate_counter = 0; - if let Some(rot) = rotate.cycle(out) { - let core = self.get_core()?; - let algo = core.algorithm(); - let key = LessSafeKey::new(UnboundKey::new(algo, &rot.key[..algo.key_len()]).unwrap()); + if let PeerCrypto::Encrypted { core, rotation, rotate_counter, algorithm, .. } = self { + core.every_second(); + *rotate_counter += 1; + if *rotate_counter >= ROTATE_INTERVAL { + *rotate_counter = 0; + if let Some(rot) = rotation.cycle(out) { + let key = LessSafeKey::new(UnboundKey::new(algorithm, &rot.key[..algorithm.key_len()]).unwrap()); core.rotate_key(key, rot.id, rot.use_for_sending); } if !out.is_empty() { out.prepend_byte(MESSAGE_TYPE_ROTATION); - self.encrypt_message(out)?; - return Ok(MessageResult::Reply) + self.encrypt_message(out); + return MessageResult::Reply } } } - Ok(MessageResult::None) + MessageResult::None } } -pub fn is_init_message(msg: &[u8]) -> bool { - // HOT PATH - !msg.is_empty() && msg[0] == INIT_MESSAGE_FIRST_BYTE -} - #[cfg(test)] mod tests { @@ -430,7 +319,7 @@ mod tests { use crate::types::NODE_ID_BYTES; - fn create_node(config: &Config) -> PeerCrypto> { + fn create_node(config: &Config) -> InitState> { let rng = SystemRandom::new(); let mut node_id = [0; NODE_ID_BYTES]; rng.fill(&mut node_id).unwrap(); @@ -445,23 +334,28 @@ mod tests { let mut node2 = create_node(&config); let mut msg = MsgBuffer::new(16); - node1.initialize(&mut msg).unwrap(); + node1.send_ping(&mut msg); assert!(!msg.is_empty()); debug!("Node1 -> Node2"); - let res = node2.handle_message(&mut msg).unwrap(); - assert_eq!(res, MessageResult::Reply); + let res = node2.handle_init(&mut msg).unwrap(); + assert_eq!(res, InitResult::Continue); assert!(!msg.is_empty()); debug!("Node1 <- Node2"); - let res = node1.handle_message(&mut msg).unwrap(); - assert_eq!(res, MessageResult::InitializedWithReply(vec![])); + let res = node1.handle_init(&mut msg).unwrap(); + assert_eq!(res, InitResult::Success { peer_payload: vec![], is_initiator: false }); assert!(!msg.is_empty()); debug!("Node1 -> Node2"); - let res = node2.handle_message(&mut msg).unwrap(); - assert_eq!(res, MessageResult::InitializedWithReply(vec![])); - assert!(!msg.is_empty()); + let res = node2.handle_init(&mut msg).unwrap(); + assert_eq!(res, InitResult::Success { peer_payload: vec![], is_initiator: true }); + assert!(msg.is_empty()); + + let node1 = node1.finish(&mut msg); + assert!(msg.is_empty()); + let node2 = node2.finish(&mut msg); + assert!(msg.is_empty()); debug!("Node1 <- Node2"); let res = node1.handle_message(&mut msg).unwrap(); @@ -473,11 +367,11 @@ mod tests { buffer.set_length(1000); rng.fill(buffer.message_mut()).unwrap(); for _ in 0..1000 { - node1.send_message(1, &mut buffer).unwrap(); + node1.send_message(1, &mut buffer); let res = node2.handle_message(&mut buffer).unwrap(); assert_eq!(res, MessageResult::Message(1)); - match node1.every_second(&mut msg).unwrap() { + match node1.every_second(&mut msg) { MessageResult::None => (), MessageResult::Reply => { let res = node2.handle_message(&mut msg).unwrap(); @@ -485,7 +379,7 @@ mod tests { } other => assert_eq!(other, MessageResult::None) } - match node2.every_second(&mut msg).unwrap() { + match node2.every_second(&mut msg) { MessageResult::None => (), MessageResult::Reply => { let res = node1.handle_message(&mut msg).unwrap(); diff --git a/src/crypto/init.rs b/src/crypto/init.rs index 24afec0..def486b 100644 --- a/src/crypto/init.rs +++ b/src/crypto/init.rs @@ -58,8 +58,8 @@ use super::{ core::{CryptoCore, EXTRA_LEN}, rotate::RotationState, - Algorithms, EcdhPrivateKey, EcdhPublicKey, Ed25519PublicKey, Error, MsgBuffer, Payload, PeerCrypto, - MESSAGE_TYPE_ROTATION + Algorithms, EcdhPrivateKey, EcdhPublicKey, Ed25519PublicKey, Payload, PeerCrypto, + common::MESSAGE_TYPE_ROTATION }; use crate::{error::Error, types::NodeId, util::MsgBuffer}; use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; @@ -679,16 +679,22 @@ impl InitState

{ pub fn finish(self, buffer: &mut MsgBuffer) -> PeerCrypto { assert!(buffer.is_empty()); - let rotation = if self.crypto.is_some() { Some(RotationState::new(!self.is_initiator, buffer)) } else { None }; - if !buffer.is_empty() { - buffer.prepend_byte(MESSAGE_TYPE_ROTATION); - } - PeerCrypto { - algorithm: self.crypto.map(|c| c.algorithm()), - core: self.crypto, - rotation, - rotate_counter: 0, - last_init_message: self.last_message + if let Some(crypto) = self.crypto { + let rotation = RotationState::new(!self.is_initiator, buffer); + if !buffer.is_empty() { + buffer.prepend_byte(MESSAGE_TYPE_ROTATION); + } + PeerCrypto::Encrypted { + algorithm: crypto.algorithm(), + core: crypto, + rotation, + rotate_counter: 0, + last_init_message: self.last_message.unwrap() + } + } else { + PeerCrypto::Unencrypted { + last_init_message: self.last_message.unwrap() + } } } } diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 3de5570..69eb995 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -80,7 +80,7 @@ pub struct GenericCloud { peers: HashMap, reconnect_peers: SmallVec<[ReconnectEntry; 3]>, own_addresses: AddrList, - pending_inits: HashMap, Hash>, + pending_inits: HashMap, Hash>, table: ClaimTable, socket: S, device: D, diff --git a/src/engine/shared.rs b/src/engine/shared.rs index b90f597..bf8ff44 100644 --- a/src/engine/shared.rs +++ b/src/engine/shared.rs @@ -30,7 +30,7 @@ impl SharedPeerCrypto { } } - pub fn for_each(&mut self, mut callback: impl FnMut(SocketAddr, &mut PeerCrypto) -> Result<(), Error>) -> Result<(), Error> { + pub fn for_each(&mut self, mut callback: impl FnMut(SocketAddr, &mut CryptoCore) -> Result<(), Error>) -> Result<(), Error> { let mut peers = self.peers.lock(); for (k, v) in peers.iter_mut() { callback(*k, v)? diff --git a/src/tests/common.rs b/src/tests/common.rs index b897b1f..6b55ba7 100644 --- a/src/tests/common.rs +++ b/src/tests/common.rs @@ -13,9 +13,9 @@ use std::{ }; pub use crate::{ - cloud::GenericCloud, config::{Config, CryptoConfig}, device::{MockDevice, Type}, + engine::GenericCloud, net::MockSocket, payload::{Frame, Packet, Protocol}, types::Range, @@ -44,8 +44,8 @@ impl DebugLogger { impl log::Log for DebugLogger { #[inline] - fn enabled(&self, metadata: &log::Metadata) -> bool { - log::max_level() > metadata.level() + fn enabled(&self, _metadata: &log::Metadata) -> bool { + true } #[inline] @@ -79,7 +79,7 @@ impl Simulator

{ pub fn new() -> Self { init_debug_logger(); MockTimeSource::set_time(0); - Self { next_port: 1, nodes: HashMap::default(), messages: VecDeque::with_capacity(10) } + Self { next_port: 1, nodes: HashMap::default(), messages: VecDeque::default() } } pub fn add_node(&mut self, nat: bool, config: &Config) -> SocketAddr {