Skip to content

Commit 88d053f

Browse files
committed
Implement Blake2Xb
1 parent f2be22d commit 88d053f

File tree

3 files changed

+332
-51
lines changed

3 files changed

+332
-51
lines changed

blake2/src/blake2x.rs

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
use crate::Blake2Parameters;
2+
use crate::Blake2bVarCore;
3+
use digest::{
4+
ExtendableOutput, Update, XofReader,
5+
block_buffer::{LazyBuffer, ReadBuffer},
6+
consts::U64,
7+
core_api::{Buffer, BufferKindUser, UpdateCore, VariableOutputCore},
8+
};
9+
10+
use super::{Blake2b512, BlockSizeUser, InvalidLength, Unsigned};
11+
12+
/// Blake2Xb root hasher
13+
pub struct Blake2Xb {
14+
root_hasher: Blake2bVarCore,
15+
buffer: LazyBuffer<<Blake2b512 as BlockSizeUser>::BlockSize>,
16+
max_length: Option<u32>,
17+
}
18+
19+
impl Blake2Xb {
20+
/// Create new instance using provided key.
21+
///
22+
/// Setting key to `None` indicates unkeyed usage.
23+
///
24+
/// # Errors
25+
///
26+
/// If key is `Some`, then its length should not be zero or bigger
27+
/// than the block size. If this conditions is false the method will
28+
/// return an error.
29+
#[inline]
30+
pub fn new(key: Option<&[u8]>, max_length: Option<u32>) -> Result<Self, InvalidLength> {
31+
let kl = key.map_or(0, |k| k.len());
32+
let bs = <Blake2b512 as BlockSizeUser>::BlockSize::USIZE;
33+
if key.is_some() && kl == 0 || kl > bs {
34+
return Err(InvalidLength);
35+
}
36+
37+
let params = Blake2Parameters {
38+
digest_length: 64,
39+
key_size: kl.try_into().unwrap(),
40+
fanout: 1,
41+
depth: 1,
42+
xof_digest_length: Some(max_length.unwrap_or(u32::MAX)),
43+
..<_>::default()
44+
};
45+
let root_hasher = Blake2bVarCore::from_params(params);
46+
47+
let mut hasher = Self {
48+
root_hasher,
49+
buffer: <_>::default(),
50+
max_length,
51+
};
52+
53+
if let Some(k) = key {
54+
// Update state with key
55+
hasher.update(k);
56+
// Pad key with zeros
57+
let pad_len = 128 - kl;
58+
let padding = [0; 128];
59+
hasher.update(&padding[..pad_len]);
60+
}
61+
62+
Ok(hasher)
63+
}
64+
}
65+
66+
pub struct Blake2bXReader {
67+
h0: [u8; 64],
68+
buffer: ReadBuffer<<Self as BlockSizeUser>::BlockSize>,
69+
node_offset: u32,
70+
total_length: u32,
71+
}
72+
73+
impl BlockSizeUser for Blake2bXReader {
74+
type BlockSize = U64;
75+
}
76+
77+
impl BufferKindUser for Blake2bXReader {
78+
type BufferKind = <Blake2bVarCore as BufferKindUser>::BufferKind;
79+
}
80+
81+
impl XofReader for Blake2bXReader {
82+
fn read(&mut self, buffer: &mut [u8]) {
83+
let Self { buffer: buf, .. } = self;
84+
buf.read(buffer, |block| {
85+
let digest_length = 64.min(self.total_length - self.node_offset * 64) as u8;
86+
87+
let mut hasher = Blake2bVarCore::from_params(Blake2Parameters {
88+
digest_length,
89+
leaf_length: 64,
90+
node_offset: self.node_offset as u64,
91+
xof_digest_length: Some(self.total_length),
92+
inner_length: 64,
93+
..<_>::default()
94+
});
95+
96+
self.node_offset += 1;
97+
98+
hasher.finalize_variable_core(&mut Buffer::<Blake2bVarCore>::new(&self.h0), block);
99+
});
100+
}
101+
}
102+
103+
#[cfg(feature = "std")]
104+
impl std::io::Read for Blake2bXReader {
105+
#[inline]
106+
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
107+
XofReader::read(self, buf);
108+
Ok(buf.len())
109+
}
110+
}
111+
112+
impl BlockSizeUser for Blake2Xb {
113+
type BlockSize = <Blake2bVarCore as BlockSizeUser>::BlockSize;
114+
}
115+
116+
impl BufferKindUser for Blake2Xb {
117+
type BufferKind = <Blake2bVarCore as BufferKindUser>::BufferKind;
118+
}
119+
120+
impl Update for Blake2Xb {
121+
fn update(&mut self, data: &[u8]) {
122+
let Self {
123+
root_hasher,
124+
buffer,
125+
..
126+
} = self;
127+
buffer.digest_blocks(data, |blocks| root_hasher.update_blocks(blocks));
128+
}
129+
}
130+
131+
impl ExtendableOutput for Blake2Xb {
132+
type Reader = Blake2bXReader;
133+
134+
fn finalize_xof(self) -> Self::Reader {
135+
let mut m = <_>::default();
136+
let Self {
137+
mut root_hasher,
138+
mut buffer,
139+
max_length,
140+
} = self;
141+
root_hasher.finalize_variable_core(&mut buffer, &mut m);
142+
143+
let mut h0 = [0; 64];
144+
h0.copy_from_slice(&m);
145+
146+
Blake2bXReader {
147+
h0,
148+
buffer: <_>::default(),
149+
node_offset: 0,
150+
total_length: max_length.unwrap_or(u32::MAX),
151+
}
152+
}
153+
}
154+
155+
#[test]
156+
fn test() {
157+
let seed = [
158+
0x72, 0x01, 0xa8, 0x01, 0xc4, 0xf9, 0x95, 0x7c, 0x76, 0x65, 0xc2, 0xfd, 0x42, 0x76, 0x1f,
159+
0x5d, 0xa6, 0xc0, 0x55, 0x51, 0xf1, 0x5c, 0x21, 0x53, 0x78, 0x8b, 0xa7, 0x0d, 0x95, 0x60,
160+
0xd7, 0xee,
161+
];
162+
let mut b = crate::blake2xb(&seed[..]);
163+
164+
let expected = [
165+
0x4b, 0xd4, 0x10, 0x91, 0x1b, 0xf5, 0xdc, 0xb1, 0x99, 0x2e, 0xb7, 0x23, 0x83, 0x54, 0x98,
166+
0xda, 0xbf, 0x58, 0xce, 0x34, 0x82, 0x39, 0x3c, 0x2b, 0xd2, 0xaa, 0x3b, 0x79, 0xc4, 0xe2,
167+
0x2c, 0xb8, 0x06, 0xe6, 0x31, 0x65, 0x2e, 0x2a, 0xff, 0x3c, 0x33, 0x98, 0x64, 0x51, 0x2e,
168+
0xdd, 0xc1, 0xe0, 0x27, 0x17, 0xb2, 0xeb, 0xd4, 0x99, 0xa6, 0xe9, 0xe1, 0xb8, 0x96, 0x7d,
169+
0x23, 0x00, 0x54, 0xa4, 0x16, 0x58, 0xa3, 0xf4, 0xfe, 0x04, 0xb0, 0x62, 0x9f, 0xc8, 0xe6,
170+
0x9f, 0x6b, 0xf5, 0x1d, 0xe7, 0x59, 0x09, 0x0c, 0xe5, 0x4d, 0x82, 0xc0, 0xda, 0xda, 0xc9,
171+
0x21, 0xa3, 0x3f, 0x18, 0xb1, 0xb6, 0xbe, 0x8e, 0x9b, 0x12, 0x4d, 0x46, 0xf2, 0x6b, 0x9c,
172+
0xb0, 0xdb, 0xec, 0xae, 0x21, 0xf5, 0x04, 0x88, 0x6b, 0xc0, 0x75, 0x3e, 0x9e, 0x62, 0xd4,
173+
0x98, 0xdf, 0xb0, 0x18, 0xb3, 0x4a, 0x14, 0xd5, 0xfc, 0xee, 0xf4, 0xc0, 0xd9, 0x78, 0xe1,
174+
0xda, 0x27, 0xa0, 0x71, 0x56, 0x4d, 0x7e, 0xbd, 0x56, 0xfd, 0x09, 0x27, 0x65, 0x19, 0x9e,
175+
0x17, 0x91, 0xdd, 0xad, 0x7b, 0x60, 0x1d, 0x26, 0xce, 0x39, 0x26, 0x39, 0xad, 0x17, 0xc2,
176+
0xeb, 0x60, 0x7f, 0x9e, 0x82, 0x78, 0x2e, 0x5f, 0x72, 0x5d, 0x19, 0x69, 0xb6, 0xb4, 0xf0,
177+
0x8b, 0x91, 0x9f, 0xf4, 0xc7, 0xf4, 0x1c, 0x04, 0xa9, 0xb8, 0xee, 0x08,
178+
];
179+
let mut buf = [0; 64 * 3];
180+
b.read(&mut buf);
181+
assert_eq!(expected, buf);
182+
}

blake2/src/lib.rs

Lines changed: 137 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ extern crate std;
1515

1616
pub use digest::{self, Digest};
1717

18-
use core::{fmt, marker::PhantomData, ops::Div};
18+
use core::{fmt, marker::PhantomData};
1919
use digest::{
2020
FixedOutput, HashMarker, InvalidOutputSize, MacMarker, Output, Update, VarOutputCustomized,
2121
array::{Array, ArraySize},
2222
block_buffer::{Lazy, LazyBuffer},
23-
consts::{U4, U16, U32, U64, U128},
23+
consts::{U16, U32, U64, U128},
2424
core_api::{
2525
AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper,
2626
CtVariableCoreWrapper, OutputSizeUser, RtVariableCoreWrapper, TruncSide, UpdateCore,
27-
VariableOutputCore,
27+
VariableOutputCore, XofReaderCoreWrapper,
2828
},
2929
crypto_common::{InvalidLength, Key, KeyInit, KeySizeUser},
3030
typenum::{IsLessOrEqual, LeEq, NonZero, Unsigned},
@@ -36,6 +36,7 @@ use digest::{FixedOutputReset, Reset};
3636
use digest::zeroize::{Zeroize, ZeroizeOnDrop};
3737

3838
mod as_bytes;
39+
mod blake2x;
3940
mod consts;
4041

4142
mod simd;
@@ -78,6 +79,20 @@ pub type Blake2b512 = Blake2b<U64>;
7879

7980
blake2_mac_impl!(Blake2bMac, Blake2bVarCore, U64, "Blake2b MAC function");
8081

82+
/// BLAKE2Xb hasher.
83+
pub type Blake2Xb = CoreWrapper<blake2x::Blake2Xb>;
84+
/// BLAKE2b XOF reader.
85+
pub type Blake2XbReader = XofReaderCoreWrapper<blake2x::Blake2bXReader>;
86+
87+
/// Create a blake2xb generator with maximum output
88+
pub fn blake2xb(seed: &[u8]) -> blake2x::Blake2bXReader {
89+
use digest::ExtendableOutput;
90+
blake2x::Blake2Xb::new(Some(seed), None)
91+
.unwrap()
92+
.finalize_xof()
93+
}
94+
// blake2_xof_impl!(Blake2Xb, Blake2bVarCore, u64, u64x4, u32::MAX, "Blake2Xb");
95+
8196
/// BLAKE2b-512 MAC state.
8297
pub type Blake2bMac512 = Blake2bMac<U64>;
8398

@@ -112,3 +127,122 @@ blake2_mac_impl!(Blake2sMac, Blake2sVarCore, U32, "Blake2s MAC function");
112127

113128
/// BLAKE2s-256 MAC state.
114129
pub type Blake2sMac256 = Blake2sMac<U32>;
130+
131+
#[derive(Clone, Copy, Default)]
132+
struct Blake2Parameters<'a> {
133+
digest_length: u8,
134+
key_size: u8,
135+
fanout: u8,
136+
depth: u8,
137+
leaf_length: u32,
138+
node_offset: u64,
139+
xof_digest_length: Option<u32>,
140+
node_depth: u8,
141+
inner_length: u8,
142+
salt: &'a [u8],
143+
persona: &'a [u8],
144+
}
145+
146+
macro_rules! pair_from_bytes {
147+
($word:ident, $data:expr, $dword_len:literal) => {
148+
if $data.len() < $dword_len {
149+
let mut padded_data = [0; $dword_len];
150+
for i in 0..$data.len() {
151+
padded_data[i] = $data[i];
152+
}
153+
(
154+
$word::from_le_bytes(padded_data[0..$dword_len / 2].try_into().unwrap()),
155+
$word::from_le_bytes(
156+
padded_data[$dword_len / 2..padded_data.len()]
157+
.try_into()
158+
.unwrap(),
159+
),
160+
)
161+
} else {
162+
(
163+
$word::from_le_bytes($data[0..$data.len() / 2].try_into().unwrap()),
164+
$word::from_le_bytes($data[$data.len() / 2..$data.len()].try_into().unwrap()),
165+
)
166+
}
167+
};
168+
}
169+
170+
// Private helper trait
171+
trait ToParamBlock<W> {
172+
fn to_param_block(self) -> [W; 8];
173+
}
174+
175+
impl ToParamBlock<u64> for Blake2Parameters<'_> {
176+
fn to_param_block(self) -> [u64; 8] {
177+
assert!(self.key_size <= 64);
178+
assert!(self.digest_length <= 64);
179+
180+
// The number of bytes needed to express two words.
181+
let length = 16;
182+
assert!(self.salt.len() <= length);
183+
assert!(self.persona.len() <= length);
184+
185+
// Build a parameter block
186+
let mut p = [0; 8];
187+
p[0] = (self.digest_length as u64)
188+
^ ((self.key_size as u64) << 8)
189+
^ ((self.fanout as u64) << 16)
190+
^ ((self.depth as u64) << 24)
191+
^ ((self.leaf_length.to_le() as u64) << 32);
192+
193+
p[1] = match self.xof_digest_length {
194+
None => self.node_offset,
195+
Some(xof_len) => {
196+
assert!(self.node_offset <= u32::MAX as u64);
197+
self.node_offset.to_le() ^ ((xof_len.to_le() as u64) << 32)
198+
}
199+
};
200+
p[2] = (self.node_depth as u64) ^ ((self.inner_length as u64) << 8);
201+
202+
// salt is two words long
203+
(p[4], p[5]) = pair_from_bytes!(u64, self.salt, 16);
204+
// persona is also two words long
205+
(p[6], p[7]) = pair_from_bytes!(u64, self.persona, 16);
206+
207+
p
208+
}
209+
}
210+
211+
impl ToParamBlock<u32> for Blake2Parameters<'_> {
212+
fn to_param_block(self) -> [u32; 8] {
213+
assert!(self.key_size <= 32);
214+
assert!(self.digest_length <= 32);
215+
216+
// The number of bytes needed to express two words.
217+
let length = 8;
218+
assert!(self.salt.len() <= length);
219+
assert!(self.persona.len() <= length);
220+
221+
// Build a parameter block
222+
let mut p = [0; 8];
223+
p[0] = (self.digest_length as u32)
224+
^ ((self.key_size as u32) << 8)
225+
^ ((self.fanout as u32) << 16)
226+
^ ((self.depth as u32) << 24);
227+
p[1] = self.leaf_length.to_le();
228+
229+
(p[2], p[3]) = match self.xof_digest_length {
230+
None => {
231+
assert!(self.node_offset < 1 << 48);
232+
pair_from_bytes!(u32, self.node_offset.to_le_bytes(), 8)
233+
}
234+
Some(xof_len) => {
235+
assert!(self.node_offset <= u32::MAX as u64);
236+
((self.node_offset as u32).to_le(), xof_len.to_le())
237+
}
238+
};
239+
p[3] ^= ((self.node_depth as u32) << 16) ^ ((self.inner_length as u32) << 24);
240+
241+
// salt is two words long
242+
(p[4], p[5]) = pair_from_bytes!(u32, self.salt, 8);
243+
// persona is also two words long
244+
(p[6], p[7]) = pair_from_bytes!(u32, self.persona, 8);
245+
246+
p
247+
}
248+
}

0 commit comments

Comments
 (0)