1use crate::oid::Oid;
10
11pub struct Sha1 {
12 state: [u32; 5],
13 len: u64,
15 buf: [u8; 64],
16 buf_len: usize,
17}
18
19impl Default for Sha1 {
20 fn default() -> Self {
21 Self::new()
22 }
23}
24
25impl Sha1 {
26 pub const fn new() -> Self {
27 Self {
28 state: [
29 0x6745_2301,
30 0xefcd_ab89,
31 0x98ba_dcfe,
32 0x1032_5476,
33 0xc3d2_e1f0,
34 ],
35 len: 0,
36 buf: [0; 64],
37 buf_len: 0,
38 }
39 }
40
41 pub fn update(&mut self, mut data: &[u8]) {
42 self.len += data.len() as u64;
43
44 if self.buf_len > 0 {
45 let take = (64 - self.buf_len).min(data.len());
46 self.buf[self.buf_len..self.buf_len + take].copy_from_slice(&data[..take]);
47 self.buf_len += take;
48 data = &data[take..];
49 if self.buf_len < 64 {
50 return;
53 }
54 let block = self.buf;
55 self.compress(&block);
56 self.buf_len = 0;
57 }
58
59 let mut chunks = data.chunks_exact(64);
60 for block in &mut chunks {
61 self.compress(block.try_into().unwrap());
62 }
63 let rest = chunks.remainder();
64 self.buf[..rest.len()].copy_from_slice(rest);
65 self.buf_len = rest.len();
66 }
67
68 pub fn finalize(mut self) -> [u8; 20] {
69 let bit_len = self.len * 8;
70 self.update(&[0x80]);
71 while self.buf_len != 56 {
72 self.update(&[0]);
73 }
74 self.update(&bit_len.to_be_bytes());
76 debug_assert_eq!(self.buf_len, 0);
77
78 let mut out = [0u8; 20];
79 for (i, word) in self.state.iter().enumerate() {
80 out[i * 4..i * 4 + 4].copy_from_slice(&word.to_be_bytes());
81 }
82 out
83 }
84
85 fn compress(&mut self, block: &[u8; 64]) {
86 let mut w = [0u32; 80];
87 for (i, chunk) in block.chunks_exact(4).enumerate() {
88 w[i] = u32::from_be_bytes(chunk.try_into().unwrap());
89 }
90 for i in 16..80 {
91 w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
92 }
93
94 let [mut a, mut b, mut c, mut d, mut e] = self.state;
95 for (i, &word) in w.iter().enumerate() {
96 let (f, k) = match i {
97 0..=19 => ((b & c) | (!b & d), 0x5a82_7999),
98 20..=39 => (b ^ c ^ d, 0x6ed9_eba1),
99 40..=59 => ((b & c) | (b & d) | (c & d), 0x8f1b_bcdc),
100 _ => (b ^ c ^ d, 0xca62_c1d6),
101 };
102 let t = a
103 .rotate_left(5)
104 .wrapping_add(f)
105 .wrapping_add(e)
106 .wrapping_add(k)
107 .wrapping_add(word);
108 e = d;
109 d = c;
110 c = b.rotate_left(30);
111 b = a;
112 a = t;
113 }
114
115 self.state[0] = self.state[0].wrapping_add(a);
116 self.state[1] = self.state[1].wrapping_add(b);
117 self.state[2] = self.state[2].wrapping_add(c);
118 self.state[3] = self.state[3].wrapping_add(d);
119 self.state[4] = self.state[4].wrapping_add(e);
120 }
121}
122
123pub fn digest(data: &[u8]) -> [u8; 20] {
125 let mut h = Sha1::new();
126 h.update(data);
127 h.finalize()
128}
129
130pub fn digest_oid(parts: &[&[u8]]) -> Oid {
132 let mut h = Sha1::new();
133 for part in parts {
134 h.update(part);
135 }
136 Oid::from_bytes(h.finalize())
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 fn hex(bytes: &[u8]) -> String {
144 bytes.iter().map(|b| format!("{b:02x}")).collect()
145 }
146
147 #[test]
149 fn rfc3174_vectors() {
150 assert_eq!(
151 hex(&digest(b"abc")),
152 "a9993e364706816aba3e25717850c26c9cd0d89d"
153 );
154 assert_eq!(
155 hex(&digest(
156 b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
157 )),
158 "84983e441c3bd26ebaae4aa1f95129e5e54670f1"
159 );
160 assert_eq!(
161 hex(&digest(&[b'a'; 1_000_000])),
162 "34aa973cd4c4daa4f61eeb2bdbad27316534016f"
163 );
164 }
165
166 #[test]
167 fn empty_input() {
168 assert_eq!(
169 hex(&digest(b"")),
170 "da39a3ee5e6b4b0d3255bfef95601890afd80709"
171 );
172 }
173
174 #[test]
176 fn streaming_matches_oneshot() {
177 let data: Vec<u8> = (0..=255u8).cycle().take(1000).collect();
178 for split in [1usize, 63, 64, 65, 127, 500] {
179 let mut h = Sha1::new();
180 for chunk in data.chunks(split) {
181 h.update(chunk);
182 }
183 assert_eq!(h.finalize(), digest(&data), "split={split}");
184 }
185 }
186}