Skip to main content

hash2curve/
hash2field.rs

1//! Traits for hashing to field elements.
2//!
3//! <https://www.rfc-editor.org/rfc/rfc9380.html>
4
5mod expand_msg;
6
7use core::num::NonZeroU16;
8
9pub use expand_msg::{xmd::*, xof::*, *};
10
11use elliptic_curve::{
12    array::{Array, ArraySize, typenum::NonZero},
13    ops::Reduce,
14};
15
16/// Convert an arbitrary byte sequence into a field element.
17///
18/// <https://www.rfc-editor.org/rfc/rfc9380.html#name-hash_to_field-implementatio>
19///
20/// For the `expand_message` call, `len_in_bytes = L * N`.
21///
22/// # Errors
23///
24/// Returns an error if the [`ExpandMsg`] implementation fails.
25#[doc(hidden)]
26pub fn hash_to_field<const N: usize, E, K, T, L>(
27    data: &[&[u8]],
28    domain: &[&[u8]],
29) -> Result<[T; N], E::Error>
30where
31    E: ExpandMsg<K>,
32    T: Reduce<Array<u8, L>>,
33    L: ArraySize + NonZero,
34{
35    // Completely degenerate case; `N` and `L` would need to be extremely large.
36    let len_in_bytes = const {
37        assert!(
38            L::USIZE.saturating_mul(N) <= u16::MAX as usize,
39            "The product of `L` and `N` must not exceed `u16::MAX`."
40        );
41        NonZeroU16::new(L::U16 * N as u16).expect("N is greater than 0")
42    };
43    let mut tmp = Array::<u8, L>::default();
44    let mut expander = E::expand_message(data, domain, len_in_bytes)?;
45    Ok(core::array::from_fn(|_| {
46        expander
47            .fill_bytes(&mut tmp)
48            .expect("never exceeds `len_in_bytes`");
49        T::reduce(&tmp)
50    }))
51}