1const INITIAL: [u32; 8] = [
18 0x6a09_e667,
19 0xbb67_ae85,
20 0x3c6e_f372,
21 0xa54f_f53a,
22 0x510e_527f,
23 0x9b05_688c,
24 0x1f83_d9ab,
25 0x5be0_cd19,
26];
27
28const ROUND: [u32; 64] = [
31 0x428a_2f98,
32 0x7137_4491,
33 0xb5c0_fbcf,
34 0xe9b5_dba5,
35 0x3956_c25b,
36 0x59f1_11f1,
37 0x923f_82a4,
38 0xab1c_5ed5,
39 0xd807_aa98,
40 0x1283_5b01,
41 0x2431_85be,
42 0x550c_7dc3,
43 0x72be_5d74,
44 0x80de_b1fe,
45 0x9bdc_06a7,
46 0xc19b_f174,
47 0xe49b_69c1,
48 0xefbe_4786,
49 0x0fc1_9dc6,
50 0x240c_a1cc,
51 0x2de9_2c6f,
52 0x4a74_84aa,
53 0x5cb0_a9dc,
54 0x76f9_88da,
55 0x983e_5152,
56 0xa831_c66d,
57 0xb003_27c8,
58 0xbf59_7fc7,
59 0xc6e0_0bf3,
60 0xd5a7_9147,
61 0x06ca_6351,
62 0x1429_2967,
63 0x27b7_0a85,
64 0x2e1b_2138,
65 0x4d2c_6dfc,
66 0x5338_0d13,
67 0x650a_7354,
68 0x766a_0abb,
69 0x81c2_c92e,
70 0x9272_2c85,
71 0xa2bf_e8a1,
72 0xa81a_664b,
73 0xc24b_8b70,
74 0xc76c_51a3,
75 0xd192_e819,
76 0xd699_0624,
77 0xf40e_3585,
78 0x106a_a070,
79 0x19a4_c116,
80 0x1e37_6c08,
81 0x2748_774c,
82 0x34b0_bcb5,
83 0x391c_0cb3,
84 0x4ed8_aa4a,
85 0x5b9c_ca4f,
86 0x682e_6ff3,
87 0x748f_82ee,
88 0x78a5_636f,
89 0x84c8_7814,
90 0x8cc7_0208,
91 0x90be_fffa,
92 0xa450_6ceb,
93 0xbef9_a3f7,
94 0xc671_78f2,
95];
96
97pub fn hex(message: &[u8]) -> String {
103 let mut state = INITIAL;
104 let mut blocks = message.chunks_exact(64);
105 for block in &mut blocks {
106 compress(&mut state, block);
107 }
108
109 let rest = blocks.remainder();
113 let mut tail = [0u8; 128];
114 tail[..rest.len()].copy_from_slice(rest);
115 tail[rest.len()] = 0x80;
116 let width = if rest.len() + 9 > 64 { 128 } else { 64 };
117 let bits = (message.len() as u64).wrapping_mul(8);
119 tail[width - 8..width].copy_from_slice(&bits.to_be_bytes());
120 for block in tail[..width].chunks_exact(64) {
121 compress(&mut state, block);
122 }
123
124 let mut out = String::with_capacity(64);
125 for word in state {
126 for byte in word.to_be_bytes() {
127 out.push(char::from_digit(u32::from(byte >> 4), 16).unwrap_or('0'));
128 out.push(char::from_digit(u32::from(byte & 0xf), 16).unwrap_or('0'));
129 }
130 }
131 out
132}
133
134fn compress(state: &mut [u32; 8], block: &[u8]) {
142 assert_eq!(block.len(), 64, "sha256 compresses sixty four bytes at a time");
143
144 let mut words = [0u32; 64];
147 for (word, bytes) in words.iter_mut().zip(block.chunks_exact(4)) {
148 let quad: [u8; 4] = bytes.try_into().expect("four bytes out of a chunk of four");
149 *word = u32::from_be_bytes(quad);
150 }
151 for index in 16..64 {
152 let first = words[index - 15];
153 let second = words[index - 2];
154 let low = first.rotate_right(7) ^ first.rotate_right(18) ^ (first >> 3);
155 let high = second.rotate_right(17) ^ second.rotate_right(19) ^ (second >> 10);
156 words[index] =
157 words[index - 16].wrapping_add(low).wrapping_add(words[index - 7]).wrapping_add(high);
158 }
159
160 let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h] = *state;
161 for (constant, word) in ROUND.iter().zip(words.iter()) {
162 let sigma = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
163 let choose = (e & f) ^ (!e & g);
164 let one =
165 h.wrapping_add(sigma).wrapping_add(choose).wrapping_add(*constant).wrapping_add(*word);
166 let sum = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
167 let majority = (a & b) ^ (a & c) ^ (b & c);
168 let two = sum.wrapping_add(majority);
169 h = g;
170 g = f;
171 f = e;
172 e = d.wrapping_add(one);
173 d = c;
174 c = b;
175 b = a;
176 a = one.wrapping_add(two);
177 }
178
179 for (slot, round) in state.iter_mut().zip([a, b, c, d, e, f, g, h]) {
180 *slot = slot.wrapping_add(round);
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::hex;
187
188 #[test]
189 fn the_published_vectors() {
190 assert_eq!(hex(b""), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
194 assert_eq!(hex(b"abc"), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
195 assert_eq!(
196 hex(b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"),
197 "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
198 );
199 }
200
201 #[test]
202 fn a_message_that_fills_its_last_block_takes_a_second_one() {
203 assert_eq!(
207 hex(&b"a".repeat(55)),
208 "9f4390f8d30c2dd92ec9f095b65e2b9ae9b0a925a5258e241c9f1e910f734318"
209 );
210 assert_eq!(
211 hex(&b"a".repeat(56)),
212 "b35439a4ac6f0948b6d6f9e3c6af0f5f590ce20f1bde7090ef7970686ec6738a"
213 );
214 assert_eq!(
215 hex(&b"a".repeat(64)),
216 "ffe054fe7ae0cb6dc65c3af9b61d5209f439851db43d0ba5997337df154668eb"
217 );
218 }
219
220 #[test]
221 fn a_long_message_is_hashed_block_by_block() {
222 assert_eq!(
225 hex(&b"a".repeat(1_000_000)),
226 "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"
227 );
228 }
229}