@@ -12,8 +12,6 @@ use core::borrow::Borrow;
1212use core:: fmt;
1313
1414use blake2:: digest:: core_api:: { Buffer , UpdateCore } ;
15- use blake2:: digest:: generic_array:: GenericArray ;
16- use blake2:: digest:: typenum:: U128 ;
1715use blake2:: digest:: Output ;
1816use blake2:: Blake2bVarCore ;
1917
@@ -339,22 +337,12 @@ fn zcash_hash_single_output(tx: &Transaction, index: usize) -> [u8; 32] {
339337
340338/// Compute BLAKE2b-256 hash with personalization for Zcash (ZIP-243).
341339pub ( crate ) fn blake2b_256_personal ( data : & [ u8 ] , personalization : & [ u8 ] ) -> [ u8 ; 32 ] {
342- // Create a new core with personalization. Parameters: (salt, persona, key_size, output_size)
343340 let mut core = Blake2bVarCore :: new_with_params ( & [ ] , personalization, 0 , 32 ) ;
344-
345- // Process data in 128-byte blocks (BLAKE2b block size)
346- let block_size = 128 ;
347- let mut pos = 0 ;
348-
349- while pos + block_size <= data. len ( ) {
350- let block = GenericArray :: < u8 , U128 > :: from_slice ( & data[ pos..pos + block_size] ) ;
351- core. update_blocks ( core:: slice:: from_ref ( block) ) ;
352- pos += block_size;
353- }
354-
355- // Handle final block with padding
341+ // Use the Lazy buffer for all data so the last block is retained in the buffer
342+ // until finalize, ensuring the correct finalization flag even when data.len()
343+ // is an exact multiple of the 128-byte BLAKE2b block size.
356344 let mut buffer: Buffer < Blake2bVarCore > = Default :: default ( ) ;
357- buffer. digest_blocks ( & data[ pos.. ] , |blocks| core. update_blocks ( blocks) ) ;
345+ buffer. digest_blocks ( data, |blocks| core. update_blocks ( blocks) ) ;
358346
359347 // Finalize
360348 let mut full_output: Output < Blake2bVarCore > = Default :: default ( ) ;
@@ -638,4 +626,50 @@ mod tests {
638626 . unwrap ( ) ;
639627 assert_eq ! ( hash_outputs, expected_outputs, "hashOutputs mismatch" ) ;
640628 }
629+
630+ /// Regression test: blake2b_256_personal must produce the correct hash when the
631+ /// input length is an exact multiple of the 128-byte BLAKE2b block size.
632+ ///
633+ /// The old implementation fed all complete blocks to `update_blocks` and left the
634+ /// buffer empty for `finalize_variable_core`, which then compressed a spurious
635+ /// all-zero block — producing a wrong hash for any block-aligned input.
636+ #[ test]
637+ fn test_blake2b_256_personal_block_aligned ( ) {
638+ // Expected values computed with Python hashlib:
639+ // hashlib.blake2b(data, digest_size=32, person=b"ZcashOutputsHash").hexdigest()
640+ let persona = b"ZcashOutputsHash" ;
641+
642+ // 256 bytes = 2 × block size
643+ let data256 = vec ! [ 0xabu8 ; 256 ] ;
644+ let expected256: [ u8 ; 32 ] =
645+ FromHex :: from_hex ( "f2cee55bab0bc6b421a97e26b7c55f63f22fea6cf5fbc5ad1c290872bd470f3e" )
646+ . unwrap ( ) ;
647+ assert_eq ! (
648+ blake2b_256_personal( & data256, persona) ,
649+ expected256,
650+ "wrong hash for 256-byte (2-block) input"
651+ ) ;
652+
653+ // 128 bytes = 1 × block size
654+ let data128 = vec ! [ 0xabu8 ; 128 ] ;
655+ let expected128: [ u8 ; 32 ] =
656+ FromHex :: from_hex ( "8e802425ab1d83222d0bcf18140d61ae70670796be480fdd50b3027a3ca5478d" )
657+ . unwrap ( ) ;
658+ assert_eq ! (
659+ blake2b_256_personal( & data128, persona) ,
660+ expected128,
661+ "wrong hash for 128-byte (1-block) input"
662+ ) ;
663+
664+ // 100 bytes (non-aligned) — sanity check that the fix didn't break the common case
665+ let data100 = vec ! [ 0xabu8 ; 100 ] ;
666+ let expected100: [ u8 ; 32 ] =
667+ FromHex :: from_hex ( "3df66bd2c00b813fff6119fde7464294eab4fbecc311dd18c2bddeb6120d97f7" )
668+ . unwrap ( ) ;
669+ assert_eq ! (
670+ blake2b_256_personal( & data100, persona) ,
671+ expected100,
672+ "wrong hash for 100-byte (non-aligned) input"
673+ ) ;
674+ }
641675}
0 commit comments