#[doc = crate::_tags!(crypto hash)]
#[doc = crate::_doc_meta!{location("data/codec/crypto")}]
#[doc(hidden)]
#[macro_export]
macro_rules! __crypto_impl_sha1 {
($(#[$attr:meta])* $vis:vis struct $Self:ident) => {
$(#[$attr])*
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
$vis struct $Self {
state: [u32; 5],
len_bits: u64,
block: [u8; $Self::BLOCK_LEN],
block_len: u8,
}
impl $crate::ConstInit for $Self { const INIT: $Self = $Self::new(); }
impl Default for $Self { fn default() -> $Self { $Self::new() } }
impl $Self {
pub const DIGEST_LEN: usize = 20;
pub const BLOCK_LEN: usize = 64;
pub const fn new() -> $Self {
$Self {
state: [0x6745_2301, 0xEFCD_AB89, 0x98BA_DCFE, 0x1032_5476, 0xC3D2_E1F0],
len_bits: 0,
block: [0; $Self::BLOCK_LEN],
block_len: 0,
}
}
pub const fn len_bits(&self) -> u64 {
self.len_bits
}
pub const fn is_empty(&self) -> bool {
self.len_bits == 0
}
pub const fn reset(&mut self) {
*self = $Self::new();
}
pub const fn update(&mut self, bytes: &[u8]) -> Result<(), $crate::CryptoError> {
if bytes.len() > (u64::MAX / 8) as usize {
return Err($crate::CryptoError::LengthOverflow)
}
let add_bits = (bytes.len() as u64) * 8;
let Some(new_len_bits) = self.len_bits.checked_add(add_bits) else {
return Err($crate::CryptoError::LengthOverflow);
};
self.len_bits = new_len_bits;
self.write_blocks(bytes);
Ok(())
}
pub const fn digest_bytes(bytes: &[u8])
-> Result<$crate::Digest<{ $Self::DIGEST_LEN }>, $crate::CryptoError> {
let mut sha = $Self::new();
$crate::unwrap![ok? sha.update(bytes)];
Ok(sha.finalize())
}
$crate::__crypto_impl_hmac![$Self];
pub const fn finalize(mut self) -> $crate::Digest<{ $Self::DIGEST_LEN }> {
let len_bits = self.len_bits;
self.push_padding_byte(0x80);
while self.block_len != 56 {
self.push_padding_byte(0);
}
let len = len_bits.to_be_bytes();
$crate::whilst! { i in 0..len.len(); {
self.push_padding_byte(len[i]);
}}
let mut out = [0u8; $Self::DIGEST_LEN];
$crate::whilst! { i in 0..self.state.len(); {
let bytes = self.state[i].to_be_bytes();
let j = i * 4;
out[j] = bytes[0];
out[j + 1] = bytes[1];
out[j + 2] = bytes[2];
out[j + 3] = bytes[3];
}}
$crate::Digest(out)
}
const fn write_blocks(&mut self, mut bytes: &[u8]) {
if self.block_len != 0 {
let used = self.block_len as usize;
let free = $Self::BLOCK_LEN - used;
let take = $crate::cmp![min free, bytes.len()];
$crate::Slice::range_mut(&mut self.block, used, used + take)
.copy_from_slice($crate::Slice::range_to(&bytes, take));
self.block_len += take as u8;
bytes = $crate::Slice::range_from(&bytes, take);
if self.block_len as usize == $Self::BLOCK_LEN {
let block = self.block;
self.process_block(&block);
self.block_len = 0;
}
}
while bytes.len() >= $Self::BLOCK_LEN {
let mut block = [0u8; $Self::BLOCK_LEN];
block.copy_from_slice($crate::Slice::range_to(&bytes, $Self::BLOCK_LEN));
self.process_block(&block);
bytes = $crate::Slice::range_from(&bytes, $Self::BLOCK_LEN);
}
if !bytes.is_empty() {
$crate::Slice::range_to_mut(&mut self.block, bytes.len()).copy_from_slice(bytes);
self.block_len = bytes.len() as u8;
}
}
const fn push_padding_byte(&mut self, byte: u8) {
self.block[self.block_len as usize] = byte;
self.block_len += 1;
if self.block_len as usize == $Self::BLOCK_LEN {
let block = self.block;
self.process_block(&block);
self.block = [0; $Self::BLOCK_LEN];
self.block_len = 0;
}
}
const fn process_block(&mut self, block: &[u8; $Self::BLOCK_LEN]) {
let mut w = [0u32; 80];
$crate::whilst! { t in 0..16; {
let i = t * 4;
w[t] = u32::from_be_bytes([block[i], block[i + 1], block[i + 2], block[i + 3]]);
}}
$crate::whilst! { t in 16..80; {
w[t] = (w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]).rotate_left(1);
}}
let [mut a, mut b, mut c, mut d, mut e] = self.state;
$crate::whilst! { t in 0..80; {
let (f, k) = if t < 20 {
((b & c) | ((!b) & d), 0x5A82_7999)
} else if t < 40 {
(b ^ c ^ d, 0x6ED9_EBA1)
} else if t < 60 {
((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).wrapping_add(w[t]);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = temp;
}}
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);
}
}
$crate::__crypto_impl_otp! { hash: $Self, otp: $crate::Otp, doc: "SHA-1" }
};
}
#[doc(hidden)]
pub use __crypto_impl_sha1;