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}