1use super::{BlockHasher, Hasher};
4
5const K: [u64; 80] = [
6 0x428a2f98d728ae22,
7 0x7137449123ef65cd,
8 0xb5c0fbcfec4d3b2f,
9 0xe9b5dba58189dbbc,
10 0x3956c25bf348b538,
11 0x59f111f1b605d019,
12 0x923f82a4af194f9b,
13 0xab1c5ed5da6d8118,
14 0xd807aa98a3030242,
15 0x12835b0145706fbe,
16 0x243185be4ee4b28c,
17 0x550c7dc3d5ffb4e2,
18 0x72be5d74f27b896f,
19 0x80deb1fe3b1696b1,
20 0x9bdc06a725c71235,
21 0xc19bf174cf692694,
22 0xe49b69c19ef14ad2,
23 0xefbe4786384f25e3,
24 0x0fc19dc68b8cd5b5,
25 0x240ca1cc77ac9c65,
26 0x2de92c6f592b0275,
27 0x4a7484aa6ea6e483,
28 0x5cb0a9dcbd41fbd4,
29 0x76f988da831153b5,
30 0x983e5152ee66dfab,
31 0xa831c66d2db43210,
32 0xb00327c898fb213f,
33 0xbf597fc7beef0ee4,
34 0xc6e00bf33da88fc2,
35 0xd5a79147930aa725,
36 0x06ca6351e003826f,
37 0x142929670a0e6e70,
38 0x27b70a8546d22ffc,
39 0x2e1b21385c26c926,
40 0x4d2c6dfc5ac42aed,
41 0x53380d139d95b3df,
42 0x650a73548baf63de,
43 0x766a0abb3c77b2a8,
44 0x81c2c92e47edaee6,
45 0x92722c851482353b,
46 0xa2bfe8a14cf10364,
47 0xa81a664bbc423001,
48 0xc24b8b70d0f89791,
49 0xc76c51a30654be30,
50 0xd192e819d6ef5218,
51 0xd69906245565a910,
52 0xf40e35855771202a,
53 0x106aa07032bbd1b8,
54 0x19a4c116b8d2d0c8,
55 0x1e376c085141ab53,
56 0x2748774cdf8eeb99,
57 0x34b0bcb5e19b48a8,
58 0x391c0cb3c5c95a63,
59 0x4ed8aa4ae3418acb,
60 0x5b9cca4f7763e373,
61 0x682e6ff3d6b2b8a3,
62 0x748f82ee5defb2fc,
63 0x78a5636f43172f60,
64 0x84c87814a1f0ab72,
65 0x8cc702081a6439ec,
66 0x90befffa23631e28,
67 0xa4506cebde82bde9,
68 0xbef9a3f7b2c67915,
69 0xc67178f2e372532b,
70 0xca273eceea26619c,
71 0xd186b8c721c0c207,
72 0xeada7dd6cde0eb1e,
73 0xf57d4f7fee6ed178,
74 0x06f067aa72176fba,
75 0x0a637dc5a2c898a6,
76 0x113f9804bef90dae,
77 0x1b710b35131c471b,
78 0x28db77f523047d84,
79 0x32caab7b40c72493,
80 0x3c9ebe0a15c9bebc,
81 0x431d67c49c100d4c,
82 0x4cc5d4becb3e42b6,
83 0x597f299cfc657e2a,
84 0x5fcb6fab3ad6faec,
85 0x6c44198c4a475817,
86];
87
88const fn ch(x: u64, y: u64, z: u64) -> u64 {
89 (x & y) ^ (!x & z)
90}
91
92const fn maj(x: u64, y: u64, z: u64) -> u64 {
93 (x & y) ^ (x & z) ^ (y & z)
94}
95
96const fn sigma_0(x: u64) -> u64 {
97 x.rotate_right(28) ^ x.rotate_right(34) ^ x.rotate_right(39)
98}
99
100const fn sigma_1(x: u64) -> u64 {
101 x.rotate_right(14) ^ x.rotate_right(18) ^ x.rotate_right(41)
102}
103
104const fn little_sigma_0(x: u64) -> u64 {
105 x.rotate_right(1) ^ x.rotate_right(8) ^ x >> 7
106}
107
108const fn little_sigma_1(x: u64) -> u64 {
109 x.rotate_right(19) ^ x.rotate_right(61) ^ x >> 6
110}
111
112#[derive(Clone)]
113pub struct Sha512 {
114 state: [u64; Self::HASH_SIZE / size_of::<u64>()],
115 len: u128,
116}
117
118impl Hasher<{ Sha512::HASH_SIZE }> for Sha512 {
119 fn new() -> Self {
120 Self {
121 state: [
122 0x6a09e667f3bcc908,
123 0xbb67ae8584caa73b,
124 0x3c6ef372fe94f82b,
125 0xa54ff53a5f1d36f1,
126 0x510e527fade682d1,
127 0x9b05688c2b3e6c1f,
128 0x1f83d9abfb41bd6b,
129 0x5be0cd19137e2179,
130 ],
131 len: 0,
132 }
133 }
134
135 fn finish_with(mut self, msg: &[u8]) -> [u8; Self::HASH_SIZE] {
136 let chunks = msg.chunks_exact(Self::BLOCK_SIZE);
138 let remainder = chunks.remainder();
139
140 for block in chunks {
141 self.update_countless(&block.try_into().unwrap());
142 }
143
144 let mut last_block = [0; Self::BLOCK_SIZE];
145 last_block[..remainder.len()].copy_from_slice(remainder);
147
148 last_block[remainder.len()] = 0x80;
149
150 if remainder.len() < Self::BLOCK_SIZE - size_of::<u128>() {
152 last_block[Self::BLOCK_SIZE - size_of::<u128>()..]
153 .copy_from_slice(&((msg.len() as u128 + self.len) * 8).to_be_bytes());
154 } else {
155 self.update_countless(&last_block);
156 last_block = [0; Self::BLOCK_SIZE];
157 last_block[Self::BLOCK_SIZE - size_of::<u128>()..]
158 .copy_from_slice(&((msg.len() as u128 + self.len) * 8).to_be_bytes());
159 }
160
161 self.update(&last_block);
162 u64_array_to_be_bytes(&self.state)
163 }
164
165 fn finish(mut self) -> [u8; Self::HASH_SIZE] {
166 let mut padding = [0; Self::BLOCK_SIZE];
167 padding[0] = 0x80;
168 padding[Self::BLOCK_SIZE - size_of::<u128>()..]
169 .copy_from_slice(&(self.len * 8).to_be_bytes());
170 self.update_countless(&padding);
171
172 u64_array_to_be_bytes(&self.state)
173 }
174
175 fn hash(msg: &[u8]) -> [u8; Self::HASH_SIZE] {
176 let hasher = Self::new();
177 hasher.finish_with(msg)
178 }
179}
180
181impl BlockHasher<{ Self::HASH_SIZE }, { Self::BLOCK_SIZE }> for Sha512 {
182 fn update(&mut self, block: &[u8; Self::BLOCK_SIZE]) {
183 self.update_countless(block);
184 self.len += Self::BLOCK_SIZE as u128;
185 }
186}
187
188impl Sha512 {
189 pub const HASH_SIZE: usize = 64;
190 pub const BLOCK_SIZE: usize = 128;
191
192 fn update_countless(&mut self, block: &[u8; Self::BLOCK_SIZE]) {
193 let block = be_bytes_to_u64_array(block);
194 let mut message_schedule = [0; 80];
195 message_schedule[..block.len()].copy_from_slice(&block);
196
197 for i in 16..message_schedule.len() {
198 message_schedule[i] = little_sigma_1(message_schedule[i - 2])
199 .wrapping_add(message_schedule[i - 7])
200 .wrapping_add(little_sigma_0(message_schedule[i - 15]))
201 .wrapping_add(message_schedule[i - 16]);
202 }
203
204 let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h] = self.state;
205
206 for i in 0..80 {
207 let temp1 = h
208 .wrapping_add(sigma_1(e))
209 .wrapping_add(ch(e, f, g))
210 .wrapping_add(K[i])
211 .wrapping_add(message_schedule[i]);
212 let temp2 = sigma_0(a).wrapping_add(maj(a, b, c));
213 h = g;
214 g = f;
215 f = e;
216 e = d.wrapping_add(temp1);
217 d = c;
218 c = b;
219 b = a;
220 a = temp1.wrapping_add(temp2);
221 }
222 self.state[0] = self.state[0].wrapping_add(a);
223 self.state[1] = self.state[1].wrapping_add(b);
224 self.state[2] = self.state[2].wrapping_add(c);
225 self.state[3] = self.state[3].wrapping_add(d);
226 self.state[4] = self.state[4].wrapping_add(e);
227 self.state[5] = self.state[5].wrapping_add(f);
228 self.state[6] = self.state[6].wrapping_add(g);
229 self.state[7] = self.state[7].wrapping_add(h);
230 }
231}
232
233fn be_bytes_to_u64_array(bytes: &[u8; Sha512::BLOCK_SIZE]) -> [u64; Sha512::BLOCK_SIZE / 8] {
234 let mut as_u64 = [64; Sha512::BLOCK_SIZE / 8];
236 for (int, chunk) in as_u64.iter_mut().zip(bytes.chunks_exact(8)) {
238 *int = u64::from_be_bytes(chunk.try_into().unwrap());
240 }
241 as_u64
242}
243
244fn u64_array_to_be_bytes(
245 array: &[u64; Sha512::HASH_SIZE / size_of::<u64>()],
246) -> [u8; Sha512::HASH_SIZE] {
247 let mut as_bytes = [0u8; Sha512::HASH_SIZE];
249 for (chunk, int) in as_bytes.chunks_exact_mut(8).zip(array) {
251 chunk.copy_from_slice(&int.to_be_bytes())
252 }
253 as_bytes
254}
255
256#[cfg(test)]
257mod tests {
258
259 use super::*;
260
261 #[test]
262 fn hash_1() {
263 let msg = [0x6f, 0x8d, 0x58, 0xb7, 0xca, 0xb1, 0x88, 0x8c];
264 let digest = [
265 0xa3, 0x94, 0x1d, 0xef, 0x28, 0x03, 0xc8, 0xdf, 0xc0, 0x8f, 0x20, 0xc0, 0x6b, 0xa7,
266 0xe9, 0xa3, 0x32, 0xae, 0x0c, 0x67, 0xe4, 0x7a, 0xe5, 0x73, 0x65, 0xc2, 0x43, 0xef,
267 0x40, 0x05, 0x9b, 0x11, 0xbe, 0x22, 0xc9, 0x1d, 0xa6, 0xa8, 0x0c, 0x2c, 0xff, 0x07,
268 0x42, 0xa8, 0xf4, 0xbc, 0xd9, 0x41, 0xbd, 0xee, 0x0b, 0x86, 0x1e, 0xc8, 0x72, 0xb2,
269 0x15, 0x43, 0x3c, 0xe8, 0xdc, 0xf3, 0xc0, 0x31,
270 ];
271 assert_eq!(Sha512::hash(&msg), digest);
272 }
273
274 #[test]
275 fn hash_2() {
276 let msg = [
277 0x4f, 0x05, 0x60, 0x09, 0x50, 0x66, 0x4d, 0x51, 0x90, 0xa2, 0xeb, 0xc2, 0x9c, 0x9e,
278 0xdb, 0x89, 0xc2, 0x00, 0x79, 0xa4, 0xd3, 0xe6, 0xbc, 0x3b, 0x27, 0xd7, 0x5e, 0x34,
279 0xe2, 0xfa, 0x3d, 0x02, 0x76, 0x85, 0x02, 0xbd, 0x69, 0x79, 0x00, 0x78, 0x59, 0x8d,
280 0x5f, 0xcf, 0x3d, 0x67, 0x79, 0xbf, 0xed, 0x12, 0x84, 0xbb, 0xe5, 0xad, 0x72, 0xfb,
281 0x45, 0x60, 0x15, 0x18, 0x1d, 0x95, 0x87, 0xd6, 0xe8, 0x64, 0xc9, 0x40, 0x56, 0x4e,
282 0xaa, 0xfb, 0x4f, 0x2f, 0xea, 0xd4, 0x34, 0x6e, 0xa0, 0x9b, 0x68, 0x77, 0xd9, 0x34,
283 0x0f, 0x6b, 0x82, 0xeb, 0x15, 0x15, 0x88, 0x08, 0x72, 0x21, 0x3d, 0xa3, 0xad, 0x88,
284 0xfe, 0xba, 0x9f, 0x4f, 0x13, 0x81, 0x7a, 0x71, 0xd6, 0xf9, 0x0a, 0x1a, 0x17, 0xc4,
285 0x3a, 0x15, 0xc0, 0x38, 0xd9, 0x88, 0xb5, 0xb2, 0x9e, 0xdf, 0xfe, 0x2d, 0x6a, 0x06,
286 0x28, 0x13, 0xce, 0xdb, 0xe8, 0x52, 0xcd, 0xe3, 0x02, 0xb3, 0xe3, 0x3b, 0x69, 0x68,
287 0x46, 0xd2, 0xa8, 0xe3, 0x6b, 0xd6, 0x80, 0xef, 0xcc, 0x6c, 0xd3, 0xf9, 0xe9, 0xa4,
288 0xc1, 0xae, 0x8c, 0xac, 0x10, 0xcc, 0x52, 0x44, 0xd1, 0x31, 0x67, 0x71, 0x40, 0x39,
289 0x91, 0x76, 0xed, 0x46, 0x70, 0x00, 0x19, 0xa0, 0x04, 0xa1, 0x63, 0x80, 0x6f, 0x7f,
290 0xa4, 0x67, 0xfc, 0x4e, 0x17, 0xb4, 0x61, 0x7b, 0xbd, 0x76, 0x41, 0xaa, 0xff, 0x7f,
291 0xf5, 0x63, 0x96, 0xba, 0x8c, 0x08, 0xa8, 0xbe, 0x10, 0x0b, 0x33, 0xa2, 0x0b, 0x5d,
292 0xaf, 0x13, 0x4a, 0x2a, 0xef, 0xa5, 0xe1, 0xc3, 0x49, 0x67, 0x70, 0xdc, 0xf6, 0xba,
293 0xa4, 0xf7, 0xbb,
294 ];
295 let digest = [
296 0xa9, 0xdb, 0x49, 0x0c, 0x70, 0x8c, 0xc7, 0x25, 0x48, 0xd7, 0x86, 0x35, 0xaa, 0x7d,
297 0xa7, 0x9b, 0xb2, 0x53, 0xf9, 0x45, 0xd7, 0x10, 0xe5, 0xcb, 0x67, 0x7a, 0x47, 0x4e,
298 0xfc, 0x7c, 0x65, 0xa2, 0xaa, 0xb4, 0x5b, 0xc7, 0xca, 0x11, 0x13, 0xc8, 0xce, 0x0f,
299 0x3c, 0x32, 0xe1, 0x39, 0x9d, 0xe9, 0xc4, 0x59, 0x53, 0x5e, 0x88, 0x16, 0x52, 0x1a,
300 0xb7, 0x14, 0xb2, 0xa6, 0xcd, 0x20, 0x05, 0x25,
301 ];
302 assert_eq!(Sha512::hash(&msg), digest);
303 }
304
305 #[test]
306 fn update() {
307 let msg = [
308 0x4f, 0x05, 0x60, 0x09, 0x50, 0x66, 0x4d, 0x51, 0x90, 0xa2, 0xeb, 0xc2, 0x9c, 0x9e,
309 0xdb, 0x89, 0xc2, 0x00, 0x79, 0xa4, 0xd3, 0xe6, 0xbc, 0x3b, 0x27, 0xd7, 0x5e, 0x34,
310 0xe2, 0xfa, 0x3d, 0x02, 0x76, 0x85, 0x02, 0xbd, 0x69, 0x79, 0x00, 0x78, 0x59, 0x8d,
311 0x5f, 0xcf, 0x3d, 0x67, 0x79, 0xbf, 0xed, 0x12, 0x84, 0xbb, 0xe5, 0xad, 0x72, 0xfb,
312 0x45, 0x60, 0x15, 0x18, 0x1d, 0x95, 0x87, 0xd6, 0xe8, 0x64, 0xc9, 0x40, 0x56, 0x4e,
313 0xaa, 0xfb, 0x4f, 0x2f, 0xea, 0xd4, 0x34, 0x6e, 0xa0, 0x9b, 0x68, 0x77, 0xd9, 0x34,
314 0x0f, 0x6b, 0x82, 0xeb, 0x15, 0x15, 0x88, 0x08, 0x72, 0x21, 0x3d, 0xa3, 0xad, 0x88,
315 0xfe, 0xba, 0x9f, 0x4f, 0x13, 0x81, 0x7a, 0x71, 0xd6, 0xf9, 0x0a, 0x1a, 0x17, 0xc4,
316 0x3a, 0x15, 0xc0, 0x38, 0xd9, 0x88, 0xb5, 0xb2, 0x9e, 0xdf, 0xfe, 0x2d, 0x6a, 0x06,
317 0x28, 0x13, 0xce, 0xdb, 0xe8, 0x52, 0xcd, 0xe3, 0x02, 0xb3, 0xe3, 0x3b, 0x69, 0x68,
318 0x46, 0xd2, 0xa8, 0xe3, 0x6b, 0xd6, 0x80, 0xef, 0xcc, 0x6c, 0xd3, 0xf9, 0xe9, 0xa4,
319 0xc1, 0xae, 0x8c, 0xac, 0x10, 0xcc, 0x52, 0x44, 0xd1, 0x31, 0x67, 0x71, 0x40, 0x39,
320 0x91, 0x76, 0xed, 0x46, 0x70, 0x00, 0x19, 0xa0, 0x04, 0xa1, 0x63, 0x80, 0x6f, 0x7f,
321 0xa4, 0x67, 0xfc, 0x4e, 0x17, 0xb4, 0x61, 0x7b, 0xbd, 0x76, 0x41, 0xaa, 0xff, 0x7f,
322 0xf5, 0x63, 0x96, 0xba, 0x8c, 0x08, 0xa8, 0xbe, 0x10, 0x0b, 0x33, 0xa2, 0x0b, 0x5d,
323 0xaf, 0x13, 0x4a, 0x2a, 0xef, 0xa5, 0xe1, 0xc3, 0x49, 0x67, 0x70, 0xdc, 0xf6, 0xba,
324 0xa4, 0xf7, 0xbb,
325 ];
326 let digest = [
327 0xa9, 0xdb, 0x49, 0x0c, 0x70, 0x8c, 0xc7, 0x25, 0x48, 0xd7, 0x86, 0x35, 0xaa, 0x7d,
328 0xa7, 0x9b, 0xb2, 0x53, 0xf9, 0x45, 0xd7, 0x10, 0xe5, 0xcb, 0x67, 0x7a, 0x47, 0x4e,
329 0xfc, 0x7c, 0x65, 0xa2, 0xaa, 0xb4, 0x5b, 0xc7, 0xca, 0x11, 0x13, 0xc8, 0xce, 0x0f,
330 0x3c, 0x32, 0xe1, 0x39, 0x9d, 0xe9, 0xc4, 0x59, 0x53, 0x5e, 0x88, 0x16, 0x52, 0x1a,
331 0xb7, 0x14, 0xb2, 0xa6, 0xcd, 0x20, 0x05, 0x25,
332 ];
333 let mut hasher = Sha512::new();
334
335 let blocks = msg.chunks_exact(Sha512::BLOCK_SIZE);
336 let remainder = blocks.remainder();
337
338 for block in blocks {
339 hasher.update(block.try_into().unwrap());
340 }
341 assert_eq!(hasher.finish_with(remainder), digest);
342 }
343}