From ff3ee0c9c7b675023a12db056f25063432e8a6cf Mon Sep 17 00:00:00 2001 From: Noam Kleinburd Date: Thu, 3 Apr 2025 11:25:43 +0300 Subject: [PATCH 1/4] Implement Blake2Xb --- blake2/src/blake2x.rs | 182 ++++++++++++++++++++++++++++++++++++++++++ blake2/src/lib.rs | 132 +++++++++++++++++++++++++++++- blake2/src/macros.rs | 61 +++----------- 3 files changed, 325 insertions(+), 50 deletions(-) create mode 100644 blake2/src/blake2x.rs diff --git a/blake2/src/blake2x.rs b/blake2/src/blake2x.rs new file mode 100644 index 000000000..40d85dba4 --- /dev/null +++ b/blake2/src/blake2x.rs @@ -0,0 +1,182 @@ +use crate::Blake2Parameters; +use crate::Blake2bVarCore; +use digest::{ + ExtendableOutput, Update, XofReader, + block_buffer::{LazyBuffer, ReadBuffer}, + consts::U64, + core_api::{Buffer, BufferKindUser, UpdateCore, VariableOutputCore}, +}; + +use super::{Blake2b512, BlockSizeUser, InvalidLength, Unsigned}; + +/// Blake2Xb root hasher +pub struct Blake2Xb { + root_hasher: Blake2bVarCore, + buffer: LazyBuffer<::BlockSize>, + max_length: Option, +} + +impl Blake2Xb { + /// Create new instance using provided key. + /// + /// Setting key to `None` indicates unkeyed usage. + /// + /// # Errors + /// + /// If key is `Some`, then its length should not be zero or bigger + /// than the block size. If this conditions is false the method will + /// return an error. + #[inline] + pub fn new(key: Option<&[u8]>, max_length: Option) -> Result { + let kl = key.map_or(0, |k| k.len()); + let bs = ::BlockSize::USIZE; + if key.is_some() && kl == 0 || kl > bs { + return Err(InvalidLength); + } + + let params = Blake2Parameters { + digest_length: 64, + key_size: kl.try_into().unwrap(), + fanout: 1, + depth: 1, + xof_digest_length: Some(max_length.unwrap_or(u32::MAX)), + ..<_>::default() + }; + let root_hasher = Blake2bVarCore::from_params(params); + + let mut hasher = Self { + root_hasher, + buffer: <_>::default(), + max_length, + }; + + if let Some(k) = key { + // Update state with key + hasher.update(k); + // Pad key with zeros + let pad_len = 128 - kl; + let padding = [0; 128]; + hasher.update(&padding[..pad_len]); + } + + Ok(hasher) + } +} + +pub struct Blake2bXReader { + h0: [u8; 64], + buffer: ReadBuffer<::BlockSize>, + node_offset: u32, + total_length: u32, +} + +impl BlockSizeUser for Blake2bXReader { + type BlockSize = U64; +} + +impl BufferKindUser for Blake2bXReader { + type BufferKind = ::BufferKind; +} + +impl XofReader for Blake2bXReader { + fn read(&mut self, buffer: &mut [u8]) { + let Self { buffer: buf, .. } = self; + buf.read(buffer, |block| { + let digest_length = 64.min(self.total_length - self.node_offset * 64) as u8; + + let mut hasher = Blake2bVarCore::from_params(Blake2Parameters { + digest_length, + leaf_length: 64, + node_offset: self.node_offset as u64, + xof_digest_length: Some(self.total_length), + inner_length: 64, + ..<_>::default() + }); + + self.node_offset += 1; + + hasher.finalize_variable_core(&mut Buffer::::new(&self.h0), block); + }); + } +} + +#[cfg(feature = "std")] +impl std::io::Read for Blake2bXReader { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + XofReader::read(self, buf); + Ok(buf.len()) + } +} + +impl BlockSizeUser for Blake2Xb { + type BlockSize = ::BlockSize; +} + +impl BufferKindUser for Blake2Xb { + type BufferKind = ::BufferKind; +} + +impl Update for Blake2Xb { + fn update(&mut self, data: &[u8]) { + let Self { + root_hasher, + buffer, + .. + } = self; + buffer.digest_blocks(data, |blocks| root_hasher.update_blocks(blocks)); + } +} + +impl ExtendableOutput for Blake2Xb { + type Reader = Blake2bXReader; + + fn finalize_xof(self) -> Self::Reader { + let mut m = <_>::default(); + let Self { + mut root_hasher, + mut buffer, + max_length, + } = self; + root_hasher.finalize_variable_core(&mut buffer, &mut m); + + let mut h0 = [0; 64]; + h0.copy_from_slice(&m); + + Blake2bXReader { + h0, + buffer: <_>::default(), + node_offset: 0, + total_length: max_length.unwrap_or(u32::MAX), + } + } +} + +#[test] +fn test() { + let seed = [ + 0x72, 0x01, 0xa8, 0x01, 0xc4, 0xf9, 0x95, 0x7c, 0x76, 0x65, 0xc2, 0xfd, 0x42, 0x76, 0x1f, + 0x5d, 0xa6, 0xc0, 0x55, 0x51, 0xf1, 0x5c, 0x21, 0x53, 0x78, 0x8b, 0xa7, 0x0d, 0x95, 0x60, + 0xd7, 0xee, + ]; + let mut b = crate::blake2xb(&seed[..]); + + let expected = [ + 0x4b, 0xd4, 0x10, 0x91, 0x1b, 0xf5, 0xdc, 0xb1, 0x99, 0x2e, 0xb7, 0x23, 0x83, 0x54, 0x98, + 0xda, 0xbf, 0x58, 0xce, 0x34, 0x82, 0x39, 0x3c, 0x2b, 0xd2, 0xaa, 0x3b, 0x79, 0xc4, 0xe2, + 0x2c, 0xb8, 0x06, 0xe6, 0x31, 0x65, 0x2e, 0x2a, 0xff, 0x3c, 0x33, 0x98, 0x64, 0x51, 0x2e, + 0xdd, 0xc1, 0xe0, 0x27, 0x17, 0xb2, 0xeb, 0xd4, 0x99, 0xa6, 0xe9, 0xe1, 0xb8, 0x96, 0x7d, + 0x23, 0x00, 0x54, 0xa4, 0x16, 0x58, 0xa3, 0xf4, 0xfe, 0x04, 0xb0, 0x62, 0x9f, 0xc8, 0xe6, + 0x9f, 0x6b, 0xf5, 0x1d, 0xe7, 0x59, 0x09, 0x0c, 0xe5, 0x4d, 0x82, 0xc0, 0xda, 0xda, 0xc9, + 0x21, 0xa3, 0x3f, 0x18, 0xb1, 0xb6, 0xbe, 0x8e, 0x9b, 0x12, 0x4d, 0x46, 0xf2, 0x6b, 0x9c, + 0xb0, 0xdb, 0xec, 0xae, 0x21, 0xf5, 0x04, 0x88, 0x6b, 0xc0, 0x75, 0x3e, 0x9e, 0x62, 0xd4, + 0x98, 0xdf, 0xb0, 0x18, 0xb3, 0x4a, 0x14, 0xd5, 0xfc, 0xee, 0xf4, 0xc0, 0xd9, 0x78, 0xe1, + 0xda, 0x27, 0xa0, 0x71, 0x56, 0x4d, 0x7e, 0xbd, 0x56, 0xfd, 0x09, 0x27, 0x65, 0x19, 0x9e, + 0x17, 0x91, 0xdd, 0xad, 0x7b, 0x60, 0x1d, 0x26, 0xce, 0x39, 0x26, 0x39, 0xad, 0x17, 0xc2, + 0xeb, 0x60, 0x7f, 0x9e, 0x82, 0x78, 0x2e, 0x5f, 0x72, 0x5d, 0x19, 0x69, 0xb6, 0xb4, 0xf0, + 0x8b, 0x91, 0x9f, 0xf4, 0xc7, 0xf4, 0x1c, 0x04, 0xa9, 0xb8, 0xee, 0x08, + ]; + let mut buf = [0; 64 * 3]; + b.read(&mut buf); + assert_eq!(expected, buf); +} diff --git a/blake2/src/lib.rs b/blake2/src/lib.rs index 7b9794f82..880b7b0e1 100644 --- a/blake2/src/lib.rs +++ b/blake2/src/lib.rs @@ -12,7 +12,7 @@ pub use digest::{self, Digest}; -use core::{fmt, marker::PhantomData, ops::Div}; +use core::{fmt, marker::PhantomData}; use digest::{ CustomizedInit, FixedOutput, HashMarker, InvalidOutputSize, MacMarker, Output, Update, VarOutputCustomized, @@ -22,7 +22,7 @@ use digest::{ UpdateCore, VariableOutputCore, }, block_buffer::{Lazy, LazyBuffer}, - consts::{U4, U16, U32, U64, U128}, + consts::{U16, U32, U64, U128}, crypto_common::{InvalidLength, Key, KeyInit, KeySizeUser}, typenum::{IsLessOrEqual, True, Unsigned}, }; @@ -33,6 +33,7 @@ use digest::{FixedOutputReset, Reset}; use digest::zeroize::{Zeroize, ZeroizeOnDrop}; mod as_bytes; +mod blake2x; mod consts; mod simd; @@ -95,6 +96,14 @@ pub type Blake2b512 = Blake2b; blake2_mac_impl!(Blake2bMac, Blake2bVarCore, U64, "Blake2b MAC function"); +/// Create a blake2xb generator with maximum output +pub fn blake2xb(seed: &[u8]) -> blake2x::Blake2bXReader { + use digest::ExtendableOutput; + blake2x::Blake2Xb::new(Some(seed), None) + .unwrap() + .finalize_xof() +} + /// BLAKE2b-512 MAC state. pub type Blake2bMac512 = Blake2bMac; @@ -149,3 +158,122 @@ blake2_mac_impl!(Blake2sMac, Blake2sVarCore, U32, "Blake2s MAC function"); /// BLAKE2s-256 MAC state. pub type Blake2sMac256 = Blake2sMac; + +#[derive(Clone, Copy, Default)] +struct Blake2Parameters<'a> { + digest_length: u8, + key_size: u8, + fanout: u8, + depth: u8, + leaf_length: u32, + node_offset: u64, + xof_digest_length: Option, + node_depth: u8, + inner_length: u8, + salt: &'a [u8], + persona: &'a [u8], +} + +macro_rules! pair_from_bytes { + ($word:ident, $data:expr, $dword_len:literal) => { + if $data.len() < $dword_len { + let mut padded_data = [0; $dword_len]; + for i in 0..$data.len() { + padded_data[i] = $data[i]; + } + ( + $word::from_le_bytes(padded_data[0..$dword_len / 2].try_into().unwrap()), + $word::from_le_bytes( + padded_data[$dword_len / 2..padded_data.len()] + .try_into() + .unwrap(), + ), + ) + } else { + ( + $word::from_le_bytes($data[0..$data.len() / 2].try_into().unwrap()), + $word::from_le_bytes($data[$data.len() / 2..$data.len()].try_into().unwrap()), + ) + } + }; +} + +// Private helper trait +trait ToParamBlock { + fn to_param_block(self) -> [W; 8]; +} + +impl ToParamBlock for Blake2Parameters<'_> { + fn to_param_block(self) -> [u64; 8] { + assert!(self.key_size <= 64); + assert!(self.digest_length <= 64); + + // The number of bytes needed to express two words. + let length = 16; + assert!(self.salt.len() <= length); + assert!(self.persona.len() <= length); + + // Build a parameter block + let mut p = [0; 8]; + p[0] = (self.digest_length as u64) + ^ ((self.key_size as u64) << 8) + ^ ((self.fanout as u64) << 16) + ^ ((self.depth as u64) << 24) + ^ ((self.leaf_length as u64) << 32); + + p[1] = match self.xof_digest_length { + None => self.node_offset, + Some(xof_len) => { + assert!(self.node_offset <= u32::MAX as u64); + self.node_offset ^ ((xof_len as u64) << 32) + } + }; + p[2] = (self.node_depth as u64) ^ ((self.inner_length as u64) << 8); + + // salt is two words long + (p[4], p[5]) = pair_from_bytes!(u64, self.salt, 16); + // persona is also two words long + (p[6], p[7]) = pair_from_bytes!(u64, self.persona, 16); + + p + } +} + +impl ToParamBlock for Blake2Parameters<'_> { + fn to_param_block(self) -> [u32; 8] { + assert!(self.key_size <= 32); + assert!(self.digest_length <= 32); + + // The number of bytes needed to express two words. + let length = 8; + assert!(self.salt.len() <= length); + assert!(self.persona.len() <= length); + + // Build a parameter block + let mut p = [0; 8]; + p[0] = (self.digest_length as u32) + ^ ((self.key_size as u32) << 8) + ^ ((self.fanout as u32) << 16) + ^ ((self.depth as u32) << 24); + p[1] = self.leaf_length.to_le(); + + (p[2], p[3]) = match self.xof_digest_length { + None => { + assert!(self.node_offset < 1 << 48); + pair_from_bytes!(u32, self.node_offset.to_le_bytes(), 8) + } + Some(xof_len) => { + assert!(self.node_offset <= u32::MAX as u64); + ((self.node_offset as u32).to_le(), xof_len.to_le()) + } + }; + p[3] ^= ((self.node_depth as u32) << 16) ^ ((self.inner_length as u32) << 24); + + // salt is two words long + (p[4], p[5]) = pair_from_bytes!(u32, self.salt, 8); + // persona is also two words long + (p[6], p[7]) = pair_from_bytes!(u32, self.persona, 8); + + p + } +} diff --git a/blake2/src/macros.rs b/blake2/src/macros.rs index a29fcdd2c..d1027494c 100644 --- a/blake2/src/macros.rs +++ b/blake2/src/macros.rs @@ -30,55 +30,20 @@ macro_rules! blake2_impl { key_size: usize, output_size: usize, ) -> Self { - assert!(key_size <= $bytes::to_usize()); - assert!(output_size <= $bytes::to_usize()); - - // The number of bytes needed to express two words. - let length = $bytes::to_usize() / 4; - assert!(salt.len() <= length); - assert!(persona.len() <= length); - - // Build a parameter block - let mut p = [0 as $word; 8]; - p[0] = 0x0101_0000 ^ ((key_size as $word) << 8) ^ (output_size as $word); - - // salt is two words long - if salt.len() < length { - let mut padded_salt = Array::>::Output>::default(); - for i in 0..salt.len() { - padded_salt[i] = salt[i]; - } - p[4] = $word::from_le_bytes(padded_salt[0..length / 2].try_into().unwrap()); - p[5] = $word::from_le_bytes( - padded_salt[length / 2..padded_salt.len()] - .try_into() - .unwrap(), - ); - } else { - p[4] = $word::from_le_bytes(salt[0..salt.len() / 2].try_into().unwrap()); - p[5] = - $word::from_le_bytes(salt[salt.len() / 2..salt.len()].try_into().unwrap()); - } - - // persona is also two words long - if persona.len() < length { - let mut padded_persona = Array::>::Output>::default(); - for i in 0..persona.len() { - padded_persona[i] = persona[i]; - } - p[6] = $word::from_le_bytes(padded_persona[0..length / 2].try_into().unwrap()); - p[7] = $word::from_le_bytes( - padded_persona[length / 2..padded_persona.len()] - .try_into() - .unwrap(), - ); - } else { - p[6] = $word::from_le_bytes(persona[0..length / 2].try_into().unwrap()); - p[7] = $word::from_le_bytes( - persona[length / 2..persona.len()].try_into().unwrap(), - ); - } + let p = Blake2Parameters { + digest_length: output_size.try_into().unwrap(), + key_size: key_size.try_into().unwrap(), + fanout: 1, + depth: 1, + salt, + persona, + ..<_>::default() + }; + Self::from_params(p) + } + fn from_params(p: Blake2Parameters) -> Self { + let p = p.to_param_block(); let h = [ Self::iv0() ^ $vec::new(p[0], p[1], p[2], p[3]), Self::iv1() ^ $vec::new(p[4], p[5], p[6], p[7]), From a349c390405f573abcda816674e0ddb40f302c1b Mon Sep 17 00:00:00 2001 From: Noam Kleinburd Date: Sun, 6 Apr 2025 10:09:40 +0300 Subject: [PATCH 2/4] Make Blake2XbReader public --- blake2/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blake2/src/lib.rs b/blake2/src/lib.rs index 880b7b0e1..ec26a72a8 100644 --- a/blake2/src/lib.rs +++ b/blake2/src/lib.rs @@ -42,6 +42,7 @@ mod simd; mod macros; use as_bytes::AsBytes; +pub use blake2x::Blake2bXReader; use consts::{BLAKE2B_IV, BLAKE2S_IV}; use simd::{Vector4, u32x4, u64x4}; @@ -97,7 +98,7 @@ pub type Blake2b512 = Blake2b; blake2_mac_impl!(Blake2bMac, Blake2bVarCore, U64, "Blake2b MAC function"); /// Create a blake2xb generator with maximum output -pub fn blake2xb(seed: &[u8]) -> blake2x::Blake2bXReader { +pub fn blake2xb(seed: &[u8]) -> Blake2bXReader { use digest::ExtendableOutput; blake2x::Blake2Xb::new(Some(seed), None) .unwrap() From 39ef1270df3d2f6afcd5e09ed3c72546c15c6370 Mon Sep 17 00:00:00 2001 From: Noam Kleinburd Date: Sun, 6 Apr 2025 15:42:01 +0300 Subject: [PATCH 3/4] Rename Blake2bXReader -> Blake2XbReader and add documentation --- blake2/src/blake2x.rs | 15 ++++++++------- blake2/src/lib.rs | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/blake2/src/blake2x.rs b/blake2/src/blake2x.rs index 40d85dba4..23d2d609d 100644 --- a/blake2/src/blake2x.rs +++ b/blake2/src/blake2x.rs @@ -63,22 +63,23 @@ impl Blake2Xb { } } -pub struct Blake2bXReader { +/// Finalized XOF instance over Blake2b +pub struct Blake2XbReader { h0: [u8; 64], buffer: ReadBuffer<::BlockSize>, node_offset: u32, total_length: u32, } -impl BlockSizeUser for Blake2bXReader { +impl BlockSizeUser for Blake2XbReader { type BlockSize = U64; } -impl BufferKindUser for Blake2bXReader { +impl BufferKindUser for Blake2XbReader { type BufferKind = ::BufferKind; } -impl XofReader for Blake2bXReader { +impl XofReader for Blake2XbReader { fn read(&mut self, buffer: &mut [u8]) { let Self { buffer: buf, .. } = self; buf.read(buffer, |block| { @@ -101,7 +102,7 @@ impl XofReader for Blake2bXReader { } #[cfg(feature = "std")] -impl std::io::Read for Blake2bXReader { +impl std::io::Read for Blake2XbReader { #[inline] fn read(&mut self, buf: &mut [u8]) -> std::io::Result { XofReader::read(self, buf); @@ -129,7 +130,7 @@ impl Update for Blake2Xb { } impl ExtendableOutput for Blake2Xb { - type Reader = Blake2bXReader; + type Reader = Blake2XbReader; fn finalize_xof(self) -> Self::Reader { let mut m = <_>::default(); @@ -143,7 +144,7 @@ impl ExtendableOutput for Blake2Xb { let mut h0 = [0; 64]; h0.copy_from_slice(&m); - Blake2bXReader { + Blake2XbReader { h0, buffer: <_>::default(), node_offset: 0, diff --git a/blake2/src/lib.rs b/blake2/src/lib.rs index ec26a72a8..b81c10181 100644 --- a/blake2/src/lib.rs +++ b/blake2/src/lib.rs @@ -42,7 +42,7 @@ mod simd; mod macros; use as_bytes::AsBytes; -pub use blake2x::Blake2bXReader; +pub use blake2x::Blake2XbReader; use consts::{BLAKE2B_IV, BLAKE2S_IV}; use simd::{Vector4, u32x4, u64x4}; @@ -98,7 +98,7 @@ pub type Blake2b512 = Blake2b; blake2_mac_impl!(Blake2bMac, Blake2bVarCore, U64, "Blake2b MAC function"); /// Create a blake2xb generator with maximum output -pub fn blake2xb(seed: &[u8]) -> Blake2bXReader { +pub fn blake2xb(seed: &[u8]) -> Blake2XbReader { use digest::ExtendableOutput; blake2x::Blake2Xb::new(Some(seed), None) .unwrap() From 25d307e3c72fb6377f066318ffde7308d59de58b Mon Sep 17 00:00:00 2001 From: Noam Kleinburd Date: Wed, 18 Jun 2025 21:06:57 +0300 Subject: [PATCH 4/4] Move Xof test to tests/xof.rs --- blake2/src/blake2x.rs | 29 ----------------------------- blake2/tests/xof.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 29 deletions(-) create mode 100644 blake2/tests/xof.rs diff --git a/blake2/src/blake2x.rs b/blake2/src/blake2x.rs index 23d2d609d..5ed273045 100644 --- a/blake2/src/blake2x.rs +++ b/blake2/src/blake2x.rs @@ -152,32 +152,3 @@ impl ExtendableOutput for Blake2Xb { } } } - -#[test] -fn test() { - let seed = [ - 0x72, 0x01, 0xa8, 0x01, 0xc4, 0xf9, 0x95, 0x7c, 0x76, 0x65, 0xc2, 0xfd, 0x42, 0x76, 0x1f, - 0x5d, 0xa6, 0xc0, 0x55, 0x51, 0xf1, 0x5c, 0x21, 0x53, 0x78, 0x8b, 0xa7, 0x0d, 0x95, 0x60, - 0xd7, 0xee, - ]; - let mut b = crate::blake2xb(&seed[..]); - - let expected = [ - 0x4b, 0xd4, 0x10, 0x91, 0x1b, 0xf5, 0xdc, 0xb1, 0x99, 0x2e, 0xb7, 0x23, 0x83, 0x54, 0x98, - 0xda, 0xbf, 0x58, 0xce, 0x34, 0x82, 0x39, 0x3c, 0x2b, 0xd2, 0xaa, 0x3b, 0x79, 0xc4, 0xe2, - 0x2c, 0xb8, 0x06, 0xe6, 0x31, 0x65, 0x2e, 0x2a, 0xff, 0x3c, 0x33, 0x98, 0x64, 0x51, 0x2e, - 0xdd, 0xc1, 0xe0, 0x27, 0x17, 0xb2, 0xeb, 0xd4, 0x99, 0xa6, 0xe9, 0xe1, 0xb8, 0x96, 0x7d, - 0x23, 0x00, 0x54, 0xa4, 0x16, 0x58, 0xa3, 0xf4, 0xfe, 0x04, 0xb0, 0x62, 0x9f, 0xc8, 0xe6, - 0x9f, 0x6b, 0xf5, 0x1d, 0xe7, 0x59, 0x09, 0x0c, 0xe5, 0x4d, 0x82, 0xc0, 0xda, 0xda, 0xc9, - 0x21, 0xa3, 0x3f, 0x18, 0xb1, 0xb6, 0xbe, 0x8e, 0x9b, 0x12, 0x4d, 0x46, 0xf2, 0x6b, 0x9c, - 0xb0, 0xdb, 0xec, 0xae, 0x21, 0xf5, 0x04, 0x88, 0x6b, 0xc0, 0x75, 0x3e, 0x9e, 0x62, 0xd4, - 0x98, 0xdf, 0xb0, 0x18, 0xb3, 0x4a, 0x14, 0xd5, 0xfc, 0xee, 0xf4, 0xc0, 0xd9, 0x78, 0xe1, - 0xda, 0x27, 0xa0, 0x71, 0x56, 0x4d, 0x7e, 0xbd, 0x56, 0xfd, 0x09, 0x27, 0x65, 0x19, 0x9e, - 0x17, 0x91, 0xdd, 0xad, 0x7b, 0x60, 0x1d, 0x26, 0xce, 0x39, 0x26, 0x39, 0xad, 0x17, 0xc2, - 0xeb, 0x60, 0x7f, 0x9e, 0x82, 0x78, 0x2e, 0x5f, 0x72, 0x5d, 0x19, 0x69, 0xb6, 0xb4, 0xf0, - 0x8b, 0x91, 0x9f, 0xf4, 0xc7, 0xf4, 0x1c, 0x04, 0xa9, 0xb8, 0xee, 0x08, - ]; - let mut buf = [0; 64 * 3]; - b.read(&mut buf); - assert_eq!(expected, buf); -} diff --git a/blake2/tests/xof.rs b/blake2/tests/xof.rs new file mode 100644 index 000000000..db9eddc08 --- /dev/null +++ b/blake2/tests/xof.rs @@ -0,0 +1,30 @@ +use blake2::blake2xb; +use digest::XofReader; +use hex_literal::hex; + +#[test] +fn blake2bx() { + let seed = hex!( + "7201a801c4f9957c7665c2fd42761f5d" + "a6c05551f15c2153788ba70d9560d7ee" + ); + let mut b = blake2xb(&seed[..]); + + let expected = hex!( + "4bd410911bf5dcb1992eb723835498da" + "bf58ce3482393c2bd2aa3b79c4e22cb8" + "06e631652e2aff3c339864512eddc1e0" + "2717b2ebd499a6e9e1b8967d230054a4" + "1658a3f4fe04b0629fc8e69f6bf51de7" + "59090ce54d82c0dadac921a33f18b1b6" + "be8e9b124d46f26b9cb0dbecae21f504" + "886bc0753e9e62d498dfb018b34a14d5" + "fceef4c0d978e1da27a071564d7ebd56" + "fd092765199e1791ddad7b601d26ce39" + "2639ad17c2eb607f9e82782e5f725d19" + "69b6b4f08b919ff4c7f41c04a9b8ee08" + ); + let mut buf = [0; 64 * 3]; + b.read(&mut buf); + assert_eq!(expected, buf); +}