use core::fmt;
use tc_block_cipher::{
BlockCipher, BlockCipherInit, BlockError, CipherDirection, InitError, KeyParams,
};
use tc_zeroize::Zeroize;
use crate::cipher::{Representation, WORKING_KEY_WORDS};
use crate::{BLOCK_BYTES, KEY_STEP_BYTES, MAX_KEY_BYTES, MIN_KEY_BYTES, SERPENT_ALGO_NAME, cipher};
pub struct SerpentEngine {
working_key: [u32; WORKING_KEY_WORDS],
for_encryption: bool,
initialised: bool,
}
impl SerpentEngine {
pub const fn new() -> Self {
Self {
working_key: [0; WORKING_KEY_WORDS],
for_encryption: false,
initialised: false,
}
}
}
impl Default for SerpentEngine {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for SerpentEngine {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(SERPENT_ALGO_NAME)
}
}
impl Drop for SerpentEngine {
fn drop(&mut self) {
self.working_key.zeroize();
}
}
impl BlockCipher for SerpentEngine {
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);
}
let (Some(input), Some(output)) = (
input.first_chunk::<BLOCK_BYTES>(),
output.first_chunk_mut::<BLOCK_BYTES>(),
) else {
return Err(BlockError::BufferTooShort);
};
if self.for_encryption {
cipher::encrypt_block(&self.working_key, Representation::Serpent, input, output);
} else {
cipher::decrypt_block(&self.working_key, Representation::Serpent, input, output);
}
Ok(BLOCK_BYTES)
}
}
impl<P: KeyParams + ?Sized> BlockCipherInit<P> for SerpentEngine {
type Error = InitError;
fn init(&mut self, direction: CipherDirection, params: &P) -> Result<(), InitError> {
let key = params.key();
if !(MIN_KEY_BYTES..=MAX_KEY_BYTES)
.step_by(KEY_STEP_BYTES)
.any(|length| length == key.len())
{
return Err(InitError::InvalidKeyLength(key.len()));
}
let for_encryption = direction == CipherDirection::Encrypt;
self.working_key.zeroize();
let mut expanded = cipher::expand_key(key, Representation::Serpent);
self.working_key.copy_from_slice(&expanded);
expanded.zeroize();
self.for_encryption = for_encryption;
self.initialised = true;
Ok(())
}
}