#![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;
use cipher::{
AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt,
BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockSizeUser, InOut,
InvalidLength, Key, KeyInit, KeySizeUser, ParBlocksSizeUser,
consts::{U1, U16, U32},
};
use core::fmt;
#[cfg(feature = "zeroize")]
use cipher::zeroize::{Zeroize, ZeroizeOnDrop};
use consts::{S1, S2, S3, S4, TM, TR};
#[derive(Clone)]
pub struct Cast6 {
masking: [[u32; 4]; 12],
rotate: [[u8; 4]; 12],
}
impl Cast6 {
fn key_schedule(&mut self, key: &[u8; 32]) {
let mut kappa = to_u32s(key);
for i in 0..12 {
let m_idx = 16 * i;
let r_idx = 16 * (i % 2);
let m = &TM[m_idx..][..8];
let r = &TR[r_idx..][..8];
forward_octave(&mut kappa, m, r);
let m = &TM[m_idx + 8..][..8];
let r = &TR[r_idx + 8..][..8];
forward_octave(&mut kappa, m, r);
let [a, b, c, d, e, f, g, h] = kappa;
self.masking[i] = [h, f, d, b];
self.rotate[i][0] = (a & 0x1f) as u8;
self.rotate[i][1] = (c & 0x1f) as u8;
self.rotate[i][2] = (e & 0x1f) as u8;
self.rotate[i][3] = (g & 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])
}};
}
#[inline]
fn forward_quad(beta: &mut [u32; 4], m: &[u32; 4], r: &[u8; 4]) {
let [a, b, c, d] = beta;
*c ^= f1!(*d, m[0], r[0]);
*b ^= f2!(*c, m[1], r[1]);
*a ^= f3!(*b, m[2], r[2]);
*d ^= f1!(*a, m[3], r[3]);
}
#[inline]
fn reverse_quad(beta: &mut [u32; 4], m: &[u32; 4], r: &[u8; 4]) {
let [a, b, c, d] = beta;
*d ^= f1!(*a, m[3], r[3]);
*a ^= f3!(*b, m[2], r[2]);
*b ^= f2!(*c, m[1], r[1]);
*c ^= f1!(*d, m[0], r[0]);
}
#[inline]
fn forward_octave(kappa: &mut [u32; 8], m: &[u32], r: &[u8]) {
let [a, b, c, d, e, f, g, h] = kappa;
*g ^= f1!(*h, m[0], r[0]);
*f ^= f2!(*g, m[1], r[1]);
*e ^= f3!(*f, m[2], r[2]);
*d ^= f1!(*e, m[3], r[3]);
*c ^= f2!(*d, m[4], r[4]);
*b ^= f3!(*c, m[5], r[5]);
*a ^= f1!(*b, m[6], r[6]);
*h ^= f2!(*a, m[7], r[7]);
}
impl KeySizeUser for Cast6 {
type KeySize = U32;
}
impl KeyInit for Cast6 {
fn new(key: &Key<Self>) -> Self {
Self::new_from_slice(key).unwrap()
}
fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
if ![16, 20, 24, 28, 32].contains(&key.len()) {
return Err(InvalidLength);
}
let mut cast6 = Self {
masking: [[0u32; 4]; 12],
rotate: [[0u8; 4]; 12],
};
let mut padded_key = [0u8; 32];
padded_key[..key.len()].copy_from_slice(key);
cast6.key_schedule(&padded_key);
Ok(cast6)
}
}
impl BlockSizeUser for Cast6 {
type BlockSize = U16;
}
impl ParBlocksSizeUser for Cast6 {
type ParBlocksSize = U1;
}
impl BlockCipherEncrypt for Cast6 {
#[inline]
fn encrypt_with_backend(&self, f: impl BlockCipherEncClosure<BlockSize = Self::BlockSize>) {
f.call(self)
}
}
impl BlockCipherEncBackend for Cast6 {
#[inline]
fn encrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
let masking = &self.masking;
let rotate = &self.rotate;
let mut beta = to_u32s(block.get_in());
forward_quad(&mut beta, &masking[0], &rotate[0]);
forward_quad(&mut beta, &masking[1], &rotate[1]);
forward_quad(&mut beta, &masking[2], &rotate[2]);
forward_quad(&mut beta, &masking[3], &rotate[3]);
forward_quad(&mut beta, &masking[4], &rotate[4]);
forward_quad(&mut beta, &masking[5], &rotate[5]);
reverse_quad(&mut beta, &masking[6], &rotate[6]);
reverse_quad(&mut beta, &masking[7], &rotate[7]);
reverse_quad(&mut beta, &masking[8], &rotate[8]);
reverse_quad(&mut beta, &masking[9], &rotate[9]);
reverse_quad(&mut beta, &masking[10], &rotate[10]);
reverse_quad(&mut beta, &masking[11], &rotate[11]);
*block.get_out() = to_u8s::<16>(&beta).into();
}
}
impl BlockCipherDecrypt for Cast6 {
#[inline]
fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure<BlockSize = Self::BlockSize>) {
f.call(self)
}
}
impl BlockCipherDecBackend for Cast6 {
#[inline]
fn decrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
let masking = &self.masking;
let rotate = &self.rotate;
let mut beta = to_u32s(block.get_in());
forward_quad(&mut beta, &masking[11], &rotate[11]);
forward_quad(&mut beta, &masking[10], &rotate[10]);
forward_quad(&mut beta, &masking[9], &rotate[9]);
forward_quad(&mut beta, &masking[8], &rotate[8]);
forward_quad(&mut beta, &masking[7], &rotate[7]);
forward_quad(&mut beta, &masking[6], &rotate[6]);
reverse_quad(&mut beta, &masking[5], &rotate[5]);
reverse_quad(&mut beta, &masking[4], &rotate[4]);
reverse_quad(&mut beta, &masking[3], &rotate[3]);
reverse_quad(&mut beta, &masking[2], &rotate[2]);
reverse_quad(&mut beta, &masking[1], &rotate[1]);
reverse_quad(&mut beta, &masking[0], &rotate[0]);
*block.get_out() = to_u8s::<16>(&beta).into();
}
}
impl fmt::Debug for Cast6 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Cast6 { ... }")
}
}
impl AlgorithmName for Cast6 {
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Cast6")
}
}
impl Drop for Cast6 {
fn drop(&mut self) {
#[cfg(feature = "zeroize")]
{
self.masking.zeroize();
self.rotate.zeroize();
}
}
}
#[cfg(feature = "zeroize")]
impl ZeroizeOnDrop for Cast6 {}
fn to_u32s<const N: usize>(src: &[u8]) -> [u32; N] {
assert_eq!(src.len(), 4 * N);
let mut res = [0u32; N];
for (chunk, dst) in src.chunks_exact(4).zip(res.iter_mut()) {
*dst = u32::from_be_bytes(chunk.try_into().unwrap());
}
res
}
fn to_u8s<const N: usize>(src: &[u32]) -> [u8; N] {
assert_eq!(4 * src.len(), N);
let mut res = [0u8; N];
for (dst_chunk, src) in res.chunks_exact_mut(4).zip(src.iter()) {
dst_chunk.copy_from_slice(&src.to_be_bytes());
}
res
}