#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![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"
)]
#![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"
)]
mod traits;
pub use aead::{self, AeadCore, AeadInOut, Error, Key, KeyInit, KeySizeUser};
pub use cipher;
use aead::{TagPosition, inout::InOutBuf};
use cipher::{
BlockCipherEncrypt, BlockSizeUser, InnerIvInit, StreamCipherCore, array::Array, consts::U16,
};
use cmac::{Cmac, Mac, digest::Output};
use core::{fmt, marker::PhantomData};
use traits::TagSize;
pub const A_MAX: u64 = 1 << 36;
pub const P_MAX: u64 = 1 << 36;
pub const C_MAX: u64 = (1 << 36) + 16;
pub type Nonce<NonceSize> = Array<u8, NonceSize>;
pub type Tag<TagSize> = Array<u8, TagSize>;
pub mod online;
type Ctr128BE<C> = ctr::CtrCore<C, ctr::flavors::Ctr128BE>;
#[derive(Clone)]
pub struct Eax<Cipher, M = U16>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
{
key: Key<Cipher>,
_tag_size: PhantomData<M>,
}
impl<Cipher, M> KeySizeUser for Eax<Cipher, M>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
{
type KeySize = Cipher::KeySize;
}
impl<Cipher, M> KeyInit for Eax<Cipher, M>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
{
fn new(key: &Key<Cipher>) -> Self {
Self {
key: key.clone(),
_tag_size: PhantomData,
}
}
}
impl<Cipher, M> AeadCore for Eax<Cipher, M>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
{
type NonceSize = Cipher::BlockSize;
type TagSize = M;
const TAG_POSITION: TagPosition = TagPosition::Postfix;
}
impl<Cipher, M> AeadInOut for Eax<Cipher, M>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
{
fn encrypt_inout_detached(
&self,
nonce: &Nonce<Self::NonceSize>,
associated_data: &[u8],
mut buffer: InOutBuf<'_, '_, u8>,
) -> Result<Tag<M>, Error> {
if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX {
return Err(Error);
}
let n = Self::cmac_with_iv(&self.key, 0, nonce);
let h = Self::cmac_with_iv(&self.key, 1, associated_data);
Ctr128BE::<Cipher>::inner_iv_init(Cipher::new(&self.key), &n)
.apply_keystream_partial(buffer.reborrow());
let c = Self::cmac_with_iv(&self.key, 2, buffer.get_out());
let tag = Array::<u8, M>::from_fn(|i| n[i] ^ h[i] ^ c[i]);
Ok(tag)
}
fn decrypt_inout_detached(
&self,
nonce: &Nonce<Self::NonceSize>,
associated_data: &[u8],
buffer: InOutBuf<'_, '_, u8>,
tag: &Tag<M>,
) -> Result<(), Error> {
if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
return Err(Error);
}
let n = Self::cmac_with_iv(&self.key, 0, nonce);
let h = Self::cmac_with_iv(&self.key, 1, associated_data);
let c = Self::cmac_with_iv(&self.key, 2, buffer.get_in());
let expected_tag = Array::<u8, M>::from_fn(|i| n[i] ^ h[i] ^ c[i]);
use ctutils::CtEq;
if expected_tag.ct_eq(tag).into() {
Ctr128BE::<Cipher>::inner_iv_init(Cipher::new(&self.key), &n)
.apply_keystream_partial(buffer);
Ok(())
} else {
Err(Error)
}
}
}
impl<Cipher, M> fmt::Debug for Eax<Cipher, M>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("Eax").finish_non_exhaustive()
}
}
impl<Cipher, M> Eax<Cipher, M>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
{
fn cmac_with_iv(key: &Array<u8, Cipher::KeySize>, iv: u8, data: &[u8]) -> Output<Cmac<Cipher>> {
let mut mac = <Cmac<Cipher> as KeyInit>::new(key);
mac.update(&[0; 15]);
mac.update(&[iv]);
mac.update(data);
mac.finalize().into_bytes()
}
}