use crate::Tag;
use aead::{
generic_array::{
typenum::{Unsigned, U16},
ArrayLength, GenericArray,
},
Buffer, Error,
};
use aes::{Aes128, Aes256};
use cipher::{
BlockCipher, BlockEncryptMut, InnerIvInit, Key, KeyInit, KeySizeUser, StreamCipherCore,
};
use cmac::Cmac;
use core::ops::Add;
use dbl::Dbl;
use digest::{CtOutput, FixedOutputReset, Mac};
use zeroize::Zeroize;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "pmac")]
use pmac::Pmac;
pub const IV_SIZE: usize = 16;
pub const MAX_HEADERS: usize = 126;
type Ctr128BE<C> = ctr::CtrCore<C, ctr::flavors::Ctr128BE>;
pub type KeySize<C> = <<C as KeySizeUser>::KeySize as Add>::Output;
pub struct Siv<C, M>
where
C: BlockCipher<BlockSize = U16> + BlockEncryptMut + KeyInit + KeySizeUser,
M: Mac<OutputSize = U16>,
{
encryption_key: Key<C>,
mac: M,
}
pub type CmacSiv<BlockCipher> = Siv<BlockCipher, Cmac<BlockCipher>>;
#[cfg(feature = "pmac")]
#[cfg_attr(docsrs, doc(cfg(feature = "pmac")))]
pub type PmacSiv<BlockCipher> = Siv<BlockCipher, Pmac<BlockCipher>>;
pub type Aes128Siv = CmacSiv<Aes128>;
pub type Aes256Siv = CmacSiv<Aes256>;
#[cfg(feature = "pmac")]
#[cfg_attr(docsrs, doc(cfg(feature = "pmac")))]
pub type Aes128PmacSiv = PmacSiv<Aes128>;
#[cfg(feature = "pmac")]
#[cfg_attr(docsrs, doc(cfg(feature = "pmac")))]
pub type Aes256PmacSiv = PmacSiv<Aes256>;
impl<C, M> KeySizeUser for Siv<C, M>
where
C: BlockCipher<BlockSize = U16> + BlockEncryptMut + KeyInit + KeySizeUser,
M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
<C as KeySizeUser>::KeySize: Add,
KeySize<C>: ArrayLength<u8>,
{
type KeySize = KeySize<C>;
}
impl<C, M> KeyInit for Siv<C, M>
where
C: BlockCipher<BlockSize = U16> + BlockEncryptMut + KeyInit + KeySizeUser,
M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
<C as KeySizeUser>::KeySize: Add,
KeySize<C>: ArrayLength<u8>,
{
fn new(key: &GenericArray<u8, KeySize<C>>) -> Self {
let encryption_key = GenericArray::clone_from_slice(&key[M::key_size()..]);
let mac = <M as Mac>::new(GenericArray::from_slice(&key[..M::KeySize::to_usize()]));
Self {
encryption_key,
mac,
}
}
}
impl<C, M> Siv<C, M>
where
C: BlockCipher<BlockSize = U16> + BlockEncryptMut + KeyInit + KeySizeUser,
M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
{
#[cfg(feature = "alloc")]
pub fn encrypt<I, T>(&mut self, headers: I, plaintext: &[u8]) -> Result<Vec<u8>, Error>
where
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
{
let mut buffer = Vec::with_capacity(plaintext.len() + IV_SIZE);
buffer.extend_from_slice(plaintext);
self.encrypt_in_place(headers, &mut buffer)?;
Ok(buffer)
}
pub fn encrypt_in_place<I, T>(
&mut self,
headers: I,
buffer: &mut dyn Buffer,
) -> Result<(), Error>
where
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
{
let pt_len = buffer.len();
buffer.extend_from_slice(Tag::default().as_slice())?;
buffer.as_mut().copy_within(..pt_len, IV_SIZE);
let tag = self.encrypt_in_place_detached(headers, &mut buffer.as_mut()[IV_SIZE..])?;
buffer.as_mut()[..IV_SIZE].copy_from_slice(tag.as_slice());
Ok(())
}
pub fn encrypt_in_place_detached<I, T>(
&mut self,
headers: I,
plaintext: &mut [u8],
) -> Result<Tag, Error>
where
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
{
let siv_tag = s2v(&mut self.mac, headers, plaintext)?;
self.xor_with_keystream(siv_tag, plaintext);
Ok(siv_tag)
}
#[cfg(feature = "alloc")]
pub fn decrypt<I, T>(&mut self, headers: I, ciphertext: &[u8]) -> Result<Vec<u8>, Error>
where
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
{
let mut buffer = ciphertext.to_vec();
self.decrypt_in_place(headers, &mut buffer)?;
Ok(buffer)
}
pub fn decrypt_in_place<I, T>(
&mut self,
headers: I,
buffer: &mut dyn Buffer,
) -> Result<(), Error>
where
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
{
if buffer.len() < IV_SIZE {
return Err(Error);
}
let siv_tag = Tag::clone_from_slice(&buffer.as_ref()[..IV_SIZE]);
self.decrypt_in_place_detached(headers, &mut buffer.as_mut()[IV_SIZE..], &siv_tag)?;
let pt_len = buffer.len() - IV_SIZE;
buffer.as_mut().copy_within(IV_SIZE.., 0);
buffer.truncate(pt_len);
Ok(())
}
pub fn decrypt_in_place_detached<I, T>(
&mut self,
headers: I,
ciphertext: &mut [u8],
siv_tag: &Tag,
) -> Result<(), Error>
where
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
{
self.xor_with_keystream(*siv_tag, ciphertext);
let computed_siv_tag = s2v(&mut self.mac, headers, ciphertext)?;
if CtOutput::<M>::new(computed_siv_tag) == CtOutput::new(*siv_tag) {
Ok(())
} else {
self.xor_with_keystream(*siv_tag, ciphertext);
Err(Error)
}
}
fn xor_with_keystream(&mut self, mut iv: Tag, msg: &mut [u8]) {
iv[8] &= 0x7f;
iv[12] &= 0x7f;
Ctr128BE::<C>::inner_iv_init(C::new(&self.encryption_key), &iv)
.apply_keystream_partial(msg.into());
}
}
impl<C, M> Drop for Siv<C, M>
where
C: BlockCipher<BlockSize = U16> + BlockEncryptMut + KeyInit + KeySizeUser,
M: Mac<OutputSize = U16>,
{
fn drop(&mut self) {
self.encryption_key.zeroize()
}
}
fn s2v<M, I, T>(mac: &mut M, headers: I, message: &[u8]) -> Result<Tag, Error>
where
M: Mac<OutputSize = U16> + FixedOutputReset,
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
{
Mac::update(mac, &Tag::default());
let mut state = mac.finalize_reset().into_bytes();
for (i, header) in headers.into_iter().enumerate() {
if i >= MAX_HEADERS {
return Err(Error);
}
state = state.dbl();
Mac::update(mac, header.as_ref());
let code = mac.finalize_reset().into_bytes();
xor_in_place(&mut state, &code);
}
if message.len() >= IV_SIZE {
let n = message.len().checked_sub(IV_SIZE).unwrap();
Mac::update(mac, &message[..n]);
xor_in_place(&mut state, &message[n..]);
} else {
state = state.dbl();
xor_in_place(&mut state, message);
state[message.len()] ^= 0x80;
};
Mac::update(mac, state.as_ref());
Ok(mac.finalize_reset().into_bytes())
}
#[inline]
fn xor_in_place(dst: &mut [u8], src: &[u8]) {
for (a, b) in dst[..src.len()].iter_mut().zip(src) {
*a ^= *b;
}
}