#![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"
)]
pub use aead::{self, AeadCore, AeadInOut, Error, Key, KeyInit, KeySizeUser, Tag};
pub use belt_block::BeltBlock;
use aead::array::ArraySize;
use aead::consts::{True, U8, U16};
use aead::{TagPosition, inout::InOutBuf};
use belt_block::cipher::common::InnerUser;
use belt_block::cipher::{Block, BlockCipherEncrypt, StreamCipher};
use belt_ctr::cipher::InnerIvInit;
use belt_ctr::{GenericBeltCtr, GenericBeltCtrCore};
use core::fmt;
use core::marker::PhantomData;
use universal_hash::UniversalHash;
use universal_hash::common::{BlockSizeUser, InnerInit};
use universal_hash::typenum::{IsLessOrEqual, NonZero};
pub type Nonce = aead::Nonce<BeltDwp>;
mod gf;
mod ghash;
use ghash::GHash;
const T: u128 = 0xE45D_4A58_8E00_6D36_3BF5_080A_C8BA_94B1;
pub type BeltDwp = Dwp<BeltBlock, U8>;
pub struct Dwp<C, TagSize>
where
C: BlockCipherEncrypt + BlockSizeUser<BlockSize = U16>,
TagSize: ArraySize + NonZero + IsLessOrEqual<U16, Output = True>,
{
cipher: C,
_pd: PhantomData<TagSize>,
}
impl<C, TagSize> InnerUser for Dwp<C, TagSize>
where
C: BlockCipherEncrypt + BlockSizeUser<BlockSize = U16>,
TagSize: ArraySize + NonZero + IsLessOrEqual<U16, Output = True>,
{
type Inner = C;
}
impl<C, TagSize> InnerInit for Dwp<C, TagSize>
where
C: BlockCipherEncrypt + BlockSizeUser<BlockSize = U16>,
TagSize: ArraySize + NonZero + IsLessOrEqual<U16, Output = True>,
{
fn inner_init(cipher: Self::Inner) -> Self {
Self {
cipher,
_pd: PhantomData,
}
}
}
impl<C, TagSize> AeadInOut for Dwp<C, TagSize>
where
C: BlockCipherEncrypt + BlockSizeUser<BlockSize = U16>,
TagSize: ArraySize + NonZero + IsLessOrEqual<U16, Output = True>,
{
fn encrypt_inout_detached(
&self,
nonce: &Nonce,
associated_data: &[u8],
mut buffer: InOutBuf<'_, '_, u8>,
) -> aead::Result<Tag<Self>> {
let sizes_block = get_sizes_block(associated_data.len(), buffer.len());
let mut s = *nonce;
self.cipher.encrypt_block(&mut s);
let mut r = s;
self.cipher.encrypt_block(&mut r);
let mut ghash = GHash::new_with_init_block(&r, T);
let core = GenericBeltCtrCore::inner_iv_init(&self.cipher, nonce);
let mut enc_cipher = GenericBeltCtr::from_core(core);
ghash.update_padded(associated_data);
enc_cipher.apply_keystream_inout(buffer.reborrow());
ghash.update_padded(buffer.get_out());
ghash.update_padded(&sizes_block);
let mut tag = ghash.finalize_reset();
self.cipher.encrypt_block(&mut tag);
tag[..TagSize::USIZE].try_into().map_err(|_| Error)
}
fn decrypt_inout_detached(
&self,
nonce: &Nonce,
associated_data: &[u8],
buffer: InOutBuf<'_, '_, u8>,
tag: &Tag<Self>,
) -> aead::Result<()> {
let sizes_block = get_sizes_block(associated_data.len(), buffer.len());
let mut s = *nonce;
self.cipher.encrypt_block(&mut s);
let mut r = s;
self.cipher.encrypt_block(&mut r);
let mut ghash = GHash::new_with_init_block(&r, T);
ghash.update_padded(associated_data);
ghash.update_padded(buffer.get_in());
ghash.update_padded(&sizes_block);
let mut tag_exact = ghash.finalize_reset();
self.cipher.encrypt_block(&mut tag_exact);
use subtle::ConstantTimeEq;
if tag_exact[..TagSize::USIZE].ct_eq(tag).into() {
let core = GenericBeltCtrCore::inner_iv_init(&self.cipher, nonce);
let mut enc_cipher = GenericBeltCtr::from_core(core);
enc_cipher.apply_keystream_inout(buffer);
Ok(())
} else {
Err(Error)
}
}
}
impl<C, TagSize> AeadCore for Dwp<C, TagSize>
where
C: BlockCipherEncrypt + BlockSizeUser<BlockSize = U16>,
TagSize: ArraySize + NonZero + IsLessOrEqual<U16, Output = True>,
{
type NonceSize = C::BlockSize;
type TagSize = TagSize;
const TAG_POSITION: TagPosition = TagPosition::Postfix;
}
impl<C, TagSize> fmt::Debug for Dwp<C, TagSize>
where
C: BlockCipherEncrypt + BlockSizeUser<BlockSize = U16>,
TagSize: ArraySize + NonZero + IsLessOrEqual<U16, Output = True>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Dwp").finish_non_exhaustive()
}
}
fn get_sizes_block(aad_len: usize, msg_len: usize) -> Block<GHash> {
let aad_bit_len = aad_len as u64 * 8;
let msg_bit_len = msg_len as u64 * 8;
let mut sizes_block: Block<GHash> = Default::default();
sizes_block[..8].copy_from_slice(&aad_bit_len.to_le_bytes());
sizes_block[8..].copy_from_slice(&msg_bit_len.to_le_bytes());
sizes_block
}
#[cfg(feature = "zeroize")]
impl<C, TagSize> zeroize::ZeroizeOnDrop for Dwp<C, TagSize>
where
C: zeroize::ZeroizeOnDrop + BlockCipherEncrypt + BlockSizeUser<BlockSize = U16>,
TagSize: ArraySize + NonZero + IsLessOrEqual<U16, Output = True>,
{
}