literate_crypto/hash/
sha2.rs

1//! SHA-1 and SHA-2 are hash functions specified by [FIPS
2//! 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
3//!
4//! SHA-1 and SHA-2 are based on the [Merkle-Damgard](crate::MerkleDamgard) and
5//! [Davies-Meyer](crate::DaviesMeyer) constructions. This means that each
6//! hashing algorithm uses a block cipher internally, [SHACAL-1](Shacal1) and
7//! [SHACAL-2](Shacal2) respectively. The block ciphers are used to mix the
8//! internal state of the hash function with padded preimage blocks. The
9//! final state (optionally truncated to a smaller size) is the hash digest.
10
11use {
12    crate::{BlockEncrypt, DaviesMeyer, DaviesMeyerStep, Hash, MerkleDamgard, MerkleDamgardPad},
13    docext::docext,
14    std::{iter, marker::PhantomData},
15};
16
17/// The $K_t^{256}$ constants for [SHA-256](Sha256).
18#[docext]
19pub const KT_256: [u32; 64] = [
20    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
21    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
22    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
23    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
24    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
25    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
26    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
27    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
28];
29
30/// The block size in bytes.
31const BLOCK_SIZE: usize = 64;
32
33/// A preimage block.
34pub type Block = [u8; BLOCK_SIZE];
35
36/// The internal state of [SHA-1](Sha1).
37pub type Sha1State = [u32; 5];
38
39/// The internal state of [SHA-256](Sha256) and [SHA-224](Sha224).
40pub type Sha2State = [u32; 8];
41
42/// SHA-1 hash specified by [FIPS
43/// 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
44///
45/// Note that this is a weak hash function with known vulnerabilities, and
46/// should be avoided in practice. It is also vulnerable to [length-extension
47/// attacks](MerkleDamgard#length-extension-attacks).
48///
49/// For more details, see the [module documentation](self).
50#[derive(Debug)]
51pub struct Sha1(
52    MerkleDamgard<
53        Sha1State,
54        Block,
55        DaviesMeyer<Shacal1, ModularAddition<Sha1State>>,
56        LengthPadding,
57    >,
58);
59
60/// SHA-256 hash specified by [FIPS
61/// 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
62///
63/// SHA-256 is vulnerable to [length-extension
64/// attacks](MerkleDamgard#length-extension-attacks).
65///
66/// For more details, see the [module documentation](self).
67#[derive(Debug)]
68pub struct Sha256(
69    MerkleDamgard<
70        Sha2State,
71        Block,
72        DaviesMeyer<Shacal2, ModularAddition<Sha2State>>,
73        LengthPadding,
74    >,
75);
76
77/// SHA-224 hash specified by [FIPS
78/// 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
79///
80/// SHA-224 is the same as [SHA-256](Sha256), with the hash digest truncated to
81/// 224 bits. Due to the truncation, SHA-224 is not vulnerable to
82/// [length-extension attacks](MerkleDamgard#length-extension-attacks), unlike
83/// SHA-256.
84///
85/// For more details, see the [module documentation](self).
86#[derive(Debug)]
87pub struct Sha224(
88    MerkleDamgard<
89        Sha2State,
90        Block,
91        DaviesMeyer<Shacal2, ModularAddition<Sha2State>>,
92        LengthPadding,
93    >,
94);
95
96/// The underlying block cipher used by [SHA-1](Sha1).
97///
98/// Applies 80 rounds of the following permutation, where $a, b, c, \dots$
99/// represent the current state in 32-bit words, $W_i$ is the message
100/// schedule (described below), [$f_t$](ft) is a helper function, [$K_t$](kt)
101/// are the round constants, and the $\mathrm{ROTL}$ function is bitwise left
102/// rotation:
103///
104/// $$
105/// T = \mathrm{ROTL}(a, 5) + f_t(b, c, d) + e + K_t + W_0 \pmod{2^{32}}\\
106/// e \gets d\\
107/// d \gets c\\
108/// c \gets \mathrm{ROTL}(b, 30)\\
109/// b \gets a\\
110/// a \gets T\\
111/// $$
112///
113/// The message schedule $W$ is a 16 element array of 32-bit words. It is
114/// initialized to the current preimage block, and updated at the end of each
115/// round as follows:
116///
117/// $$
118/// T = \mathrm{ROTL}(W_{13} \oplus W_8 \oplus W_2 \oplus W_0, 1)\\
119/// W_i \gets W_{i + 1}, \forall i \in \{0, 1, \dots, 14\}\\
120/// W_{15} \gets T
121/// $$
122///
123/// Meaning, the entire array is shifted left, and then the last element is
124/// updated as a combination of the other elements.
125///
126/// There are well-known vulnerabilities applicable to SHACAL-1 with a reduced
127/// number of rounds.
128#[docext]
129#[derive(Debug)]
130pub struct Shacal1(());
131
132/// The underlying block cipher used by [SHA-265](Sha256) and [SHA-224](Sha224).
133///
134/// Applies 64 rounds of the following permutation, where $a, b, c, \dots$
135/// represent the current state in 32-bit words, $W_i$ is the message
136/// schedule (described later), [$\Sigma_0^{256}$](uppercase_sigma_0),
137/// [$\Sigma_1^{256}$](uppercase_sigma_1),
138/// [$\sigma_0^{256}$](lowercase_sigma_0), [$\sigma_1^{256}$](lowercase_sigma_1)
139/// [$Ch$](ch), and [$Maj$](maj) are helper functions, and [$K_t^{256}$](KT_256)
140/// are the round constants:
141///
142/// $$
143/// T_1 = h + \Sigma_1^{256}(e) + Ch(e, f, g) + K_t^{256} + W_0
144/// \pmod{2^{32}}\\
145/// T_2 = \Sigma_0^{256}(a) + Maj(a, b, c) \pmod{2^{32}}\\
146/// h \gets g\\
147/// g \gets f\\
148/// f \gets e\\
149/// e \gets d + T_1\\
150/// d \gets c\\
151/// c \gets b\\
152/// b \gets a\\
153/// a \gets T_1 + T_2
154/// $$
155///
156/// The message schedule $W$ is a 16 element array of 32-bit words. It is
157/// initialized to the current preimage block, and updated at the end of each
158/// round as follows:
159///
160/// $$
161/// T = \sigma_1^{256}(W_{14}) + W_9 + \sigma_0^{256}(W_1) + W_0
162/// \pmod{2^{32}}\\
163/// W_i \gets W_{i + 1}, \forall i \in \{0, 1, \dots, 14\}\\
164/// W_{15} \gets T
165/// $$
166///
167/// Meaning, the entire array is shifted left, and then the last element is
168/// updated as a combination of the other elements.
169///
170/// There are well-known vulnerabilities applicable to SHACAL-2 with a reduced
171/// number of rounds.
172#[docext]
173#[derive(Debug)]
174pub struct Shacal2(());
175
176impl Default for Sha1 {
177    fn default() -> Self {
178        Self(MerkleDamgard::new(
179            DaviesMeyer::new(Shacal1(()), ModularAddition(Default::default())),
180            LengthPadding(()),
181            [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0],
182        ))
183    }
184}
185
186impl Hash for Sha1 {
187    type Digest = [u8; 20];
188    type Block = Block;
189
190    fn hash(&self, preimage: &[u8]) -> Self::Digest {
191        let mut result = [0; 20];
192        self.0
193            .hash(preimage)
194            .into_iter()
195            .flat_map(u32::to_be_bytes)
196            .zip(result.iter_mut())
197            .for_each(|(b, r)| *r = b);
198        result
199    }
200}
201
202impl Default for Sha256 {
203    fn default() -> Self {
204        Self(MerkleDamgard::new(
205            DaviesMeyer::new(Shacal2(()), ModularAddition(Default::default())),
206            LengthPadding(()),
207            [
208                0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
209                0x5be0cd19,
210            ],
211        ))
212    }
213}
214
215impl Hash for Sha256 {
216    type Digest = [u8; 32];
217    type Block = Block;
218
219    fn hash(&self, preimage: &[u8]) -> Self::Digest {
220        let mut result = [0; 32];
221        self.0
222            .hash(preimage)
223            .into_iter()
224            .flat_map(u32::to_be_bytes)
225            .zip(result.iter_mut())
226            .for_each(|(b, r)| *r = b);
227        result
228    }
229}
230
231impl Default for Sha224 {
232    fn default() -> Self {
233        Self(MerkleDamgard::new(
234            DaviesMeyer::new(Shacal2(()), ModularAddition(Default::default())),
235            LengthPadding(()),
236            [
237                0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7,
238                0xbefa4fa4,
239            ],
240        ))
241    }
242}
243
244impl Hash for Sha224 {
245    type Digest = [u8; 28];
246    type Block = Block;
247
248    fn hash(&self, preimage: &[u8]) -> Self::Digest {
249        let mut result = [0; 28];
250        self.0
251            .hash(preimage)
252            .into_iter()
253            .flat_map(u32::to_be_bytes)
254            .zip(result.iter_mut())
255            .for_each(|(b, r)| *r = b);
256        result
257    }
258}
259
260impl BlockEncrypt for Shacal1 {
261    type EncryptionBlock = Sha1State;
262    type EncryptionKey = Block;
263
264    fn encrypt(
265        &self,
266        data: Self::EncryptionBlock,
267        key: Self::EncryptionKey,
268    ) -> Self::EncryptionBlock {
269        let state = data;
270        let block = key;
271
272        // Initialize the message schedule.
273        let mut schedule = [0; 16];
274        schedule
275            .iter_mut()
276            .zip(block.array_chunks::<4>())
277            .for_each(|(s, b)| *s = u32::from_be_bytes(*b));
278
279        // Execute the rounds.
280        let mut a = state[0];
281        let mut b = state[1];
282        let mut c = state[2];
283        let mut d = state[3];
284        let mut e = state[4];
285        for t in 0..80 {
286            let wt = schedule[0];
287            let temp = a
288                .rotate_left(5)
289                .wrapping_add(ft(t, b, c, d))
290                .wrapping_add(e)
291                .wrapping_add(kt(t))
292                .wrapping_add(wt);
293            e = d;
294            d = c;
295            c = b.rotate_left(30);
296            b = a;
297            a = temp;
298
299            // Update the message schedule.
300            let next = (schedule[13] ^ schedule[8] ^ schedule[2] ^ schedule[0]).rotate_left(1);
301            schedule.rotate_left(1);
302            schedule[15] = next;
303        }
304
305        [a, b, c, d, e]
306    }
307}
308
309impl BlockEncrypt for Shacal2 {
310    type EncryptionBlock = Sha2State;
311    type EncryptionKey = Block;
312
313    fn encrypt(
314        &self,
315        data: Self::EncryptionBlock,
316        key: Self::EncryptionKey,
317    ) -> Self::EncryptionBlock {
318        let state = data;
319        let block = key;
320
321        // Initialize the message schedule.
322        let mut schedule = [0; 16];
323        schedule
324            .iter_mut()
325            .zip(block.array_chunks::<4>())
326            .for_each(|(s, b)| *s = u32::from_be_bytes(*b));
327
328        // Execute the rounds.
329        let mut a = state[0];
330        let mut b = state[1];
331        let mut c = state[2];
332        let mut d = state[3];
333        let mut e = state[4];
334        let mut f = state[5];
335        let mut g = state[6];
336        let mut h = state[7];
337        #[allow(clippy::needless_range_loop)]
338        for t in 0..64 {
339            let wt = schedule[0];
340            let temp1 = h
341                .wrapping_add(uppercase_sigma_1(e))
342                .wrapping_add(ch(e, f, g))
343                .wrapping_add(KT_256[t])
344                .wrapping_add(wt);
345            let temp2 = uppercase_sigma_0(a).wrapping_add(maj(a, b, c));
346            h = g;
347            g = f;
348            f = e;
349            e = d.wrapping_add(temp1);
350            d = c;
351            c = b;
352            b = a;
353            a = temp1.wrapping_add(temp2);
354
355            // Update the message schedule.
356            let next = lowercase_sigma_1(schedule[14])
357                .wrapping_add(schedule[9])
358                .wrapping_add(lowercase_sigma_0(schedule[1]))
359                .wrapping_add(schedule[0]);
360            schedule.rotate_left(1);
361            schedule[15] = next;
362        }
363
364        [a, b, c, d, e, f, g, h]
365    }
366}
367
368/// Helper function $f_t$ used by [SHA-1](Sha1).
369///
370/// Uses [$Ch$](ch), [$Maj$](maj), and [$Parity$](parity) functions.
371///
372/// $$
373/// f_t(x, y, z) =
374/// \begin{cases}
375/// Ch(x, y, z) & 0 \le t < 20\\
376/// Maj(x, y, z) & 40 \le t < 60\\
377/// Parity(x, y, z) & otherwise \\
378/// \end{cases}
379/// $$
380#[docext]
381pub fn ft(t: u32, x: u32, y: u32, z: u32) -> u32 {
382    match t {
383        0..=19 => ch(x, y, z),
384        40..=59 => maj(x, y, z),
385        _ => parity(x, y, z),
386    }
387}
388
389/// Round constant $K_t$ used by [SHA-1](Sha1).
390#[docext]
391pub fn kt(t: u32) -> u32 {
392    match t {
393        0..=19 => 0x5a827999,
394        20..=39 => 0x6ed9eba1,
395        40..=59 => 0x8f1bbcdc,
396        _ => 0xca62c1d6,
397    }
398}
399
400/// Helper function $Ch$.
401///
402/// $$
403/// Ch(x, y, z) = (x \land y) \oplus (\neg x \land z)
404/// $$
405#[docext]
406pub fn ch(x: u32, y: u32, z: u32) -> u32 {
407    (x & y) ^ ((!x) & z)
408}
409
410/// Helper function $Maj$.
411///
412/// $$
413/// Maj(x, y, z) = (x \land y) \oplus (x \land z) \oplus (y \land z)
414/// $$
415#[docext]
416pub fn maj(x: u32, y: u32, z: u32) -> u32 {
417    (x & y) ^ (x & z) ^ (y & z)
418}
419
420/// Helper function $Parity$.
421///
422/// $$
423/// Parity(x, y, z) = x \oplus y \oplus z
424/// $$
425#[docext]
426pub fn parity(x: u32, y: u32, z: u32) -> u32 {
427    x ^ y ^ z
428}
429
430/// Helper function $\Sigma_0^{256}$.
431///
432/// $$
433/// \Sigma_0^{256}(x) = \mathrm{ROTR}(x, 2) \oplus \mathrm{ROTR}(x, 13) \oplus
434/// \mathrm{ROTR}(x, 22) $$
435///
436/// Where $\mathrm{ROTR}$ is bitwise rotation to the right.
437#[docext]
438pub fn uppercase_sigma_0(x: u32) -> u32 {
439    x.rotate_right(2) ^ x.rotate_right(13) ^ x.rotate_right(22)
440}
441
442/// Helper function $\Sigma_1^{256}$.
443///
444/// $$
445/// \Sigma_1^{256}(x) = \mathrm{ROTR}(x, 6) \oplus \mathrm{ROTR}(x, 11) \oplus
446/// \mathrm{ROTR}(x, 25) $$
447///
448/// Where $\mathrm{ROTR}$ is bitwise rotation to the right.
449#[docext]
450pub fn uppercase_sigma_1(x: u32) -> u32 {
451    x.rotate_right(6) ^ x.rotate_right(11) ^ x.rotate_right(25)
452}
453
454/// Helper function $\sigma_0^{256}$.
455///
456/// $$
457/// \sigma_0^{256}(x) = \mathrm{ROTR}(x, 7) \oplus \mathrm{ROTR}(x, 18) \oplus
458/// (x \gg 3) $$
459///
460/// Where $\mathrm{ROTR}$ is bitwise rotation to the right, and $\gg$ is the
461/// bitwise right shift operation.
462#[docext]
463pub fn lowercase_sigma_0(x: u32) -> u32 {
464    x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3)
465}
466
467/// Helper function $\sigma_1^{256}$.
468///
469/// $$
470/// \sigma_1^{256}(x) = \mathrm{ROTR}(x, 17) \oplus \mathrm{ROTR}(x, 19) \oplus
471/// (x \gg 10) $$
472///
473/// Where $\mathrm{ROTR}$ is bitwise rotation to the right, and $\gg$ is the
474/// bitwise right shift operation.
475#[docext]
476pub fn lowercase_sigma_1(x: u32) -> u32 {
477    x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10)
478}
479
480/// Because the new state is derived by adding the "working variables" to the
481/// current state, the [Davies-Meyer step](DaviesMeyerStep) in SHA-1 and SHA-2
482/// is modular addition.
483#[derive(Debug)]
484pub struct ModularAddition<State>(PhantomData<State>);
485
486impl<State> DaviesMeyerStep for ModularAddition<State>
487where
488    State: AsMut<[u32]> + AsRef<[u32]>,
489{
490    type State = State;
491
492    fn step(&self, prev: Self::State, mut new: Self::State) -> Self::State {
493        new.as_mut()
494            .iter_mut()
495            .zip(prev.as_ref().iter())
496            .for_each(|(n, p)| *n = n.wrapping_add(*p));
497        new
498    }
499}
500
501/// SHA-2 length padding.
502///
503/// The preimage is padded by appending a single 1 bit, followed by as many bits
504/// as needed to pad to a multiple of 512 - 64 = 448 bits, followed by the _bit
505/// length_ of the preimage encoded as an unsigned big-endian 64 bit integer.
506/// This results in a [Merkle-Damgard compliant padding](MerkleDamgardPad) into
507/// blocks of 512 bits.
508#[derive(Debug)]
509pub struct LengthPadding(());
510
511impl MerkleDamgardPad for LengthPadding {
512    type Block = Block;
513
514    fn pad(&self, preimage: &[u8]) -> impl Iterator<Item = Self::Block> {
515        preimage
516            .chunks(BLOCK_SIZE)
517            .chain(
518                // If the input is a multiple of the block size, a full block of padding needs to
519                // be added.
520                iter::once([].as_slice()).take(if preimage.len() % BLOCK_SIZE == 0 {
521                    1
522                } else {
523                    0
524                }),
525            )
526            .flat_map(|chunk| {
527                if chunk.len() == BLOCK_SIZE {
528                    // This block does not need padding.
529                    vec![chunk.try_into().unwrap()]
530                } else if BLOCK_SIZE - chunk.len() <= 8 {
531                    // This block requires an additional block of padding.
532                    let mut block = [0u8; BLOCK_SIZE];
533                    block[..chunk.len()].copy_from_slice(chunk);
534                    block[chunk.len()] = 0x80;
535                    let mut next = [0u8; BLOCK_SIZE];
536                    next[BLOCK_SIZE - 8..]
537                        .copy_from_slice(&u64::try_from(8 * preimage.len()).unwrap().to_be_bytes());
538                    vec![block, next]
539                } else {
540                    // This block needs to be padded.
541                    let mut block = [0u8; BLOCK_SIZE];
542                    block[..chunk.len()].copy_from_slice(chunk);
543                    block[chunk.len()] = 0x80;
544                    block[BLOCK_SIZE - 8..]
545                        .copy_from_slice(&u64::try_from(8 * preimage.len()).unwrap().to_be_bytes());
546                    vec![block]
547                }
548            })
549    }
550}