diff --git a/Cargo.toml b/Cargo.toml index 84c0095..7f7ab32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ crate_type = [ "lib", "cdylib", "staticlib" ] [dependencies] # please keep these in alphabetical order +arc-swap = { version = "^1.6" } # base85 repo has no tags, fix on a commit. v1.1.1 points to older, wrong version base85 = { git = "https://gitlab.com/darkwyrm/base85", rev = "d98efbfd171dd9ba48e30a5c88f94db92fc7b3c6" } futures = { version = "^0.3" } @@ -33,6 +34,7 @@ libc = { version = "^0.2" } num-traits = { version = "^0.2" } num-derive = { version = "^0.3" } ring = { version = "^0.16" } +bincode = { version = "^1.3" } strum = { version = "^0.24" } strum_macros = { version = "^0.24" } thiserror = { version = "^1.0" } @@ -43,6 +45,7 @@ tracing = { version = "^0.1" } trust-dns-resolver = { version = "^0.22", features = [ "dnssec-ring" ] } trust-dns-client = { version = "^0.22", features = [ "dnssec" ] } trust-dns-proto = { version = "^0.22" } +x25519-dalek = { version = "^1.2", features = [ "serde" ] } [profile.dev] diff --git a/src/connection/handshake/dirsync.rs b/src/connection/handshake/dirsync.rs index 15ae3c5..add8fa8 100644 --- a/src/connection/handshake/dirsync.rs +++ b/src/connection/handshake/dirsync.rs @@ -9,7 +9,10 @@ //! change the DNSSEC public/private keys use super::{Error, HandshakeData}; -use crate::connection::{KeyID, UnparsedExchangePubKey, ID}; +use crate::{ + connection::ID, + enc::asym::{ExchangePubKey, KeyID}, +}; use ::std::vec::Vec; /// Parsed handshake @@ -25,11 +28,11 @@ pub enum DirSync { #[derive(Debug, Clone)] pub struct Req { /// Id of the server key used for the key exchange - key_id: KeyID, + pub key_id: KeyID, /// Client ephemeral public key used for key exchanges - exchange_key: UnparsedExchangePubKey, + pub exchange_key: ExchangePubKey, /// encrypted data - enc: Vec, + pub enc: Vec, } impl super::HandshakeParsing for Req { @@ -40,11 +43,10 @@ impl super::HandshakeParsing for Req { } 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 (exchange_key, len) = match ExchangePubKey::from_slice(&raw[2..]) { + Ok(exchange_key) => exchange_key, + Err(e) => return Err(e.into()), + }; let enc = raw[(2 + len)..].to_vec(); Ok(HandshakeData::DirSync(DirSync::Req(Self { key_id, @@ -58,13 +60,13 @@ impl super::HandshakeParsing for Req { #[derive(Debug, Clone, Copy)] pub struct ReqData { /// Random nonce, the client can use this to track multiple key exchanges - nonce: [u8; 16], + pub nonce: [u8; 16], /// Client key id so the client can use and rotate keys - client_key_id: KeyID, + pub client_key_id: KeyID, /// Authentication token - token: [u8; 32], + pub token: [u8; 32], /// Receiving connection id for the client - id: ID, + pub id: ID, // TODO: service info } @@ -72,9 +74,9 @@ pub struct ReqData { #[derive(Debug, Clone)] pub struct Resp { /// Tells the client with which key the exchange was done - client_key_id: KeyID, + pub client_key_id: KeyID, /// encrypted data - enc: Vec, + pub enc: Vec, } impl super::HandshakeParsing for Resp { diff --git a/src/connection/handshake/mod.rs b/src/connection/handshake/mod.rs index 0070df3..f1149bd 100644 --- a/src/connection/handshake/mod.rs +++ b/src/connection/handshake/mod.rs @@ -4,7 +4,7 @@ pub mod dirsync; use ::num_traits::FromPrimitive; -use crate::connection::ProtocolVersion; +use crate::connection::{self, ProtocolVersion}; /// Handshake errors #[derive(::thiserror::Error, Debug, Copy, Clone)] @@ -13,11 +13,22 @@ pub enum Error { /// TODO: more detailed parsing errors #[error("not an handshake packet")] Parsing, + /// No such Key ID + #[error("unknown key id")] + UnknownKeyID, + /// Key error + #[error("key: {0:?}")] + Key(#[from] crate::enc::Error), /// Not enough data #[error("not enough data")] NotEnoughData, } +pub(crate) struct HandshakeKey { + pub id: crate::enc::asym::KeyID, + pub key: crate::enc::asym::PrivKey, +} + /// Parsed handshake #[derive(Debug, Clone)] pub enum HandshakeData { @@ -49,9 +60,9 @@ pub enum Kind { #[derive(Debug, Clone)] pub struct Handshake { /// Fenrir Protocol version - fenrir_version: ProtocolVersion, + pub fenrir_version: ProtocolVersion, /// enum for the parsed data - data: HandshakeData, + pub data: HandshakeData, } impl Handshake { @@ -69,11 +80,16 @@ impl Handshake { Some(handshake_kind) => handshake_kind, None => return Err(Error::Parsing), }; - let parsed = match handshake_kind { + let data = match handshake_kind { Kind::DirSyncReq => dirsync::Req::parse(&raw[2..])?, Kind::DirSyncResp => dirsync::Resp::parse(&raw[2..])?, }; - + Ok(Self { + fenrir_version, + data, + }) + } + pub(crate) fn work(&self, keys: &[HandshakeKey]) -> Result<(), Error> { todo!() } } diff --git a/src/connection/mod.rs b/src/connection/mod.rs index ad20748..96dfd9c 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -10,10 +10,6 @@ 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)] @@ -21,54 +17,3 @@ 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), -} - -/// 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); - } - } - } -} diff --git a/src/dnssec/record.rs b/src/dnssec/record.rs index 76d89c2..5f5e09f 100644 --- a/src/dnssec/record.rs +++ b/src/dnssec/record.rs @@ -33,13 +33,13 @@ use ::std::{net::IpAddr, vec::Vec}; /// Public Key ID #[derive(Debug, Copy, Clone)] -pub struct PublicKeyId(u8); +pub struct PublicKeyID(u8); -impl TryFrom<&str> for PublicKeyId { +impl TryFrom<&str> for PublicKeyID { type Error = ::std::io::Error; fn try_from(raw: &str) -> Result { if let Ok(id_u8) = raw.parse::() { - return Ok(PublicKeyId(id_u8)); + return Ok(PublicKeyID(id_u8)); } return Err(::std::io::Error::new( ::std::io::ErrorKind::InvalidData, @@ -95,7 +95,7 @@ pub struct PublicKey { /// type of public key pub kind: PublicKeyType, /// id of public key - pub id: PublicKeyId, + pub id: PublicKeyID, } impl PublicKey { @@ -114,7 +114,7 @@ impl PublicKey { } let kind = PublicKeyType::from_u8(raw[0]).unwrap(); - let id = PublicKeyId(raw[1]); + let id = PublicKeyID(raw[1]); if raw.len() < 2 + kind.key_len() { return Err(Error::NotEnoughData(2)); } @@ -230,12 +230,45 @@ 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 { + if let Ok(handshake_u8) = raw.parse::() { + 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 /// * priority /// * weight within priority -#[derive(Debug, Clone, Copy)] +/// * list of supported handshakes IDs +/// * list of public keys IDs +#[derive(Debug, Clone)] pub struct Address { /// Ip address of server, v4 or v6 pub ip: IpAddr, @@ -246,14 +279,18 @@ pub struct Address { pub priority: AddressPriority, /// Weight of this address in the priority group pub weight: AddressWeight, + /// List of supported handshakes + pub handshake_ids: Vec, /// Public key IDs used by this address - pub public_key_id: PublicKeyId, + pub public_key_ids: Vec, } impl Address { fn raw_len(&self) -> usize { - let size = 4; // UDP port + Priority + Weight - match self.ip { + // UDP port + Priority + Weight + pubkey_len + handshake_len + let mut size = 6; + size = size + self.public_key_ids.len() + self.handshake_ids.len(); + size + match self.ip { IpAddr::V4(_) => size + 4, IpAddr::V6(_) => size + 16, } @@ -270,7 +307,6 @@ impl Address { raw.push(bitfield); - raw.push(self.public_key_id.0); raw.extend_from_slice( &(match self.port { Some(port) => port.get().to_le_bytes(), @@ -278,6 +314,11 @@ impl Address { }), ); + raw.push(self.public_key_ids.len() as u8); + for id in self.public_key_ids.iter() { + raw.push(id.0); + } + match self.ip { IpAddr::V4(ip) => { let raw_ip = ip.octets(); @@ -290,7 +331,7 @@ impl Address { }; } fn decode_raw(raw: &[u8]) -> Result<(Self, usize), Error> { - if raw.len() < 8 { + if raw.len() < 10 { return Err(Error::NotEnoughData(0)); } let ip_type = raw[0] >> 6; @@ -315,19 +356,60 @@ impl Address { let priority = AddressPriority::from_u8(raw_priority).unwrap(); let weight = AddressWeight::from_u8(raw_weight).unwrap(); - let public_key_id = PublicKeyId(raw[1]); + let raw_port = u16::from_le_bytes([raw[1], raw[2]]); + + // Add publi key ids + let num_pubkey_ids = raw[3] as usize; + if raw.len() < 3 + num_pubkey_ids { + return Err(Error::NotEnoughData(3)); + } + let mut public_key_ids = Vec::with_capacity(num_pubkey_ids); + + for raw_pubkey_id in raw[4..num_pubkey_ids].iter() { + public_key_ids.push(PublicKeyID(*raw_pubkey_id)); + } + + // add handshake ids + let next_ptr = 3 + num_pubkey_ids; + let num_handshake_ids = raw[next_ptr] as usize; + if raw.len() < next_ptr + num_handshake_ids { + return Err(Error::NotEnoughData(next_ptr)); + } + let mut handshake_ids = Vec::with_capacity(num_handshake_ids); + for raw_handshake_id in + raw[next_ptr..(next_ptr + num_pubkey_ids)].iter() + { + match HandshakeID::from_u8(*raw_handshake_id) { + Some(h_id) => handshake_ids.push(h_id), + None => { + ::tracing::warn!( + "Unsupported handshake {}. Upgrade?", + *raw_handshake_id + ); + // ignore unsupported handshakes + } + } + } + let next_ptr = next_ptr + num_pubkey_ids; - let raw_port = u16::from_le_bytes([raw[2], raw[3]]); let port = if raw_port == 0 { None } else { Some(NonZeroU16::new(raw_port).unwrap()) }; let ip = if is_ipv6 { - let raw_ip: [u8; 16] = raw[4..20].try_into().unwrap(); + let ip_end = next_ptr + 16; + if raw.len() < ip_end { + return Err(Error::NotEnoughData(next_ptr)); + } + let raw_ip: [u8; 16] = raw[next_ptr..ip_end].try_into().unwrap(); IpAddr::from(raw_ip) } else { - let raw_ip: [u8; 4] = raw[4..8].try_into().unwrap(); + let ip_end = next_ptr + 4; + if raw.len() < ip_end { + return Err(Error::NotEnoughData(next_ptr)); + } + let raw_ip: [u8; 4] = raw[next_ptr..ip_end].try_into().unwrap(); IpAddr::from(raw_ip) }; @@ -337,7 +419,8 @@ impl Address { port, priority, weight, - public_key_id, + public_key_ids, + handshake_ids, }, total_length, )) diff --git a/src/enc/asym.rs b/src/enc/asym.rs new file mode 100644 index 0000000..cbf0b5d --- /dev/null +++ b/src/enc/asym.rs @@ -0,0 +1,92 @@ +//! Asymmetric key handling and wrappers + +use ::num_traits::FromPrimitive; +use ::std::vec::Vec; + +use super::Error; + +/// Public key ID +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct KeyID(pub u16); + +/// Kind of key in the handshake +#[derive(Clone)] +#[allow(missing_debug_implementations)] +pub enum PrivKey { + /// Keys to be used only in key exchanges, not for signing + Exchange(ExchangePrivKey), + /// Keys to be used only for signing + Signing, +} + +/// Ephemeral private keys +#[derive(Clone)] +#[allow(missing_debug_implementations)] +pub enum ExchangePrivKey { + /// X25519(Curve25519) used for key exchange + X25519(::x25519_dalek::StaticSecret), +} + +impl ExchangePrivKey { + /// Run the key exchange between two keys of the same kind + pub fn key_exchange( + &self, + pub_key: ExchangePubKey, + ) -> Result<[u8; 32], Error> { + todo!(); + } +} + +/// Kind of key in the key exchange +#[derive(Debug, Copy, Clone, ::num_derive::FromPrimitive)] +#[repr(u8)] +pub enum ExchangePubKeyKind { + /// X25519 Public key + X25519 = 0, +} +impl ExchangePubKeyKind { + fn len(&self) -> usize { + match self { + // FIXME: 99% wrong size + ExchangePubKeyKind::X25519 => { + ::ring::signature::ED25519_PUBLIC_KEY_LEN + } + } + } +} + +/// all Ephemeral Public keys +#[derive(Debug, Copy, Clone)] +pub enum ExchangePubKey { + /// X25519(Curve25519) used for key exchange + X25519(::x25519_dalek::PublicKey), +} + +impl ExchangePubKey { + /// 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_slice(raw: &[u8]) -> Result<(Self, usize), Error> { + // FIXME: get *real* minimum key size + const MIN_KEY_SIZE: usize = 8; + if raw.len() < 1 + MIN_KEY_SIZE { + return Err(Error::NotEnoughData); + } + match ExchangePubKeyKind::from_u8(raw[0]) { + Some(kind) => match kind { + ExchangePubKeyKind::X25519 => { + let pub_key: ::x25519_dalek::PublicKey = + match ::bincode::deserialize(&raw[1..(1 + kind.len())]) + { + Ok(pub_key) => pub_key, + Err(_) => return Err(Error::Parsing), + }; + Ok((ExchangePubKey::X25519(pub_key), kind.len())) + } + }, + None => { + return Err(Error::Parsing); + } + } + } +} diff --git a/src/enc/errors.rs b/src/enc/errors.rs new file mode 100644 index 0000000..cec5864 --- /dev/null +++ b/src/enc/errors.rs @@ -0,0 +1,16 @@ +//! Common errors when handling cryptographic functions + +/// Crypto errors +#[derive(::thiserror::Error, Debug, Copy, Clone)] +pub enum Error { + /// Error while parsing key material + #[error("can't parse key")] + Parsing, + /// Not enough data + #[error("not enough data")] + NotEnoughData, + /// Wrong Key type found. + /// You might have passed rsa keys where x25519 was expected + #[error("wrong key type")] + WrongKey, +} diff --git a/src/enc/mod.rs b/src/enc/mod.rs new file mode 100644 index 0000000..0d7e416 --- /dev/null +++ b/src/enc/mod.rs @@ -0,0 +1,7 @@ +//! Public or symmetric key wrappers, nonces and all that magic stuff + +pub mod asym; +mod errors; +pub mod sym; + +pub use errors::Error; diff --git a/src/enc/sym.rs b/src/enc/sym.rs new file mode 100644 index 0000000..6debc4a --- /dev/null +++ b/src/enc/sym.rs @@ -0,0 +1,75 @@ +//! Symmetric cypher stuff + +#[derive(Debug, Copy, Clone)] +#[repr(C)] +#[allow(missing_debug_implementations)] +struct NonceNum { + high: u32, + low: u64, +} +/// Nonce with sequence for chach20_apoly1305 +#[repr(C)] +pub union Nonce { + num: NonceNum, + raw: ::core::mem::ManuallyDrop<::ring::aead::Nonce>, + easy_from: [u8; 12], +} + +impl ::core::fmt::Debug for Nonce { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> Result<(), ::std::fmt::Error> { + // use the Debug from NonceNum + #[allow(unsafe_code)] + unsafe { + core::fmt::Debug::fmt(&self.num, f) + } + } +} + +impl Nonce { + // FIXME: nonces should be random! + /// Generate a new random Nonce + pub fn new() -> Self { + #[allow(unsafe_code)] + unsafe { + Self { + num: NonceNum { high: 42, low: 69 }, + } + } + } + /// Create Nonce from array + pub fn from_slice(raw: [u8; 12]) -> Self { + #[allow(unsafe_code)] + unsafe { + Self { easy_from: raw } + } + } +} + +//impl Copy for Nonce {} +impl Clone for Nonce { + fn clone(&self) -> Self { + #[allow(unsafe_code)] + unsafe { + Self { num: self.num } + } + } +} + +impl ::ring::aead::NonceSequence for Nonce { + fn advance( + &mut self, + ) -> Result<::ring::aead::Nonce, ::ring::error::Unspecified> { + #[allow(unsafe_code)] + unsafe { + let old_low = self.num.low; + self.num.low = self.num.low + 1; + if self.num.low < old_low { + self.num.high = self.num.high; + } + Ok(::core::mem::ManuallyDrop::take(&mut self.raw)) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 656aa8a..314b293 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,13 +13,17 @@ //! //! libFenrir is the official rust library implementing the Fenrir protocol +mod config; +pub mod connection; +pub mod dnssec; +pub mod enc; + +use ::arc_swap::{ArcSwap, ArcSwapAny}; use ::std::{net::SocketAddr, sync::Arc}; use ::tokio::{net::UdpSocket, task::JoinHandle}; -mod config; pub use config::Config; -pub mod connection; -pub mod dnssec; +use connection::handshake::{self, Handshake, HandshakeKey}; /// Main fenrir library errors #[derive(::thiserror::Error, Debug)] @@ -35,7 +39,66 @@ pub enum Error { Dnssec(#[from] dnssec::Error), /// Handshake errors #[error("Handshake: {0:?}")] - Handshake(#[from] connection::handshake::Error), + Handshake(#[from] handshake::Error), +} + +struct FenrirInner { + keys: ArcSwapAny>>, +} + +impl FenrirInner { + fn recv_handshake(&self, handshake: Handshake) -> Result<(), Error> { + use connection::handshake::{dirsync::DirSync, HandshakeData}; + match handshake.data { + HandshakeData::DirSync(ds) => match ds { + DirSync::Req(req) => { + let ephemeral_key = { + // Keep this block short to avoid contention + // on self.keys + let keys = self.keys.load(); + if let Some(h_k) = + keys.iter().find(|k| k.id == req.key_id) + { + use enc::asym::PrivKey; + // Directory synchronized can only used keys + // for key exchange, not signing keys + if let PrivKey::Exchange(k) = &h_k.key { + Some(k.clone()) + } else { + None + } + } else { + None + } + }; + if ephemeral_key.is_none() { + ::tracing::debug!("No such key id: {:?}", req.key_id); + return Err(handshake::Error::UnknownKeyID.into()); + } + let ephemeral_key = ephemeral_key.unwrap(); + let shared_key = match ephemeral_key + .key_exchange(req.exchange_key) + { + Ok(shared_key) => shared_key, + Err(e) => return Err(handshake::Error::Key(e).into()), + }; + use crate::enc::sym::Nonce; + use ::ring::aead::{self, BoundKey}; + let alg = aead::UnboundKey::new( + &aead::CHACHA20_POLY1305, + &shared_key, + ) + .unwrap(); + let nonce = Nonce::new(); + let chacha = aead::OpeningKey::new(alg, nonce); + todo!(); + } + DirSync::Resp(resp) => { + todo!(); + } + }, + } + } } /// Instance of a fenrir endpoint @@ -49,6 +112,8 @@ pub struct Fenrir { dnssec: Option, /// Broadcast channel to tell workers to stop working stop_working: ::tokio::sync::broadcast::Sender, + /// Private keys used in the handshake + _inner: Arc, } // TODO: graceful vs immediate stop @@ -69,6 +134,9 @@ impl Fenrir { sockets: Vec::with_capacity(listen_num), dnssec: None, stop_working: sender, + _inner: Arc::new(FenrirInner { + keys: ArcSwapAny::new(Arc::new(Vec::new())), + }), }; Ok(endpoint) } @@ -162,6 +230,7 @@ impl Fenrir { /// Run a dedicated loop to read packets on the listening socket async fn listen_udp( mut stop_working: ::tokio::sync::broadcast::Receiver, + fenrir: Arc, socket: Arc, ) -> ::std::io::Result<()> { // jumbo frames are 9K max @@ -175,7 +244,7 @@ impl Fenrir { result? } }; - Self::recv(&buffer[0..bytes], sock_from).await; + Self::recv(fenrir.clone(), &buffer[0..bytes], sock_from).await; } Ok(()) } @@ -195,6 +264,7 @@ impl Fenrir { let stop_working = self.stop_working.subscribe(); let join = ::tokio::spawn(Self::listen_udp( stop_working, + self._inner.clone(), s.clone(), )); self.sockets.push((s, join)); @@ -223,14 +293,29 @@ impl Fenrir { const MIN_PACKET_BYTES: usize = 8; /// Read and do stuff with the udp packet - async fn recv(buffer: &[u8], _sock_from: SocketAddr) { + async fn recv( + fenrir: Arc, + buffer: &[u8], + _sock_from: SocketAddr, + ) { if buffer.len() < Self::MIN_PACKET_BYTES { return; } use connection::ID; let raw_id: [u8; 8] = buffer.try_into().expect("unreachable"); if ID::from(raw_id).is_handshake() { - todo!(); + use connection::handshake::Handshake; + let handshake = match Handshake::parse(&buffer[8..]) { + Ok(handshake) => handshake, + Err(e) => { + ::tracing::warn!("Handshake parsing: {}", e); + return; + } + }; + if let Err(err) = fenrir.recv_handshake(handshake) { + ::tracing::debug!("Handshake recv error {}", err); + return; + } } // copy packet, spawn todo!();