1use crate::constants::CRYPTO_HASH_SHA512_BYTES;
18use crate::sha2_impl::sha2_hasher;
19use crate::types::*;
20
21#[cfg(all(target_arch = "aarch64", target_endian = "little"))]
22mod sha512_aarch64;
23
24pub type Digest = StackByteArray<CRYPTO_HASH_SHA512_BYTES>;
26
27const BLOCK_BYTES: usize = 128;
28
29const IV: [u64; 8] = [
30 0x6a09e667f3bcc908,
31 0xbb67ae8584caa73b,
32 0x3c6ef372fe94f82b,
33 0xa54ff53a5f1d36f1,
34 0x510e527fade682d1,
35 0x9b05688c2b3e6c1f,
36 0x1f83d9abfb41bd6b,
37 0x5be0cd19137e2179,
38];
39
40#[inline]
47fn compress(state: &mut [u64; 8], blocks: &[[u8; BLOCK_BYTES]]) {
48 #[cfg(all(target_arch = "aarch64", target_endian = "little"))]
49 if let Some(sha3) = crate::aarch64::Sha3::new() {
50 sha512_aarch64::compress(sha3, state, blocks);
51 return;
52 }
53 sha2::block_api::compress512(state, blocks);
54}
55
56sha2_hasher! {
57 pub struct Sha512;
64 algorithm: "SHA-512",
65 word: u64,
66 word_bytes: 8,
67 length: u128,
68 length_bytes: 16,
69 block_bytes: BLOCK_BYTES,
70 digest_bytes: CRYPTO_HASH_SHA512_BYTES,
71 iv: IV,
72 compress: compress,
73}
74
75impl Sha512 {
76 #[inline]
82 pub(crate) fn absorb_key_blocks(
83 &mut self,
84 a: &[u8; BLOCK_BYTES],
85 other: &mut Self,
86 b: &[u8; BLOCK_BYTES],
87 ) {
88 #[cfg(all(target_arch = "aarch64", target_endian = "little"))]
89 if let Some(sha3) = crate::aarch64::Sha3::new() {
90 debug_assert!(self.len == 0 && self.buflen == 0);
91 debug_assert!(other.len == 0 && other.buflen == 0);
92 sha512_aarch64::compress2(sha3, &mut self.state, a, &mut other.state, b);
93 self.len = BLOCK_BYTES as u128;
94 other.len = BLOCK_BYTES as u128;
95 return;
96 }
97 self.absorb_key_block(a);
98 other.absorb_key_block(b);
99 }
100}
101
102#[cfg(all(test, feature = "alloc"))]
103mod tests {
104 use sha2::Digest as _;
105
106 use super::*;
107 use crate::test_prelude::*;
108 use crate::utils::test_util::hex;
109
110 #[test]
113 fn test_sha512_known_answers() {
114 assert_eq!(
115 Sha512::compute_to_vec(b""),
116 hex(concat!(
117 "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce",
118 "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
119 ))
120 );
121 assert_eq!(
122 Sha512::compute_to_vec(b"abc"),
123 hex(concat!(
124 "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a",
125 "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
126 ))
127 );
128 assert_eq!(
129 Sha512::compute_to_vec(
130 b"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno\
131 ijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
132 ),
133 hex(concat!(
134 "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018",
135 "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"
136 ))
137 );
138 }
139
140 #[cfg(all(target_arch = "aarch64", target_endian = "little"))]
143 #[test]
144 fn test_compress2_matches_compress() {
145 let Some(sha3) = crate::aarch64::Sha3::new() else {
146 return;
147 };
148 let mut seed = 0x1234_5678_9abc_def0u64;
149 let mut next = || {
150 seed ^= seed << 13;
151 seed ^= seed >> 7;
152 seed ^= seed << 17;
153 seed
154 };
155 for i in 0..500 {
156 let mut a = [0u8; BLOCK_BYTES];
157 let mut b = [0u8; BLOCK_BYTES];
158 match i {
159 0 => {}
160 1 => b = [0xff; BLOCK_BYTES],
161 _ => {
162 for chunk in a.chunks_mut(8).chain(b.chunks_mut(8)) {
163 chunk.copy_from_slice(&next().to_le_bytes());
164 }
165 }
166 }
167 let mut sa = IV;
168 let mut sb = IV;
169 if i % 3 == 0 {
170 for w in sa.iter_mut().chain(sb.iter_mut()) {
172 *w = next();
173 }
174 }
175 let (mut ea, mut eb) = (sa, sb);
176 compress(&mut ea, core::slice::from_ref(&a));
177 compress(&mut eb, core::slice::from_ref(&b));
178 sha512_aarch64::compress2(sha3, &mut sa, &a, &mut sb, &b);
179 assert_eq!(sa, ea, "state a {i}");
180 assert_eq!(sb, eb, "state b {i}");
181 }
182 let (mut x, mut y) = (Sha512::new(), Sha512::new());
183 x.absorb_key_blocks(&[0x36; BLOCK_BYTES], &mut y, &[0x5c; BLOCK_BYTES]);
184 let (mut ex, mut ey) = (Sha512::new(), Sha512::new());
185 ex.absorb_key_block(&[0x36; BLOCK_BYTES]);
186 ey.absorb_key_block(&[0x5c; BLOCK_BYTES]);
187 assert_eq!(x.state, ex.state);
188 assert_eq!(y.state, ey.state);
189 }
190
191 #[test]
194 fn test_sha512_matches_sha2_for_all_lengths_and_chunkings() {
195 let message: Vec<u8> = (0..1200u32).map(|i| (i * 31 % 251) as u8).collect();
196 for len in (0..400).chain([511, 512, 513, 1023, 1024, 1025, 1199, 1200]) {
197 let expected = sha2::Sha512::digest(&message[..len]).to_vec();
198 assert_eq!(
199 Sha512::compute_to_vec(&message[..len]),
200 expected,
201 "len {len}"
202 );
203
204 let mut hasher = Sha512::new();
205 let mut offset = 0;
206 for chunk in [1usize, 7, 127, 128, 129, 200, 3].iter().cycle() {
207 if offset >= len {
208 break;
209 }
210 let end = (offset + chunk).min(len);
211 hasher.update(&message[offset..end]);
212 offset = end;
213 }
214 assert_eq!(hasher.finalize_to_vec(), expected, "chunked len {len}");
215 }
216 }
217
218 #[test]
223 fn test_empty_and_exact_fill_updates_match_sha2() {
224 const B: usize = BLOCK_BYTES;
225 let message: Vec<u8> = (0..3 * B as u32).map(|i| (i * 31 % 251) as u8).collect();
226 for len in [B, B + 1, 2 * B, 2 * B + 1, 3 * B - 1, 3 * B] {
227 let message = &message[..len];
228 let mut cuts: Vec<usize> = [0, 1, B, 2 * B, 3 * B - 1, len]
229 .into_iter()
230 .filter(|&cut| cut <= len)
231 .collect();
232 cuts.dedup();
233 let mut hasher = Sha512::new();
234 hasher.update(b"");
235 for window in cuts.windows(2) {
236 hasher.update(&message[window[0]..window[1]]);
237 hasher.update(b"");
238 }
239 assert_eq!(
240 hasher.finalize_to_vec(),
241 sha2::Sha512::digest(message).to_vec(),
242 "len {len}"
243 );
244 }
245 }
246
247 #[cfg(dryoc_native_tests)]
248 #[test]
249 fn test_sha512_matches_libsodium() {
250 use crate::native_test_util::HashSha512State;
251 use crate::rng::randombytes_buf;
252
253 let mut their_state = HashSha512State::new();
254 let mut our_state = Sha512::new();
255
256 for _ in 0..10 {
257 let r = randombytes_buf(64);
258 their_state.update(&r);
259 our_state.update(&r);
260 }
261
262 let their_digest = their_state.finalize();
263 let our_digest = our_state.finalize_to_vec();
264
265 assert_eq!(their_digest.as_slice(), our_digest);
266 }
267
268 #[cfg(all(target_arch = "aarch64", target_endian = "little"))]
271 #[test]
272 fn test_hw_compress_matches_portable() {
273 let Some(sha3) = crate::aarch64::Sha3::new() else {
274 return;
275 };
276 let blocks: Vec<[u8; 128]> = (0..40u32)
277 .map(|b| {
278 core::array::from_fn(|i| (b * 128 + i as u32).wrapping_mul(2654435761) as u8 >> 1)
279 })
280 .collect();
281 for n in 0..=blocks.len() {
282 let mut expected = IV;
283 let mut actual = IV;
284 sha2::block_api::compress512(&mut expected, &blocks[..n]);
285 sha512_aarch64::compress(sha3, &mut actual, &blocks[..n]);
286 assert_eq!(actual, expected, "{n} blocks");
287 }
288 }
289}