diff --git a/src/connection/handshake/dirsync.rs b/src/connection/handshake/dirsync.rs index 564a810..780f468 100644 --- a/src/connection/handshake/dirsync.rs +++ b/src/connection/handshake/dirsync.rs @@ -14,7 +14,7 @@ use crate::{ connection::ID, enc::{ asym::{ExchangePubKey, KeyExchange, KeyID}, - sym::CipherKind, + sym::{CipherKind, Secret}, }, }; use ::std::{collections::VecDeque, num::NonZeroU64, vec::Vec}; @@ -250,16 +250,16 @@ impl super::HandshakeParsing for Resp { } /// Decrypted response data -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub struct RespData { /// Client nonce, copied from the request - client_nonce: Nonce, + pub client_nonce: Nonce, /// Server Connection ID - id: ID, + pub id: ID, /// Service Connection ID - service_id: ID, + pub service_id: ID, /// Service encryption key - service_key: [u8; 32], + pub service_key: Secret, } impl RespData { @@ -282,6 +282,6 @@ impl RespData { self.service_id.serialize(&mut out[start..end]); start = end; end = end + Self::NONCE_LEN; - out[start..end].copy_from_slice(&self.service_key); + out[start..end].copy_from_slice(self.service_key.as_ref()); } } diff --git a/src/enc/errors.rs b/src/enc/errors.rs index ae1961b..6effb50 100644 --- a/src/enc/errors.rs +++ b/src/enc/errors.rs @@ -9,6 +9,9 @@ pub enum Error { /// Not enough data #[error("not enough data")] NotEnoughData, + /// buffer too small + #[error("buffer too small")] + InsufficientBuffer, /// Wrong Key type found. /// You might have passed rsa keys where x25519 was expected #[error("wrong key type")] diff --git a/src/enc/hkdf.rs b/src/enc/hkdf.rs index c10fe69..bb6ca59 100644 --- a/src/enc/hkdf.rs +++ b/src/enc/hkdf.rs @@ -30,9 +30,19 @@ impl Drop for HkdfInner { } } } +impl Clone for HkdfInner { + fn clone(&self) -> Self { + #[allow(unsafe_code)] + unsafe { + Self { + hkdf: self.hkdf.clone(), + } + } + } +} /// Sha3 based HKDF -#[allow(missing_debug_implementations)] +#[derive(Clone)] pub struct HkdfSha3 { inner: HkdfInner, } @@ -60,3 +70,13 @@ impl HkdfSha3 { out.into() } } + +// Fake debug implementation to avoid leaking secrets +impl ::core::fmt::Debug for HkdfSha3 { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> Result<(), ::std::fmt::Error> { + ::core::fmt::Debug::fmt("[hidden hkdf]", f) + } +} diff --git a/src/enc/sym.rs b/src/enc/sym.rs index ecf5c9b..6e3d3e5 100644 --- a/src/enc/sym.rs +++ b/src/enc/sym.rs @@ -6,10 +6,18 @@ use ::zeroize::Zeroize; /// Secret, used for keys. /// Grants that on drop() we will zero out memory -#[derive(Zeroize)] +#[derive(Zeroize, Clone)] #[zeroize(drop)] -#[allow(missing_debug_implementations)] pub struct Secret([u8; 32]); +// Fake debug implementation to avoid leaking secrets +impl ::core::fmt::Debug for Secret { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> Result<(), ::std::fmt::Error> { + ::core::fmt::Debug::fmt("[hidden secret]", f) + } +} impl Secret { /// New randomly generated secret @@ -49,12 +57,12 @@ impl CipherKind { /// required length of the nonce pub fn nonce_len(&self) -> usize { // TODO: how the hell do I take this from ::chacha20poly1305? - ::ring::aead::CHACHA20_POLY1305.nonce_len() + Nonce::len() } /// required length of the key pub fn key_len(&self) -> usize { - use ::chacha20poly1305::KeySizeUser; - ::chacha20poly1305::XChaCha20Poly1305::key_size() + use ::chacha20poly1305::{KeySizeUser, XChaCha20Poly1305}; + XChaCha20Poly1305::key_size() } /// Length of the authentication tag pub fn tag_len(&self) -> usize { @@ -109,11 +117,7 @@ impl Cipher { } } } - fn decrypt<'a>( - &self, - aad: AAD, - data: &mut VecDeque, - ) -> Result<(), Error> { + fn decrypt(&self, aad: AAD, data: &mut VecDeque) -> Result<(), Error> { match self { Cipher::XChaCha20Poly1305(cipher) => { use ::chacha20poly1305::{ @@ -147,6 +151,32 @@ impl Cipher { } } } + fn encrypt( + &self, + aad: AAD, + nonce: Nonce, + data: &mut [u8], + ) -> Result<(), Error> { + match self { + Cipher::XChaCha20Poly1305(cipher) => { + use ::chacha20poly1305::{ + aead::generic_array::GenericArray, AeadInPlace, + }; + let min_len: usize = CipherKind::XChaCha20Poly1305.nonce_len() + + CipherKind::XChaCha20Poly1305.tag_len() + + 1; + if data.len() < min_len { + return Err(Error::InsufficientBuffer); + } + // write Nonce, then advance it + + // encrypt data + + // add tag + } + } + todo!() + } } /// Receive only cipher @@ -254,7 +284,7 @@ impl Nonce { } } /// Length of this nonce in bytes - pub fn len() -> usize { + pub const fn len() -> usize { return 12; } /// Get reference to the nonce bytes diff --git a/src/lib.rs b/src/lib.rs index 621f4e6..ad8fea1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,13 +61,24 @@ struct FenrirInner { keys: ArcSwapAny>>, } +/// Information needed to reply after the key exchange +#[derive(Debug, Clone)] +pub struct AuthNeededInfo { + /// Parsed handshake + pub handshake: Handshake, + /// hkdf generated from the handshake + pub hkdf: HkdfSha3, + /// cipher to be used in both directions + pub cipher: CipherKind, +} + /// Intermediate actions to be taken while parsing the handshake #[derive(Debug, Clone)] pub enum HandshakeAction { /// Parsing finished, all ok, nothing to do None, /// Packet parsed, now go perform authentication - AuthNeeded(Handshake), + AuthNeeded(AuthNeededInfo), } // No async here @@ -144,7 +155,13 @@ impl FenrirInner { } req.set_data(dirsync::ReqData::deserialize(&req.data)?); - return Ok(HandshakeAction::AuthNeeded(handshake)); + let cipher = req.cipher; + + return Ok(HandshakeAction::AuthNeeded(AuthNeededInfo { + handshake, + hkdf, + cipher, + })); } DirSync::Resp(resp) => { todo!(); @@ -396,7 +413,7 @@ impl Fenrir { } }; match action { - HandshakeAction::AuthNeeded(hshake) => { + HandshakeAction::AuthNeeded(authinfo) => { let tk_check = match token_check.load_full() { Some(tk_check) => tk_check, None => { @@ -410,7 +427,7 @@ impl Fenrir { dirsync::{self, DirSync}, HandshakeData, }; - match hshake.data { + match authinfo.handshake.data { HandshakeData::DirSync(ds) => match ds { DirSync::Req(req) => { use dirsync::ReqInner; @@ -458,6 +475,29 @@ impl Fenrir { let srv_secret = enc::sym::Secret::new_rand(&fenrir.rand); + let resp_data = dirsync::RespData { + client_nonce: req_data.nonce, + id: auth_conn_id, + service_id: srv_conn_id, + service_key: srv_secret, + }; + // build response + let secret_send = + authinfo.hkdf.get_secret(b"to_client"); + let cipher_send = CipherRecv::new( + authinfo.cipher, + secret_send, + ); + use crate::enc::sym::AAD; + let aad = AAD(&mut []); // no aad for now + /* + match cipher_send.encrypt(aad, &mut req.data.ciphertext()) { + Ok(()) => req.data.mark_as_cleartext(), + Err(e) => { + return Err(handshake::Error::Key(e).into()); + } + } + */ todo!() } _ => {