macro_rules! kalyna_ctr_variant {
($name:ident, $expanded:ident, $key_bytes:literal, $block_bytes:literal) => {
#[doc = concat!(
"CTR 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],
feed: [u8; $block_bytes],
used_gamma_len: usize,
}
impl $name {
#[must_use]
pub fn new(key: &[u8; $key_bytes], iv: &[u8; $block_bytes]) -> Self {
let key = super::kalyna::$expanded::new(key);
let gamma = key.encrypt_block(iv);
Self {
key,
gamma,
feed: gamma,
used_gamma_len: $block_bytes,
}
}
pub fn apply_in_place(&mut self, buf: &mut [u8]) {
let mut data_off = 0usize;
let mut offset = self.used_gamma_len;
if offset != 0 {
while offset < $block_bytes && data_off < buf.len() {
buf[data_off] ^= self.gamma[offset];
data_off += 1;
offset += 1;
}
if offset == $block_bytes {
Self::increment_counter(&mut self.feed);
self.gamma = self.key.encrypt_block(&self.feed);
offset = 0;
}
}
while data_off + $block_bytes <= buf.len() {
for (b, g) in buf[data_off..data_off + $block_bytes]
.iter_mut()
.zip(&self.gamma)
{
*b ^= *g;
}
Self::increment_counter(&mut self.feed);
self.gamma = self.key.encrypt_block(&self.feed);
data_off += $block_bytes;
}
while data_off < buf.len() {
buf[data_off] ^= self.gamma[offset];
offset += 1;
data_off += 1;
}
self.used_gamma_len = offset;
}
fn increment_counter(counter: &mut [u8; $block_bytes]) {
for byte in counter.iter_mut() {
*byte = byte.wrapping_add(1);
if *byte != 0 {
return;
}
}
}
}
};
}
kalyna_ctr_variant!(Kalyna128_128Ctr, Kalyna128_128ExpandedKey, 16, 16);
kalyna_ctr_variant!(Kalyna128_256Ctr, Kalyna128_256ExpandedKey, 32, 16);
kalyna_ctr_variant!(Kalyna256_256Ctr, Kalyna256_256ExpandedKey, 32, 32);
kalyna_ctr_variant!(Kalyna256_512Ctr, Kalyna256_512ExpandedKey, 64, 32);
kalyna_ctr_variant!(Kalyna512_512Ctr, Kalyna512_512ExpandedKey, 64, 64);