diff --git a/Cargo.toml b/Cargo.toml index 0e60097..93dcec2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ crate_type = [ "lib", "cdylib", "staticlib" ] 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" } +chacha20poly1305 = { version = "0.10" } futures = { version = "0.3" } hkdf = { version = "0.12" } libc = { version = "0.2" } @@ -47,11 +48,11 @@ 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" ] } -# NOTE: can not force newer versions of zeroize: -# * zeroize is a dependency of x25519-dalek -# * zeroize is not *pure* rust, but uses `alloc` -# * rust can have multiple versions as deps, but only if the are pure rust +# don't use stable dalek. forces zeroize 1.3, +# breaks our and chacha20poly1305 +# reason: zeroize is not pure rust, +# so we can't have multiple versions of if +x25519-dalek = { version = "2.0.0-pre.1", features = [ "serde" ] } zeroize = { version = "1" } [profile.dev] diff --git a/src/enc/hkdf.rs b/src/enc/hkdf.rs index c2ec54d..c10fe69 100644 --- a/src/enc/hkdf.rs +++ b/src/enc/hkdf.rs @@ -8,9 +8,9 @@ use ::zeroize::Zeroize; use crate::enc::sym::Secret; // Hack & tricks: -// HKDF are pretty important, but they don't zero out the data. -// we can't user #[derive(Zeroing)] either. -// So we craete a union with a Zeroing object, and drop manually both. +// HKDF are pretty important, but this lib don't zero out the data. +// we can't use #[derive(Zeroing)] either. +// So we craete a union with a Zeroing object, and drop both manually. #[derive(Zeroize)] #[zeroize(drop)] @@ -34,14 +34,29 @@ impl Drop for HkdfInner { /// Sha3 based HKDF #[allow(missing_debug_implementations)] pub struct HkdfSha3 { - _inner: Hkdf, + inner: HkdfInner, } impl HkdfSha3 { /// Instantiate a new HKDF with Sha3-256 - pub fn new(salt: Option<&[u8]>, key: Secret) -> Self { - Self { - _inner: Hkdf::::new(salt, key.as_ref()), + pub fn new(salt: &[u8], key: Secret) -> Self { + let hkdf = Hkdf::::new(Some(salt), key.as_ref()); + #[allow(unsafe_code)] + unsafe { + Self { + inner: HkdfInner { + hkdf: ::core::mem::ManuallyDrop::new(hkdf), + }, + } } } + /// Get a secret generated from the key and a given context + pub fn get_secret(&self, context: &[u8]) -> Secret { + let mut out: [u8; 32] = [0; 32]; + #[allow(unsafe_code)] + unsafe { + self.inner.hkdf.expand(context, &mut out); + } + out.into() + } } diff --git a/src/enc/sym.rs b/src/enc/sym.rs index aecb90d..55c4a1e 100644 --- a/src/enc/sym.rs +++ b/src/enc/sym.rs @@ -17,6 +17,15 @@ impl Secret { &self.secret } } + +impl From<[u8; 32]> for Secret { + fn from(shared_secret: [u8; 32]) -> Self { + Self { + secret: shared_secret, + } + } +} + impl From<::x25519_dalek::SharedSecret> for Secret { fn from(shared_secret: ::x25519_dalek::SharedSecret) -> Self { Self { @@ -29,68 +38,160 @@ impl From<::x25519_dalek::SharedSecret> for Secret { #[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)] #[repr(u8)] pub enum CipherKind { - /// Chaha20_Poly1305 - Chacha20Poly1305 = 0, + /// XChaCha20_Poly1305 + XChaCha20Poly1305 = 0, } 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() } /// required length of the key pub fn key_len(&self) -> usize { - ::ring::aead::CHACHA20_POLY1305.key_len() + use ::chacha20poly1305::KeySizeUser; + ::chacha20poly1305::XChaCha20Poly1305::key_size() } /// Length of the authentication tag pub fn tag_len(&self) -> usize { + // TODO: how the hell do I take this from ::chacha20poly1305? ::ring::aead::CHACHA20_POLY1305.tag_len() } } -/// actual ciphers +/// Additional Authenticated Data #[derive(Debug)] -pub enum Cipher { - /// Cipher Chaha20_Poly1305 - Chacha20Poly1305(Chacha20Poly1305), +pub struct AAD<'a>(pub &'a mut [u8]); + +/// Cipher direction, to make sure we don't reuse the same cipher +/// for both decrypting and encrypting +#[derive(Debug, Copy, Clone)] +#[repr(u8)] +pub enum CipherDirection { + /// Receive, to decrypt only + Recv = 0, + /// Send, to encrypt only + Send, +} + +/// actual ciphers +enum Cipher { + /// Cipher XChaha20_Poly1305 + XChaCha20Poly1305(XChaCha20Poly1305), } impl Cipher { /// Build a new Cipher - pub fn new(kind: CipherKind, secret: Secret) -> Self { + fn new(kind: CipherKind, secret: Secret) -> Self { match kind { - CipherKind::Chacha20Poly1305 => { - Self::Chacha20Poly1305(Chacha20Poly1305::new(secret)) + CipherKind::XChaCha20Poly1305 => { + Self::XChaCha20Poly1305(XChaCha20Poly1305::new(secret)) + } + } + } + fn nonce_len(&self) -> usize { + match self { + Cipher::XChaCha20Poly1305(_) => { + // TODO: how the hell do I take this from ::chacha20poly1305? + ::ring::aead::CHACHA20_POLY1305.nonce_len() + } + } + } + pub fn tag_len(&self) -> usize { + match self { + Cipher::XChaCha20Poly1305(_) => { + // TODO: how the hell do I take this from ::chacha20poly1305? + ::ring::aead::CHACHA20_POLY1305.tag_len() + } + } + } + fn decrypt( + &self, + nonce: Nonce, + aad: AAD, + data: &mut [u8], + ) -> Result<(), ()> { + match self { + Cipher::XChaCha20Poly1305(cipher) => { + use ::chacha20poly1305::{ + aead::generic_array::GenericArray, AeadInPlace, + }; + let (data_notag, tag_bytes) = data.split_at_mut( + data.len() + 1 - ::ring::aead::CHACHA20_POLY1305.tag_len(), + ); + let tag = GenericArray::from_slice(tag_bytes); + let maybe = cipher.cipher.decrypt_in_place_detached( + nonce.as_bytes().into(), + aad.0, + data_notag, + tag, + ); + if maybe.is_err() { + Err(()) + } else { + Ok(()) + } } } } } -/// Chacha20Poly1305 cipher -#[derive(Debug)] -pub struct Chacha20Poly1305 { - cipher: ::ring::aead::OpeningKey, +/// Receive only cipher +#[allow(missing_debug_implementations)] +pub struct CipherRecv(Cipher); + +impl CipherRecv { + /// Build a new Cipher + pub fn new(kind: CipherKind, secret: Secret) -> Self { + Self(Cipher::new(kind, secret)) + } + /// Get the length of the nonce for this cipher + pub fn nonce_len(&self) -> usize { + self.0.nonce_len() + } } -impl Chacha20Poly1305 { - /// Initialize the cipher with the given nonce - pub fn with_nonce(key: Secret, nonce: Nonce) -> Self { - let unbound_key = ::ring::aead::UnboundKey::new( - &::ring::aead::CHACHA20_POLY1305, - key.as_ref(), - ) - .unwrap(); - use ::ring::aead::BoundKey; +/// Send only cipher +#[allow(missing_debug_implementations)] +pub struct CipherSend { + nonce: Nonce, + cipher: Cipher, +} + +impl CipherSend { + /// Build a new Cipher + pub fn new(kind: CipherKind, secret: Secret) -> Self { Self { - cipher: ::ring::aead::OpeningKey::::new(unbound_key, nonce), + nonce: Nonce::new(), + cipher: Cipher::new(kind, secret), } } - /// Initialize a new ChachaPoly20 cipher with a random nonce - pub fn new(key: Secret) -> Self { - Self::with_nonce(key, Nonce::new()) + /// Get the current nonce as &[u8] + pub fn nonce_as_bytes(&self) -> &[u8] { + self.nonce.as_bytes() } } +/// XChaCha20Poly1305 cipher +struct XChaCha20Poly1305 { + cipher: ::chacha20poly1305::XChaCha20Poly1305, +} + +impl XChaCha20Poly1305 { + /// Initialize a new ChaChaPoly20 cipher with a random nonce + fn new(key: Secret) -> Self { + use ::chacha20poly1305::{KeyInit, XChaCha20Poly1305}; + Self { + cipher: XChaCha20Poly1305::new(key.as_ref().into()), + } + } +} + +// +// TODO: For efficiency "Nonce" should become a reference. +// + #[derive(Debug, Copy, Clone)] #[repr(C)] #[allow(missing_debug_implementations)] @@ -99,13 +200,11 @@ struct NonceNum { low: u64, } /// Nonce with sequence for chach20_apoly1305 +#[derive(Copy, Clone)] #[repr(C)] pub union Nonce { num: NonceNum, - // ring::aead::Nonce does not implement 'Copy' - // even though it's just [u8;12] - raw: ::core::mem::ManuallyDrop<::ring::aead::Nonce>, - easy_from: [u8; 12], + raw: [u8; 12], } impl ::core::fmt::Debug for Nonce { @@ -113,7 +212,6 @@ impl ::core::fmt::Debug for Nonce { &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) @@ -128,32 +226,28 @@ impl Nonce { #[allow(unsafe_code)] unsafe { Self { + // chosen by a fair dice roll + // ahh, who am I kidding... num: NonceNum { high: 42, low: 69 }, } } } + /// Get reference to the nonce bytes + pub fn as_bytes(&self) -> &[u8] { + #[allow(unsafe_code)] + unsafe { + &self.raw + } + } /// Create Nonce from array pub fn from_slice(raw: [u8; 12]) -> Self { #[allow(unsafe_code)] unsafe { - Self { easy_from: raw } + Self { raw } } } -} - -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> { + /// Go to the next nonce + pub fn advance(&mut self) { #[allow(unsafe_code)] unsafe { let old_low = self.num.low; @@ -161,7 +255,6 @@ impl ::ring::aead::NonceSequence for Nonce { 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 aca90aa..4526d2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,11 @@ use ::arc_swap::{ArcSwap, ArcSwapAny}; use ::std::{net::SocketAddr, sync::Arc}; use ::tokio::{net::UdpSocket, task::JoinHandle}; -use crate::enc::{asym, sym::CipherKind}; +use crate::enc::{ + asym, + hkdf::HkdfSha3, + sym::{CipherKind, CipherRecv, CipherSend}, +}; pub use config::Config; use connection::handshake::{self, Handshake, HandshakeKey}; @@ -106,6 +110,10 @@ impl FenrirInner { Ok(shared_key) => shared_key, Err(e) => return Err(handshake::Error::Key(e).into()), }; + let hkdf = HkdfSha3::new(b"fenrir", shared_key); + let secret_in = hkdf.get_secret(b"to_server"); + let cipher_in = CipherRecv::new(req.cipher, secret_in); + todo!(); } DirSync::Resp(resp) => {