1use alloc::vec::Vec;
2
3#[derive(Copy, Clone)]
4struct Sha256 {
5 h: [u32; 8],
6}
7
8impl Sha256 {
9 const K: [u32; 64] = [
10 0x428a_2f98,
11 0x7137_4491,
12 0xb5c0_fbcf,
13 0xe9b5_dba5,
14 0x3956_c25b,
15 0x59f1_11f1,
16 0x923f_82a4,
17 0xab1c_5ed5,
18 0xd807_aa98,
19 0x1283_5b01,
20 0x2431_85be,
21 0x550c_7dc3,
22 0x72be_5d74,
23 0x80de_b1fe,
24 0x9bdc_06a7,
25 0xc19b_f174,
26 0xe49b_69c1,
27 0xefbe_4786,
28 0x0fc1_9dc6,
29 0x240c_a1cc,
30 0x2de9_2c6f,
31 0x4a74_84aa,
32 0x5cb0_a9dc,
33 0x76f9_88da,
34 0x983e_5152,
35 0xa831_c66d,
36 0xb003_27c8,
37 0xbf59_7fc7,
38 0xc6e0_0bf3,
39 0xd5a7_9147,
40 0x06ca_6351,
41 0x1429_2967,
42 0x27b7_0a85,
43 0x2e1b_2138,
44 0x4d2c_6dfc,
45 0x5338_0d13,
46 0x650a_7354,
47 0x766a_0abb,
48 0x81c2_c92e,
49 0x9272_2c85,
50 0xa2bf_e8a1,
51 0xa81a_664b,
52 0xc24b_8b70,
53 0xc76c_51a3,
54 0xd192_e819,
55 0xd699_0624,
56 0xf40e_3585,
57 0x106a_a070,
58 0x19a4_c116,
59 0x1e37_6c08,
60 0x2748_774c,
61 0x34b0_bcb5,
62 0x391c_0cb3,
63 0x4ed8_aa4a,
64 0x5b9c_ca4f,
65 0x682e_6ff3,
66 0x748f_82ee,
67 0x78a5_636f,
68 0x84c8_7814,
69 0x8cc7_0208,
70 0x90be_fffa,
71 0xa450_6ceb,
72 0xbef9_a3f7,
73 0xc671_78f2,
74 ];
75
76 const fn padding_value_at_idx(input_length: usize, idx: usize) -> u8 {
77 let padding_length = Self::padding_length_for_input_length(input_length);
78 if idx == 0 {
79 0b1000_0000
80 } else if idx <= padding_length - 9 {
81 0
82 } else {
83 let offset = idx + 8 - padding_length;
84 let bytes = (input_length as u64).wrapping_mul(8).to_be_bytes();
85 bytes[offset]
86 }
87 }
88
89 const fn apply_chunk(self, chunk: [u8; 64]) -> Self {
90 let mut w = [0_u32; 64];
91 {
92 let mut i = 0;
93 while i < 64 {
94 if i < 16 {
95 w[i] = u32::from_be_bytes([
96 chunk[4 * i],
97 chunk[4 * i + 1],
98 chunk[4 * i + 2],
99 chunk[4 * i + 3],
100 ]);
101 } else {
102 let s0 =
103 w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
104 let s1 =
105 w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
106 w[i] = w[i - 16]
107 .wrapping_add(s0)
108 .wrapping_add(w[i - 7])
109 .wrapping_add(s1);
110 }
111 i += 1;
112 }
113 }
114
115 let mut h = self.h;
116
117 let mut i = 0;
118 while i < 64 {
119 let current_w = w[i];
120 let s1 = h[4].rotate_right(6) ^ h[4].rotate_right(11) ^ h[4].rotate_right(25);
121 let ch = (h[4] & h[5]) ^ ((!h[4]) & h[6]);
122 let temp1 = h[7]
123 .wrapping_add(s1)
124 .wrapping_add(ch)
125 .wrapping_add(Self::K[i])
126 .wrapping_add(current_w);
127 let s0 = h[0].rotate_right(2) ^ h[0].rotate_right(13) ^ h[0].rotate_right(22);
128 let maj = (h[0] & h[1]) ^ (h[0] & h[2]) ^ (h[1] & h[2]);
129 let temp2 = s0.wrapping_add(maj);
130
131 h[7] = h[6];
132 h[6] = h[5];
133 h[5] = h[4];
134 h[4] = h[3].wrapping_add(temp1);
135 h[3] = h[2];
136 h[2] = h[1];
137 h[1] = h[0];
138 h[0] = temp1.wrapping_add(temp2);
139
140 i += 1;
141 }
142
143 Self {
144 h: [
145 self.h[0].wrapping_add(h[0]),
146 self.h[1].wrapping_add(h[1]),
147 self.h[2].wrapping_add(h[2]),
148 self.h[3].wrapping_add(h[3]),
149 self.h[4].wrapping_add(h[4]),
150 self.h[5].wrapping_add(h[5]),
151 self.h[6].wrapping_add(h[6]),
152 self.h[7].wrapping_add(h[7]),
153 ],
154 }
155 }
156
157 const fn get_num_chunks(data_length: usize) -> usize {
158 (data_length + Self::padding_length_for_input_length(data_length)) / 64
159 }
160
161 const fn get_chunk(data: &[u8], data_len: usize, chunk_idx: usize) -> [u8; 64] {
162 let mut chunk = [0; 64];
163 let mut i = 0;
164 while i < 64 {
165 if chunk_idx * 64 + i < data.len() {
166 chunk[i] = data[chunk_idx * 64 + i];
167 } else {
168 let padding_len = Self::padding_length_for_input_length(data_len);
169 let index_into_padding = chunk_idx * 64 + i - data.len();
170 if index_into_padding < padding_len {
171 chunk[i] = Self::padding_value_at_idx(data_len, index_into_padding);
172 } else {
173 panic!("unreachable: internal error");
174 }
175 }
176 i += 1;
177 }
178 chunk
179 }
180
181 const fn hash_from_data(self) -> [u8; 32] {
182 let h = [
183 self.h[0].to_be_bytes(),
184 self.h[1].to_be_bytes(),
185 self.h[2].to_be_bytes(),
186 self.h[3].to_be_bytes(),
187 self.h[4].to_be_bytes(),
188 self.h[5].to_be_bytes(),
189 self.h[6].to_be_bytes(),
190 self.h[7].to_be_bytes(),
191 ];
192 [
193 h[0][0], h[0][1], h[0][2], h[0][3], h[1][0], h[1][1], h[1][2], h[1][3], h[2][0],
194 h[2][1], h[2][2], h[2][3], h[3][0], h[3][1], h[3][2], h[3][3], h[4][0], h[4][1],
195 h[4][2], h[4][3], h[5][0], h[5][1], h[5][2], h[5][3], h[6][0], h[6][1], h[6][2],
196 h[6][3], h[7][0], h[7][1], h[7][2], h[7][3],
197 ]
198 }
199
200 const fn padding_length_for_input_length(input_length: usize) -> usize {
201 if input_length % 64 <= 55 {
202 64 - input_length % 64
203 } else {
204 128 - input_length % 64
205 }
206 }
207
208 const fn new() -> Self {
209 Self {
210 h: [
211 0x6a09_e667,
212 0xbb67_ae85,
213 0x3c6e_f372,
214 0xa54f_f53a,
215 0x510e_527f,
216 0x9b05_688c,
217 0x1f83_d9ab,
218 0x5be0_cd19,
219 ],
220 }
221 }
222
223 const fn from(hash: [u8; 32]) -> Self {
224 Self {
225 h: [
226 u32::from_be_bytes([hash[0], hash[1], hash[2], hash[3]]),
227 u32::from_be_bytes([hash[4], hash[5], hash[6], hash[7]]),
228 u32::from_be_bytes([hash[8], hash[9], hash[10], hash[11]]),
229 u32::from_be_bytes([hash[12], hash[13], hash[14], hash[15]]),
230 u32::from_be_bytes([hash[16], hash[17], hash[18], hash[19]]),
231 u32::from_be_bytes([hash[20], hash[21], hash[22], hash[23]]),
232 u32::from_be_bytes([hash[24], hash[25], hash[26], hash[27]]),
233 u32::from_be_bytes([hash[28], hash[29], hash[30], hash[31]]),
234 ],
235 }
236 }
237}
238
239#[must_use]
271pub fn padding_for_length(input_length: usize) -> Vec<u8> {
272 let padding_length = padding_length_for_input_length(input_length);
273 let mut result = Vec::with_capacity(padding_length);
274 for i in 0..padding_length {
275 result.push(Sha256::padding_value_at_idx(input_length, i));
276 }
277 result
278}
279
280#[must_use]
306pub const fn padding_length_for_input_length(input_length: usize) -> usize {
307 Sha256::padding_length_for_input_length(input_length)
308}
309
310#[must_use]
334pub const fn compute_hash(input: &[u8]) -> [u8; 32] {
335 let num_chunks = Sha256::get_num_chunks(input.len());
336 let mut sha256 = Sha256::new();
337 let mut i = 0;
338 while i < num_chunks {
339 let chunk = Sha256::get_chunk(input, input.len(), i);
340 sha256 = sha256.apply_chunk(chunk);
341 i += 1;
342 }
343 sha256.hash_from_data()
344}
345
346#[must_use]
390pub const fn extend_hash(hash: [u8; 32], length: usize, additional_input: &[u8]) -> [u8; 32] {
391 let len = length + padding_length_for_input_length(length) + additional_input.len();
392 let num_chunks = (additional_input.len() + padding_length_for_input_length(len)) / 64;
393 let mut sha256 = Sha256::from(hash);
394 let mut i = 0;
395 while i < num_chunks {
396 let chunk = Sha256::get_chunk(additional_input, len, i);
397 sha256 = sha256.apply_chunk(chunk);
398 i += 1;
399 }
400 sha256.hash_from_data()
401}
402
403#[cfg(test)]
404mod tests {
405 use crate::sha256;
406 use alloc::vec::Vec;
407
408 #[test]
409 fn empty_hash() {
410 assert_eq!(
411 sha256::compute_hash(&[]),
412 [
413 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
414 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
415 0x78, 0x52, 0xb8, 0x55
416 ]
417 );
418 }
419
420 #[test]
421 fn a_test() {
422 assert_eq!(
423 sha256::compute_hash(b"a"),
424 [
425 0xca, 0x97, 0x81, 0x12, 0xca, 0x1b, 0xbd, 0xca, 0xfa, 0xc2, 0x31, 0xb3, 0x9a, 0x23,
426 0xdc, 0x4d, 0xa7, 0x86, 0xef, 0xf8, 0x14, 0x7c, 0x4e, 0x72, 0xb9, 0x80, 0x77, 0x85,
427 0xaf, 0xee, 0x48, 0xbb
428 ]
429 );
430 }
431
432 #[test]
433 fn quick_brown_fox_test() {
434 let s = b"The quick brown fox jumps over the lazy dog";
435 assert_eq!(
436 sha256::compute_hash(s),
437 [
438 0xd7, 0xa8, 0xfb, 0xb3, 0x07, 0xd7, 0x80, 0x94, 0x69, 0xca, 0x9a, 0xbc, 0xb0, 0x08,
439 0x2e, 0x4f, 0x8d, 0x56, 0x51, 0xe4, 0x6d, 0x3c, 0xdb, 0x76, 0x2d, 0x02, 0xd0, 0xbf,
440 0x37, 0xc9, 0xe5, 0x92
441 ]
442 );
443 }
444
445 #[test]
446 fn quick_brown_fox_test_2() {
447 let s = b"The quick brown fox jumps over the lazy cog";
448 assert_eq!(
449 sha256::compute_hash(s),
450 [
451 0xe4, 0xc4, 0xd8, 0xf3, 0xbf, 0x76, 0xb6, 0x92, 0xde, 0x79, 0x1a, 0x17, 0x3e, 0x05,
452 0x32, 0x11, 0x50, 0xf7, 0xa3, 0x45, 0xb4, 0x64, 0x84, 0xfe, 0x42, 0x7f, 0x6a, 0xcc,
453 0x7e, 0xcc, 0x81, 0xbe
454 ]
455 );
456 }
457
458 #[test]
459 fn abc_test() {
460 let s = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
461 abcdefghijklmnopqrstuvwxyz0123456789";
462 assert_eq!(
463 sha256::compute_hash(s),
464 [
465 0xdb, 0x4b, 0xfc, 0xbd, 0x4d, 0xa0, 0xcd, 0x85, 0xa6, 0x0c, 0x3c, 0x37, 0xd3, 0xfb,
466 0xd8, 0x80, 0x5c, 0x77, 0xf1, 0x5f, 0xc6, 0xb1, 0xfd, 0xfe, 0x61, 0x4e, 0xe0, 0xa7,
467 0xc8, 0xfd, 0xb4, 0xc0
468 ]
469 );
470 }
471
472 #[test]
473 fn long_test() {
474 assert_eq!(
475 sha256::compute_hash(&alloc::vec![b'a'; 1_000_000].into_boxed_slice()),
476 [
477 0xcd, 0xc7, 0x6e, 0x5c, 0x99, 0x14, 0xfb, 0x92, 0x81, 0xa1, 0xc7, 0xe2, 0x84, 0xd7,
478 0x3e, 0x67, 0xf1, 0x80, 0x9a, 0x48, 0xa4, 0x97, 0x20, 0x0e, 0x04, 0x6d, 0x39, 0xcc,
479 0xc7, 0x11, 0x2c, 0xd0
480 ]
481 );
482 }
483
484 #[test]
485 fn padding_length_tests() {
486 assert_eq!(sha256::padding_length_for_input_length(0), 64);
487 assert_eq!(sha256::padding_length_for_input_length(1), 63);
488 assert_eq!(sha256::padding_length_for_input_length(2), 62);
489 assert_eq!(sha256::padding_length_for_input_length(3), 61);
490 assert_eq!(sha256::padding_length_for_input_length(4), 60);
491
492 assert_eq!(sha256::padding_length_for_input_length(50), 14);
493 assert_eq!(sha256::padding_length_for_input_length(54), 10);
494 assert_eq!(sha256::padding_length_for_input_length(55), 9);
495 assert_eq!(sha256::padding_length_for_input_length(56), 64 + 8);
496 assert_eq!(sha256::padding_length_for_input_length(57), 64 + 7);
497 assert_eq!(sha256::padding_length_for_input_length(62), 64 + 2);
498 assert_eq!(sha256::padding_length_for_input_length(63), 64 + 1);
499 assert_eq!(sha256::padding_length_for_input_length(64), 64);
500 assert_eq!(sha256::padding_length_for_input_length(128), 64);
501 assert_eq!(sha256::padding_length_for_input_length(64 * 100_000), 64);
502 }
503
504 #[test]
505 fn test_hash_ext() {
506 let secret = b"count=10&lat=37.351&user_id=1&\
507 long=-119.827&waffle=eggo";
508 let hash = sha256::compute_hash(secret);
509
510 let appended_str = b"&waffle=liege";
511 let combined_hash = sha256::extend_hash(hash, secret.len(), appended_str);
512
513 let mut concatenation = Vec::<u8>::new();
514 concatenation.extend_from_slice(secret);
515 let padding = sha256::padding_for_length(secret.len());
516 concatenation.extend_from_slice(padding.as_slice());
517 concatenation.extend_from_slice(appended_str);
518 assert_eq!(
519 combined_hash,
520 sha256::compute_hash(concatenation.as_slice())
521 );
522 }
523}