From c6a3bf08202e4aca75961b451ba23a452a33401c Mon Sep 17 00:00:00 2001 From: Luca Fulchir Date: Tue, 30 May 2023 10:52:54 +0200 Subject: [PATCH] More work on connect(), use our own Random We use ::ring::rand::SystemRandom, but we need to wrap it for a couple of traits needed by ::x25519_dalek Signed-off-by: Luca Fulchir --- Cargo.toml | 1 + src/auth/mod.rs | 4 +- src/connection/handshake/mod.rs | 1 + src/connection/mod.rs | 3 +- src/connection/packet.rs | 12 +++--- src/dnssec/record.rs | 2 +- src/enc/asym.rs | 26 ++++++++++++- src/enc/mod.rs | 66 ++++++++++++++++++++++++++++++++ src/enc/sym.rs | 15 +++----- src/inner/worker.rs | 67 +++++++++++++++++++++++++++------ src/lib.rs | 13 ++++--- 11 files changed, 173 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8142aa8..12ed78d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ hwloc2 = {version = "2.2" } libc = { version = "0.2" } num-traits = { version = "0.2" } num-derive = { version = "0.3" } +rand_core = {version = "0.6" } ring = { version = "0.16" } bincode = { version = "1.3" } sha3 = { version = "0.10" } diff --git a/src/auth/mod.rs b/src/auth/mod.rs index 2d8dc65..955cb12 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -1,6 +1,6 @@ //! Authentication related struct definitions -use ::ring::rand::SecureRandom; +use crate::enc::Random; use ::zeroize::Zeroize; /// User identifier. 16 bytes for easy uuid conversion @@ -15,7 +15,7 @@ impl From<[u8; 16]> for UserID { impl UserID { /// New random user id - pub fn new(rand: &::ring::rand::SystemRandom) -> Self { + pub fn new(rand: &Random) -> Self { let mut ret = Self([0; 16]); rand.fill(&mut ret.0); ret diff --git a/src/connection/handshake/mod.rs b/src/connection/handshake/mod.rs index 8897c1a..76eb353 100644 --- a/src/connection/handshake/mod.rs +++ b/src/connection/handshake/mod.rs @@ -39,6 +39,7 @@ pub(crate) struct HandshakeClient { pub service_id: crate::auth::ServiceID, pub service_conn_id: connection::IDRecv, pub connection: Rc, + pub timeout: Rc, } /// Parsed handshake diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 6d3325d..2f8e3c9 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -17,6 +17,7 @@ use crate::{ asym::PubKey, hkdf::HkdfSha3, sym::{CipherKind, CipherRecv, CipherSend}, + Random, }, inner::ThreadTracker, }; @@ -78,7 +79,7 @@ impl Connection { hkdf: HkdfSha3, cipher: CipherKind, role: Role, - rand: &::ring::rand::SystemRandom, + rand: &Random, ) -> Self { let (secret_recv, secret_send) = match role { Role::Server => { diff --git a/src/connection/packet.rs b/src/connection/packet.rs index 400f900..b051594 100644 --- a/src/connection/packet.rs +++ b/src/connection/packet.rs @@ -1,7 +1,10 @@ // //! Raw packet handling, encryption, decryption, parsing -use crate::enc::sym::{HeadLen, TagLen}; +use crate::enc::{ + sym::{HeadLen, TagLen}, + Random, +}; /// Fenrir Connection id /// 0 is special as it represents the handshake @@ -33,8 +36,7 @@ impl ConnectionID { } } /// New random service ID - pub fn new_rand(rand: &::ring::rand::SystemRandom) -> Self { - use ::ring::rand::SecureRandom; + pub fn new_rand(rand: &Random) -> Self { let mut raw = [0; 8]; let mut num = 0; while num == 0 { @@ -100,7 +102,7 @@ impl PacketData { pub fn len(&self) -> usize { match self { PacketData::Handshake(h) => h.len(), - PacketData::Raw(len) => *len + PacketData::Raw(len) => *len, } } /// serialize data into bytes @@ -134,7 +136,7 @@ pub struct Packet { impl Packet { /// New recevied packet, yet unparsed - pub fn deserialize_id(raw: &[u8]) -> Result { + pub fn deserialize_id(raw: &[u8]) -> Result { // TODO: proper min_packet length. 16 is too conservative. if raw.len() < MIN_PACKET_BYTES { return Err(()); diff --git a/src/dnssec/record.rs b/src/dnssec/record.rs index 6709751..48e8edd 100644 --- a/src/dnssec/record.rs +++ b/src/dnssec/record.rs @@ -32,7 +32,7 @@ use ::std::{net::IpAddr, vec::Vec}; */ /// Public Key ID -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub struct PublicKeyID(u8); impl TryFrom<&str> for PublicKeyID { diff --git a/src/enc/asym.rs b/src/enc/asym.rs index 6fd7901..ff8dda1 100644 --- a/src/enc/asym.rs +++ b/src/enc/asym.rs @@ -3,7 +3,7 @@ use ::num_traits::FromPrimitive; use super::Error; -use crate::enc::sym::Secret; +use crate::enc::{sym::Secret, Random}; /// Public key ID #[derive(Debug, Copy, Clone, PartialEq)] @@ -72,6 +72,7 @@ impl KeyKind { /// Kind of key exchange #[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)] +#[non_exhaustive] #[repr(u8)] pub enum KeyExchange { /// X25519 Public key @@ -82,6 +83,23 @@ impl KeyExchange { pub fn len() -> usize { 1 } + /// Build a new keypair for key exchange + pub fn new_keypair( + &self, + rnd: &Random, + ) -> Result<(ExchangePrivKey, ExchangePubKey), Error> { + match self { + KeyExchange::X25519DiffieHellman => { + let raw_priv = ::x25519_dalek::StaticSecret::new(rnd); + let pub_key = ExchangePubKey::X25519( + ::x25519_dalek::PublicKey::from(&raw_priv), + ); + let priv_key = ExchangePrivKey::X25519(raw_priv); + Ok((priv_key, pub_key)) + } + _ => Err(Error::UnsupportedKeyExchange), + } + } } /// Kind of public key in the handshake @@ -162,6 +180,7 @@ pub enum PrivKey { /// Keys to be used only in key exchanges, not for signing Exchange(ExchangePrivKey), /// Keys to be used only for signing + // TODO: implement ed25519 Signing, } @@ -259,3 +278,8 @@ impl ExchangePubKey { } } } + +/// Build a new pair of private/public key pair +pub fn new_keypair(kind: KeyKind, rnd: &Random) -> (PrivKey, PubKey) { + todo!() +} diff --git a/src/enc/mod.rs b/src/enc/mod.rs index eda3385..4da9a0c 100644 --- a/src/enc/mod.rs +++ b/src/enc/mod.rs @@ -6,3 +6,69 @@ pub mod hkdf; pub mod sym; pub use errors::Error; + +use ::ring::rand::SecureRandom; + +/// wrapper where we implement whatever random traint stuff each library needs +pub struct Random { + /// actual source of randomness + rnd: ::ring::rand::SystemRandom, +} + +impl Random { + /// Build a nre Random source + pub fn new() -> Self { + Self { + rnd: ::ring::rand::SystemRandom::new(), + } + } + /// Fill a buffer with randomness + pub fn fill(&self, out: &mut [u8]) { + self.rnd.fill(out); + } +} + +// Fake debug implementation to avoid leaking secrets +impl ::core::fmt::Debug for Random { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> Result<(), ::std::fmt::Error> { + ::core::fmt::Debug::fmt("[hidden randomness]", f) + } +} + +// ::rand_core::{RngCore, CryptoRng} needed for ::x25519::dalek +impl ::rand_core::RngCore for &Random { + fn next_u32(&mut self) -> u32 { + use ::core::mem::MaybeUninit; + let mut out: MaybeUninit<[u8; 4]> = MaybeUninit::uninit(); + #[allow(unsafe_code)] + unsafe { + let _ = self.rnd.fill(out.assume_init_mut()); + u32::from_le_bytes(out.assume_init()) + } + } + fn next_u64(&mut self) -> u64 { + use ::core::mem::MaybeUninit; + let mut out: MaybeUninit<[u8; 8]> = MaybeUninit::uninit(); + #[allow(unsafe_code)] + unsafe { + let _ = self.rnd.fill(out.assume_init_mut()); + u64::from_le_bytes(out.assume_init()) + } + } + fn fill_bytes(&mut self, dest: &mut [u8]) { + let _ = self.rnd.fill(dest); + } + fn try_fill_bytes( + &mut self, + dest: &mut [u8], + ) -> Result<(), ::rand_core::Error> { + match self.rnd.fill(dest) { + Ok(()) => Ok(()), + Err(e) => Err(::rand_core::Error::new(e)), + } + } +} +impl ::rand_core::CryptoRng for &Random {} diff --git a/src/enc/sym.rs b/src/enc/sym.rs index e6f9e11..d9673a0 100644 --- a/src/enc/sym.rs +++ b/src/enc/sym.rs @@ -1,6 +1,7 @@ //! Symmetric cypher stuff use super::Error; +use crate::enc::Random; use ::zeroize::Zeroize; /// Secret, used for keys. @@ -20,8 +21,7 @@ impl ::core::fmt::Debug for Secret { impl Secret { /// New randomly generated secret - pub fn new_rand(rand: &::ring::rand::SystemRandom) -> Self { - use ::ring::rand::SecureRandom; + pub fn new_rand(rand: &Random) -> Self { let mut ret = Self([0; 32]); rand.fill(&mut ret.0); ret @@ -265,11 +265,7 @@ impl ::core::fmt::Debug for CipherSend { impl CipherSend { /// Build a new Cipher - pub fn new( - kind: CipherKind, - secret: Secret, - rand: &::ring::rand::SystemRandom, - ) -> Self { + pub fn new(kind: CipherKind, secret: Secret, rand: &Random) -> Self { Self { nonce: NonceSync::new(rand), cipher: Cipher::new(kind, secret), @@ -335,8 +331,7 @@ impl ::core::fmt::Debug for Nonce { impl Nonce { /// Generate a new random Nonce - pub fn new(rand: &::ring::rand::SystemRandom) -> Self { - use ring::rand::SecureRandom; + pub fn new(rand: &Random) -> Self { let mut raw = [0; 12]; rand.fill(&mut raw); Self { raw } @@ -376,7 +371,7 @@ pub struct NonceSync { } impl NonceSync { /// Create a new thread safe nonce - pub fn new(rand: &::ring::rand::SystemRandom) -> Self { + pub fn new(rand: &Random) -> Self { Self { nonce: ::std::sync::Mutex::new(Nonce::new(rand)), } diff --git a/src/inner/worker.rs b/src/inner/worker.rs index 512c2f6..fcc69ea 100644 --- a/src/inner/worker.rs +++ b/src/inner/worker.rs @@ -11,7 +11,7 @@ use crate::{ ConnList, Connection, IDSend, Packet, }, dnssec, - enc::{asym::PubKey, hkdf::HkdfSha3, sym::Secret}, + enc::{asym::PubKey, hkdf::HkdfSha3, sym::Secret, Random}, inner::{HandshakeAction, HandshakeTracker, ThreadTracker}, }; use ::std::{rc::Rc, sync::Arc, vec::Vec}; @@ -29,16 +29,17 @@ pub(crate) struct RawUdp { pub packet: Packet, } -pub(crate) enum ConnectionResult { - Failed(crate::Error), - Established((PubKey, IDSend)), -} - pub(crate) enum Work { /// ask the thread to report to the main thread the total number of /// connections present CountConnections(oneshot::Sender), - Connect((oneshot::Sender, dnssec::Record, ServiceID)), + Connect( + ( + oneshot::Sender>, + dnssec::Record, + ServiceID, + ), + ), Recv(RawUdp), } pub(crate) enum WorkAnswer { @@ -49,7 +50,7 @@ pub(crate) enum WorkAnswer { pub(crate) struct Worker { thread_id: ThreadTracker, // PERF: rand uses syscalls. how to do that async? - rand: ::ring::rand::SystemRandom, + rand: Random, stop_working: ::tokio::sync::broadcast::Receiver, token_check: Option>>, sockets: Vec, @@ -121,7 +122,7 @@ impl Worker { Ok(Self { thread_id, - rand: ::ring::rand::SystemRandom::new(), + rand: Random::new(), stop_working, token_check, sockets, @@ -132,7 +133,7 @@ impl Worker { }) } pub(crate) async fn work_loop(&mut self) { - loop { + 'mainloop: loop { let work = ::tokio::select! { _done = self.stop_working.recv() => { break; @@ -149,7 +150,51 @@ impl Worker { let conn_num = self.connections.len(); let _ = sender.send(conn_num); } - Work::Connect((send_res, dnssec_record, service_id)) => { + Work::Connect((send_res, dnssec_record, _service_id)) => { + let destination = + dnssec_record.addresses.iter().find_map(|addr| { + let maybe_key = + dnssec_record.public_keys.iter().find( + |(id, _)| addr.public_key_ids.contains(id), + ); + match maybe_key { + Some(key) => Some((addr, key)), + None => None, + } + }); + let (addr, key) = match destination { + Some((addr, key)) => (addr, key), + None => { + let _ = + send_res.send(Err(crate::Error::Resolution( + "No selectable address and key combination" + .to_owned(), + ))); + continue 'mainloop; + } + }; + use crate::enc::asym; + let exchange = asym::KeyExchange::X25519DiffieHellman; + let (priv_key, pub_key) = + match exchange.new_keypair(&self.rand) { + Ok(pair) => pair, + Err(_) => todo!(), + }; + // build request + /* + let req = dirsync::Req { + key_id: key.0, + exchange: exchange, + cipher: 42, + exchange_key: client_pub_key, + data: 42, + }; + */ + + // start timeout + + // send packet + todo!() } //TODO: reconf message to add channels diff --git a/src/lib.rs b/src/lib.rs index 6f48ec5..d09d39e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,9 @@ pub enum Error { /// Key error #[error("key: {0:?}")] Key(#[from] crate::enc::Error), + /// Resolution problems. wrong or incomplete DNSSEC data + #[error("DNSSEC resolution: {0}")] + Resolution(String), } /// Instance of a fenrir endpoint @@ -199,7 +202,7 @@ impl Fenrir { // we very likely have multiple threads, pinned to different cpus. // use the ConnectionID to send the same connection // to the same thread. - // Handshakes have conenction ID 0, so we use the sender's UDP port + // Handshakes have connection ID 0, so we use the sender's UDP port let packet = match Packet::deserialize_id(&data) { Ok(packet) => packet, @@ -266,7 +269,7 @@ impl Fenrir { continue; } Reservation::Reserved => break, - Reservation::Present(id_send) => { + Reservation::Present(_id_send) => { //TODO: reuse connection todo!() } @@ -275,7 +278,6 @@ impl Fenrir { // Spot reserved for the connection // find the thread with less connections - let th_num = self._thread_work.len(); let mut conn_count = Vec::::with_capacity(th_num); let mut wait_res = @@ -313,15 +315,14 @@ impl Fenrir { match recv.await { Ok(res) => { - use crate::inner::worker::ConnectionResult; match res { - ConnectionResult::Failed(e) => { + Err(e) => { let mut conn_auth_lock = self.conn_auth_srv.lock().await; conn_auth_lock.remove_reserved(&resolved); Err(e) } - ConnectionResult::Established((pubkey, id_send)) => { + Ok((pubkey, id_send)) => { let mut conn_auth_lock = self.conn_auth_srv.lock().await; conn_auth_lock.add(&pubkey, id_send, &resolved);