#![no_std]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
)]
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms)]
#![cfg_attr(feature = "getrandom", doc = "```")]
#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
#![cfg_attr(all(feature = "getrandom", feature = "arrayvec"), doc = "```")]
#![cfg_attr(
not(all(feature = "getrandom", feature = "arrayvec")),
doc = "```ignore"
)]
extern crate alloc;
pub use aead::{self, AeadCore, AeadInOut, Error, Key, KeyInit, KeySizeUser, TagPosition};
use aead::inout::InOutBuf;
use core::ops::Add;
use eme2::cipher::{
array::Array,
consts::{U16, U24, U32},
BlockCipherDecrypt, BlockCipherEncrypt, BlockSizeUser,
};
#[cfg(feature = "aes")]
pub use aes;
use blake3::Hasher;
use eme2::cipher::InnerIvInit;
use eme2::Eme2;
use subtle::ConstantTimeEq;
#[cfg(feature = "zeroize")]
use zeroize::{Zeroize, ZeroizeOnDrop};
const SIV_CONTEXT: &str = "AES-EME2-BLAKE3-SIV-01";
pub type Nonce<NonceSize = U16> = Array<u8, NonceSize>;
pub type Tag<TagSize = U24> = Array<u8, TagSize>;
#[cfg(feature = "aes")]
pub type Aes128Eme2Blake3 = Eme2Blake3<aes::Aes128>;
#[cfg(feature = "aes")]
pub type Aes256Eme2Blake3 = Eme2Blake3<aes::Aes256>;
#[cfg(feature = "zeroize")]
pub trait ZeroizeMarker: zeroize::ZeroizeOnDrop {}
#[cfg(feature = "zeroize")]
impl<T: zeroize::ZeroizeOnDrop> ZeroizeMarker for T {}
#[cfg(not(feature = "zeroize"))]
pub trait ZeroizeMarker {}
#[cfg(not(feature = "zeroize"))]
impl<T> ZeroizeMarker for T {}
#[derive(Clone)]
#[cfg_attr(feature = "zeroize", derive(ZeroizeOnDrop))]
pub struct Eme2Blake3<C>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + BlockCipherDecrypt + Clone + ZeroizeMarker,
{
cipher: C,
base_hasher: Hasher,
}
impl<C> core::fmt::Debug for Eme2Blake3<C>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + BlockCipherDecrypt + Clone + ZeroizeMarker,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Eme2Blake3").finish_non_exhaustive()
}
}
impl<C> KeySizeUser for Eme2Blake3<C>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + BlockCipherDecrypt + KeySizeUser + Clone + ZeroizeMarker,
C::KeySize: Add<U32>,
<C::KeySize as Add<U32>>::Output: eme2::cipher::array::ArraySize,
{
type KeySize = <C::KeySize as Add<U32>>::Output;
}
impl<C> KeyInit for Eme2Blake3<C>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + BlockCipherDecrypt + KeySizeUser + KeyInit + Clone + ZeroizeMarker,
C::KeySize: Add<U32>,
<C::KeySize as Add<U32>>::Output: eme2::cipher::array::ArraySize,
{
fn new(key: &Key<Self>) -> Self {
use eme2::cipher::typenum::Unsigned;
let enc_size = C::KeySize::USIZE;
let (enc_slice, mac_slice) = key.split_at(enc_size);
let cipher = C::new_from_slice(enc_slice).unwrap();
let mut base_hasher = Hasher::new_keyed(mac_slice.try_into().unwrap());
base_hasher.update(SIV_CONTEXT.as_bytes());
Self { cipher, base_hasher }
}
}
impl<C> AeadCore for Eme2Blake3<C>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + BlockCipherDecrypt + Clone + ZeroizeMarker,
{
type NonceSize = U16;
type TagSize = U24;
const TAG_POSITION: TagPosition = TagPosition::Prefix;
}
impl<C> AeadInOut for Eme2Blake3<C>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + BlockCipherDecrypt + Clone + ZeroizeMarker,
{
fn encrypt_inout_detached(
&self,
nonce: &aead::Nonce<Self>,
associated_data: &[u8],
mut buffer: InOutBuf<'_, '_, u8>,
) -> Result<aead::Tag<Self>, Error> {
let plaintext_len = buffer.len();
if plaintext_len < 16 {
return Err(Error);
}
let in_data = buffer.get_in();
let mut temp = alloc::vec::Vec::from(in_data);
let tag_bytes = self.compute_siv(nonce, associated_data, &temp);
let tweak: [u8; 16] = tag_bytes[..16].try_into().unwrap();
let eme2_cipher = Eme2::inner_iv_init(self.cipher.clone(), &tweak.into());
eme2_cipher.encrypt(&mut temp).map_err(|_| Error)?;
buffer.get_out().copy_from_slice(&temp);
#[cfg(feature = "zeroize")]
temp.zeroize();
Ok(tag_bytes.into())
}
fn decrypt_inout_detached(
&self,
nonce: &aead::Nonce<Self>,
associated_data: &[u8],
mut buffer: InOutBuf<'_, '_, u8>,
tag: &aead::Tag<Self>,
) -> Result<(), Error> {
if buffer.len() < 16 {
return Err(Error);
}
let in_data = buffer.get_in();
let mut temp = alloc::vec::Vec::from(in_data);
let tweak: [u8; 16] = tag[..16].try_into().unwrap();
let eme2_cipher = Eme2::inner_iv_init(self.cipher.clone(), &tweak.into());
eme2_cipher.decrypt(&mut temp).map_err(|_| Error)?;
let expected_tag = self.compute_siv(nonce, associated_data, &temp);
if expected_tag.ct_eq(tag.as_slice()).unwrap_u8() == 0 {
#[cfg(feature = "zeroize")]
temp.zeroize();
return Err(Error);
}
buffer.get_out().copy_from_slice(&temp);
#[cfg(feature = "zeroize")]
temp.zeroize();
Ok(())
}
}
impl<C> Eme2Blake3<C>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + BlockCipherDecrypt + Clone + ZeroizeMarker,
{
fn compute_siv(&self, nonce: &[u8], ad: &[u8], payload: &[u8]) -> [u8; 24] {
let mut hasher = self.base_hasher.clone();
hasher.update(nonce);
hasher.update(&(ad.len() as u64).to_le_bytes());
hasher.update(&(payload.len() as u64).to_le_bytes());
hasher.update(ad);
hasher.update(payload);
let mut tag = [0u8; 24];
hasher.finalize_xof().fill(&mut tag);
tag
}
}