bash_f/lib.rs
1#![no_std]
2#![doc = include_str!("../README.md")]
3#![doc(
4 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
5 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
6)]
7#![cfg_attr(docsrs, feature(doc_cfg))]
8#![warn(missing_docs)]
9
10/// Number of 64-bit words in the [`bash-f`][bash_f] state.
11pub const STATE_WORDS: usize = 24;
12
13/// `bash-s` transformation defined in Section 6.1 of STB 34.101.77-2020.
14fn bash_s(
15 mut w0: u64,
16 mut w1: u64,
17 mut w2: u64,
18 m1: u32,
19 n1: u32,
20 m2: u32,
21 n2: u32,
22) -> (u64, u64, u64) {
23 // 1. T0 ← RotHi^m1(W0)
24 let t0 = w0.rotate_left(m1);
25
26 // 2. W0 ← W0 ⊕ W1 ⊕ W2
27 w0 ^= w1 ^ w2;
28
29 // 3. T1 ← W1 ⊕ RotHi^n1(W0)
30 let t1 = w1 ^ w0.rotate_left(n1);
31
32 // 4. W1 ← T0 ⊕ T1
33 w1 = t0 ^ t1;
34
35 // 5. W2 ← W2 ⊕ RotHi^m2(W2) ⊕ RotHi^n2(T1)
36 w2 ^= w2.rotate_left(m2) ^ t1.rotate_left(n2);
37
38 // 6. T0 ← ¬W2
39 let t0 = !w2;
40
41 // 7. T1 ← W0 ∨ W2
42 let t1 = w0 | w2;
43
44 // 8. T2 ← W0 ∧ W1
45 let t2 = w0 & w1;
46
47 // 9. T0 ← T0 ∨ W1
48 let t0 = t0 | w1;
49
50 // 10. W1 ← W1 ⊕ T1
51 w1 ^= t1;
52
53 // 11. W2 ← W2 ⊕ T2
54 w2 ^= t2;
55
56 // 12. W0 ← W0 ⊕ T0
57 w0 ^= t0;
58
59 // 13. Return (W0, W1, W2)
60 (w0, w1, w2)
61}
62
63/// `bash-f` sponge permutation defined in Section 6.2 of STB 34.101.77-2020.
64pub fn bash_f(state: &mut [u64; STATE_WORDS]) {
65 // 1. Split S into words (S0, S1, ..., S23)
66
67 // 2. C ← B194BAC80A08F53B (initialize round constant, swapped to little-endian)
68 let mut c: u64 = 0x3BF5080AC8BA94B1;
69
70 // 3. For i = 1, 2, ..., 24 perform 24 rounds
71 for _ in 0..STATE_WORDS {
72 // 3.1. Apply S-box layer with varying rotation parameters
73 // (m1, n1, m2, n2) ← (8, 53, 14, 1)
74 let (mut m1, mut n1, mut m2, mut n2) = (8, 53, 14, 1);
75
76 // 3.2. For j = 0, 1, ..., 7 apply bash-s to each of 8 columns
77 for j in 0..8 {
78 // 3.2.a. (Sj, S8+j, S16+j) ← bash-s(Sj, S8+j, S16+j, m1, n1, m2, n2)
79 let (s0, s1, s2) = bash_s(state[j], state[8 + j], state[16 + j], m1, n1, m2, n2);
80 state[j] = s0;
81 state[8 + j] = s1;
82 state[16 + j] = s2;
83
84 // 3.2.b. (m1, n1, m2, n2) ← (7·m1 mod 64, 7·n1 mod 64, 7·m2 mod 64, 7·n2 mod 64)
85 (m1, n1, m2, n2) = ((7 * m1) % 64, (7 * n1) % 64, (7 * m2) % 64, (7 * n2) % 64);
86 }
87
88 // 3.3. Apply word permutation
89 // S ← S15 ‖ S10 ‖ S9 ‖ S12 ‖ S11 ‖ S14 ‖ S13 ‖ S8 ‖
90 // S17 ‖ S16 ‖ S19 ‖ S18 ‖ S21 ‖ S20 ‖ S23 ‖ S22 ‖
91 // S6 ‖ S3 ‖ S0 ‖ S5 ‖ S2 ‖ S7 ‖ S4 ‖ S1
92 const INDEXES: [usize; STATE_WORDS] = [
93 15, 10, 9, 12, 11, 14, 13, 8, 17, 16, 19, 18, 21, 20, 23, 22, 6, 3, 0, 5, 2, 7, 4, 1,
94 ];
95 *state = INDEXES.map(|i| state[i]);
96
97 // 3.4. S23 ← S23 ⊕ C (add round constant)
98 state[23] ^= c;
99
100 // 3.5. Update LFSR (Galois configuration)
101 // if ⌊C⌉ is even, then C ← ShLo(C)
102 // else C ← ShLo(C) ⊕ AED8E07F99E12BDC
103 if c & 1 == 0 {
104 c >>= 1;
105 } else {
106 c = (c >> 1) ^ 0xDC2BE1997FE0D8AE;
107 }
108 }
109
110 // 4. Return S - state is modified in place
111}
112
113/// Test vector from Table A.1 of STB 34.101.77-2020.
114#[test]
115fn test_bash_s() {
116 // Note that constants in the spec are provided using the LE order (see Section 4.2.2).
117 let w0 = 0xB194BAC80A08F53Bu64.swap_bytes();
118 let w1 = 0xE12BDC1AE28257ECu64.swap_bytes();
119 let w2 = 0xE9DEE72C8F0C0FA6u64.swap_bytes();
120
121 let (w0_out, w1_out, w2_out) = bash_s(w0, w1, w2, 8, 53, 14, 1);
122
123 assert_eq!(w0_out, 0x479E76129979DC5Fu64.swap_bytes());
124 assert_eq!(w1_out, 0x0F2B2C93ED128EDDu64.swap_bytes());
125 assert_eq!(w2_out, 0x41009B1B112DFEF3u64.swap_bytes());
126}