@@ -194,6 +194,8 @@ unsafe impl Send for MessageDigest {}
194
194
enum State {
195
195
Reset ,
196
196
Updated ,
197
+ #[ cfg( ossl330) ]
198
+ Squeeze ,
197
199
Finalized ,
198
200
}
199
201
@@ -260,6 +262,8 @@ impl Hasher {
260
262
Updated => {
261
263
self . finish ( ) ?;
262
264
}
265
+ #[ cfg( ossl330) ]
266
+ Squeeze => ( ) ,
263
267
Finalized => ( ) ,
264
268
}
265
269
unsafe {
@@ -274,6 +278,19 @@ impl Hasher {
274
278
if self . state == Finalized {
275
279
self . init ( ) ?;
276
280
}
281
+ #[ cfg( ossl330) ]
282
+ if self . state == Squeeze {
283
+ // [`EVP_DigestUpdate`], depending on the implementation, may allow Updates after Squeezes.
284
+ // But, [FIPS 202], as shown in Figure 7, has a distinguished absorbing phase followed by a squeezing phase.
285
+ // Indeed, the [`sha3.c`] implmentation disallows Updates after Squeezes.
286
+ // For consistency, we always return an error when Update is called after Squeeze.
287
+ //
288
+ // [`EVP_DigestUpdate`]: https://github.com/openssl/openssl/blob/b3bb214720f20f3b126ae4b9c330e9a48b835415/crypto/evp/digest.c#L385-L393
289
+ // [FIPS 202]: https://dx.doi.org/10.6028/NIST.FIPS.202
290
+ // [`sha3.c`]: https://github.com/openssl/openssl/blob/b3bb214720f20f3b126ae4b9c330e9a48b835415/crypto/sha/sha3.c#L52-L63
291
+ let errors = ErrorStack :: get ( ) ;
292
+ return Err ( errors) ;
293
+ }
277
294
unsafe {
278
295
cvt ( ffi:: EVP_DigestUpdate (
279
296
self . ctx ,
@@ -285,6 +302,21 @@ impl Hasher {
285
302
Ok ( ( ) )
286
303
}
287
304
305
+ /// Squeezes buf out of the hasher. Can be called multiple times, unlike `finish_xof`.
306
+ /// The output will be as long as the buf.
307
+ #[ cfg( ossl330) ]
308
+ pub fn squeeze_xof ( & mut self , buf : & mut [ u8 ] ) -> Result < ( ) , ErrorStack > {
309
+ unsafe {
310
+ cvt ( ffi:: EVP_DigestSqueeze (
311
+ self . ctx ,
312
+ buf. as_mut_ptr ( ) ,
313
+ buf. len ( ) ,
314
+ ) ) ?;
315
+ self . state = Squeeze ;
316
+ Ok ( ( ) )
317
+ }
318
+ }
319
+
288
320
/// Returns the hash of the data written and resets the non-XOF hasher.
289
321
pub fn finish ( & mut self ) -> Result < DigestBytes , ErrorStack > {
290
322
if self . state == Finalized {
@@ -481,6 +513,21 @@ mod tests {
481
513
assert_eq ! ( buf, expected) ;
482
514
}
483
515
516
+ /// Squeezes the expected length by doing two squeezes.
517
+ #[ cfg( ossl330) ]
518
+ fn hash_xof_squeeze_test ( hashtype : MessageDigest , hashtest : & ( & str , & str ) ) {
519
+ let data = Vec :: from_hex ( hashtest. 0 ) . unwrap ( ) ;
520
+ let mut h = Hasher :: new ( hashtype) . unwrap ( ) ;
521
+ h. update ( & data) . unwrap ( ) ;
522
+
523
+ let expected = Vec :: from_hex ( hashtest. 1 ) . unwrap ( ) ;
524
+ let mut buf = vec ! [ 0 ; expected. len( ) ] ;
525
+ assert ! ( expected. len( ) > 10 ) ;
526
+ h. squeeze_xof ( & mut buf[ ..10 ] ) . unwrap ( ) ;
527
+ h. squeeze_xof ( & mut buf[ 10 ..] ) . unwrap ( ) ;
528
+ assert_eq ! ( buf, expected) ;
529
+ }
530
+
484
531
fn hash_recycle_test ( h : & mut Hasher , hashtest : & ( & str , & str ) ) {
485
532
h. write_all ( & Vec :: from_hex ( hashtest. 0 ) . unwrap ( ) ) . unwrap ( ) ;
486
533
let res = h. finish ( ) . unwrap ( ) ;
@@ -537,6 +584,40 @@ mod tests {
537
584
assert_eq ! ( & * res, & * null) ;
538
585
}
539
586
587
+ #[ cfg( ossl330) ]
588
+ #[ test]
589
+ fn test_finish_then_squeeze ( ) {
590
+ let digest = MessageDigest :: shake_128 ( ) ;
591
+ let mut h = Hasher :: new ( digest) . unwrap ( ) ;
592
+ let mut buf = vec ! [ 0 ; digest. size( ) ] ;
593
+ h. finish_xof ( & mut buf) . unwrap ( ) ;
594
+ h. squeeze_xof ( & mut buf)
595
+ . expect_err ( "squeezing after finalize should fail" ) ;
596
+ }
597
+
598
+ #[ cfg( ossl330) ]
599
+ #[ test]
600
+ fn test_squeeze_then_update ( ) {
601
+ let digest = MessageDigest :: shake_128 ( ) ;
602
+ let data = Vec :: from_hex ( MD5_TESTS [ 6 ] . 0 ) . unwrap ( ) ;
603
+ let mut h = Hasher :: new ( digest) . unwrap ( ) ;
604
+ let mut buf = vec ! [ 0 ; digest. size( ) ] ;
605
+ h. squeeze_xof ( & mut buf) . unwrap ( ) ;
606
+ h. update ( & data)
607
+ . expect_err ( "updating after squeeze should fail" ) ;
608
+ }
609
+
610
+ #[ cfg( ossl330) ]
611
+ #[ test]
612
+ fn test_squeeze_then_finalize ( ) {
613
+ let digest = MessageDigest :: shake_128 ( ) ;
614
+ let mut h = Hasher :: new ( digest) . unwrap ( ) ;
615
+ let mut buf = vec ! [ 0 ; digest. size( ) ] ;
616
+ h. squeeze_xof ( & mut buf) . unwrap ( ) ;
617
+ h. finish_xof ( & mut buf)
618
+ . expect_err ( "finalize after squeeze should fail" ) ;
619
+ }
620
+
540
621
#[ test]
541
622
#[ allow( clippy:: redundant_clone) ]
542
623
fn test_clone ( ) {
@@ -710,6 +791,8 @@ mod tests {
710
791
711
792
for test in tests. iter ( ) {
712
793
hash_xof_test ( MessageDigest :: shake_128 ( ) , test) ;
794
+ #[ cfg( ossl330) ]
795
+ hash_xof_squeeze_test ( MessageDigest :: shake_128 ( ) , test) ;
713
796
}
714
797
715
798
assert_eq ! ( MessageDigest :: shake_128( ) . block_size( ) , 168 ) ;
@@ -730,6 +813,8 @@ mod tests {
730
813
731
814
for test in tests. iter ( ) {
732
815
hash_xof_test ( MessageDigest :: shake_256 ( ) , test) ;
816
+ #[ cfg( ossl330) ]
817
+ hash_xof_squeeze_test ( MessageDigest :: shake_256 ( ) , test) ;
733
818
}
734
819
735
820
assert_eq ! ( MessageDigest :: shake_256( ) . block_size( ) , 136 ) ;
0 commit comments