use crate::{KeySize, Tag};
use aead::generic_array::{
typenum::{Unsigned, U16},
ArrayLength, GenericArray,
};
use aead::{Buffer, Error};
use aes::{Aes128, Aes256};
use cmac::Cmac;
use core::ops::Add;
use crypto_mac::{Mac, NewMac};
use ctr::Ctr128;
use dbl::Dbl;
use stream_cipher::{NewStreamCipher, SyncStreamCipher};
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;
pub struct Siv<C, M>
where
C: NewStreamCipher<NonceSize = U16> + SyncStreamCipher,
M: Mac<OutputSize = U16>,
{
encryption_key: GenericArray<u8, <C as NewStreamCipher>::KeySize>,
mac: M,
}
pub type CmacSiv<BlockCipher> = Siv<Ctr128<BlockCipher>, Cmac<BlockCipher>>;
#[cfg(feature = "pmac")]
pub type PmacSiv<BlockCipher> = Siv<Ctr128<BlockCipher>, Pmac<BlockCipher>>;
pub type Aes128Siv = CmacSiv<Aes128>;
pub type Aes256Siv = CmacSiv<Aes256>;
#[cfg(feature = "pmac")]
pub type Aes128PmacSiv = PmacSiv<Aes128>;
#[cfg(feature = "pmac")]
pub type Aes256PmacSiv = PmacSiv<Aes256>;
impl<C, M> Siv<C, M>
where
C: NewStreamCipher<NonceSize = U16> + SyncStreamCipher,
M: Mac<OutputSize = U16> + NewMac,
<C as NewStreamCipher>::KeySize: Add,
KeySize<C>: ArrayLength<u8>,
{
pub fn new(key: GenericArray<u8, KeySize<C>>) -> Self {
let encryption_key = GenericArray::clone_from_slice(&key[M::KeySize::to_usize()..]);
let mac = M::new(GenericArray::from_slice(&key[..M::KeySize::to_usize()]));
Self {
encryption_key,
mac,
}
}
}
impl<C, M> Siv<C, M>
where
C: NewStreamCipher<NonceSize = U16> + SyncStreamCipher,
M: Mac<OutputSize = U16> + NewMac,
{
#[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 crypto_mac::Output::<M>::new(computed_siv_tag) == crypto_mac::Output::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;
C::new(GenericArray::from_slice(&self.encryption_key), &iv).apply_keystream(msg);
}
}
impl<C, M> Drop for Siv<C, M>
where
C: NewStreamCipher<NonceSize = U16> + SyncStreamCipher,
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>,
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
{
mac.update(&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(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(&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(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;
}
}