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}