sha3_rust/
lib.rs

1/// This crate provides an implementation of the Keccak (SHA-3) cryptographic hash function family.
2/// 
3/// # Simple Overview of the Sha3 Program
4/// 
5/// The Sha3 program is a Rust crate that provides an implementation of the Keccak (SHA-3)
6/// cryptographic hash function family. The crate provides several Hash functions with
7/// different output lengths (224, 256, 384, 512 bits). The hash functions are created using
8/// the macro `sha3!`, which defines the Hash function with a specific output length.
9/// The Hash functions are exported from the crate as public functions and can be used
10/// in any Rust program.
11///
12/// The Hash functions take a byte slice as input and return a fixed-size array containing
13/// the hash output. The hash functions can be used to hash strings, files, or any other
14/// binary data.
15///
16/// The crate also provides utility functions to convert bytes to bits and vice versa.
17/// These functions are used internally by the Hash functions but can also be used by
18/// other parts of the program if needed.
19///
20/// The crate uses the Keccak permutation, which is the core of the SHA-3 algorithm.
21/// The Keccak permutation is a sponge function that can be used to transform any
22/// input data of any length into a fixed-size output. The crate defines the Keccak
23/// permutation as a macro that can be used to create other sponge functions.
24///
25/// The crate also provides some example code in the `main.rs` file that demonstrates
26/// how to use the Hash functions to hash strings, files, and multiple inputs.
27///
28
29use std::convert::TryInto;
30
31/// The width of the Keccak permutation (1600 bits).
32const B: usize = 1600;
33
34/// The lane size (64 bits).
35const W: usize = B / 25;
36
37/// The number of rounds.
38///
39/// The number of rounds is calculated using the number of trailing zeros in the binary
40/// representation of `B`. This ensures that the number of rounds is correct for the
41/// specific Keccak permutation.
42const L: usize = W.trailing_zeros() as usize;
43
44/// Number of bits in a byte.
45///
46/// This constant is used to calculate the number of rounds in the Keccak permutation.
47const U8BITS: usize = u8::BITS as usize;
48
49/// A macro to iterate over the state array.
50///
51/// This macro is used to iterate over the state array in the Keccak permutation. It
52/// allows for more concise code when performing operations on the state array. The macro
53/// takes three identifiers `$x`, `$y`, and `$z` which represent the indices of the state
54/// array. The body of the macro is executed for each iteration.
55#[macro_export]
56macro_rules! iterate {
57    // The macro takes a pattern consisting of three identifiers and a block of code.
58    ($x:ident, $y:ident, $z:ident => $body:block) => {
59        // The macro iterates over the indices of the state array.
60        for $y in 0..5 {
61            for $x in 0..5 {
62                for $z in 0..W {
63                    // The code block is executed for each iteration.
64                    $body
65                }
66            }
67        }
68    };
69}
70
71/// Type definition for padding functions.
72type PadFn = fn(isize, isize) -> Vec<bool>;
73
74/// Type definition for sponge functions.
75type SpongeFn = fn(&[bool]) -> [bool; B];
76
77/// The state of the Keccak permutation.
78type State = [[[bool; W]; 5]; 5];
79
80/// Creates a new state filled with `false`.
81fn new_state() -> State {
82    [[[false; W]; 5]; 5]
83}
84
85/// Fills the state array with the provided bits.
86fn fill_state(state: &mut State, bits: &[bool]) {
87    let mut i = 0usize;
88    iterate!(x, y, z => {
89        if i >= bits.len() {
90            return;
91        }
92        state[x][y][z] = bits[i];
93        i += 1;
94    });
95}
96
97/// Copies the state from `src` to `dest`.
98fn copy_state(dest: &mut State, src: &State) {
99    iterate!(x, y, z => {
100        dest[x][y][z] = src[x][y][z];
101    });
102}
103
104/// Dumps the state array into a single array of bits.
105///
106/// # Returns
107///
108/// A vector of boolean values representing the state array.
109fn dump_state(state: State) -> [bool; B] {
110    let mut bits = [false; B];
111    let mut i = 0usize;
112    // Iterate over each element in the state array and assign it to the corresponding
113    // element in the bits vector.
114    iterate!(x, y, z => {
115        if i >= bits.len() {
116            return bits;
117        }
118        bits[i] = state[x][y][z];
119        i += 1;
120    });
121    bits
122}
123
124/// The theta step mapping of Keccak.
125///
126/// This function computes the parity for each column in the state array and then
127/// computes the intermediate array `d` by performing XOR operations on the elements
128/// of the state array. Finally, it modifies the state array by performing XOR operations
129/// on the elements of the `d` array.
130fn theta(state: &mut State) {
131    let mut c = [[false; W]; 5];
132    let mut d = [[false; W]; 5];
133
134    // Compute parity for each column
135    for x in 0..5 {
136        for z in 0..W {
137            c[x][z] = state[x][0][z];
138            for y in 1..5 {
139                c[x][z] ^= state[x][y][z];
140            }
141        }
142    }
143
144    // Compute the intermediate array `d`
145    for x in 0..5 {
146        for z in 0..W {
147            let x1 = (x + 4) % 5;
148            let z2 = (z + W - 1) % W;
149            d[x][z] = c[x1][z] ^ c[(x + 1) % 5][z2];
150        }
151    }
152
153    // Modify the state with `d`
154    iterate!(x, y, z => {
155        state[x][y][z] ^= d[x][z];
156    });
157}
158
159/// The rho step mapping of Keccak.
160///
161/// This function performs the permutation of the remaining bits in the state array.
162/// It copies the bit from state`[0][0]` directly and performs permutation of the remaining
163/// bits.
164fn rho(state: &mut State) {
165    let mut new_state = new_state();
166
167    // Copy the bit from state[0][0] directly
168    for z in 0..W {
169        new_state[0][0][z] = state[0][0][z];
170    }
171
172    let mut x = 1;
173    let mut y = 0;
174
175    // Permutation of the remaining bits
176    for t in 0..24 {
177        for z in 0..W {
178            let new_z = (z + (t * (t + 1)) / 2) % W;
179            new_state[x][y][z] = state[x][y][new_z];
180        }
181        let (new_x, new_y) = (y, (2 * x + 3 * y) % 5);
182        x = new_x;
183        y = new_y;
184    }
185
186    copy_state(state, &new_state);
187}
188
189/// The pi step mapping of Keccak.
190///
191/// This function performs the permutation of the state array by swapping the elements
192/// of the state array based on the given indices.
193fn pi(state: &mut State) {
194    let mut new_state = new_state();
195    iterate!(x, y, z => {
196        new_state[x][y][z] = state[(x + 3 * y) % 5][x][z];
197    });
198    copy_state(state, &new_state);
199}
200
201/// The chi step mapping of Keccak.
202///
203/// This function performs the permutation of the state array by performing XOR operations
204/// on the elements of the state array.
205fn chi(state: &mut State) {
206    let mut new_state = new_state();
207    iterate!(x, y, z => {
208        new_state[x][y][z] = state[x][y][z] ^ ((!state[(x + 1) % 5][y][z]) & state[(x + 2) % 5][y][z]);
209    });
210    copy_state(state, &new_state);
211}
212
213/// The iota step mapping of Keccak, incorporating the round constants.
214///
215/// This function performs the XOR operation between the state array and the round constants.
216fn iota(state: &mut State, round_index: u8) {
217    let mut rc_arr = [false; W];
218    for j in 0..=L {
219        rc_arr[(1 << j) - 1] = rc(j as u8 + 7 * round_index);
220    }
221    for (z, bit) in rc_arr.iter().enumerate() {
222        state[0][0][z] ^= *bit;
223    }
224}
225
226/// Computes the round constants for the iota step.
227///
228/// This function computes the round constants for the iota step by performing a series
229/// of bitwise operations.
230fn rc(t: u8) -> bool {
231    let mut r: u16 = 0x80;
232    for _ in 0..(t % 255) {
233        r = ((r << 1) ^ ((r >> 7) & 1) * 0x71) & 0xff;
234    }
235    (r >> 7) & 1 != 0
236}
237
238/// Performs a single round of the Keccak-f permutation.
239///
240/// This function performs a single round of the Keccak-f permutation by calling the
241/// theta, rho, pi, chi, and iota steps.
242fn round(state: &mut State, round_index: u8) {
243    theta(state);
244    rho(state);
245    pi(state);
246    chi(state);
247    iota(state, round_index);
248}
249
250/// The Keccak-f permutation function.
251///
252/// This function performs the Keccak-f permutation on the given input bits.
253/// It applies `num_rounds` rounds of the Keccak-f permutation, where
254/// `num_rounds` is calculated based on the number of rounds used in the
255/// Keccak hash function family.
256///
257/// # Arguments
258///
259/// * `bits` - A slice of boolean values representing the input bits.
260///
261/// # Returns
262///
263/// A vector of boolean values representing the output of the Keccak-f
264/// permutation.
265fn keccak_f(bits: &[bool]) -> [bool; B] {
266    // Calculate the number of rounds to be applied
267    let num_rounds = 12 + 2 * L;
268
269    // Create a new state array and fill it with the input bits
270    let mut state = new_state();
271    fill_state(&mut state, bits);
272
273    // Apply the Keccak-f permutation
274    for i in 0..num_rounds {
275        round(&mut state, i as u8);
276    }
277
278    // Dump the state array into a single array of bits
279    dump_state(state)
280}
281
282/// Pads the input with the `101` pattern according to Keccak specifications.
283fn pad101(x: isize, m: isize) -> Vec<bool> {
284    let j = (x - (m % x) - 2).rem_euclid(x);
285    let mut padding = vec![false; (j + 2) as usize];
286    padding[0] = true;
287    padding[j as usize + 1] = true;
288    padding
289}
290
291/// The sponge construction used in Keccak.
292///
293/// This function implements the sponge construction used in the Keccak hash function.
294/// It takes as input a sponge function `f`, a padding function `pad`, a block size `r`,
295/// an input `n`, and a desired output size `d`. It then iteratively applies the sponge
296/// construction to the input until the desired output size is reached.
297///
298/// # Arguments
299///
300/// * `f` - The sponge function to be used.
301/// * `pad` - The padding function to be used.
302/// * `r` - The block size of the sponge function.
303/// * `n` - The input to be processed.
304/// * `d` - The desired output size.
305///
306/// # Returns
307///
308/// A vector of boolean values representing the output of the sponge construction.
309fn sponge(f: SpongeFn, pad: PadFn, r: usize, n: &[bool], d: usize) -> Vec<bool> {
310    // Create a new vector `p` by extending `n` with the result of applying the padding function
311    let mut p = Vec::from(n);
312    p.append(&mut pad(r as isize, n.len() as isize));
313    assert!(r < B);
314
315    // Create a new state `s`
316    let mut s = [false; B];
317
318    // Iterate over the chunks of `p` of size `r`
319    for chunk in p.chunks(r) {
320        // XOR each element of `s` with the corresponding element of `chunk`
321        for (s_i, c_i) in s.iter_mut().zip(chunk) {
322            *s_i ^= *c_i;
323        }
324        // Apply the sponge function `f` to `s`
325        s = f(&s);
326    }
327
328    // Create an empty vector `z`
329    let mut z = Vec::new();
330    // Repeat the following process until `z` has a length of `d`
331    while z.len() < d {
332        // Extend `z` with the elements of `s`
333        z.extend_from_slice(&s);
334        // Apply the sponge function `f` to `s`
335        s = f(&s);
336    }
337
338    // Truncate `z` to a length of `d`
339    z.truncate(d);
340    // Return `z`
341    z
342}
343
344/// The Keccak hash function.
345fn keccak(c: usize, n: &[bool], d: usize) -> Vec<bool> {
346    sponge(keccak_f, pad101, B - c, n, d)
347}
348
349/// Converts a byte array to a bit array.
350///
351/// # Arguments
352///
353/// * `h` - The byte array to convert.
354/// * `n` - The number of bits to take from the byte array.
355///
356/// # Returns
357///
358/// A vector of `n` bits.
359fn h2b(h: &[u8], n: usize) -> Vec<bool> {
360    // Map each byte to a vector of its bits, then flatten the result.
361    h.iter()
362        .flat_map(|byte| (0..8).map(move |i| (byte >> i) & 1 == 1))
363        // Take only the first `n` bits.
364        .take(n)
365        // Collect the bits into a vector.
366        .collect()
367}
368
369/// Converts a bit array to a byte array.
370///
371/// # Arguments
372///
373/// * `s` - The bit array to convert.
374///
375/// # Returns
376///
377/// A vector of bytes.
378fn b2h(s: &[bool]) -> Vec<u8> {
379    // Chunk the bit array into chunks of 8 bits.
380    s.chunks(U8BITS)
381        // For each chunk, fold the bits into a byte.
382        .map(|chunk| chunk.iter().enumerate().fold(0, |byte, (i, &bit)| byte | ((bit as u8) << i)))
383        // Collect the bytes into a vector.
384        .collect()
385}
386
387/// A macro to define SHA-3 hash functions with different output lengths.
388macro_rules! sha3 {
389    ($name:ident, $n:literal) => {
390        /// Computes the SHA-3 hash of the input data.
391        ///
392        /// # Arguments
393        ///
394        /// * `input` - A byte slice containing the data to hash.
395        ///
396        /// # Returns
397        ///
398        /// A fixed-size array containing the hash output.
399        pub fn $name(input: &[u8]) -> [u8; $n / U8BITS] {
400            let mut bits = h2b(input, input.len() * U8BITS);
401            bits.append(&mut vec![false, true]);
402            let result_bits = keccak($n * 2, &bits, $n);
403            let result_bytes = b2h(&result_bits);
404            result_bytes.try_into().expect("incorrect length")
405        }
406    };
407}
408
409// Define SHA-3 hash functions with different output lengths.
410sha3!(sha3_224, 224);
411sha3!(sha3_256, 256);
412sha3!(sha3_384, 384);
413sha3!(sha3_512, 512);