#![cfg_attr(feature = "alloc", doc = "```")]
#![cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#![cfg_attr(feature = "alloc", doc = "```")]
#![cfg_attr(not(feature = "alloc"), doc = "```ignore")]
use crate::Tag;
use aead::{
Buffer, Error,
array::{Array, ArraySize, typenum::U16},
inout::InOutBuf,
};
use aes::{Aes128, Aes256};
use cipher::{
BlockCipherEncrypt, BlockSizeUser, InnerIvInit, Key, KeyInit, KeySizeUser, StreamCipherCore,
};
use cmac::Cmac;
use core::{fmt, ops::Add};
use dbl::Dbl;
use digest::{CtOutput, FixedOutputReset, Mac};
#[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(crate) type KeySize<C> = <<C as KeySizeUser>::KeySize as Add>::Output;
pub struct Siv<C, M>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + 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: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit + KeySizeUser,
M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
<C as KeySizeUser>::KeySize: Add,
KeySize<C>: ArraySize,
{
type KeySize = KeySize<C>;
}
impl<C, M> KeyInit for Siv<C, M>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit + KeySizeUser,
M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
<C as KeySizeUser>::KeySize: Add,
KeySize<C>: ArraySize,
{
fn new(key: &Array<u8, KeySize<C>>) -> Self {
let (mac_key, enc_key) = key.split_at(M::key_size());
Self {
encryption_key: enc_key.try_into().expect("encryption key size mismatch"),
mac: <M as KeyInit>::new(mac_key.try_into().expect("MAC key size mismatch")),
}
}
}
impl<C, M> Siv<C, M>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + 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_inout_detached(headers, (&mut buffer.as_mut()[IV_SIZE..]).into())?;
buffer.as_mut()[..IV_SIZE].copy_from_slice(tag.as_slice());
Ok(())
}
pub fn encrypt_inout_detached<I, T>(
&mut self,
headers: I,
plaintext: InOutBuf<'_, '_, u8>,
) -> Result<Tag, Error>
where
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
{
let siv_tag = s2v(&mut self.mac, headers, plaintext.get_in())?;
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::try_from(&buffer.as_ref()[..IV_SIZE]).map_err(|_| Error)?;
self.decrypt_inout_detached(headers, (&mut buffer.as_mut()[IV_SIZE..]).into(), &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_inout_detached<I, T>(
&mut self,
headers: I,
mut ciphertext: InOutBuf<'_, '_, u8>,
siv_tag: &Tag,
) -> Result<(), Error>
where
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
{
self.xor_with_keystream(*siv_tag, ciphertext.reborrow());
let computed_siv_tag = s2v(&mut self.mac, headers, ciphertext.get_out())?;
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: InOutBuf<'_, '_, u8>) {
iv[8] &= 0x7f;
iv[12] &= 0x7f;
Ctr128BE::<C>::inner_iv_init(C::new(&self.encryption_key), &iv)
.apply_keystream_partial(msg);
}
}
impl<C, M> fmt::Debug for Siv<C, M>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit + KeySizeUser,
M: Mac<OutputSize = U16>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("Siv").finish_non_exhaustive()
}
}
impl<C, M> Drop for Siv<C, M>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit + KeySizeUser,
M: Mac<OutputSize = U16>,
{
fn drop(&mut self) {
#[cfg(feature = "zeroize")]
{
use zeroize::Zeroize;
self.encryption_key.zeroize();
}
}
}
#[cfg(feature = "zeroize")]
impl<C, M> zeroize::ZeroizeOnDrop for Siv<C, M>
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit + KeySizeUser,
M: Mac<OutputSize = U16>,
{
}
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);
}
match message.len().checked_sub(IV_SIZE) {
Some(n) => {
Mac::update(mac, &message[..n]);
xor_in_place(&mut state, &message[n..]);
}
None => {
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;
}
}