web engine - experimental web browser

Implement SHA-2 hash functions: SHA-256, SHA-384, SHA-512

Add pure Rust implementations of the SHA-2 family per FIPS 180-4
in the crypto crate:

- SHA-256: 256-bit digest, 64-byte blocks, 64 rounds
- SHA-512: 512-bit digest, 128-byte blocks, 80 rounds
- SHA-384: truncated SHA-512 with different initial values

All three provide streaming (new/update/finalize) and one-shot APIs.
21 tests including NIST test vectors (empty, abc, 448-bit, 896-bit,
million-a) plus streaming and edge-case coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

authored by pierrelf.com

Claude Opus 4.6 and committed by tangled.org 0fc095c9 a469cd30

+737
+2
crates/crypto/src/lib.rs
··· 1 1 //! Pure Rust cryptography — AES-GCM, ChaCha20-Poly1305, SHA-2, X25519, RSA, X.509, ASN.1. 2 + 3 + pub mod sha2;
+735
crates/crypto/src/sha2.rs
··· 1 + //! SHA-2 hash functions: SHA-256, SHA-384, SHA-512 (FIPS 180-4). 2 + 3 + // --------------------------------------------------------------------------- 4 + // SHA-256 5 + // --------------------------------------------------------------------------- 6 + 7 + /// SHA-256 round constants (first 32 bits of the fractional parts of the 8 + /// cube roots of the first 64 primes). 9 + const K256: [u32; 64] = [ 10 + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 11 + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 12 + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 13 + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 14 + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 15 + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 16 + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 17 + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, 18 + ]; 19 + 20 + /// Initial hash values for SHA-256 (first 32 bits of the fractional parts 21 + /// of the square roots of the first 8 primes). 22 + const H256_INIT: [u32; 8] = [ 23 + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, 24 + ]; 25 + 26 + /// SHA-256 hasher with streaming API. 27 + pub struct Sha256 { 28 + state: [u32; 8], 29 + buf: [u8; 64], 30 + buf_len: usize, 31 + total_len: u64, 32 + } 33 + 34 + impl Sha256 { 35 + pub fn new() -> Self { 36 + Self { 37 + state: H256_INIT, 38 + buf: [0u8; 64], 39 + buf_len: 0, 40 + total_len: 0, 41 + } 42 + } 43 + 44 + pub fn update(&mut self, data: &[u8]) { 45 + self.total_len += data.len() as u64; 46 + let mut offset = 0; 47 + 48 + // Fill the buffer if partially filled. 49 + if self.buf_len > 0 { 50 + let space = 64 - self.buf_len; 51 + let copy_len = space.min(data.len()); 52 + self.buf[self.buf_len..self.buf_len + copy_len].copy_from_slice(&data[..copy_len]); 53 + self.buf_len += copy_len; 54 + offset += copy_len; 55 + 56 + if self.buf_len == 64 { 57 + let block = self.buf; 58 + sha256_compress(&mut self.state, &block); 59 + self.buf_len = 0; 60 + } 61 + } 62 + 63 + // Process full blocks directly. 64 + while offset + 64 <= data.len() { 65 + let block: [u8; 64] = data[offset..offset + 64].try_into().unwrap(); 66 + sha256_compress(&mut self.state, &block); 67 + offset += 64; 68 + } 69 + 70 + // Buffer remaining bytes. 71 + let remaining = data.len() - offset; 72 + if remaining > 0 { 73 + self.buf[..remaining].copy_from_slice(&data[offset..]); 74 + self.buf_len = remaining; 75 + } 76 + } 77 + 78 + pub fn finalize(mut self) -> [u8; 32] { 79 + // Pad per FIPS 180-4 §5.1.1. 80 + let bit_len = self.total_len * 8; 81 + 82 + // Append 0x80 byte. 83 + self.buf[self.buf_len] = 0x80; 84 + self.buf_len += 1; 85 + 86 + // If not enough room for the 8-byte length, pad and compress. 87 + if self.buf_len > 56 { 88 + for i in self.buf_len..64 { 89 + self.buf[i] = 0; 90 + } 91 + let block = self.buf; 92 + sha256_compress(&mut self.state, &block); 93 + self.buf_len = 0; 94 + } 95 + 96 + // Zero-pad up to byte 56, then append 64-bit big-endian length. 97 + for i in self.buf_len..56 { 98 + self.buf[i] = 0; 99 + } 100 + self.buf[56..64].copy_from_slice(&bit_len.to_be_bytes()); 101 + let block = self.buf; 102 + sha256_compress(&mut self.state, &block); 103 + 104 + // Produce the digest. 105 + let mut out = [0u8; 32]; 106 + for (i, word) in self.state.iter().enumerate() { 107 + out[i * 4..(i + 1) * 4].copy_from_slice(&word.to_be_bytes()); 108 + } 109 + out 110 + } 111 + } 112 + 113 + impl Default for Sha256 { 114 + fn default() -> Self { 115 + Self::new() 116 + } 117 + } 118 + 119 + /// One-shot SHA-256. 120 + pub fn sha256(data: &[u8]) -> [u8; 32] { 121 + let mut h = Sha256::new(); 122 + h.update(data); 123 + h.finalize() 124 + } 125 + 126 + /// SHA-256 compression function: process one 64-byte block. 127 + fn sha256_compress(state: &mut [u32; 8], block: &[u8; 64]) { 128 + // Prepare the message schedule. 129 + let mut w = [0u32; 64]; 130 + for i in 0..16 { 131 + w[i] = u32::from_be_bytes([ 132 + block[i * 4], 133 + block[i * 4 + 1], 134 + block[i * 4 + 2], 135 + block[i * 4 + 3], 136 + ]); 137 + } 138 + for i in 16..64 { 139 + let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3); 140 + let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10); 141 + w[i] = w[i - 16] 142 + .wrapping_add(s0) 143 + .wrapping_add(w[i - 7]) 144 + .wrapping_add(s1); 145 + } 146 + 147 + let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h] = *state; 148 + 149 + for i in 0..64 { 150 + let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25); 151 + let ch = (e & f) ^ ((!e) & g); 152 + let temp1 = h 153 + .wrapping_add(s1) 154 + .wrapping_add(ch) 155 + .wrapping_add(K256[i]) 156 + .wrapping_add(w[i]); 157 + let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22); 158 + let maj = (a & b) ^ (a & c) ^ (b & c); 159 + let temp2 = s0.wrapping_add(maj); 160 + 161 + h = g; 162 + g = f; 163 + f = e; 164 + e = d.wrapping_add(temp1); 165 + d = c; 166 + c = b; 167 + b = a; 168 + a = temp1.wrapping_add(temp2); 169 + } 170 + 171 + state[0] = state[0].wrapping_add(a); 172 + state[1] = state[1].wrapping_add(b); 173 + state[2] = state[2].wrapping_add(c); 174 + state[3] = state[3].wrapping_add(d); 175 + state[4] = state[4].wrapping_add(e); 176 + state[5] = state[5].wrapping_add(f); 177 + state[6] = state[6].wrapping_add(g); 178 + state[7] = state[7].wrapping_add(h); 179 + } 180 + 181 + // --------------------------------------------------------------------------- 182 + // SHA-512 183 + // --------------------------------------------------------------------------- 184 + 185 + /// SHA-512 round constants (first 64 bits of the fractional parts of the 186 + /// cube roots of the first 80 primes). 187 + const K512: [u64; 80] = [ 188 + 0x428a2f98d728ae22, 189 + 0x7137449123ef65cd, 190 + 0xb5c0fbcfec4d3b2f, 191 + 0xe9b5dba58189dbbc, 192 + 0x3956c25bf348b538, 193 + 0x59f111f1b605d019, 194 + 0x923f82a4af194f9b, 195 + 0xab1c5ed5da6d8118, 196 + 0xd807aa98a3030242, 197 + 0x12835b0145706fbe, 198 + 0x243185be4ee4b28c, 199 + 0x550c7dc3d5ffb4e2, 200 + 0x72be5d74f27b896f, 201 + 0x80deb1fe3b1696b1, 202 + 0x9bdc06a725c71235, 203 + 0xc19bf174cf692694, 204 + 0xe49b69c19ef14ad2, 205 + 0xefbe4786384f25e3, 206 + 0x0fc19dc68b8cd5b5, 207 + 0x240ca1cc77ac9c65, 208 + 0x2de92c6f592b0275, 209 + 0x4a7484aa6ea6e483, 210 + 0x5cb0a9dcbd41fbd4, 211 + 0x76f988da831153b5, 212 + 0x983e5152ee66dfab, 213 + 0xa831c66d2db43210, 214 + 0xb00327c898fb213f, 215 + 0xbf597fc7beef0ee4, 216 + 0xc6e00bf33da88fc2, 217 + 0xd5a79147930aa725, 218 + 0x06ca6351e003826f, 219 + 0x142929670a0e6e70, 220 + 0x27b70a8546d22ffc, 221 + 0x2e1b21385c26c926, 222 + 0x4d2c6dfc5ac42aed, 223 + 0x53380d139d95b3df, 224 + 0x650a73548baf63de, 225 + 0x766a0abb3c77b2a8, 226 + 0x81c2c92e47edaee6, 227 + 0x92722c851482353b, 228 + 0xa2bfe8a14cf10364, 229 + 0xa81a664bbc423001, 230 + 0xc24b8b70d0f89791, 231 + 0xc76c51a30654be30, 232 + 0xd192e819d6ef5218, 233 + 0xd69906245565a910, 234 + 0xf40e35855771202a, 235 + 0x106aa07032bbd1b8, 236 + 0x19a4c116b8d2d0c8, 237 + 0x1e376c085141ab53, 238 + 0x2748774cdf8eeb99, 239 + 0x34b0bcb5e19b48a8, 240 + 0x391c0cb3c5c95a63, 241 + 0x4ed8aa4ae3418acb, 242 + 0x5b9cca4f7763e373, 243 + 0x682e6ff3d6b2b8a3, 244 + 0x748f82ee5defb2fc, 245 + 0x78a5636f43172f60, 246 + 0x84c87814a1f0ab72, 247 + 0x8cc702081a6439ec, 248 + 0x90befffa23631e28, 249 + 0xa4506cebde82bde9, 250 + 0xbef9a3f7b2c67915, 251 + 0xc67178f2e372532b, 252 + 0xca273eceea26619c, 253 + 0xd186b8c721c0c207, 254 + 0xeada7dd6cde0eb1e, 255 + 0xf57d4f7fee6ed178, 256 + 0x06f067aa72176fba, 257 + 0x0a637dc5a2c898a6, 258 + 0x113f9804bef90dae, 259 + 0x1b710b35131c471b, 260 + 0x28db77f523047d84, 261 + 0x32caab7b40c72493, 262 + 0x3c9ebe0a15c9bebc, 263 + 0x431d67c49c100d4c, 264 + 0x4cc5d4becb3e42b6, 265 + 0x597f299cfc657e2a, 266 + 0x5fcb6fab3ad6faec, 267 + 0x6c44198c4a475817, 268 + ]; 269 + 270 + /// Initial hash values for SHA-512. 271 + const H512_INIT: [u64; 8] = [ 272 + 0x6a09e667f3bcc908, 273 + 0xbb67ae8584caa73b, 274 + 0x3c6ef372fe94f82b, 275 + 0xa54ff53a5f1d36f1, 276 + 0x510e527fade682d1, 277 + 0x9b05688c2b3e6c1f, 278 + 0x1f83d9abfb41bd6b, 279 + 0x5be0cd19137e2179, 280 + ]; 281 + 282 + /// Initial hash values for SHA-384 (different from SHA-512). 283 + const H384_INIT: [u64; 8] = [ 284 + 0xcbbb9d5dc1059ed8, 285 + 0x629a292a367cd507, 286 + 0x9159015a3070dd17, 287 + 0x152fecd8f70e5939, 288 + 0x67332667ffc00b31, 289 + 0x8eb44a8768581511, 290 + 0xdb0c2e0d64f98fa7, 291 + 0x47b5481dbefa4fa4, 292 + ]; 293 + 294 + /// SHA-512 hasher with streaming API. 295 + pub struct Sha512 { 296 + state: [u64; 8], 297 + buf: [u8; 128], 298 + buf_len: usize, 299 + total_len: u128, 300 + } 301 + 302 + impl Sha512 { 303 + pub fn new() -> Self { 304 + Self { 305 + state: H512_INIT, 306 + buf: [0u8; 128], 307 + buf_len: 0, 308 + total_len: 0, 309 + } 310 + } 311 + 312 + fn with_init(init: [u64; 8]) -> Self { 313 + Self { 314 + state: init, 315 + buf: [0u8; 128], 316 + buf_len: 0, 317 + total_len: 0, 318 + } 319 + } 320 + 321 + pub fn update(&mut self, data: &[u8]) { 322 + self.total_len += data.len() as u128; 323 + let mut offset = 0; 324 + 325 + if self.buf_len > 0 { 326 + let space = 128 - self.buf_len; 327 + let copy_len = space.min(data.len()); 328 + self.buf[self.buf_len..self.buf_len + copy_len].copy_from_slice(&data[..copy_len]); 329 + self.buf_len += copy_len; 330 + offset += copy_len; 331 + 332 + if self.buf_len == 128 { 333 + let block = self.buf; 334 + sha512_compress(&mut self.state, &block); 335 + self.buf_len = 0; 336 + } 337 + } 338 + 339 + while offset + 128 <= data.len() { 340 + let block: [u8; 128] = data[offset..offset + 128].try_into().unwrap(); 341 + sha512_compress(&mut self.state, &block); 342 + offset += 128; 343 + } 344 + 345 + let remaining = data.len() - offset; 346 + if remaining > 0 { 347 + self.buf[..remaining].copy_from_slice(&data[offset..]); 348 + self.buf_len = remaining; 349 + } 350 + } 351 + 352 + pub fn finalize(mut self) -> [u8; 64] { 353 + // Pad per FIPS 180-4 §5.1.2. 354 + let bit_len = self.total_len * 8; 355 + 356 + self.buf[self.buf_len] = 0x80; 357 + self.buf_len += 1; 358 + 359 + // Need 16 bytes for the 128-bit length at the end. 360 + if self.buf_len > 112 { 361 + for i in self.buf_len..128 { 362 + self.buf[i] = 0; 363 + } 364 + let block = self.buf; 365 + sha512_compress(&mut self.state, &block); 366 + self.buf_len = 0; 367 + } 368 + 369 + for i in self.buf_len..112 { 370 + self.buf[i] = 0; 371 + } 372 + self.buf[112..128].copy_from_slice(&bit_len.to_be_bytes()); 373 + let block = self.buf; 374 + sha512_compress(&mut self.state, &block); 375 + 376 + let mut out = [0u8; 64]; 377 + for (i, word) in self.state.iter().enumerate() { 378 + out[i * 8..(i + 1) * 8].copy_from_slice(&word.to_be_bytes()); 379 + } 380 + out 381 + } 382 + } 383 + 384 + impl Default for Sha512 { 385 + fn default() -> Self { 386 + Self::new() 387 + } 388 + } 389 + 390 + /// One-shot SHA-512. 391 + pub fn sha512(data: &[u8]) -> [u8; 64] { 392 + let mut h = Sha512::new(); 393 + h.update(data); 394 + h.finalize() 395 + } 396 + 397 + /// SHA-512 compression function: process one 128-byte block. 398 + fn sha512_compress(state: &mut [u64; 8], block: &[u8; 128]) { 399 + let mut w = [0u64; 80]; 400 + for i in 0..16 { 401 + w[i] = u64::from_be_bytes([ 402 + block[i * 8], 403 + block[i * 8 + 1], 404 + block[i * 8 + 2], 405 + block[i * 8 + 3], 406 + block[i * 8 + 4], 407 + block[i * 8 + 5], 408 + block[i * 8 + 6], 409 + block[i * 8 + 7], 410 + ]); 411 + } 412 + for i in 16..80 { 413 + let s0 = w[i - 15].rotate_right(1) ^ w[i - 15].rotate_right(8) ^ (w[i - 15] >> 7); 414 + let s1 = w[i - 2].rotate_right(19) ^ w[i - 2].rotate_right(61) ^ (w[i - 2] >> 6); 415 + w[i] = w[i - 16] 416 + .wrapping_add(s0) 417 + .wrapping_add(w[i - 7]) 418 + .wrapping_add(s1); 419 + } 420 + 421 + let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h] = *state; 422 + 423 + for i in 0..80 { 424 + let s1 = e.rotate_right(14) ^ e.rotate_right(18) ^ e.rotate_right(41); 425 + let ch = (e & f) ^ ((!e) & g); 426 + let temp1 = h 427 + .wrapping_add(s1) 428 + .wrapping_add(ch) 429 + .wrapping_add(K512[i]) 430 + .wrapping_add(w[i]); 431 + let s0 = a.rotate_right(28) ^ a.rotate_right(34) ^ a.rotate_right(39); 432 + let maj = (a & b) ^ (a & c) ^ (b & c); 433 + let temp2 = s0.wrapping_add(maj); 434 + 435 + h = g; 436 + g = f; 437 + f = e; 438 + e = d.wrapping_add(temp1); 439 + d = c; 440 + c = b; 441 + b = a; 442 + a = temp1.wrapping_add(temp2); 443 + } 444 + 445 + state[0] = state[0].wrapping_add(a); 446 + state[1] = state[1].wrapping_add(b); 447 + state[2] = state[2].wrapping_add(c); 448 + state[3] = state[3].wrapping_add(d); 449 + state[4] = state[4].wrapping_add(e); 450 + state[5] = state[5].wrapping_add(f); 451 + state[6] = state[6].wrapping_add(g); 452 + state[7] = state[7].wrapping_add(h); 453 + } 454 + 455 + // --------------------------------------------------------------------------- 456 + // SHA-384 457 + // --------------------------------------------------------------------------- 458 + 459 + /// SHA-384 hasher (SHA-512 with different initial values, truncated to 384 bits). 460 + pub struct Sha384 { 461 + inner: Sha512, 462 + } 463 + 464 + impl Sha384 { 465 + pub fn new() -> Self { 466 + Self { 467 + inner: Sha512::with_init(H384_INIT), 468 + } 469 + } 470 + 471 + pub fn update(&mut self, data: &[u8]) { 472 + self.inner.update(data); 473 + } 474 + 475 + pub fn finalize(self) -> [u8; 48] { 476 + let full = self.inner.finalize(); 477 + let mut out = [0u8; 48]; 478 + out.copy_from_slice(&full[..48]); 479 + out 480 + } 481 + } 482 + 483 + impl Default for Sha384 { 484 + fn default() -> Self { 485 + Self::new() 486 + } 487 + } 488 + 489 + /// One-shot SHA-384. 490 + pub fn sha384(data: &[u8]) -> [u8; 48] { 491 + let mut h = Sha384::new(); 492 + h.update(data); 493 + h.finalize() 494 + } 495 + 496 + // --------------------------------------------------------------------------- 497 + // Tests 498 + // --------------------------------------------------------------------------- 499 + 500 + #[cfg(test)] 501 + mod tests { 502 + use super::*; 503 + 504 + fn hex(bytes: &[u8]) -> String { 505 + bytes.iter().map(|b| format!("{b:02x}")).collect() 506 + } 507 + 508 + // ----------------------------------------------------------------------- 509 + // SHA-256 NIST test vectors (FIPS 180-4 / NIST CSRC examples) 510 + // ----------------------------------------------------------------------- 511 + 512 + #[test] 513 + fn sha256_empty() { 514 + assert_eq!( 515 + hex(&sha256(b"")), 516 + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" 517 + ); 518 + } 519 + 520 + #[test] 521 + fn sha256_abc() { 522 + assert_eq!( 523 + hex(&sha256(b"abc")), 524 + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" 525 + ); 526 + } 527 + 528 + #[test] 529 + fn sha256_448bit() { 530 + // "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" (448 bits) 531 + assert_eq!( 532 + hex(&sha256( 533 + b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 534 + )), 535 + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1" 536 + ); 537 + } 538 + 539 + #[test] 540 + fn sha256_896bit() { 541 + // "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" 542 + assert_eq!( 543 + hex(&sha256(b"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")), 544 + "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1" 545 + ); 546 + } 547 + 548 + #[test] 549 + fn sha256_million_a() { 550 + // 1,000,000 repetitions of 'a' 551 + let mut h = Sha256::new(); 552 + // Feed in chunks to test streaming. 553 + let chunk = [b'a'; 1000]; 554 + for _ in 0..1000 { 555 + h.update(&chunk); 556 + } 557 + assert_eq!( 558 + hex(&h.finalize()), 559 + "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0" 560 + ); 561 + } 562 + 563 + #[test] 564 + fn sha256_streaming() { 565 + // Verify streaming matches one-shot. 566 + let data = b"The quick brown fox jumps over the lazy dog"; 567 + let expected = sha256(data); 568 + 569 + let mut h = Sha256::new(); 570 + h.update(&data[..10]); 571 + h.update(&data[10..20]); 572 + h.update(&data[20..]); 573 + assert_eq!(h.finalize(), expected); 574 + } 575 + 576 + // ----------------------------------------------------------------------- 577 + // SHA-512 NIST test vectors 578 + // ----------------------------------------------------------------------- 579 + 580 + #[test] 581 + fn sha512_empty() { 582 + assert_eq!( 583 + hex(&sha512(b"")), 584 + "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" 585 + ); 586 + } 587 + 588 + #[test] 589 + fn sha512_abc() { 590 + assert_eq!( 591 + hex(&sha512(b"abc")), 592 + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f" 593 + ); 594 + } 595 + 596 + #[test] 597 + fn sha512_896bit() { 598 + assert_eq!( 599 + hex(&sha512(b"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")), 600 + "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909" 601 + ); 602 + } 603 + 604 + #[test] 605 + fn sha512_million_a() { 606 + let mut h = Sha512::new(); 607 + let chunk = [b'a'; 1000]; 608 + for _ in 0..1000 { 609 + h.update(&chunk); 610 + } 611 + assert_eq!( 612 + hex(&h.finalize()), 613 + "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b" 614 + ); 615 + } 616 + 617 + #[test] 618 + fn sha512_streaming() { 619 + let data = b"The quick brown fox jumps over the lazy dog"; 620 + let expected = sha512(data); 621 + 622 + let mut h = Sha512::new(); 623 + h.update(&data[..5]); 624 + h.update(&data[5..]); 625 + assert_eq!(h.finalize(), expected); 626 + } 627 + 628 + // ----------------------------------------------------------------------- 629 + // SHA-384 NIST test vectors 630 + // ----------------------------------------------------------------------- 631 + 632 + #[test] 633 + fn sha384_empty() { 634 + assert_eq!( 635 + hex(&sha384(b"")), 636 + "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b" 637 + ); 638 + } 639 + 640 + #[test] 641 + fn sha384_abc() { 642 + assert_eq!( 643 + hex(&sha384(b"abc")), 644 + "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7" 645 + ); 646 + } 647 + 648 + #[test] 649 + fn sha384_896bit() { 650 + assert_eq!( 651 + hex(&sha384(b"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")), 652 + "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039" 653 + ); 654 + } 655 + 656 + #[test] 657 + fn sha384_million_a() { 658 + let mut h = Sha384::new(); 659 + let chunk = [b'a'; 1000]; 660 + for _ in 0..1000 { 661 + h.update(&chunk); 662 + } 663 + assert_eq!( 664 + hex(&h.finalize()), 665 + "9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985" 666 + ); 667 + } 668 + 669 + #[test] 670 + fn sha384_streaming() { 671 + let data = b"The quick brown fox jumps over the lazy dog"; 672 + let expected = sha384(data); 673 + 674 + let mut h = Sha384::new(); 675 + h.update(&data[..15]); 676 + h.update(&data[15..]); 677 + assert_eq!(h.finalize(), expected); 678 + } 679 + 680 + // ----------------------------------------------------------------------- 681 + // Edge cases 682 + // ----------------------------------------------------------------------- 683 + 684 + #[test] 685 + fn sha256_exactly_one_block() { 686 + // 55 bytes + 1 byte (0x80) + 8 bytes (length) = 64 = one block after padding 687 + let data = [0xABu8; 55]; 688 + let result = sha256(&data); 689 + // Verify streaming matches. 690 + let mut h = Sha256::new(); 691 + h.update(&data); 692 + assert_eq!(h.finalize(), result); 693 + } 694 + 695 + #[test] 696 + fn sha256_exactly_56_bytes() { 697 + // 56 bytes requires two blocks (56 + 1 + 8 > 64). 698 + let data = [0xCDu8; 56]; 699 + let result = sha256(&data); 700 + let mut h = Sha256::new(); 701 + h.update(&data); 702 + assert_eq!(h.finalize(), result); 703 + } 704 + 705 + #[test] 706 + fn sha512_exactly_one_block() { 707 + // 111 bytes + 1 + 16 = 128 708 + let data = [0xABu8; 111]; 709 + let result = sha512(&data); 710 + let mut h = Sha512::new(); 711 + h.update(&data); 712 + assert_eq!(h.finalize(), result); 713 + } 714 + 715 + #[test] 716 + fn sha512_exactly_112_bytes() { 717 + // 112 + 1 + 16 > 128 => requires second block 718 + let data = [0xCDu8; 112]; 719 + let result = sha512(&data); 720 + let mut h = Sha512::new(); 721 + h.update(&data); 722 + assert_eq!(h.finalize(), result); 723 + } 724 + 725 + #[test] 726 + fn sha256_byte_at_a_time() { 727 + let data = b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; 728 + let expected = sha256(data); 729 + let mut h = Sha256::new(); 730 + for byte in data.iter() { 731 + h.update(&[*byte]); 732 + } 733 + assert_eq!(h.finalize(), expected); 734 + } 735 + }