ark_ff/fields/field_hashers/expander/
mod.rs1use core::marker::PhantomData;
5
6use ark_std::vec::*;
7
8use digest::{ExtendableOutput, FixedOutputReset, Update};
9
10pub trait Expander {
11 fn expand(&self, msg: &[u8], length: usize) -> Vec<u8>;
12}
13const MAX_DST_LENGTH: usize = 255;
14
15const LONG_DST_PREFIX: &[u8; 17] = b"H2C-OVERSIZE-DST-";
16
17#[allow(clippy::upper_case_acronyms)]
21pub struct DST {
22 len: usize,
23 data: [u8; MAX_DST_LENGTH],
24}
25
26impl DST {
27 pub fn new_xmd<H: FixedOutputReset + Default>(dst: &[u8]) -> DST {
28 let mut new_dst = [0u8; MAX_DST_LENGTH];
29 let len = if dst.len() > MAX_DST_LENGTH {
30 let mut long = H::default();
31 long.update(&LONG_DST_PREFIX[..]);
32 long.update(dst);
33 Self::copy_bytes(&mut new_dst, long.finalize_fixed().as_ref())
34 } else {
35 Self::copy_bytes(&mut new_dst, dst)
36 };
37 Self { data: new_dst, len }
38 }
39
40 #[allow(dead_code)]
41 pub fn new_xof<H: ExtendableOutput + Default>(dst: &[u8], k: usize) -> DST {
42 let mut new_dst = [0u8; MAX_DST_LENGTH];
43 let len = if dst.len() > MAX_DST_LENGTH {
44 let mut long = H::default();
45 long.update(&LONG_DST_PREFIX[..]);
46 long.update(dst);
47
48 let new_dst = &mut new_dst[0..((2 * k + 7) >> 3)];
49 long.finalize_xof_into(new_dst);
50 new_dst.len()
51 } else {
52 Self::copy_bytes(&mut new_dst, dst)
53 };
54 Self { data: new_dst, len }
55 }
56
57 pub fn update<H: Update>(&self, h: &mut H) {
58 h.update(self.slice());
59 h.update(&[self.len as u8]);
61 }
62
63 fn copy_bytes(new_dst: &mut [u8; MAX_DST_LENGTH], data: &[u8]) -> usize {
64 new_dst[..data.len()].copy_from_slice(data);
65 data.len()
66 }
67
68 fn slice(&self) -> &[u8] {
69 self.data.get(..self.len).unwrap_or_default()
70 }
71}
72
73#[allow(dead_code)]
74pub(super) struct ExpanderXof<H: ExtendableOutput + Clone + Default> {
75 pub(super) xofer: PhantomData<H>,
76 pub(super) dst: Vec<u8>,
77 pub(super) k: usize,
78}
79
80impl<H: ExtendableOutput + Clone + Default> Expander for ExpanderXof<H> {
81 fn expand(&self, msg: &[u8], n: usize) -> Vec<u8> {
82 let mut xofer = H::default();
83 xofer.update(msg);
84
85 let lib_str = (n as u16).to_be_bytes();
87 xofer.update(&lib_str);
88
89 DST::new_xof::<H>(self.dst.as_ref(), self.k).update(&mut xofer);
90 xofer.finalize_boxed(n).into_vec()
91 }
92}
93
94pub(super) struct ExpanderXmd<H: FixedOutputReset + Default + Clone> {
95 pub(super) hasher: PhantomData<H>,
96 pub(super) dst: Vec<u8>,
97 pub(super) block_size: usize,
98}
99
100static Z_PAD: [u8; 256] = [0u8; 256];
101
102impl<H: FixedOutputReset + Default + Clone> Expander for ExpanderXmd<H> {
103 fn expand(&self, msg: &[u8], n: usize) -> Vec<u8> {
104 use digest::typenum::Unsigned;
105 let b_len = H::OutputSize::to_usize();
107 let ell = n.div_ceil(b_len);
108 assert!(
109 ell <= 255,
110 "The ratio of desired output to the output size of hash function is too large!"
111 );
112
113 let dst_prime = DST::new_xmd::<H>(self.dst.as_ref());
114 assert!(n < (1 << 16), "Length should be smaller than 2^16");
118 let lib_str: [u8; 2] = (n as u16).to_be_bytes();
119
120 let mut hasher = H::default();
121 hasher.update(&Z_PAD[0..self.block_size]);
122 hasher.update(msg);
123 hasher.update(&lib_str);
124 hasher.update(&[0u8]);
125 dst_prime.update(&mut hasher);
126 let b0 = hasher.finalize_fixed_reset();
127
128 hasher.update(&b0);
129 hasher.update(&[1u8]);
130 dst_prime.update(&mut hasher);
131 let mut bi = hasher.finalize_fixed_reset();
132
133 let mut uniform_bytes: Vec<u8> = Vec::with_capacity(n);
134 uniform_bytes.extend_from_slice(&bi);
135 for i in 2..=ell {
136 for (l, r) in b0.iter().zip(bi.iter()) {
138 hasher.update(&[*l ^ *r]);
139 }
140 hasher.update(&[i as u8]);
141 dst_prime.update(&mut hasher);
142 bi = hasher.finalize_fixed_reset();
143 uniform_bytes.extend_from_slice(&bi);
144 }
145 uniform_bytes.truncate(n);
146 uniform_bytes
147 }
148}
149
150#[cfg(all(test, feature = "std"))]
151mod tests;