More work on key exhcnage negotiation
Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
parent
1bae4c9953
commit
ac213a6528
|
@ -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<SocketAddr>,
|
||||
/// List of DNS resolvers to use
|
||||
pub resolvers: Vec<SocketAddr>,
|
||||
/// Supported handshakes
|
||||
pub handshakes: Vec<HandshakeID>,
|
||||
/// Supported key exchanges
|
||||
pub key_exchanges: Vec<KeyExchange>,
|
||||
/// Supported Hkdfs
|
||||
pub hkdfs: Vec<HkdfKind>,
|
||||
/// Supported Ciphers
|
||||
pub ciphers: Vec<CipherKind>,
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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<Self, Self::Error> {
|
||||
if let Ok(handshake_u8) = raw.parse::<u8>() {
|
||||
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,
|
||||
|
|
|
@ -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::{
|
||||
use crate::{
|
||||
connection::handshake::HandshakeID,
|
||||
enc::{
|
||||
self,
|
||||
asym::{KeyExchange, PubKey},
|
||||
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<Self, Self::Error> {
|
||||
if let Ok(id_u8) = raw.parse::<u8>() {
|
||||
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<Self, Self::Error> {
|
||||
if let Ok(handshake_u8) = raw.parse::<u8>() {
|
||||
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<HandshakeID>,
|
||||
/// Public key IDs used by this address
|
||||
pub public_key_ids: Vec<PublicKeyID>,
|
||||
pub public_key_idx: Vec<PubKeyIdx>,
|
||||
}
|
||||
|
||||
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<Address>,
|
||||
|
@ -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<Self, Error> {
|
||||
// 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 {
|
||||
|
|
|
@ -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<KeyExchange>,
|
||||
) -> Option<KeyExchange> {
|
||||
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<KeyExchange>,
|
||||
) -> Option<KeyExchange> {
|
||||
server_supported
|
||||
.iter()
|
||||
.find(|k| cfg.key_exchanges.contains(k))
|
||||
.copied()
|
||||
}
|
||||
|
|
|
@ -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<Sha3_256>>()]);
|
||||
|
@ -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::<Sha3_256>::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<HkdfKind>,
|
||||
) -> Option<HkdfKind> {
|
||||
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<HkdfKind>,
|
||||
) -> Option<HkdfKind> {
|
||||
server_supported
|
||||
.iter()
|
||||
.find(|h| cfg.hkdfs.contains(h))
|
||||
.copied()
|
||||
}
|
||||
|
|
|
@ -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<CipherKind>,
|
||||
) -> Option<CipherKind> {
|
||||
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<CipherKind>,
|
||||
) -> Option<CipherKind> {
|
||||
server_supported
|
||||
.iter()
|
||||
.find(|c| cfg.ciphers.contains(c))
|
||||
.copied()
|
||||
}
|
||||
|
|
|
@ -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<bool>,
|
||||
token_check: Option<Arc<Mutex<TokenChecker>>>,
|
||||
|
@ -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<bool>,
|
||||
token_check: Option<Arc<Mutex<TokenChecker>>>,
|
||||
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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::<Work>();
|
||||
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,
|
||||
|
|
Loading…
Reference in New Issue