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}