@@ -485,10 +485,6 @@ macro_rules! blake2x_impl {
485485 $vardoc: expr, $base_core: path,
486486 ) => {
487487 /// Blake2X XOF core implementation.
488- ///
489- /// Implements the Blake2X extended output function which builds on top of Blake2b/Blake2s
490- /// to provide variable-length output. The XOF length parameter is incorporated into the
491- /// root hash computation to ensure different output lengths produce different results.
492488 #[ derive( Clone ) ]
493489 pub struct $core_name {
494490 /// The root hasher for this variant of Blake2X.
@@ -500,69 +496,53 @@ macro_rules! blake2x_impl {
500496 impl $core_name {
501497 /// Create new core with specified output length.
502498 pub fn new( xof_len: $xof_len_type) -> Self {
503- // Root hasher construction is delegated to new_blake2x_root_hasher for deduplication and correctness.
504- $core_name {
505- root_hasher: Self :: new_blake2x_root_hasher( xof_len) ,
499+ Self {
500+ root_hasher: Self :: new_root_hasher( xof_len) ,
506501 xof_len,
507502 }
508503 }
509504
510505 /// Create new core with specified output length and a key.
511506 pub fn new_with_key( key: & [ u8 ] , xof_len: $xof_len_type) -> Self {
512- $core_name {
513- root_hasher: Self :: new_blake2x_root_hasher_with_key ( key, xof_len) ,
507+ Self {
508+ root_hasher: Self :: new_keyed_root_hasher ( key, xof_len) ,
514509 xof_len,
515510 }
516511 }
517512
518- /// Apply XOF length parameter adjustments to a Blake2 hasher state.
519- ///
520- /// The Blake2X specification requires the total output length (xof_len) to be
521- /// incorporated into the parameter block of the root hash computation. This ensures
522- /// that Blake2X(M, L1) and Blake2X(M, L2) produce different outputs when L1 ≠ L2.
523- ///
524- /// This helper function encapsulates the word-size specific logic for both
525- /// Blake2b (64-bit) and Blake2s (32-bit) variants.
526- fn xof_parameter_adjustment( hasher: & mut $base_core, xof_len: $xof_len_type) {
513+ /// Apply XOF length parameter to the hasher state.
514+ fn apply_xof_param( hasher: & mut $base_core, xof_len: $xof_len_type) {
527515 let xof_param_vec = if core:: mem:: size_of:: <$word>( ) == 8 {
528516 // Blake2b: xof_length is a u32 in the high 32-bits of parameter word p[1]
529517 let xof_param_val = ( ( xof_len as u64 ) << 32 ) as $word;
530- $vec:: new( 0 as $word, xof_param_val, 0 as $word, 0 as $word)
531-
518+ $vec:: new( 0 , xof_param_val, 0 , 0 )
532519 } else {
533- // Blake2s: xof_digest_length is a u32 in parameter word p[3].
534- // We start with a standard Blake2s parameter block and then
535- // XOR in the xof_len to modify the h state corresponding to p[3].
536- let xof_param_val = xof_len as $word;
537- // The parameter p[3] is the 4th element of the first SIMD vector.
538- $vec:: new( 0 , 0 , 0 , xof_param_val)
520+ // Blake2s: xof_digest_length is a u32 in parameter word p[3]
521+ $vec:: new( 0 , 0 , 0 , xof_len as $word)
539522 } ;
540523 hasher. h[ 0 ] = hasher. h[ 0 ] ^ xof_param_vec;
541524 #[ cfg( feature = "reset" ) ]
542525 { hasher. h0[ 0 ] = hasher. h0[ 0 ] ^ xof_param_vec; }
543526 }
544527
545- /// Create Blake2 hasher specifically for Blake2X root hash computation.
546- fn new_blake2x_root_hasher ( xof_len: $xof_len_type) -> $base_core {
528+ /// Create Blake2 hasher for Blake2X root hash computation.
529+ fn new_root_hasher ( xof_len: $xof_len_type) -> $base_core {
547530 let mut hasher = <$base_core>:: new_with_params( & [ ] , & [ ] , 0 , $hash_size) ;
548- Self :: xof_parameter_adjustment ( & mut hasher, xof_len) ;
531+ Self :: apply_xof_param ( & mut hasher, xof_len) ;
549532 hasher
550533 }
551534
552535 /// Create Blake2 hasher for a keyed Blake2X root hash.
553- ///
554- /// This is similar to new_blake2x_root_hasher but includes the key length
555- /// in the parameter block for proper keyed hashing support.
556- fn new_blake2x_root_hasher_with_key( key: & [ u8 ] , xof_len: $xof_len_type) -> $base_core {
536+ fn new_keyed_root_hasher( key: & [ u8 ] , xof_len: $xof_len_type) -> $base_core {
557537 let mut hasher = <$base_core>:: new_with_params( & [ ] , & [ ] , key. len( ) , $hash_size) ;
558- Self :: xof_parameter_adjustment ( & mut hasher, xof_len) ;
538+ Self :: apply_xof_param ( & mut hasher, xof_len) ;
559539 hasher
560540 }
561541 }
562542
563543 impl Default for $core_name {
564544 fn default ( ) -> Self {
565- Self :: new( <$xof_len_type>:: MAX ) // Unknown size by default
545+ Self :: new( <$xof_len_type>:: MAX )
566546 }
567547 }
568548
@@ -587,10 +567,8 @@ macro_rules! blake2x_impl {
587567 type ReaderCore = $reader_name;
588568
589569 fn finalize_xof_core( & mut self , buffer: & mut Buffer <Self >) -> Self :: ReaderCore {
590- // Finalize the root hash H0
591570 let mut root_output = Output :: <$base_core>:: default ( ) ;
592571 self . root_hasher. finalize_variable_core( buffer, & mut root_output) ;
593-
594572 $reader_name:: new( root_output. into( ) , self . xof_len)
595573 }
596574 }
@@ -628,10 +606,8 @@ macro_rules! blake2x_impl {
628606 serialized_state: & digest:: crypto_common:: hazmat:: SerializedState <Self >,
629607 ) -> Result <Self , digest:: crypto_common:: hazmat:: DeserializeStateError > {
630608 let len_bytes = core:: mem:: size_of:: <$xof_len_type>( ) ;
631- let mut xof_len_bytes = [ 0u8 ; 8 ] ; // Max size for either u16 or u32
632- xof_len_bytes[ ..len_bytes] . copy_from_slice( & serialized_state[ ..len_bytes] ) ;
633609 let xof_len = <$xof_len_type>:: from_le_bytes(
634- xof_len_bytes [ ..len_bytes] . try_into( ) . unwrap( )
610+ serialized_state [ ..len_bytes] . try_into( ) . unwrap( )
635611 ) ;
636612 Ok ( Self :: new( xof_len) )
637613 }
@@ -642,7 +618,6 @@ macro_rules! blake2x_impl {
642618 pub struct $reader_name {
643619 root_hash: [ u8 ; $hash_size] ,
644620 node_offset: u32 ,
645- position: usize ,
646621 remaining: $xof_len_type,
647622 total_xof_len: $xof_len_type,
648623 }
@@ -652,25 +627,13 @@ macro_rules! blake2x_impl {
652627 Self {
653628 root_hash,
654629 node_offset: 0 ,
655- position: 0 ,
656630 remaining: xof_len,
657631 total_xof_len: xof_len,
658632 }
659633 }
660- }
661-
662- impl BlockSizeUser for $reader_name {
663- type BlockSize = $reader_block_size;
664- }
665634
666-
667- impl $reader_name {
668635 /// Build expansion node parameter array for Blake2X.
669- ///
670- /// Creates a unified parameter word array that encodes the expansion node
671- /// parameters according to the Blake2X specification. This handles the
672- /// word-size differences between Blake2b and Blake2s variants.
673- fn build_expansion_node_params(
636+ fn build_node_params(
674637 node_offset: u32 ,
675638 output_size: usize ,
676639 total_xof_len: $xof_len_type
@@ -679,115 +642,77 @@ macro_rules! blake2x_impl {
679642
680643 if core:: mem:: size_of:: <$word>( ) == 4 {
681644 // Blake2s (32-bit words)
682- // p[0]: digest_len | key_len (0) | fanout (0) | depth (0)
683645 p[ 0 ] = output_size as $word;
684- // p[1]: leaf_length (should be hash_size for Blake2Xs per specification)
685646 p[ 1 ] = $hash_size as $word;
686- // p[2]: node_offset (the block counter)
687647 p[ 2 ] = node_offset as $word;
688- // p[3]: xof_len(u16) | node_depth(u8, 0) << 16 | inner_len(u8, hash_size) << 24
689- let xof_len = total_xof_len as $word;
690- let node_depth = 0 as $word;
691- let inner_len = $hash_size as $word;
692- p[ 3 ] = xof_len | ( node_depth << 16 ) | ( inner_len << 24 ) ;
648+ // xof_len(u16) | node_depth(u8, 0) << 16 | inner_len(u8, hash_size) << 24
649+ p[ 3 ] = ( total_xof_len as $word) | ( ( $hash_size as $word) << 24 ) ;
693650 } else {
694651 // Blake2b (64-bit words)
695-
696- // p[0]: Contains bytes 0-7 with digest_length, key_length, fanout, depth, leaf_length
697- let digest_length = output_size as u64 ;
698- let key_length = 0u64 ; // No key for expansion nodes
699- let fanout = 0u64 ; // Unlimited fanout
700- let depth = 0u64 ; // Unlimited depth
701- let leaf_length = ( $hash_size as u64 ) << 32 ;
702- p[ 0 ] = ( digest_length | ( key_length << 8 ) | ( fanout << 16 ) | ( depth << 24 ) | leaf_length) as $word;
703-
704- // p[1]: Contains bytes 8-15 with node_offset (with XOF length in upper 32 bits)
705- // For Blake2b expansion nodes, we include the XOF length in the upper bits
706- let node_offset_full = ( ( total_xof_len as u64 ) << 32 ) + ( node_offset as u64 ) ;
707- p[ 1 ] = node_offset_full as $word;
708-
709- // p[2]: Contains bytes 16-23 with node_depth, inner_length
710- let node_depth = 0u64 ; // Leaf level
711- let inner_length = ( $hash_size as u64 ) << 8 ;
712- p[ 2 ] = ( node_depth | inner_length) as $word;
713-
714- // p[3..7]: All zeros (already initialized)
652+ // p[0]: digest_length | key_length(0) << 8 | fanout(0) << 16 | depth(0) << 24 | leaf_length << 32
653+ p[ 0 ] = ( output_size as u64 | ( ( $hash_size as u64 ) << 32 ) ) as $word;
654+ // p[1]: node_offset with XOF length in upper 32 bits
655+ p[ 1 ] = ( ( ( total_xof_len as u64 ) << 32 ) | ( node_offset as u64 ) ) as $word;
656+ // p[2]: node_depth(0) | inner_length << 8
657+ p[ 2 ] = ( ( $hash_size as u64 ) << 8 ) as $word;
715658 }
716659
717660 p
718661 }
719662
720663 /// Create hasher with expansion node parameter state.
721- ///
722- /// Initializes a Blake2 hasher with the given parameter array by XORing
723- /// the initialization vector with the parameter block.
724664 fn create_hasher_with_params( p: & [ $word; 8 ] ) -> $base_core {
725- // Initialize state h = IV ^ p
726- let h = [
665+ let mut node = <$base_core> :: new_with_params ( & [ ] , & [ ] , 0 , $hash_size ) ;
666+ node . h = [
727667 $vec:: new( $IV[ 0 ] , $IV[ 1 ] , $IV[ 2 ] , $IV[ 3 ] ) ^ $vec:: new( p[ 0 ] , p[ 1 ] , p[ 2 ] , p[ 3 ] ) ,
728668 $vec:: new( $IV[ 4 ] , $IV[ 5 ] , $IV[ 6 ] , $IV[ 7 ] ) ^ $vec:: new( p[ 4 ] , p[ 5 ] , p[ 6 ] , p[ 7 ] ) ,
729669 ] ;
730-
731- // Create hasher with expansion node parameters
732- let mut node = <$base_core>:: new_with_params( & [ ] , & [ ] , 0 , $hash_size) ;
733- node. h = h;
734670 #[ cfg( feature = "reset" ) ]
735- { node. h0 = h; }
736-
671+ { node. h0 = node. h; }
737672 node
738673 }
739674
740675 /// Blake2X expansion node function.
741- ///
742- /// Implements B2(node_offset, output_size, H0) from the Blake2X specification.
743- /// Each expansion node computes Blake2(H0) with specific tree parameters that
744- /// encode the node offset and output size.
745- fn expand_node( h0: & [ u8 ; $hash_size] , node_offset: u32 , output_size: usize , total_xof_len: $xof_len_type) -> [ u8 ; $hash_size] {
746- // Build expansion node parameter array
747- let p = Self :: build_expansion_node_params( node_offset, output_size, total_xof_len) ;
748-
749- // Create the node hasher with the parameter state
676+ fn expand_node(
677+ & self ,
678+ node_offset: u32 ,
679+ output_size: usize
680+ ) -> [ u8 ; $hash_size] {
681+ let p = Self :: build_node_params( node_offset, output_size, self . total_xof_len) ;
750682 let mut node_hasher = Self :: create_hasher_with_params( & p) ;
751683
752- // The hashing logic is now unified for both Blake2b and Blake2s
684+ // Hash the root hash as a single block
753685 let mut buffer = LazyBuffer :: default ( ) ;
754- buffer. digest_blocks( h0 , |blocks| {
686+ buffer. digest_blocks( & self . root_hash , |blocks| {
755687 node_hasher. update_blocks( blocks) ;
756688 } ) ;
757689
758- // Finalize and return
759- let mut output = [ 0u8 ; $hash_size] ;
760690 let mut var_output = Output :: <$base_core>:: default ( ) ;
761691 node_hasher. finalize_variable_core( & mut buffer, & mut var_output) ;
692+
693+ let mut output = [ 0u8 ; $hash_size] ;
762694 output[ ..output_size] . copy_from_slice( & var_output[ ..output_size] ) ;
763695 output
764696 }
765697 }
766698
699+ impl BlockSizeUser for $reader_name {
700+ type BlockSize = $reader_block_size;
701+ }
702+
767703 impl XofReaderCore for $reader_name {
768704 fn read_block( & mut self ) -> Block <Self > {
769705 let mut block = Block :: <Self >:: default ( ) ;
770706
771707 if self . remaining == 0 {
772- return block; // Return zeros if no more output needed
708+ return block;
773709 }
774710
775- // Determine output size for this block
776- let output_size = if self . remaining >= $hash_size as $xof_len_type {
777- $hash_size
778- } else {
779- self . remaining as usize
780- } ;
781-
782- // Blake2x expansion: Hash H0 with specific tree parameters for this node
783- let node_output = Self :: expand_node( & self . root_hash, self . node_offset, output_size, self . total_xof_len) ;
784-
785- // Copy output to block
711+ let output_size = core:: cmp:: min( self . remaining as usize , $hash_size) ;
712+ let node_output = self . expand_node( self . node_offset, output_size) ;
786713 block[ ..output_size] . copy_from_slice( & node_output[ ..output_size] ) ;
787714
788- // Update state
789715 self . node_offset += 1 ;
790- self . position += output_size;
791716 self . remaining -= output_size as $xof_len_type;
792717
793718 block
@@ -800,7 +725,6 @@ macro_rules! blake2x_impl {
800725 }
801726 }
802727
803-
804728 // Use buffer_xof macro to create the wrapper types
805729 digest:: buffer_xof!(
806730 #[ doc=$vardoc]
0 commit comments