1#![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 html_root_url = "https://docs.rs/idea/0.5.1"
18)]
19#![deny(unsafe_code)]
20#![cfg_attr(docsrs, feature(doc_cfg))]
21#![warn(missing_docs, rust_2018_idioms)]
22#![allow(clippy::many_single_char_names)]
23
24pub use cipher;
25
26use cipher::{
27 consts::{U16, U8},
28 inout::InOut,
29 AlgorithmName, Block, BlockCipher, Key, KeyInit, KeySizeUser,
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#[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 BlockCipher for Idea {}
188
189impl KeySizeUser for Idea {
190 type KeySize = U16;
191}
192
193impl KeyInit for Idea {
194 fn new(key: &Key<Self>) -> Self {
195 let mut cipher = Self {
196 enc_keys: [0u16; 52],
197 dec_keys: [0u16; 52],
198 };
199 cipher.expand_key(key);
200 cipher.invert_sub_keys();
201 cipher
202 }
203}
204
205impl fmt::Debug for Idea {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 f.write_str("Idea { ... }")
208 }
209}
210
211impl AlgorithmName for Idea {
212 fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 f.write_str("Idea")
214 }
215}
216
217#[cfg(feature = "zeroize")]
218#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
219impl Drop for Idea {
220 fn drop(&mut self) {
221 self.enc_keys.zeroize();
222 self.dec_keys.zeroize();
223 }
224}
225
226#[cfg(feature = "zeroize")]
227#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
228impl ZeroizeOnDrop for Idea {}
229
230cipher::impl_simple_block_encdec!(
231 Idea, U8, cipher, block,
232 encrypt: {
233 cipher.crypt(block, &cipher.enc_keys);
234 }
235 decrypt: {
236 cipher.crypt(block, &cipher.dec_keys);
237 }
238);