1- use core:: fmt;
1+ use core:: { fmt, marker :: PhantomData } ;
22use digest:: {
33 HashMarker , Output ,
4- array:: Array ,
54 block_api:: {
65 AlgorithmName , Block , BlockSizeUser , Buffer , BufferKindUser , Eager , FixedOutputCore ,
76 OutputSizeUser , Reset , UpdateCore ,
@@ -10,48 +9,33 @@ use digest::{
109 typenum:: U192 ,
1110} ;
1211
13- use crate :: variants :: { Bash256 , Bash384 , Bash512 , Variant } ;
12+ use crate :: OutputSize ;
1413use bash_f:: { STATE_WORDS , bash_f} ;
15- use digest:: typenum:: Unsigned ;
1614
17- /// Core Bash hasher state with generic security level .
15+ /// Core `bash-hash` hasher generic over output size .
1816///
19- /// Implements bash-hash[ℓ] algorithm according to section 7 of STB 34.101.77-2020.
20- /// Parameters:
21- /// - BlockSize: block size r = (1536 - 4ℓ) / 8 bytes
22- /// - OutputSize: output size 2ℓ / 8 bytes
23- #[ derive( Clone ) ]
24- pub struct BashHashCore < V : Variant > {
17+ /// Specified in Section 7 of STB 34.101.77-2020.
18+ pub struct BashHashCore < OS : OutputSize > {
2519 state : [ u64 ; STATE_WORDS ] ,
26- _variant : core :: marker :: PhantomData < V > ,
20+ _pd : PhantomData < OS > ,
2721}
2822
29- impl < V > BashHashCore < V >
30- where
31- V : Variant ,
32- {
33- /// Calculate security level ℓ
34- ///
35- /// According to section 5.3: ℓ = OutputSize * 8 / 2 = OutputSize * 4
23+ impl < OS : OutputSize > Clone for BashHashCore < OS > {
3624 #[ inline]
37- const fn get_level ( ) -> usize {
38- // 3. ℓ ← OutSize * 8 / 2
39- V :: OutputSize :: USIZE * 4
40- }
41-
42- /// Calculate buffer size r in bytes
43- #[ inline]
44- const fn get_r_bytes ( ) -> usize {
45- V :: BlockSize :: USIZE
25+ fn clone ( & self ) -> Self {
26+ Self {
27+ state : self . state ,
28+ _pd : PhantomData ,
29+ }
4630 }
31+ }
4732
33+ impl < OS : OutputSize > BashHashCore < OS > {
4834 /// Compress one data block
4935 fn compress_block ( & mut self , block : & Block < Self > ) {
50- let r_bytes = Self :: get_r_bytes ( ) ;
51- debug_assert_eq ! ( r_bytes % 8 , 0 ) ;
52-
5336 // 4.1: S[...1536 - 4ℓ) ← Xi
54- for ( dst, chunk) in self . state . iter_mut ( ) . zip ( block[ ..r_bytes] . chunks_exact ( 8 ) ) {
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 ) ) {
5539 // `chunk` is guaranteed to be 8 bytes long due to `r_bytes` being a multiple of 8
5640 * dst = u64:: from_le_bytes ( chunk. try_into ( ) . unwrap ( ) ) ;
5741 }
@@ -61,33 +45,21 @@ where
6145 }
6246}
6347
64- impl < V > HashMarker for BashHashCore < V > where V : Variant { }
48+ impl < OS : OutputSize > HashMarker for BashHashCore < OS > { }
6549
66- impl < V > BlockSizeUser for BashHashCore < V >
67- where
68- V : Variant ,
69- {
70- type BlockSize = V :: BlockSize ;
50+ impl < OS : OutputSize > BlockSizeUser for BashHashCore < OS > {
51+ type BlockSize = OS :: BlockSize ;
7152}
7253
73- impl < V > BufferKindUser for BashHashCore < V >
74- where
75- V : Variant ,
76- {
54+ impl < OS : OutputSize > BufferKindUser for BashHashCore < OS > {
7755 type BufferKind = Eager ;
7856}
7957
80- impl < V > OutputSizeUser for BashHashCore < V >
81- where
82- V : Variant ,
83- {
84- type OutputSize = V :: OutputSize ;
58+ impl < OS : OutputSize > OutputSizeUser for BashHashCore < OS > {
59+ type OutputSize = OS ;
8560}
8661
87- impl < V > UpdateCore for BashHashCore < V >
88- where
89- V : Variant ,
90- {
62+ impl < OS : OutputSize > UpdateCore for BashHashCore < OS > {
9163 #[ inline]
9264 fn update_blocks ( & mut self , blocks : & [ Block < Self > ] ) {
9365 for block in blocks {
@@ -96,85 +68,62 @@ where
9668 }
9769}
9870
99- impl < V > FixedOutputCore for BashHashCore < V >
100- where
101- V : Variant ,
102- {
71+ impl < OS : OutputSize > FixedOutputCore for BashHashCore < OS > {
10372 fn finalize_fixed_core ( & mut self , buffer : & mut Buffer < Self > , out : & mut Output < Self > ) {
104- let pos = buffer. get_pos ( ) ;
105-
10673 // 1. Split(X || 01, r) - split message with appended 01
10774 // 2: Xn ← Xn || 0^(1536-4ℓ-|Xn|) - pad last block with zeros
108- let mut padding_block = Array :: < u8 , V :: BlockSize > :: default ( ) ;
109- let block = buffer. pad_with_zeros ( ) ;
110- padding_block. copy_from_slice ( & block) ;
111- padding_block[ pos] = 0x40 ;
75+ let pos = buffer. get_pos ( ) ;
76+ let mut block = buffer. pad_with_zeros ( ) ;
77+ block[ pos] = 0x40 ;
11278
11379 // 4. for i = 1, 2, ..., n, do:
114- self . compress_block ( & padding_block) ;
115-
116- //5. Y ← S[...2ℓ)
117- self . state
118- . iter ( )
119- . flat_map ( |w| w. to_le_bytes ( ) )
120- . take ( V :: OutputSize :: USIZE )
121- . zip ( out. iter_mut ( ) )
122- . for_each ( |( src, dst) | * dst = src) ;
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+ }
12387 }
12488}
12589
126- impl < V > Default for BashHashCore < V >
127- where
128- V : Variant ,
129- {
90+ impl < OS : OutputSize > Default for BashHashCore < OS > {
13091 #[ inline]
13192 fn default ( ) -> Self {
13293 let mut state = [ 0u64 ; STATE_WORDS ] ;
13394
95+ // 3. ℓ ← OutSize * 8 / 2
96+ let level = OS :: USIZE * 4 ;
13497 // 3. S ← 0^1472 || ⟨ℓ/4⟩_64
135- let level = Self :: get_level ( ) ;
13698 state[ 23 ] = ( level / 4 ) as u64 ;
13799
138100 Self {
139101 state,
140- _variant : core :: marker :: PhantomData ,
102+ _pd : PhantomData ,
141103 }
142104 }
143105}
144106
145- impl < V > Reset for BashHashCore < V >
146- where
147- V : Variant ,
148- {
107+ impl < OS : OutputSize > Reset for BashHashCore < OS > {
149108 #[ inline]
150109 fn reset ( & mut self ) {
151110 * self = Default :: default ( ) ;
152111 }
153112}
154113
155- impl < V > AlgorithmName for BashHashCore < V >
156- where
157- V : Variant ,
158- {
114+ impl < OS : OutputSize > AlgorithmName for BashHashCore < OS > {
159115 fn write_alg_name ( f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
160- let level = Self :: get_level ( ) ;
161- write ! ( f, "Bash{}" , level * 2 )
116+ write ! ( f, "BashHash{}" , OS :: USIZE )
162117 }
163118}
164119
165- impl < V > fmt:: Debug for BashHashCore < V >
166- where
167- V : Variant ,
168- {
120+ impl < OS : OutputSize > fmt:: Debug for BashHashCore < OS > {
169121 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
170122 f. write_str ( "BashHashCore { ... }" )
171123 }
172124}
173125
174- impl < V > Drop for BashHashCore < V >
175- where
176- V : Variant ,
177- {
126+ impl < OS : OutputSize > Drop for BashHashCore < OS > {
178127 fn drop ( & mut self ) {
179128 #[ cfg( feature = "zeroize" ) ]
180129 {
@@ -185,48 +134,31 @@ where
185134}
186135
187136#[ cfg( feature = "zeroize" ) ]
188- impl < V > digest:: zeroize:: ZeroizeOnDrop for BashHashCore < V > where V : Variant { }
137+ impl < OS : OutputSize > digest:: zeroize:: ZeroizeOnDrop for BashHashCore < OS > { }
189138
190- impl < V > SerializableState for BashHashCore < V >
191- where
192- V : Variant ,
193- {
139+ impl < OS : OutputSize > SerializableState for BashHashCore < OS > {
194140 type SerializedStateSize = U192 ;
195141
196142 fn serialize ( & self ) -> SerializedState < Self > {
197- let mut dst = SerializedState :: < Self > :: default ( ) ;
198-
199- for ( word, chunk) in self . state . iter ( ) . zip ( dst. chunks_exact_mut ( 8 ) ) {
200- // `word` is guaranteed to be 8 bytes long due to `STATE_WORDS` being a multiple of 8
201- // and `chunk` being a slice of 8 bytes
202- chunk. copy_from_slice ( & word. to_le_bytes ( ) ) ;
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 ( ) ) ;
203147 }
204-
205- dst
148+ res
206149 }
207150
208151 fn deserialize (
209152 serialized_state : & SerializedState < Self > ,
210153 ) -> Result < Self , DeserializeStateError > {
211154 let mut state = [ 0u64 ; STATE_WORDS ] ;
212-
213- for ( dst, chunk) in state. iter_mut ( ) . zip ( serialized_state. chunks_exact ( 8 ) ) {
214- // `chunk` is guaranteed to be 8 bytes long due to `STATE_WORDS` being a multiple of 8
215- // and `dst` being a slice of 8 bytes
216- * dst = u64:: from_le_bytes ( chunk. try_into ( ) . map_err ( |_| DeserializeStateError ) ?) ;
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 ( ) ) ;
217158 }
218-
219159 Ok ( Self {
220160 state,
221- _variant : core :: marker :: PhantomData ,
161+ _pd : PhantomData ,
222162 } )
223163 }
224164}
225-
226- // Standard Bash hash variants according to section 5.3 and 7.1
227- // Bash256: ℓ = 128, output = 2ℓ = 256 bits, block = (1536 - 4×128)/8 = 128 bytes
228- // Bash384: ℓ = 192, output = 2ℓ = 384 bits, block = (1536 - 4×192)/8 = 96 bytes
229- // Bash512: ℓ = 256, output = 2ℓ = 512 bits, block = (1536 - 4×256)/8 = 64 bytes
230- pub ( crate ) type Bash256Core = BashHashCore < Bash256 > ;
231- pub ( crate ) type Bash384Core = BashHashCore < Bash384 > ;
232- pub ( crate ) type Bash512Core = BashHashCore < Bash512 > ;
0 commit comments