1#![no_std]
6#![doc(
7 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg",
8 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg",
9 html_root_url = "https://docs.rs/speck/0.0.1"
10)]
11#![forbid(unsafe_code)]
12#![warn(missing_docs, rust_2018_idioms)]
13
14pub use cipher;
15
16use cipher::{
17 consts::{U12, U16, U18, U24, U32, U4, U6, U8, U9},
18 AlgorithmName, BlockCipher, Key, KeyInit, KeySizeUser,
19};
20use core::fmt;
21use core::mem::size_of;
22
23macro_rules! define_speck_impl {
24 (
25 $name:ident,
26 $block_size:ty,
27 $key_size:ty,
28 $word_type:ty,
29 $n:literal,
30 $m:literal,
31 $alpha:literal,
32 $beta:literal,
33 $mask:literal,
34 $rounds:literal,
35 $doc:expr $(,)?
36 ) => {
37 #[doc=$doc]
38 #[doc = "block cipher"]
39 #[derive(Clone)]
40 pub struct $name {
41 k: [$word_type; $rounds],
42 }
43
44 impl $name {
45 #[inline]
46 fn from_be_bytes(bytes: &[u8]) -> $word_type {
47 let mut tmp = [0u8; size_of::<$word_type>()];
48 let offset = size_of::<$word_type>() - $n / 8;
49 tmp[offset..].copy_from_slice(bytes);
50 <$word_type>::from_be_bytes(tmp)
51 }
52
53 #[inline]
54 #[allow(clippy::wrong_self_convention)]
55 fn to_be_bytes(word: $word_type) -> [u8; $n / 8] {
56 let tmp = word.to_be_bytes();
57 let offset = size_of::<$word_type>() - $n / 8;
58 tmp[offset..].try_into().unwrap()
59 }
60
61 #[inline]
62 fn rotate_right(x: $word_type, pos: $word_type) -> $word_type {
63 (x >> pos) | (x << ($n - pos))
65 }
66
67 #[inline]
68 fn rotate_left(x: $word_type, pos: $word_type) -> $word_type {
69 (x << pos) | (x >> ($n - pos))
71 }
72
73 #[inline]
74 fn round_function(
75 k: $word_type,
76 mut x: $word_type,
77 mut y: $word_type,
78 ) -> ($word_type, $word_type) {
79 x = $name::rotate_right(x, $alpha);
80 x = <$word_type>::wrapping_add(x, y) & $mask;
81 x = (x ^ k) & $mask;
82 y = $name::rotate_left(y, $beta);
83 y = (y ^ x) & $mask;
84 (x, y)
85 }
86
87 #[inline]
88 fn inverse_round_function(
89 k: $word_type,
90 mut x: $word_type,
91 mut y: $word_type,
92 ) -> ($word_type, $word_type) {
93 y = (y ^ x) & $mask;
94 y = $name::rotate_right(y, $beta);
95 x = (x ^ k) & $mask;
96 x = <$word_type>::wrapping_sub(x, y) & $mask;
97 x = $name::rotate_left(x, $alpha);
98 (x, y)
99 }
100 }
101
102 impl BlockCipher for $name {}
103
104 impl KeySizeUser for $name {
105 type KeySize = $key_size;
106 }
107
108 impl KeyInit for $name {
109 fn new(key: &Key<Self>) -> Self {
110 let mut k = [0; $rounds];
111 let mut l = [0; $m - 1 + $rounds - 1];
112 k[0] = $name::from_be_bytes(&key[($m - 1) * ($n / 8)..($m) * ($n / 8)]);
113
114 for i in 0..$m - 1 {
115 l[i] = $name::from_be_bytes(
116 &key[($m - 2 - i) * ($n / 8)..($m - 1 - i) * ($n / 8)]
117 );
118 }
119
120 for i in 0..($rounds - 1) {
121 let res = $name::round_function(i.try_into().unwrap(), l[i], k[i]);
122 l[i + $m - 1] = res.0;
123 k[i + 1] = res.1;
124 }
125
126 Self { k }
127 }
128 }
129
130 impl fmt::Debug for $name {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 f.write_str(concat!(stringify!($name), " { .. }"))
133 }
134 }
135
136 impl AlgorithmName for $name {
137 fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 f.write_str(stringify!($name))
139 }
140 }
141
142 cipher::impl_simple_block_encdec!($name, $block_size, cipher, block,
143 encrypt: {
144 let b = block.get_in();
145 let mut x = $name::from_be_bytes(&b[0..($n / 8)]);
146 let mut y = $name::from_be_bytes(&b[($n / 8)..2 * ($n / 8)]);
147 for i in 0..$rounds {
148 let res = $name::round_function(cipher.k[i], x, y);
149 x = res.0;
150 y = res.1;
151 }
152
153 let b = block.get_out();
154 b[0..($n / 8)].copy_from_slice(&$name::to_be_bytes(x));
155 b[($n / 8)..2 * ($n / 8)].copy_from_slice(&$name::to_be_bytes(y));
156 }
157 decrypt: {
158 let b = block.get_in();
159 let mut x = $name::from_be_bytes(&b[0..($n / 8)]);
160 let mut y = $name::from_be_bytes(&b[($n / 8)..2 * ($n / 8)]);
161 for i in (0..$rounds).rev() {
162 let res = $name::inverse_round_function(cipher.k[i], x, y);
163 x = res.0;
164 y = res.1;
165 }
166
167 let b = block.get_out();
168 b[0..($n / 8)].copy_from_slice(&$name::to_be_bytes(x));
169 b[($n / 8)..2 * ($n / 8)].copy_from_slice(&$name::to_be_bytes(y));
170 }
171 );
172 };
173}
174
175define_speck_impl!(
176 Speck32_64,
177 U4,
178 U8,
179 u16,
180 16,
181 4,
182 7,
183 2,
184 0xFFFF,
185 22,
186 "Speck32/64"
187);
188define_speck_impl!(
189 Speck48_72,
190 U6,
191 U9,
192 u32,
193 24,
194 3,
195 8,
196 3,
197 0xFFFFFF,
198 22,
199 "Speck48/72"
200);
201define_speck_impl!(
202 Speck48_96,
203 U6,
204 U12,
205 u32,
206 24,
207 4,
208 8,
209 3,
210 0xFFFFFF,
211 23,
212 "Speck48/96"
213);
214define_speck_impl!(
215 Speck64_96,
216 U8,
217 U12,
218 u32,
219 32,
220 3,
221 8,
222 3,
223 0xFFFFFFFF,
224 26,
225 "Speck64/96"
226);
227define_speck_impl!(
228 Speck64_128,
229 U8,
230 U16,
231 u32,
232 32,
233 4,
234 8,
235 3,
236 0xFFFFFFFF,
237 27,
238 "Speck64/128"
239);
240define_speck_impl!(
241 Speck96_96,
242 U12,
243 U12,
244 u64,
245 48,
246 2,
247 8,
248 3,
249 0xFFFFFFFFFFFF,
250 28,
251 "Speck96/96"
252);
253define_speck_impl!(
254 Speck96_144,
255 U12,
256 U18,
257 u64,
258 48,
259 3,
260 8,
261 3,
262 0xFFFFFFFFFFFF,
263 29,
264 "Speck96/144"
265);
266define_speck_impl!(
267 Speck128_128,
268 U16,
269 U16,
270 u64,
271 64,
272 2,
273 8,
274 3,
275 0xFFFFFFFFFFFFFFFF,
276 32,
277 "Speck128/128"
278);
279define_speck_impl!(
280 Speck128_192,
281 U16,
282 U24,
283 u64,
284 64,
285 3,
286 8,
287 3,
288 0xFFFFFFFFFFFFFFFF,
289 33,
290 "Speck128/192"
291);
292define_speck_impl!(
293 Speck128_256,
294 U16,
295 U32,
296 u64,
297 64,
298 4,
299 8,
300 3,
301 0xFFFFFFFFFFFFFFFF,
302 34,
303 "Speck128/256"
304);