#![allow(clippy::indexing_slicing)]
use self::kernels::CompressBlocksFn;
use super::sha512::Sha512;
use crate::{
hashes::crypto::dispatch_util::{SizeClassDispatch, len_hint_from_u128},
traits::Digest,
};
#[doc(hidden)]
pub(crate) mod dispatch;
#[doc(hidden)]
pub(crate) mod dispatch_tables;
#[cfg(test)]
mod kernel_test;
pub(crate) mod kernels;
const BLOCK_LEN: usize = 128;
pub(crate) const H0: [u64; 8] = [
0xcbbb_9d5d_c105_9ed8,
0x629a_292a_367c_d507,
0x9159_015a_3070_dd17,
0x152f_ecd8_f70e_5939,
0x6733_2667_ffc0_0b31,
0x8eb4_4a87_6858_1511,
0xdb0c_2e0d_64f9_8fa7,
0x47b5_481d_befa_4fa4,
];
#[derive(Clone)]
pub struct Sha384 {
state: [u64; 8],
block: [u8; BLOCK_LEN],
block_len: usize,
bytes_hashed: u128,
compress_blocks: CompressBlocksFn,
dispatch: Option<SizeClassDispatch<CompressBlocksFn>>,
}
#[derive(Clone, Copy)]
#[cfg(feature = "hmac")]
pub(crate) struct Sha384Prefix {
state: [u64; 8],
bytes_hashed: u128,
compress_blocks: CompressBlocksFn,
dispatch: Option<SizeClassDispatch<CompressBlocksFn>>,
}
#[cfg(feature = "hmac")]
impl Sha384Prefix {
pub(crate) fn zeroize(&mut self) {
for word in self.state.iter_mut() {
unsafe { core::ptr::write_volatile(word, 0) };
}
unsafe { core::ptr::write_volatile(&mut self.bytes_hashed, 0) };
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
}
impl Sha384 {
#[inline]
#[must_use]
pub fn digest(data: &[u8]) -> [u8; 48] {
dispatch::digest(data)
}
}
impl core::fmt::Debug for Sha384 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Sha384").finish_non_exhaustive()
}
}
impl Default for Sha384 {
#[inline]
fn default() -> Self {
Self {
state: H0,
block: [0u8; BLOCK_LEN],
block_len: 0,
bytes_hashed: 0,
compress_blocks: Sha512::compress_blocks_portable,
dispatch: None,
}
}
}
impl Sha384 {
#[inline]
fn select_compress(&mut self, incoming_len: usize) -> CompressBlocksFn {
let dispatch = match self.dispatch {
Some(d) => d,
None => {
let d = dispatch::compress_dispatch();
self.dispatch = Some(d);
d
}
};
let total = self
.bytes_hashed
.strict_add(self.block_len as u128)
.strict_add(incoming_len as u128);
let compress = dispatch.select(len_hint_from_u128(total));
self.compress_blocks = compress;
compress
}
#[inline]
fn update_with(&mut self, mut data: &[u8], compress_blocks: CompressBlocksFn) {
if data.is_empty() {
return;
}
if self.block_len != 0 {
let take = core::cmp::min(BLOCK_LEN.strict_sub(self.block_len), data.len());
self.block[self.block_len..self.block_len.strict_add(take)].copy_from_slice(&data[..take]);
self.block_len = self.block_len.strict_add(take);
data = &data[take..];
if self.block_len == BLOCK_LEN {
compress_blocks(&mut self.state, &self.block);
self.bytes_hashed = self.bytes_hashed.strict_add(BLOCK_LEN as u128);
self.block_len = 0;
}
}
let full_len = data.len().strict_sub(data.len() % BLOCK_LEN);
if full_len != 0 {
let (blocks, rest) = data.split_at(full_len);
compress_blocks(&mut self.state, blocks);
self.bytes_hashed = self.bytes_hashed.strict_add(blocks.len() as u128);
data = rest;
}
if !data.is_empty() {
self.block[..data.len()].copy_from_slice(data);
self.block_len = data.len();
}
}
#[inline]
fn finalize_inner_with(&self, compress_blocks: CompressBlocksFn) -> [u8; 48] {
let mut state = self.state;
let mut block = self.block;
let mut block_len = self.block_len;
let total_len = self.bytes_hashed.strict_add(block_len as u128);
block[block_len] = 0x80;
block_len = block_len.strict_add(1);
if block_len > 112 {
block[block_len..].fill(0);
compress_blocks(&mut state, &block);
block = [0u8; BLOCK_LEN];
block_len = 0;
}
block[block_len..112].fill(0);
let bit_len = total_len << 3;
block[112..128].copy_from_slice(&bit_len.to_be_bytes());
compress_blocks(&mut state, &block);
let mut out = [0u8; 48];
out[0..8].copy_from_slice(&state[0].to_be_bytes());
out[8..16].copy_from_slice(&state[1].to_be_bytes());
out[16..24].copy_from_slice(&state[2].to_be_bytes());
out[24..32].copy_from_slice(&state[3].to_be_bytes());
out[32..40].copy_from_slice(&state[4].to_be_bytes());
out[40..48].copy_from_slice(&state[5].to_be_bytes());
out
}
#[inline]
#[must_use]
#[cfg(feature = "hmac")]
pub(crate) fn aligned_prefix(&self) -> Sha384Prefix {
debug_assert_eq!(self.block_len, 0);
Sha384Prefix {
state: self.state,
bytes_hashed: self.bytes_hashed,
compress_blocks: self.compress_blocks,
dispatch: self.dispatch,
}
}
#[inline]
#[must_use]
#[cfg(feature = "hmac")]
pub(crate) fn from_aligned_prefix(prefix: Sha384Prefix) -> Self {
Self {
state: prefix.state,
block: [0u8; BLOCK_LEN],
block_len: 0,
bytes_hashed: prefix.bytes_hashed,
compress_blocks: prefix.compress_blocks,
dispatch: prefix.dispatch,
}
}
#[inline]
#[cfg(feature = "hmac")]
pub(crate) fn reset_to_aligned_prefix(&mut self, prefix: Sha384Prefix) {
self.state = prefix.state;
self.block_len = 0;
self.bytes_hashed = prefix.bytes_hashed;
self.compress_blocks = prefix.compress_blocks;
self.dispatch = prefix.dispatch;
}
#[cfg(all(feature = "hmac", test))]
#[inline]
pub(crate) fn new_with_compress_for_test(compress_blocks: CompressBlocksFn) -> Self {
Self {
state: H0,
block: [0u8; BLOCK_LEN],
block_len: 0,
bytes_hashed: 0,
compress_blocks,
dispatch: Some(SizeClassDispatch {
boundaries: [usize::MAX; 3],
xs: compress_blocks,
s: compress_blocks,
m: compress_blocks,
l: compress_blocks,
}),
}
}
}
impl Drop for Sha384 {
fn drop(&mut self) {
for word in self.state.iter_mut() {
unsafe { core::ptr::write_volatile(word, 0) };
}
crate::traits::ct::zeroize(&mut self.block);
unsafe { core::ptr::write_volatile(&mut self.bytes_hashed, 0) };
unsafe { core::ptr::write_volatile(&mut self.block_len, 0) };
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
}
impl Digest for Sha384 {
const OUTPUT_SIZE: usize = 48;
type Output = [u8; 48];
#[inline]
fn new() -> Self {
Self::default()
}
#[inline]
fn update(&mut self, data: &[u8]) {
if data.is_empty() {
return;
}
let compress = self.select_compress(data.len());
self.update_with(data, compress);
}
#[inline]
fn finalize(&self) -> Self::Output {
self.finalize_inner_with(self.compress_blocks)
}
#[inline]
fn reset(&mut self) {
*self = Self::default();
}
#[inline]
fn digest(data: &[u8]) -> Self::Output {
dispatch::digest(data)
}
}