Skip to main content

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