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" }
|
||||
num-traits = { version = "^0.2" }
|
||||
num-derive = { version = "^0.3" }
|
||||
ring = { version = "^0.16" }
|
||||
strum = { version = "^0.24" }
|
||||
strum_macros = { version = "^0.24" }
|
||||
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
|
||||
|
||||
mod handshake;
|
||||
pub mod handshake;
|
||||
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;
|
||||
|
||||
/// 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 {
|
||||
/// ed25519 asymmetric key
|
||||
Ed25519 = 0,
|
||||
/// Ephemeral X25519 (Curve25519) key.
|
||||
/// Used in the directory synchronized handshake
|
||||
X25519,
|
||||
}
|
||||
|
||||
impl PublicKeyType {
|
||||
|
@ -64,6 +67,7 @@ impl PublicKeyType {
|
|||
pub fn key_len(&self) -> usize {
|
||||
match &self {
|
||||
PublicKeyType::Ed25519 => 32,
|
||||
PublicKeyType::X25519 => 32, // FIXME: hopefully...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,9 +120,8 @@ impl PublicKey {
|
|||
}
|
||||
|
||||
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();
|
||||
raw_key.extend_from_slice(&raw[2..total_length]);
|
||||
|
||||
Ok((
|
||||
Self {
|
||||
|
@ -243,7 +246,7 @@ pub struct Address {
|
|||
pub priority: AddressPriority,
|
||||
/// Weight of this address in the priority group
|
||||
pub weight: AddressWeight,
|
||||
/// Public key ID used by this address
|
||||
/// Public key IDs used by this address
|
||||
pub public_key_id: PublicKeyId,
|
||||
}
|
||||
|
||||
|
|
14
src/lib.rs
14
src/lib.rs
|
@ -24,15 +24,18 @@ pub mod dnssec;
|
|||
/// Main fenrir library errors
|
||||
#[derive(::thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
/// The library was not initialized (run .start())
|
||||
#[error("not initialized")]
|
||||
NotInitialized,
|
||||
/// General I/O error
|
||||
#[error("IO: {0:?}")]
|
||||
IO(#[from] ::std::io::Error),
|
||||
/// Dnssec errors
|
||||
#[error("Dnssec: {0:?}")]
|
||||
Dnssec(#[from] dnssec::Error),
|
||||
/// The library was not initialized (run .start())
|
||||
#[error("not initialized")]
|
||||
NotInitialized,
|
||||
/// Handshake errors
|
||||
#[error("Handshake: {0:?}")]
|
||||
Handshake(#[from] connection::handshake::Error),
|
||||
}
|
||||
|
||||
/// Instance of a fenrir endpoint
|
||||
|
@ -224,11 +227,12 @@ impl Fenrir {
|
|||
if buffer.len() < Self::MIN_PACKET_BYTES {
|
||||
return;
|
||||
}
|
||||
use connection::ConnectionID;
|
||||
use connection::ID;
|
||||
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!();
|
||||
}
|
||||
// copy packet, spawn
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue