#![no_std]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms)]
use belt_block::{
BeltBlock, belt_wblock_dec, belt_wblock_enc,
cipher::{
Array,
array::ArraySize,
consts::U16,
typenum::{GrEq, IsGreaterOrEqual, NonZero, Sum, Unsigned},
},
};
use core::{fmt, ops::Add};
pub use belt_block::cipher::{self, Key, KeyInit, KeySizeUser};
pub type IvLen = U16;
pub type WrappedKey<N> = Array<u8, Sum<N, IvLen>>;
pub const IV_LEN: usize = IvLen::USIZE;
#[derive(Clone, Copy, PartialEq)]
pub struct BeltKwp {
key: [u32; 8],
}
impl fmt::Debug for BeltKwp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("BeltKwp { ... }")
}
}
impl BeltKwp {
#[inline]
pub fn wrap_key<'a>(
&self,
x: &[u8],
iv: &[u8; IV_LEN],
out: &'a mut [u8],
) -> Result<&'a [u8], Error> {
if x.len() < 16 {
return Err(Error::InvalidDataSize);
}
let out_len = x.len() + IV_LEN;
if out.len() < out_len {
return Err(Error::InvalidOutputSize {
expected: x.len() + IV_LEN,
});
}
let out = &mut out[..out_len];
let (l, r) = out.split_at_mut(x.len());
l.copy_from_slice(x);
r.copy_from_slice(iv);
belt_wblock_enc(out, &self.key).map_err(|_| Error::InvalidDataSize)?;
Ok(out)
}
#[inline]
pub fn wrap_fixed_key<N>(&self, x: &Array<u8, N>, iv: &[u8; IV_LEN]) -> WrappedKey<N>
where
N: ArraySize + Add<IvLen> + IsGreaterOrEqual<IvLen>,
Sum<N, IvLen>: ArraySize,
GrEq<N, IvLen>: NonZero,
{
let mut res = WrappedKey::<N>::default();
let (l, r) = res.split_at_mut(x.len());
l.copy_from_slice(x);
r.copy_from_slice(iv);
belt_wblock_enc(&mut res, &self.key).expect("res has correct size");
res
}
#[inline]
pub fn unwrap_key<'a>(
&self,
y: &[u8],
iv: &[u8; IV_LEN],
out: &'a mut [u8],
) -> Result<&'a [u8], Error> {
if y.len() < 32 {
return Err(Error::InvalidDataSize);
}
if out.len() < y.len() {
return Err(Error::InvalidOutputSize { expected: y.len() });
}
let out = &mut out[..y.len()];
out.copy_from_slice(y);
belt_wblock_dec(out, &self.key).map_err(|_| Error::InvalidDataSize)?;
let (key, rem) = out.split_at_mut(y.len() - IV_LEN);
let calc_iv = u128::from_ne_bytes(rem.try_into().unwrap());
let expected_iv = u128::from_ne_bytes(*iv);
if calc_iv == expected_iv {
Ok(key)
} else {
key.fill(0);
rem.fill(0);
Err(Error::IntegrityCheckFailed)
}
}
#[inline]
pub fn unwrap_fixed_key<N>(
&self,
y: &WrappedKey<N>,
iv: &[u8; IV_LEN],
) -> Result<Array<u8, N>, IntegrityCheckFailed>
where
N: ArraySize + Add<IvLen> + IsGreaterOrEqual<IvLen>,
Sum<N, IvLen>: ArraySize,
GrEq<N, IvLen>: NonZero,
{
let mut y = y.clone();
belt_wblock_dec(&mut y, &self.key).expect("y has correct size");
let (key, rem) = y.split_at(N::USIZE);
let calc_iv = u128::from_ne_bytes(rem.try_into().unwrap());
let expected_iv = u128::from_ne_bytes(*iv);
if calc_iv == expected_iv {
Ok(key.try_into().unwrap())
} else {
Err(IntegrityCheckFailed)
}
}
}
impl KeyInit for BeltKwp {
fn new(key: &Key<Self>) -> Self {
let mut res = [0u32; 8];
res.iter_mut()
.zip(key.chunks_exact(4))
.for_each(|(dst, src)| *dst = u32::from_le_bytes(src.try_into().unwrap()));
Self { key: res }
}
}
impl KeySizeUser for BeltKwp {
type KeySize = <BeltBlock as KeySizeUser>::KeySize;
fn key_size() -> usize {
BeltBlock::key_size()
}
}
#[derive(Debug)]
pub enum Error {
InvalidDataSize,
InvalidOutputSize {
expected: usize,
},
IntegrityCheckFailed,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::InvalidDataSize => f.write_str("invalid data size"),
Error::InvalidOutputSize { expected } => {
write!(f, "invalid output buffer size: expected {expected}")
}
Error::IntegrityCheckFailed => f.write_str("integrity check failed"),
}
}
}
impl core::error::Error for Error {}
#[derive(Clone, Copy, Debug)]
pub struct IntegrityCheckFailed;
impl fmt::Display for IntegrityCheckFailed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("integrity check failed")
}
}
impl core::error::Error for IntegrityCheckFailed {}