1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT-0
//! SHA1, for legacy protocols only.
//!
//! SHA1 is described in [FIPS180](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
//! Its collision resistance is broken, so it must not be used where that
//! matters, such as in signatures.
use crate::low::cpu::verify_cpu_features;
use crate::low::{Blockwise, compress_padded};
/// SHA1's chaining value after a whole number of blocks.
///
/// Finishing needs no buffer, so a one-shot hash, or HMAC from its keyed
/// states, works on this alone.
#[derive(Clone)]
pub(crate) struct Sha1State {
h: [u32; 5],
// u64 so 32-bit targets can hash past 4 GiB.
nblocks: u64,
}
impl Sha1State {
/// SHA1's initial value.
pub(crate) const fn new() -> Self {
Self {
h: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0],
nblocks: 0,
}
}
/// Add `blocks`, a whole number of blocks, to the hash computation.
pub(crate) fn update_blocks(&mut self, blocks: &[u8]) {
debug_assert!(blocks.len().is_multiple_of(Sha1Context::BLOCK_SZ));
if !blocks.is_empty() {
Self::compress(&mut self.h, blocks);
self.nblocks = self
.nblocks
.saturating_add((blocks.len() / Sha1Context::BLOCK_SZ) as u64);
}
}
fn compress(h: &mut [u32; 5], blocks: &[u8]) {
verify_cpu_features();
crate::low::sha1_compress_blocks(h, blocks);
}
/// The hash of the input so far followed by `message`, leaving this state as it was.
pub(crate) fn finish_with(&self, message: &[u8]) -> [u8; Sha1Context::OUTPUT_SZ] {
const BLOCK_SZ: usize = Sha1Context::BLOCK_SZ;
let bits = self
.nblocks
.checked_mul(BLOCK_SZ as u64)
.and_then(|bytes| bytes.checked_add(message.len() as u64))
.and_then(|bytes| bytes.checked_mul(8))
.expect("excess data processed by hash function");
// Copies `h` alone: copying the whole state can leave it straddling
// two stores, which the SHA extensions' 16-byte load of it then
// waits on instead of forwarding.
let mut h = self.h;
compress_padded::<BLOCK_SZ, _>(message, bits.to_be_bytes(), |blocks| {
Self::compress(&mut h, blocks)
});
let mut r = [0u8; Sha1Context::OUTPUT_SZ];
let (chunks, _) = r.as_chunks_mut::<4>();
for (out, word) in chunks.iter_mut().zip(h.iter()) {
out.copy_from_slice(&word.to_be_bytes());
}
r
}
}
/// A context for incremental computation of SHA1.
#[derive(Clone)]
pub(crate) struct Sha1Context {
state: Sha1State,
blockwise: Blockwise<{ Self::BLOCK_SZ }>,
}
impl Sha1Context {
/// Start a new SHA1 hash computation.
pub(crate) const fn new() -> Self {
Self::resume(Sha1State::new())
}
/// Continue a hash computation from `state`.
pub(crate) const fn resume(state: Sha1State) -> Self {
Self {
state,
blockwise: Blockwise::new(),
}
}
/// Add `bytes` to the ongoing hash computation.
pub(crate) fn update(&mut self, bytes: &[u8]) {
self.blockwise
.update(bytes, |blocks| self.state.update_blocks(blocks));
}
/// Complete the SHA1 computation, returning the hash output.
pub(crate) fn finish(&self) -> [u8; Self::OUTPUT_SZ] {
self.state.finish_with(self.blockwise.buffered())
}
/// The internal block size of SHA1.
pub(crate) const BLOCK_SZ: usize = 64;
/// The output size of SHA1.
pub(crate) const OUTPUT_SZ: usize = 20;
}
#[cfg(test)]
mod tests {
use super::*;
// The bit length overflows a 32-bit usize at 512 MiB, and the byte count
// at 4 GiB.
#[test]
fn length_past_32_bit_usize() {
for bytes in [1u64 << 29, 1 << 32] {
let mut ctx = Sha1Context::new();
ctx.state.nblocks = bytes / Sha1Context::BLOCK_SZ as u64;
ctx.update(b"x");
ctx.finish();
}
}
}