Skip to main content

cast5/
lib.rs

1//! Pure Rust implementation of the [CAST5] block cipher ([RFC 2144]).
2//!
3//! # ⚠️ Security Warning: Hazmat!
4//!
5//! This crate implements only the low-level block cipher function, and is intended
6//! for use for implementing higher-level constructions *only*. It is NOT
7//! intended for direct use in applications.
8//!
9//! USE AT YOUR OWN RISK!
10//!
11//! # Examples
12//! ```
13//! use cast5::cipher::{Array, BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
14//! use cast5::Cast5;
15//!
16//! let key = Array::from([0u8; 16]);
17//! let mut block = Array::from([0u8; 8]);
18//! // Initialize cipher
19//! let cipher = Cast5::new(&key);
20//!
21//! let block_copy = block;
22//! // Encrypt block in-place
23//! cipher.encrypt_block(&mut block);
24//! // And decrypt it back
25//! cipher.decrypt_block(&mut block);
26//! assert_eq!(block, block_copy);
27//! ```
28//!
29//! [CAST5]: https://en.wikipedia.org/wiki/CAST-128
30//! [RFC 2144]: https://tools.ietf.org/html/rfc2144
31
32#![no_std]
33#![doc(
34    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg",
35    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg"
36)]
37#![deny(unsafe_code)]
38#![cfg_attr(docsrs, feature(doc_cfg))]
39#![warn(missing_docs, rust_2018_idioms)]
40
41pub use cipher;
42
43mod consts;
44mod schedule;
45
46use cipher::{
47    AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt,
48    BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockSizeUser, InOut,
49    InvalidLength, Key, KeyInit, KeySizeUser, ParBlocksSizeUser,
50    consts::{U1, U8, U16},
51};
52use core::fmt;
53
54#[cfg(feature = "zeroize")]
55use cipher::zeroize::{Zeroize, ZeroizeOnDrop};
56
57use consts::{S1, S2, S3, S4};
58use schedule::key_schedule;
59
60/// The CAST5 block cipher.
61#[derive(Clone)]
62pub struct Cast5 {
63    masking: [u32; 16],
64    rotate: [u8; 16],
65    /// If this is set to true, it means a small key is used and only 12 rounds instead of 16
66    /// rounds are used in the algorithm.
67    small_key: bool,
68}
69
70impl Cast5 {
71    fn init_state(key_len: usize) -> Cast5 {
72        let small_key = key_len <= 10;
73
74        Cast5 {
75            masking: [0u32; 16],
76            rotate: [0u8; 16],
77            small_key,
78        }
79    }
80
81    /// Implements the key schedule according to RFC 2144 2.4.
82    /// https://tools.ietf.org/html/rfc2144#section-2.4
83    fn key_schedule(&mut self, key: &[u8]) {
84        let mut x = [
85            u32::from_be_bytes(key[0..4].try_into().unwrap()),
86            u32::from_be_bytes(key[4..8].try_into().unwrap()),
87            u32::from_be_bytes(key[8..12].try_into().unwrap()),
88            u32::from_be_bytes(key[12..16].try_into().unwrap()),
89        ];
90
91        let mut z = [0u32; 4];
92        let mut k = [0u32; 16];
93
94        key_schedule(&mut x, &mut z, &mut k);
95        self.masking[..].clone_from_slice(&k[..]);
96
97        key_schedule(&mut x, &mut z, &mut k);
98
99        for (i, ki) in k.iter().enumerate() {
100            self.rotate[i] = (ki & 0x1f) as u8;
101        }
102    }
103}
104
105macro_rules! f1 {
106    ($D:expr, $m:expr, $r:expr) => {{
107        let i = ($m.wrapping_add($D)).rotate_left(u32::from($r));
108        (S1[(i >> 24) as usize] ^ S2[((i >> 16) & 0xff) as usize])
109            .wrapping_sub(S3[((i >> 8) & 0xff) as usize])
110            .wrapping_add(S4[(i & 0xff) as usize])
111    }};
112}
113
114macro_rules! f2 {
115    ($D:expr, $m:expr, $r:expr) => {{
116        let i = ($m ^ $D).rotate_left(u32::from($r));
117        S1[(i >> 24) as usize]
118            .wrapping_sub(S2[((i >> 16) & 0xff) as usize])
119            .wrapping_add(S3[((i >> 8) & 0xff) as usize])
120            ^ S4[(i & 0xff) as usize]
121    }};
122}
123
124macro_rules! f3 {
125    ($D:expr, $m:expr, $r:expr) => {{
126        let i = ($m.wrapping_sub($D)).rotate_left(u32::from($r));
127        (S1[(i >> 24) as usize].wrapping_add(S2[((i >> 16) & 0xff) as usize])
128            ^ S3[((i >> 8) & 0xff) as usize])
129            .wrapping_sub(S4[(i & 0xff) as usize])
130    }};
131}
132
133impl KeySizeUser for Cast5 {
134    type KeySize = U16;
135}
136
137impl KeyInit for Cast5 {
138    fn new(key: &Key<Self>) -> Self {
139        Self::new_from_slice(key).unwrap()
140    }
141
142    fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
143        // Available key sizes are 40...128 bits.
144        if key.len() < 5 || key.len() > 16 {
145            return Err(InvalidLength);
146        }
147        let mut cast5 = Cast5::init_state(key.len());
148
149        if key.len() < 16 {
150            // Pad keys that are less than 128 bits long.
151            let mut padded_key = [0u8; 16];
152            padded_key[..key.len()].copy_from_slice(key);
153            cast5.key_schedule(&padded_key[..]);
154        } else {
155            cast5.key_schedule(key);
156        }
157        Ok(cast5)
158    }
159}
160
161impl BlockSizeUser for Cast5 {
162    type BlockSize = U8;
163}
164
165impl ParBlocksSizeUser for Cast5 {
166    type ParBlocksSize = U1;
167}
168
169impl BlockCipherEncrypt for Cast5 {
170    #[inline]
171    fn encrypt_with_backend(&self, f: impl BlockCipherEncClosure<BlockSize = Self::BlockSize>) {
172        f.call(self)
173    }
174}
175
176impl BlockCipherEncBackend for Cast5 {
177    #[inline]
178    fn encrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
179        let masking = self.masking;
180        let rotate = self.rotate;
181
182        // (L0,R0) <-- (m1...m64). (Split the plaintext into left and
183        // right 32-bit halves L0 = m1...m32 and R0 = m33...m64.)
184        let b = block.get_in();
185        let l = u32::from_be_bytes(b[0..4].try_into().unwrap());
186        let r = u32::from_be_bytes(b[4..8].try_into().unwrap());
187        // (16 rounds) for i from 1 to 16, compute Li and Ri as follows:
188        //   Li = Ri-1;
189        //   Ri = Li-1 ^ f(Ri-1,Kmi,Kri), where f is defined in Section 2.2
190        // (f is of Type 1, Type 2, or Type 3, depending on i).
191        //
192        // Rounds 1, 4, 7, 10, 13, and 16 use f function Type 1.
193        // Rounds 2, 5, 8, 11, and 14 use f function Type 2.
194        // Rounds 3, 6, 9, 12, and 15 use f function Type 3.
195
196        let (l, r) = (r, l ^ f1!(r, masking[0], rotate[0]));
197        let (l, r) = (r, l ^ f2!(r, masking[1], rotate[1]));
198        let (l, r) = (r, l ^ f3!(r, masking[2], rotate[2]));
199        let (l, r) = (r, l ^ f1!(r, masking[3], rotate[3]));
200        let (l, r) = (r, l ^ f2!(r, masking[4], rotate[4]));
201        let (l, r) = (r, l ^ f3!(r, masking[5], rotate[5]));
202        let (l, r) = (r, l ^ f1!(r, masking[6], rotate[6]));
203        let (l, r) = (r, l ^ f2!(r, masking[7], rotate[7]));
204        let (l, r) = (r, l ^ f3!(r, masking[8], rotate[8]));
205        let (l, r) = (r, l ^ f1!(r, masking[9], rotate[9]));
206        let (l, r) = (r, l ^ f2!(r, masking[10], rotate[10]));
207        let (l, r) = (r, l ^ f3!(r, masking[11], rotate[11]));
208
209        let (l, r) = if self.small_key {
210            (l, r)
211        } else {
212            // Rounds 13..16 are only executed for keys > 80 bits.
213            let (l, r) = (r, l ^ f1!(r, masking[12], rotate[12]));
214            let (l, r) = (r, l ^ f2!(r, masking[13], rotate[13]));
215            let (l, r) = (r, l ^ f3!(r, masking[14], rotate[14]));
216            (r, l ^ f1!(r, masking[15], rotate[15]))
217        };
218
219        // c1...c64 <-- (R16,L16).  (Exchange final blocks L16, R16 and
220        // concatenate to form the ciphertext.)
221        let block = block.get_out();
222        block[0..4].copy_from_slice(&r.to_be_bytes());
223        block[4..8].copy_from_slice(&l.to_be_bytes());
224    }
225}
226
227impl BlockCipherDecrypt for Cast5 {
228    #[inline]
229    fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure<BlockSize = Self::BlockSize>) {
230        f.call(self)
231    }
232}
233
234impl BlockCipherDecBackend for Cast5 {
235    #[inline]
236    fn decrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
237        let masking = self.masking;
238        let rotate = self.rotate;
239
240        let b = block.get_in();
241        let l = u32::from_be_bytes(b[0..4].try_into().unwrap());
242        let r = u32::from_be_bytes(b[4..8].try_into().unwrap());
243
244        let (l, r) = if self.small_key {
245            (l, r)
246        } else {
247            let (l, r) = (r, l ^ f1!(r, masking[15], rotate[15]));
248            let (l, r) = (r, l ^ f3!(r, masking[14], rotate[14]));
249            let (l, r) = (r, l ^ f2!(r, masking[13], rotate[13]));
250            (r, l ^ f1!(r, masking[12], rotate[12]))
251        };
252
253        let (l, r) = (r, l ^ f3!(r, masking[11], rotate[11]));
254        let (l, r) = (r, l ^ f2!(r, masking[10], rotate[10]));
255        let (l, r) = (r, l ^ f1!(r, masking[9], rotate[9]));
256        let (l, r) = (r, l ^ f3!(r, masking[8], rotate[8]));
257        let (l, r) = (r, l ^ f2!(r, masking[7], rotate[7]));
258        let (l, r) = (r, l ^ f1!(r, masking[6], rotate[6]));
259        let (l, r) = (r, l ^ f3!(r, masking[5], rotate[5]));
260        let (l, r) = (r, l ^ f2!(r, masking[4], rotate[4]));
261        let (l, r) = (r, l ^ f1!(r, masking[3], rotate[3]));
262        let (l, r) = (r, l ^ f3!(r, masking[2], rotate[2]));
263        let (l, r) = (r, l ^ f2!(r, masking[1], rotate[1]));
264        let (l, r) = (r, l ^ f1!(r, masking[0], rotate[0]));
265
266        let block = block.get_out();
267        block[0..4].copy_from_slice(&r.to_be_bytes());
268        block[4..8].copy_from_slice(&l.to_be_bytes());
269    }
270}
271
272impl fmt::Debug for Cast5 {
273    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274        f.write_str("Cast5 { ... }")
275    }
276}
277
278impl AlgorithmName for Cast5 {
279    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
280        f.write_str("Cast5")
281    }
282}
283
284impl Drop for Cast5 {
285    fn drop(&mut self) {
286        #[cfg(feature = "zeroize")]
287        {
288            self.masking.zeroize();
289            self.rotate.zeroize();
290            self.small_key.zeroize();
291        }
292    }
293}
294
295#[cfg(feature = "zeroize")]
296impl ZeroizeOnDrop for Cast5 {}