Initial half-done Handshake stubs

Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
Luca Fulchir 2023-02-15 15:50:54 +01:00
parent 70038b8558
commit e3af78eaba
Signed by: luca.fulchir
GPG Key ID: 8F6440603D13A78E
7 changed files with 266 additions and 13 deletions

View File

@ -32,6 +32,7 @@ futures = { version = "^0.3" }
libc = { version = "^0.2" } libc = { version = "^0.2" }
num-traits = { version = "^0.2" } num-traits = { version = "^0.2" }
num-derive = { version = "^0.3" } num-derive = { version = "^0.3" }
ring = { version = "^0.16" }
strum = { version = "^0.24" } strum = { version = "^0.24" }
strum_macros = { version = "^0.24" } strum_macros = { version = "^0.24" }
thiserror = { version = "^1.0" } thiserror = { version = "^1.0" }

View File

@ -1,3 +0,0 @@
//! Handhsake handling

View File

@ -0,0 +1,97 @@
//! Directory synchronized handshake
//! 1-RTT connection
//!
//! The simplest, fastest handshake supported by Fenrir
//! Downside: It does not offer protection from DDos,
//! no perfect forward secrecy
//!
//! To grant a form of perfect forward secrecy, the server should periodically
//! change the DNSSEC public/private keys
use super::{Error, HandshakeData};
use crate::connection::{KeyID, UnparsedExchangePubKey, ID};
use ::std::vec::Vec;
/// Parsed handshake
#[derive(Debug, Clone)]
pub enum DirSync {
/// Directory synchronized handshake: client request
Req(Req),
/// Directory synchronized handshake: server response
Resp(Resp),
}
/// Client request of a directory synchronized handshake
#[derive(Debug, Clone)]
pub struct Req {
/// Id of the server key used for the key exchange
key_id: KeyID,
/// Client ephemeral public key used for key exchanges
exchange_key: UnparsedExchangePubKey,
/// encrypted data
enc: Vec<u8>,
}
impl super::HandshakeParsing for Req {
fn parse(raw: &[u8]) -> Result<HandshakeData, Error> {
const MIN_PKT_LEN: usize = 8;
if raw.len() < MIN_PKT_LEN {
return Err(Error::NotEnoughData);
}
let key_id: KeyID =
KeyID(u16::from_le_bytes(raw[0..1].try_into().unwrap()));
let (exchange_key, len) =
match UnparsedExchangePubKey::from_raw(&raw[2..]) {
Ok(exchange_key) => exchange_key,
Err(e) => return Err(e),
};
let enc = raw[(2 + len)..].to_vec();
Ok(HandshakeData::DirSync(DirSync::Req(Self {
key_id,
exchange_key,
enc,
})))
}
}
/// Decrypted request data
#[derive(Debug, Clone, Copy)]
pub struct ReqData {
/// Random nonce, the client can use this to track multiple key exchanges
nonce: [u8; 16],
/// Client key id so the client can use and rotate keys
client_key_id: KeyID,
/// Authentication token
token: [u8; 32],
/// Receiving connection id for the client
id: ID,
// TODO: service info
}
/// Server response in a directory synchronized handshake
#[derive(Debug, Clone)]
pub struct Resp {
/// Tells the client with which key the exchange was done
client_key_id: KeyID,
/// encrypted data
enc: Vec<u8>,
}
impl super::HandshakeParsing for Resp {
fn parse(raw: &[u8]) -> Result<HandshakeData, Error> {
todo!()
}
}
/// Decrypted response data
#[derive(Debug, Clone, Copy)]
pub struct RespData {
/// Client nonce, copied from the request
client_nonce: [u8; 16],
/// Server Connection ID
id: ID,
/// Service Connection ID
service_id: ID,
/// Service encryption key
service_key: [u8; 32],
}

View File

@ -0,0 +1,83 @@
//! Handhsake handling
pub mod dirsync;
use ::num_traits::FromPrimitive;
use crate::connection::ProtocolVersion;
/// Handshake errors
#[derive(::thiserror::Error, Debug, Copy, Clone)]
pub enum Error {
/// Error while parsing the handshake packet
/// TODO: more detailed parsing errors
#[error("not an handshake packet")]
Parsing,
/// Not enough data
#[error("not enough data")]
NotEnoughData,
}
/// Parsed handshake
#[derive(Debug, Clone)]
pub enum HandshakeData {
/// Directory synchronized handhsake
DirSync(dirsync::DirSync),
}
/// Kind of handshake
#[derive(::num_derive::FromPrimitive, Debug, Clone, Copy)]
#[repr(u8)]
pub enum Kind {
/// 1-RTT, Directory synchronized handshake
/// Request
DirSyncReq = 0,
/// 1-RTT, Directory synchronized handshake
/// Response
DirSyncResp,
/*
/// 2-RTT, Stateful connection
Stateful,
...
/// 3 RTT, Stateless connection
Stateless,
....
*/
}
/// Parsed handshake
#[derive(Debug, Clone)]
pub struct Handshake {
/// Fenrir Protocol version
fenrir_version: ProtocolVersion,
/// enum for the parsed data
data: HandshakeData,
}
impl Handshake {
const MIN_PKT_LEN: usize = 8;
/// Parse the packet and return the parsed handshake
pub fn parse(raw: &[u8]) -> Result<Self, Error> {
if raw.len() < Self::MIN_PKT_LEN {
return Err(Error::NotEnoughData);
}
let fenrir_version = match ProtocolVersion::from_u8(raw[0]) {
Some(fenrir_version) => fenrir_version,
None => return Err(Error::Parsing),
};
let handshake_kind = match Kind::from_u8(raw[1]) {
Some(handshake_kind) => handshake_kind,
None => return Err(Error::Parsing),
};
let parsed = match handshake_kind {
Kind::DirSyncReq => dirsync::Req::parse(&raw[2..])?,
Kind::DirSyncResp => dirsync::Resp::parse(&raw[2..])?,
};
todo!()
}
}
trait HandshakeParsing {
fn parse(raw: &[u8]) -> Result<HandshakeData, Error>;
}

View File

@ -1,6 +1,74 @@
//! Connection handling and send/receive queues //! Connection handling and send/receive queues
mod handshake; pub mod handshake;
mod packet; mod packet;
pub use packet::ConnectionID;
use ::num_traits::FromPrimitive;
use ::std::vec::Vec;
pub use handshake::Handshake;
pub use packet::ConnectionID as ID;
pub use packet::Packet; pub use packet::Packet;
/// Public key ID
#[derive(Debug, Copy, Clone)]
pub struct KeyID(u16);
/// Version of the fenrir protocol in use
#[derive(::num_derive::FromPrimitive, Debug, Copy, Clone)]
#[repr(u8)]
pub enum ProtocolVersion {
/// First Fenrir Protocol Version
V0 = 0,
}
/// Kind of key in the key exchange
#[derive(Debug, Copy, Clone, ::num_derive::FromPrimitive)]
#[repr(u8)]
pub enum ExchangePubKeyKind {
/// X25519 Public key
X25519 = 0,
}
/// all Ephemeral Public key types
#[derive(Debug, Clone)]
pub enum ExchangePubKey {
/// X25519(Curve25519) used for key exchange
X25519(Vec<u8>),
}
/// Mark the Ephemeral key as yet unparsed, meaning it could be
/// maliciously currupted data
#[derive(Debug, Clone)]
pub struct UnparsedExchangePubKey(ExchangePubKey);
impl UnparsedExchangePubKey {
/// Load public key used for key exchange from it raw bytes
/// The riesult is "unparsed" since we don't verify
/// the actual key
pub fn from_raw(raw: &[u8]) -> Result<(Self, usize), handshake::Error> {
// FIXME: get *real* minimum key size
const MIN_KEY_SIZE: usize = 8;
if raw.len() < 1 + MIN_KEY_SIZE {
return Err(handshake::Error::NotEnoughData);
}
match ExchangePubKeyKind::from_u8(raw[0]) {
Some(kind) => match kind {
ExchangePubKeyKind::X25519 => {
// FIXME: is this really the
const len: usize =
1 + ::ring::signature::ED25519_PUBLIC_KEY_LEN;
Ok((
UnparsedExchangePubKey(ExchangePubKey::X25519(
raw[1..len].to_vec(),
)),
len,
))
}
},
None => {
return Err(handshake::Error::Parsing);
}
}
}
}

View File

@ -57,6 +57,9 @@ impl TryFrom<&str> for PublicKeyId {
pub enum PublicKeyType { pub enum PublicKeyType {
/// ed25519 asymmetric key /// ed25519 asymmetric key
Ed25519 = 0, Ed25519 = 0,
/// Ephemeral X25519 (Curve25519) key.
/// Used in the directory synchronized handshake
X25519,
} }
impl PublicKeyType { impl PublicKeyType {
@ -64,6 +67,7 @@ impl PublicKeyType {
pub fn key_len(&self) -> usize { pub fn key_len(&self) -> usize {
match &self { match &self {
PublicKeyType::Ed25519 => 32, PublicKeyType::Ed25519 => 32,
PublicKeyType::X25519 => 32, // FIXME: hopefully...
} }
} }
} }
@ -116,9 +120,8 @@ impl PublicKey {
} }
let mut raw_key = Vec::with_capacity(kind.key_len()); let mut raw_key = Vec::with_capacity(kind.key_len());
raw_key.extend_from_slice(&raw[2..(2 + kind.key_len())]);
let total_length = 2 + kind.key_len(); let total_length = 2 + kind.key_len();
raw_key.extend_from_slice(&raw[2..total_length]);
Ok(( Ok((
Self { Self {
@ -243,7 +246,7 @@ pub struct Address {
pub priority: AddressPriority, pub priority: AddressPriority,
/// Weight of this address in the priority group /// Weight of this address in the priority group
pub weight: AddressWeight, pub weight: AddressWeight,
/// Public key ID used by this address /// Public key IDs used by this address
pub public_key_id: PublicKeyId, pub public_key_id: PublicKeyId,
} }

View File

@ -24,15 +24,18 @@ pub mod dnssec;
/// Main fenrir library errors /// Main fenrir library errors
#[derive(::thiserror::Error, Debug)] #[derive(::thiserror::Error, Debug)]
pub enum Error { pub enum Error {
/// The library was not initialized (run .start())
#[error("not initialized")]
NotInitialized,
/// General I/O error /// General I/O error
#[error("IO: {0:?}")] #[error("IO: {0:?}")]
IO(#[from] ::std::io::Error), IO(#[from] ::std::io::Error),
/// Dnssec errors /// Dnssec errors
#[error("Dnssec: {0:?}")] #[error("Dnssec: {0:?}")]
Dnssec(#[from] dnssec::Error), Dnssec(#[from] dnssec::Error),
/// The library was not initialized (run .start()) /// Handshake errors
#[error("not initialized")] #[error("Handshake: {0:?}")]
NotInitialized, Handshake(#[from] connection::handshake::Error),
} }
/// Instance of a fenrir endpoint /// Instance of a fenrir endpoint
@ -224,11 +227,12 @@ impl Fenrir {
if buffer.len() < Self::MIN_PACKET_BYTES { if buffer.len() < Self::MIN_PACKET_BYTES {
return; return;
} }
use connection::ConnectionID; use connection::ID;
let raw_id: [u8; 8] = buffer.try_into().expect("unreachable"); let raw_id: [u8; 8] = buffer.try_into().expect("unreachable");
if ConnectionID::from(raw_id).is_handshake() { if ID::from(raw_id).is_handshake() {
todo!(); todo!();
} }
// copy packet, spawn
todo!(); todo!();
} }
} }