Skip to main content

hash2curve/hash2field/expand_msg/
xmd.rs

1//! `expand_message_xmd` based on a hash function.
2
3use core::{marker::PhantomData, num::NonZero, ops::Mul};
4
5use super::{Domain, ExpandMsg, Expander};
6use digest::{
7    FixedOutput, HashMarker,
8    array::{
9        Array,
10        typenum::{IsGreaterOrEqual, IsLessOrEqual, Prod, True, U2, Unsigned},
11    },
12    block_api::BlockSizeUser,
13};
14use elliptic_curve::Error;
15
16/// Implements `expand_message_xof` via the [`ExpandMsg`] trait:
17/// <https://www.rfc-editor.org/rfc/rfc9380.html#name-expand_message_xmd>
18///
19/// # Errors
20///
21/// See [`ExpandMsgXmdError`] for details.
22#[derive(Debug)]
23pub struct ExpandMsgXmd<HashT>(PhantomData<HashT>)
24where
25    HashT: BlockSizeUser + Default + FixedOutput + HashMarker,
26    HashT::OutputSize: IsLessOrEqual<HashT::BlockSize, Output = True>;
27
28impl<HashT, K> ExpandMsg<K> for ExpandMsgXmd<HashT>
29where
30    HashT: BlockSizeUser + Default + FixedOutput + HashMarker,
31    // The number of bits output by `HashT` MUST be at most `HashT::BlockSize`.
32    // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.1-4
33    HashT::OutputSize: IsLessOrEqual<HashT::BlockSize, Output = True>,
34    // The number of bits output by `HashT` MUST be at least `K * 2`.
35    // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.1-2.1
36    K: Mul<U2>,
37    HashT::OutputSize: IsGreaterOrEqual<Prod<K, U2>, Output = True>,
38{
39    type Hash = HashT;
40    type Expander<'dst> = ExpanderXmd<'dst, HashT>;
41    type Error = ExpandMsgXmdError;
42
43    fn expand_message<'dst>(
44        msg: &[&[u8]],
45        dst: &'dst [&[u8]],
46        len_in_bytes: NonZero<u16>,
47    ) -> Result<Self::Expander<'dst>, ExpandMsgXmdError> {
48        let b_in_bytes = HashT::OutputSize::USIZE;
49
50        // `255 * <b_in_bytes>` can not exceed `u16::MAX`
51        if usize::from(len_in_bytes.get()) > 255 * b_in_bytes {
52            return Err(ExpandMsgXmdError::Length);
53        }
54
55        debug_assert!(
56            usize::from(len_in_bytes.get()).div_ceil(b_in_bytes) <= u8::MAX.into(),
57            "should never pass the previous check"
58        );
59
60        let domain = Domain::xmd::<HashT>(dst)?;
61        let mut b_0 = HashT::default();
62        b_0.update(&Array::<u8, HashT::BlockSize>::default());
63
64        for msg in msg {
65            b_0.update(msg);
66        }
67
68        b_0.update(&len_in_bytes.get().to_be_bytes());
69        b_0.update(&[0]);
70        domain.update_hash(&mut b_0);
71        b_0.update(&[domain.len()]);
72        let b_0 = b_0.finalize_fixed();
73
74        let mut b_vals = HashT::default();
75        b_vals.update(&b_0[..]);
76        b_vals.update(&[1u8]);
77        domain.update_hash(&mut b_vals);
78        b_vals.update(&[domain.len()]);
79        let b_vals = b_vals.finalize_fixed();
80
81        Ok(ExpanderXmd {
82            b_0,
83            b_vals,
84            domain,
85            index: 1,
86            offset: 0,
87            remaining: len_in_bytes.get(),
88        })
89    }
90}
91
92/// [`Expander`] type for [`ExpandMsgXmd`].
93#[derive(Debug)]
94pub struct ExpanderXmd<'a, HashT>
95where
96    HashT: BlockSizeUser + Default + FixedOutput + HashMarker,
97    HashT::OutputSize: IsLessOrEqual<HashT::BlockSize, Output = True>,
98{
99    b_0: Array<u8, HashT::OutputSize>,
100    b_vals: Array<u8, HashT::OutputSize>,
101    domain: Domain<'a, HashT::OutputSize>,
102    index: u8,
103    offset: usize,
104    remaining: u16,
105}
106
107impl<HashT> Expander for ExpanderXmd<'_, HashT>
108where
109    HashT: BlockSizeUser + Default + FixedOutput + HashMarker,
110    HashT::OutputSize: IsLessOrEqual<HashT::BlockSize, Output = True>,
111{
112    fn fill_bytes(&mut self, mut okm: &mut [u8]) -> Result<usize, Error> {
113        if self.remaining == 0 {
114            return Err(Error);
115        }
116
117        let mut read_bytes = 0;
118
119        while self.remaining != 0 {
120            if self.offset == self.b_vals.len() {
121                self.index += 1;
122                self.offset = 0;
123                // b_0 XOR b_(idx - 1)
124                let mut tmp = Array::<u8, HashT::OutputSize>::default();
125                self.b_0
126                    .iter()
127                    .zip(&self.b_vals[..])
128                    .enumerate()
129                    .for_each(|(j, (b0val, bi1val))| tmp[j] = b0val ^ bi1val);
130                let mut b_vals = HashT::default();
131                b_vals.update(&tmp);
132                b_vals.update(&[self.index]);
133                self.domain.update_hash(&mut b_vals);
134                b_vals.update(&[self.domain.len()]);
135                self.b_vals = b_vals.finalize_fixed();
136            }
137
138            let bytes_to_read = self
139                .remaining
140                .min(okm.len().try_into().unwrap_or(u16::MAX))
141                .min(
142                    (self.b_vals.len() - self.offset)
143                        .try_into()
144                        .unwrap_or(u16::MAX),
145                );
146
147            if bytes_to_read == 0 {
148                return Ok(read_bytes);
149            }
150
151            okm[..bytes_to_read.into()].copy_from_slice(
152                &self.b_vals[self.offset..self.offset + usize::from(bytes_to_read)],
153            );
154            okm = &mut okm[bytes_to_read.into()..];
155
156            self.offset += usize::from(bytes_to_read);
157            self.remaining -= bytes_to_read;
158            read_bytes += usize::from(bytes_to_read);
159        }
160
161        Ok(read_bytes)
162    }
163}
164
165/// Error type for [`ExpandMsgXmd`].
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167pub enum ExpandMsgXmdError {
168    /// The domain separation tag must not be empty.
169    EmptyDst,
170    /// The hash's output size must not be greater then `255`
171    /// if the domain separation tag is longer than `255`.
172    DstHash,
173    /// The length in bytes is too large.
174    ///
175    /// `len_in_bytes` must be at most `255 * HashT::OutputSize`.
176    Length,
177}
178
179impl core::fmt::Display for ExpandMsgXmdError {
180    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
181        match self {
182            Self::EmptyDst => write!(f, "the domain separation tag is empty"),
183            Self::DstHash => write!(f, "hash output size is too large"),
184            Self::Length => write!(f, "`len_in_bytes` is too large"),
185        }
186    }
187}
188
189impl core::error::Error for ExpandMsgXmdError {}
190
191#[cfg(test)]
192mod test {
193    use super::*;
194    use elliptic_curve::array::{
195        ArraySize,
196        typenum::{IsLess, U4, U8, U32, U128, U65536},
197    };
198    use hex_literal::hex;
199    use sha2::Sha256;
200
201    #[test]
202    fn edge_cases() {
203        fn generate() -> ExpanderXmd<'static, Sha256> {
204            <ExpandMsgXmd<Sha256> as ExpandMsg<U4>>::expand_message(
205                &[b"test message"],
206                &[b"test DST"],
207                NonZero::new(64).unwrap(),
208            )
209            .unwrap()
210        }
211
212        assert_eq!(generate().fill_bytes(&mut [0; 0]), Ok(0));
213        assert_eq!(generate().fill_bytes(&mut [0; 1]), Ok(1));
214        assert_eq!(generate().fill_bytes(&mut [0; 64]), Ok(64));
215        assert_eq!(generate().fill_bytes(&mut [0; 65]), Ok(64));
216
217        let mut expander = generate();
218        assert_eq!(expander.fill_bytes(&mut [0; 0]), Ok(0));
219        assert_eq!(expander.fill_bytes(&mut [0; 32]), Ok(32));
220        assert_eq!(expander.fill_bytes(&mut [0; 0]), Ok(0));
221        assert_eq!(expander.fill_bytes(&mut [0; 31]), Ok(31));
222        assert_eq!(expander.fill_bytes(&mut [0; 2]), Ok(1));
223        assert_eq!(expander.fill_bytes(&mut [0; 1]), Err(Error));
224    }
225
226    fn assert_message<HashT>(
227        msg: &[u8],
228        domain: &Domain<'_, HashT::OutputSize>,
229        len_in_bytes: u16,
230        bytes: &[u8],
231    ) where
232        HashT: BlockSizeUser + Default + FixedOutput + HashMarker,
233    {
234        let block = HashT::BlockSize::USIZE;
235        assert_eq!(
236            Array::<u8, HashT::BlockSize>::default().as_slice(),
237            &bytes[..block]
238        );
239
240        let msg_len = block + msg.len();
241        assert_eq!(msg, &bytes[block..msg_len]);
242
243        let l = msg_len + size_of::<u16>();
244        assert_eq!(len_in_bytes.to_be_bytes(), &bytes[msg_len..l]);
245
246        let pad = l + size_of::<u8>();
247        assert_eq!([0], &bytes[l..pad]);
248
249        let dst = pad + usize::from(domain.len());
250        domain.assert(&bytes[pad..dst]);
251
252        let dst_len = dst + size_of::<u8>();
253        assert_eq!([domain.len()], &bytes[dst..dst_len]);
254
255        assert_eq!(dst_len, bytes.len());
256    }
257
258    struct TestVector {
259        msg: &'static [u8],
260        msg_prime: &'static [u8],
261        uniform_bytes: &'static [u8],
262    }
263
264    impl TestVector {
265        #[allow(clippy::panic_in_result_fn)]
266        fn assert<HashT, L>(&self, dst: &'static [u8], domain: &Domain<'_, HashT::OutputSize>)
267        where
268            HashT: BlockSizeUser + Default + FixedOutput + HashMarker,
269            HashT::OutputSize: IsLessOrEqual<HashT::BlockSize, Output = True>,
270            HashT::OutputSize: IsGreaterOrEqual<U8, Output = True>,
271            L: ArraySize + IsLess<U65536, Output = True>,
272        {
273            assert_message::<HashT>(self.msg, domain, L::U16, self.msg_prime);
274
275            let dst = [dst];
276            let mut expander = <ExpandMsgXmd<HashT> as ExpandMsg<U4>>::expand_message(
277                &[self.msg],
278                &dst,
279                NonZero::new(L::U16).unwrap(),
280            )
281            .unwrap();
282
283            let mut uniform_bytes = Array::<u8, L>::default();
284            expander.fill_bytes(&mut uniform_bytes).unwrap();
285
286            assert_eq!(uniform_bytes.as_slice(), self.uniform_bytes);
287        }
288    }
289
290    #[test]
291    fn expand_message_xmd_sha_256() {
292        const DST: &[u8] = b"QUUX-V01-CS02-with-expander-SHA256-128";
293        const DST_PRIME: &[u8] =
294            &hex!("515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826");
295
296        let dst_prime = Domain::xmd::<Sha256>(&[DST]).unwrap();
297        dst_prime.assert_dst(DST_PRIME);
298
299        const TEST_VECTORS_32: &[TestVector] = &[
300            TestVector {
301                msg: b"",
302                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826"),
303                uniform_bytes: &hex!("68a985b87eb6b46952128911f2a4412bbc302a9d759667f87f7a21d803f07235"),
304            },
305            TestVector {
306                msg: b"abc",
307                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616263002000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826"),
308                uniform_bytes: &hex!("d8ccab23b5985ccea865c6c97b6e5b8350e794e603b4b97902f53a8a0d605615"),
309            },
310            TestVector {
311                msg: b"abcdef0123456789",
312                msg_prime: &hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061626364656630313233343536373839002000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826"),
313                uniform_bytes: &hex!("eff31487c770a893cfb36f912fbfcbff40d5661771ca4b2cb4eafe524333f5c1"),
314            },
315            TestVector {
316                msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
317                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171002000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826"),
318                uniform_bytes: &hex!("b23a1d2b4d97b2ef7785562a7e8bac7eed54ed6e97e29aa51bfe3f12ddad1ff9"),
319            },
320            TestVector {
321                msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
322                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161002000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826"),
323                uniform_bytes: &hex!("4623227bcc01293b8c130bf771da8c298dede7383243dc0993d2d94823958c4c"),
324            },
325        ];
326
327        for test_vector in TEST_VECTORS_32 {
328            test_vector.assert::<Sha256, U32>(DST, &dst_prime);
329        }
330
331        const TEST_VECTORS_128: &[TestVector] = &[
332            TestVector {
333                msg: b"",
334                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826"),
335                uniform_bytes: &hex!("af84c27ccfd45d41914fdff5df25293e221afc53d8ad2ac06d5e3e29485dadbee0d121587713a3e0dd4d5e69e93eb7cd4f5df4cd103e188cf60cb02edc3edf18eda8576c412b18ffb658e3dd6ec849469b979d444cf7b26911a08e63cf31f9dcc541708d3491184472c2c29bb749d4286b004ceb5ee6b9a7fa5b646c993f0ced"),
336            },            TestVector {
337                msg: b"abc",
338                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616263008000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826"),
339                uniform_bytes: &hex!("abba86a6129e366fc877aab32fc4ffc70120d8996c88aee2fe4b32d6c7b6437a647e6c3163d40b76a73cf6a5674ef1d890f95b664ee0afa5359a5c4e07985635bbecbac65d747d3d2da7ec2b8221b17b0ca9dc8a1ac1c07ea6a1e60583e2cb00058e77b7b72a298425cd1b941ad4ec65e8afc50303a22c0f99b0509b4c895f40"),
340            },            TestVector {
341                msg: b"abcdef0123456789",
342                msg_prime: &hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061626364656630313233343536373839008000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826"),
343                uniform_bytes: &hex!("ef904a29bffc4cf9ee82832451c946ac3c8f8058ae97d8d629831a74c6572bd9ebd0df635cd1f208e2038e760c4994984ce73f0d55ea9f22af83ba4734569d4bc95e18350f740c07eef653cbb9f87910d833751825f0ebefa1abe5420bb52be14cf489b37fe1a72f7de2d10be453b2c9d9eb20c7e3f6edc5a60629178d9478df"),
344            },            TestVector {
345                msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
346                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171008000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826"),
347                uniform_bytes: &hex!("80be107d0884f0d881bb460322f0443d38bd222db8bd0b0a5312a6fedb49c1bbd88fd75d8b9a09486c60123dfa1d73c1cc3169761b17476d3c6b7cbbd727acd0e2c942f4dd96ae3da5de368d26b32286e32de7e5a8cb2949f866a0b80c58116b29fa7fabb3ea7d520ee603e0c25bcaf0b9a5e92ec6a1fe4e0391d1cdbce8c68a"),
348            },            TestVector {
349                msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
350                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161008000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826"),
351                uniform_bytes: &hex!("546aff5444b5b79aa6148bd81728704c32decb73a3ba76e9e75885cad9def1d06d6792f8a7d12794e90efed817d96920d728896a4510864370c207f99bd4a608ea121700ef01ed879745ee3e4ceef777eda6d9e5e38b90c86ea6fb0b36504ba4a45d22e86f6db5dd43d98a294bebb9125d5b794e9d2a81181066eb954966a487"),
352            },
353        ];
354
355        for test_vector in TEST_VECTORS_128 {
356            test_vector.assert::<Sha256, U128>(DST, &dst_prime);
357        }
358    }
359
360    #[test]
361    fn expand_message_xmd_sha_256_long() {
362        const DST: &[u8] = b"QUUX-V01-CS02-with-expander-SHA256-128-long-DST-1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
363        const DST_PRIME: &[u8] =
364            &hex!("412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620");
365
366        let dst_prime = Domain::xmd::<Sha256>(&[DST]).unwrap();
367        dst_prime.assert_dst(DST_PRIME);
368
369        const TEST_VECTORS_32: &[TestVector] = &[
370            TestVector {
371                msg: b"",
372                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620"),
373                uniform_bytes: &hex!("e8dc0c8b686b7ef2074086fbdd2f30e3f8bfbd3bdf177f73f04b97ce618a3ed3"),
374            },
375            TestVector {
376                msg: b"abc",
377                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616263002000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620"),
378                uniform_bytes: &hex!("52dbf4f36cf560fca57dedec2ad924ee9c266341d8f3d6afe5171733b16bbb12"),
379            },
380            TestVector {
381                msg: b"abcdef0123456789",
382                msg_prime: &hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061626364656630313233343536373839002000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620"),
383                uniform_bytes: &hex!("35387dcf22618f3728e6c686490f8b431f76550b0b2c61cbc1ce7001536f4521"),
384            },
385            TestVector {
386                msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
387                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171002000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620"),
388                uniform_bytes: &hex!("01b637612bb18e840028be900a833a74414140dde0c4754c198532c3a0ba42bc"),
389            },
390            TestVector {
391                msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
392                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161002000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620"),
393                uniform_bytes: &hex!("20cce7033cabc5460743180be6fa8aac5a103f56d481cf369a8accc0c374431b"),
394            },
395        ];
396
397        for test_vector in TEST_VECTORS_32 {
398            test_vector.assert::<Sha256, U32>(DST, &dst_prime);
399        }
400
401        const TEST_VECTORS_128: &[TestVector] = &[
402            TestVector {
403                msg: b"",
404                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620"),
405                uniform_bytes: &hex!("14604d85432c68b757e485c8894db3117992fc57e0e136f71ad987f789a0abc287c47876978e2388a02af86b1e8d1342e5ce4f7aaa07a87321e691f6fba7e0072eecc1218aebb89fb14a0662322d5edbd873f0eb35260145cd4e64f748c5dfe60567e126604bcab1a3ee2dc0778102ae8a5cfd1429ebc0fa6bf1a53c36f55dfc"),
406            },
407            TestVector {
408                msg: b"abc",
409                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616263008000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620"),
410                uniform_bytes: &hex!("1a30a5e36fbdb87077552b9d18b9f0aee16e80181d5b951d0471d55b66684914aef87dbb3626eaabf5ded8cd0686567e503853e5c84c259ba0efc37f71c839da2129fe81afdaec7fbdc0ccd4c794727a17c0d20ff0ea55e1389d6982d1241cb8d165762dbc39fb0cee4474d2cbbd468a835ae5b2f20e4f959f56ab24cd6fe267"),
411            },
412            TestVector {
413                msg: b"abcdef0123456789",
414                msg_prime: &hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061626364656630313233343536373839008000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620"),
415                uniform_bytes: &hex!("d2ecef3635d2397f34a9f86438d772db19ffe9924e28a1caf6f1c8f15603d4028f40891044e5c7e39ebb9b31339979ff33a4249206f67d4a1e7c765410bcd249ad78d407e303675918f20f26ce6d7027ed3774512ef5b00d816e51bfcc96c3539601fa48ef1c07e494bdc37054ba96ecb9dbd666417e3de289d4f424f502a982"),
416            },
417            TestVector {
418                msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
419                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171008000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620"),
420                uniform_bytes: &hex!("ed6e8c036df90111410431431a232d41a32c86e296c05d426e5f44e75b9a50d335b2412bc6c91e0a6dc131de09c43110d9180d0a70f0d6289cb4e43b05f7ee5e9b3f42a1fad0f31bac6a625b3b5c50e3a83316783b649e5ecc9d3b1d9471cb5024b7ccf40d41d1751a04ca0356548bc6e703fca02ab521b505e8e45600508d32"),
421            },
422            TestVector {
423                msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
424                msg_prime: &hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161008000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620"),
425                uniform_bytes: &hex!("78b53f2413f3c688f07732c10e5ced29a17c6a16f717179ffbe38d92d6c9ec296502eb9889af83a1928cd162e845b0d3c5424e83280fed3d10cffb2f8431f14e7a23f4c68819d40617589e4c41169d0b56e0e3535be1fd71fbb08bb70c5b5ffed953d6c14bf7618b35fc1f4c4b30538236b4b08c9fbf90462447a8ada60be495"),
426            },
427        ];
428
429        for test_vector in TEST_VECTORS_128 {
430            test_vector.assert::<Sha256, U128>(DST, &dst_prime);
431        }
432    }
433
434    #[test]
435    fn expand_message_xmd_sha_512() {
436        use sha2::Sha512;
437
438        const DST: &[u8] = b"QUUX-V01-CS02-with-expander-SHA512-256";
439        const DST_PRIME: &[u8] =
440            &hex!("515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626");
441
442        let dst_prime = Domain::xmd::<Sha512>(&[DST]).unwrap();
443        dst_prime.assert_dst(DST_PRIME);
444
445        const TEST_VECTORS_32: &[TestVector] = &[
446            TestVector {
447                msg: b"",
448                msg_prime: &hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626"),
449                uniform_bytes: &hex!("6b9a7312411d92f921c6f68ca0b6380730a1a4d982c507211a90964c394179ba"),
450            },
451            TestVector {
452                msg: b"abc",
453                msg_prime: &hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616263002000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626"),
454                uniform_bytes: &hex!("0da749f12fbe5483eb066a5f595055679b976e93abe9be6f0f6318bce7aca8dc"),
455            },
456            TestVector {
457                msg: b"abcdef0123456789",
458                msg_prime: &hex!("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061626364656630313233343536373839002000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626"),
459                uniform_bytes: &hex!("087e45a86e2939ee8b91100af1583c4938e0f5fc6c9db4b107b83346bc967f58"),
460            },
461            TestVector {
462                msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
463                msg_prime: &hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171002000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626"),
464                uniform_bytes: &hex!("7336234ee9983902440f6bc35b348352013becd88938d2afec44311caf8356b3"),
465            },
466            TestVector {
467                msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
468                msg_prime: &hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161002000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626"),
469                uniform_bytes: &hex!("57b5f7e766d5be68a6bfe1768e3c2b7f1228b3e4b3134956dd73a59b954c66f4"),
470            },
471        ];
472
473        for test_vector in TEST_VECTORS_32 {
474            test_vector.assert::<Sha512, U32>(DST, &dst_prime);
475        }
476
477        const TEST_VECTORS_128: &[TestVector] = &[
478            TestVector {
479                msg: b"",
480                msg_prime: &hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626"),
481                uniform_bytes: &hex!("41b037d1734a5f8df225dd8c7de38f851efdb45c372887be655212d07251b921b052b62eaed99b46f72f2ef4cc96bfaf254ebbbec091e1a3b9e4fb5e5b619d2e0c5414800a1d882b62bb5cd1778f098b8eb6cb399d5d9d18f5d5842cf5d13d7eb00a7cff859b605da678b318bd0e65ebff70bec88c753b159a805d2c89c55961"),
482            },
483            TestVector {
484                msg: b"abc",
485                msg_prime: &hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616263008000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626"),
486                uniform_bytes: &hex!("7f1dddd13c08b543f2e2037b14cefb255b44c83cc397c1786d975653e36a6b11bdd7732d8b38adb4a0edc26a0cef4bb45217135456e58fbca1703cd6032cb1347ee720b87972d63fbf232587043ed2901bce7f22610c0419751c065922b488431851041310ad659e4b23520e1772ab29dcdeb2002222a363f0c2b1c972b3efe1"),
487            },
488            TestVector {
489                msg: b"abcdef0123456789",
490                msg_prime: &hex!("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061626364656630313233343536373839008000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626"),
491                uniform_bytes: &hex!("3f721f208e6199fe903545abc26c837ce59ac6fa45733f1baaf0222f8b7acb0424814fcb5eecf6c1d38f06e9d0a6ccfbf85ae612ab8735dfdf9ce84c372a77c8f9e1c1e952c3a61b7567dd0693016af51d2745822663d0c2367e3f4f0bed827feecc2aaf98c949b5ed0d35c3f1023d64ad1407924288d366ea159f46287e61ac"),
492            },
493            TestVector {
494                msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
495                msg_prime: &hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171008000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626"),
496                uniform_bytes: &hex!("b799b045a58c8d2b4334cf54b78260b45eec544f9f2fb5bd12fb603eaee70db7317bf807c406e26373922b7b8920fa29142703dd52bdf280084fb7ef69da78afdf80b3586395b433dc66cde048a258e476a561e9deba7060af40adf30c64249ca7ddea79806ee5beb9a1422949471d267b21bc88e688e4014087a0b592b695ed"),
497            },
498            TestVector {
499                msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
500                msg_prime: &hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161008000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626"),
501                uniform_bytes: &hex!("05b0bfef265dcee87654372777b7c44177e2ae4c13a27f103340d9cd11c86cb2426ffcad5bd964080c2aee97f03be1ca18e30a1f14e27bc11ebbd650f305269cc9fb1db08bf90bfc79b42a952b46daf810359e7bc36452684784a64952c343c52e5124cd1f71d474d5197fefc571a92929c9084ffe1112cf5eea5192ebff330b"),
502            },
503        ];
504
505        for test_vector in TEST_VECTORS_128 {
506            test_vector.assert::<Sha512, U128>(DST, &dst_prime);
507        }
508    }
509}