1const PI: [[u8; 16]; 8] = [
17 [12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1], [6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15], [11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0], [12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11], [7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12], [5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0], [8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7], [1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2], ];
26
27#[inline]
32fn t(v: u32) -> u32 {
33 let mut r = 0u32;
34 for (i, sbox) in PI.iter().enumerate() {
35 let nibble = ((v >> (4 * i)) & 0xf) as usize;
36 let sub = sbox[nibble];
37 r |= u32::from(sub) << (4 * i);
38 }
39 r
40}
41
42#[inline]
47fn nibble_monomials(nibble: u8) -> ([u8; 4], [u8; 6], [u8; 4]) {
48 let bits = [
49 nibble & 1,
50 (nibble >> 1) & 1,
51 (nibble >> 2) & 1,
52 (nibble >> 3) & 1,
53 ];
54 let pairs = [
55 bits[0] & bits[1],
56 bits[0] & bits[2],
57 bits[0] & bits[3],
58 bits[1] & bits[2],
59 bits[1] & bits[3],
60 bits[2] & bits[3],
61 ];
62 let triples = [
63 pairs[0] & bits[2],
64 pairs[0] & bits[3],
65 pairs[1] & bits[3],
66 pairs[3] & bits[3],
67 ];
68 (bits, pairs, triples)
69}
70
71#[inline]
75fn pi0_ct(nibble: u8) -> u8 {
76 let (bits, pairs, triples) = nibble_monomials(nibble);
77
78 let b0 = pairs[1] ^ pairs[3] ^ triples[0] ^ pairs[4] ^ triples[3];
79 let b1 = bits[1] ^ bits[2] ^ pairs[1] ^ pairs[3] ^ bits[3] ^ pairs[2] ^ triples[2] ^ triples[3];
80 let b2 = 1 ^ pairs[0] ^ bits[2] ^ pairs[1] ^ pairs[2] ^ triples[3];
81 let b3 = 1 ^ bits[0] ^ bits[1] ^ pairs[0] ^ pairs[3] ^ pairs[2] ^ pairs[4] ^ pairs[5];
82
83 b0 | (b1 << 1) | (b2 << 2) | (b3 << 3)
84}
85
86#[inline]
87fn pi1_ct(nibble: u8) -> u8 {
88 let (bits, pairs, triples) = nibble_monomials(nibble);
89
90 let b0 = pairs[0]
91 ^ bits[2]
92 ^ pairs[1]
93 ^ triples[0]
94 ^ bits[3]
95 ^ pairs[2]
96 ^ pairs[4]
97 ^ triples[1]
98 ^ pairs[5];
99 let b1 = 1 ^ bits[0] ^ pairs[0] ^ bits[2] ^ bits[3] ^ triples[1] ^ triples[3];
100 let b2 = 1
101 ^ bits[0]
102 ^ bits[1]
103 ^ pairs[0]
104 ^ bits[2]
105 ^ pairs[1]
106 ^ triples[0]
107 ^ bits[3]
108 ^ pairs[5]
109 ^ triples[2]
110 ^ triples[3];
111 let b3 = bits[0] ^ pairs[0] ^ bits[2] ^ pairs[1] ^ pairs[3];
112
113 b0 | (b1 << 1) | (b2 << 2) | (b3 << 3)
114}
115
116#[inline]
117fn pi2_ct(nibble: u8) -> u8 {
118 let (bits, pairs, triples) = nibble_monomials(nibble);
119
120 let b0 = 1
121 ^ pairs[0]
122 ^ bits[2]
123 ^ pairs[1]
124 ^ triples[0]
125 ^ bits[3]
126 ^ pairs[2]
127 ^ pairs[4]
128 ^ triples[1]
129 ^ pairs[5]
130 ^ triples[2]
131 ^ triples[3];
132 let b1 = 1 ^ bits[1] ^ pairs[3] ^ triples[0] ^ pairs[2] ^ pairs[4] ^ pairs[5] ^ triples[2];
133 let b2 = bits[1]
134 ^ pairs[0]
135 ^ pairs[1]
136 ^ pairs[3]
137 ^ triples[0]
138 ^ bits[3]
139 ^ pairs[2]
140 ^ pairs[4]
141 ^ triples[2]
142 ^ triples[3];
143 let b3 = 1 ^ bits[0] ^ bits[1] ^ bits[2] ^ triples[0] ^ triples[1] ^ pairs[5] ^ triples[2];
144
145 b0 | (b1 << 1) | (b2 << 2) | (b3 << 3)
146}
147
148#[inline]
149fn pi3_ct(nibble: u8) -> u8 {
150 let (bits, pairs, triples) = nibble_monomials(nibble);
151
152 let b0 = pairs[0]
153 ^ bits[2]
154 ^ pairs[1]
155 ^ triples[0]
156 ^ bits[3]
157 ^ pairs[2]
158 ^ pairs[4]
159 ^ triples[1]
160 ^ pairs[5]
161 ^ triples[2]
162 ^ triples[3];
163 let b1 = bits[1]
164 ^ pairs[0]
165 ^ triples[0]
166 ^ bits[3]
167 ^ pairs[2]
168 ^ pairs[4]
169 ^ triples[1]
170 ^ triples[2]
171 ^ triples[3];
172 let b2 = 1
173 ^ bits[0]
174 ^ bits[1]
175 ^ pairs[0]
176 ^ pairs[1]
177 ^ pairs[3]
178 ^ triples[0]
179 ^ triples[1]
180 ^ pairs[5]
181 ^ triples[2];
182 let b3 = 1 ^ bits[1] ^ pairs[1] ^ pairs[3] ^ bits[3] ^ triples[1] ^ triples[3];
183
184 b0 | (b1 << 1) | (b2 << 2) | (b3 << 3)
185}
186
187#[inline]
188fn pi4_ct(nibble: u8) -> u8 {
189 let (bits, pairs, triples) = nibble_monomials(nibble);
190
191 let b0 = 1
192 ^ pairs[0]
193 ^ bits[2]
194 ^ pairs[1]
195 ^ triples[0]
196 ^ bits[3]
197 ^ pairs[2]
198 ^ pairs[4]
199 ^ triples[1]
200 ^ triples[2];
201 let b1 = 1 ^ bits[1] ^ pairs[0] ^ bits[2] ^ bits[3] ^ triples[1] ^ triples[2] ^ triples[3];
202 let b2 = 1
203 ^ pairs[0]
204 ^ bits[2]
205 ^ pairs[3]
206 ^ triples[0]
207 ^ bits[3]
208 ^ pairs[5]
209 ^ triples[2]
210 ^ triples[3];
211 let b3 = bits[0] ^ bits[2] ^ pairs[3];
212
213 b0 | (b1 << 1) | (b2 << 2) | (b3 << 3)
214}
215
216#[inline]
217fn pi5_ct(nibble: u8) -> u8 {
218 let (bits, pairs, triples) = nibble_monomials(nibble);
219
220 let b0 = 1 ^ pairs[0] ^ pairs[1] ^ pairs[3] ^ pairs[4] ^ pairs[5];
221 let b1 = bits[1] ^ pairs[1] ^ pairs[3] ^ bits[3] ^ pairs[5] ^ triples[3];
222 let b2 = 1 ^ bits[2] ^ pairs[3] ^ triples[0] ^ bits[3] ^ pairs[2] ^ triples[1] ^ triples[3];
223 let b3 = bits[0] ^ bits[1] ^ bits[2] ^ pairs[3] ^ triples[0] ^ bits[3] ^ pairs[4] ^ triples[2];
224
225 b0 | (b1 << 1) | (b2 << 2) | (b3 << 3)
226}
227
228#[inline]
229fn pi6_ct(nibble: u8) -> u8 {
230 let (bits, pairs, triples) = nibble_monomials(nibble);
231
232 let b0 = pairs[0]
233 ^ pairs[1]
234 ^ pairs[3]
235 ^ triples[0]
236 ^ bits[3]
237 ^ pairs[2]
238 ^ triples[1]
239 ^ triples[2]
240 ^ triples[3];
241 let b1 = bits[0] ^ bits[1] ^ bits[2] ^ triples[0] ^ bits[3] ^ pairs[4] ^ triples[3];
242 let b2 = bits[0]
243 ^ bits[2]
244 ^ pairs[3]
245 ^ bits[3]
246 ^ pairs[2]
247 ^ pairs[4]
248 ^ pairs[5]
249 ^ triples[2]
250 ^ triples[3];
251 let b3 = 1 ^ bits[1] ^ bits[2] ^ pairs[1] ^ pairs[3] ^ pairs[2] ^ pairs[4] ^ pairs[5];
252
253 b0 | (b1 << 1) | (b2 << 2) | (b3 << 3)
254}
255
256#[inline]
257fn pi7_ct(nibble: u8) -> u8 {
258 let (bits, pairs, triples) = nibble_monomials(nibble);
259
260 let b0 = 1
261 ^ bits[1]
262 ^ pairs[0]
263 ^ bits[2]
264 ^ pairs[1]
265 ^ pairs[3]
266 ^ triples[0]
267 ^ bits[3]
268 ^ pairs[2]
269 ^ pairs[4]
270 ^ triples[2]
271 ^ triples[3];
272 let b1 = bits[0] ^ bits[1] ^ pairs[1] ^ pairs[3] ^ triples[0] ^ triples[1] ^ triples[3];
273 let b2 = bits[0] ^ bits[1] ^ pairs[0] ^ pairs[3] ^ bits[3] ^ pairs[2] ^ pairs[5] ^ triples[2];
274 let b3 = bits[1] ^ triples[0] ^ pairs[2] ^ pairs[5] ^ triples[2] ^ triples[3];
275
276 b0 | (b1 << 1) | (b2 << 2) | (b3 << 3)
277}
278
279#[cfg(test)]
280#[inline]
281fn pi_ct(box_idx: usize, nibble: u8) -> u8 {
282 match box_idx {
283 0 => pi0_ct(nibble),
284 1 => pi1_ct(nibble),
285 2 => pi2_ct(nibble),
286 3 => pi3_ct(nibble),
287 4 => pi4_ct(nibble),
288 5 => pi5_ct(nibble),
289 6 => pi6_ct(nibble),
290 7 => pi7_ct(nibble),
291 _ => unreachable!(),
292 }
293}
294
295#[inline]
300fn t_ct(v: u32) -> u32 {
301 let n0 = (v & 0x0000_000f) as u8;
302 let n1 = ((v >> 4) & 0x0000_000f) as u8;
303 let n2 = ((v >> 8) & 0x0000_000f) as u8;
304 let n3 = ((v >> 12) & 0x0000_000f) as u8;
305 let n4 = ((v >> 16) & 0x0000_000f) as u8;
306 let n5 = ((v >> 20) & 0x0000_000f) as u8;
307 let n6 = ((v >> 24) & 0x0000_000f) as u8;
308 let n7 = ((v >> 28) & 0x0000_000f) as u8;
309
310 u32::from(pi0_ct(n0))
311 | (u32::from(pi1_ct(n1)) << 4)
312 | (u32::from(pi2_ct(n2)) << 8)
313 | (u32::from(pi3_ct(n3)) << 12)
314 | (u32::from(pi4_ct(n4)) << 16)
315 | (u32::from(pi5_ct(n5)) << 20)
316 | (u32::from(pi6_ct(n6)) << 24)
317 | (u32::from(pi7_ct(n7)) << 28)
318}
319
320#[inline]
322fn g(k: u32, a: u32) -> u32 {
323 t(a.wrapping_add(k)).rotate_left(11)
324}
325
326#[inline]
327fn g_ct(k: u32, a: u32) -> u32 {
328 t_ct(a.wrapping_add(k)).rotate_left(11)
331}
332
333fn build_round_keys(key: &[u8; 32]) -> ([u32; 32], [u32; 32]) {
345 let mut k = [0u32; 8];
346 for i in 0..8 {
347 k[i] = u32::from_be_bytes(key[4 * i..4 * i + 4].try_into().unwrap());
348 }
349
350 let mut enc = [0u32; 32];
351 for i in 0..24 {
352 enc[i] = k[i % 8];
353 } for i in 0..8 {
355 enc[24 + i] = k[7 - i];
356 } let mut dec = enc;
359 dec.reverse();
360
361 (enc, dec)
362}
363
364fn magma_core(block: [u8; 8], rk: &[u32; 32]) -> [u8; 8] {
372 let mut a1 = u32::from_be_bytes(block[0..4].try_into().unwrap()); let mut a0 = u32::from_be_bytes(block[4..8].try_into().unwrap()); for &round_key in rk.iter().take(31) {
376 let tmp = g(round_key, a0) ^ a1;
377 a1 = a0;
378 a0 = tmp;
379 }
380
381 let c1 = g(rk[31], a0) ^ a1; let c0 = a0; let mut out = [0u8; 8];
386 out[0..4].copy_from_slice(&c1.to_be_bytes());
387 out[4..8].copy_from_slice(&c0.to_be_bytes());
388 out
389}
390
391fn magma_core_ct(block: [u8; 8], rk: &[u32; 32]) -> [u8; 8] {
392 let mut a1 = u32::from_be_bytes(block[0..4].try_into().unwrap());
393 let mut a0 = u32::from_be_bytes(block[4..8].try_into().unwrap());
394
395 for &round_key in rk.iter().take(31) {
396 let tmp = g_ct(round_key, a0) ^ a1;
397 a1 = a0;
398 a0 = tmp;
399 }
400
401 let c1 = g_ct(rk[31], a0) ^ a1;
402 let c0 = a0;
403
404 let mut out = [0u8; 8];
405 out[0..4].copy_from_slice(&c1.to_be_bytes());
406 out[4..8].copy_from_slice(&c0.to_be_bytes());
407 out
408}
409
410pub struct Magma {
416 enc_rk: [u32; 32],
417 dec_rk: [u32; 32],
418}
419
420impl Magma {
421 #[must_use]
423 pub fn new(key: &[u8; 32]) -> Self {
424 let (enc_rk, dec_rk) = build_round_keys(key);
425 Magma { enc_rk, dec_rk }
426 }
427
428 pub fn new_wiping(key: &mut [u8; 32]) -> Self {
430 let out = Self::new(key);
431 crate::ct::zeroize_slice(key.as_mut_slice());
432 out
433 }
434
435 #[must_use]
437 pub fn encrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
438 magma_core(*block, &self.enc_rk)
439 }
440
441 #[must_use]
443 pub fn decrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
444 magma_core(*block, &self.dec_rk)
445 }
446}
447
448pub struct MagmaCt {
454 enc_rk: [u32; 32],
455 dec_rk: [u32; 32],
456}
457
458impl MagmaCt {
459 #[must_use]
461 pub fn new(key: &[u8; 32]) -> Self {
462 let (enc_rk, dec_rk) = build_round_keys(key);
463 MagmaCt { enc_rk, dec_rk }
464 }
465
466 pub fn new_wiping(key: &mut [u8; 32]) -> Self {
468 let out = Self::new(key);
469 crate::ct::zeroize_slice(key.as_mut_slice());
470 out
471 }
472
473 #[must_use]
475 pub fn encrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
476 magma_core_ct(*block, &self.enc_rk)
477 }
478
479 #[must_use]
481 pub fn decrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
482 magma_core_ct(*block, &self.dec_rk)
483 }
484}
485
486impl crate::BlockCipher for Magma {
487 const BLOCK_LEN: usize = 8;
488 fn encrypt(&self, block: &mut [u8]) {
489 let arr: &[u8; 8] = (&*block).try_into().expect("wrong block length");
490 block.copy_from_slice(&self.encrypt_block(arr));
491 }
492 fn decrypt(&self, block: &mut [u8]) {
493 let arr: &[u8; 8] = (&*block).try_into().expect("wrong block length");
494 block.copy_from_slice(&self.decrypt_block(arr));
495 }
496}
497
498impl crate::BlockCipher for MagmaCt {
499 const BLOCK_LEN: usize = 8;
500 fn encrypt(&self, block: &mut [u8]) {
501 let arr: &[u8; 8] = (&*block).try_into().expect("wrong block length");
502 block.copy_from_slice(&self.encrypt_block(arr));
503 }
504 fn decrypt(&self, block: &mut [u8]) {
505 let arr: &[u8; 8] = (&*block).try_into().expect("wrong block length");
506 block.copy_from_slice(&self.decrypt_block(arr));
507 }
508}
509
510impl Drop for Magma {
511 fn drop(&mut self) {
512 crate::ct::zeroize_slice(self.enc_rk.as_mut_slice());
514 crate::ct::zeroize_slice(self.dec_rk.as_mut_slice());
515 }
516}
517
518impl Drop for MagmaCt {
519 fn drop(&mut self) {
520 crate::ct::zeroize_slice(self.enc_rk.as_mut_slice());
521 crate::ct::zeroize_slice(self.dec_rk.as_mut_slice());
522 }
523}
524
525#[cfg(test)]
528mod tests {
529 use super::*;
530
531 fn h8(s: &str) -> [u8; 8] {
532 let b: Vec<u8> = (0..s.len())
533 .step_by(2)
534 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
535 .collect();
536 b.try_into().unwrap()
537 }
538
539 fn h32(s: &str) -> [u8; 32] {
540 let b: Vec<u8> = (0..s.len())
541 .step_by(2)
542 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
543 .collect();
544 b.try_into().unwrap()
545 }
546
547 #[test]
550 fn encrypt_decrypt_rfc() {
551 let key = h32("ffeeddccbbaa99887766554433221100f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
552 let pt = h8("fedcba9876543210");
553 let ct = h8("4ee901e5c2d8ca3d");
554 let m = Magma::new(&key);
555 assert_eq!(m.encrypt_block(&pt), ct, "encrypt");
556 assert_eq!(m.decrypt_block(&ct), pt, "decrypt");
557 }
558
559 #[test]
562 fn roundtrip() {
563 let key = [0x42u8; 32];
564 let pt = [0xABu8; 8];
565 let m = Magma::new(&key);
566 assert_eq!(m.decrypt_block(&m.encrypt_block(&pt)), pt);
567 }
568
569 #[test]
570 fn ct_sboxes_match_tables() {
571 for (box_idx, table) in PI.iter().enumerate() {
572 for nibble in 0u8..16 {
573 assert_eq!(pi_ct(box_idx, nibble), table[nibble as usize]);
574 }
575 }
576 }
577
578 #[test]
579 fn encrypt_decrypt_rfc_ct() {
580 let key = h32("ffeeddccbbaa99887766554433221100f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
581 let pt = h8("fedcba9876543210");
582 let ct = h8("4ee901e5c2d8ca3d");
583 let fast = Magma::new(&key);
584 let slow = MagmaCt::new(&key);
585 assert_eq!(slow.encrypt_block(&pt), ct, "encrypt");
586 assert_eq!(slow.decrypt_block(&ct), pt, "decrypt");
587 assert_eq!(
588 slow.encrypt_block(&pt),
589 fast.encrypt_block(&pt),
590 "match fast"
591 );
592 }
593}