@@ -75,6 +75,28 @@ pub mod _hashlib {
7575 }
7676 }
7777
78+ const KECCAK_WIDTH_BITS : usize = 1600 ;
79+
80+ fn keccak_suffix ( name : & str ) -> Option < u8 > {
81+ match name {
82+ "sha3_224" | "sha3_256" | "sha3_384" | "sha3_512" => Some ( 0x06 ) ,
83+ "shake_128" | "shake_256" => Some ( 0x1f ) ,
84+ _ => None ,
85+ }
86+ }
87+
88+ fn keccak_rate_bits ( name : & str , block_size : usize ) -> Option < usize > {
89+ keccak_suffix ( name) . map ( |_| block_size * 8 )
90+ }
91+
92+ fn keccak_capacity_bits ( name : & str , block_size : usize ) -> Option < usize > {
93+ keccak_rate_bits ( name, block_size) . map ( |rate| KECCAK_WIDTH_BITS - rate)
94+ }
95+
96+ fn missing_hash_attribute < T > ( vm : & VirtualMachine , class_name : & str , attr : & str ) -> PyResult < T > {
97+ Err ( vm. new_attribute_error ( format ! ( "'{class_name}' object has no attribute '{attr}'" ) ) )
98+ }
99+
78100 #[ derive( FromArgs ) ]
79101 #[ allow( unused) ]
80102 struct XofDigestArgs {
@@ -139,6 +161,32 @@ pub mod _hashlib {
139161 self . ctx . read ( ) . block_size ( )
140162 }
141163
164+ #[ pygetset]
165+ fn _capacity_bits ( & self , vm : & VirtualMachine ) -> PyResult < usize > {
166+ let block_size = self . ctx . read ( ) . block_size ( ) ;
167+ match keccak_capacity_bits ( & self . name , block_size) {
168+ Some ( capacity) => Ok ( capacity) ,
169+ None => missing_hash_attribute ( vm, "HASH" , "_capacity_bits" ) ,
170+ }
171+ }
172+
173+ #[ pygetset]
174+ fn _rate_bits ( & self , vm : & VirtualMachine ) -> PyResult < usize > {
175+ let block_size = self . ctx . read ( ) . block_size ( ) ;
176+ match keccak_rate_bits ( & self . name , block_size) {
177+ Some ( rate) => Ok ( rate) ,
178+ None => missing_hash_attribute ( vm, "HASH" , "_rate_bits" ) ,
179+ }
180+ }
181+
182+ #[ pygetset]
183+ fn _suffix ( & self , vm : & VirtualMachine ) -> PyResult < PyBytes > {
184+ match keccak_suffix ( & self . name ) {
185+ Some ( suffix) => Ok ( vec ! [ suffix] . into ( ) ) ,
186+ None => missing_hash_attribute ( vm, "HASH" , "_suffix" ) ,
187+ }
188+ }
189+
142190 #[ pymethod]
143191 fn update ( & self , data : ArgBytesLike ) {
144192 data. with_ref ( |bytes| self . ctx . write ( ) . update ( bytes) ) ;
@@ -212,6 +260,32 @@ pub mod _hashlib {
212260 self . ctx . read ( ) . block_size ( )
213261 }
214262
263+ #[ pygetset]
264+ fn _capacity_bits ( & self , vm : & VirtualMachine ) -> PyResult < usize > {
265+ let block_size = self . ctx . read ( ) . block_size ( ) ;
266+ match keccak_capacity_bits ( & self . name , block_size) {
267+ Some ( capacity) => Ok ( capacity) ,
268+ None => missing_hash_attribute ( vm, "HASHXOF" , "_capacity_bits" ) ,
269+ }
270+ }
271+
272+ #[ pygetset]
273+ fn _rate_bits ( & self , vm : & VirtualMachine ) -> PyResult < usize > {
274+ let block_size = self . ctx . read ( ) . block_size ( ) ;
275+ match keccak_rate_bits ( & self . name , block_size) {
276+ Some ( rate) => Ok ( rate) ,
277+ None => missing_hash_attribute ( vm, "HASHXOF" , "_rate_bits" ) ,
278+ }
279+ }
280+
281+ #[ pygetset]
282+ fn _suffix ( & self , vm : & VirtualMachine ) -> PyResult < PyBytes > {
283+ match keccak_suffix ( & self . name ) {
284+ Some ( suffix) => Ok ( vec ! [ suffix] . into ( ) ) ,
285+ None => missing_hash_attribute ( vm, "HASHXOF" , "_suffix" ) ,
286+ }
287+ }
288+
215289 #[ pymethod]
216290 fn update ( & self , data : ArgBytesLike ) {
217291 data. with_ref ( |bytes| self . ctx . write ( ) . update ( bytes) ) ;
0 commit comments