#![cfg_attr(all(feature = "getrandom", feature = "std"), doc = "```")]
#![cfg_attr(not(all(feature = "getrandom", feature = "std")), doc = "```ignore")]
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![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"
)]
#![deny(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms)]
pub use aead::{self, AeadCore, AeadInPlace, Error, Key, KeyInit, KeySizeUser};
pub use cipher;
use cipher::{
consts::{U0, U16},
generic_array::{functional::FunctionalSequence, GenericArray},
BlockCipher, BlockEncrypt, InnerIvInit, StreamCipherCore,
};
use cmac::{digest::Output, Cmac, Mac};
use core::marker::PhantomData;
mod traits;
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> = GenericArray<u8, NonceSize>;
pub type Tag<TagSize> = GenericArray<u8, TagSize>;
pub mod online;
type Ctr128BE<C> = ctr::CtrCore<C, ctr::flavors::Ctr128BE>;
#[derive(Clone)]
pub struct Eax<Cipher, M = U16>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
key: Key<Cipher>,
_tag_size: PhantomData<M>,
}
impl<Cipher, M> KeySizeUser for Eax<Cipher, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
type KeySize = Cipher::KeySize;
}
impl<Cipher, M> KeyInit for Eax<Cipher, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + 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: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
type NonceSize = Cipher::BlockSize;
type TagSize = M;
type CiphertextOverhead = U0;
}
impl<Cipher, M> AeadInPlace for Eax<Cipher, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
fn encrypt_in_place_detached(
&self,
nonce: &Nonce<Self::NonceSize>,
associated_data: &[u8],
buffer: &mut [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.into());
let c = Self::cmac_with_iv(&self.key, 2, buffer);
let full_tag = n.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b);
let tag = Tag::<M>::clone_from_slice(&full_tag[..M::to_usize()]);
Ok(tag)
}
fn decrypt_in_place_detached(
&self,
nonce: &Nonce<Self::NonceSize>,
associated_data: &[u8],
buffer: &mut [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);
let expected_tag = n.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b);
let expected_tag = &expected_tag[..tag.len()];
use subtle::ConstantTimeEq;
if expected_tag.ct_eq(tag).into() {
Ctr128BE::<Cipher>::inner_iv_init(Cipher::new(&self.key), &n)
.apply_keystream_partial(buffer.into());
Ok(())
} else {
Err(Error)
}
}
}
impl<Cipher, M> Eax<Cipher, M>
where
Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
M: TagSize,
{
fn cmac_with_iv(
key: &GenericArray<u8, Cipher::KeySize>,
iv: u8,
data: &[u8],
) -> Output<Cmac<Cipher>> {
let mut mac = <Cmac<Cipher> as Mac>::new(key);
mac.update(&[0; 15]);
mac.update(&[iv]);
mac.update(data);
mac.finalize().into_bytes()
}
}