1use generic_array::{ArrayLength, GenericArray};
2#[cfg(feature = "rust-crypto")]
3use sha3::{digest::core_api::CoreProxy, CShake128, CShake256};
4use sha3_utils::{encode_string, right_encode, right_encode_bytes};
5
6pub trait Xof: Clone {
8 type Reader: XofReader;
10
11 fn new(s: &[u8]) -> Self;
13
14 fn update(&mut self, data: &[u8]);
16
17 fn finalize_xof(self) -> Self::Reader;
19
20 fn finalize_xof_into(self, out: &mut [u8]) {
22 self.finalize_xof().read(out);
23 }
24}
25
26pub trait XofReader {
28 fn read(&mut self, out: &mut [u8]);
30
31 fn read_n<N: ArrayLength>(&mut self) -> GenericArray<u8, N> {
33 let mut out = GenericArray::default();
34 self.read(&mut out);
35 out
36 }
37}
38
39#[cfg(feature = "rust-crypto")]
40#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
41impl<R> XofReader for R
42where
43 R: sha3::digest::XofReader,
44{
45 #[inline]
46 fn read(&mut self, out: &mut [u8]) {
47 sha3::digest::XofReader::read(self, out);
48 }
49}
50
51#[cfg(feature = "rust-crypto")]
55#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
56pub type TupleHash128 = TupleHash<CShake128>;
57
58#[cfg(feature = "rust-crypto")]
62#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
63pub type TupleHash256 = TupleHash<CShake256>;
64
65#[derive(Clone, Debug, Default)]
79pub struct TupleHash<X> {
80 xof: X,
81}
82
83impl<X: Xof> TupleHash<X> {
84 pub fn new(s: &[u8]) -> Self {
86 Self { xof: X::new(s) }
87 }
88
89 pub fn update(&mut self, s: &[u8]) {
91 for x in &encode_string(s) {
92 self.xof.update(x);
93 }
94 }
95
96 pub fn finalize_into(mut self, out: &mut [u8]) {
98 self.xof.update(right_encode_bytes(out.len()).as_bytes());
99 self.xof.finalize_xof_into(out)
100 }
101
102 pub fn finalize<N: ArrayLength>(self) -> GenericArray<u8, N> {
104 let mut out = GenericArray::default();
105 self.finalize_into(&mut out);
106 out
107 }
108}
109
110#[cfg(feature = "rust-crypto")]
111#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
112impl<X: Xof> sha3::digest::HashMarker for TupleHash<X> {}
113
114#[cfg(feature = "rust-crypto")]
115#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
116impl<X: Xof> sha3::digest::Update for TupleHash<X> {
117 #[inline]
118 fn update(&mut self, data: &[u8]) {
119 self.update(data);
120 }
121}
122
123#[cfg(feature = "rust-crypto")]
125#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
126pub type TupleHashXof128 = TupleHashXof<CShake128>;
127
128#[cfg(feature = "rust-crypto")]
130#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
131pub type TupleHashXof256 = TupleHashXof<CShake256>;
132
133#[derive(Clone, Debug, Default)]
145pub struct TupleHashXof<X> {
146 xof: X,
147}
148
149impl<X: Xof> TupleHashXof<X> {
150 pub fn new(s: &[u8]) -> Self {
152 Self { xof: X::new(s) }
153 }
154
155 pub fn update(&mut self, s: &[u8]) {
157 for x in &encode_string(s) {
158 self.xof.update(x);
159 }
160 }
161
162 pub fn finalize_xof(mut self) -> TupleHashXofReader<X::Reader> {
164 self.xof.update(right_encode(0).as_bytes());
165 TupleHashXofReader(self.xof.finalize_xof())
166 }
167}
168
169impl<X: Xof> Xof for TupleHashXof<X> {
170 type Reader = TupleHashXofReader<X::Reader>;
171
172 #[inline]
173 fn new(s: &[u8]) -> Self {
174 Self { xof: X::new(s) }
175 }
176
177 #[inline]
178 fn update(&mut self, data: &[u8]) {
179 self.update(data);
180 }
181
182 #[inline]
183 fn finalize_xof(self) -> Self::Reader {
184 self.finalize_xof()
185 }
186}
187
188#[cfg(feature = "rust-crypto")]
189#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
190impl<X: Xof> sha3::digest::ExtendableOutput for TupleHashXof<X> {
191 type Reader = TupleHashXofReader<X::Reader>;
192
193 fn finalize_xof(self) -> Self::Reader {
194 self.finalize_xof()
195 }
196}
197
198#[cfg(feature = "rust-crypto")]
199#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
200impl<X: Xof> sha3::digest::HashMarker for TupleHashXof<X> {}
201
202#[cfg(feature = "rust-crypto")]
203#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
204impl<X: Xof> sha3::digest::Update for TupleHashXof<X> {
205 #[inline]
206 fn update(&mut self, data: &[u8]) {
207 self.update(data);
208 }
209}
210
211#[derive(Clone, Debug)]
213pub struct TupleHashXofReader<R>(R);
214
215#[cfg(not(feature = "rust-crypto"))]
216impl<R: XofReader> XofReader for TupleHashXofReader<R> {
217 #[inline]
218 fn read(&mut self, out: &mut [u8]) {
219 self.0.read(out);
220 }
221}
222
223#[cfg(feature = "rust-crypto")]
224impl<R: XofReader> sha3::digest::XofReader for TupleHashXofReader<R> {
225 #[inline]
226 fn read(&mut self, out: &mut [u8]) {
227 self.0.read(out);
228 }
229}
230
231pub fn tuple_hash<X, I, N>(s: &[u8], x: I) -> GenericArray<u8, N>
239where
240 X: Xof,
241 I: IntoIterator,
242 I::Item: AsRef<[u8]>,
243 N: ArrayLength,
244{
245 let mut h = TupleHash::<X>::new(s);
246 for xi in x {
247 h.update(xi.as_ref());
248 }
249 h.finalize()
250}
251
252#[cfg(feature = "rust-crypto")]
256#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
257pub fn tuple_hash128<I, N>(s: &[u8], x: I) -> GenericArray<u8, N>
258where
259 I: IntoIterator,
260 I::Item: AsRef<[u8]>,
261 N: ArrayLength,
262{
263 tuple_hash::<CShake128, I, N>(s, x)
264}
265
266#[cfg(feature = "rust-crypto")]
270#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
271pub fn tuple_hash256<I, N>(s: &[u8], x: I) -> GenericArray<u8, N>
272where
273 I: IntoIterator,
274 I::Item: AsRef<[u8]>,
275 N: ArrayLength,
276{
277 tuple_hash::<CShake256, I, N>(s, x)
278}
279
280pub fn tuple_hash_xof<X, I>(s: &[u8], x: I) -> impl XofReader
288where
289 X: Xof,
290 I: IntoIterator,
291 I::Item: AsRef<[u8]>,
292{
293 let mut h = TupleHashXof::<X>::new(s);
294 for xi in x {
295 h.update(xi.as_ref());
296 }
297 h.finalize_xof()
298}
299
300#[cfg(feature = "rust-crypto")]
302#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
303pub fn tuple_hash_xof128<I>(s: &[u8], x: I) -> impl XofReader
304where
305 I: IntoIterator,
306 I::Item: AsRef<[u8]>,
307{
308 tuple_hash_xof::<CShake128, I>(s, x)
309}
310
311#[cfg(feature = "rust-crypto")]
313#[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
314pub fn tuple_hash_xof256<I>(s: &[u8], x: I) -> impl XofReader
315where
316 I: IntoIterator,
317 I::Item: AsRef<[u8]>,
318{
319 tuple_hash_xof::<CShake256, I>(s, x)
320}
321
322macro_rules! impl_cshake {
323 ($ty:ty) => {
324 #[cfg(feature = "rust-crypto")]
325 #[cfg_attr(docsrs, doc(cfg(feature = "rust-crypto")))]
326 impl Xof for $ty {
327 type Reader = <$ty as sha3::digest::ExtendableOutput>::Reader;
328
329 fn new(s: &[u8]) -> Self {
330 let core = <$ty as CoreProxy>::Core::new_with_function_name(b"TupleHash", s);
331 <$ty>::from_core(core)
332 }
333
334 fn update(&mut self, data: &[u8]) {
335 sha3::digest::Update::update(self, data);
336 }
337
338 fn finalize_xof(self) -> Self::Reader {
339 sha3::digest::ExtendableOutput::finalize_xof(self)
340 }
341 }
342 };
343}
344impl_cshake!(CShake128);
345impl_cshake!(CShake256);
346
347#[cfg(test)]
348#[allow(clippy::type_complexity, reason = "Tests")]
349mod tests {
350 use generic_array::typenum::{U32, U64};
351
352 use super::*;
353
354 #[test]
355 fn test_tuple_hash128_basic() {
356 let lhs = tuple_hash128::<_, U32>(b"test", ["abc", "d"]);
357 let rhs = tuple_hash128::<_, U32>(b"test", ["ab", "cd"]);
358 assert_ne!(lhs, rhs);
359 }
360
361 #[test]
362 fn test_tuple_hash256_basic() {
363 let lhs = tuple_hash256::<_, U32>(b"test", ["abc", "d"]);
364 let rhs = tuple_hash256::<_, U32>(b"test", ["ab", "cd"]);
365 assert_ne!(lhs, rhs);
366 }
367
368 #[test]
370 fn test_tuple_hash128_vectors() {
371 let vectors: &[(&[u8], &[&[u8]], [u8; 32])] = &[
372 (
373 &[],
374 &[&[0x0, 0x1, 0x2], &[0x10, 0x11, 0x12, 0x13, 0x14, 0x15]],
375 [
376 0xC5, 0xD8, 0x78, 0x6C, 0x1A, 0xFB, 0x9B, 0x82, 0x11, 0x1A, 0xB3, 0x4B, 0x65,
377 0xB2, 0xC0, 0x04, 0x8F, 0xA6, 0x4E, 0x6D, 0x48, 0xE2, 0x63, 0x26, 0x4C, 0xE1,
378 0x70, 0x7D, 0x3F, 0xFC, 0x8E, 0xD1,
379 ],
380 ),
381 (
382 b"My Tuple App",
383 &[&[0x0, 0x1, 0x2], &[0x10, 0x11, 0x12, 0x13, 0x14, 0x15]],
384 [
385 0x75, 0xCD, 0xB2, 0x0F, 0xF4, 0xDB, 0x11, 0x54, 0xE8, 0x41, 0xD7, 0x58, 0xE2,
386 0x41, 0x60, 0xC5, 0x4B, 0xAE, 0x86, 0xEB, 0x8C, 0x13, 0xE7, 0xF5, 0xF4, 0x0E,
387 0xB3, 0x55, 0x88, 0xE9, 0x6D, 0xFB,
388 ],
389 ),
390 (
391 b"My Tuple App",
392 &[
393 &[0x0, 0x1, 0x2],
394 &[0x10, 0x11, 0x12, 0x13, 0x14, 0x15],
395 &[0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28],
396 ],
397 [
398 0xE6, 0x0F, 0x20, 0x2C, 0x89, 0xA2, 0x63, 0x1E, 0xDA, 0x8D, 0x4C, 0x58, 0x8C,
399 0xA5, 0xFD, 0x07, 0xF3, 0x9E, 0x51, 0x51, 0x99, 0x8D, 0xEC, 0xCF, 0x97, 0x3A,
400 0xDB, 0x38, 0x04, 0xBB, 0x6E, 0x84,
401 ],
402 ),
403 ];
404 for (i, (s, x, want)) in vectors.iter().enumerate() {
405 let got = tuple_hash128::<&[&[u8]], U32>(s, x);
406 let want = GenericArray::from(*want);
407 assert_eq!(got, want, "#{i}");
408 }
409 }
410
411 #[test]
413 fn test_tuple_hash256_vectors() {
414 let vectors: &[(&[u8], &[&[u8]], [u8; 64])] = &[
415 (
416 &[],
417 &[&[0x0, 0x1, 0x2], &[0x10, 0x11, 0x12, 0x13, 0x14, 0x15]],
418 [
419 0xCF, 0xB7, 0x05, 0x8C, 0xAC, 0xA5, 0xE6, 0x68, 0xF8, 0x1A, 0x12, 0xA2, 0x0A,
420 0x21, 0x95, 0xCE, 0x97, 0xA9, 0x25, 0xF1, 0xDB, 0xA3, 0xE7, 0x44, 0x9A, 0x56,
421 0xF8, 0x22, 0x01, 0xEC, 0x60, 0x73, 0x11, 0xAC, 0x26, 0x96, 0xB1, 0xAB, 0x5E,
422 0xA2, 0x35, 0x2D, 0xF1, 0x42, 0x3B, 0xDE, 0x7B, 0xD4, 0xBB, 0x78, 0xC9, 0xAE,
423 0xD1, 0xA8, 0x53, 0xC7, 0x86, 0x72, 0xF9, 0xEB, 0x23, 0xBB, 0xE1, 0x94,
424 ],
425 ),
426 (
427 b"My Tuple App",
428 &[&[0x0, 0x1, 0x2], &[0x10, 0x11, 0x12, 0x13, 0x14, 0x15]],
429 [
430 0x14, 0x7C, 0x21, 0x91, 0xD5, 0xED, 0x7E, 0xFD, 0x98, 0xDB, 0xD9, 0x6D, 0x7A,
431 0xB5, 0xA1, 0x16, 0x92, 0x57, 0x6F, 0x5F, 0xE2, 0xA5, 0x06, 0x5F, 0x3E, 0x33,
432 0xDE, 0x6B, 0xBA, 0x9F, 0x3A, 0xA1, 0xC4, 0xE9, 0xA0, 0x68, 0xA2, 0x89, 0xC6,
433 0x1C, 0x95, 0xAA, 0xB3, 0x0A, 0xEE, 0x1E, 0x41, 0x0B, 0x0B, 0x60, 0x7D, 0xE3,
434 0x62, 0x0E, 0x24, 0xA4, 0xE3, 0xBF, 0x98, 0x52, 0xA1, 0xD4, 0x36, 0x7E,
435 ],
436 ),
437 (
438 b"My Tuple App",
439 &[
440 &[0x0, 0x1, 0x2],
441 &[0x10, 0x11, 0x12, 0x13, 0x14, 0x15],
442 &[0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28],
443 ],
444 [
445 0x45, 0x00, 0x0B, 0xE6, 0x3F, 0x9B, 0x6B, 0xFD, 0x89, 0xF5, 0x47, 0x17, 0x67,
446 0x0F, 0x69, 0xA9, 0xBC, 0x76, 0x35, 0x91, 0xA4, 0xF0, 0x5C, 0x50, 0xD6, 0x88,
447 0x91, 0xA7, 0x44, 0xBC, 0xC6, 0xE7, 0xD6, 0xD5, 0xB5, 0xE8, 0x2C, 0x01, 0x8D,
448 0xA9, 0x99, 0xED, 0x35, 0xB0, 0xBB, 0x49, 0xC9, 0x67, 0x8E, 0x52, 0x6A, 0xBD,
449 0x8E, 0x85, 0xC1, 0x3E, 0xD2, 0x54, 0x02, 0x1D, 0xB9, 0xE7, 0x90, 0xCE,
450 ],
451 ),
452 ];
453 for (i, (s, x, want)) in vectors.iter().enumerate() {
454 let got = tuple_hash256::<&[&[u8]], U64>(s, x);
455 let want = GenericArray::from(*want);
456 assert_eq!(got, want, "#{i}");
457 }
458 }
459}