//! SHA-1 (FIPS 180-4), used only to check the `Sec-WebSocket-Accept` header of a WebSocket //! handshake (RFC 6455, section 4.2.2). Never use it for anything that needs to resist attack. /// The digest of `data`. pub fn sha1(data: &[u8]) -> [u8; 20] { let mut h = Sha1::new(); h.update(data); h.finish() } /// SHA-1 fed in pieces. #[derive(Debug, Clone)] pub struct Sha1 { state: [u32; 5], block: [u8; 64], filled: usize, length: u64, } impl Default for Sha1 { fn default() -> Self { Self::new() } } impl Sha1 { pub fn new() -> Sha1 { Sha1 { state: [ 0x6745_2301, 0xEFCD_AB89, 0x98BA_DCFE, 0x1032_5476, 0xC3D2_E1F0, ], block: [0; 64], filled: 0, length: 0, } } pub fn update(&mut self, mut data: &[u8]) { // Add 8 * data.len() to `length` (wrapping; `u64::try_from`, never `as`). Copy bytes into // `block` from `filled` on; each time it is full (64), `compress` it and set `filled` to 0. // Use `split_at` and `get_mut(..)`, no indexing that can go out of bounds. self.length = self .length .wrapping_add(8u64.wrapping_mul(u64::try_from(data.len()).unwrap_or(u64::MAX))); while !data.is_empty() { let space = 64 - self.filled; let take = data.len().min(space); let (left, right) = data.split_at(take); if let Some(dst) = self.block.get_mut(self.filled..self.filled + take) { dst.copy_from_slice(left); } self.filled += take; if self.filled == 64 { let block = self.block; self.compress(&block); self.filled = 0; } data = right; } } pub fn finish(mut self) -> [u8; 20] { // Save `length`. Feed 0x80 then zeros so that 56 bytes of the block are filled (56 - // filled, or 120 - filled when filled >= 56), then the saved length as 8 big-endian bytes, // through `update`. `update` adds to `length`: put the saved value back after. Then the // five state words, big-endian. let saved = self.length; let zeros = if self.filled < 56 { 56 - self.filled } else { 120 - self.filled }; let total = zeros + 8; let mut pad = [0u8; 128]; pad[0] = 0x80; pad[zeros..total].copy_from_slice(&saved.to_be_bytes()); self.update(&pad[..total]); self.length = saved; let mut out = [0u8; 20]; for (i, word) in self.state.iter().enumerate() { out[i * 4..i * 4 + 4].copy_from_slice(&word.to_be_bytes()); } out } fn compress(&mut self, block: &[u8; 64]) { // FIPS 180-4, section 6.1.2: w[0..16] are the block as big-endian u32s; w[i] = (w[i-3] ^ // w[i-8] ^ w[i-14] ^ w[i-16]).rotate_left(1) for 16..80. Eighty rounds with f and k by // range: 0..=19 (b & c) | (!b & d), 0x5A827999; 20..=39 b ^ c ^ d, 0x6ED9EBA1; 40..=59 (b & // c) | (b & d) | (c & d), 0x8F1BBCDC; 60..=79 b ^ c ^ d, 0xCA62C1D6. All additions // wrapping. Add a..e into state. let mut w: [u32; 80] = [0; 80]; let (chunks, _) = block.as_chunks::<4>(); for (i, chunk) in chunks.iter().enumerate() { w[i] = u32::from_be_bytes(*chunk); } for i in 16..80 { w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1); } let (mut a, mut b, mut c, mut d, mut e) = ( self.state[0], self.state[1], self.state[2], self.state[3], self.state[4], ); for (round, &ww) in w.iter().enumerate() { let (f, k) = if round <= 19 { ((b & c) | (!b & d), 0x5A82_7999) } else if round <= 39 { (b ^ c ^ d, 0x6ED9_EBA1) } else if round <= 59 { ((b & c) | (b & d) | (c & d), 0x8F1B_BCDC) } else { (b ^ c ^ d, 0xCA62_C1D6) }; let temp = a .rotate_left(5) .wrapping_add(f) .wrapping_add(e) .wrapping_add(k); e = d; d = c; c = b.rotate_left(30); b = a; a = temp.wrapping_add(ww); } self.state[0] = self.state[0].wrapping_add(a); self.state[1] = self.state[1].wrapping_add(b); self.state[2] = self.state[2].wrapping_add(c); self.state[3] = self.state[3].wrapping_add(d); self.state[4] = self.state[4].wrapping_add(e); } }