#![no_std]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg"
)]
#![deny(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(missing_docs, rust_2018_idioms)]
pub use cipher;
mod consts;
mod schedule;
use cipher::{
AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt,
BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockSizeUser, InOut,
InvalidLength, Key, KeyInit, KeySizeUser, ParBlocksSizeUser,
consts::{U1, U8, U16},
};
use core::fmt;
#[cfg(feature = "zeroize")]
use cipher::zeroize::{Zeroize, ZeroizeOnDrop};
use consts::{S1, S2, S3, S4};
use schedule::key_schedule;
#[derive(Clone)]
pub struct Cast5 {
masking: [u32; 16],
rotate: [u8; 16],
small_key: bool,
}
impl Cast5 {
fn init_state(key_len: usize) -> Cast5 {
let small_key = key_len <= 10;
Cast5 {
masking: [0u32; 16],
rotate: [0u8; 16],
small_key,
}
}
fn key_schedule(&mut self, key: &[u8]) {
let mut x = [
u32::from_be_bytes(key[0..4].try_into().unwrap()),
u32::from_be_bytes(key[4..8].try_into().unwrap()),
u32::from_be_bytes(key[8..12].try_into().unwrap()),
u32::from_be_bytes(key[12..16].try_into().unwrap()),
];
let mut z = [0u32; 4];
let mut k = [0u32; 16];
key_schedule(&mut x, &mut z, &mut k);
self.masking[..].clone_from_slice(&k[..]);
key_schedule(&mut x, &mut z, &mut k);
for (i, ki) in k.iter().enumerate() {
self.rotate[i] = (ki & 0x1f) as u8;
}
}
}
macro_rules! f1 {
($D:expr, $m:expr, $r:expr) => {{
let i = ($m.wrapping_add($D)).rotate_left(u32::from($r));
(S1[(i >> 24) as usize] ^ S2[((i >> 16) & 0xff) as usize])
.wrapping_sub(S3[((i >> 8) & 0xff) as usize])
.wrapping_add(S4[(i & 0xff) as usize])
}};
}
macro_rules! f2 {
($D:expr, $m:expr, $r:expr) => {{
let i = ($m ^ $D).rotate_left(u32::from($r));
S1[(i >> 24) as usize]
.wrapping_sub(S2[((i >> 16) & 0xff) as usize])
.wrapping_add(S3[((i >> 8) & 0xff) as usize])
^ S4[(i & 0xff) as usize]
}};
}
macro_rules! f3 {
($D:expr, $m:expr, $r:expr) => {{
let i = ($m.wrapping_sub($D)).rotate_left(u32::from($r));
(S1[(i >> 24) as usize].wrapping_add(S2[((i >> 16) & 0xff) as usize])
^ S3[((i >> 8) & 0xff) as usize])
.wrapping_sub(S4[(i & 0xff) as usize])
}};
}
impl KeySizeUser for Cast5 {
type KeySize = U16;
}
impl KeyInit for Cast5 {
fn new(key: &Key<Self>) -> Self {
Self::new_from_slice(key).unwrap()
}
fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
if key.len() < 5 || key.len() > 16 {
return Err(InvalidLength);
}
let mut cast5 = Cast5::init_state(key.len());
if key.len() < 16 {
let mut padded_key = [0u8; 16];
padded_key[..key.len()].copy_from_slice(key);
cast5.key_schedule(&padded_key[..]);
} else {
cast5.key_schedule(key);
}
Ok(cast5)
}
}
impl BlockSizeUser for Cast5 {
type BlockSize = U8;
}
impl ParBlocksSizeUser for Cast5 {
type ParBlocksSize = U1;
}
impl BlockCipherEncrypt for Cast5 {
#[inline]
fn encrypt_with_backend(&self, f: impl BlockCipherEncClosure<BlockSize = Self::BlockSize>) {
f.call(self)
}
}
impl BlockCipherEncBackend for Cast5 {
#[inline]
fn encrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
let masking = self.masking;
let rotate = self.rotate;
let b = block.get_in();
let l = u32::from_be_bytes(b[0..4].try_into().unwrap());
let r = u32::from_be_bytes(b[4..8].try_into().unwrap());
let (l, r) = (r, l ^ f1!(r, masking[0], rotate[0]));
let (l, r) = (r, l ^ f2!(r, masking[1], rotate[1]));
let (l, r) = (r, l ^ f3!(r, masking[2], rotate[2]));
let (l, r) = (r, l ^ f1!(r, masking[3], rotate[3]));
let (l, r) = (r, l ^ f2!(r, masking[4], rotate[4]));
let (l, r) = (r, l ^ f3!(r, masking[5], rotate[5]));
let (l, r) = (r, l ^ f1!(r, masking[6], rotate[6]));
let (l, r) = (r, l ^ f2!(r, masking[7], rotate[7]));
let (l, r) = (r, l ^ f3!(r, masking[8], rotate[8]));
let (l, r) = (r, l ^ f1!(r, masking[9], rotate[9]));
let (l, r) = (r, l ^ f2!(r, masking[10], rotate[10]));
let (l, r) = (r, l ^ f3!(r, masking[11], rotate[11]));
let (l, r) = if self.small_key {
(l, r)
} else {
let (l, r) = (r, l ^ f1!(r, masking[12], rotate[12]));
let (l, r) = (r, l ^ f2!(r, masking[13], rotate[13]));
let (l, r) = (r, l ^ f3!(r, masking[14], rotate[14]));
(r, l ^ f1!(r, masking[15], rotate[15]))
};
let block = block.get_out();
block[0..4].copy_from_slice(&r.to_be_bytes());
block[4..8].copy_from_slice(&l.to_be_bytes());
}
}
impl BlockCipherDecrypt for Cast5 {
#[inline]
fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure<BlockSize = Self::BlockSize>) {
f.call(self)
}
}
impl BlockCipherDecBackend for Cast5 {
#[inline]
fn decrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
let masking = self.masking;
let rotate = self.rotate;
let b = block.get_in();
let l = u32::from_be_bytes(b[0..4].try_into().unwrap());
let r = u32::from_be_bytes(b[4..8].try_into().unwrap());
let (l, r) = if self.small_key {
(l, r)
} else {
let (l, r) = (r, l ^ f1!(r, masking[15], rotate[15]));
let (l, r) = (r, l ^ f3!(r, masking[14], rotate[14]));
let (l, r) = (r, l ^ f2!(r, masking[13], rotate[13]));
(r, l ^ f1!(r, masking[12], rotate[12]))
};
let (l, r) = (r, l ^ f3!(r, masking[11], rotate[11]));
let (l, r) = (r, l ^ f2!(r, masking[10], rotate[10]));
let (l, r) = (r, l ^ f1!(r, masking[9], rotate[9]));
let (l, r) = (r, l ^ f3!(r, masking[8], rotate[8]));
let (l, r) = (r, l ^ f2!(r, masking[7], rotate[7]));
let (l, r) = (r, l ^ f1!(r, masking[6], rotate[6]));
let (l, r) = (r, l ^ f3!(r, masking[5], rotate[5]));
let (l, r) = (r, l ^ f2!(r, masking[4], rotate[4]));
let (l, r) = (r, l ^ f1!(r, masking[3], rotate[3]));
let (l, r) = (r, l ^ f3!(r, masking[2], rotate[2]));
let (l, r) = (r, l ^ f2!(r, masking[1], rotate[1]));
let (l, r) = (r, l ^ f1!(r, masking[0], rotate[0]));
let block = block.get_out();
block[0..4].copy_from_slice(&r.to_be_bytes());
block[4..8].copy_from_slice(&l.to_be_bytes());
}
}
impl fmt::Debug for Cast5 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Cast5 { ... }")
}
}
impl AlgorithmName for Cast5 {
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Cast5")
}
}
impl Drop for Cast5 {
fn drop(&mut self) {
#[cfg(feature = "zeroize")]
{
self.masking.zeroize();
self.rotate.zeroize();
self.small_key.zeroize();
}
}
}
#[cfg(feature = "zeroize")]
impl ZeroizeOnDrop for Cast5 {}