hash2field/
lib.rs

1//!
2
3#![no_std]
4
5mod expand_msg;
6mod expand_msg_xmd;
7mod expand_msg_xof;
8
9use core::convert::TryFrom;
10pub use expand_msg::*;
11pub use expand_msg_xmd::*;
12pub use expand_msg_xof::*;
13
14/// The trait for helping to convert to a scalar
15pub trait FromOkm<const L: usize>: Sized {
16    /// Convert a byte sequence into a scalar
17    fn from_okm(data: &[u8; L]) -> Self;
18}
19
20/// Convert an arbitrary byte sequence according to
21/// <https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3>
22pub fn hash_to_field<E, T, const L: usize, const COUNT: usize, const OUT: usize>(
23    data: &[u8],
24    domain: &[u8],
25) -> [T; COUNT]
26where
27    E: ExpandMsg<OUT>,
28    T: FromOkm<L> + Default + Copy,
29{
30    let random_bytes = E::expand_message(data.as_ref(), domain.as_ref());
31    let mut out = [T::default(); COUNT];
32    for i in 0..COUNT {
33        let u = <[u8; L]>::try_from(&random_bytes[(L * i)..L * (i + 1)]).unwrap();
34        out[i] = T::from_okm(&u);
35    }
36    out
37}
38
39// Tests has to be here in order to implement FromOkm for other crates
40#[test]
41fn secp256k1_test() {
42    use digest::generic_array::{typenum::U32, GenericArray};
43    use k256::FieldElement;
44    use num_bigint::BigUint;
45    use num_integer::Integer;
46    use sha2::Sha256;
47
48    const L: usize = 48;
49    const COUNT: usize = 2;
50    const OUT: usize = L * COUNT;
51    const DST: &[u8] = b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_";
52
53    impl FromOkm<L> for FieldElement {
54        fn from_okm(data: &[u8; L]) -> Self {
55            let p = BigUint::from_bytes_be(
56                &hex::decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F")
57                    .unwrap(),
58            );
59            let mut x = BigUint::from_bytes_be(&data[..]);
60            x = x.mod_floor(&p);
61            let mut t = x.to_bytes_be();
62            while t.len() < 32 {
63                t.insert(0, 0u8);
64            }
65            let t = GenericArray::<u8, U32>::clone_from_slice(&t);
66            FieldElement::from_bytes(&t).unwrap()
67        }
68    }
69
70    let tests: [(&[u8], &str, &str); 5] = [
71        (b"", "6b0f9910dd2ba71c78f2ee9f04d73b5f4c5f7fc773a701abea1e573cab002fb3", "1ae6c212e08fe1a5937f6202f929a2cc8ef4ee5b9782db68b0d5799fd8f09e16"),
72        (b"abc", "128aab5d3679a1f7601e3bdf94ced1f43e491f544767e18a4873f397b08a2b61", "5897b65da3b595a813d0fdcc75c895dc531be76a03518b044daaa0f2e4689e00"),
73        (b"abcdef0123456789", "ea67a7c02f2cd5d8b87715c169d055a22520f74daeb080e6180958380e2f98b9", "7434d0d1a500d38380d1f9615c021857ac8d546925f5f2355319d823a478da18"),
74        (b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", "eda89a5024fac0a8207a87e8cc4e85aa3bce10745d501a30deb87341b05bcdf5", "dfe78cd116818fc2c16f3837fedbe2639fab012c407eac9dfe9245bf650ac51d"),
75        (b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "8d862e7e7e23d7843fe16d811d46d7e6480127a6b78838c277bca17df6900e9f", "68071d2530f040f081ba818d3c7188a94c900586761e9115efa47ae9bd847938"),
76    ];
77
78    for (msg, e0, e1) in &tests {
79        let output = hash_to_field::<ExpandMsgXmd<Sha256>, FieldElement, L, COUNT, OUT>(*msg, DST);
80        let exp0 = GenericArray::clone_from_slice(&hex::decode(e0).unwrap());
81        let exp1 = GenericArray::clone_from_slice(&hex::decode(e1).unwrap());
82        assert_eq!(output[0], FieldElement::from_bytes(&exp0).unwrap());
83        assert_eq!(output[1], FieldElement::from_bytes(&exp1).unwrap());
84    }
85}