1const K: [u32; 64] = [
4 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
5 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
6 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
7 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
8 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
9 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
10 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
11 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
12];
13
14#[derive(Clone, Copy)]
20pub struct ConstSha256 {
21 state: [u32; 8],
22 block: [u8; 64],
23 block_len: usize,
24 total_len: u64,
25}
26
27impl ConstSha256 {
28 pub const fn new() -> Self {
30 Self {
31 state: [
32 0x6a09e667u32,
33 0xbb67ae85,
34 0x3c6ef372,
35 0xa54ff53a,
36 0x510e527f,
37 0x9b05688c,
38 0x1f83d9ab,
39 0x5be0cd19,
40 ],
41 block: [0; 64],
42 block_len: 0,
43 total_len: 0,
44 }
45 }
46
47 pub const fn update(mut self, input: &[u8]) -> Self {
49 let mut i = 0;
50 while i < input.len() {
51 self.block[self.block_len] = input[i];
52 self.block_len += 1;
53 self.total_len = self.total_len.wrapping_add(1);
54 i += 1;
55 if self.block_len == 64 {
56 self.state = compress(self.state, self.block);
57 self.block = [0; 64];
58 self.block_len = 0;
59 }
60 }
61 self
62 }
63
64 pub const fn finalize(mut self) -> [u8; 32] {
66 self.block[self.block_len] = 0x80;
67 if self.block_len >= 56 {
68 self.state = compress(self.state, self.block);
69 self.block = [0; 64];
70 }
71 write_len(&mut self.block, self.total_len.wrapping_mul(8));
72 self.state = compress(self.state, self.block);
73 state_to_bytes(self.state)
74 }
75}
76
77impl Default for ConstSha256 {
78 fn default() -> Self {
79 Self::new()
80 }
81}
82
83pub const fn sha256(data: &[u8]) -> [u8; 32] {
85 sha256_concat(data, &[])
86}
87
88pub const fn sha256_concat(left: &[u8], right: &[u8]) -> [u8; 32] {
90 let mut state = [
91 0x6a09e667u32,
92 0xbb67ae85,
93 0x3c6ef372,
94 0xa54ff53a,
95 0x510e527f,
96 0x9b05688c,
97 0x1f83d9ab,
98 0x5be0cd19,
99 ];
100 let len = left.len() + right.len();
101 let full_blocks = len / 64;
102 let mut block_index = 0;
103 while block_index < full_blocks {
104 let mut block = [0u8; 64];
105 let mut i = 0;
106 while i < 64 {
107 block[i] = concat_byte(left, right, block_index * 64 + i);
108 i += 1;
109 }
110 state = compress(state, block);
111 block_index += 1;
112 }
113
114 let rem = len % 64;
115 let mut block = [0u8; 64];
116 let mut i = 0;
117 while i < rem {
118 block[i] = concat_byte(left, right, full_blocks * 64 + i);
119 i += 1;
120 }
121 block[rem] = 0x80;
122 let bit_len = (len as u64).wrapping_mul(8);
123 if rem <= 55 {
124 write_len(&mut block, bit_len);
125 state = compress(state, block);
126 } else {
127 state = compress(state, block);
128 let mut last = [0u8; 64];
129 write_len(&mut last, bit_len);
130 state = compress(state, last);
131 }
132
133 state_to_bytes(state)
134}
135
136pub const fn sha256_prefix8(left: &[u8], right: &[u8]) -> [u8; 8] {
138 let hash = sha256_concat(left, right);
139 [
140 hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7],
141 ]
142}
143
144const fn concat_byte(left: &[u8], right: &[u8], index: usize) -> u8 {
145 if index < left.len() {
146 left[index]
147 } else {
148 right[index - left.len()]
149 }
150}
151
152const fn write_len(block: &mut [u8; 64], bit_len: u64) {
153 let bytes = bit_len.to_be_bytes();
154 let mut i = 0;
155 while i < 8 {
156 block[56 + i] = bytes[i];
157 i += 1;
158 }
159}
160
161const fn compress(mut state: [u32; 8], block: [u8; 64]) -> [u32; 8] {
162 let mut w = [0u32; 64];
163 let mut i = 0;
164 while i < 16 {
165 let j = i * 4;
166 w[i] = u32::from_be_bytes([block[j], block[j + 1], block[j + 2], block[j + 3]]);
167 i += 1;
168 }
169 while i < 64 {
170 let s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >> 3);
171 let s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >> 10);
172 w[i] = w[i - 16]
173 .wrapping_add(s0)
174 .wrapping_add(w[i - 7])
175 .wrapping_add(s1);
176 i += 1;
177 }
178
179 let mut a = state[0];
180 let mut b = state[1];
181 let mut c = state[2];
182 let mut d = state[3];
183 let mut e = state[4];
184 let mut f = state[5];
185 let mut g = state[6];
186 let mut h = state[7];
187 i = 0;
188 while i < 64 {
189 let s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
190 let ch = (e & f) ^ ((!e) & g);
191 let temp1 = h
192 .wrapping_add(s1)
193 .wrapping_add(ch)
194 .wrapping_add(K[i])
195 .wrapping_add(w[i]);
196 let s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
197 let maj = (a & b) ^ (a & c) ^ (b & c);
198 let temp2 = s0.wrapping_add(maj);
199 h = g;
200 g = f;
201 f = e;
202 e = d.wrapping_add(temp1);
203 d = c;
204 c = b;
205 b = a;
206 a = temp1.wrapping_add(temp2);
207 i += 1;
208 }
209
210 state[0] = state[0].wrapping_add(a);
211 state[1] = state[1].wrapping_add(b);
212 state[2] = state[2].wrapping_add(c);
213 state[3] = state[3].wrapping_add(d);
214 state[4] = state[4].wrapping_add(e);
215 state[5] = state[5].wrapping_add(f);
216 state[6] = state[6].wrapping_add(g);
217 state[7] = state[7].wrapping_add(h);
218 state
219}
220
221const fn rotr(value: u32, by: u32) -> u32 {
222 value.rotate_right(by)
223}
224
225const fn state_to_bytes(state: [u32; 8]) -> [u8; 32] {
226 let mut out = [0u8; 32];
227 let mut i = 0;
228 while i < 8 {
229 let bytes = state[i].to_be_bytes();
230 out[i * 4] = bytes[0];
231 out[i * 4 + 1] = bytes[1];
232 out[i * 4 + 2] = bytes[2];
233 out[i * 4 + 3] = bytes[3];
234 i += 1;
235 }
236 out
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 #[test]
244 fn sha256_empty_matches_known_vector() {
245 assert_eq!(
246 sha256(b""),
247 [
248 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
249 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
250 0x78, 0x52, 0xb8, 0x55,
251 ]
252 );
253 }
254
255 #[test]
256 fn sha256_abc_matches_known_vector() {
257 assert_eq!(
258 sha256(b"abc"),
259 [
260 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
261 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
262 0xf2, 0x00, 0x15, 0xad,
263 ]
264 );
265 }
266
267 #[test]
268 fn concat_matches_single_slice() {
269 assert_eq!(
270 sha256_concat(b"global:", b"initialize"),
271 sha256(b"global:initialize")
272 );
273 }
274
275 #[test]
276 fn incremental_matches_single_slice_across_block_boundaries() {
277 let left = b"a canonical prefix longer than one short field";
278 let right = b" plus enough suffix bytes to cross the sixty-four-byte block boundary";
279 let incremental = ConstSha256::new().update(left).update(right).finalize();
280 assert_eq!(incremental, sha256_concat(left, right));
281 }
282
283 #[test]
284 fn incremental_empty_matches_one_shot_empty() {
285 assert_eq!(ConstSha256::new().finalize(), sha256(b""));
286 }
287}