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
130
131
132
133
134
135
136
137
138
139
140
141
// 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::Blockwise;
use crate::low::cpu::verify_cpu_features;
/// 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 (blocks, tail) = message.split_at(message.len() & !(BLOCK_SZ - 1));
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;
if !blocks.is_empty() {
Self::compress(&mut h, blocks);
}
let mut block = [0u8; BLOCK_SZ];
block[..tail.len()].copy_from_slice(tail);
block[tail.len()] = 0x80;
// The length takes a block of its own when the tail leaves no room for it.
if tail.len() >= BLOCK_SZ - 8 {
Self::compress(&mut h, &block);
block = [0u8; BLOCK_SZ];
}
block[BLOCK_SZ - 8..].copy_from_slice(&bits.to_be_bytes());
Self::compress(&mut h, &block);
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();
}
}
}