Initial half-done Handshake stubs
Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
parent
70038b8558
commit
e3af78eaba
|
@ -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" }
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
//! Handhsake handling
|
|
||||||
|
|
||||||
|
|
|
@ -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],
|
||||||
|
}
|
|
@ -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>;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
src/lib.rs
14
src/lib.rs
|
@ -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!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue