use core::fmt;
use tc_block_cipher::{BlockCipher, BlockCipherInit, BlockError, CipherDirection, InitError};
use tc_zeroize::Zeroize;
use crate::Rc2Params;
use crate::rc2::cipher::{self, SUBKEYS};
use crate::rc2::{ALGO_NAME, BLOCK_BYTES, MAX_EFFECTIVE_KEY_BITS, MAX_KEY_BYTES};
pub struct Rc2TableEngine {
working_key: [u16; SUBKEYS],
direction: CipherDirection,
initialised: bool,
}
impl Rc2TableEngine {
pub const fn new() -> Self {
Self {
working_key: [0; SUBKEYS],
direction: CipherDirection::Encrypt,
initialised: false,
}
}
}
impl Default for Rc2TableEngine {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for Rc2TableEngine {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(ALGO_NAME)
}
}
impl Drop for Rc2TableEngine {
fn drop(&mut self) {
self.working_key.zeroize();
}
}
impl BlockCipher for Rc2TableEngine {
type Error = BlockError;
fn block_size(&self) -> usize {
BLOCK_BYTES
}
fn process_block(&mut self, input: &[u8], output: &mut [u8]) -> Result<usize, BlockError> {
if !self.initialised {
return Err(BlockError::NotInitialised);
}
if input.len() < BLOCK_BYTES || output.len() < BLOCK_BYTES {
return Err(BlockError::BufferTooShort);
}
let input: &[u8; BLOCK_BYTES] = input[..BLOCK_BYTES].try_into().unwrap();
let output: &mut [u8; BLOCK_BYTES] = (&mut output[..BLOCK_BYTES]).try_into().unwrap();
match self.direction {
CipherDirection::Encrypt => cipher::encrypt(&self.working_key, input, output),
CipherDirection::Decrypt => cipher::decrypt(&self.working_key, input, output),
}
Ok(BLOCK_BYTES)
}
}
impl<P: Rc2Params + ?Sized> BlockCipherInit<P> for Rc2TableEngine {
type Error = InitError;
fn init(&mut self, direction: CipherDirection, params: &P) -> Result<(), InitError> {
let key = params.key();
if key.is_empty() || key.len() > MAX_KEY_BYTES {
return Err(InitError::InvalidKeyLength(key.len()));
}
let effective_key_bits = params.effective_key_bits();
if effective_key_bits == 0 || effective_key_bits > MAX_EFFECTIVE_KEY_BITS {
return Err(InitError::InvalidEffectiveKeyBits(effective_key_bits));
}
self.working_key.zeroize();
let mut expanded = cipher::expand_key(key, effective_key_bits);
self.working_key.copy_from_slice(&expanded);
expanded.zeroize();
self.direction = direction;
self.initialised = true;
Ok(())
}
}