use crate::{Cmac, Error, Nonce, Tag, TagSize};
use aead::consts::U16;
use cipher::{
generic_array::functional::FunctionalSequence, BlockCipher, BlockEncrypt, Key, KeyInit,
KeyIvInit, StreamCipher,
};
use cmac::Mac;
use core::marker::PhantomData;
pub use Eax as EaxOnline;
pub trait CipherOp {}
pub struct Encrypt;
impl CipherOp for Encrypt {}
pub struct Decrypt;
impl CipherOp for Decrypt {}
pub struct Eax<Cipher, Op, M = U16>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
Op: CipherOp,
M: TagSize,
{
imp: EaxImpl<Cipher, M>,
marker: PhantomData<Op>,
}
impl<Cipher, Op, M> Eax<Cipher, Op, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
Op: CipherOp,
M: TagSize,
{
pub fn with_key_and_nonce(key: &Key<Cipher>, nonce: &Nonce<Cipher::BlockSize>) -> Self {
let imp = EaxImpl::<Cipher, M>::with_key_and_nonce(key, nonce);
Self {
imp,
marker: PhantomData,
}
}
#[inline]
pub fn update_assoc(&mut self, aad: &[u8]) {
self.imp.update_assoc(aad);
}
#[inline]
pub fn tag_clone(&self) -> Tag<M> {
self.imp.tag_clone()
}
}
impl<Cipher, M> Eax<Cipher, Encrypt, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
#[inline]
pub fn encrypt(&mut self, msg: &mut [u8]) {
self.imp.encrypt(msg)
}
#[must_use = "tag must be saved to later verify decrypted data"]
#[inline]
pub fn finish(self) -> Tag<M> {
self.imp.tag()
}
}
impl<Cipher, M> Eax<Cipher, Decrypt, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
#[inline]
pub fn decrypt_unauthenticated_hazmat(&mut self, msg: &mut [u8]) {
self.imp.decrypt(msg)
}
#[must_use = "decrypted data stream must be verified for authenticity"]
pub fn finish(self, expected: &Tag<M>) -> Result<(), Error> {
self.imp.verify_ct(expected)
}
}
#[doc(hidden)]
struct EaxImpl<Cipher, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
nonce: Nonce<Cipher::BlockSize>,
data: Cmac<Cipher>,
message: Cmac<Cipher>,
ctr: ctr::Ctr128BE<Cipher>,
#[cfg(test)]
key: Key<Cipher>,
_tag_size: PhantomData<M>,
}
impl<Cipher, M> EaxImpl<Cipher, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
fn with_key_and_nonce(key: &Key<Cipher>, nonce: &Nonce<Cipher::BlockSize>) -> Self {
let prepend_cmac = |key, init_val, data| {
let mut cmac = <Cmac<Cipher> as Mac>::new(key);
cmac.update(&[0; 15]);
cmac.update(&[init_val]);
cmac.update(data);
cmac
};
let n = prepend_cmac(key, 0, nonce);
let n = n.finalize().into_bytes();
let h = prepend_cmac(key, 1, &[]);
let c = prepend_cmac(key, 2, &[]);
let cipher = ctr::Ctr128BE::<Cipher>::new(key, &n);
Self {
nonce: n,
data: h,
message: c,
ctr: cipher,
#[cfg(test)]
key: key.clone(),
_tag_size: Default::default(),
}
}
#[inline]
pub fn update_assoc(&mut self, aad: &[u8]) {
self.data.update(aad);
}
#[inline]
fn encrypt(&mut self, msg: &mut [u8]) {
self.ctr.apply_keystream(msg);
self.message.update(msg);
}
#[inline]
fn decrypt(&mut self, msg: &mut [u8]) {
self.message.update(msg);
self.ctr.apply_keystream(msg);
}
#[inline]
fn tag(self) -> Tag<M> {
let h = self.data.finalize().into_bytes();
let c = self.message.finalize().into_bytes();
let full_tag = self.nonce.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b);
Tag::<M>::clone_from_slice(&full_tag[..M::to_usize()])
}
#[inline]
fn tag_clone(&self) -> Tag<M> {
let h = self.data.clone().finalize().into_bytes();
let c = self.message.clone().finalize().into_bytes();
let full_tag = self.nonce.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b);
Tag::<M>::clone_from_slice(&full_tag[..M::to_usize()])
}
fn verify_ct(self, expected: &Tag<M>) -> Result<(), Error> {
use subtle::ConstantTimeEq;
let resulting_tag = &self.tag()[..expected.len()];
if resulting_tag.ct_eq(expected).into() {
Ok(())
} else {
Err(Error)
}
}
}
#[cfg(test)]
mod test_impl {
use super::*;
use aead::{
consts::U0, generic_array::GenericArray, AeadCore, AeadMutInPlace, KeyInit, KeySizeUser,
};
impl<Cipher, M> KeySizeUser for EaxImpl<Cipher, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
type KeySize = Cipher::KeySize;
}
impl<Cipher, M> KeyInit for EaxImpl<Cipher, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
fn new(key: &Key<Cipher>) -> Self {
let nonce = GenericArray::default();
Self::with_key_and_nonce(key, &nonce)
}
}
impl<Cipher, M> AeadCore for super::EaxImpl<Cipher, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
type NonceSize = Cipher::BlockSize;
type TagSize = M;
type CiphertextOverhead = U0;
}
impl<Cipher, M> AeadMutInPlace for super::EaxImpl<Cipher, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
fn encrypt_in_place_detached(
&mut self,
nonce: &Nonce<Self::NonceSize>,
associated_data: &[u8],
buffer: &mut [u8],
) -> Result<Tag<M>, Error> {
*self = Self::with_key_and_nonce(&self.key.clone(), nonce);
self.update_assoc(associated_data);
self.encrypt(buffer);
Ok(self.tag_clone())
}
fn decrypt_in_place_detached(
&mut self,
nonce: &Nonce<Self::NonceSize>,
associated_data: &[u8],
buffer: &mut [u8],
expected_tag: &Tag<M>,
) -> Result<(), Error> {
*self = Self::with_key_and_nonce(&self.key.clone(), nonce);
self.update_assoc(associated_data);
self.decrypt(buffer);
let tag = self.tag_clone();
use subtle::ConstantTimeEq;
if expected_tag.ct_eq(&tag).into() {
Ok(())
} else {
Err(Error)
}
}
}
}