Skip to main content

cryptography/ciphers/
speck.rs

1//! Speck family of lightweight block ciphers.
2//!
3//! Implemented from "The SIMON and SPECK Families of Lightweight Block Ciphers"
4//! (Beaulieu et al., NSA, 2013), §4 and Appendix B.  All 10 variants.
5//!
6//! # Byte conventions
7//!
8//! **Block** — two words *(x ∥ y)* laid out in little-endian word order with x
9//! first.  x is the word that is right-rotated in each ARX round.
10//!
11//! **Key** — m words *(k₀ ∥ ℓ₀ ∥ … ∥ ℓ_{m−2})* in little-endian word order,
12//! k₀ first.  This matches the C reference-implementation convention.
13//!
14//! # Naming
15//!
16//! `Speck{B}_{K}` denotes a B-bit block with a K-bit key, e.g. `Speck64_128`.
17//!
18//! # Test vectors
19//!
20//! Known-answer tests use Appendix B of the 2013 paper.
21
22use super::simon_speck_util::{load_le, rotl, rotr, store_le};
23
24// ─────────────────────────────────────────────────────────────────────────────
25// Key expansion — §4.2, Figure 4.4
26//
27// Initial words (from key bytes, k₀ first):
28//   rk[0]  = k₀
29//   ℓ[j]   = k_{j+1}  for j = 0 … m−2
30//
31// For i = 0 … T−2:
32//   ℓ[i+m−1] = (rk[i] + S^{−α}(ℓ[i])) ⊕ i
33//   rk[i+1]  = S^β(rk[i]) ⊕ ℓ[i+m−1]
34//
35// The ℓ-array is maintained as a plain slice; max index = T−2+m−1 ≤ 35.
36// A 40-entry stack buffer covers every variant.
37// ─────────────────────────────────────────────────────────────────────────────
38
39#[derive(Clone, Copy)]
40struct SpeckParams {
41    alpha: u32,
42    beta: u32,
43    word_bits: u32,
44    key_words: usize,
45    rounds: usize,
46    mask: u64,
47}
48
49fn speck_expand(key: &[u8], params: SpeckParams, rk: &mut [u64]) {
50    let wb = (params.word_bits / 8) as usize;
51    let mut l = [0u64; 40];
52    rk[0] = load_le(&key[0..wb]);
53    for j in 0..params.key_words - 1 {
54        l[j] = load_le(&key[(j + 1) * wb..(j + 2) * wb]);
55    }
56    for i in 0..params.rounds - 1 {
57        l[i + params.key_words - 1] =
58            (rk[i].wrapping_add(rotr(l[i], params.alpha, params.word_bits, params.mask))
59                ^ u64::try_from(i).expect("round index fits in u64"))
60                & params.mask;
61        rk[i + 1] = (rotl(rk[i], params.beta, params.word_bits, params.mask)
62            ^ l[i + params.key_words - 1])
63            & params.mask;
64    }
65}
66
67// ─────────────────────────────────────────────────────────────────────────────
68// Block cipher core — §4.1, Figure 4.1
69//
70// Round function (encrypt):
71//   x ← (S^{−α}(x) + y) ⊕ k     right-rotate x by α, add y (mod 2ⁿ), XOR k
72//   y ← S^β(y) ⊕ x               left-rotate old y by β, XOR new x
73//
74// Inverse round (decrypt, applied in reverse round order):
75//   y ← S^{−β}(y ⊕ x)            right-rotate (y XOR x) by β → recovers old y
76//   x ← S^α((x ⊕ k) − y)         left-rotate  (x XOR k − y) by α → recovers old x
77//                                 (subtraction is mod 2ⁿ)
78// ─────────────────────────────────────────────────────────────────────────────
79
80fn speck_enc(block: &mut [u8], rk: &[u64], alpha: u32, beta: u32, n: u32, mask: u64) {
81    let wb = (n / 8) as usize;
82    let mut x = load_le(&block[0..wb]);
83    let mut y = load_le(&block[wb..2 * wb]);
84    for &k in rk {
85        x = (rotr(x, alpha, n, mask).wrapping_add(y) ^ k) & mask;
86        y = (rotl(y, beta, n, mask) ^ x) & mask;
87    }
88    store_le(x, &mut block[0..wb]);
89    store_le(y, &mut block[wb..2 * wb]);
90}
91
92fn speck_dec(block: &mut [u8], rk: &[u64], alpha: u32, beta: u32, n: u32, mask: u64) {
93    let wb = (n / 8) as usize;
94    let mut x = load_le(&block[0..wb]);
95    let mut y = load_le(&block[wb..2 * wb]);
96    for &k in rk.iter().rev() {
97        y = rotr(y ^ x, beta, n, mask);
98        x = rotl((x ^ k).wrapping_sub(y) & mask, alpha, n, mask);
99    }
100    store_le(x, &mut block[0..wb]);
101    store_le(y, &mut block[wb..2 * wb]);
102}
103
104// ─────────────────────────────────────────────────────────────────────────────
105// Public API — one struct per variant (Table 4.1)
106//
107// Macro arguments:
108//   $Name     — struct identifier
109//   $n        — word size in bits
110//   $m        — number of key words (so m−1 initial ℓ values)
111//   $T        — number of rounds (literal; also used as fixed-array size)
112//   $alpha    — rotation constant for x  (right-rotate in encrypt)
113//   $beta     — rotation constant for y  (left-rotate  in encrypt)
114//   $mask     — (1 << $n) − 1  (u64::MAX when n = 64)
115//   $key_len  — key length in bytes  = ($n / 8) × $m
116//   $blk_len  — block length in bytes = ($n / 8) × 2
117// ─────────────────────────────────────────────────────────────────────────────
118
119macro_rules! speck_variant {
120    ($Name:ident, $n:expr, $m:expr, $T:literal, $alpha:expr, $beta:expr, $mask:expr,
121     $key_len:literal, $blk_len:literal) => {
122        pub struct $Name {
123            round_keys: [u64; $T],
124        }
125        impl $Name {
126            /// Expand the paper-defined master key into this variant's round keys.
127            pub fn new(key: &[u8; $key_len]) -> Self {
128                let mut rk = [0u64; $T];
129                speck_expand(
130                    key,
131                    SpeckParams {
132                        alpha: $alpha,
133                        beta: $beta,
134                        word_bits: $n,
135                        key_words: $m,
136                        rounds: $T,
137                        mask: $mask,
138                    },
139                    &mut rk,
140                );
141                Self { round_keys: rk }
142            }
143            /// Expand the key and then wipe the caller-owned key buffer.
144            pub fn new_wiping(key: &mut [u8; $key_len]) -> Self {
145                // Mirrors `new`, but clears the caller-owned key bytes after
146                // expansion so only the internal round keys remain live.
147                let out = Self::new(key);
148                crate::ct::zeroize_slice(key.as_mut_slice());
149                out
150            }
151            /// Encrypt one block using the cached ARX round keys.
152            pub fn encrypt_block(&self, block: &[u8; $blk_len]) -> [u8; $blk_len] {
153                let mut out = *block;
154                speck_enc(&mut out, &self.round_keys, $alpha, $beta, $n, $mask);
155                out
156            }
157            /// Decrypt one block using the cached ARX round keys.
158            pub fn decrypt_block(&self, block: &[u8; $blk_len]) -> [u8; $blk_len] {
159                let mut out = *block;
160                speck_dec(&mut out, &self.round_keys, $alpha, $beta, $n, $mask);
161                out
162            }
163        }
164        impl crate::BlockCipher for $Name {
165            const BLOCK_LEN: usize = $blk_len;
166            fn encrypt(&self, block: &mut [u8]) {
167                let arr: &[u8; $blk_len] = (&*block).try_into().expect("wrong block length");
168                block.copy_from_slice(&self.encrypt_block(arr));
169            }
170            fn decrypt(&self, block: &mut [u8]) {
171                let arr: &[u8; $blk_len] = (&*block).try_into().expect("wrong block length");
172                block.copy_from_slice(&self.decrypt_block(arr));
173            }
174        }
175        impl Drop for $Name {
176            fn drop(&mut self) {
177                // SPECK's round function is already ARX-only; wipe the cached
178                // round keys when the instance is released.
179                crate::ct::zeroize_slice(self.round_keys.as_mut_slice());
180            }
181        }
182    };
183}
184
185//                            n    m   T    α  β  mask                      key  blk
186speck_variant!(Speck32_64, 16, 4, 22, 7, 2, 0xffff_u64, 8, 4);
187speck_variant!(Speck48_72, 24, 3, 22, 8, 3, 0xff_ffff_u64, 9, 6);
188speck_variant!(Speck48_96, 24, 4, 23, 8, 3, 0xff_ffff_u64, 12, 6);
189speck_variant!(Speck64_96, 32, 3, 26, 8, 3, 0xffff_ffff_u64, 12, 8);
190speck_variant!(Speck64_128, 32, 4, 27, 8, 3, 0xffff_ffff_u64, 16, 8);
191speck_variant!(Speck96_96, 48, 2, 28, 8, 3, 0xffff_ffff_ffff_u64, 12, 12);
192speck_variant!(Speck96_144, 48, 3, 29, 8, 3, 0xffff_ffff_ffff_u64, 18, 12);
193speck_variant!(Speck128_128, 64, 2, 32, 8, 3, u64::MAX, 16, 16);
194speck_variant!(Speck128_192, 64, 3, 33, 8, 3, u64::MAX, 24, 16);
195speck_variant!(Speck128_256, 64, 4, 34, 8, 3, u64::MAX, 32, 16);
196
197// ─────────────────────────────────────────────────────────────────────────────
198// Tests — known-answer vector from Appendix B of the 2013 paper;
199//         all other variants verified by encrypt→decrypt roundtrip.
200//
201// Block bytes: (x ∥ y) little-endian, x first.
202// Key bytes:   (k₀ ∥ ℓ₀ ∥ … ∥ ℓ_{m-2}) little-endian, k₀ first.
203//
204// Speck 32/64 derivation (Appendix B):
205//   Paper words: k₃k₂k₁k₀ = 0x1918 0x1110 0x0908 0x0100
206//   k₀ = 0x0100 → LE bytes 00 01; …; k₃ = 0x1918 → LE bytes 18 19
207//   Key bytes: 00 01 08 09 10 11 18 19
208//   PT: x = 0x6574 → 74 65;  y = 0x694c → 4c 69  (x first, LE)
209//   CT: x = 0xa868 → 68 a8;  y = 0x42f2 → f2 42  (x first, LE)
210// ─────────────────────────────────────────────────────────────────────────────
211
212#[cfg(test)]
213mod tests {
214    use super::*;
215
216    fn parse<const N: usize>(s: &str) -> [u8; N] {
217        let v: Vec<u8> = (0..s.len())
218            .step_by(2)
219            .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
220            .collect();
221        v.try_into().unwrap()
222    }
223
224    // ── Speck 32/64 — Appendix B ─────────────────────────────────────────────
225    // Key words (k₃,k₂,k₁,k₀) = (0x1918, 0x1110, 0x0908, 0x0100).
226    // PT (x,y) = (0x6574, 0x694c).  CT (x,y) = (0xa868, 0x42f2).
227
228    #[test]
229    fn speck32_64_kat() {
230        let key: [u8; 8] = parse("0001080910111819");
231        let pt: [u8; 4] = parse("74654c69");
232        let ct: [u8; 4] = parse("68a8f242");
233        let c = Speck32_64::new(&key);
234        assert_eq!(c.encrypt_block(&pt), ct, "encrypt");
235        assert_eq!(c.decrypt_block(&ct), pt, "decrypt");
236    }
237
238    // ── Speck 48/72 — Appendix C ─────────────────────────────────────────────
239    // Key (k₂,k₁,k₀) = (0x121110, 0x0a0908, 0x020100).
240    // PT (x,y) = (0x20796c, 0x6c6172).  CT (x,y) = (0xc049a5, 0x385adc).
241
242    #[test]
243    fn speck48_72_kat() {
244        let key: [u8; 9] = parse("00010208090a101112");
245        let pt: [u8; 6] = parse("6c792072616c");
246        let ct: [u8; 6] = parse("a549c0dc5a38");
247        let c = Speck48_72::new(&key);
248        assert_eq!(c.encrypt_block(&pt), ct, "encrypt");
249        assert_eq!(c.decrypt_block(&ct), pt, "decrypt");
250    }
251
252    // ── Speck 48/96 — Appendix C ─────────────────────────────────────────────
253    // Key (k₃..k₀) = (0x1a1918, 0x121110, 0x0a0908, 0x020100).
254    // PT (x,y) = (0x6d2073, 0x696874).  CT (x,y) = (0x735e10, 0xb6445d).
255
256    #[test]
257    fn speck48_96_kat() {
258        let key: [u8; 12] = parse("00010208090a10111218191a");
259        let pt: [u8; 6] = parse("73206d746869");
260        let ct: [u8; 6] = parse("105e735d44b6");
261        let c = Speck48_96::new(&key);
262        assert_eq!(c.encrypt_block(&pt), ct, "encrypt");
263        assert_eq!(c.decrypt_block(&ct), pt, "decrypt");
264    }
265
266    // ── Speck 64/96 — Appendix C ─────────────────────────────────────────────
267    // Key (k₂,k₁,k₀) = (0x13121110, 0x0b0a0908, 0x03020100).
268    // PT (x,y) = (0x74614620, 0x736e6165).  CT (x,y) = (0x9f7952ec, 0x4175946c).
269
270    #[test]
271    fn speck64_96_kat() {
272        let key: [u8; 12] = parse("0001020308090a0b10111213");
273        let pt: [u8; 8] = parse("2046617465616e73");
274        let ct: [u8; 8] = parse("ec52799f6c947541");
275        let c = Speck64_96::new(&key);
276        assert_eq!(c.encrypt_block(&pt), ct, "encrypt");
277        assert_eq!(c.decrypt_block(&ct), pt, "decrypt");
278    }
279
280    // ── Speck 64/128 — Appendix C ────────────────────────────────────────────
281    // Key (k₃..k₀) = (0x1b1a1918, 0x13121110, 0x0b0a0908, 0x03020100).
282    // PT (x,y) = (0x3b726574, 0x7475432d).  CT (x,y) = (0x8c6fa548, 0x454e028b).
283
284    #[test]
285    fn speck64_128_kat() {
286        let key: [u8; 16] = parse("0001020308090a0b1011121318191a1b");
287        let pt: [u8; 8] = parse("7465723b2d437574");
288        let ct: [u8; 8] = parse("48a56f8c8b024e45");
289        let c = Speck64_128::new(&key);
290        assert_eq!(c.encrypt_block(&pt), ct, "encrypt");
291        assert_eq!(c.decrypt_block(&ct), pt, "decrypt");
292    }
293
294    // ── Speck 96/96 — Appendix C ─────────────────────────────────────────────
295    // Key (k₁,k₀) = (0x0d0c0b0a0908, 0x050403020100).
296    // PT (x,y) = (0x65776f68202c, 0x656761737520).
297    // CT (x,y) = (0x9e4d09ab7178, 0x62bdde8f79aa).
298
299    #[test]
300    fn speck96_96_kat() {
301        let key: [u8; 12] = parse("00010203040508090a0b0c0d");
302        let pt: [u8; 12] = parse("2c20686f7765207573616765");
303        let ct: [u8; 12] = parse("7871ab094d9eaa798fdebd62");
304        let c = Speck96_96::new(&key);
305        assert_eq!(c.encrypt_block(&pt), ct, "encrypt");
306        assert_eq!(c.decrypt_block(&ct), pt, "decrypt");
307    }
308
309    // ── Speck 96/144 — Appendix C ────────────────────────────────────────────
310    // Key (k₂..k₀) = (0x151413121110, 0x0d0c0b0a0908, 0x050403020100).
311    // PT (x,y) = (0x656d6974206e, 0x69202c726576).
312    // CT (x,y) = (0x2bf31072228a, 0x7ae440252ee6).
313
314    #[test]
315    fn speck96_144_kat() {
316        let key: [u8; 18] = parse("00010203040508090a0b0c0d101112131415");
317        let pt: [u8; 12] = parse("6e2074696d657665722c2069");
318        let ct: [u8; 12] = parse("8a227210f32be62e2540e47a");
319        let c = Speck96_144::new(&key);
320        assert_eq!(c.encrypt_block(&pt), ct, "encrypt");
321        assert_eq!(c.decrypt_block(&ct), pt, "decrypt");
322    }
323
324    // ── Speck 128/128 — Appendix C ───────────────────────────────────────────
325    // Key (k₁,k₀) = (0x0f0e0d0c0b0a0908, 0x0706050403020100).
326    // PT (x,y) = (0x6c61766975716520, 0x7469206564616d20).
327    // CT (x,y) = (0xa65d985179783265, 0x7860fedf5c570d18).
328
329    #[test]
330    fn speck128_128_kat() {
331        let key: [u8; 16] = parse("000102030405060708090a0b0c0d0e0f");
332        let pt: [u8; 16] = parse("206571756976616c206d616465206974");
333        let ct: [u8; 16] = parse("6532787951985da6180d575cdffe6078");
334        let c = Speck128_128::new(&key);
335        assert_eq!(c.encrypt_block(&pt), ct, "encrypt");
336        assert_eq!(c.decrypt_block(&ct), pt, "decrypt");
337    }
338
339    // ── Speck 128/192 — Appendix C ───────────────────────────────────────────
340    // Key (k₂..k₀) = (0x1716151413121110, 0x0f0e0d0c0b0a0908, 0x0706050403020100).
341    // PT (x,y) = (0x7261482066656968, 0x43206f7420746e65).
342    // CT (x,y) = (0x1be4cf3a13135566, 0xf9bc185de03c1886).
343
344    #[test]
345    fn speck128_192_kat() {
346        let key: [u8; 24] = parse("000102030405060708090a0b0c0d0e0f1011121314151617");
347        let pt: [u8; 16] = parse("6869656620486172656e7420746f2043");
348        let ct: [u8; 16] = parse("665513133acfe41b86183ce05d18bcf9");
349        let c = Speck128_192::new(&key);
350        assert_eq!(c.encrypt_block(&pt), ct, "encrypt");
351        assert_eq!(c.decrypt_block(&ct), pt, "decrypt");
352    }
353
354    // ── Speck 128/256 — Appendix C ───────────────────────────────────────────
355    // Key (k₃..k₀) = (0x1f1e1d1c1b1a1918, 0x1716151413121110,
356    //                  0x0f0e0d0c0b0a0908, 0x0706050403020100).
357    // PT (x,y) = (0x65736f6874206e49, 0x202e72656e6f6f70).
358    // CT (x,y) = (0x4109010405c0f53e, 0x4eeeb48d9c188f43).
359
360    #[test]
361    fn speck128_256_kat() {
362        let key: [u8; 32] =
363            parse("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
364        let pt: [u8; 16] = parse("496e2074686f7365706f6f6e65722e20");
365        let ct: [u8; 16] = parse("3ef5c00504010941438f189c8db4ee4e");
366        let c = Speck128_256::new(&key);
367        assert_eq!(c.encrypt_block(&pt), ct, "encrypt");
368        assert_eq!(c.decrypt_block(&ct), pt, "decrypt");
369    }
370}