From ac213a6528dc65ab6f857612946217f62b21db4d Mon Sep 17 00:00:00 2001 From: Luca Fulchir Date: Thu, 1 Jun 2023 11:41:10 +0200 Subject: [PATCH] More work on key exhcnage negotiation Signed-off-by: Luca Fulchir --- src/config/mod.rs | 16 +++ src/connection/handshake/dirsync.rs | 52 ++++++++-- src/connection/handshake/mod.rs | 31 ++++++ src/dnssec/record.rs | 145 +++++++++++++--------------- src/enc/asym.rs | 43 ++++++++- src/enc/hkdf.rs | 40 +++++++- src/enc/sym.rs | 29 +++++- src/inner/worker.rs | 97 ++++++++++++++++--- src/lib.rs | 5 + 9 files changed, 350 insertions(+), 108 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index e3fd4c8..3e489b9 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,6 +1,10 @@ //! //! Configuration to initialize the Fenrir networking library +use crate::{ + connection::handshake::HandshakeID, + enc::{asym::KeyExchange, hkdf::HkdfKind, sym::CipherKind}, +}; use ::std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, num::NonZeroUsize, @@ -18,6 +22,14 @@ pub struct Config { pub listen: Vec, /// List of DNS resolvers to use pub resolvers: Vec, + /// Supported handshakes + pub handshakes: Vec, + /// Supported key exchanges + pub key_exchanges: Vec, + /// Supported Hkdfs + pub hkdfs: Vec, + /// Supported Ciphers + pub ciphers: Vec, } impl Default for Config { @@ -34,6 +46,10 @@ impl Default for Config { ), ], resolvers: Vec::new(), + handshakes: [HandshakeID::DirectorySynchronized].to_vec(), + key_exchanges: [KeyExchange::X25519DiffieHellman].to_vec(), + hkdfs: [HkdfKind::Sha3].to_vec(), + ciphers: [CipherKind::XChaCha20Poly1305].to_vec(), } } } diff --git a/src/connection/handshake/dirsync.rs b/src/connection/handshake/dirsync.rs index 775f301..02bb097 100644 --- a/src/connection/handshake/dirsync.rs +++ b/src/connection/handshake/dirsync.rs @@ -14,13 +14,41 @@ use crate::{ connection::{ProtocolVersion, ID}, enc::{ asym::{ExchangePubKey, KeyExchange, KeyID}, + hkdf::HkdfKind, sym::{CipherKind, HeadLen, Secret, TagLen}, + Random, }, }; use ::arrayref::array_mut_ref; -type Nonce = [u8; 16]; +// TODO: merge with crate::enc::sym::Nonce +/// random nonce +#[derive(Debug, Clone, Copy)] +pub struct Nonce([u8; 16]); + +impl Nonce { + /// Create a new random Nonce + pub fn new(rnd: &Random) -> Self { + use ::core::mem::MaybeUninit; + let mut out: MaybeUninit<[u8; 16]>; + #[allow(unsafe_code)] + unsafe { + out = MaybeUninit::uninit(); + let _ = rnd.fill(out.assume_init_mut()); + Self(out.assume_init()) + } + } + /// Length of the serialized Nonce + pub const fn len() -> usize { + 16 + } +} +impl From<&[u8; 16]> for Nonce { + fn from(raw: &[u8; 16]) -> Self { + Self(raw.clone()) + } +} /// Parsed handshake #[derive(Debug, Clone)] @@ -61,6 +89,8 @@ pub struct Req { pub key_id: KeyID, /// Selected key exchange pub exchange: KeyExchange, + /// Selected hkdf + pub hkdf: HkdfKind, /// Selected cipher pub cipher: CipherKind, /// Client ephemeral public key used for key exchanges @@ -77,6 +107,7 @@ impl Req { ProtocolVersion::len() + KeyID::len() + KeyExchange::len() + + HkdfKind::len() + CipherKind::len() + self.exchange_key.kind().pub_len() } @@ -91,6 +122,7 @@ impl Req { pub fn len(&self) -> usize { KeyID::len() + KeyExchange::len() + + HkdfKind::len() + CipherKind::len() + self.exchange_key.kind().pub_len() + self.data.len() @@ -121,18 +153,23 @@ impl super::HandshakeParsing for Req { Some(exchange) => exchange, None => return Err(Error::Parsing), }; - let cipher: CipherKind = match CipherKind::from_u8(raw[3]) { + let hkdf: HkdfKind = match HkdfKind::from_u8(raw[3]) { + Some(exchange) => exchange, + None => return Err(Error::Parsing), + }; + let cipher: CipherKind = match CipherKind::from_u8(raw[4]) { Some(cipher) => cipher, None => return Err(Error::Parsing), }; - let (exchange_key, len) = match ExchangePubKey::from_slice(&raw[4..]) { + let (exchange_key, len) = match ExchangePubKey::from_slice(&raw[5..]) { Ok(exchange_key) => exchange_key, Err(e) => return Err(e.into()), }; - let data = ReqInner::CipherText(raw.len() - (4 + len)); + let data = ReqInner::CipherText(raw.len() - (5 + len)); Ok(HandshakeData::DirSync(DirSync::Req(Self { key_id, exchange, + hkdf, cipher, exchange_key, data, @@ -253,7 +290,7 @@ pub struct ReqData { impl ReqData { /// actual length of the request data pub fn len(&self) -> usize { - self.nonce.len() + KeyID::len() + ID::len() + self.auth.len() + Nonce::len() + KeyID::len() + ID::len() + self.auth.len() } /// Minimum byte length of the request data pub const MIN_PKT_LEN: usize = @@ -265,7 +302,8 @@ impl ReqData { } let mut start = 0; let mut end = 16; - let nonce: Nonce = raw[start..end].try_into().unwrap(); + let raw_sized: &[u8; 16] = raw[start..end].try_into().unwrap(); + let nonce: Nonce = raw_sized.into(); start = end; end = end + KeyID::len(); let client_key_id = @@ -440,7 +478,7 @@ impl RespData { assert!(out.len() == Self::len(), "wrong buffer size"); let mut start = 0; let mut end = Self::NONCE_LEN; - out[start..end].copy_from_slice(&self.client_nonce); + out[start..end].copy_from_slice(&self.client_nonce.0); start = end; end = end + Self::NONCE_LEN; self.id.serialize(&mut out[start..end]); diff --git a/src/connection/handshake/mod.rs b/src/connection/handshake/mod.rs index 76eb353..778a003 100644 --- a/src/connection/handshake/mod.rs +++ b/src/connection/handshake/mod.rs @@ -27,6 +27,37 @@ pub enum Error { NotEnoughData, } +/// List of possible handshakes +#[derive(::num_derive::FromPrimitive, Debug, Clone, Copy, PartialEq)] +#[repr(u8)] +pub enum HandshakeID { + /// 1-RTT Directory synchronized handshake. Fast, no forward secrecy + DirectorySynchronized = 0, + /// 2-RTT Stateful exchange. Little DDos protection + Stateful, + /// 3-RTT stateless exchange. Forward secrecy and ddos protection + Stateless, +} + +impl TryFrom<&str> for HandshakeID { + type Error = ::std::io::Error; + // TODO: from actual names, not only numeric + fn try_from(raw: &str) -> Result { + if let Ok(handshake_u8) = raw.parse::() { + if handshake_u8 >= 1 { + if let Some(handshake) = HandshakeID::from_u8(handshake_u8 - 1) + { + return Ok(handshake); + } + } + } + return Err(::std::io::Error::new( + ::std::io::ErrorKind::InvalidData, + "Unknown handshake ID", + )); + } +} + pub(crate) struct HandshakeServer { pub id: crate::enc::asym::KeyID, pub key: crate::enc::asym::PrivKey, diff --git a/src/dnssec/record.rs b/src/dnssec/record.rs index 51f7fd3..2457c0b 100644 --- a/src/dnssec/record.rs +++ b/src/dnssec/record.rs @@ -19,15 +19,15 @@ //! * 2..4 priority (for failover) //! * 5..7 weight between priority //! * 1 byte: divided in half: -//! * half: num of public key ids -//! * half: num of handhskae ids +//! * half: num of public key indexes +//! * half: num of handshake ids //! * 2 bytes: UDP port -//! * [ 1 byte per public key id ] +//! * [ HALF byte per public key idx ] (index on the list of public keys) //! * [ 1 byte per handshake id ] //! * X bytes: IP //! ] -//! [ # list of pubkeys -//! * 1 byte: pubkey id +//! [ # list of pubkeys (max: 16) +//! * 2 byte: pubkey id //! * 1 byte: pubkey length //! * 1 byte: pubkey type //! * Y bytes: pubkey @@ -42,11 +42,14 @@ //! * 1 byte for each cipher //! ] -use crate::enc::{ - self, - asym::{KeyExchange, PubKey}, - hkdf::HkdfKind, - sym::CipherKind, +use crate::{ + connection::handshake::HandshakeID, + enc::{ + self, + asym::{KeyExchange, KeyID, PubKey}, + hkdf::HkdfKind, + sym::CipherKind, + }, }; use ::core::num::NonZeroU16; use ::num_traits::FromPrimitive; @@ -55,22 +58,12 @@ use ::std::{net::IpAddr, vec::Vec}; * Public key data */ -/// Public Key ID +/// Public Key Index. +/// this points to the index of the public key in the array of public keys. +/// needed to have one byte less in the list of public keys +/// supported by and address #[derive(Debug, Copy, Clone, PartialEq)] -pub struct PublicKeyID(u8); - -impl TryFrom<&str> for PublicKeyID { - type Error = ::std::io::Error; - fn try_from(raw: &str) -> Result { - if let Ok(id_u8) = raw.parse::() { - return Ok(PublicKeyID(id_u8)); - } - return Err(::std::io::Error::new( - ::std::io::ErrorKind::InvalidData, - "Public Key ID must be between 0 and 256", - )); - } -} +pub struct PubKeyIdx(pub u8); /* * Address data @@ -168,37 +161,6 @@ impl TryFrom<&str> for AddressWeight { } } -/// List of possible handshakes -#[derive(::num_derive::FromPrimitive, Debug, Clone, Copy)] -#[repr(u8)] -pub enum HandshakeID { - /// 1-RTT Directory synchronized handshake. Fast, no forward secrecy - DirectorySynchronized = 0, - /// 2-RTT Stateful exchange. Little DDos protection - Stateful, - /// 3-RTT stateless exchange. Forward secrecy and ddos protection - Stateless, -} - -impl TryFrom<&str> for HandshakeID { - type Error = ::std::io::Error; - // TODO: from actual names, not only numeric - fn try_from(raw: &str) -> Result { - if let Ok(handshake_u8) = raw.parse::() { - if handshake_u8 >= 1 { - if let Some(handshake) = HandshakeID::from_u8(handshake_u8 - 1) - { - return Ok(handshake); - } - } - } - return Err(::std::io::Error::new( - ::std::io::ErrorKind::InvalidData, - "Unknown handshake ID", - )); - } -} - /// Authentication server address information: /// * ip /// * udp port @@ -220,14 +182,16 @@ pub struct Address { /// List of supported handshakes pub handshake_ids: Vec, /// Public key IDs used by this address - pub public_key_ids: Vec, + pub public_key_idx: Vec, } impl Address { fn raw_len(&self) -> usize { // UDP port + Priority + Weight + pubkey_len + handshake_len let mut size = 6; - size = size + self.public_key_ids.len() + self.handshake_ids.len(); + let num_pubkey_idx = self.public_key_idx.len(); + let idx_bytes = (num_pubkey_idx / 2) + (num_pubkey_idx % 2); + size = size + idx_bytes + self.handshake_ids.len(); size + match self.ip { IpAddr::V4(_) => size + 4, IpAddr::V6(_) => size + 16, @@ -244,7 +208,7 @@ impl Address { bitfield |= self.weight as u8; raw.push(bitfield); - let len_combined: u8 = self.public_key_ids.len() as u8; + let len_combined: u8 = self.public_key_idx.len() as u8; let len_combined = len_combined << 4; let len_combined = len_combined | self.handshake_ids.len() as u8; raw.push(len_combined); @@ -256,8 +220,18 @@ impl Address { }), ); - for id in self.public_key_ids.iter() { - raw.push(id.0); + // pair every idx, since the max is 16 + for chunk in self.public_key_idx.chunks(2) { + let second = { + if chunk.len() == 2 { + chunk[1].0 + } else { + 0 + } + }; + let tmp = chunk[0].0 << 4; + let tmp = tmp | second; + raw.push(tmp); } for id in self.handshake_ids.iter() { raw.push(*id as u8); @@ -301,21 +275,29 @@ impl Address { }; // Add publickey ids - let num_pubkey_ids = (raw[3] >> 4) as usize; + let num_pubkey_idx = (raw[3] >> 4) as usize; let num_handshake_ids = (raw[3] & 0x0F) as usize; - if raw.len() <= 3 + num_pubkey_ids + num_handshake_ids { + if raw.len() <= 3 + num_pubkey_idx + num_handshake_ids { return Err(Error::NotEnoughData(3)); } let mut bytes_parsed = 4; - let mut public_key_ids = Vec::with_capacity(num_pubkey_ids); - for raw_pubkey_id in - raw[bytes_parsed..(bytes_parsed + num_pubkey_ids)].iter() + let mut public_key_idx = Vec::with_capacity(num_pubkey_idx); + let idx_bytes = (num_pubkey_idx / 2) + (num_pubkey_idx % 2); + let mut idx_added = 0; + for raw_pubkey_idx_pair in + raw[bytes_parsed..(bytes_parsed + idx_bytes)].iter() { - public_key_ids.push(PublicKeyID(*raw_pubkey_id)); + let first = PubKeyIdx(raw_pubkey_idx_pair >> 4); + let second = PubKeyIdx(raw_pubkey_idx_pair & 0x0F); + public_key_idx.push(first); + if num_pubkey_idx - idx_added >= 2 { + public_key_idx.push(second); + } + idx_added = idx_added + 2; } // add handshake ids - bytes_parsed = bytes_parsed + num_pubkey_ids; + bytes_parsed = bytes_parsed + idx_bytes; let mut handshake_ids = Vec::with_capacity(num_handshake_ids); for raw_handshake_id in raw[bytes_parsed..(bytes_parsed + num_handshake_ids)].iter() @@ -358,7 +340,7 @@ impl Address { port, priority, weight, - public_key_ids, + public_key_idx, handshake_ids, }, bytes_parsed, @@ -374,7 +356,7 @@ impl Address { #[derive(Debug, Clone)] pub struct Record { /// Public keys used by any authentication server - pub public_keys: Vec<(PublicKeyID, PubKey)>, + pub public_keys: Vec<(KeyID, PubKey)>, /// List of all authentication servers' addresses. /// Multiple ones can point to the same authentication server pub addresses: Vec
, @@ -443,7 +425,8 @@ impl Record { address.encode_into(&mut raw); } for (public_key_id, public_key) in self.public_keys.iter() { - raw.push(public_key_id.0); + let key_id_bytes = public_key_id.0.to_le_bytes(); + raw.extend_from_slice(&key_id_bytes); raw.push(public_key.kind().pub_len() as u8); raw.push(public_key.kind() as u8); public_key.serialize_into(&mut raw); @@ -462,8 +445,8 @@ impl Record { } /// Decode from base85 to the actual object pub fn decode(raw: &[u8]) -> Result { - // bare minimum for 1 address, 1 key, 1 key exchange and 1 cipher - const MIN_RAW_LENGTH: usize = 1 + 1 + 1 + 8 + 9 + 1 + 1; + // bare minimum for lengths, (1 address), (1 key), cipher negotiation + const MIN_RAW_LENGTH: usize = 3 + (6 + 4) + (4 + 32) + 1 + 1 + 1; if raw.len() <= MIN_RAW_LENGTH { return Err(Error::NotEnoughData(0)); } @@ -499,11 +482,14 @@ impl Record { num_addresses = num_addresses - 1; } while num_public_keys > 0 { - if bytes_parsed + 2 >= raw.len() { + if bytes_parsed + 3 >= raw.len() { return Err(Error::NotEnoughData(bytes_parsed)); } - let id = PublicKeyID(raw[bytes_parsed]); - bytes_parsed = bytes_parsed + 1; + + let raw_key_id = + u16::from_le_bytes([raw[bytes_parsed], raw[bytes_parsed + 1]]); + let id = KeyID(raw_key_id); + bytes_parsed = bytes_parsed + 2; let pubkey_length = raw[bytes_parsed] as usize; bytes_parsed = bytes_parsed + 1; if pubkey_length + bytes_parsed >= raw.len() { @@ -590,6 +576,13 @@ impl Record { result.ciphers.push(cipher); num_ciphers = num_ciphers - 1; } + for addr in result.addresses.iter() { + for idx in addr.public_key_idx.iter() { + if idx.0 as usize >= result.public_keys.len() { + return Err(Error::Max16PublicKeys); + } + } + } if bytes_parsed != raw.len() { Err(Error::UnknownData(bytes_parsed)) } else { diff --git a/src/enc/asym.rs b/src/enc/asym.rs index ff8dda1..49f530c 100644 --- a/src/enc/asym.rs +++ b/src/enc/asym.rs @@ -3,7 +3,10 @@ use ::num_traits::FromPrimitive; use super::Error; -use crate::enc::{sym::Secret, Random}; +use crate::{ + config::Config, + enc::{sym::Secret, Random}, +}; /// Public key ID #[derive(Debug, Copy, Clone, PartialEq)] @@ -68,8 +71,19 @@ impl KeyKind { KeyKind::X25519 => KeyCapabilities::Exchange, } } + /// Returns the key exchanges supported by this key + pub fn key_exchanges(&self) -> &'static [KeyExchange] { + const EMPTY: [KeyExchange; 0] = []; + const X25519_KEY_EXCHANGES: [KeyExchange; 1] = + [KeyExchange::X25519DiffieHellman]; + match self { + KeyKind::Ed25519 => &EMPTY, + KeyKind::X25519 => &X25519_KEY_EXCHANGES, + } + } } +// FIXME: rename in KeyExchangeKind /// Kind of key exchange #[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)] #[non_exhaustive] @@ -279,7 +293,28 @@ impl ExchangePubKey { } } -/// Build a new pair of private/public key pair -pub fn new_keypair(kind: KeyKind, rnd: &Random) -> (PrivKey, PubKey) { - todo!() +/// Select the best key exchange from our supported list +/// and the other endpoint supported list. +/// Give priority to our list +pub fn server_select_key_exchange( + cfg: &Config, + client_supported: &Vec, +) -> Option { + cfg.key_exchanges + .iter() + .find(|k| client_supported.contains(k)) + .copied() +} +/// Select the best key exchange from our supported list +/// and the other endpoint supported list. +/// Give priority to the server list +/// This is used only in the Directory Synchronized handshake +pub fn client_select_key_exchange( + cfg: &Config, + server_supported: &Vec, +) -> Option { + server_supported + .iter() + .find(|k| cfg.key_exchanges.contains(k)) + .copied() } diff --git a/src/enc/hkdf.rs b/src/enc/hkdf.rs index a4b6868..872b7f0 100644 --- a/src/enc/hkdf.rs +++ b/src/enc/hkdf.rs @@ -4,7 +4,7 @@ use ::sha3::Sha3_256; use ::zeroize::Zeroize; -use crate::enc::sym::Secret; +use crate::{config::Config, enc::sym::Secret}; /// Kind of HKDF #[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)] @@ -14,6 +14,12 @@ pub enum HkdfKind { /// Sha3 Sha3 = 0, } +impl HkdfKind { + /// Length of the serialized type + pub const fn len() -> usize { + 1 + } +} /// Generic wrapper on Hkdfs #[derive(Clone)] @@ -52,6 +58,8 @@ impl Hkdf { // we can't use #[derive(Zeroing)] either. // So we craete a union with a Zeroing object, and drop both manually. +// TODO: move this to Hkdf instead of Sha3 + #[derive(Zeroize)] #[zeroize(drop)] struct Zeroable([u8; ::core::mem::size_of::<::hkdf::Hkdf>()]); @@ -89,7 +97,7 @@ pub struct HkdfSha3 { impl HkdfSha3 { /// Instantiate a new HKDF with Sha3-256 - pub fn new(salt: &[u8], key: Secret) -> Self { + pub(crate) fn new(salt: &[u8], key: Secret) -> Self { let hkdf = ::hkdf::Hkdf::::new(Some(salt), key.as_ref()); Self { inner: HkdfInner { @@ -98,7 +106,7 @@ impl HkdfSha3 { } } /// Get a secret generated from the key and a given context - pub fn get_secret(&self, context: &[u8]) -> Secret { + pub(crate) fn get_secret(&self, context: &[u8]) -> Secret { let mut out: [u8; 32] = [0; 32]; #[allow(unsafe_code)] unsafe { @@ -117,3 +125,29 @@ impl ::core::fmt::Debug for HkdfSha3 { ::core::fmt::Debug::fmt("[hidden hkdf]", f) } } + +/// Select the best hkdf from our supported list +/// and the other endpoint supported list. +/// Give priority to our list +pub fn server_select_hkdf( + cfg: &Config, + client_supported: &Vec, +) -> Option { + cfg.hkdfs + .iter() + .find(|h| client_supported.contains(h)) + .copied() +} +/// Select the best hkdf from our supported list +/// and the other endpoint supported list. +/// Give priority to the server list +/// this is used only in the directory synchronized handshake +pub fn client_select_hkdf( + cfg: &Config, + server_supported: &Vec, +) -> Option { + server_supported + .iter() + .find(|h| cfg.hkdfs.contains(h)) + .copied() +} diff --git a/src/enc/sym.rs b/src/enc/sym.rs index d9673a0..f8d76e3 100644 --- a/src/enc/sym.rs +++ b/src/enc/sym.rs @@ -1,7 +1,7 @@ //! Symmetric cypher stuff use super::Error; -use crate::enc::Random; +use crate::{config::Config, enc::Random}; use ::zeroize::Zeroize; /// Secret, used for keys. @@ -299,7 +299,7 @@ impl XChaCha20Poly1305 { } // -// TODO: For efficiency "Nonce" should become a reference. +// TODO: Merge crate::{enc::sym::Nonce, connection::handshake::dirsync::Nonce} // #[derive(Debug, Copy, Clone)] @@ -387,3 +387,28 @@ impl NonceSync { old_nonce } } +/// Select the best cipher from our supported list +/// and the other endpoint supported list. +/// Give priority to our list +pub fn server_select_cipher( + cfg: &Config, + client_supported: &Vec, +) -> Option { + cfg.ciphers + .iter() + .find(|c| client_supported.contains(c)) + .copied() +} +/// Select the best cipher from our supported list +/// and the other endpoint supported list. +/// Give priority to the server list +/// This is used only in the Directory synchronized handshake +pub fn client_select_cipher( + cfg: &Config, + server_supported: &Vec, +) -> Option { + server_supported + .iter() + .find(|c| cfg.ciphers.contains(c)) + .copied() +} diff --git a/src/inner/worker.rs b/src/inner/worker.rs index 1e34b56..5ce3201 100644 --- a/src/inner/worker.rs +++ b/src/inner/worker.rs @@ -1,6 +1,7 @@ //! Worker thread implementation use crate::{ auth::{ServiceID, TokenChecker}, + config::Config, connection::{ self, handshake::{ @@ -12,9 +13,9 @@ use crate::{ }, dnssec, enc::{ - asym::PubKey, - hkdf::{Hkdf, HkdfKind}, - sym::Secret, + asym::{self, PubKey}, + hkdf::{self, Hkdf, HkdfKind}, + sym::{self, Secret}, Random, }, inner::{HandshakeAction, HandshakeTracker, ThreadTracker}, @@ -53,6 +54,7 @@ pub(crate) enum WorkAnswer { /// Actual worker implementation. pub(crate) struct Worker { + cfg: Config, thread_id: ThreadTracker, // PERF: rand uses syscalls. how to do that async? rand: Random, @@ -67,6 +69,7 @@ pub(crate) struct Worker { impl Worker { pub(crate) async fn new_and_loop( + cfg: Config, thread_id: ThreadTracker, stop_working: ::tokio::sync::broadcast::Receiver, token_check: Option>>, @@ -75,6 +78,7 @@ impl Worker { ) -> ::std::io::Result<()> { // TODO: get a channel to send back information, and send the error let mut worker = Self::new( + cfg, thread_id, stop_working, token_check, @@ -86,6 +90,7 @@ impl Worker { Ok(()) } pub(crate) async fn new( + cfg: Config, thread_id: ThreadTracker, stop_working: ::tokio::sync::broadcast::Receiver, token_check: Option>>, @@ -126,6 +131,7 @@ impl Worker { }; Ok(Self { + cfg, thread_id, rand: Random::new(), stop_working, @@ -156,19 +162,52 @@ impl Worker { let _ = sender.send(conn_num); } Work::Connect((send_res, dnssec_record, _service_id)) => { + // PERF: geolocation + + // Find the first destination with a coherent + // pubkey/key exchange 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, + if addr + .handshake_ids + .iter() + .find(|h_srv| { + self.cfg.handshakes.contains(h_srv) + }) + .is_none() + { + // skip servers with no corresponding + // handshake types + return None; } + + for idx in addr.public_key_idx.iter() { + let key_supported_k_x = + dnssec_record.public_keys[idx.0 as usize] + .1 + .kind() + .key_exchanges(); + match self + .cfg + .key_exchanges + .iter() + .find(|x| key_supported_k_x.contains(x)) + { + Some(exchange) => { + return Some(( + addr, + dnssec_record.public_keys + [idx.0 as usize], + exchange, + )) + } + None => return None, + } + } + return None; }); - let (addr, key) = match destination { - Some((addr, key)) => (addr, key), + let (addr, key, exchange) = match destination { + Some((addr, key, exchange)) => (addr, key, exchange), None => { let _ = send_res.send(Err(crate::Error::Resolution( @@ -178,8 +217,29 @@ impl Worker { continue 'mainloop; } }; - use crate::enc::asym; - let exchange = asym::KeyExchange::X25519DiffieHellman; + let hkdf = match hkdf::client_select_hkdf( + &self.cfg, + &dnssec_record.hkdfs, + ) { + Some(hkdf) => hkdf, + None => { + let _ = send_res + .send(Err(crate::Error::HandshakeNegotiation)); + continue 'mainloop; + } + }; + let cipher = match sym::client_select_cipher( + &self.cfg, + &dnssec_record.ciphers, + ) { + Some(cipher) => cipher, + None => { + let _ = send_res + .send(Err(crate::Error::HandshakeNegotiation)); + continue 'mainloop; + } + }; + let (priv_key, pub_key) = match exchange.new_keypair(&self.rand) { Ok(pair) => pair, @@ -187,10 +247,15 @@ impl Worker { }; // build request /* + let req_data = dirsync::ReqData { + nonce: dirsync::Nonce::new(&self.rand), + client_key_id: + }; let req = dirsync::Req { key_id: key.0, - exchange: exchange, - cipher: 42, + exchange, + hkdf, + cipher, exchange_key: client_pub_key, data: 42, }; diff --git a/src/lib.rs b/src/lib.rs index d09d39e..6156f21 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,9 @@ pub enum Error { /// Resolution problems. wrong or incomplete DNSSEC data #[error("DNSSEC resolution: {0}")] Resolution(String), + /// No common cryptographic primitives + #[error("No common cryptographic primitives")] + HandshakeNegotiation, } /// Instance of a fenrir endpoint @@ -381,6 +384,7 @@ impl Fenrir { ::tracing::debug!("Spawning thread {}", core); let th_topology = hw_topology.clone(); let th_tokio_rt = tokio_rt.clone(); + let th_config = self.cfg.clone(); let (work_send, work_recv) = ::async_channel::unbounded::(); let th_stop_working = self.stop_working.subscribe(); let th_token_check = self.token_check.clone(); @@ -417,6 +421,7 @@ impl Fenrir { let _ = tk_local.block_on( &th_tokio_rt, Worker::new_and_loop( + th_config, thread_id, th_stop_working, th_token_check,