More on negotiation and dnssec record verification

Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
Luca Fulchir 2023-06-01 12:52:43 +02:00
parent 08d2755656
commit 5b338c8758
Signed by: luca.fulchir
GPG Key ID: 8F6440603D13A78E
5 changed files with 74 additions and 17 deletions

View File

@ -11,6 +11,7 @@ use ::std::rc::Rc;
/// Handshake errors /// Handshake errors
#[derive(::thiserror::Error, Debug, Copy, Clone)] #[derive(::thiserror::Error, Debug, Copy, Clone)]
#[non_exhaustive]
pub enum Error { pub enum Error {
/// Error while parsing the handshake packet /// Error while parsing the handshake packet
/// TODO: more detailed parsing errors /// TODO: more detailed parsing errors
@ -25,6 +26,12 @@ pub enum Error {
/// Not enough data /// Not enough data
#[error("not enough data")] #[error("not enough data")]
NotEnoughData, NotEnoughData,
/// Could not find common cryptography
#[error("Negotiation of keys/hkdfs/ciphers failed")]
Negotiation,
/// Could not generate Keys
#[error("Key generation failed")]
KeyGeneration,
} }
/// List of possible handshakes /// List of possible handshakes

View File

@ -46,7 +46,7 @@ use crate::{
connection::handshake::HandshakeID, connection::handshake::HandshakeID,
enc::{ enc::{
self, self,
asym::{KeyExchangeKind, KeyID, PubKey}, asym::{ExchangePubKey, KeyExchangeKind, KeyID, PubKey},
hkdf::HkdfKind, hkdf::HkdfKind,
sym::CipherKind, sym::CipherKind,
}, },
@ -498,7 +498,7 @@ impl Record {
let (public_key, bytes) = match PubKey::deserialize( let (public_key, bytes) = match PubKey::deserialize(
&raw[bytes_parsed..(bytes_parsed + pubkey_length)], &raw[bytes_parsed..(bytes_parsed + pubkey_length)],
) { ) {
Ok(public_key_and_bytes) => public_key_and_bytes, Ok((public_key, bytes)) => (public_key, bytes),
Err(enc::Error::UnsupportedKey(_)) => { Err(enc::Error::UnsupportedKey(_)) => {
// continue parsing. This could be a new pubkey type // continue parsing. This could be a new pubkey type
// that is not supported by an older client // that is not supported by an older client
@ -582,6 +582,14 @@ impl Record {
if idx.0 as usize >= result.public_keys.len() { if idx.0 as usize >= result.public_keys.len() {
return Err(Error::Max16PublicKeys); return Err(Error::Max16PublicKeys);
} }
if !result.public_keys[idx.0 as usize]
.1
.kind()
.capabilities()
.has_exchange()
{
return Err(Error::UnsupportedData(bytes_parsed));
}
} }
} }
if bytes_parsed != raw.len() { if bytes_parsed != raw.len() {

View File

@ -41,6 +41,17 @@ pub enum KeyCapabilities {
/// All: sign, encrypt, Key Exchange /// All: sign, encrypt, Key Exchange
SignEncryptExchage, SignEncryptExchage,
} }
impl KeyCapabilities {
/// Check if this key supports eky exchage
pub fn has_exchange(&self) -> bool {
match self {
KeyCapabilities::Exchange
| KeyCapabilities::SignExchange
| KeyCapabilities::SignEncryptExchage => true,
_ => false,
}
}
}
/// Kind of key used in the handshake /// Kind of key used in the handshake
#[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)]
@ -253,7 +264,7 @@ impl ExchangePubKey {
} }
/// serialize the key into the buffer /// serialize the key into the buffer
/// NOTE: Assumes there is enough space /// NOTE: Assumes there is enough space
fn serialize_into(&self, out: &mut [u8]) { pub fn serialize_into(&self, out: &mut [u8]) {
match self { match self {
ExchangePubKey::X25519(pk) => { ExchangePubKey::X25519(pk) => {
let bytes = pk.as_bytes(); let bytes = pk.as_bytes();

View File

@ -5,6 +5,7 @@ use crate::{
connection::{ connection::{
self, self,
handshake::{ handshake::{
self,
dirsync::{self, DirSync}, dirsync::{self, DirSync},
Handshake, HandshakeData, Handshake, HandshakeData,
}, },
@ -13,7 +14,7 @@ use crate::{
}, },
dnssec, dnssec,
enc::{ enc::{
asym::{self, PubKey}, asym::{self, PrivKey, PubKey},
hkdf::{self, Hkdf, HkdfKind}, hkdf::{self, Hkdf, HkdfKind},
sym::{self, Secret}, sym::{self, Secret},
Random, Random,
@ -181,6 +182,9 @@ impl Worker {
return None; return None;
} }
// make sure this server has a public key
// that supports one of the key exchanges that
// *we* support
for idx in addr.public_key_idx.iter() { for idx in addr.public_key_idx.iter() {
let key_supported_k_x = let key_supported_k_x =
dnssec_record.public_keys[idx.0 as usize] dnssec_record.public_keys[idx.0 as usize]
@ -198,7 +202,7 @@ impl Worker {
addr, addr,
dnssec_record.public_keys dnssec_record.public_keys
[idx.0 as usize], [idx.0 as usize],
exchange, exchange.clone(),
)) ))
} }
None => return None, None => return None,
@ -217,25 +221,27 @@ impl Worker {
continue 'mainloop; continue 'mainloop;
} }
}; };
let hkdf = match hkdf::client_select_hkdf( let hkdf_selected = match hkdf::client_select_hkdf(
&self.cfg, &self.cfg,
&dnssec_record.hkdfs, &dnssec_record.hkdfs,
) { ) {
Some(hkdf) => hkdf, Some(hkdf_selected) => hkdf_selected,
None => { None => {
let _ = send_res let _ = send_res.send(Err(
.send(Err(crate::Error::HandshakeNegotiation)); handshake::Error::Negotiation.into(),
));
continue 'mainloop; continue 'mainloop;
} }
}; };
let cipher = match sym::client_select_cipher( let cipher_selected = match sym::client_select_cipher(
&self.cfg, &self.cfg,
&dnssec_record.ciphers, &dnssec_record.ciphers,
) { ) {
Some(cipher) => cipher, Some(cipher_selected) => cipher_selected,
None => { None => {
let _ = send_res let _ = send_res.send(Err(
.send(Err(crate::Error::HandshakeNegotiation)); handshake::Error::Negotiation.into(),
));
continue 'mainloop; continue 'mainloop;
} }
}; };
@ -243,8 +249,36 @@ impl Worker {
let (priv_key, pub_key) = let (priv_key, pub_key) =
match exchange.new_keypair(&self.rand) { match exchange.new_keypair(&self.rand) {
Ok(pair) => pair, Ok(pair) => pair,
Err(_) => todo!(), Err(_) => {
::tracing::error!("Failed to generate keys");
let _ = send_res.send(Err(
handshake::Error::KeyGeneration.into(),
));
continue 'mainloop;
}
}; };
let hkdf;
if let PubKey::Exchange(srv_pub) = key.1 {
let secret =
match priv_key.key_exchange(exchange, srv_pub) {
Ok(secret) => secret,
Err(_) => {
::tracing::warn!(
"Could not run the key exchange"
);
let _ = send_res.send(Err(
handshake::Error::Negotiation.into(),
));
continue 'mainloop;
}
};
hkdf = Hkdf::new(hkdf_selected, b"fenrir", secret);
} else {
// crate::dnssec already verifies that the keys
// listed in dnssec::Record.addresses.public_key_idx
// are PubKey::Exchange
unreachable!()
}
// build request // build request
/* /*
let req_data = dirsync::ReqData { let req_data = dirsync::ReqData {

View File

@ -62,9 +62,6 @@ pub enum Error {
/// Resolution problems. wrong or incomplete DNSSEC data /// Resolution problems. wrong or incomplete DNSSEC data
#[error("DNSSEC resolution: {0}")] #[error("DNSSEC resolution: {0}")]
Resolution(String), Resolution(String),
/// No common cryptographic primitives
#[error("No common cryptographic primitives")]
HandshakeNegotiation,
} }
/// Instance of a fenrir endpoint /// Instance of a fenrir endpoint