#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvalidLength;
macro_rules! kalyna_cbc_variant {
($name:ident, $expanded:ident, $key_bytes:literal, $block_bytes:literal) => {
#[doc = concat!(
"CBC mode over [`super::kalyna::", stringify!($expanded), "`] - see the module doc ",
"comment for the citation and the misuse warning."
)]
pub struct $name {
key: super::kalyna::$expanded,
gamma: [u8; $block_bytes],
}
impl $name {
#[must_use]
pub fn new(key: &[u8; $key_bytes], iv: &[u8; $block_bytes]) -> Self {
Self {
key: super::kalyna::$expanded::new(key),
gamma: *iv,
}
}
pub fn encrypt_in_place(&mut self, buf: &mut [u8]) -> Result<(), InvalidLength> {
if !buf.len().is_multiple_of($block_bytes) {
return Err(InvalidLength);
}
for block in buf.chunks_exact_mut($block_bytes) {
for (g, b) in self.gamma.iter_mut().zip(block.iter()) {
*g ^= *b;
}
self.gamma = self.key.encrypt_block(&self.gamma);
block.copy_from_slice(&self.gamma);
}
Ok(())
}
pub fn decrypt_in_place(&mut self, buf: &mut [u8]) -> Result<(), InvalidLength> {
if !buf.len().is_multiple_of($block_bytes) {
return Err(InvalidLength);
}
for block in buf.chunks_exact_mut($block_bytes) {
let mut ciphertext_block = [0u8; $block_bytes];
ciphertext_block.copy_from_slice(block);
let decrypted = self.key.decrypt_block(&ciphertext_block);
for (b, (d, g)) in block
.iter_mut()
.zip(decrypted.iter().zip(self.gamma.iter()))
{
*b = *d ^ *g;
}
self.gamma = ciphertext_block;
}
Ok(())
}
}
};
}
kalyna_cbc_variant!(Kalyna128_128Cbc, Kalyna128_128ExpandedKey, 16, 16);
kalyna_cbc_variant!(Kalyna128_256Cbc, Kalyna128_256ExpandedKey, 32, 16);
kalyna_cbc_variant!(Kalyna256_256Cbc, Kalyna256_256ExpandedKey, 32, 32);
kalyna_cbc_variant!(Kalyna256_512Cbc, Kalyna256_512ExpandedKey, 64, 32);
kalyna_cbc_variant!(Kalyna512_512Cbc, Kalyna512_512ExpandedKey, 64, 64);