1#![allow(clippy::module_name_repetitions)]
18
19pub const DIGEST_BYTES: usize = 32;
21
22const K: [u32; 64] = [
25 0x428a_2f98,
26 0x7137_4491,
27 0xb5c0_fbcf,
28 0xe9b5_dba5,
29 0x3956_c25b,
30 0x59f1_11f1,
31 0x923f_82a4,
32 0xab1c_5ed5,
33 0xd807_aa98,
34 0x1283_5b01,
35 0x2431_85be,
36 0x550c_7dc3,
37 0x72be_5d74,
38 0x80de_b1fe,
39 0x9bdc_06a7,
40 0xc19b_f174,
41 0xe49b_69c1,
42 0xefbe_4786,
43 0x0fc1_9dc6,
44 0x240c_a1cc,
45 0x2de9_2c6f,
46 0x4a74_84aa,
47 0x5cb0_a9dc,
48 0x76f9_88da,
49 0x983e_5152,
50 0xa831_c66d,
51 0xb003_27c8,
52 0xbf59_7fc7,
53 0xc6e0_0bf3,
54 0xd5a7_9147,
55 0x06ca_6351,
56 0x1429_2967,
57 0x27b7_0a85,
58 0x2e1b_2138,
59 0x4d2c_6dfc,
60 0x5338_0d13,
61 0x650a_7354,
62 0x766a_0abb,
63 0x81c2_c92e,
64 0x9272_2c85,
65 0xa2bf_e8a1,
66 0xa81a_664b,
67 0xc24b_8b70,
68 0xc76c_51a3,
69 0xd192_e819,
70 0xd699_0624,
71 0xf40e_3585,
72 0x106a_a070,
73 0x19a4_c116,
74 0x1e37_6c08,
75 0x2748_774c,
76 0x34b0_bcb5,
77 0x391c_0cb3,
78 0x4ed8_aa4a,
79 0x5b9c_ca4f,
80 0x682e_6ff3,
81 0x748f_82ee,
82 0x78a5_636f,
83 0x84c8_7814,
84 0x8cc7_0208,
85 0x90be_fffa,
86 0xa450_6ceb,
87 0xbef9_a3f7,
88 0xc671_78f2,
89];
90
91const H_INIT: [u32; 8] = [
94 0x6a09_e667,
95 0xbb67_ae85,
96 0x3c6e_f372,
97 0xa54f_f53a,
98 0x510e_527f,
99 0x9b05_688c,
100 0x1f83_d9ab,
101 0x5be0_cd19,
102];
103
104#[derive(Clone)]
111pub struct Sha256 {
112 state: [u32; 8],
113 buffer: [u8; 64],
114 buffer_len: usize,
115 len_bits: u64,
116}
117
118impl Default for Sha256 {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124impl Sha256 {
125 #[must_use]
127 pub const fn new() -> Self {
128 Self {
129 state: H_INIT,
130 buffer: [0u8; 64],
131 buffer_len: 0,
132 len_bits: 0,
133 }
134 }
135
136 pub fn update(&mut self, data: &[u8]) {
139 self.len_bits = self.len_bits.wrapping_add((data.len() as u64) * 8);
143
144 let mut input = data;
145
146 if self.buffer_len > 0 {
148 let take = (64 - self.buffer_len).min(input.len());
149 self.buffer[self.buffer_len..self.buffer_len + take].copy_from_slice(&input[..take]);
150 self.buffer_len += take;
151 input = &input[take..];
152 if self.buffer_len == 64 {
153 let block = self.buffer;
154 Self::compress(&mut self.state, &block);
155 self.buffer_len = 0;
156 }
157 }
158
159 while let Some((block, rest)) = input.split_first_chunk::<64>() {
164 Self::compress(&mut self.state, block);
165 input = rest;
166 }
167
168 if !input.is_empty() {
170 self.buffer[..input.len()].copy_from_slice(input);
171 self.buffer_len = input.len();
172 }
173 }
174
175 #[must_use]
177 pub fn finalize(mut self) -> [u8; DIGEST_BYTES] {
178 let len_bits = self.len_bits;
182
183 let mut buf = self.buffer;
184 let mut buf_len = self.buffer_len;
185 buf[buf_len] = 0x80;
186 buf_len += 1;
187
188 if buf_len > 56 {
189 for byte in &mut buf[buf_len..64] {
192 *byte = 0;
193 }
194 Self::compress(&mut self.state, &buf);
195 buf = [0u8; 64];
196 buf_len = 0;
197 }
198
199 for byte in &mut buf[buf_len..56] {
200 *byte = 0;
201 }
202 buf[56..64].copy_from_slice(&len_bits.to_be_bytes());
203 Self::compress(&mut self.state, &buf);
204
205 let mut digest = [0u8; 32];
206 for (i, word) in self.state.iter().enumerate() {
207 digest[i * 4..i * 4 + 4].copy_from_slice(&word.to_be_bytes());
208 }
209 digest
210 }
211
212 #[allow(clippy::many_single_char_names)]
220 fn compress(state: &mut [u32; 8], block: &[u8; 64]) {
221 let mut w = [0u32; 64];
222 for i in 0..16 {
223 let off = i * 4;
224 w[i] = u32::from_be_bytes([block[off], block[off + 1], block[off + 2], block[off + 3]]);
225 }
226 for i in 16..64 {
227 let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
228 let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
229 w[i] = w[i - 16]
230 .wrapping_add(s0)
231 .wrapping_add(w[i - 7])
232 .wrapping_add(s1);
233 }
234
235 let mut a = state[0];
236 let mut b = state[1];
237 let mut c = state[2];
238 let mut d = state[3];
239 let mut e = state[4];
240 let mut f = state[5];
241 let mut g = state[6];
242 let mut h = state[7];
243
244 for i in 0..64 {
245 let big_sigma1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
246 let ch = (e & f) ^ ((!e) & g);
247 let t1 = h
248 .wrapping_add(big_sigma1)
249 .wrapping_add(ch)
250 .wrapping_add(K[i])
251 .wrapping_add(w[i]);
252 let big_sigma0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
253 let maj = (a & b) ^ (a & c) ^ (b & c);
254 let t2 = big_sigma0.wrapping_add(maj);
255
256 h = g;
257 g = f;
258 f = e;
259 e = d.wrapping_add(t1);
260 d = c;
261 c = b;
262 b = a;
263 a = t1.wrapping_add(t2);
264 }
265
266 state[0] = state[0].wrapping_add(a);
267 state[1] = state[1].wrapping_add(b);
268 state[2] = state[2].wrapping_add(c);
269 state[3] = state[3].wrapping_add(d);
270 state[4] = state[4].wrapping_add(e);
271 state[5] = state[5].wrapping_add(f);
272 state[6] = state[6].wrapping_add(g);
273 state[7] = state[7].wrapping_add(h);
274 }
275}
276
277#[must_use]
280pub fn sha256(bytes: &[u8]) -> [u8; DIGEST_BYTES] {
281 let mut hasher = Sha256::new();
282 hasher.update(bytes);
283 hasher.finalize()
284}
285
286#[must_use]
289pub fn format_digest(digest: &[u8; DIGEST_BYTES]) -> [u8; 71] {
290 const HEX: &[u8; 16] = b"0123456789abcdef";
291 let mut out = [0u8; 71];
292 out[..7].copy_from_slice(b"sha256:");
293 for (i, byte) in digest.iter().enumerate() {
294 out[7 + i * 2] = HEX[(byte >> 4) as usize];
295 out[7 + i * 2 + 1] = HEX[(byte & 0x0F) as usize];
296 }
297 out
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303
304 #[test]
307 fn sha256_empty_string() {
308 let digest = sha256(b"");
309 let expected = [
310 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
311 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
312 0x78, 0x52, 0xb8, 0x55,
313 ];
314 assert_eq!(digest, expected);
315 }
316
317 #[test]
320 fn sha256_abc() {
321 let digest = sha256(b"abc");
322 let expected = [
323 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
324 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
325 0xf2, 0x00, 0x15, 0xad,
326 ];
327 assert_eq!(digest, expected);
328 }
329
330 #[test]
335 fn sha256_56_byte_input_exercises_padding() {
336 let msg: &[u8] = b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
337 assert_eq!(msg.len(), 56);
338 let digest = sha256(msg);
339 let expected = [
340 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e,
341 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4,
342 0x19, 0xdb, 0x06, 0xc1,
343 ];
344 assert_eq!(digest, expected);
345 }
346
347 #[test]
351 fn sha256_thousand_a() {
352 let mut data = [0u8; 1000];
353 data.fill(b'a');
354 let digest = sha256(&data);
355 let expected = [
357 0x41, 0xed, 0xec, 0xe4, 0x2d, 0x63, 0xe8, 0xd9, 0xbf, 0x51, 0x5a, 0x9b, 0xa6, 0x93,
358 0x2e, 0x1c, 0x20, 0xcb, 0xc9, 0xf5, 0xa5, 0xd1, 0x34, 0x64, 0x5a, 0xdb, 0x5d, 0xb1,
359 0xb9, 0x73, 0x7e, 0xa3,
360 ];
361 assert_eq!(digest, expected);
362 }
363
364 #[test]
367 fn streaming_matches_one_shot() {
368 let msg: &[u8] = b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
369 let one_shot = sha256(msg);
370 let mut h = Sha256::new();
371 for byte in msg {
372 h.update(core::slice::from_ref(byte));
373 }
374 let streamed = h.finalize();
375 assert_eq!(streamed, one_shot);
376 }
377
378 #[test]
380 fn streaming_with_split_chunks_matches() {
381 let msg: &[u8] = b"the quick brown fox jumps over the lazy dog the quick brown fox jumps over the lazy dog";
382 let one_shot = sha256(msg);
383 let mut h = Sha256::new();
384 h.update(&msg[..3]);
385 h.update(&msg[3..30]);
386 h.update(&msg[30..64]);
387 h.update(&msg[64..]);
388 assert_eq!(h.finalize(), one_shot);
389 }
390
391 #[test]
393 fn format_digest_emits_sha256_prefix() {
394 let digest = sha256(b"");
395 let formatted = format_digest(&digest);
396 assert!(formatted.starts_with(b"sha256:"));
397 assert_eq!(formatted.len(), 71);
398 assert_eq!(
400 &formatted[7..],
401 b"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
402 );
403 }
404}