1#[inline]
16const fn gf_mul2(a: u8) -> u8 {
17 (a << 1) ^ (0xC3 & 0u8.wrapping_sub(a >> 7))
18}
19
20const fn gf_mul_const(mut a: u8, mut b: u8) -> u8 {
21 let mut r = 0u8;
22 while b != 0 {
23 if b & 1 != 0 {
24 r ^= a;
25 }
26 a = gf_mul2(a);
27 b >>= 1;
28 }
29 r
30}
31
32#[inline]
33fn gf_mul(mut a: u8, mut b: u8) -> u8 {
34 let mut r = 0u8;
35 for _ in 0..8 {
36 let mask = 0u8.wrapping_sub(b & 1);
37 r ^= a & mask;
38 a = gf_mul2(a);
39 b >>= 1;
40 }
41 r
42}
43
44const PI: [u8; 256] = [
50 252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, 218, 35, 197, 4, 77, 233, 119, 240, 219,
51 147, 46, 153, 186, 23, 54, 241, 187, 20, 205, 95, 193, 249, 24, 101, 90, 226, 92, 239, 33, 129,
52 28, 60, 66, 139, 1, 142, 79, 5, 132, 2, 174, 227, 106, 143, 160, 6, 11, 237, 152, 127, 212,
53 211, 31, 235, 52, 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 253, 58, 206, 204, 181, 112,
54 14, 86, 8, 12, 118, 18, 191, 114, 19, 71, 156, 183, 93, 135, 21, 161, 150, 41, 16, 123, 154,
55 199, 243, 145, 120, 111, 157, 158, 178, 177, 50, 117, 25, 61, 255, 53, 138, 126, 109, 84, 198,
56 128, 195, 189, 13, 87, 223, 245, 36, 169, 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185,
57 3, 224, 15, 236, 222, 122, 148, 176, 188, 220, 232, 40, 80, 78, 51, 10, 74, 167, 151, 96, 115,
58 30, 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65, 173, 69, 70, 146, 39, 94, 85, 47, 140, 163,
59 165, 125, 105, 213, 149, 59, 7, 88, 179, 64, 134, 172, 29, 247, 48, 55, 107, 228, 136, 217,
60 231, 137, 225, 27, 131, 73, 76, 63, 248, 254, 141, 83, 170, 144, 202, 216, 133, 97, 32, 113,
61 103, 164, 45, 43, 9, 91, 203, 155, 37, 208, 190, 229, 108, 82, 89, 166, 116, 210, 230, 244,
62 180, 192, 209, 102, 175, 194, 57, 75, 99, 182,
63];
64
65const PI_INV: [u8; 256] = [
66 165, 45, 50, 143, 14, 48, 56, 192, 84, 230, 158, 57, 85, 126, 82, 145, 100, 3, 87, 90, 28, 96,
67 7, 24, 33, 114, 168, 209, 41, 198, 164, 63, 224, 39, 141, 12, 130, 234, 174, 180, 154, 99, 73,
68 229, 66, 228, 21, 183, 200, 6, 112, 157, 65, 117, 25, 201, 170, 252, 77, 191, 42, 115, 132,
69 213, 195, 175, 43, 134, 167, 177, 178, 91, 70, 211, 159, 253, 212, 15, 156, 47, 155, 67, 239,
70 217, 121, 182, 83, 127, 193, 240, 35, 231, 37, 94, 181, 30, 162, 223, 166, 254, 172, 34, 249,
71 226, 74, 188, 53, 202, 238, 120, 5, 107, 81, 225, 89, 163, 242, 113, 86, 17, 106, 137, 148,
72 101, 140, 187, 119, 60, 123, 40, 171, 210, 49, 222, 196, 95, 204, 207, 118, 44, 184, 216, 46,
73 54, 219, 105, 179, 20, 149, 190, 98, 161, 59, 22, 102, 233, 92, 108, 109, 173, 55, 97, 75, 185,
74 227, 186, 241, 160, 133, 131, 218, 71, 197, 176, 51, 250, 150, 111, 110, 194, 246, 80, 255, 93,
75 169, 142, 23, 27, 151, 125, 236, 88, 247, 31, 251, 124, 9, 13, 122, 103, 69, 135, 220, 232, 79,
76 29, 78, 4, 235, 248, 243, 62, 61, 189, 138, 136, 221, 205, 11, 19, 152, 2, 147, 128, 144, 208,
77 36, 52, 203, 237, 244, 206, 153, 16, 68, 64, 146, 58, 1, 38, 18, 26, 72, 104, 245, 129, 139,
78 199, 214, 32, 10, 8, 0, 76, 215, 116,
79];
80
81const fn build_pi_anf(table: &[u8; 256]) -> [[u128; 2]; 8] {
86 crate::ct::build_byte_sbox_anf(table)
87}
88
89const PI_ANF: [[u128; 2]; 8] = build_pi_anf(&PI);
90const PI_INV_ANF: [[u128; 2]; 8] = build_pi_anf(&PI_INV);
91
92const L_COEFF: [u8; 16] = [
108 148, 32, 133, 16, 194, 192, 1, 251, 1, 192, 194, 16, 133, 32, 148, 1,
109];
110
111const fn build_l_tables() -> [[u8; 256]; 16] {
112 let mut t = [[0u8; 256]; 16];
113 let mut i = 0usize;
114 while i < 16 {
115 let mut v = 0u8;
116 loop {
117 t[i][v as usize] = gf_mul_const(L_COEFF[i], v);
118 if v == u8::MAX {
119 break;
120 }
121 v = v.wrapping_add(1);
122 }
123 i += 1;
124 }
125 t
126}
127
128static L_TABLES: [[u8; 256]; 16] = build_l_tables();
129
130#[inline]
133fn xor_block(a: &mut [u8; 16], b: &[u8; 16]) {
134 for (x, y) in a.iter_mut().zip(b.iter()) {
135 *x ^= y;
136 }
137}
138
139#[inline]
140fn apply_s(block: &mut [u8; 16]) {
141 for b in block.iter_mut() {
142 *b = PI[*b as usize];
143 }
144}
145
146#[inline]
147fn apply_s_inv(block: &mut [u8; 16]) {
148 for b in block.iter_mut() {
149 *b = PI_INV[*b as usize];
150 }
151}
152
153#[inline]
155fn l_func(block: &[u8; 16]) -> u8 {
156 let mut r = 0u8;
157 for i in 0..16 {
158 r ^= L_TABLES[i][block[i] as usize];
159 }
160 r
161}
162
163#[inline]
165fn r_step(block: &mut [u8; 16]) {
166 let lc = l_func(block);
167 block.copy_within(0..15, 1); block[0] = lc;
169}
170
171#[inline]
176fn r_inv_step(block: &mut [u8; 16]) {
177 let sum: u8 = (0..15).fold(0u8, |acc, i| acc ^ L_TABLES[i][block[i + 1] as usize]);
179 let new_last = block[0] ^ sum;
180 block.copy_within(1..16, 0); block[15] = new_last;
182}
183
184fn apply_l(block: &mut [u8; 16]) {
186 for _ in 0..16 {
187 r_step(block);
188 }
189}
190
191fn apply_l_inv(block: &mut [u8; 16]) {
193 for _ in 0..16 {
194 r_inv_step(block);
195 }
196}
197
198#[inline]
199fn pi_eval(coeffs: &[[u128; 2]; 8], input: u8) -> u8 {
200 crate::ct::eval_byte_sbox(coeffs, input)
201}
202
203#[inline]
204fn apply_s_ct(block: &mut [u8; 16]) {
205 for b in block.iter_mut() {
208 *b = pi_eval(&PI_ANF, *b);
209 }
210}
211
212#[inline]
213fn apply_s_inv_ct(block: &mut [u8; 16]) {
214 for b in block.iter_mut() {
216 *b = pi_eval(&PI_INV_ANF, *b);
217 }
218}
219
220#[inline]
221fn l_func_ct(block: &[u8; 16]) -> u8 {
222 let mut r = 0u8;
225 for i in 0..16 {
226 r ^= gf_mul(L_COEFF[i], block[i]);
227 }
228 r
229}
230
231#[inline]
232fn r_step_ct(block: &mut [u8; 16]) {
233 let lc = l_func_ct(block);
234 block.copy_within(0..15, 1);
235 block[0] = lc;
236}
237
238#[inline]
239fn r_inv_step_ct(block: &mut [u8; 16]) {
240 let sum: u8 = (0..15).fold(0u8, |acc, i| acc ^ gf_mul(L_COEFF[i], block[i + 1]));
241 let new_last = block[0] ^ sum;
242 block.copy_within(1..16, 0);
243 block[15] = new_last;
244}
245
246fn apply_l_ct(block: &mut [u8; 16]) {
247 for _ in 0..16 {
248 r_step_ct(block);
249 }
250}
251
252fn apply_l_inv_ct(block: &mut [u8; 16]) {
253 for _ in 0..16 {
254 r_inv_step_ct(block);
255 }
256}
257
258fn round_const(i: u8) -> [u8; 16] {
271 let mut v = [0u8; 16];
272 v[15] = i;
273 apply_l(&mut v);
274 v
275}
276
277fn round_const_ct(i: u8) -> [u8; 16] {
278 let mut v = [0u8; 16];
279 v[15] = i;
280 apply_l_ct(&mut v);
281 v
282}
283
284fn f_step(a1: &mut [u8; 16], a0: &mut [u8; 16], c: &[u8; 16]) {
285 let mut tmp = *a1;
286 xor_block(&mut tmp, c); apply_s(&mut tmp); apply_l(&mut tmp); xor_block(&mut tmp, a0); *a0 = *a1; *a1 = tmp; }
293
294fn f_step_ct(a1: &mut [u8; 16], a0: &mut [u8; 16], c: &[u8; 16]) {
295 let mut tmp = *a1;
296 xor_block(&mut tmp, c);
297 apply_s_ct(&mut tmp);
298 apply_l_ct(&mut tmp);
299 xor_block(&mut tmp, a0);
300 *a0 = *a1;
301 *a1 = tmp;
302}
303
304fn key_schedule(key: &[u8; 32]) -> [[u8; 16]; 10] {
305 let mut rk = [[0u8; 16]; 10];
306 rk[0].copy_from_slice(&key[0..16]); rk[1].copy_from_slice(&key[16..32]); let mut a1 = rk[0];
310 let mut a0 = rk[1];
311
312 for group in 0usize..4 {
313 for step in 0usize..8 {
314 let ci = u8::try_from(group * 8 + step + 1).expect("round constant index fits in u8"); let c = round_const(ci);
316 f_step(&mut a1, &mut a0, &c);
317 }
318 rk[2 + group * 2] = a1; rk[3 + group * 2] = a0; }
321
322 rk
323}
324
325fn key_schedule_ct(key: &[u8; 32]) -> [[u8; 16]; 10] {
326 let mut rk = [[0u8; 16]; 10];
327 rk[0].copy_from_slice(&key[0..16]);
328 rk[1].copy_from_slice(&key[16..32]);
329
330 let mut a1 = rk[0];
331 let mut a0 = rk[1];
332
333 for group in 0usize..4 {
334 for step in 0usize..8 {
335 let ci = u8::try_from(group * 8 + step + 1).expect("round constant index fits in u8");
336 let c = round_const_ct(ci);
337 f_step_ct(&mut a1, &mut a0, &c);
338 }
339 rk[2 + group * 2] = a1;
340 rk[3 + group * 2] = a0;
341 }
342
343 rk
344}
345
346pub struct Grasshopper {
352 rk: [[u8; 16]; 10],
353}
354
355impl Grasshopper {
356 #[must_use]
358 pub fn new(key: &[u8; 32]) -> Self {
359 Grasshopper {
360 rk: key_schedule(key),
361 }
362 }
363
364 pub fn new_wiping(key: &mut [u8; 32]) -> Self {
366 let out = Self::new(key);
367 crate::ct::zeroize_slice(key.as_mut_slice());
368 out
369 }
370
371 #[must_use]
373 pub fn encrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
374 let mut s = *block;
375 for i in 0..9 {
376 xor_block(&mut s, &self.rk[i]);
377 apply_s(&mut s);
378 apply_l(&mut s);
379 }
380 xor_block(&mut s, &self.rk[9]);
381 s
382 }
383
384 #[must_use]
386 pub fn decrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
387 let mut s = *block;
388 xor_block(&mut s, &self.rk[9]);
389 for i in (0..9).rev() {
390 apply_l_inv(&mut s);
391 apply_s_inv(&mut s);
392 xor_block(&mut s, &self.rk[i]);
393 }
394 s
395 }
396}
397
398pub struct GrasshopperCt {
404 rk: [[u8; 16]; 10],
405}
406
407impl GrasshopperCt {
408 #[must_use]
410 pub fn new(key: &[u8; 32]) -> Self {
411 GrasshopperCt {
412 rk: key_schedule_ct(key),
413 }
414 }
415
416 pub fn new_wiping(key: &mut [u8; 32]) -> Self {
418 let out = Self::new(key);
419 crate::ct::zeroize_slice(key.as_mut_slice());
420 out
421 }
422
423 #[must_use]
425 pub fn encrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
426 let mut s = *block;
427 for i in 0..9 {
428 xor_block(&mut s, &self.rk[i]);
429 apply_s_ct(&mut s);
430 apply_l_ct(&mut s);
431 }
432 xor_block(&mut s, &self.rk[9]);
433 s
434 }
435
436 #[must_use]
438 pub fn decrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
439 let mut s = *block;
440 xor_block(&mut s, &self.rk[9]);
441 for i in (0..9).rev() {
442 apply_l_inv_ct(&mut s);
443 apply_s_inv_ct(&mut s);
444 xor_block(&mut s, &self.rk[i]);
445 }
446 s
447 }
448}
449
450impl crate::BlockCipher for Grasshopper {
451 const BLOCK_LEN: usize = 16;
452 fn encrypt(&self, block: &mut [u8]) {
453 let arr: &[u8; 16] = (&*block).try_into().expect("wrong block length");
454 block.copy_from_slice(&self.encrypt_block(arr));
455 }
456 fn decrypt(&self, block: &mut [u8]) {
457 let arr: &[u8; 16] = (&*block).try_into().expect("wrong block length");
458 block.copy_from_slice(&self.decrypt_block(arr));
459 }
460}
461
462impl crate::BlockCipher for GrasshopperCt {
463 const BLOCK_LEN: usize = 16;
464 fn encrypt(&self, block: &mut [u8]) {
465 let arr: &[u8; 16] = (&*block).try_into().expect("wrong block length");
466 block.copy_from_slice(&self.encrypt_block(arr));
467 }
468 fn decrypt(&self, block: &mut [u8]) {
469 let arr: &[u8; 16] = (&*block).try_into().expect("wrong block length");
470 block.copy_from_slice(&self.decrypt_block(arr));
471 }
472}
473
474impl Drop for Grasshopper {
475 fn drop(&mut self) {
476 for rk in &mut self.rk {
478 crate::ct::zeroize_slice(rk.as_mut_slice());
479 }
480 }
481}
482
483impl Drop for GrasshopperCt {
484 fn drop(&mut self) {
485 for rk in &mut self.rk {
486 crate::ct::zeroize_slice(rk.as_mut_slice());
487 }
488 }
489}
490
491#[cfg(test)]
494mod tests {
495 use super::*;
496
497 fn h16(s: &str) -> [u8; 16] {
498 let b: Vec<u8> = (0..s.len())
499 .step_by(2)
500 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
501 .collect();
502 b.try_into().unwrap()
503 }
504
505 fn h32(s: &str) -> [u8; 32] {
506 let b: Vec<u8> = (0..s.len())
507 .step_by(2)
508 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
509 .collect();
510 b.try_into().unwrap()
511 }
512
513 #[test]
514 fn ct_sboxes_match_tables() {
515 for x in 0u16..=255 {
516 let b = u8::try_from(x).expect("table index fits in u8");
517 assert_eq!(pi_eval(&PI_ANF, b), PI[x as usize], "pi {x:02x}");
518 assert_eq!(
519 pi_eval(&PI_INV_ANF, b),
520 PI_INV[x as usize],
521 "pi_inv {x:02x}"
522 );
523 }
524 }
525
526 #[test]
529 fn s_vectors() {
530 let cases = [
531 (
532 "ffeeddccbbaa99881122334455667700",
533 "b66cd8887d38e8d77765aeea0c9a7efc",
534 ),
535 (
536 "b66cd8887d38e8d77765aeea0c9a7efc",
537 "559d8dd7bd06cbfe7e7b262523280d39",
538 ),
539 (
540 "559d8dd7bd06cbfe7e7b262523280d39",
541 "0c3322fed531e4630d80ef5c5a81c50b",
542 ),
543 (
544 "0c3322fed531e4630d80ef5c5a81c50b",
545 "23ae65633f842d29c5df529c13f5acda",
546 ),
547 ];
548 for (inp, exp) in cases {
549 let mut b = h16(inp);
550 apply_s(&mut b);
551 assert_eq!(b, h16(exp), "S({inp})");
552 }
553 }
554
555 #[test]
558 fn r_vectors() {
559 let cases = [
560 (
561 "00000000000000000000000000000100",
562 "94000000000000000000000000000001",
563 ),
564 (
565 "94000000000000000000000000000001",
566 "a5940000000000000000000000000000",
567 ),
568 (
569 "a5940000000000000000000000000000",
570 "64a59400000000000000000000000000",
571 ),
572 (
573 "64a59400000000000000000000000000",
574 "0d64a594000000000000000000000000",
575 ),
576 ];
577 for (inp, exp) in cases {
578 let mut b = h16(inp);
579 r_step(&mut b);
580 assert_eq!(b, h16(exp), "R({inp})");
581 }
582 }
583
584 #[test]
587 fn l_vectors() {
588 let cases = [
589 (
590 "64a59400000000000000000000000000",
591 "d456584dd0e3e84cc3166e4b7fa2890d",
592 ),
593 (
594 "d456584dd0e3e84cc3166e4b7fa2890d",
595 "79d26221b87b584cd42fbc4ffea5de9a",
596 ),
597 (
598 "79d26221b87b584cd42fbc4ffea5de9a",
599 "0e93691a0cfc60408b7b68f66b513c13",
600 ),
601 (
602 "0e93691a0cfc60408b7b68f66b513c13",
603 "e6a8094fee0aa204fd97bcb0b44b8580",
604 ),
605 ];
606 for (inp, exp) in cases {
607 let mut b = h16(inp);
608 apply_l(&mut b);
609 assert_eq!(b, h16(exp), "L({inp})");
610 }
611 }
612
613 #[test]
616 fn encrypt_decrypt_rfc() {
617 let key = h32("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef");
618 let pt = h16("1122334455667700ffeeddccbbaa9988");
619 let ct = h16("7f679d90bebc24305a468d42b9d4edcd");
620 let c = Grasshopper::new(&key);
621 assert_eq!(c.encrypt_block(&pt), ct, "encrypt");
622 assert_eq!(c.decrypt_block(&ct), pt, "decrypt");
623 }
624
625 #[test]
626 fn encrypt_decrypt_rfc_ct() {
627 let key = h32("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef");
628 let pt = h16("1122334455667700ffeeddccbbaa9988");
629 let ct = h16("7f679d90bebc24305a468d42b9d4edcd");
630 let fast = Grasshopper::new(&key);
631 let slow = GrasshopperCt::new(&key);
632 assert_eq!(slow.encrypt_block(&pt), ct, "encrypt");
633 assert_eq!(slow.decrypt_block(&ct), pt, "decrypt");
634 assert_eq!(
635 slow.encrypt_block(&pt),
636 fast.encrypt_block(&pt),
637 "match fast"
638 );
639 }
640
641 #[test]
644 fn key_schedule_vectors() {
645 let key = h32("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef");
646 let rk = key_schedule(&key);
647 assert_eq!(rk[0], h16("8899aabbccddeeff0011223344556677"), "K_1");
648 assert_eq!(rk[1], h16("fedcba98765432100123456789abcdef"), "K_2");
649 assert_eq!(rk[2], h16("db31485315694343228d6aef8cc78c44"), "K_3");
650 assert_eq!(rk[3], h16("3d4553d8e9cfec6815ebadc40a9ffd04"), "K_4");
651 assert_eq!(rk[4], h16("57646468c44a5e28d3e59246f429f1ac"), "K_5");
652 assert_eq!(rk[9], h16("72e9dd7416bcf45b755dbaa88e4a4043"), "K_10");
653 }
654
655 #[test]
658 fn roundtrip() {
659 let key = [0xABu8; 32];
660 let pt = [0x42u8; 16];
661 let c = Grasshopper::new(&key);
662 assert_eq!(c.decrypt_block(&c.encrypt_block(&pt)), pt);
663 }
664
665 #[test]
668 fn round_const_vectors() {
669 assert_eq!(
670 round_const(1),
671 h16("6ea276726c487ab85d27bd10dd849401"),
672 "C_1"
673 );
674 assert_eq!(
675 round_const(2),
676 h16("dc87ece4d890f4b3ba4eb92079cbeb02"),
677 "C_2"
678 );
679 assert_eq!(
680 round_const(3),
681 h16("b2259a96b4d88e0be7690430a44f7f03"),
682 "C_3"
683 );
684 assert_eq!(
685 round_const(4),
686 h16("7bcd1b0b73e32ba5b79cb140f2551504"),
687 "C_4"
688 );
689 assert_eq!(
690 round_const(5),
691 h16("156f6d791fab511deabb0c502fd18105"),
692 "C_5"
693 );
694 assert_eq!(
695 round_const(6),
696 h16("a74af7efab73df160dd208608b9efe06"),
697 "C_6"
698 );
699 assert_eq!(
700 round_const(7),
701 h16("c9e8819dc73ba5ae50f5b570561a6a07"),
702 "C_7"
703 );
704 assert_eq!(
705 round_const(8),
706 h16("f6593616e6055689adfba18027aa2a08"),
707 "C_8"
708 );
709 }
710}