use core::ops::{Add, Div, Mul};
use crate::{Error, IV_LEN, IntegrityCheckFailed, IvLen, ctx::Ctx};
use aes::cipher::{
Array, Block, BlockCipherDecrypt, BlockCipherEncrypt,
array::ArraySize,
common::{InnerInit, InnerUser},
consts::{B1, U7, U4294967296},
typenum::{Add1, IsLess, Le, NonZero, Prod, Quot, Sum, U16},
};
type KwpMaxLen = U4294967296;
const KWP_MAX_LEN: usize = u32::MAX as usize;
const KWP_IV_PREFIX: [u8; IV_LEN / 2] = [0xA6, 0x59, 0x59, 0xA6];
type IvLenM1 = U7;
pub type KwpWrappedKey<N> = Array<u8, Prod<Add1<Quot<Sum<N, IvLenM1>, IvLen>>, IvLen>>;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct AesKwp<C> {
cipher: C,
}
impl<C> InnerUser for AesKwp<C> {
type Inner = C;
}
impl<C> InnerInit for AesKwp<C> {
#[inline]
fn inner_init(cipher: Self::Inner) -> Self {
AesKwp { cipher }
}
}
impl<C: BlockCipherEncrypt<BlockSize = U16>> AesKwp<C> {
fn wrap_key_trusted(&self, key: &[u8], buf: &mut [u8]) {
let semiblocks_len = key.len().div_ceil(IV_LEN);
let block = &mut Block::<C>::default();
let (prefix, mli) = block[..IV_LEN].split_at_mut(IV_LEN / 2);
prefix.copy_from_slice(&KWP_IV_PREFIX);
mli.copy_from_slice(&(key.len() as u32).to_be_bytes());
if semiblocks_len == 1 {
block[IV_LEN..][..key.len()].copy_from_slice(key);
self.cipher
.encrypt_block_b2b(block, buf.try_into().unwrap());
} else {
buf[IV_LEN..][..key.len()].copy_from_slice(key);
self.cipher.encrypt_with_backend(Ctx {
blocks_len: semiblocks_len,
block,
buf,
});
buf[..IV_LEN].copy_from_slice(&block[..IV_LEN]);
}
}
#[inline]
pub fn wrap_key<'a>(&self, key: &[u8], buf: &'a mut [u8]) -> Result<&'a [u8], Error> {
if key.len() > KWP_MAX_LEN {
return Err(Error::InvalidDataSize);
}
let semiblocks_len = key.len().div_ceil(IV_LEN);
let expected_len = semiblocks_len * IV_LEN + IV_LEN;
let buf = buf
.get_mut(..expected_len)
.ok_or(Error::InvalidOutputSize { expected_len })?;
self.wrap_key_trusted(key, buf);
Ok(buf)
}
#[inline]
pub fn wrap_fixed_key<N>(&self, key: &Array<u8, N>) -> KwpWrappedKey<N>
where
N: ArraySize + Add<IvLenM1> + IsLess<KwpMaxLen>,
Le<N, KwpMaxLen>: NonZero,
Sum<N, IvLenM1>: Div<IvLen>,
Quot<Sum<N, IvLenM1>, IvLen>: Add<B1>,
Add1<Quot<Sum<N, IvLenM1>, IvLen>>: Mul<IvLen>,
Prod<Add1<Quot<Sum<N, IvLenM1>, IvLen>>, IvLen>: ArraySize,
{
let semiblocks_len = key.len().div_ceil(IV_LEN);
let mut buf = KwpWrappedKey::<N>::default();
assert_eq!(semiblocks_len * IV_LEN + IV_LEN, buf.len());
self.wrap_key_trusted(key, &mut buf);
buf
}
}
impl<C: BlockCipherDecrypt<BlockSize = U16>> AesKwp<C> {
fn unwrap_key_trusted<'a>(
&self,
wkey: &[u8],
buf: &'a mut [u8],
) -> Result<&'a [u8], IntegrityCheckFailed> {
let blocks_len = buf.len() / IV_LEN;
let block = &mut Block::<C>::default();
if blocks_len == 1 {
block.copy_from_slice(wkey);
self.cipher.decrypt_block(block);
buf.copy_from_slice(&block[IV_LEN..]);
} else {
block[..IV_LEN].copy_from_slice(&wkey[..IV_LEN]);
buf.copy_from_slice(&wkey[IV_LEN..]);
self.cipher.decrypt_with_backend(Ctx {
blocks_len,
block,
buf,
});
}
let prefix_calc = u32::from_ne_bytes(block[..IV_LEN / 2].try_into().unwrap());
let prefix_exp = u32::from_ne_bytes(KWP_IV_PREFIX);
if prefix_calc != prefix_exp {
buf.fill(0);
return Err(IntegrityCheckFailed);
}
let mli_bytes = block[IV_LEN / 2..IV_LEN].try_into().unwrap();
let mli: usize = usize::try_from(u32::from_be_bytes(mli_bytes)).map_err(|_| {
buf.fill(0);
IntegrityCheckFailed
})?;
if mli.div_ceil(IV_LEN) != blocks_len {
buf.fill(0);
return Err(IntegrityCheckFailed);
}
let (res, pad) = buf.split_at_mut(mli);
if !pad.iter().all(|&b| b == 0) {
res.fill(0);
pad.fill(0);
return Err(IntegrityCheckFailed);
}
Ok(res)
}
#[inline]
pub fn unwrap_key<'a>(&self, data: &[u8], buf: &'a mut [u8]) -> Result<&'a [u8], Error> {
let blocks_len = data.len() / IV_LEN;
let blocks_rem = data.len() % IV_LEN;
if blocks_rem != 0 || blocks_len < 1 || data.len() > KWP_MAX_LEN {
return Err(Error::InvalidDataSize);
}
let blocks_len = blocks_len - 1;
let expected_len = blocks_len * IV_LEN;
let buf = buf
.get_mut(..expected_len)
.ok_or(Error::InvalidOutputSize { expected_len })?;
self.unwrap_key_trusted(data, buf)
.map_err(|_| Error::IntegrityCheckFailed)
}
#[inline]
pub fn unwrap_fixed_key<N>(
&self,
wkey: &KwpWrappedKey<N>,
) -> Result<Array<u8, N>, IntegrityCheckFailed>
where
N: ArraySize + Add<IvLenM1> + IsLess<KwpMaxLen>,
Le<N, KwpMaxLen>: NonZero,
Sum<N, IvLenM1>: Div<IvLen>,
Quot<Sum<N, IvLenM1>, IvLen>: Add<B1> + Mul<IvLen>,
Add1<Quot<Sum<N, IvLenM1>, IvLen>>: Mul<IvLen>,
Prod<Add1<Quot<Sum<N, IvLenM1>, IvLen>>, IvLen>: ArraySize,
Prod<Quot<Sum<N, IvLenM1>, IvLen>, IvLen>: ArraySize,
{
let mut buf = Array::<u8, Prod<Quot<Sum<N, IvLenM1>, IvLen>, IvLen>>::default();
self.unwrap_key_trusted(wkey, &mut buf)
.map(|res| res.try_into().unwrap())
}
}
#[cfg(feature = "zeroize")]
impl<C: zeroize::ZeroizeOnDrop> zeroize::ZeroizeOnDrop for AesKwp<C> {}