we (web engine): Experimental web browser project to understand the limits of Claude
at flexbox 443 lines 14 kB view raw
1//! HMAC: keyed-hash message authentication code (RFC 2104). 2 3use crate::sha2::{Sha256, Sha384, Sha512}; 4 5/// Trait abstracting a hash function for use with HMAC. 6pub trait HashFunction { 7 /// Block size in bytes (64 for SHA-256, 128 for SHA-512/SHA-384). 8 const BLOCK_SIZE: usize; 9 /// Output digest size in bytes. 10 const OUTPUT_SIZE: usize; 11 12 fn hash_new() -> Self; 13 fn hash_update(&mut self, data: &[u8]); 14 fn hash_finalize(self) -> Vec<u8>; 15} 16 17impl HashFunction for Sha256 { 18 const BLOCK_SIZE: usize = 64; 19 const OUTPUT_SIZE: usize = 32; 20 21 fn hash_new() -> Self { 22 Sha256::new() 23 } 24 25 fn hash_update(&mut self, data: &[u8]) { 26 Sha256::update(self, data); 27 } 28 29 fn hash_finalize(self) -> Vec<u8> { 30 Sha256::finalize(self).to_vec() 31 } 32} 33 34impl HashFunction for Sha512 { 35 const BLOCK_SIZE: usize = 128; 36 const OUTPUT_SIZE: usize = 64; 37 38 fn hash_new() -> Self { 39 Sha512::new() 40 } 41 42 fn hash_update(&mut self, data: &[u8]) { 43 Sha512::update(self, data); 44 } 45 46 fn hash_finalize(self) -> Vec<u8> { 47 Sha512::finalize(self).to_vec() 48 } 49} 50 51impl HashFunction for Sha384 { 52 const BLOCK_SIZE: usize = 128; 53 const OUTPUT_SIZE: usize = 48; 54 55 fn hash_new() -> Self { 56 Sha384::new() 57 } 58 59 fn hash_update(&mut self, data: &[u8]) { 60 Sha384::update(self, data); 61 } 62 63 fn hash_finalize(self) -> Vec<u8> { 64 Sha384::finalize(self).to_vec() 65 } 66} 67 68/// HMAC hasher, generic over the hash function `H`. 69/// 70/// Implements RFC 2104: `HMAC(K, m) = H((K' ⊕ opad) ∥ H((K' ⊕ ipad) ∥ m))` 71pub struct Hmac<H: HashFunction> { 72 /// Inner hash, pre-seeded with `K' ⊕ ipad`. 73 inner: H, 74 /// Pre-computed `K' ⊕ opad` for the outer hash. 75 opad_key: Vec<u8>, 76} 77 78impl<H: HashFunction> Hmac<H> { 79 /// Create a new HMAC instance with the given key. 80 /// 81 /// Keys longer than the hash block size are hashed first. 82 /// Keys shorter than the block size are zero-padded. 83 pub fn new(key: &[u8]) -> Self { 84 // Step 1: Derive the block-sized key K'. 85 let mut key_block = vec![0u8; H::BLOCK_SIZE]; 86 if key.len() > H::BLOCK_SIZE { 87 let mut h = H::hash_new(); 88 h.hash_update(key); 89 let hashed = h.hash_finalize(); 90 key_block[..hashed.len()].copy_from_slice(&hashed); 91 } else { 92 key_block[..key.len()].copy_from_slice(key); 93 } 94 95 // Step 2: Compute ipad and opad keys. 96 let mut ipad_key = vec![0u8; H::BLOCK_SIZE]; 97 let mut opad_key = vec![0u8; H::BLOCK_SIZE]; 98 for i in 0..H::BLOCK_SIZE { 99 ipad_key[i] = key_block[i] ^ 0x36; 100 opad_key[i] = key_block[i] ^ 0x5c; 101 } 102 103 // Step 3: Start the inner hash with K' ⊕ ipad. 104 let mut inner = H::hash_new(); 105 inner.hash_update(&ipad_key); 106 107 Self { inner, opad_key } 108 } 109 110 /// Feed data into the HMAC computation. 111 pub fn update(&mut self, data: &[u8]) { 112 self.inner.hash_update(data); 113 } 114 115 /// Finalize and return the HMAC digest. 116 pub fn finalize(self) -> Vec<u8> { 117 // Complete inner hash: H((K' ⊕ ipad) ∥ message) 118 let inner_hash = self.inner.hash_finalize(); 119 120 // Compute outer hash: H((K' ⊕ opad) ∥ inner_hash) 121 let mut outer = H::hash_new(); 122 outer.hash_update(&self.opad_key); 123 outer.hash_update(&inner_hash); 124 outer.hash_finalize() 125 } 126} 127 128/// One-shot HMAC-SHA-256. 129pub fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; 32] { 130 let mut h = Hmac::<Sha256>::new(key); 131 h.update(data); 132 let result = h.finalize(); 133 let mut out = [0u8; 32]; 134 out.copy_from_slice(&result); 135 out 136} 137 138/// One-shot HMAC-SHA-384. 139pub fn hmac_sha384(key: &[u8], data: &[u8]) -> [u8; 48] { 140 let mut h = Hmac::<Sha384>::new(key); 141 h.update(data); 142 let result = h.finalize(); 143 let mut out = [0u8; 48]; 144 out.copy_from_slice(&result); 145 out 146} 147 148/// One-shot HMAC-SHA-512. 149pub fn hmac_sha512(key: &[u8], data: &[u8]) -> [u8; 64] { 150 let mut h = Hmac::<Sha512>::new(key); 151 h.update(data); 152 let result = h.finalize(); 153 let mut out = [0u8; 64]; 154 out.copy_from_slice(&result); 155 out 156} 157 158// --------------------------------------------------------------------------- 159// Tests — RFC 4231 test vectors 160// --------------------------------------------------------------------------- 161 162#[cfg(test)] 163mod tests { 164 use super::*; 165 166 fn hex(bytes: &[u8]) -> String { 167 bytes.iter().map(|b| format!("{b:02x}")).collect() 168 } 169 170 // ----------------------------------------------------------------------- 171 // Test Case 1: Key = 0x0b * 20, Data = "Hi There" 172 // ----------------------------------------------------------------------- 173 174 #[test] 175 fn rfc4231_case1_sha256() { 176 let key = [0x0bu8; 20]; 177 assert_eq!( 178 hex(&hmac_sha256(&key, b"Hi There")), 179 "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7" 180 ); 181 } 182 183 #[test] 184 fn rfc4231_case1_sha384() { 185 let key = [0x0bu8; 20]; 186 assert_eq!( 187 hex(&hmac_sha384(&key, b"Hi There")), 188 "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6" 189 ); 190 } 191 192 #[test] 193 fn rfc4231_case1_sha512() { 194 let key = [0x0bu8; 20]; 195 assert_eq!( 196 hex(&hmac_sha512(&key, b"Hi There")), 197 "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854" 198 ); 199 } 200 201 // ----------------------------------------------------------------------- 202 // Test Case 2: Key = "Jefe", Data = "what do ya want for nothing?" 203 // ----------------------------------------------------------------------- 204 205 #[test] 206 fn rfc4231_case2_sha256() { 207 assert_eq!( 208 hex(&hmac_sha256(b"Jefe", b"what do ya want for nothing?")), 209 "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843" 210 ); 211 } 212 213 #[test] 214 fn rfc4231_case2_sha384() { 215 assert_eq!( 216 hex(&hmac_sha384(b"Jefe", b"what do ya want for nothing?")), 217 "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649" 218 ); 219 } 220 221 #[test] 222 fn rfc4231_case2_sha512() { 223 assert_eq!( 224 hex(&hmac_sha512(b"Jefe", b"what do ya want for nothing?")), 225 "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737" 226 ); 227 } 228 229 // ----------------------------------------------------------------------- 230 // Test Case 3: Key = 0xaa * 20, Data = 0xdd * 50 231 // ----------------------------------------------------------------------- 232 233 #[test] 234 fn rfc4231_case3_sha256() { 235 let key = [0xaau8; 20]; 236 let data = [0xddu8; 50]; 237 assert_eq!( 238 hex(&hmac_sha256(&key, &data)), 239 "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe" 240 ); 241 } 242 243 #[test] 244 fn rfc4231_case3_sha384() { 245 let key = [0xaau8; 20]; 246 let data = [0xddu8; 50]; 247 assert_eq!( 248 hex(&hmac_sha384(&key, &data)), 249 "88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b2a5ab39dc13814b94e3ab6e101a34f27" 250 ); 251 } 252 253 #[test] 254 fn rfc4231_case3_sha512() { 255 let key = [0xaau8; 20]; 256 let data = [0xddu8; 50]; 257 assert_eq!( 258 hex(&hmac_sha512(&key, &data)), 259 "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb" 260 ); 261 } 262 263 // ----------------------------------------------------------------------- 264 // Test Case 4: Key = 0x01..0x19 (25 bytes), Data = 0xcd * 50 265 // ----------------------------------------------------------------------- 266 267 #[test] 268 fn rfc4231_case4_sha256() { 269 let key: Vec<u8> = (0x01..=0x19).collect(); 270 let data = [0xcdu8; 50]; 271 assert_eq!( 272 hex(&hmac_sha256(&key, &data)), 273 "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b" 274 ); 275 } 276 277 #[test] 278 fn rfc4231_case4_sha384() { 279 let key: Vec<u8> = (0x01..=0x19).collect(); 280 let data = [0xcdu8; 50]; 281 assert_eq!( 282 hex(&hmac_sha384(&key, &data)), 283 "3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb" 284 ); 285 } 286 287 #[test] 288 fn rfc4231_case4_sha512() { 289 let key: Vec<u8> = (0x01..=0x19).collect(); 290 let data = [0xcdu8; 50]; 291 assert_eq!( 292 hex(&hmac_sha512(&key, &data)), 293 "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd" 294 ); 295 } 296 297 // ----------------------------------------------------------------------- 298 // Test Case 5: Truncation (Key = 0x0c * 20, Data = "Test With Truncation") 299 // Verify the first 16 bytes of the full HMAC match the truncated vector. 300 // ----------------------------------------------------------------------- 301 302 #[test] 303 fn rfc4231_case5_sha256_truncated() { 304 let key = [0x0cu8; 20]; 305 let result = hmac_sha256(&key, b"Test With Truncation"); 306 assert_eq!(hex(&result[..16]), "a3b6167473100ee06e0c796c2955552b"); 307 } 308 309 #[test] 310 fn rfc4231_case5_sha384_truncated() { 311 let key = [0x0cu8; 20]; 312 let result = hmac_sha384(&key, b"Test With Truncation"); 313 assert_eq!(hex(&result[..16]), "3abf34c3503b2a23a46efc619baef897"); 314 } 315 316 #[test] 317 fn rfc4231_case5_sha512_truncated() { 318 let key = [0x0cu8; 20]; 319 let result = hmac_sha512(&key, b"Test With Truncation"); 320 assert_eq!(hex(&result[..16]), "415fad6271580a531d4179bc891d87a6"); 321 } 322 323 // ----------------------------------------------------------------------- 324 // Test Case 6: Key longer than block size (0xaa * 131) 325 // Data = "Test Using Larger Than Block-Size Key - Hash Key First" 326 // ----------------------------------------------------------------------- 327 328 #[test] 329 fn rfc4231_case6_sha256() { 330 let key = [0xaau8; 131]; 331 assert_eq!( 332 hex(&hmac_sha256( 333 &key, 334 b"Test Using Larger Than Block-Size Key - Hash Key First" 335 )), 336 "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54" 337 ); 338 } 339 340 #[test] 341 fn rfc4231_case6_sha384() { 342 let key = [0xaau8; 131]; 343 assert_eq!( 344 hex(&hmac_sha384(&key, b"Test Using Larger Than Block-Size Key - Hash Key First")), 345 "4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c60c2ef6ab4030fe8296248df163f44952" 346 ); 347 } 348 349 #[test] 350 fn rfc4231_case6_sha512() { 351 let key = [0xaau8; 131]; 352 assert_eq!( 353 hex(&hmac_sha512(&key, b"Test Using Larger Than Block-Size Key - Hash Key First")), 354 "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598" 355 ); 356 } 357 358 // ----------------------------------------------------------------------- 359 // Test Case 7: Key longer than block size, data longer than block size 360 // Key = 0xaa * 131 361 // ----------------------------------------------------------------------- 362 363 const CASE7_DATA: &[u8] = b"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm."; 364 365 #[test] 366 fn rfc4231_case7_sha256() { 367 let key = [0xaau8; 131]; 368 assert_eq!( 369 hex(&hmac_sha256(&key, CASE7_DATA)), 370 "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2" 371 ); 372 } 373 374 #[test] 375 fn rfc4231_case7_sha384() { 376 let key = [0xaau8; 131]; 377 assert_eq!( 378 hex(&hmac_sha384(&key, CASE7_DATA)), 379 "6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5a678cc31e799176d3860e6110c46523e" 380 ); 381 } 382 383 #[test] 384 fn rfc4231_case7_sha512() { 385 let key = [0xaau8; 131]; 386 assert_eq!( 387 hex(&hmac_sha512(&key, CASE7_DATA)), 388 "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58" 389 ); 390 } 391 392 // ----------------------------------------------------------------------- 393 // Streaming API tests 394 // ----------------------------------------------------------------------- 395 396 #[test] 397 fn streaming_matches_oneshot_sha256() { 398 let key = b"secret key"; 399 let data = b"The quick brown fox jumps over the lazy dog"; 400 let expected = hmac_sha256(key, data); 401 402 let mut h = Hmac::<Sha256>::new(key); 403 h.update(&data[..10]); 404 h.update(&data[10..30]); 405 h.update(&data[30..]); 406 let result = h.finalize(); 407 assert_eq!(&result[..], &expected[..]); 408 } 409 410 #[test] 411 fn streaming_matches_oneshot_sha512() { 412 let key = b"another secret key"; 413 let data = b"Some message to authenticate"; 414 let expected = hmac_sha512(key, data); 415 416 let mut h = Hmac::<Sha512>::new(key); 417 h.update(&data[..5]); 418 h.update(&data[5..]); 419 let result = h.finalize(); 420 assert_eq!(&result[..], &expected[..]); 421 } 422 423 #[test] 424 fn empty_data() { 425 let key = [0x0bu8; 20]; 426 let result = hmac_sha256(&key, b""); 427 // Just verify it doesn't panic and produces 32 bytes. 428 assert_eq!(result.len(), 32); 429 } 430 431 #[test] 432 fn key_exactly_block_size_sha256() { 433 // Key exactly 64 bytes (SHA-256 block size) — no hashing, no padding needed. 434 let key = [0x42u8; 64]; 435 let result = hmac_sha256(&key, b"test"); 436 assert_eq!(result.len(), 32); 437 438 // Verify streaming matches. 439 let mut h = Hmac::<Sha256>::new(&key); 440 h.update(b"test"); 441 assert_eq!(&h.finalize()[..], &result[..]); 442 } 443}