diff --git a/src/auth/mod.rs b/src/auth/mod.rs index 464e02a..aef9cbc 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -106,8 +106,10 @@ impl Domain { } } +/// Reserve the first service ID for the authentication service +pub const SERVICEID_AUTH: ServiceID = ServiceID([0; 16]); /// The Service ID is a UUID associated with the service. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub struct ServiceID([u8; 16]); impl From<[u8; 16]> for ServiceID { diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 8dac30c..ee71c4e 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -185,7 +185,7 @@ impl ConnList { self.connections[id_in_thread] = Some(conn); Ok(()) } - pub(crate) fn delete(&mut self, id: IDRecv) { + pub(crate) fn remove(&mut self, id: IDRecv) { if let IDRecv(ID::ID(raw_id)) = id { let id_in_thread: usize = (raw_id.get() / (self.thread_id.total as u64)) as usize; diff --git a/src/enc/asym.rs b/src/enc/asym.rs index 83e8dd5..ea67ac3 100644 --- a/src/enc/asym.rs +++ b/src/enc/asym.rs @@ -25,6 +25,24 @@ impl KeyID { } } +impl TryFrom<&str> for KeyID { + type Error = ::std::io::Error; + fn try_from(raw: &str) -> Result { + if let Ok(id_u16) = raw.parse::() { + return Ok(KeyID(id_u16)); + } + return Err(::std::io::Error::new( + ::std::io::ErrorKind::InvalidData, + "KeyID must be between 0 and 65535", + )); + } +} +impl ::std::fmt::Display for KeyID { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "{}", self.0) + } +} + /// Capabilities of each key #[derive(Debug, Clone, Copy)] pub enum KeyCapabilities { @@ -56,13 +74,23 @@ impl KeyCapabilities { } /// Kind of key used in the handshake -#[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)] +#[derive( + Debug, + Copy, + Clone, + PartialEq, + ::num_derive::FromPrimitive, + ::strum_macros::EnumString, + ::strum_macros::IntoStaticStr, +)] #[non_exhaustive] #[repr(u8)] pub enum KeyKind { /// Ed25519 Public key (sign only) + #[strum(serialize = "ed25519")] Ed25519 = 0, /// X25519 Public key (key exchange) + #[strum(serialize = "x25519")] X25519, } // FIXME: actually check this @@ -73,8 +101,7 @@ impl KeyKind { match self { // FIXME: 99% wrong size KeyKind::Ed25519 => ::ring::signature::ED25519_PUBLIC_KEY_LEN, - // FIXME: 99% wrong size - KeyKind::X25519 => ::ring::signature::ED25519_PUBLIC_KEY_LEN, + KeyKind::X25519 => 32, } } /// Get the capabilities of this key type @@ -94,15 +121,30 @@ impl KeyKind { KeyKind::X25519 => &X25519_KEY_EXCHANGES, } } + /// generate new keypair + pub fn new_keypair( + &self, + rnd: &Random, + ) -> Result<(PrivKey, PubKey), Error> { + PubKey::new_keypair(*self, rnd) + } } -// FIXME: rename in KeyExchangeKind /// Kind of key exchange -#[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)] +#[derive( + Debug, + Copy, + Clone, + PartialEq, + ::num_derive::FromPrimitive, + ::strum_macros::EnumString, + ::strum_macros::IntoStaticStr, +)] #[non_exhaustive] #[repr(u8)] pub enum KeyExchangeKind { /// X25519 Public key + #[strum(serialize = "x25519diffiehellman")] X25519DiffieHellman = 0, } impl KeyExchangeKind { @@ -141,6 +183,13 @@ pub enum PubKey { } impl PubKey { + /// Get the serialized key length + pub fn len(&self) -> usize { + match self { + PubKey::Exchange(ex) => ex.len(), + PubKey::Signing => todo!(), + } + } /// return the kind of public key pub fn kind(&self) -> KeyKind { match self { @@ -149,6 +198,20 @@ impl PubKey { PubKey::Exchange(ex) => ex.kind(), } } + /// generate new keypair + fn new_keypair( + kind: KeyKind, + rnd: &Random, + ) -> Result<(PrivKey, PubKey), Error> { + match kind { + KeyKind::Ed25519 => todo!(), + KeyKind::X25519 => { + let (priv_key, pub_key) = + KeyExchangeKind::X25519DiffieHellman.new_keypair(rnd)?; + Ok((PrivKey::Exchange(priv_key), PubKey::Exchange(pub_key))) + } + } + } /// serialize the key into the buffer /// NOTE: Assumes there is enough space pub fn serialize_into(&self, out: &mut [u8]) { @@ -211,6 +274,24 @@ pub enum PrivKey { Signing, } +impl PrivKey { + /// Get the serialized key length + pub fn len(&self) -> usize { + match self { + PrivKey::Exchange(ex) => ex.len(), + PrivKey::Signing => todo!(), + } + } + /// serialize the key into the buffer + /// NOTE: Assumes there is enough space + pub fn serialize_into(&self, out: &mut [u8]) { + match self { + PrivKey::Exchange(ex) => ex.serialize_into(out), + PrivKey::Signing => todo!(), + } + } +} + /// Ephemeral private keys #[derive(Clone)] #[allow(missing_debug_implementations)] @@ -221,6 +302,12 @@ pub enum ExchangePrivKey { } impl ExchangePrivKey { + /// Get the serialized key length + pub fn len(&self) -> usize { + match self { + ExchangePrivKey::X25519(_) => KeyKind::X25519.pub_len(), + } + } /// Get the kind of key pub fn kind(&self) -> KeyKind { match self { @@ -238,12 +325,18 @@ impl ExchangePrivKey { if exchange != KeyExchangeKind::X25519DiffieHellman { return Err(Error::UnsupportedKeyExchange); } - if let ExchangePubKey::X25519(inner_pub_key) = pub_key { - let shared_secret = priv_key.diffie_hellman(&inner_pub_key); - Ok(shared_secret.into()) - } else { - Err(Error::UnsupportedKeyExchange) - } + let ExchangePubKey::X25519(inner_pub_key) = pub_key; + let shared_secret = priv_key.diffie_hellman(&inner_pub_key); + Ok(shared_secret.into()) + } + } + } + /// serialize the key into the buffer + /// NOTE: Assumes there is enough space + pub fn serialize_into(&self, out: &mut [u8]) { + match self { + ExchangePrivKey::X25519(key) => { + out[..32].copy_from_slice(&key.to_bytes()); } } } @@ -258,6 +351,12 @@ pub enum ExchangePubKey { } impl ExchangePubKey { + /// Get the serialized key length + pub fn len(&self) -> usize { + match self { + ExchangePubKey::X25519(_) => KeyKind::X25519.pub_len(), + } + } /// Get the kind of key pub fn kind(&self) -> KeyKind { match self { diff --git a/src/enc/hkdf.rs b/src/enc/hkdf.rs index c8a706d..d81276f 100644 --- a/src/enc/hkdf.rs +++ b/src/enc/hkdf.rs @@ -7,11 +7,20 @@ use ::zeroize::Zeroize; use crate::{config::Config, enc::Secret}; /// Kind of HKDF -#[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)] +#[derive( + Debug, + Copy, + Clone, + PartialEq, + ::num_derive::FromPrimitive, + ::strum_macros::EnumString, + ::strum_macros::IntoStaticStr, +)] #[non_exhaustive] #[repr(u8)] pub enum HkdfKind { /// Sha3 + #[strum(serialize = "sha3")] Sha3 = 0, } impl HkdfKind { diff --git a/src/enc/mod.rs b/src/enc/mod.rs index 09feb7b..06a6200 100644 --- a/src/enc/mod.rs +++ b/src/enc/mod.rs @@ -27,6 +27,10 @@ impl Random { pub fn fill(&self, out: &mut [u8]) { self.rnd.fill(out); } + /// return the underlying ring SystemRandom + pub fn ring_rnd(&self) -> &::ring::rand::SystemRandom { + &self.rnd + } } // Fake debug implementation to avoid leaking secrets diff --git a/src/enc/sym.rs b/src/enc/sym.rs index 4d28c64..37b5d78 100644 --- a/src/enc/sym.rs +++ b/src/enc/sym.rs @@ -8,10 +8,19 @@ use crate::{ use ::zeroize::Zeroize; /// List of possible Ciphers -#[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)] +#[derive( + Debug, + Copy, + Clone, + PartialEq, + ::num_derive::FromPrimitive, + ::strum_macros::EnumString, + ::strum_macros::IntoStaticStr, +)] #[repr(u8)] pub enum CipherKind { /// XChaCha20_Poly1305 + #[strum(serialize = "xchacha20poly1305")] XChaCha20Poly1305 = 0, } diff --git a/src/inner/mod.rs b/src/inner/mod.rs index 67add2a..24ff170 100644 --- a/src/inner/mod.rs +++ b/src/inner/mod.rs @@ -51,7 +51,7 @@ pub(crate) struct ClientConnectInfo { #[derive(Debug)] pub(crate) enum HandshakeAction { /// Parsing finished, all ok, nothing to do - None, + Nonthing, /// Packet parsed, now go perform authentication AuthNeeded(AuthNeededInfo), /// the client can fully establish a connection with this info @@ -227,6 +227,9 @@ impl HandshakeTracker { } let hshake = self.hshake_cli.remove(resp.client_key_id).unwrap(); + if let Some(timeout) = hshake.timeout { + timeout.abort(); + } return Ok(HandshakeAction::ClientConnect( ClientConnectInfo { service_id: hshake.service_id, diff --git a/src/inner/worker.rs b/src/inner/worker.rs index 642d9d2..0daec59 100644 --- a/src/inner/worker.rs +++ b/src/inner/worker.rs @@ -1,6 +1,6 @@ //! Worker thread implementation use crate::{ - auth::{Domain, ServiceID, Token, TokenChecker, UserID}, + auth::{self, Domain, ServiceID, Token, TokenChecker, UserID}, config::Config, connection::{ self, @@ -390,13 +390,10 @@ impl Worker { self.handshakes.timeout_client(key_id) { for conn_id in connections.into_iter() { - if !conn_id.0.is_handshake() { - self.connections.delete(conn_id); - } + self.connections.remove(conn_id); } }; } - //TODO: reconf message to add channels Work::Recv(pkt) => { self.recv(pkt).await; } @@ -545,7 +542,6 @@ impl Worker { return; } self.send_packet(raw_out, udp.src, udp.dst).await; - return; } HandshakeAction::ClientConnect(cci) => { let ds_resp; @@ -573,19 +569,19 @@ impl Worker { let id_recv = conn.id_recv; let cipher = conn.cipher_recv.kind(); // track the connection to the authentication server - if self.connections.track(Rc::new(conn)).is_err() { + if self.connections.track(conn.into()).is_err() { ::tracing::error!("Could not track new connection"); - self.connections.delete(id_recv); + self.connections.remove(id_recv); return; } - if id_recv.0 == resp_data.service_connection_id { + if cci.service_id == auth::SERVICEID_AUTH { // the user asked a single connection // to the authentication server, without any additional // service. No more connections to setup return; } // create and track the connection to the service - // SECURITY: + // SECURITY: xor with secrets //FIXME: the Secret should be XORed with the client stored // secret (if any) let hkdf = Hkdf::new( @@ -603,13 +599,10 @@ impl Worker { service_connection.id_send = IDSend(resp_data.service_connection_id); let _ = self.connections.track(service_connection.into()); - return; } - _ => {} + HandshakeAction::Nonthing => {} }; } - // copy packet, spawn - todo!(); } async fn send_packet( &self,