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
15pub 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 fn compress_block(&mut self, block: &Block<Self>) {
36 for (dst, chunk) in self.state.iter_mut().zip(block.chunks_exact(8)) {
39 *dst = u64::from_le_bytes(chunk.try_into().unwrap());
41 }
42
43 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 let pos = buffer.get_pos();
76 let mut block = buffer.pad_with_zeros();
77 block[pos] = 0x40;
78
79 self.compress_block(&block);
81
82 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 let level = OS::USIZE * 4;
97 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 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 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}