diff --git a/ec/src/hashing/curve_maps/mod.rs b/ec/src/hashing/curve_maps/mod.rs deleted file mode 100644 index 7b0c3911d..000000000 --- a/ec/src/hashing/curve_maps/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod swu; -pub mod wb; diff --git a/ec/src/hashing/map_to_curve_hasher.rs b/ec/src/hashing/map_to_curve_hasher.rs deleted file mode 100644 index 4cdfb017f..000000000 --- a/ec/src/hashing/map_to_curve_hasher.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::{hashing::*, AffineRepr, CurveGroup}; -use ark_ff::field_hashers::HashToField; -use ark_std::marker::PhantomData; - -/// Trait for mapping a random field element to a random curve point. -pub trait MapToCurve: Sized { - /// Constructs a new mapping. - fn new() -> Result; - - /// Map an arbitary field element to a corresponding curve point. - fn map_to_curve(&self, point: T::BaseField) -> Result; -} - -/// Helper struct that can be used to construct elements on the elliptic curve -/// from arbitrary messages, by first hashing the message onto a field element -/// and then mapping it to the elliptic curve defined over that field. -pub struct MapToCurveBasedHasher -where - T: CurveGroup, - H2F: HashToField, - M2C: MapToCurve, -{ - field_hasher: H2F, - curve_mapper: M2C, - _params_t: PhantomData, -} - -impl HashToCurve for MapToCurveBasedHasher -where - T: CurveGroup, - H2F: HashToField, - M2C: MapToCurve, -{ - fn new(domain: &[u8]) -> Result { - let field_hasher = H2F::new(domain); - let curve_mapper = M2C::new()?; - let _params_t = PhantomData; - Ok(MapToCurveBasedHasher { - field_hasher, - curve_mapper, - _params_t, - }) - } - - /// Produce a hash of the message, using the hash to field and map to curve - /// traits. This uses the IETF hash to curve's specification for Random - /// oracle encoding (hash_to_curve) defined by combining these components. - /// See - fn hash(&self, msg: &[u8]) -> Result { - // IETF spec of hash_to_curve, from hash_to_field and map_to_curve - // sub-components - // 1. u = hash_to_field(msg, 2) - // 2. Q0 = map_to_curve(u[0]) - // 3. Q1 = map_to_curve(u[1]) - // 4. R = Q0 + Q1 # Point addition - // 5. P = clear_cofactor(R) - // 6. return P - - let rand_field_elems = self.field_hasher.hash_to_field(msg, 2); - - let rand_curve_elem_0 = self.curve_mapper.map_to_curve(rand_field_elems[0])?; - let rand_curve_elem_1 = self.curve_mapper.map_to_curve(rand_field_elems[1])?; - - let rand_curve_elem = (rand_curve_elem_0 + rand_curve_elem_1).into(); - let rand_subgroup_elem = rand_curve_elem.clear_cofactor(); - - Ok(rand_subgroup_elem) - } -} diff --git a/ec/src/hashing/mod.rs b/ec/src/hashing/mod.rs index e4034b44b..2dfd78dff 100644 --- a/ec/src/hashing/mod.rs +++ b/ec/src/hashing/mod.rs @@ -1,19 +1,96 @@ -use crate::CurveGroup; +//! # Hash-to-curve +//! +//! Implements traits and map-to-curve operations for the +//! [IRTF CFRG hash-to-curve draft](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16) + +pub mod swu; +pub mod wb; + + use ark_std::string::String; use core::fmt; -pub mod curve_maps; -pub mod map_to_curve_hasher; +pub use ark_ff::field_hashers::{ + self,AsDST,Expander,Zpad, + digest::{self,FixedOutputReset,Update,XofReader} +}; + +use crate::{CurveGroup,AffineRepr}; + + +/// Trait for mapping a random field element to a random curve point. +pub trait MapToCurve: Sized { + /// Security parameters used by symetric components. + /// Almost always 128 bits, unsued if merely supporting WB. + const SEC_PARAM: u16 = 128; -/// Trait for hashing arbitrary data to a group element on an elliptic curve -pub trait HashToCurve: Sized { - /// Create a new hash to curve instance, with a given domain. - fn new(domain: &[u8]) -> Result; + /// Checks whether supplied parameters represent a valid map. + fn check_parameters() -> Result<(), HashToCurveError>; + + /// Map an arbitary field element to a corresponding curve point. + fn map_to_curve(point: C::BaseField) -> Result; +} + +/// Applies a map-to-curve to a hash-to-base-field fed by an +/// extendable output hash function (XoF), as in the +/// [IRTF CFRG hash-to-curve draft](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16) +pub fn xof_map_to_curve(xof: &mut H) -> Result +where C: CurveGroup, M: MapToCurve, H: XofReader, +{ + let mut f = || field_hashers::hash_to_field::(>::SEC_PARAM, xof); + let p0 = >::map_to_curve(f())?; + let p1 = >::map_to_curve(f())?; + + // We've no projective clear_cofactor metho so normalize twice. + Ok( (p0 + p1).into_affine().clear_cofactor().into_group() ) +} - /// Produce a hash of the message, which also depends on the domain. - /// The output of the hash is a curve point in the prime order subgroup - /// of the given elliptic curve. - fn hash(&self, message: &[u8]) -> Result; +/// Applies the domain seperation tag (DST) to the hasher, and then +/// completes teh hash-to-curve, as in the +/// [IRTF CFRG hash-to-curve draft](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16) +pub fn expand_to_curve(exp: impl Expander, dst: impl AsDST) -> Result +where C: CurveGroup, M: MapToCurve +{ + #[cfg(debug_assertions)] + >::check_parameters()?; + let mut xof = exp.expand_for_field::(>::SEC_PARAM, dst); + xof_map_to_curve::(&mut xof) +} + +/// Hash-to-curves need an extendable output hash function (XoF). +/// Initalizes sha2 flavors for the sha2 XoF hack from the +/// [IRTF CFRG hash-to-curve draft](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16) +/// +/// All newer curves should prefer true XoFs like shake128 or similar +/// instead, which you initilize like `sha3::Shake128::default()`. +/// All higher security level curves must use shake256 or similar, not sha2. +pub fn zpad_expander() -> Zpad +where C: CurveGroup, M: MapToCurve, H: FixedOutputReset+Default, +{ + Zpad::::new_for_field::(>::SEC_PARAM) +} + +/// [IRTF CFRG hash-to-curve draft](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16) +/// based upon a map-to-curve and an extendable output hash function (XoF). +/// +/// We expect isogenious curves to have incompatible hash-to-curves, +/// ala Bandersnarh in short Weierstrass or twisted Edwards forms. +pub trait HashToCurve: CurveGroup { + type Map: MapToCurve; + type Expand: Expander+Update; + + /// Initalize the standard hasher for this hash-to-curve. + fn expander() -> Self::Expand; + + fn finalize_to_curve(exp: impl Expander, dst: impl AsDST) -> Result { + expand_to_curve::(exp,dst) + } + + fn hash_to_curve(dst: impl AsDST, msg: &[u8]) -> Result { + let mut exp = Self::expander(); + exp.update(msg); + Self::finalize_to_curve(exp, dst) + } } /// This is an error that could occur during the hash to curve process @@ -37,5 +114,6 @@ impl fmt::Display for HashToCurveError { } } + #[cfg(test)] mod tests; diff --git a/ec/src/hashing/curve_maps/swu/mod.rs b/ec/src/hashing/swu.rs similarity index 85% rename from ec/src/hashing/curve_maps/swu/mod.rs rename to ec/src/hashing/swu.rs index 47330c52c..6beed75de 100644 --- a/ec/src/hashing/curve_maps/swu/mod.rs +++ b/ec/src/hashing/swu.rs @@ -1,10 +1,14 @@ use crate::models::short_weierstrass::SWCurveConfig; -use ark_ff::{BigInteger, Field, One, PrimeField, Zero}; +use ark_ff::{ + BigInteger, Field, One, PrimeField, Zero, +}; use ark_std::string::ToString; use core::marker::PhantomData; +pub use super::MapToCurve; + use crate::{ - hashing::{map_to_curve_hasher::MapToCurve, HashToCurveError}, + hashing::{HashToCurveError}, models::short_weierstrass::{Affine, Projective}, }; @@ -19,6 +23,10 @@ pub trait SWUConfig: SWCurveConfig { /// we use a `ZETA` with low absolute value coefficients when they are /// represented as integers. const ZETA: Self::BaseField; + + /// Security parameters used by symetric components. + /// Almost always 128 bits, unsued if merely supporting WB. + const SEC_PARAM: u16 = 128; } /// Represents the SWU hash-to-curve map defined by `P`. @@ -35,8 +43,12 @@ pub fn parity(element: &F) -> bool { } impl MapToCurve> for SWUMap

{ - /// Constructs a new map if `P` represents a valid map. - fn new() -> Result { + /// Security parameters used by symetric components. + /// Almost always 128 bits. + const SEC_PARAM: u16 =

::SEC_PARAM; + + /// Checks if `P` represents a valid map. + fn check_parameters() -> Result<(), HashToCurveError> { // Verifying that ZETA is a non-square if P::ZETA.legendre().is_qr() { return Err(HashToCurveError::MapToCurveError( @@ -49,13 +61,13 @@ impl MapToCurve> for SWUMap

{ return Err(HashToCurveError::MapToCurveError("Simplified SWU requires a * b != 0 in the short Weierstrass form of y^2 = x^3 + a*x + b ".to_string())); } - Ok(SWUMap(PhantomData)) + Ok(()) } /// Map an arbitrary base field element to a curve point. /// Based on /// . - fn map_to_curve(&self, point: P::BaseField) -> Result, HashToCurveError> { + fn map_to_curve(point: P::BaseField) -> Result, HashToCurveError> { // 1. tv1 = inv0(Z^2 * u^4 + Z * u^2) // 2. x1 = (-B / A) * (1 + tv1) // 3. If tv1 == 0, set x1 = B / (Z * A) @@ -152,10 +164,10 @@ impl MapToCurve> for SWUMap

{ #[cfg(test)] mod test { use crate::{ - hashing::{map_to_curve_hasher::MapToCurveBasedHasher, HashToCurve}, - CurveConfig, + hashing::{zpad_expander, Update, expand_to_curve, HashToCurve}, + CurveConfig, CurveGroup }; - use ark_ff::field_hashers::DefaultFieldHasher; + // use ark_ff::field_hashers::{ ?? }; use ark_std::vec::Vec; use super::*; @@ -236,17 +248,12 @@ mod test { /// simple hash #[test] fn hash_arbitary_string_to_curve_swu() { - let test_swu_to_curve_hasher = MapToCurveBasedHasher::< - Projective, - DefaultFieldHasher, - SWUMap, - >::new(&[1]) - .unwrap(); - - let hash_result = test_swu_to_curve_hasher.hash(b"if you stick a Babel fish in your ear you can instantly understand anything said to you in any form of language.").expect("fail to hash the string to curve"); + let mut h = crate::hashing::zpad_expander::,SWUMap,Sha256>(); + h.update(b"if you stick a Babel fish in your ear you can instantly understand anything said to you in any form of language."); + let hash_result: Projective = expand_to_curve::,SWUMap>(h, b"domain").expect("fail to hash the string to curve"); assert!( - hash_result.is_on_curve(), + hash_result.into_affine().is_on_curve(), "hash results into a point off the curve" ); } @@ -256,15 +263,12 @@ mod test { /// elements should be mapped to curve successfully. everything can be mapped #[test] fn map_field_to_curve_swu() { - let test_map_to_curve = SWUMap::::new().unwrap(); + SWUMap::::check_parameters().unwrap(); let mut map_range: Vec> = vec![]; for current_field_element in 0..127 { - map_range.push( - test_map_to_curve - .map_to_curve(F127::from(current_field_element as u64)) - .unwrap(), - ); + let point = F127::from(current_field_element as u64); + map_range.push(SWUMap::::map_to_curve(point).unwrap()); } let mut counts = HashMap::new(); diff --git a/ec/src/hashing/tests.rs b/ec/src/hashing/tests.rs index 1aaca09cb..d03d2beb5 100644 --- a/ec/src/hashing/tests.rs +++ b/ec/src/hashing/tests.rs @@ -1,4 +1,4 @@ -use crate::hashing::curve_maps::swu::parity; +use crate::hashing::swu::parity; use ark_test_curves::bls12_381::{Fq, Fq2, Fq6}; #[test] diff --git a/ec/src/hashing/curve_maps/wb/mod.rs b/ec/src/hashing/wb.rs similarity index 87% rename from ec/src/hashing/curve_maps/wb/mod.rs rename to ec/src/hashing/wb.rs index 10ddbb0f7..7dd820519 100644 --- a/ec/src/hashing/curve_maps/wb/mod.rs +++ b/ec/src/hashing/wb.rs @@ -1,16 +1,18 @@ use core::marker::PhantomData; use crate::{models::short_weierstrass::SWCurveConfig, CurveConfig}; -use ark_ff::batch_inversion; +use ark_ff::{ + batch_inversion, +}; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial}; use crate::{ - hashing::{map_to_curve_hasher::MapToCurve, HashToCurveError}, + hashing::{HashToCurveError}, models::short_weierstrass::{Affine, Projective}, AffineRepr, }; -use super::swu::{SWUConfig, SWUMap}; +use super::{MapToCurve, swu::{SWUConfig, SWUMap}}; type BaseField = ::BaseField; /// [`IsogenyMap`] defines an isogeny between curves of @@ -76,16 +78,24 @@ pub trait WBConfig: SWCurveConfig + Sized { type IsogenousCurve: SWUConfig>; const ISOGENY_MAP: IsogenyMap<'static, Self::IsogenousCurve, Self>; + + /// Security parameters used by symetric components. + /// Almost always 128 bits. + const SEC_PARAM: u16 = 128; } pub struct WBMap { - swu_field_curve_hasher: SWUMap, + swu_field_curve_hasher: PhantomData>, curve_params: PhantomData P>, } impl MapToCurve> for WBMap

{ - /// Constructs a new map if `P` represents a valid map. - fn new() -> Result { + /// Security parameters used by symetric components. + /// Almost always 128 bits. + const SEC_PARAM: u16 =

::SEC_PARAM; + + /// Checks if `P` represents a valid map. + fn check_parameters() -> Result<(), HashToCurveError> { match P::ISOGENY_MAP.apply(P::IsogenousCurve::GENERATOR) { Ok(point_on_curve) => { if !point_on_curve.is_on_curve() { @@ -95,21 +105,18 @@ impl MapToCurve> for WBMap

{ Err(e) => return Err(e), } - Ok(WBMap { - swu_field_curve_hasher: SWUMap::::new().unwrap(), - curve_params: PhantomData, - }) + SWUMap::::check_parameters().unwrap(); // Or ? + Ok(()) } /// Map random field point to a random curve point /// inspired from /// fn map_to_curve( - &self, element: as AffineRepr>::BaseField, ) -> Result, HashToCurveError> { // first we need to map the field point to the isogenous curve - let point_on_isogenious_curve = self.swu_field_curve_hasher.map_to_curve(element).unwrap(); + let point_on_isogenious_curve = SWUMap::::map_to_curve(element).unwrap(); P::ISOGENY_MAP.apply(point_on_isogenious_curve) } } @@ -118,18 +125,15 @@ impl MapToCurve> for WBMap

{ mod test { use crate::{ hashing::{ - curve_maps::{ - swu::SWUConfig, - wb::{IsogenyMap, WBConfig, WBMap}, - }, - map_to_curve_hasher::MapToCurveBasedHasher, - HashToCurve, + zpad_expander, Update, expand_to_curve, + swu::SWUConfig, + wb::{IsogenyMap, WBConfig, WBMap}, }, models::short_weierstrass::SWCurveConfig, short_weierstrass::{Affine, Projective}, - CurveConfig, + CurveConfig, CurveGroup, }; - use ark_ff::{field_hashers::DefaultFieldHasher, fields::Fp64, MontBackend, MontFp}; + use ark_ff::{fields::Fp64, MontBackend, MontFp}; #[derive(ark_ff::MontConfig)] #[modulus = "127"] @@ -310,19 +314,18 @@ mod test { ISOGENY_MAP_TESTWBF127; } + /// The point of the test is to get a simple WB compatible curve /// and make simple hash #[test] fn hash_arbitrary_string_to_curve_wb() { use sha2::Sha256; - let test_wb_to_curve_hasher = MapToCurveBasedHasher::< - Projective, - DefaultFieldHasher, - WBMap, - >::new(&[1]) - .unwrap(); - - let hash_result = test_wb_to_curve_hasher.hash(b"if you stick a Babel fish in your ear you can instantly understand anything said to you in any form of language.").expect("fail to hash the string to curve"); + + let mut h = zpad_expander::,WBMap,Sha256>(); + h.update(b"if you stick a Babel fish in your ear you can instantly understand anything said to you in any form of language."); + + let hash_result: Projective = expand_to_curve::,WBMap>(h, b"domain").expect("fail to hash the string to curve"); + let hash_result = hash_result.into_affine(); assert!( hash_result.x != F127_ZERO && hash_result.y != F127_ZERO, diff --git a/ff/Cargo.toml b/ff/Cargo.toml index 69eeeafdb..de8fe2986 100644 --- a/ff/Cargo.toml +++ b/ff/Cargo.toml @@ -18,6 +18,7 @@ ark-ff-asm = { version = "0.4.2", path = "../ff-asm" } ark-ff-macros = { version = "0.4.2", path = "../ff-macros" } ark-std = { version = "0.4.0", default-features = false } ark-serialize = { version = "0.4.2", path = "../serialize", default-features = false } +arrayvec = { version = "0.7", default-features = false } derivative = { version = "2", features = ["use_core"] } num-traits = { version = "0.2", default-features = false } paste = "1.0" diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 8b1ef0a12..8e03b6298 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -2,114 +2,192 @@ // With some optimisations use ark_std::vec::Vec; -use digest::{DynDigest, ExtendableOutput, Update}; -pub trait Expander { - fn construct_dst_prime(&self) -> Vec; - fn expand(&self, msg: &[u8], length: usize) -> Vec; -} +use crate::{Field}; + +use digest::{FixedOutputReset, ExtendableOutput, XofReader, Update}; +use arrayvec::ArrayVec; + + const MAX_DST_LENGTH: usize = 255; -const LONG_DST_PREFIX: [u8; 17] = [ - //'H', '2', 'C', '-', 'O', 'V', 'E', 'R', 'S', 'I', 'Z', 'E', '-', 'D', 'S', 'T', '-', - 0x48, 0x32, 0x43, 0x2d, 0x4f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x5a, 0x45, 0x2d, 0x44, 0x53, 0x54, - 0x2d, -]; +const LONG_DST_PREFIX: &[u8; 17] = b"H2C-OVERSIZE-DST-"; + +/// A domain seperation tag for the hashed-to-curve. +pub trait AsDST { + fn as_dst(&self) -> &[u8]; + fn update_digest(&self, h: &mut H) { + h.update(self.as_dst()); + // I2OSP(len,1) https://www.rfc-editor.org/rfc/rfc8017.txt + h.update(&[self.as_dst().len() as u8]); + } +} + +impl AsDST for &[u8] { + fn as_dst(&self) -> &[u8] { + assert!(self.len() < MAX_DST_LENGTH); + self + } +} +impl AsDST for &[u8; N] { + fn as_dst(&self) -> &[u8] { + assert!(self.len() < MAX_DST_LENGTH); + self.as_ref() + } +} + +/// Implements section [5.3.3](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3) +/// "Using DSTs longer than 255 bytes" of the +/// [IRTF CFRG hash-to-curve draft #16](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3). +pub struct DST(arrayvec::ArrayVec); -pub(super) struct ExpanderXof { - pub(super) xofer: T, - pub(super) dst: Vec, - pub(super) k: usize, +impl AsDST for &DST { + fn as_dst(&self) -> &[u8] { + self.0.as_ref() + } } -impl Expander for ExpanderXof { - fn construct_dst_prime(&self) -> Vec { - let mut dst_prime = if self.dst.len() > MAX_DST_LENGTH { - let mut xofer = self.xofer.clone(); - xofer.update(&LONG_DST_PREFIX.clone()); - xofer.update(&self.dst); - xofer.finalize_boxed((2 * self.k + 7) >> 3).to_vec() - } else { - self.dst.clone() - }; - dst_prime.push(dst_prime.len() as u8); - dst_prime +impl DST { + pub fn new_xmd(dst: &[u8]) -> DST { + DST(ArrayVec::try_from(dst).unwrap_or_else( |_| { + let mut long = H::default(); + long.update(&LONG_DST_PREFIX[..]); + long.update(&dst); + ArrayVec::try_from( long.finalize_fixed().as_ref() ).unwrap() + } )) + } + + // pub fn sec_param(dst: &[u8]) -> usize { + // use core::any::TypeId; + // match TypeId::of:: { + // TypeId::of:: => 128, + // TypeId::of:: => 256, + // } + // } + + pub fn new_xof(dst: &[u8], sec_param: Option) -> DST { + // use digest::core_api::BlockSizeUser; + // H: +BlockSizeUser+ + // Ask if ::block_size() == sec_param? + DST(ArrayVec::try_from(dst).unwrap_or_else( |_| { + let sec_param = sec_param.expect("expand_message_xof wants a security parameter for compressing a long domain string.") as usize; + let mut long = H::default(); + long.update(&LONG_DST_PREFIX[..]); + long.update(&dst); + + let mut new_dst = [0u8; MAX_DST_LENGTH]; + let new_dst = &mut new_dst[0..((2 * sec_param + 7) >> 3)]; + long.finalize_xof_into(new_dst); + ArrayVec::try_from( &*new_dst ).unwrap() + } )) } - fn expand(&self, msg: &[u8], n: usize) -> Vec { - let dst_prime = self.construct_dst_prime(); - let lib_str = &[((n >> 8) & 0xFF) as u8, (n & 0xFF) as u8]; - - let mut xofer = self.xofer.clone(); - xofer.update(msg); - xofer.update(lib_str); - xofer.update(&dst_prime); - xofer.finalize_boxed(n).to_vec() +} + +pub trait Expander: Sized { + type R: XofReader; + fn expand(self, dst: impl AsDST, length: usize) -> Self::R; + fn expand_for_field(self, sec_param: u16, dst: impl AsDST) -> Self::R { + let len_per_base_elem = super::get_len_per_elem::(sec_param); + let m = F::extension_degree() as usize; + let total_length = N * m * len_per_base_elem; + self.expand(dst, total_length) } } -pub(super) struct ExpanderXmd { - pub(super) hasher: T, - pub(super) dst: Vec, - pub(super) block_size: usize, +impl Expander for H { + type R = ::Reader; + fn expand(mut self, dst: impl AsDST, n: usize) -> Self::R + { + assert!(n < (1 << 16), "Length should be smaller than 2^16"); + // I2OSP(len,2) https://www.rfc-editor.org/rfc/rfc8017.txt + self.update(& (n as u16).to_be_bytes()); + + dst.update_digest(&mut self); + self.finalize_xof() + } } -impl Expander for ExpanderXmd { - fn construct_dst_prime(&self) -> Vec { - let mut dst_prime = if self.dst.len() > MAX_DST_LENGTH { - let mut hasher = self.hasher.clone(); - hasher.update(&LONG_DST_PREFIX); - hasher.update(&self.dst); - hasher.finalize_reset().to_vec() - } else { - self.dst.clone() - }; - dst_prime.push(dst_prime.len() as u8); - dst_prime +static Z_PAD: [u8; 256] = [0u8; 256]; + +pub struct Zpad(pub H); + +impl Update for Zpad { + fn update(&mut self, data: &[u8]) { + self.0.update(data); + } +} + +impl Zpad { + pub fn new(block_size: usize) -> Zpad { + let mut hasher = H::default(); + hasher.update(&Z_PAD[0..block_size]); + Zpad(hasher) + } + pub fn new_for_field(sec_param: u16) -> Zpad { + let len_per_base_elem = super::get_len_per_elem::(sec_param); + Self::new(len_per_base_elem) } - fn expand(&self, msg: &[u8], n: usize) -> Vec { - let mut hasher = self.hasher.clone(); +} + +impl Expander for Zpad { + type R = XofVec; + fn expand(self, dst: impl AsDST, n: usize) -> XofVec + { + use digest::typenum::Unsigned; // output size of the hash function, e.g. 32 bytes = 256 bits for sha2::Sha256 - let b_len = hasher.output_size(); + let b_len = H::OutputSize::to_usize(); let ell = (n + (b_len - 1)) / b_len; assert!( ell <= 255, "The ratio of desired output to the output size of hash function is too large!" ); - let dst_prime = self.construct_dst_prime(); - let z_pad: Vec = vec![0; self.block_size]; - // Represent `len_in_bytes` as a 2-byte array. - // As per I2OSP method outlined in https://tools.ietf.org/pdf/rfc8017.pdf, - // The program should abort if integer that we're trying to convert is too large. + let Zpad(mut hasher) = self; assert!(n < (1 << 16), "Length should be smaller than 2^16"); - let lib_str: [u8; 2] = (n as u16).to_be_bytes(); + // I2OSP(len,2) https://www.rfc-editor.org/rfc/rfc8017.txt + hasher.update(& (n as u16).to_be_bytes()); - hasher.update(&z_pad); - hasher.update(msg); - hasher.update(&lib_str); hasher.update(&[0u8]); - hasher.update(&dst_prime); - let b0 = hasher.finalize_reset(); + dst.update_digest(& mut hasher); + let b0 = hasher.finalize_fixed_reset(); hasher.update(&b0); hasher.update(&[1u8]); - hasher.update(&dst_prime); - let mut bi = hasher.finalize_reset(); + dst.update_digest(& mut hasher); + let mut bi = hasher.finalize_fixed_reset(); - let mut uniform_bytes: Vec = Vec::with_capacity(n); - uniform_bytes.extend_from_slice(&bi); + let mut bytes: Vec = Vec::with_capacity(n); + bytes.extend_from_slice(&bi); for i in 2..=ell { // update the hasher with xor of b_0 and b_i elements for (l, r) in b0.iter().zip(bi.iter()) { hasher.update(&[*l ^ *r]); } hasher.update(&[i as u8]); - hasher.update(&dst_prime); - bi = hasher.finalize_reset(); - uniform_bytes.extend_from_slice(&bi); + dst.update_digest(& mut hasher); + bi = hasher.finalize_fixed_reset(); + bytes.extend_from_slice(&bi); } - uniform_bytes[0..n].to_vec() + bytes.truncate(n); + XofVec { bytes, pos: 0 } } } +pub struct XofVec { + bytes: Vec, + pos: usize, +} + +impl XofReader for XofVec { + fn read(&mut self, buffer: &mut [u8]) { + let end = self.pos + buffer.len(); + if end > self.bytes.len() { + panic!("Read more than claimed form expand_message_xmd") + } + buffer.copy_from_slice(&self.bytes[self.pos..end]); + self.pos = end; + } +} + + #[cfg(all(test, feature = "std"))] mod tests; diff --git a/ff/src/fields/field_hashers/expander/tests.rs b/ff/src/fields/field_hashers/expander/tests.rs index 36b4190f9..7aba547d6 100644 --- a/ff/src/fields/field_hashers/expander/tests.rs +++ b/ff/src/fields/field_hashers/expander/tests.rs @@ -1,26 +1,26 @@ use libtest_mimic::{run, Arguments, Failed, Trial}; use sha2::{Sha256, Sha384, Sha512}; -use sha3::{Shake128, Shake256}; +use sha3::{Shake128, Shake256, digest::{Update,XofReader}}; use std::{ fs::{read_dir, File}, io::BufReader, }; -use super::{Expander, ExpanderXmd, ExpanderXof}; +use super::{DST,Zpad,Expander}; #[derive(Debug, serde_derive::Serialize, serde_derive::Deserialize)] pub struct ExpanderVector { #[serde(rename = "DST")] pub dst: String, - pub k: usize, + pub k: usize, // sec_param pub hash: String, pub name: String, #[serde(rename = "tests")] pub vectors: Vec, } -#[derive(Debug, serde_derive::Serialize, serde_derive:: Deserialize)] +#[derive(Debug, serde_derive::Serialize, serde_derive::Deserialize)] pub struct TestExpander { #[serde(rename = "DST_prime")] pub dst_prime: String, @@ -49,40 +49,28 @@ fn expander() { run(&args, tests).exit_if_failed(); } -#[derive(Copy, Clone)] -pub enum ExpID { - XMD(HashID), - XOF(XofID), -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum HashID { - SHA256, - SHA384, - SHA512, -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum XofID { - SHAKE128, - SHAKE256, -} - fn do_test(data: ExpanderVector) -> Result<(), Failed> { - let exp_id = match data.hash.as_str() { - "SHA256" => ExpID::XMD(HashID::SHA256), - "SHA384" => ExpID::XMD(HashID::SHA384), - "SHA512" => ExpID::XMD(HashID::SHA512), - "SHAKE128" => ExpID::XOF(XofID::SHAKE128), - "SHAKE256" => ExpID::XOF(XofID::SHAKE256), + let dst = match data.hash.as_str() { + "SHA256" => DST::new_xmd::(data.dst.as_bytes()), + "SHA384" => DST::new_xmd::(data.dst.as_bytes()), + "SHA512" => DST::new_xmd::(data.dst.as_bytes()), + "SHAKE128" => DST::new_xof::(data.dst.as_bytes(), Some(data.k as u16)), + "SHAKE256" => DST::new_xof::(data.dst.as_bytes(), Some(data.k as u16)), _ => unimplemented!(), }; - let exp = get_expander(exp_id, data.dst.as_bytes(), data.k); + for v in data.vectors.iter() { let len = usize::from_str_radix(v.len_in_bytes.trim_start_matches("0x"), 16).unwrap(); - let got = exp.expand(v.msg.as_bytes(), len); + let got = match data.hash.as_str() { + "SHA256" => Zpad::::new(64).chain(v.msg.as_bytes()).expand(&dst,len).read_boxed(len), + "SHA384" => Zpad::::new(128).chain(v.msg.as_bytes()).expand(&dst,len).read_boxed(len), + "SHA512" => Zpad::::new(128).chain(v.msg.as_bytes()).expand(&dst,len).read_boxed(len), + "SHAKE128" => Shake128::default().chain(v.msg.as_bytes()).expand(&dst, len).read_boxed(len), + "SHAKE256" => Shake256::default().chain(v.msg.as_bytes()).expand(&dst, len).read_boxed(len), + _ => unimplemented!(), + }; let want = hex::decode(&v.uniform_bytes).unwrap(); - if got != want { + if &*got != want.as_slice() { return Err(format!( "Expander: {}\nVector: {}\ngot: {:?}\nwant: {:?}", data.hash, v.msg, got, want, @@ -92,39 +80,3 @@ fn do_test(data: ExpanderVector) -> Result<(), Failed> { } Ok(()) } - -fn get_expander(id: ExpID, _dst: &[u8], k: usize) -> Box { - let dst = _dst.to_vec(); - - match id { - ExpID::XMD(h) => match h { - HashID::SHA256 => Box::new(ExpanderXmd { - hasher: Sha256::default(), - block_size: 64, - dst, - }), - HashID::SHA384 => Box::new(ExpanderXmd { - hasher: Sha384::default(), - block_size: 128, - dst, - }), - HashID::SHA512 => Box::new(ExpanderXmd { - hasher: Sha512::default(), - block_size: 128, - dst, - }), - }, - ExpID::XOF(x) => match x { - XofID::SHAKE128 => Box::new(ExpanderXof { - xofer: Shake128::default(), - k, - dst, - }), - XofID::SHAKE256 => Box::new(ExpanderXof { - xofer: Shake256::default(), - k, - dst, - }), - }, - } -} diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index 82454f231..1de66a46e 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -3,11 +3,16 @@ mod expander; use crate::{Field, PrimeField}; use ark_std::vec::Vec; -use digest::DynDigest; -use expander::Expander; +pub use digest::{self,Update}; +use digest::{ExtendableOutput,FixedOutputReset,XofReader}; +pub use expander::{AsDST, DST, Zpad, Expander}; -use self::expander::ExpanderXmd; +// pub trait HashToField: Field { +// fn hash_to_field() -> Self; +// } + +/* /// Trait for hashing messages to field elements. pub trait HashToField: Sized { /// Initialises a new hash-to-field helper struct. @@ -18,8 +23,9 @@ pub trait HashToField: Sized { fn new(domain: &[u8]) -> Self; /// Hash an arbitrary `msg` to #`count` elements from field `F`. - fn hash_to_field(&self, msg: &[u8], count: usize) -> Vec; + fn hash_to_field(&self, msg: &[u8]) -> [F; N]; } +*/ /// This field hasher constructs a Hash-To-Field based on a fixed-output hash function, /// like SHA2, SHA3 or Blake2. @@ -28,76 +34,65 @@ pub trait HashToField: Sized { /// # Examples /// /// ``` -/// use ark_ff::fields::field_hashers::{DefaultFieldHasher, HashToField}; +/// use ark_ff::fields::field_hashers::{xmd_hash_to_field}; /// use ark_test_curves::bls12_381::Fq; /// use sha2::Sha256; /// -/// let hasher = as HashToField>::new(&[1, 2, 3]); -/// let field_elements: Vec = hasher.hash_to_field(b"Hello, World!", 2); +/// let field_elements: [Fq; 2] = xmd_hash_to_field::(128,b"Application",b"Hello, World!"); /// /// assert_eq!(field_elements.len(), 2); /// ``` -pub struct DefaultFieldHasher { - expander: ExpanderXmd, - len_per_base_elem: usize, +pub fn xmd_hash_to_field(sec_param: u16, dst: &[u8], msg: &[u8]) -> [F; N] +where F: Field, H: FixedOutputReset+Default, +{ + let dst = DST::new_xmd::(dst); + let mut xmd = Zpad::::new_for_field::(sec_param).chain(msg).expand_for_field::(sec_param,&dst); + let h2f = |_| hash_to_field::(sec_param, &mut xmd); + ark_std::array::from_fn::(h2f) } -impl HashToField - for DefaultFieldHasher +pub fn xof_hash_to_field(sec_param: u16, dst: &[u8], msg: &[u8]) -> [F; N] +where F: Field, H: ExtendableOutput+Default, { - fn new(dst: &[u8]) -> Self { - // The final output of `hash_to_field` will be an array of field - // elements from F::BaseField, each of size `len_per_elem`. - let len_per_base_elem = get_len_per_elem::(); - - let expander = ExpanderXmd { - hasher: H::default(), - dst: dst.to_vec(), - block_size: len_per_base_elem, - }; - - DefaultFieldHasher { - expander, - len_per_base_elem, - } - } - - fn hash_to_field(&self, message: &[u8], count: usize) -> Vec { - let m = F::extension_degree() as usize; + let dst = DST::new_xof::(dst,Some(sec_param)); + let mut xof = H::default().chain(msg).expand_for_field::(sec_param,&dst); + let h2f = |_| hash_to_field::(sec_param,&mut xof); + ark_std::array::from_fn::(h2f) +} - // The user imposes a `count` of elements of F_p^m to output per input msg, - // each field element comprising `m` BasePrimeField elements. - let len_in_bytes = count * m * self.len_per_base_elem; - let uniform_bytes = self.expander.expand(message, len_in_bytes); +pub fn hash_to_field(sec_param: u16, h: &mut H) -> F { + // The final output of `hash_to_field` will be an array of field + // elements from F::BaseField, each of size `len_per_elem`. + let len_per_base_elem = get_len_per_elem::(sec_param); + // Rust *still* lacks alloca, hence this ugly hack. + let mut alloca = [0u8; 256]; + let mut vec = Vec::new(); + let bytes = if len_per_base_elem > 256 { + vec.resize(len_per_base_elem, 0u8); + vec.as_mut() + } else { + &mut alloca[0..len_per_base_elem] + }; - let mut output = Vec::with_capacity(count); - let mut base_prime_field_elems = Vec::with_capacity(m); - for i in 0..count { - base_prime_field_elems.clear(); - for j in 0..m { - let elm_offset = self.len_per_base_elem * (j + i * m); - let val = F::BasePrimeField::from_be_bytes_mod_order( - &uniform_bytes[elm_offset..][..self.len_per_base_elem], - ); - base_prime_field_elems.push(val); - } - let f = F::from_base_prime_field_elems(&base_prime_field_elems).unwrap(); - output.push(f); - } + let m = F::extension_degree() as usize; - output - } + let base_prime_field_elem = |_| { + h.read(&mut *bytes); + bytes.reverse(); // Need BE but Arkworks' LE is faster + F::BasePrimeField::from_le_bytes_mod_order(&mut *bytes) + }; + F::from_base_prime_field_elems( (0..m).map(base_prime_field_elem) ).unwrap() } /// This function computes the length in bytes that a hash function should output /// for hashing an element of type `Field`. /// See section 5.1 and 5.3 of the /// [IETF hash standardization draft](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/14/) -fn get_len_per_elem() -> usize { +const fn get_len_per_elem(sec_param: u16) -> usize { // ceil(log(p)) let base_field_size_in_bits = F::BasePrimeField::MODULUS_BIT_SIZE as usize; // ceil(log(p)) + security_parameter - let base_field_size_with_security_padding_in_bits = base_field_size_in_bits + SEC_PARAM; + let base_field_size_with_security_padding_in_bits = base_field_size_in_bits + (sec_param as usize); // ceil( (ceil(log(p)) + security_parameter) / 8) let bytes_per_base_field_elem = ((base_field_size_with_security_padding_in_bits + 7) / 8) as u64; diff --git a/ff/src/fields/mod.rs b/ff/src/fields/mod.rs index 08e3ebc45..58f7af117 100644 --- a/ff/src/fields/mod.rs +++ b/ff/src/fields/mod.rs @@ -6,6 +6,7 @@ use ark_serialize::{ CanonicalSerializeWithFlags, EmptyFlags, Flags, }; use ark_std::{ + iter::IntoIterator, fmt::{Debug, Display}, hash::Hash, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, @@ -219,7 +220,7 @@ pub trait Field: /// Convert a slice of base prime field elements into a field element. /// If the slice length != Self::extension_degree(), must return None. - fn from_base_prime_field_elems(elems: &[Self::BasePrimeField]) -> Option; + fn from_base_prime_field_elems(elems: impl IntoIterator) -> Option; /// Constructs a field element from a single base prime field elements. /// ``` diff --git a/ff/src/fields/models/cubic_extension.rs b/ff/src/fields/models/cubic_extension.rs index 8369706be..2fa9a958a 100644 --- a/ff/src/fields/models/cubic_extension.rs +++ b/ff/src/fields/models/cubic_extension.rs @@ -6,7 +6,7 @@ use ark_std::{ cmp::{Ord, Ordering, PartialOrd}, fmt, io::{Read, Write}, - iter::Chain, + iter::{Chain,IntoIterator}, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, vec::Vec, }; @@ -216,17 +216,20 @@ impl Field for CubicExtField

{ ) } - fn from_base_prime_field_elems(elems: &[Self::BasePrimeField]) -> Option { - if elems.len() != (Self::extension_degree() as usize) { - return None; - } + fn from_base_prime_field_elems(elems: impl IntoIterator) -> Option { + let mut elems = elems.into_iter(); + let elems = elems.by_ref(); let base_ext_deg = P::BaseField::extension_degree() as usize; - Some(Self::new( - P::BaseField::from_base_prime_field_elems(&elems[0..base_ext_deg]).unwrap(), - P::BaseField::from_base_prime_field_elems(&elems[base_ext_deg..2 * base_ext_deg]) - .unwrap(), - P::BaseField::from_base_prime_field_elems(&elems[2 * base_ext_deg..]).unwrap(), - )) + let element = Some(Self::new( + P::BaseField::from_base_prime_field_elems(elems.take(base_ext_deg)) ?, + P::BaseField::from_base_prime_field_elems(elems.take(base_ext_deg)) ?, + P::BaseField::from_base_prime_field_elems(elems.take(base_ext_deg)) ?, + )); + if elems.next().is_some() { + None + } else { + element + } } #[inline] @@ -734,7 +737,7 @@ mod cube_ext_tests { for _ in 0..d { random_coeffs.push(Fq::rand(&mut test_rng())); } - let res = Fq6::from_base_prime_field_elems(&random_coeffs); + let res = Fq6::from_base_prime_field_elems(random_coeffs); assert_eq!(res, None); } // Test on slice lengths that are equal to the extension degree @@ -745,12 +748,13 @@ mod cube_ext_tests { for _ in 0..ext_degree { random_coeffs.push(Fq::rand(&mut test_rng())); } - let actual = Fq6::from_base_prime_field_elems(&random_coeffs).unwrap(); let expected_0 = Fq2::new(random_coeffs[0], random_coeffs[1]); let expected_1 = Fq2::new(random_coeffs[2], random_coeffs[3]); let expected_2 = Fq2::new(random_coeffs[3], random_coeffs[4]); let expected = Fq6::new(expected_0, expected_1, expected_2); + + let actual = Fq6::from_base_prime_field_elems(random_coeffs).unwrap(); assert_eq!(actual, expected); } } @@ -766,7 +770,7 @@ mod cube_ext_tests { random_coeffs[0] = random_coeff; assert_eq!( res, - Fq6::from_base_prime_field_elems(&random_coeffs).unwrap() + Fq6::from_base_prime_field_elems(random_coeffs).unwrap() ); } } diff --git a/ff/src/fields/models/fp/mod.rs b/ff/src/fields/models/fp/mod.rs index 1113c1222..3768871a7 100644 --- a/ff/src/fields/models/fp/mod.rs +++ b/ff/src/fields/models/fp/mod.rs @@ -232,11 +232,13 @@ impl, const N: usize> Field for Fp { iter::once(*self) } - fn from_base_prime_field_elems(elems: &[Self::BasePrimeField]) -> Option { - if elems.len() != (Self::extension_degree() as usize) { + fn from_base_prime_field_elems(elems: impl IntoIterator) -> Option { + let mut elems = elems.into_iter(); + let elem = elems.next() ?; + if elems.next().is_some() { return None; } - Some(elems[0]) + Some(elem) } #[inline] diff --git a/ff/src/fields/models/quadratic_extension.rs b/ff/src/fields/models/quadratic_extension.rs index 5d7f6de96..0b15c42df 100644 --- a/ff/src/fields/models/quadratic_extension.rs +++ b/ff/src/fields/models/quadratic_extension.rs @@ -6,7 +6,7 @@ use ark_std::{ cmp::{Ord, Ordering, PartialOrd}, fmt, io::{Read, Write}, - iter::Chain, + iter::{Chain,IntoIterator}, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, vec::Vec, }; @@ -242,15 +242,19 @@ impl Field for QuadExtField

{ .chain(self.c1.to_base_prime_field_elements()) } - fn from_base_prime_field_elems(elems: &[Self::BasePrimeField]) -> Option { - if elems.len() != (Self::extension_degree() as usize) { - return None; - } + fn from_base_prime_field_elems(elems: impl IntoIterator) -> Option { + let mut elems = elems.into_iter(); + let elems = elems.by_ref(); let base_ext_deg = P::BaseField::extension_degree() as usize; - Some(Self::new( - P::BaseField::from_base_prime_field_elems(&elems[0..base_ext_deg]).unwrap(), - P::BaseField::from_base_prime_field_elems(&elems[base_ext_deg..]).unwrap(), - )) + let element = Some(Self::new( + P::BaseField::from_base_prime_field_elems(elems.take(base_ext_deg)) ?, + P::BaseField::from_base_prime_field_elems(elems.take(base_ext_deg)) ?, + )); + if elems.next().is_some() { + None + } else { + element + } } fn square(&self) -> Self { @@ -794,7 +798,7 @@ mod quad_ext_tests { for _ in 0..d { random_coeffs.push(Fq::rand(&mut test_rng())); } - let res = Fq2::from_base_prime_field_elems(&random_coeffs); + let res = Fq2::from_base_prime_field_elems(random_coeffs); assert_eq!(res, None); } // Test on slice lengths that are equal to the extension degree @@ -805,8 +809,8 @@ mod quad_ext_tests { for _ in 0..ext_degree { random_coeffs.push(Fq::rand(&mut test_rng())); } - let actual = Fq2::from_base_prime_field_elems(&random_coeffs).unwrap(); let expected = Fq2::new(random_coeffs[0], random_coeffs[1]); + let actual = Fq2::from_base_prime_field_elems(random_coeffs).unwrap(); assert_eq!(actual, expected); } } @@ -822,7 +826,7 @@ mod quad_ext_tests { random_coeffs[0] = random_coeff; assert_eq!( res, - Fq2::from_base_prime_field_elems(&random_coeffs).unwrap() + Fq2::from_base_prime_field_elems(random_coeffs).unwrap() ); } } diff --git a/test-curves/src/bls12_381/g1.rs b/test-curves/src/bls12_381/g1.rs index ff05bb6aa..549c3b216 100644 --- a/test-curves/src/bls12_381/g1.rs +++ b/test-curves/src/bls12_381/g1.rs @@ -1,6 +1,6 @@ use crate::bls12_381::*; use ark_ec::{ - hashing::curve_maps::wb::{IsogenyMap, WBConfig}, + hashing::wb::{IsogenyMap, WBConfig}, models::CurveConfig, scalar_mul::glv::GLVConfig, short_weierstrass::{self, *}, diff --git a/test-curves/src/bls12_381/g1_swu_iso.rs b/test-curves/src/bls12_381/g1_swu_iso.rs index 8532422eb..a2acc5ea2 100644 --- a/test-curves/src/bls12_381/g1_swu_iso.rs +++ b/test-curves/src/bls12_381/g1_swu_iso.rs @@ -1,6 +1,6 @@ use crate::bls12_381::*; use ark_ec::{ - hashing::curve_maps::{swu::SWUConfig, wb::IsogenyMap}, + hashing::{swu::SWUConfig, wb::IsogenyMap}, models::{ short_weierstrass::{Affine, SWCurveConfig}, CurveConfig, diff --git a/test-curves/src/bls12_381/g2.rs b/test-curves/src/bls12_381/g2.rs index 4b0065eb1..e8f8c518f 100644 --- a/test-curves/src/bls12_381/g2.rs +++ b/test-curves/src/bls12_381/g2.rs @@ -3,7 +3,7 @@ use core::ops::Neg; use crate::bls12_381::*; use ark_ec::{ bls12::{self, Bls12Config}, - hashing::curve_maps::wb::{IsogenyMap, WBConfig}, + hashing::wb::{IsogenyMap, WBConfig}, models::CurveConfig, short_weierstrass::{self, *}, AffineRepr, CurveGroup, PrimeGroup, diff --git a/test-curves/src/bls12_381/g2_swu_iso.rs b/test-curves/src/bls12_381/g2_swu_iso.rs index 988e30ef1..7337522bd 100644 --- a/test-curves/src/bls12_381/g2_swu_iso.rs +++ b/test-curves/src/bls12_381/g2_swu_iso.rs @@ -5,7 +5,7 @@ use ark_ec::models::{ }; use ark_ff::MontFp; -use ark_ec::hashing::curve_maps::{swu::SWUConfig, wb::IsogenyMap}; +use ark_ec::hashing::{swu::SWUConfig, wb::IsogenyMap}; type G2Affine = Affine; diff --git a/test-templates/src/h2c/mod.rs b/test-templates/src/h2c/mod.rs index c65391d23..f5b64d2c8 100644 --- a/test-templates/src/h2c/mod.rs +++ b/test-templates/src/h2c/mod.rs @@ -15,12 +15,13 @@ macro_rules! test_h2c { extern crate std; use ark_ec::{ hashing::{ - curve_maps::wb::WBMap, map_to_curve_hasher::MapToCurveBasedHasher, HashToCurve, + wb::WBMap, HashToCurve, }, short_weierstrass::{Affine, Projective}, + CurveGroup, }; use ark_ff::{ - field_hashers::{DefaultFieldHasher, HashToField}, + field_hashers::{xmd_hash_to_field}, // zpad_expander, expand_for_field fields::Field, One, UniformRand, Zero, }; @@ -41,30 +42,23 @@ macro_rules! test_h2c { assert_eq!(data.hash, "sha256"); let dst = data.dst.as_bytes(); - let hasher; - let g1_mapper = MapToCurveBasedHasher::< - Projective<$group>, - DefaultFieldHasher, - WBMap<$group>, - >::new(dst) - .unwrap(); - hasher = as HashToField<$field>>::new(dst); for v in data.vectors.iter() { + // first, hash-to-field tests - let got: Vec<$base_prime_field> = - hasher.hash_to_field(&v.msg.as_bytes(), 2 * $m); + let got: [$base_prime_field; { 2* $m } ] = + xmd_hash_to_field::,{ 2* $m }>(128,dst,&v.msg.as_bytes()); let want: Vec<$base_prime_field> = v.u.iter().map(read_fq_vec).flatten().collect(); - assert_eq!(got, want); + assert_eq!(got[..], *want); // then, test curve points let x = read_fq_vec(&v.p.x); let y = read_fq_vec(&v.p.y); - let got = g1_mapper.hash(&v.msg.as_bytes()).unwrap(); + let got = as HashToCurve>::hash_to_curve(dst,v.msg.as_bytes()).unwrap().into_affine(); let want = Affine::<$group>::new_unchecked( - <$field>::from_base_prime_field_elems(&x[..]).unwrap(), - <$field>::from_base_prime_field_elems(&y[..]).unwrap(), + <$field>::from_base_prime_field_elems(x).unwrap(), + <$field>::from_base_prime_field_elems(y).unwrap(), ); assert!(got.is_on_curve()); assert!(want.is_on_curve());