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);