Skip to content

Commit dd838fe

Browse files
committed
feat: download BLAKE2X test vectors at test time
1 parent c6ee00d commit dd838fe

File tree

6 files changed

+102
-6391
lines changed

6 files changed

+102
-6391
lines changed

blake2/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ rand = "0.8"
2525
paste = "1.0"
2626
serde = { version = "1.0", features = ["derive"] }
2727
serde_json = "1.0"
28+
ureq = "2"
2829

2930
[features]
3031
default = ["alloc"]

blake2/src/macros.rs

Lines changed: 45 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)