Skip to main content

idea/
lib.rs

1//! Pure Rust implementation of the [IDEA] block cipher.
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//! [IDEA]: https://en.wikipedia.org/wiki/International_Data_Encryption_Algorithm
12
13#![no_std]
14#![doc(
15    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg",
16    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg"
17)]
18#![deny(unsafe_code)]
19#![cfg_attr(docsrs, feature(doc_cfg))]
20#![warn(missing_docs, rust_2018_idioms)]
21#![allow(clippy::many_single_char_names)]
22
23pub use cipher;
24
25use cipher::{
26    AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt,
27    BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockSizeUser, InOut, Key,
28    KeyInit, KeySizeUser, ParBlocksSizeUser,
29    consts::{U1, U8, U16},
30};
31use core::fmt;
32
33#[cfg(feature = "zeroize")]
34use cipher::zeroize::{Zeroize, ZeroizeOnDrop};
35
36mod consts;
37#[cfg(test)]
38mod tests;
39
40use consts::{FUYI, LENGTH_SUB_KEYS, MAXIM, ONE, ROUNDS};
41
42/// The International Data Encryption Algorithm (IDEA) block cipher.
43#[derive(Clone)]
44pub struct Idea {
45    enc_keys: [u16; LENGTH_SUB_KEYS],
46    dec_keys: [u16; LENGTH_SUB_KEYS],
47}
48
49impl Idea {
50    fn expand_key(&mut self, key: &Key<Self>) {
51        let length_key = key.len();
52        for i in 0..(length_key / 2) {
53            self.enc_keys[i] = (u16::from(key[2 * i]) << 8) + u16::from(key[2 * i + 1]);
54        }
55
56        let mut a: u16;
57        let mut b: u16;
58        for i in (length_key / 2)..LENGTH_SUB_KEYS {
59            if (i + 1) % 8 == 0 {
60                a = self.enc_keys[i - 15];
61            } else {
62                a = self.enc_keys[i - 7];
63            }
64
65            if (i + 2) % 8 < 2 {
66                b = self.enc_keys[i - 14];
67            } else {
68                b = self.enc_keys[i - 6];
69            }
70
71            self.enc_keys[i] = (a << 9) + (b >> 7);
72        }
73    }
74
75    fn invert_sub_keys(&mut self) {
76        let mut k = ROUNDS * 6;
77        for i in 0..=ROUNDS {
78            let j = i * 6;
79            let l = k - j;
80
81            let (m, n) = if i > 0 && i < 8 { (2, 1) } else { (1, 2) };
82
83            self.dec_keys[j] = self.mul_inv(self.enc_keys[l]);
84            self.dec_keys[j + 1] = self.add_inv(self.enc_keys[l + m]);
85            self.dec_keys[j + 2] = self.add_inv(self.enc_keys[l + n]);
86            self.dec_keys[j + 3] = self.mul_inv(self.enc_keys[l + 3]);
87        }
88
89        k = (ROUNDS - 1) * 6;
90        for i in 0..ROUNDS {
91            let j = i * 6;
92            let l = k - j;
93            self.dec_keys[j + 4] = self.enc_keys[l + 4];
94            self.dec_keys[j + 5] = self.enc_keys[l + 5];
95        }
96    }
97
98    fn crypt(&self, mut block: InOut<'_, '_, Block<Self>>, sub_keys: &[u16; LENGTH_SUB_KEYS]) {
99        let b = block.get_in();
100        let mut x1 = u16::from_be_bytes(b[0..2].try_into().unwrap());
101        let mut x2 = u16::from_be_bytes(b[2..4].try_into().unwrap());
102        let mut x3 = u16::from_be_bytes(b[4..6].try_into().unwrap());
103        let mut x4 = u16::from_be_bytes(b[6..8].try_into().unwrap());
104
105        for i in 0..ROUNDS {
106            let j = i * 6;
107            let y1 = self.mul(x1, sub_keys[j]);
108            let y2 = self.add(x2, sub_keys[j + 1]);
109            let y3 = self.add(x3, sub_keys[j + 2]);
110            let y4 = self.mul(x4, sub_keys[j + 3]);
111
112            let t0 = self.mul(y1 ^ y3, sub_keys[j + 4]);
113            let _t = self.add(y2 ^ y4, t0);
114            let t1 = self.mul(_t, sub_keys[j + 5]);
115            let t2 = self.add(t0, t1);
116
117            x1 = y1 ^ t1;
118            x2 = y3 ^ t1;
119            x3 = y2 ^ t2;
120            x4 = y4 ^ t2;
121        }
122
123        let y1 = self.mul(x1, sub_keys[48]);
124        let y2 = self.add(x3, sub_keys[49]);
125        let y3 = self.add(x2, sub_keys[50]);
126        let y4 = self.mul(x4, sub_keys[51]);
127
128        let block = block.get_out();
129        block[0..2].copy_from_slice(&y1.to_be_bytes());
130        block[2..4].copy_from_slice(&y2.to_be_bytes());
131        block[4..6].copy_from_slice(&y3.to_be_bytes());
132        block[6..8].copy_from_slice(&y4.to_be_bytes());
133    }
134
135    fn mul(&self, a: u16, b: u16) -> u16 {
136        let x = u32::from(a);
137        let y = u32::from(b);
138        let mut r: i32;
139
140        if x == 0 {
141            r = (MAXIM - y) as i32;
142        } else if y == 0 {
143            r = (MAXIM - x) as i32;
144        } else {
145            let c: u32 = x * y;
146            r = ((c & ONE) as i32) - ((c >> 16) as i32);
147            if r < 0 {
148                r += MAXIM as i32;
149            }
150        }
151
152        (r & (ONE as i32)) as u16
153    }
154
155    fn add(&self, a: u16, b: u16) -> u16 {
156        ((u32::from(a) + u32::from(b)) & ONE) as u16
157    }
158
159    fn mul_inv(&self, a: u16) -> u16 {
160        if a <= 1 {
161            a
162        } else {
163            let mut x = u32::from(a);
164            let mut y = MAXIM;
165            let mut t0 = 1u32;
166            let mut t1 = 0u32;
167            loop {
168                t1 += y / x * t0;
169                y %= x;
170                if y == 1 {
171                    return (MAXIM - t1) as u16;
172                }
173                t0 += x / y * t1;
174                x %= y;
175                if x == 1 {
176                    return t0 as u16;
177                }
178            }
179        }
180    }
181
182    fn add_inv(&self, a: u16) -> u16 {
183        ((FUYI - (u32::from(a))) & ONE) as u16
184    }
185}
186
187impl KeySizeUser for Idea {
188    type KeySize = U16;
189}
190
191impl KeyInit for Idea {
192    fn new(key: &Key<Self>) -> Self {
193        let mut cipher = Self {
194            enc_keys: [0u16; 52],
195            dec_keys: [0u16; 52],
196        };
197        cipher.expand_key(key);
198        cipher.invert_sub_keys();
199        cipher
200    }
201}
202
203impl BlockSizeUser for Idea {
204    type BlockSize = U8;
205}
206
207impl ParBlocksSizeUser for Idea {
208    type ParBlocksSize = U1;
209}
210
211impl BlockCipherEncrypt for Idea {
212    #[inline]
213    fn encrypt_with_backend(&self, f: impl BlockCipherEncClosure<BlockSize = Self::BlockSize>) {
214        f.call(self)
215    }
216}
217
218impl BlockCipherEncBackend for Idea {
219    #[inline]
220    fn encrypt_block(&self, block: InOut<'_, '_, Block<Self>>) {
221        self.crypt(block, &self.enc_keys);
222    }
223}
224
225impl BlockCipherDecrypt for Idea {
226    #[inline]
227    fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure<BlockSize = Self::BlockSize>) {
228        f.call(self)
229    }
230}
231
232impl BlockCipherDecBackend for Idea {
233    #[inline]
234    fn decrypt_block(&self, block: InOut<'_, '_, Block<Self>>) {
235        self.crypt(block, &self.dec_keys);
236    }
237}
238
239impl fmt::Debug for Idea {
240    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241        f.write_str("Idea { ... }")
242    }
243}
244
245impl AlgorithmName for Idea {
246    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
247        f.write_str("Idea")
248    }
249}
250
251impl Drop for Idea {
252    fn drop(&mut self) {
253        #[cfg(feature = "zeroize")]
254        {
255            self.enc_keys.zeroize();
256            self.dec_keys.zeroize();
257        }
258    }
259}
260
261#[cfg(feature = "zeroize")]
262impl ZeroizeOnDrop for Idea {}