//! Symmetric cypher stuff use super::Error; use crate::{ config::Config, enc::{Random, Secret}, }; use ::zeroize::Zeroize; /// List of possible Ciphers #[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)] #[repr(u8)] pub enum CipherKind { /// XChaCha20_Poly1305 XChaCha20Poly1305 = 0, } impl CipherKind { /// length of the serialized id for the cipher kind field pub fn len() -> usize { 1 } /// required length of the nonce pub fn nonce_len(&self) -> HeadLen { // TODO: how the hell do I take this from ::chacha20poly1305? HeadLen(Nonce::len()) } /// required length of the key pub fn key_len(&self) -> usize { use ::chacha20poly1305::{KeySizeUser, XChaCha20Poly1305}; XChaCha20Poly1305::key_size() } /// Length of the authentication tag pub fn tag_len(&self) -> TagLen { // TODO: how the hell do I take this from ::chacha20poly1305? TagLen(::ring::aead::CHACHA20_POLY1305.tag_len()) } } /// Additional Authenticated Data #[derive(Debug)] 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, } /// strong typedef for header length /// aka: nonce length in the encrypted data) #[derive(Debug, Copy, Clone)] pub struct HeadLen(pub usize); /// strong typedef for the Tag length /// aka: cryptographic authentication tag length at the end /// of the encrypted data #[derive(Debug, Copy, Clone)] pub struct TagLen(pub usize); /// actual ciphers enum Cipher { /// Cipher XChaha20_Poly1305 XChaCha20Poly1305(XChaCha20Poly1305), } impl Cipher { /// Build a new Cipher fn new(kind: CipherKind, secret: Secret) -> Self { match kind { CipherKind::XChaCha20Poly1305 => { Self::XChaCha20Poly1305(XChaCha20Poly1305::new(secret)) } } } pub fn kind(&self) -> CipherKind { match self { Cipher::XChaCha20Poly1305(_) => CipherKind::XChaCha20Poly1305, } } fn nonce_len(&self) -> HeadLen { match self { Cipher::XChaCha20Poly1305(_) => { // TODO: how the hell do I take this from ::chacha20poly1305? HeadLen(::ring::aead::CHACHA20_POLY1305.nonce_len()) } } } fn tag_len(&self) -> TagLen { match self { Cipher::XChaCha20Poly1305(_) => { // TODO: how the hell do I take this from ::chacha20poly1305? TagLen(::ring::aead::CHACHA20_POLY1305.tag_len()) } } } fn decrypt<'a>( &self, aad: AAD, raw_data: &'a mut [u8], ) -> Result<&'a [u8], Error> { match self { Cipher::XChaCha20Poly1305(cipher) => { use ::chacha20poly1305::{ aead::generic_array::GenericArray, AeadInPlace, }; let final_len: usize = { // FIXME: check min data length let (nonce_bytes, data_and_tag) = raw_data.split_at_mut(13); let (data_notag, tag_bytes) = data_and_tag.split_at_mut( data_and_tag.len() + 1 - ::ring::aead::CHACHA20_POLY1305.tag_len(), ); let nonce = GenericArray::from_slice(nonce_bytes); let tag = GenericArray::from_slice(tag_bytes); let maybe = cipher.cipher.decrypt_in_place_detached( nonce.into(), aad.0, data_notag, tag, ); if maybe.is_err() { return Err(Error::Decrypt); } data_notag.len() }; //data.drain(..Nonce::len()); //data.truncate(final_len); Ok(&raw_data[Nonce::len()..Nonce::len() + final_len]) } } } fn overhead(&self) -> usize { match self { Cipher::XChaCha20Poly1305(_) => { let cipher = CipherKind::XChaCha20Poly1305; cipher.nonce_len().0 + cipher.tag_len().0 } } } fn encrypt( &self, nonce: &Nonce, aad: AAD, data: &mut [u8], ) -> Result<(), Error> { // FIXME: check minimum buffer size match self { Cipher::XChaCha20Poly1305(cipher) => { use ::chacha20poly1305::AeadInPlace; let tag_len: usize = ::ring::aead::CHACHA20_POLY1305.tag_len(); let data_len_notag = data.len() - tag_len; // write nonce data[..Nonce::len()].copy_from_slice(nonce.as_bytes()); // encrypt data match cipher.cipher.encrypt_in_place_detached( nonce.as_bytes().into(), aad.0, &mut data[Nonce::len()..data_len_notag], ) { Ok(tag) => { data[data_len_notag..] // add tag //data.get_tag_slice() .copy_from_slice(tag.as_slice()); Ok(()) } Err(_) => Err(Error::Encrypt), } } } } } /// Receive only cipher pub struct CipherRecv(Cipher); impl ::core::fmt::Debug for CipherRecv { fn fmt( &self, f: &mut core::fmt::Formatter<'_>, ) -> Result<(), ::std::fmt::Error> { ::core::fmt::Debug::fmt("[hidden cipher recv]", f) } } 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) -> HeadLen { self.0.nonce_len() } /// Decrypt a paket. Nonce and Tag are taken from the packet, /// while you need to provide AAD (Additional Authenticated Data) pub fn decrypt<'a>( &self, aad: AAD, data: &'a mut [u8], ) -> Result<&'a [u8], Error> { self.0.decrypt(aad, data) } /// return the underlying cipher id pub fn kind(&self) -> CipherKind { self.0.kind() } } /// Send only cipher pub struct CipherSend { nonce: NonceSync, cipher: Cipher, } impl ::core::fmt::Debug for CipherSend { fn fmt( &self, f: &mut core::fmt::Formatter<'_>, ) -> Result<(), ::std::fmt::Error> { ::core::fmt::Debug::fmt("[hidden cipher send]", f) } } impl CipherSend { /// Build a new Cipher pub fn new(kind: CipherKind, secret: Secret, rand: &Random) -> Self { Self { nonce: NonceSync::new(rand), cipher: Cipher::new(kind, secret), } } /// Encrypt the given data pub fn encrypt(&self, aad: AAD, data: &mut [u8]) -> Result<(), Error> { let old_nonce = self.nonce.advance(); self.cipher.encrypt(&old_nonce, aad, data)?; Ok(()) } /// return the underlying cipher id pub fn kind(&self) -> CipherKind { self.cipher.kind() } } /// 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: Merge crate::{enc::sym::Nonce, connection::handshake::dirsync::Nonce} // #[derive(Debug, Copy, Clone)] #[repr(C)] #[allow(missing_debug_implementations)] struct NonceNum { high: u32, low: u64, } /// Nonce with sequence for chach20_apoly1305 #[derive(Copy, Clone)] #[repr(C)] pub union Nonce { num: NonceNum, raw: [u8; 12], } impl ::core::fmt::Debug for Nonce { fn fmt( &self, f: &mut core::fmt::Formatter<'_>, ) -> Result<(), ::std::fmt::Error> { #[allow(unsafe_code)] unsafe { ::core::fmt::Debug::fmt(&self.num, f) } } } impl Nonce { /// Generate a new random Nonce pub fn new(rand: &Random) -> Self { let mut raw = [0; 12]; rand.fill(&mut raw); Self { raw } } /// Length of this nonce in bytes pub const fn len() -> usize { return 12; } /// 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 { Self { raw } } /// Go to the next nonce pub fn advance(&mut self) { #[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; } } } } /// Synchronize the mutex acess with a nonce for multithread safety #[derive(Debug)] pub struct NonceSync { nonce: ::std::sync::Mutex, } impl NonceSync { /// Create a new thread safe nonce pub fn new(rand: &Random) -> Self { Self { nonce: ::std::sync::Mutex::new(Nonce::new(rand)), } } /// Advance the nonce and return the *old* value pub fn advance(&self) -> Nonce { let old_nonce: Nonce; { let mut nonce = self.nonce.lock().unwrap(); old_nonce = *nonce; nonce.advance(); } old_nonce } } /// Select the best cipher from our supported list /// and the other endpoint supported list. /// Give priority to our list pub fn server_select_cipher( cfg: &Config, client_supported: &Vec, ) -> Option { cfg.ciphers .iter() .find(|c| client_supported.contains(c)) .copied() } /// Select the best cipher from our supported list /// and the other endpoint supported list. /// Give priority to the server list /// This is used only in the Directory synchronized handshake pub fn client_select_cipher( cfg: &Config, server_supported: &Vec, ) -> Option { server_supported .iter() .find(|c| cfg.ciphers.contains(c)) .copied() }