speck_cipher/
lib.rs

1//! Pure Rust implementation of the [Speck] block cipher.
2//!
3//! [Speck]: https://en.wikipedia.org/wiki/Speck_(cipher)
4
5#![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                // We can't use $word_type.rotate_right here because the word size might be different from the word type.
64                (x >> pos) | (x << ($n - pos))
65            }
66
67            #[inline]
68            fn rotate_left(x: $word_type, pos: $word_type) -> $word_type {
69                // We can't use $word_type.rotate_left here because the word size might be different from the word type.
70                (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);