bash_hash/
block_api.rs

1use core::{fmt, marker::PhantomData};
2use digest::{
3    HashMarker, Output,
4    block_api::{
5        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, Eager, FixedOutputCore,
6        OutputSizeUser, Reset, UpdateCore,
7    },
8    crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState},
9    typenum::U192,
10};
11
12use crate::OutputSize;
13use bash_f::{STATE_WORDS, bash_f};
14
15/// Core `bash-hash` hasher generic over output size.
16///
17/// Specified in Section 7 of STB 34.101.77-2020.
18pub struct BashHashCore<OS: OutputSize> {
19    state: [u64; STATE_WORDS],
20    _pd: PhantomData<OS>,
21}
22
23impl<OS: OutputSize> Clone for BashHashCore<OS> {
24    #[inline]
25    fn clone(&self) -> Self {
26        Self {
27            state: self.state,
28            _pd: PhantomData,
29        }
30    }
31}
32
33impl<OS: OutputSize> BashHashCore<OS> {
34    /// Compress one data block
35    fn compress_block(&mut self, block: &Block<Self>) {
36        // 4.1: S[...1536 - 4ℓ) ← Xi
37        // TODO: use `as_chunks` after MSRV is bumped to 1.88+
38        for (dst, chunk) in self.state.iter_mut().zip(block.chunks_exact(8)) {
39            // `chunk` is guaranteed to be 8 bytes long due to `r_bytes` being a multiple of 8
40            *dst = u64::from_le_bytes(chunk.try_into().unwrap());
41        }
42
43        // 4.2: S ← bash-f(S)
44        bash_f(&mut self.state);
45    }
46}
47
48impl<OS: OutputSize> HashMarker for BashHashCore<OS> {}
49
50impl<OS: OutputSize> BlockSizeUser for BashHashCore<OS> {
51    type BlockSize = OS::BlockSize;
52}
53
54impl<OS: OutputSize> BufferKindUser for BashHashCore<OS> {
55    type BufferKind = Eager;
56}
57
58impl<OS: OutputSize> OutputSizeUser for BashHashCore<OS> {
59    type OutputSize = OS;
60}
61
62impl<OS: OutputSize> UpdateCore for BashHashCore<OS> {
63    #[inline]
64    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
65        for block in blocks {
66            self.compress_block(block);
67        }
68    }
69}
70
71impl<OS: OutputSize> FixedOutputCore for BashHashCore<OS> {
72    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
73        // 1. Split(X || 01, r) - split message with appended 01
74        // 2: Xn ← Xn || 0^(1536-4ℓ-|Xn|) - pad last block with zeros
75        let pos = buffer.get_pos();
76        let mut block = buffer.pad_with_zeros();
77        block[pos] = 0x40;
78
79        // 4. for i = 1, 2, ..., n, do:
80        self.compress_block(&block);
81
82        // 5. Y ← S[...2ℓ)
83        // TODO: use `as_chunks` after MSRV is bumped to 1.88+
84        for (src, dst) in self.state.iter().zip(out.chunks_exact_mut(8)) {
85            dst.copy_from_slice(&src.to_le_bytes());
86        }
87    }
88}
89
90impl<OS: OutputSize> Default for BashHashCore<OS> {
91    #[inline]
92    fn default() -> Self {
93        let mut state = [0u64; STATE_WORDS];
94
95        // 3. ℓ ← OutSize * 8 / 2
96        let level = OS::USIZE * 4;
97        // 3. S ← 0^1472 || ⟨ℓ/4⟩_64
98        state[23] = (level / 4) as u64;
99
100        Self {
101            state,
102            _pd: PhantomData,
103        }
104    }
105}
106
107impl<OS: OutputSize> Reset for BashHashCore<OS> {
108    #[inline]
109    fn reset(&mut self) {
110        *self = Default::default();
111    }
112}
113
114impl<OS: OutputSize> AlgorithmName for BashHashCore<OS> {
115    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        write!(f, "BashHash{}", OS::USIZE)
117    }
118}
119
120impl<OS: OutputSize> fmt::Debug for BashHashCore<OS> {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        f.write_str("BashHashCore { ... }")
123    }
124}
125
126impl<OS: OutputSize> Drop for BashHashCore<OS> {
127    fn drop(&mut self) {
128        #[cfg(feature = "zeroize")]
129        {
130            use digest::zeroize::Zeroize;
131            self.state.zeroize();
132        }
133    }
134}
135
136#[cfg(feature = "zeroize")]
137impl<OS: OutputSize> digest::zeroize::ZeroizeOnDrop for BashHashCore<OS> {}
138
139impl<OS: OutputSize> SerializableState for BashHashCore<OS> {
140    type SerializedStateSize = U192;
141
142    fn serialize(&self) -> SerializedState<Self> {
143        let mut res = SerializedState::<Self>::default();
144        // TODO: use `as_chunks` after MSRV is bumped to 1.88+
145        for (src, dst) in self.state.iter().zip(res.chunks_exact_mut(8)) {
146            dst.copy_from_slice(&src.to_le_bytes());
147        }
148        res
149    }
150
151    fn deserialize(
152        serialized_state: &SerializedState<Self>,
153    ) -> Result<Self, DeserializeStateError> {
154        let mut state = [0u64; STATE_WORDS];
155        // TODO: use `as_chunks` after MSRV is bumped to 1.88+
156        for (src, dst) in serialized_state.chunks_exact(8).zip(state.iter_mut()) {
157            *dst = u64::from_le_bytes(src.try_into().unwrap());
158        }
159        Ok(Self {
160            state,
161            _pd: PhantomData,
162        })
163    }
164}