From d652bf37b87c1cf0743a9a27e340159e28c19891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Mon, 20 Mar 2023 23:21:13 +0100 Subject: [PATCH 01/47] Avoid unused subdirectories --- ec/src/hashing/curve_maps/{swu/mod.rs => swu.rs} | 0 ec/src/hashing/curve_maps/{wb/mod.rs => wb.rs} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename ec/src/hashing/curve_maps/{swu/mod.rs => swu.rs} (100%) rename ec/src/hashing/curve_maps/{wb/mod.rs => wb.rs} (100%) diff --git a/ec/src/hashing/curve_maps/swu/mod.rs b/ec/src/hashing/curve_maps/swu.rs similarity index 100% rename from ec/src/hashing/curve_maps/swu/mod.rs rename to ec/src/hashing/curve_maps/swu.rs diff --git a/ec/src/hashing/curve_maps/wb/mod.rs b/ec/src/hashing/curve_maps/wb.rs similarity index 100% rename from ec/src/hashing/curve_maps/wb/mod.rs rename to ec/src/hashing/curve_maps/wb.rs From c17b654c698667a2c029dd598a3fa8d153631b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Mon, 20 Mar 2023 23:55:12 +0100 Subject: [PATCH 02/47] Pseduo-remove MapToCurve::new, renamed to test_parameters MapToCurve::new seemingly originates from a more runtime oriented elliptic curve crate: https://github.com/armfazh/redox-ecc/blob/master/src/ellipticcurve.rs#L36 Arguably test_parameters should be inherent methods, invoked by whoever defined the parameters, but maybe not? I left the trait method for now. --- ec/src/hashing/curve_maps/swu.rs | 14 ++++++-------- ec/src/hashing/curve_maps/wb.rs | 15 ++++++--------- ec/src/hashing/map_to_curve_hasher.rs | 23 +++++++++++------------ 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/ec/src/hashing/curve_maps/swu.rs b/ec/src/hashing/curve_maps/swu.rs index 47330c52c..e1f66bef6 100644 --- a/ec/src/hashing/curve_maps/swu.rs +++ b/ec/src/hashing/curve_maps/swu.rs @@ -35,8 +35,8 @@ pub fn parity(element: &F) -> bool { } impl MapToCurve> for SWUMap

{ - /// Constructs a new map if `P` represents a valid map. - fn new() -> Result { + /// Checks if `P` represents a valid map. + fn test_parameters() -> Result<(), HashToCurveError> { // Verifying that ZETA is a non-square if P::ZETA.legendre().is_qr() { return Err(HashToCurveError::MapToCurveError( @@ -49,13 +49,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) @@ -256,14 +256,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::::test_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(), + SWUMap::::map_to_curve(F127::from(current_field_element as u64)).unwrap(), ); } diff --git a/ec/src/hashing/curve_maps/wb.rs b/ec/src/hashing/curve_maps/wb.rs index 10ddbb0f7..c8d0730aa 100644 --- a/ec/src/hashing/curve_maps/wb.rs +++ b/ec/src/hashing/curve_maps/wb.rs @@ -79,13 +79,13 @@ pub trait WBConfig: SWCurveConfig + Sized { } 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 { + /// Checks if `P` represents a valid map. + fn test_parameters() -> Result<(), HashToCurveError> { match P::ISOGENY_MAP.apply(P::IsogenousCurve::GENERATOR) { Ok(point_on_curve) => { if !point_on_curve.is_on_curve() { @@ -95,21 +95,18 @@ impl MapToCurve> for WBMap

{ Err(e) => return Err(e), } - Ok(WBMap { - swu_field_curve_hasher: SWUMap::::new().unwrap(), - curve_params: PhantomData, - }) + SWUMap::::test_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) } } diff --git a/ec/src/hashing/map_to_curve_hasher.rs b/ec/src/hashing/map_to_curve_hasher.rs index 4cdfb017f..d1d19cc38 100644 --- a/ec/src/hashing/map_to_curve_hasher.rs +++ b/ec/src/hashing/map_to_curve_hasher.rs @@ -4,11 +4,11 @@ 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; + /// Checks whether supplied parameters represent a valid map. + fn test_parameters() -> Result<(), HashToCurveError>; /// Map an arbitary field element to a corresponding curve point. - fn map_to_curve(&self, point: T::BaseField) -> Result; + fn map_to_curve(point: T::BaseField) -> Result; } /// Helper struct that can be used to construct elements on the elliptic curve @@ -21,7 +21,7 @@ where M2C: MapToCurve, { field_hasher: H2F, - curve_mapper: M2C, + _curve_mapper: PhantomData, _params_t: PhantomData, } @@ -32,13 +32,12 @@ where M2C: MapToCurve, { fn new(domain: &[u8]) -> Result { - let field_hasher = H2F::new(domain); - let curve_mapper = M2C::new()?; - let _params_t = PhantomData; + #[cfg(test)] + M2C::test_parameters() ?; Ok(MapToCurveBasedHasher { - field_hasher, - curve_mapper, - _params_t, + field_hasher: H2F::new(domain), + _curve_mapper: PhantomData, + _params_t: PhantomData, }) } @@ -58,8 +57,8 @@ where 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_0 = M2C::map_to_curve(rand_field_elems[0])?; + let rand_curve_elem_1 = M2C::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(); From b4633c9c0b5aeb9a58b0d78f47157aa5365048d7 Mon Sep 17 00:00:00 2001 From: Jeff Burdges Date: Tue, 21 Mar 2023 01:16:02 +0100 Subject: [PATCH 03/47] Update ec/src/hashing/curve_maps/wb.rs Co-authored-by: Pratyush Mishra --- ec/src/hashing/curve_maps/wb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ec/src/hashing/curve_maps/wb.rs b/ec/src/hashing/curve_maps/wb.rs index c8d0730aa..f2674704c 100644 --- a/ec/src/hashing/curve_maps/wb.rs +++ b/ec/src/hashing/curve_maps/wb.rs @@ -85,7 +85,7 @@ pub struct WBMap { impl MapToCurve> for WBMap

{ /// Checks if `P` represents a valid map. - fn test_parameters() -> Result<(), HashToCurveError> { + fn check_parameters() -> Result<(), HashToCurveError> { match P::ISOGENY_MAP.apply(P::IsogenousCurve::GENERATOR) { Ok(point_on_curve) => { if !point_on_curve.is_on_curve() { From 6c1a3557b30f6fc71bf7ca58ed0aaf8c15f029f0 Mon Sep 17 00:00:00 2001 From: Jeff Burdges Date: Tue, 21 Mar 2023 01:16:11 +0100 Subject: [PATCH 04/47] Update ec/src/hashing/curve_maps/swu.rs Co-authored-by: Pratyush Mishra --- ec/src/hashing/curve_maps/swu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ec/src/hashing/curve_maps/swu.rs b/ec/src/hashing/curve_maps/swu.rs index e1f66bef6..28b1f15cc 100644 --- a/ec/src/hashing/curve_maps/swu.rs +++ b/ec/src/hashing/curve_maps/swu.rs @@ -36,7 +36,7 @@ pub fn parity(element: &F) -> bool { impl MapToCurve> for SWUMap

{ /// Checks if `P` represents a valid map. - fn test_parameters() -> Result<(), HashToCurveError> { + fn check_parameters() -> Result<(), HashToCurveError> { // Verifying that ZETA is a non-square if P::ZETA.legendre().is_qr() { return Err(HashToCurveError::MapToCurveError( From d21186157908a2a12b3eadc0925318b4df3f9f8b Mon Sep 17 00:00:00 2001 From: Jeff Burdges Date: Tue, 21 Mar 2023 01:16:19 +0100 Subject: [PATCH 05/47] Update ec/src/hashing/map_to_curve_hasher.rs Co-authored-by: Pratyush Mishra --- ec/src/hashing/map_to_curve_hasher.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ec/src/hashing/map_to_curve_hasher.rs b/ec/src/hashing/map_to_curve_hasher.rs index d1d19cc38..ad0c675f6 100644 --- a/ec/src/hashing/map_to_curve_hasher.rs +++ b/ec/src/hashing/map_to_curve_hasher.rs @@ -5,7 +5,7 @@ use ark_std::marker::PhantomData; /// Trait for mapping a random field element to a random curve point. pub trait MapToCurve: Sized { /// Checks whether supplied parameters represent a valid map. - fn test_parameters() -> Result<(), HashToCurveError>; + fn check_parameters() -> Result<(), HashToCurveError>; /// Map an arbitary field element to a corresponding curve point. fn map_to_curve(point: T::BaseField) -> Result; From 53d9cda8a099f91a595a83b25bcfad378b48acd2 Mon Sep 17 00:00:00 2001 From: Jeff Burdges Date: Tue, 21 Mar 2023 01:16:29 +0100 Subject: [PATCH 06/47] Update ec/src/hashing/curve_maps/swu.rs Co-authored-by: Pratyush Mishra --- ec/src/hashing/curve_maps/swu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ec/src/hashing/curve_maps/swu.rs b/ec/src/hashing/curve_maps/swu.rs index 28b1f15cc..556b963bc 100644 --- a/ec/src/hashing/curve_maps/swu.rs +++ b/ec/src/hashing/curve_maps/swu.rs @@ -256,7 +256,7 @@ mod test { /// elements should be mapped to curve successfully. everything can be mapped #[test] fn map_field_to_curve_swu() { - SWUMap::::test_parameters().unwrap(); + SWUMap::::check_parameters().unwrap(); let mut map_range: Vec> = vec![]; for current_field_element in 0..127 { From 10d2d6d869962e1b8ad8d6fc85f825847f911904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Tue, 21 Mar 2023 11:35:19 +0100 Subject: [PATCH 07/47] Two more renames --- ec/src/hashing/curve_maps/wb.rs | 2 +- ec/src/hashing/map_to_curve_hasher.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ec/src/hashing/curve_maps/wb.rs b/ec/src/hashing/curve_maps/wb.rs index f2674704c..46b635b1c 100644 --- a/ec/src/hashing/curve_maps/wb.rs +++ b/ec/src/hashing/curve_maps/wb.rs @@ -95,7 +95,7 @@ impl MapToCurve> for WBMap

{ Err(e) => return Err(e), } - SWUMap::::test_parameters().unwrap(); // Or ? + SWUMap::::check_parameters().unwrap(); // Or ? Ok(()) } diff --git a/ec/src/hashing/map_to_curve_hasher.rs b/ec/src/hashing/map_to_curve_hasher.rs index ad0c675f6..e3a80b5e2 100644 --- a/ec/src/hashing/map_to_curve_hasher.rs +++ b/ec/src/hashing/map_to_curve_hasher.rs @@ -33,7 +33,7 @@ where { fn new(domain: &[u8]) -> Result { #[cfg(test)] - M2C::test_parameters() ?; + M2C::check_parameters() ?; Ok(MapToCurveBasedHasher { field_hasher: H2F::new(domain), _curve_mapper: PhantomData, From 6d8fd5894b6d9860c5c19811b82c19df1647ec9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Tue, 21 Mar 2023 20:31:30 +0100 Subject: [PATCH 08/47] make rustfmt happier --- ec/src/hashing/curve_maps/swu.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ec/src/hashing/curve_maps/swu.rs b/ec/src/hashing/curve_maps/swu.rs index 556b963bc..b5884bf75 100644 --- a/ec/src/hashing/curve_maps/swu.rs +++ b/ec/src/hashing/curve_maps/swu.rs @@ -260,9 +260,8 @@ mod test { let mut map_range: Vec> = vec![]; for current_field_element in 0..127 { - map_range.push( - SWUMap::::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(); From c7ffca17c4d5c1ddd18e5ce00ac9331d4964bfd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Tue, 21 Mar 2023 20:35:57 +0100 Subject: [PATCH 09/47] Again rustfmt --- ec/src/hashing/curve_maps/swu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ec/src/hashing/curve_maps/swu.rs b/ec/src/hashing/curve_maps/swu.rs index b5884bf75..c989ece48 100644 --- a/ec/src/hashing/curve_maps/swu.rs +++ b/ec/src/hashing/curve_maps/swu.rs @@ -261,7 +261,7 @@ mod test { let mut map_range: Vec> = vec![]; for current_field_element in 0..127 { let point = F127::from(current_field_element as u64); - map_range.push( SWUMap::::map_to_curve(point).unwrap() ); + map_range.push(SWUMap::::map_to_curve(point).unwrap()); } let mut counts = HashMap::new(); From 80f889b7dfb165d23cc2fc8d515c93b6ef848925 Mon Sep 17 00:00:00 2001 From: Jeff Burdges Date: Tue, 21 Mar 2023 21:14:11 +0100 Subject: [PATCH 10/47] rustfmt is wrong on this one but whatever rustfmt is wrong that f()? should be preferred over f() ?, as errors paths should often be highlighted, but nobody merged that fix yet, so whatever. https://github.com/rust-lang/rustfmt/issues/5595 --- ec/src/hashing/map_to_curve_hasher.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ec/src/hashing/map_to_curve_hasher.rs b/ec/src/hashing/map_to_curve_hasher.rs index e3a80b5e2..e332f3c83 100644 --- a/ec/src/hashing/map_to_curve_hasher.rs +++ b/ec/src/hashing/map_to_curve_hasher.rs @@ -33,7 +33,7 @@ where { fn new(domain: &[u8]) -> Result { #[cfg(test)] - M2C::check_parameters() ?; + M2C::check_parameters()?; Ok(MapToCurveBasedHasher { field_hasher: H2F::new(domain), _curve_mapper: PhantomData, From 5d149dac5f545bed068b3e9e7fa39974bcf0afa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Wed, 22 Mar 2023 22:56:41 +0100 Subject: [PATCH 11/47] Z_pad without Vec --- ff/src/fields/field_hashers/expander/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 8b1ef0a12..fdbe68ed5 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -52,6 +52,8 @@ pub(super) struct ExpanderXmd { pub(super) block_size: usize, } +static Z_PAD: [u8; 256] = [0u8; 256]; + impl Expander for ExpanderXmd { fn construct_dst_prime(&self) -> Vec { let mut dst_prime = if self.dst.len() > MAX_DST_LENGTH { @@ -76,14 +78,13 @@ impl Expander for ExpanderXmd { ); 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. assert!(n < (1 << 16), "Length should be smaller than 2^16"); let lib_str: [u8; 2] = (n as u16).to_be_bytes(); - hasher.update(&z_pad); + hasher.update(&Z_PAD[0..self.block_size]); hasher.update(msg); hasher.update(&lib_str); hasher.update(&[0u8]); From b3eaa74956a1417e6e214f017788cf734d928bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Wed, 22 Mar 2023 22:58:37 +0100 Subject: [PATCH 12/47] inherent method for construct_dst_prime --- ff/src/fields/field_hashers/expander/mod.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index fdbe68ed5..4f69773b2 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -4,7 +4,6 @@ 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; } const MAX_DST_LENGTH: usize = 255; @@ -21,7 +20,7 @@ pub(super) struct ExpanderXof { pub(super) k: usize, } -impl Expander for ExpanderXof { +impl ExpanderXof { fn construct_dst_prime(&self) -> Vec { let mut dst_prime = if self.dst.len() > MAX_DST_LENGTH { let mut xofer = self.xofer.clone(); @@ -34,6 +33,9 @@ impl Expander for ExpanderXof { dst_prime.push(dst_prime.len() as u8); dst_prime } +} + +impl Expander for ExpanderXof { 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]; @@ -54,7 +56,7 @@ pub(super) struct ExpanderXmd { static Z_PAD: [u8; 256] = [0u8; 256]; -impl Expander for ExpanderXmd { +impl ExpanderXmd { fn construct_dst_prime(&self) -> Vec { let mut dst_prime = if self.dst.len() > MAX_DST_LENGTH { let mut hasher = self.hasher.clone(); @@ -67,6 +69,9 @@ impl Expander for ExpanderXmd { dst_prime.push(dst_prime.len() as u8); dst_prime } +} + +impl Expander for ExpanderXmd { fn expand(&self, msg: &[u8], n: usize) -> Vec { let mut hasher = self.hasher.clone(); // output size of the hash function, e.g. 32 bytes = 256 bits for sha2::Sha256 From 65f2229628ffb4e1d760e555ff8e281ce779afc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Thu, 23 Mar 2023 02:24:24 +0100 Subject: [PATCH 13/47] Use update directly for ExpanderXoF --- ff/src/fields/field_hashers/expander/mod.rs | 35 +++++++++++---------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 4f69773b2..c6a2c3a7d 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -14,36 +14,39 @@ const LONG_DST_PREFIX: [u8; 17] = [ 0x2d, ]; -pub(super) struct ExpanderXof { - pub(super) xofer: T, +pub(super) struct ExpanderXof { + pub(super) xofer: H, pub(super) dst: Vec, pub(super) k: usize, } -impl 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() +impl ExpanderXof { + fn update_dst_prime(&self, h: &mut H) { + if self.dst.len() > MAX_DST_LENGTH { + let mut long = H::default(); + long.update(&LONG_DST_PREFIX.clone()); + long.update(&self.dst); + + let mut new_dst = [0u8; MAX_DST_LENGTH]; + let new_dst = &mut new_dst[0..((2 * self.k + 7) >> 3)]; + long.finalize_xof_into(new_dst); + h.update(new_dst); + h.update(&[new_dst.len() as u8]); } else { - self.dst.clone() - }; - dst_prime.push(dst_prime.len() as u8); - dst_prime + h.update(&self.dst); + h.update(&[self.dst.len() as u8]); + } } } -impl Expander for ExpanderXof { +impl Expander for ExpanderXof { 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); + self.update_dst_prime(&mut xofer); xofer.finalize_boxed(n).to_vec() } } From 90ed3086b532107c96017efbe812248da6438c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Thu, 23 Mar 2023 02:26:27 +0100 Subject: [PATCH 14/47] More idiomatic lib_str --- ff/src/fields/field_hashers/expander/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index c6a2c3a7d..102f8bc2f 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -41,11 +41,13 @@ impl ExpanderXof { impl Expander for ExpanderXof { fn expand(&self, msg: &[u8], n: usize) -> Vec { - 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); + + // I2OSP(len,2) https://www.rfc-editor.org/rfc/rfc8017.txt + let lib_str = (n as u16).to_be_bytes(); + xofer.update(&lib_str); + self.update_dst_prime(&mut xofer); xofer.finalize_boxed(n).to_vec() } From f311cc2ed4e4d9f7d7c82e01a9f298f060061170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Thu, 23 Mar 2023 13:24:48 +0100 Subject: [PATCH 15/47] DST abstraction --- ff/Cargo.toml | 1 + ff/src/fields/field_hashers/expander/mod.rs | 45 ++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) 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 102f8bc2f..dc177bc14 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -2,7 +2,11 @@ // With some optimisations use ark_std::vec::Vec; -use digest::{DynDigest, ExtendableOutput, Update}; + +use digest::{DynDigest, FixedOutput, ExtendableOutput, Update}; +use arrayvec::ArrayVec; + + pub trait Expander { fn expand(&self, msg: &[u8], length: usize) -> Vec; } @@ -14,6 +18,45 @@ const LONG_DST_PREFIX: [u8; 17] = [ 0x2d, ]; + +pub(super) struct DST(pub arrayvec::ArrayVec); + +impl DST { + pub fn new_fixed(dst: &[u8]) -> DST { + DST(if dst.len() > MAX_DST_LENGTH { + let mut long = H::default(); + long.update(&LONG_DST_PREFIX.clone()); + long.update(&dst); + ArrayVec::try_from( long.finalize_fixed().as_ref() ).unwrap() + } else { + ArrayVec::try_from(dst).unwrap() + }) + } + + pub fn new_xof(dst: &[u8], k: usize) -> DST { + DST(if dst.len() > MAX_DST_LENGTH { + let mut long = H::default(); + long.update(&LONG_DST_PREFIX.clone()); + long.update(&dst); + + let mut new_dst = [0u8; MAX_DST_LENGTH]; + let new_dst = &mut new_dst[0..((2 * k + 7) >> 3)]; + long.finalize_xof_into(new_dst); + ArrayVec::try_from( &*new_dst ).unwrap() + } else { + ArrayVec::try_from(dst).unwrap() + }) + } + + pub fn update(&self, h: &mut H) { + h.update(self.0.as_ref()); + // I2OSP(len,1) https://www.rfc-editor.org/rfc/rfc8017.txt + h.update(&[self.0.len() as u8]); + } +} + + + pub(super) struct ExpanderXof { pub(super) xofer: H, pub(super) dst: Vec, From 6c8eeff366d7dca11639f2118c0bbb4e61bba740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Thu, 23 Mar 2023 13:45:22 +0100 Subject: [PATCH 16/47] Test that DST works --- ff/src/fields/field_hashers/expander/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index dc177bc14..0430623be 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -19,7 +19,7 @@ const LONG_DST_PREFIX: [u8; 17] = [ ]; -pub(super) struct DST(pub arrayvec::ArrayVec); +pub(super) struct DST(arrayvec::ArrayVec); impl DST { pub fn new_fixed(dst: &[u8]) -> DST { @@ -91,7 +91,8 @@ impl Expander for ExpanderXof { let lib_str = (n as u16).to_be_bytes(); xofer.update(&lib_str); - self.update_dst_prime(&mut xofer); + DST::new_xof::(self.dst.as_ref(), self.k).update(&mut xofer); + // self.update_dst_prime(&mut xofer); xofer.finalize_boxed(n).to_vec() } } From 343a8b773c40e6bd7e73767f11ed25236cb61f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Thu, 23 Mar 2023 13:47:25 +0100 Subject: [PATCH 17/47] Remove inherent method in favor of DST --- ff/src/fields/field_hashers/expander/mod.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 0430623be..f5cc4cca3 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -63,25 +63,6 @@ pub(super) struct ExpanderXof { pub(super) k: usize, } -impl ExpanderXof { - fn update_dst_prime(&self, h: &mut H) { - if self.dst.len() > MAX_DST_LENGTH { - let mut long = H::default(); - long.update(&LONG_DST_PREFIX.clone()); - long.update(&self.dst); - - let mut new_dst = [0u8; MAX_DST_LENGTH]; - let new_dst = &mut new_dst[0..((2 * self.k + 7) >> 3)]; - long.finalize_xof_into(new_dst); - h.update(new_dst); - h.update(&[new_dst.len() as u8]); - } else { - h.update(&self.dst); - h.update(&[self.dst.len() as u8]); - } - } -} - impl Expander for ExpanderXof { fn expand(&self, msg: &[u8], n: usize) -> Vec { let mut xofer = self.xofer.clone(); @@ -92,7 +73,6 @@ impl Expander for ExpanderXof { xofer.update(&lib_str); DST::new_xof::(self.dst.as_ref(), self.k).update(&mut xofer); - // self.update_dst_prime(&mut xofer); xofer.finalize_boxed(n).to_vec() } } From e9ce28798835598aaf8fcf4f2244f6286e013b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Thu, 23 Mar 2023 13:47:41 +0100 Subject: [PATCH 18/47] defalt instead of clone --- ff/src/fields/field_hashers/expander/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index f5cc4cca3..ecb26c678 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -65,7 +65,7 @@ pub(super) struct ExpanderXof { impl Expander for ExpanderXof { fn expand(&self, msg: &[u8], n: usize) -> Vec { - let mut xofer = self.xofer.clone(); + let mut xofer = H::default(); xofer.update(msg); // I2OSP(len,2) https://www.rfc-editor.org/rfc/rfc8017.txt From 23f177299a8409b879c4c9cd04fdea841afd79af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Thu, 23 Mar 2023 13:51:37 +0100 Subject: [PATCH 19/47] Default insead of Clone --- ff/src/fields/field_hashers/expander/mod.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index ecb26c678..aa22c2817 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -77,15 +77,15 @@ impl Expander for ExpanderXof { } } -pub(super) struct ExpanderXmd { - pub(super) hasher: T, +pub(super) struct ExpanderXmd { + pub(super) hasher: H, pub(super) dst: Vec, pub(super) block_size: usize, } static Z_PAD: [u8; 256] = [0u8; 256]; -impl ExpanderXmd { +impl ExpanderXmd { fn construct_dst_prime(&self) -> Vec { let mut dst_prime = if self.dst.len() > MAX_DST_LENGTH { let mut hasher = self.hasher.clone(); @@ -100,7 +100,7 @@ impl ExpanderXmd { } } -impl Expander for ExpanderXmd { +impl Expander for ExpanderXmd { fn expand(&self, msg: &[u8], n: usize) -> Vec { let mut hasher = self.hasher.clone(); // output size of the hash function, e.g. 32 bytes = 256 bits for sha2::Sha256 @@ -118,6 +118,7 @@ impl Expander for ExpanderXmd { assert!(n < (1 << 16), "Length should be smaller than 2^16"); let lib_str: [u8; 2] = (n as u16).to_be_bytes(); + let mut hasher = H::default(); hasher.update(&Z_PAD[0..self.block_size]); hasher.update(msg); hasher.update(&lib_str); @@ -125,6 +126,7 @@ impl Expander for ExpanderXmd { hasher.update(&dst_prime); let b0 = hasher.finalize_reset(); + let mut hasher = H::default(); hasher.update(&b0); hasher.update(&[1u8]); hasher.update(&dst_prime); @@ -133,6 +135,7 @@ impl Expander for ExpanderXmd { let mut uniform_bytes: Vec = Vec::with_capacity(n); uniform_bytes.extend_from_slice(&bi); for i in 2..=ell { + let mut hasher = H::default(); // 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]); From 15fc1a56ccfc8c911372f2411337a0ab04a24138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Fri, 14 Apr 2023 01:41:14 +0200 Subject: [PATCH 20/47] Field::from_base_prime_field_elems takes Iterator, not slice. --- ff/src/fields/field_hashers/mod.rs | 2 +- ff/src/fields/mod.rs | 3 +- ff/src/fields/models/cubic_extension.rs | 32 ++++++++++++--------- ff/src/fields/models/fp/mod.rs | 8 ++++-- ff/src/fields/models/quadratic_extension.rs | 28 ++++++++++-------- test-templates/src/h2c/mod.rs | 4 +-- 6 files changed, 44 insertions(+), 33 deletions(-) diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index 82454f231..bfd44f231 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -81,7 +81,7 @@ impl HashToFie ); base_prime_field_elems.push(val); } - let f = F::from_base_prime_field_elems(&base_prime_field_elems).unwrap(); + let f = F::from_base_prime_field_elems(base_prime_field_elems.drain(..)).unwrap(); output.push(f); } 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-templates/src/h2c/mod.rs b/test-templates/src/h2c/mod.rs index c65391d23..4cd52eb35 100644 --- a/test-templates/src/h2c/mod.rs +++ b/test-templates/src/h2c/mod.rs @@ -63,8 +63,8 @@ macro_rules! test_h2c { let y = read_fq_vec(&v.p.y); let got = g1_mapper.hash(&v.msg.as_bytes()).unwrap(); 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()); From f19976ecbb8060a0ad86fd0f214d9ae6f51480d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Fri, 14 Apr 2023 08:12:40 +0200 Subject: [PATCH 21/47] use const len in hash_to_field --- ec/src/hashing/map_to_curve_hasher.rs | 2 +- ff/src/fields/field_hashers/mod.rs | 30 +++++++++++---------------- test-templates/src/h2c/mod.rs | 6 +++--- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/ec/src/hashing/map_to_curve_hasher.rs b/ec/src/hashing/map_to_curve_hasher.rs index e332f3c83..ed9713c10 100644 --- a/ec/src/hashing/map_to_curve_hasher.rs +++ b/ec/src/hashing/map_to_curve_hasher.rs @@ -55,7 +55,7 @@ where // 5. P = clear_cofactor(R) // 6. return P - let rand_field_elems = self.field_hasher.hash_to_field(msg, 2); + let rand_field_elems = self.field_hasher.hash_to_field::<2>(msg); let rand_curve_elem_0 = M2C::map_to_curve(rand_field_elems[0])?; let rand_curve_elem_1 = M2C::map_to_curve(rand_field_elems[1])?; diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index bfd44f231..c81b271c6 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -18,7 +18,7 @@ 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, @@ -33,7 +33,7 @@ pub trait HashToField: Sized { /// 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] = hasher.hash_to_field(b"Hello, World!"); /// /// assert_eq!(field_elements.len(), 2); /// ``` @@ -62,30 +62,24 @@ impl HashToFie } } - fn hash_to_field(&self, message: &[u8], count: usize) -> Vec { + fn hash_to_field(&self, message: &[u8]) -> [F; N] { let m = F::extension_degree() as usize; // 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 len_in_bytes = N * m * self.len_per_base_elem; let uniform_bytes = self.expander.expand(message, len_in_bytes); - 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 cb = |i| { + let base_prime_field_elem = |j| { let elm_offset = self.len_per_base_elem * (j + i * m); - let val = F::BasePrimeField::from_be_bytes_mod_order( + 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.drain(..)).unwrap(); - output.push(f); - } - - output + ) + }; + F::from_base_prime_field_elems( (0..m).map( base_prime_field_elem ) ).unwrap() + }; + ark_std::array::from_fn::(cb) } } diff --git a/test-templates/src/h2c/mod.rs b/test-templates/src/h2c/mod.rs index 4cd52eb35..378aed971 100644 --- a/test-templates/src/h2c/mod.rs +++ b/test-templates/src/h2c/mod.rs @@ -52,11 +52,11 @@ macro_rules! test_h2c { 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 } ] = + hasher.hash_to_field(&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); From 99299cef35d1d83c302cf9ff7613cf0ce7dbc70f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Fri, 14 Apr 2023 08:13:22 +0200 Subject: [PATCH 22/47] Ugh Vec -> Vec here was just stupid --- ff/src/fields/field_hashers/expander/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index aa22c2817..411ba1c75 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -145,7 +145,8 @@ impl Expander for ExpanderXmd { bi = hasher.finalize_reset(); uniform_bytes.extend_from_slice(&bi); } - uniform_bytes[0..n].to_vec() + uniform_bytes.truncate(n); + uniform_bytes } } From 20b4111c5d6469ae4b47da3d156ba2a123a74ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Fri, 14 Apr 2023 08:32:16 +0200 Subject: [PATCH 23/47] Kill DynDigest --- ff/src/fields/field_hashers/expander/mod.rs | 20 ++++++++++---------- ff/src/fields/field_hashers/mod.rs | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 411ba1c75..d9789e839 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -3,7 +3,7 @@ use ark_std::vec::Vec; -use digest::{DynDigest, FixedOutput, ExtendableOutput, Update}; +use digest::{FixedOutput, ExtendableOutput, Update}; use arrayvec::ArrayVec; @@ -77,7 +77,7 @@ impl Expander for ExpanderXof { } } -pub(super) struct ExpanderXmd { +pub(super) struct ExpanderXmd { pub(super) hasher: H, pub(super) dst: Vec, pub(super) block_size: usize, @@ -85,13 +85,13 @@ pub(super) struct ExpanderXmd { static Z_PAD: [u8; 256] = [0u8; 256]; -impl ExpanderXmd { +impl 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() + hasher.finalize_fixed().to_vec() } else { self.dst.clone() }; @@ -100,11 +100,11 @@ impl ExpanderXmd { } } -impl Expander for ExpanderXmd { +impl Expander for ExpanderXmd { fn expand(&self, msg: &[u8], n: usize) -> Vec { - let mut hasher = self.hasher.clone(); + 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, @@ -124,13 +124,13 @@ impl Expander for ExpanderXmd { hasher.update(&lib_str); hasher.update(&[0u8]); hasher.update(&dst_prime); - let b0 = hasher.finalize_reset(); + let b0 = hasher.finalize_fixed(); let mut hasher = H::default(); hasher.update(&b0); hasher.update(&[1u8]); hasher.update(&dst_prime); - let mut bi = hasher.finalize_reset(); + let mut bi = hasher.finalize_fixed(); let mut uniform_bytes: Vec = Vec::with_capacity(n); uniform_bytes.extend_from_slice(&bi); @@ -142,7 +142,7 @@ impl Expander for ExpanderXmd { } hasher.update(&[i as u8]); hasher.update(&dst_prime); - bi = hasher.finalize_reset(); + bi = hasher.finalize_fixed(); uniform_bytes.extend_from_slice(&bi); } uniform_bytes.truncate(n); diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index c81b271c6..aa6d5550b 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -3,7 +3,7 @@ mod expander; use crate::{Field, PrimeField}; use ark_std::vec::Vec; -use digest::DynDigest; +use digest::FixedOutput; use expander::Expander; use self::expander::ExpanderXmd; @@ -37,12 +37,12 @@ pub trait HashToField: Sized { /// /// assert_eq!(field_elements.len(), 2); /// ``` -pub struct DefaultFieldHasher { +pub struct DefaultFieldHasher { expander: ExpanderXmd, len_per_base_elem: usize, } -impl HashToField +impl HashToField for DefaultFieldHasher { fn new(dst: &[u8]) -> Self { From fd0d50fedecfccb60868778333961651c8369027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Fri, 14 Apr 2023 08:45:01 +0200 Subject: [PATCH 24/47] Idiomatic const --- ff/src/fields/field_hashers/expander/mod.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index d9789e839..d9ca235d1 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -12,11 +12,7 @@ pub trait Expander { } 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-"; pub(super) struct DST(arrayvec::ArrayVec); @@ -25,7 +21,7 @@ impl DST { pub fn new_fixed(dst: &[u8]) -> DST { DST(if dst.len() > MAX_DST_LENGTH { let mut long = H::default(); - long.update(&LONG_DST_PREFIX.clone()); + long.update(&LONG_DST_PREFIX[..]); long.update(&dst); ArrayVec::try_from( long.finalize_fixed().as_ref() ).unwrap() } else { @@ -36,7 +32,7 @@ impl DST { pub fn new_xof(dst: &[u8], k: usize) -> DST { DST(if dst.len() > MAX_DST_LENGTH { let mut long = H::default(); - long.update(&LONG_DST_PREFIX.clone()); + long.update(&LONG_DST_PREFIX[..]); long.update(&dst); let mut new_dst = [0u8; MAX_DST_LENGTH]; From 727daafbc6255a4b35bdcffe89a17d4b35ca5215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Fri, 14 Apr 2023 08:45:58 +0200 Subject: [PATCH 25/47] semi-merge dst_prime --- ff/src/fields/field_hashers/expander/mod.rs | 24 +++++---------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index d9ca235d1..c199a6720 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -81,21 +81,6 @@ pub(super) struct ExpanderXmd { static Z_PAD: [u8; 256] = [0u8; 256]; -impl 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_fixed().to_vec() - } else { - self.dst.clone() - }; - dst_prime.push(dst_prime.len() as u8); - dst_prime - } -} - impl Expander for ExpanderXmd { fn expand(&self, msg: &[u8], n: usize) -> Vec { use digest::typenum::Unsigned; @@ -107,7 +92,7 @@ impl Expander for ExpanderXmd { "The ratio of desired output to the output size of hash function is too large!" ); - let dst_prime = self.construct_dst_prime(); + let dst_prime = DST::new_fixed::(self.dst.as_ref()); // 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. @@ -119,13 +104,14 @@ impl Expander for ExpanderXmd { hasher.update(msg); hasher.update(&lib_str); hasher.update(&[0u8]); - hasher.update(&dst_prime); + dst_prime.update(& mut hasher); let b0 = hasher.finalize_fixed(); + let mut hasher = H::default(); hasher.update(&b0); hasher.update(&[1u8]); - hasher.update(&dst_prime); + dst_prime.update(& mut hasher); let mut bi = hasher.finalize_fixed(); let mut uniform_bytes: Vec = Vec::with_capacity(n); @@ -137,7 +123,7 @@ impl Expander for ExpanderXmd { hasher.update(&[*l ^ *r]); } hasher.update(&[i as u8]); - hasher.update(&dst_prime); + dst_prime.update(& mut hasher); bi = hasher.finalize_fixed(); uniform_bytes.extend_from_slice(&bi); } From e0f1ab7481f37cfd5e5d35c77180068a13e0f72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Fri, 14 Apr 2023 08:52:18 +0200 Subject: [PATCH 26/47] FixedOutputReset instead of FixedOutput --- ff/src/fields/field_hashers/expander/mod.rs | 13 ++++++------- ff/src/fields/field_hashers/mod.rs | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index c199a6720..675ad0982 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -3,7 +3,7 @@ use ark_std::vec::Vec; -use digest::{FixedOutput, ExtendableOutput, Update}; +use digest::{FixedOutputReset, ExtendableOutput, Update}; use arrayvec::ArrayVec; @@ -18,7 +18,7 @@ const LONG_DST_PREFIX: &[u8; 17] = b"H2C-OVERSIZE-DST-"; pub(super) struct DST(arrayvec::ArrayVec); impl DST { - pub fn new_fixed(dst: &[u8]) -> DST { + pub fn new_fixed(dst: &[u8]) -> DST { DST(if dst.len() > MAX_DST_LENGTH { let mut long = H::default(); long.update(&LONG_DST_PREFIX[..]); @@ -73,7 +73,7 @@ impl Expander for ExpanderXof { } } -pub(super) struct ExpanderXmd { +pub(super) struct ExpanderXmd { pub(super) hasher: H, pub(super) dst: Vec, pub(super) block_size: usize, @@ -81,7 +81,7 @@ pub(super) struct ExpanderXmd { static Z_PAD: [u8; 256] = [0u8; 256]; -impl Expander for ExpanderXmd { +impl Expander for ExpanderXmd { fn expand(&self, msg: &[u8], n: usize) -> Vec { use digest::typenum::Unsigned; // output size of the hash function, e.g. 32 bytes = 256 bits for sha2::Sha256 @@ -107,7 +107,6 @@ impl Expander for ExpanderXmd { dst_prime.update(& mut hasher); let b0 = hasher.finalize_fixed(); - let mut hasher = H::default(); hasher.update(&b0); hasher.update(&[1u8]); @@ -116,15 +115,15 @@ impl Expander for ExpanderXmd { let mut uniform_bytes: Vec = Vec::with_capacity(n); uniform_bytes.extend_from_slice(&bi); + let mut hasher = H::default(); for i in 2..=ell { - let mut hasher = H::default(); // 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]); dst_prime.update(& mut hasher); - bi = hasher.finalize_fixed(); + bi = hasher.finalize_fixed_reset(); uniform_bytes.extend_from_slice(&bi); } uniform_bytes.truncate(n); diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index aa6d5550b..ef257cf4c 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -3,7 +3,7 @@ mod expander; use crate::{Field, PrimeField}; use ark_std::vec::Vec; -use digest::FixedOutput; +use digest::FixedOutputReset; use expander::Expander; use self::expander::ExpanderXmd; @@ -37,12 +37,12 @@ pub trait HashToField: Sized { /// /// assert_eq!(field_elements.len(), 2); /// ``` -pub struct DefaultFieldHasher { +pub struct DefaultFieldHasher { expander: ExpanderXmd, len_per_base_elem: usize, } -impl HashToField +impl HashToField for DefaultFieldHasher { fn new(dst: &[u8]) -> Self { From 25af4a605aced85154bd2fc42591e4fb74701554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Fri, 14 Apr 2023 09:06:21 +0200 Subject: [PATCH 27/47] More double Vec stupidity --- ff/src/fields/field_hashers/expander/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 675ad0982..ca49c5b75 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -69,7 +69,7 @@ impl Expander for ExpanderXof { xofer.update(&lib_str); DST::new_xof::(self.dst.as_ref(), self.k).update(&mut xofer); - xofer.finalize_boxed(n).to_vec() + xofer.finalize_boxed(n).into_vec() } } From 9982b4d43d89a6daee602a1979cc1373b25cf574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Fri, 14 Apr 2023 09:25:11 +0200 Subject: [PATCH 28/47] Initial XofReader based hash_to_field --- ff/src/fields/field_hashers/mod.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index ef257cf4c..436003958 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -3,7 +3,7 @@ mod expander; use crate::{Field, PrimeField}; use ark_std::vec::Vec; -use digest::FixedOutputReset; +use digest::{FixedOutputReset,XofReader}; use expander::Expander; use self::expander::ExpanderXmd; @@ -83,11 +83,28 @@ impl Ha } } +pub fn hash_to_field(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::(); + // Rust *still* lacks alloca, hence this ugly hack. + let mut alloca = [0u8; 2048]; + let alloca = &mut alloca[0..len_per_base_elem]; + + let m = F::extension_degree() as usize; + + let base_prime_field_elem = |_| { + h.read(alloca); + F::BasePrimeField::from_be_bytes_mod_order(alloca) + }; + 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() -> usize { // ceil(log(p)) let base_field_size_in_bits = F::BasePrimeField::MODULUS_BIT_SIZE as usize; // ceil(log(p)) + security_parameter From 5fa83efc418d3bc80ba27b077668b5c9d34c7394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Mon, 17 Apr 2023 06:53:22 +0200 Subject: [PATCH 29/47] Expose DST --- ff/src/fields/field_hashers/expander/mod.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index ca49c5b75..928bcedc9 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -14,11 +14,13 @@ const MAX_DST_LENGTH: usize = 255; const LONG_DST_PREFIX: &[u8; 17] = b"H2C-OVERSIZE-DST-"; - -pub(super) struct DST(arrayvec::ArrayVec); +/// 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); impl DST { - pub fn new_fixed(dst: &[u8]) -> DST { + pub fn new_xmd(dst: &[u8]) -> DST { DST(if dst.len() > MAX_DST_LENGTH { let mut long = H::default(); long.update(&LONG_DST_PREFIX[..]); @@ -52,7 +54,6 @@ impl DST { } - pub(super) struct ExpanderXof { pub(super) xofer: H, pub(super) dst: Vec, @@ -92,7 +93,7 @@ impl Expander for ExpanderXmd { "The ratio of desired output to the output size of hash function is too large!" ); - let dst_prime = DST::new_fixed::(self.dst.as_ref()); + let dst_prime = DST::new_xmd::(self.dst.as_ref()); // 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. From 6573052331e4871693d6cd1f5108704d877d5892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Mon, 17 Apr 2023 07:24:13 +0200 Subject: [PATCH 30/47] user reset --- ff/src/fields/field_hashers/expander/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 928bcedc9..6da786ee7 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -106,17 +106,15 @@ impl Expander for ExpanderXmd { hasher.update(&lib_str); hasher.update(&[0u8]); dst_prime.update(& mut hasher); - let b0 = hasher.finalize_fixed(); + let b0 = hasher.finalize_fixed_reset(); - let mut hasher = H::default(); hasher.update(&b0); hasher.update(&[1u8]); dst_prime.update(& mut hasher); - let mut bi = hasher.finalize_fixed(); + let mut bi = hasher.finalize_fixed_reset(); let mut uniform_bytes: Vec = Vec::with_capacity(n); uniform_bytes.extend_from_slice(&bi); - let mut hasher = H::default(); 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()) { From 818e8218f531910ace7c3031dc114c7cd9ff8fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Mon, 17 Apr 2023 22:19:24 +0200 Subject: [PATCH 31/47] Switch to XofReader Interface remains shit, but it becomes fixable now. --- ff/src/fields/field_hashers/expander/mod.rs | 41 ++++++++++++++++----- ff/src/fields/field_hashers/mod.rs | 22 +++++------ 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 6da786ee7..a14fa1eb3 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -3,12 +3,13 @@ use ark_std::vec::Vec; -use digest::{FixedOutputReset, ExtendableOutput, Update}; +use digest::{FixedOutputReset, ExtendableOutput, XofReader, Update}; use arrayvec::ArrayVec; pub trait Expander { - fn expand(&self, msg: &[u8], length: usize) -> Vec; + type R: XofReader; + fn expand(&self, msg: &[u8], length: usize) -> Self::R; } const MAX_DST_LENGTH: usize = 255; @@ -61,7 +62,8 @@ pub(super) struct ExpanderXof { } impl Expander for ExpanderXof { - fn expand(&self, msg: &[u8], n: usize) -> Vec { + type R = ::Reader; + fn expand(&self, msg: &[u8], n: usize) -> Self::R { let mut xofer = H::default(); xofer.update(msg); @@ -70,7 +72,7 @@ impl Expander for ExpanderXof { xofer.update(&lib_str); DST::new_xof::(self.dst.as_ref(), self.k).update(&mut xofer); - xofer.finalize_boxed(n).into_vec() + xofer.finalize_xof() } } @@ -83,7 +85,8 @@ pub(super) struct ExpanderXmd { static Z_PAD: [u8; 256] = [0u8; 256]; impl Expander for ExpanderXmd { - fn expand(&self, msg: &[u8], n: usize) -> Vec { + type R = XofVec; + fn expand(&self, msg: &[u8], n: usize) -> Self::R { use digest::typenum::Unsigned; // output size of the hash function, e.g. 32 bytes = 256 bits for sha2::Sha256 let b_len = H::OutputSize::to_usize(); @@ -113,8 +116,8 @@ impl Expander for ExpanderXmd { dst_prime.update(& 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()) { @@ -123,12 +126,30 @@ impl Expander for ExpanderXmd { hasher.update(&[i as u8]); dst_prime.update(& mut hasher); bi = hasher.finalize_fixed_reset(); - uniform_bytes.extend_from_slice(&bi); + bytes.extend_from_slice(&bi); } - uniform_bytes.truncate(n); - uniform_bytes + 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/mod.rs b/ff/src/fields/field_hashers/mod.rs index 436003958..fba5e61d0 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -68,17 +68,9 @@ impl Ha // 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 = N * m * self.len_per_base_elem; - let uniform_bytes = self.expander.expand(message, len_in_bytes); - - let cb = |i| { - let base_prime_field_elem = |j| { - let elm_offset = self.len_per_base_elem * (j + i * m); - F::BasePrimeField::from_be_bytes_mod_order( - &uniform_bytes[elm_offset..][..self.len_per_base_elem], - ) - }; - F::from_base_prime_field_elems( (0..m).map( base_prime_field_elem ) ).unwrap() - }; + let mut uniform_bytes = self.expander.expand(message, len_in_bytes); + + let cb = |_| hash_to_field::(&mut uniform_bytes); ark_std::array::from_fn::(cb) } } @@ -88,14 +80,18 @@ pub fn hash_to_field(h: &mut H) - // elements from F::BaseField, each of size `len_per_elem`. let len_per_base_elem = get_len_per_elem::(); // Rust *still* lacks alloca, hence this ugly hack. - let mut alloca = [0u8; 2048]; + if len_per_base_elem > 256 { + panic!("PrimeField larger than 1913 bits!"); + } + let mut alloca = [0u8; 256]; let alloca = &mut alloca[0..len_per_base_elem]; let m = F::extension_degree() as usize; let base_prime_field_elem = |_| { h.read(alloca); - F::BasePrimeField::from_be_bytes_mod_order(alloca) + alloca.reverse(); // Need BE but Arkworks' LE is faster + F::BasePrimeField::from_le_bytes_mod_order(alloca) }; F::from_base_prime_field_elems( (0..m).map(base_prime_field_elem) ).unwrap() } From a1fdce4500997296c6debe7f491a1d3c73a92c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Tue, 18 Apr 2023 22:57:39 +0200 Subject: [PATCH 32/47] Sergey complained. lol --- ff/src/fields/field_hashers/mod.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index fba5e61d0..399ed8c55 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -80,18 +80,21 @@ pub fn hash_to_field(h: &mut H) - // elements from F::BaseField, each of size `len_per_elem`. let len_per_base_elem = get_len_per_elem::(); // Rust *still* lacks alloca, hence this ugly hack. - if len_per_base_elem > 256 { - panic!("PrimeField larger than 1913 bits!"); - } let mut alloca = [0u8; 256]; - let alloca = &mut alloca[0..len_per_base_elem]; + 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 m = F::extension_degree() as usize; let base_prime_field_elem = |_| { - h.read(alloca); - alloca.reverse(); // Need BE but Arkworks' LE is faster - F::BasePrimeField::from_le_bytes_mod_order(alloca) + 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() } From a697434c35ef413b34a27dd076284fa92811fcfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Tue, 18 Apr 2023 22:57:55 +0200 Subject: [PATCH 33/47] Switch to fn interface --- ff/src/fields/field_hashers/expander/mod.rs | 57 ++++++------ ff/src/fields/field_hashers/expander/tests.rs | 88 +++++-------------- ff/src/fields/field_hashers/mod.rs | 73 +++++++-------- 3 files changed, 79 insertions(+), 139 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index a14fa1eb3..8f5961513 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -32,14 +32,23 @@ impl DST { }) } - pub fn new_xof(dst: &[u8], k: usize) -> DST { + // 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 { DST(if dst.len() > MAX_DST_LENGTH { + let sec_param = sec_param.expect("expand_message_xof wants a security parameter for compressing a long domain string."); 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 * k + 7) >> 3)]; + let new_dst = &mut new_dst[0..((2 * sec_param + 7) >> 3)]; long.finalize_xof_into(new_dst); ArrayVec::try_from( &*new_dst ).unwrap() } else { @@ -52,18 +61,11 @@ impl DST { // I2OSP(len,1) https://www.rfc-editor.org/rfc/rfc8017.txt h.update(&[self.0.len() as u8]); } -} - -pub(super) struct ExpanderXof { - pub(super) xofer: H, - pub(super) dst: Vec, - pub(super) k: usize, -} - -impl Expander for ExpanderXof { - type R = ::Reader; - fn expand(&self, msg: &[u8], n: usize) -> Self::R { + pub fn expand_xof(&self, msg: &[u8], n: usize) -> impl XofReader + where H: ExtendableOutput+Default + { + let dst = self; let mut xofer = H::default(); xofer.update(msg); @@ -71,22 +73,15 @@ impl Expander for ExpanderXof { let lib_str = (n as u16).to_be_bytes(); xofer.update(&lib_str); - DST::new_xof::(self.dst.as_ref(), self.k).update(&mut xofer); + // DST::new_xof::(self.dst.as_ref(), self.k) + dst.update(&mut xofer); xofer.finalize_xof() } -} -pub(super) struct ExpanderXmd { - pub(super) hasher: H, - pub(super) dst: Vec, - pub(super) block_size: usize, -} - -static Z_PAD: [u8; 256] = [0u8; 256]; - -impl Expander for ExpanderXmd { - type R = XofVec; - fn expand(&self, msg: &[u8], n: usize) -> Self::R { + pub fn expand_xmd(&self, block_size: usize, msg: &[u8], n: usize) -> impl XofReader + where H: FixedOutputReset+Default + { + let dst = self; use digest::typenum::Unsigned; // output size of the hash function, e.g. 32 bytes = 256 bits for sha2::Sha256 let b_len = H::OutputSize::to_usize(); @@ -96,7 +91,6 @@ impl Expander for ExpanderXmd { "The ratio of desired output to the output size of hash function is too large!" ); - let dst_prime = DST::new_xmd::(self.dst.as_ref()); // 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. @@ -104,16 +98,16 @@ impl Expander for ExpanderXmd { let lib_str: [u8; 2] = (n as u16).to_be_bytes(); let mut hasher = H::default(); - hasher.update(&Z_PAD[0..self.block_size]); + hasher.update(&Z_PAD[0..block_size]); hasher.update(msg); hasher.update(&lib_str); hasher.update(&[0u8]); - dst_prime.update(& mut hasher); + dst.update(& mut hasher); let b0 = hasher.finalize_fixed_reset(); hasher.update(&b0); hasher.update(&[1u8]); - dst_prime.update(& mut hasher); + dst.update(& mut hasher); let mut bi = hasher.finalize_fixed_reset(); let mut bytes: Vec = Vec::with_capacity(n); @@ -124,7 +118,7 @@ impl Expander for ExpanderXmd { hasher.update(&[*l ^ *r]); } hasher.update(&[i as u8]); - dst_prime.update(& mut hasher); + dst.update(& mut hasher); bi = hasher.finalize_fixed_reset(); bytes.extend_from_slice(&bi); } @@ -133,6 +127,7 @@ impl Expander for ExpanderXmd { } } +static Z_PAD: [u8; 256] = [0u8; 256]; pub struct XofVec { bytes: Vec, diff --git a/ff/src/fields/field_hashers/expander/tests.rs b/ff/src/fields/field_hashers/expander/tests.rs index 36b4190f9..75675a438 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::XofReader}; use std::{ fs::{read_dir, File}, io::BufReader, }; -use super::{Expander, ExpanderXmd, ExpanderXof}; +use super::{DST}; #[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)), + "SHAKE256" => DST::new_xof::(data.dst.as_bytes(), Some(data.k)), _ => 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" => dst.expand_xmd::(64, v.msg.as_bytes(), len).read_boxed(len), + "SHA384" => dst.expand_xmd::(128, v.msg.as_bytes(), len).read_boxed(len), + "SHA512" => dst.expand_xmd::(128, v.msg.as_bytes(), len).read_boxed(len), + "SHAKE128" => dst.expand_xof::(v.msg.as_bytes(), len).read_boxed(len), + "SHAKE256" => dst.expand_xof::(v.msg.as_bytes(), 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 399ed8c55..d541ed65e 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -3,10 +3,13 @@ mod expander; use crate::{Field, PrimeField}; use ark_std::vec::Vec; -use digest::{FixedOutputReset,XofReader}; -use expander::Expander; +use digest::{ExtendableOutput,FixedOutputReset,XofReader}; +pub use expander::DST; -use self::expander::ExpanderXmd; + +// pub trait HashToField: Field { +// fn hash_to_field() -> Self; +// } /// Trait for hashing messages to field elements. pub trait HashToField: Sized { @@ -28,54 +31,44 @@ 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: [Fq; 2] = hasher.hash_to_field(b"Hello, World!"); +/// let field_elements: [Fq; 2] = xmd_hash_to_field::(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(dst: &[u8], msg: &[u8]) -> [F; N] +where F: Field, H: FixedOutputReset+Default, +{ + let dst = DST::new_xmd::(dst); + + let len_per_base_elem = get_len_per_elem::(); + let m = F::extension_degree() as usize; + let total_length = N * m * len_per_base_elem; + let mut xmd = dst.expand_xmd::(len_per_base_elem, msg, total_length); + + let h2f = |_| hash_to_field::(&mut xmd); + ark_std::array::from_fn::(h2f) } -impl HashToField - for DefaultFieldHasher + +pub fn xof_hash_to_field(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]) -> [F; N] { - let m = F::extension_degree() as usize; - - // 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 = N * m * self.len_per_base_elem; - let mut uniform_bytes = self.expander.expand(message, len_in_bytes); - - let cb = |_| hash_to_field::(&mut uniform_bytes); - ark_std::array::from_fn::(cb) - } + let dst = DST::new_xof::(dst,Some(SEC_PARAM)); + + let len_per_base_elem = get_len_per_elem::(); + let m = F::extension_degree() as usize; + let total_length = N * m * len_per_base_elem; + let mut xof = dst.expand_xof::(msg, total_length); + + let h2f = |_| hash_to_field::(&mut xof); + ark_std::array::from_fn::(h2f) } -pub fn hash_to_field(h: &mut H) -> F { +pub fn hash_to_field(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::(); From 25c93fbdd377c095603c8de69a228611e27fe6b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Wed, 19 Apr 2023 07:55:42 +0200 Subject: [PATCH 34/47] Compute block_size and use Update --- ff/Cargo.toml | 2 +- ff/src/fields/field_hashers/expander/mod.rs | 87 ++++++++++++------- ff/src/fields/field_hashers/expander/tests.rs | 14 +-- ff/src/fields/field_hashers/mod.rs | 12 +-- 4 files changed, 70 insertions(+), 45 deletions(-) diff --git a/ff/Cargo.toml b/ff/Cargo.toml index de8fe2986..4efc2e2da 100644 --- a/ff/Cargo.toml +++ b/ff/Cargo.toml @@ -25,7 +25,7 @@ paste = "1.0" rayon = { version = "1", optional = true } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } num-bigint = { version = "0.4", default-features = false } -digest = { version = "0.10", default-features = false, features = ["alloc"] } +digest = { version = "0.10", default-features = false, features = ["alloc", "core-api"] } itertools = { version = "0.10", default-features = false } [dev-dependencies] diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 8f5961513..73ac1cd97 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -3,7 +3,7 @@ use ark_std::vec::Vec; -use digest::{FixedOutputReset, ExtendableOutput, XofReader, Update}; +use digest::{FixedOutputReset, ExtendableOutput, XofReader, Update, core_api::BlockSizeUser}; use arrayvec::ArrayVec; @@ -20,16 +20,29 @@ const LONG_DST_PREFIX: &[u8; 17] = b"H2C-OVERSIZE-DST-"; /// [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); +impl TryFrom<&'static [u8]> for DST { + type Error = &'static str; + fn try_from(dst: &'static [u8]) -> Result { + Ok(DST(ArrayVec::try_from(dst).map_err(|_| "DST longer than 255 bytes!") ?)) + } +} + +/* +impl From<&'static [u8]> for DST { + fn from(dst: &'static [u8]) -> Self { + ArrayVec::try_from(dst).expect("DST longer than 255 bytes!"); + } +} +*/ + impl DST { pub fn new_xmd(dst: &[u8]) -> DST { - DST(if dst.len() > MAX_DST_LENGTH { + 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() - } else { - ArrayVec::try_from(dst).unwrap() - }) + } )) } // pub fn sec_param(dst: &[u8]) -> usize { @@ -41,7 +54,7 @@ impl DST { // } pub fn new_xof(dst: &[u8], sec_param: Option) -> DST { - DST(if dst.len() > MAX_DST_LENGTH { + 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."); let mut long = H::default(); long.update(&LONG_DST_PREFIX[..]); @@ -51,9 +64,7 @@ impl DST { let new_dst = &mut new_dst[0..((2 * sec_param + 7) >> 3)]; long.finalize_xof_into(new_dst); ArrayVec::try_from( &*new_dst ).unwrap() - } else { - ArrayVec::try_from(dst).unwrap() - }) + } )) } pub fn update(&self, h: &mut H) { @@ -61,27 +72,48 @@ impl DST { // I2OSP(len,1) https://www.rfc-editor.org/rfc/rfc8017.txt h.update(&[self.0.len() as u8]); } +} + +static Z_PAD: [u8; 256] = [0u8; 256]; + +pub struct IrtfH2F(H); + +impl Update for IrtfH2F { + fn update(&mut self, data: &[u8]) { + self.0.update(data); + } +} - pub fn expand_xof(&self, msg: &[u8], n: usize) -> impl XofReader - where H: ExtendableOutput+Default +impl IrtfH2F { + pub fn new_xof() -> IrtfH2F + where H: ExtendableOutput { - let dst = self; - let mut xofer = H::default(); - xofer.update(msg); + IrtfH2F(H::default()) + } + pub fn expand_xof(mut self, dst: &DST, n: usize) -> impl XofReader + where H: ExtendableOutput + { + assert!(n < (1 << 16), "Length should be smaller than 2^16"); // I2OSP(len,2) https://www.rfc-editor.org/rfc/rfc8017.txt - let lib_str = (n as u16).to_be_bytes(); - xofer.update(&lib_str); + self.0.update(& (n as u16).to_be_bytes()); // DST::new_xof::(self.dst.as_ref(), self.k) - dst.update(&mut xofer); - xofer.finalize_xof() + dst.update(&mut self.0); + self.0.finalize_xof() + } + + pub fn new_xmd() -> IrtfH2F + where H: FixedOutputReset+BlockSizeUser + { + let mut hasher = H::default(); + hasher.update(&Z_PAD[0 .. H::block_size()]); + IrtfH2F(hasher) } - pub fn expand_xmd(&self, block_size: usize, msg: &[u8], n: usize) -> impl XofReader - where H: FixedOutputReset+Default + pub fn expand_xmd(self, dst: &DST, n: usize) -> impl XofReader + where H: FixedOutputReset { - let dst = self; use digest::typenum::Unsigned; // output size of the hash function, e.g. 32 bytes = 256 bits for sha2::Sha256 let b_len = H::OutputSize::to_usize(); @@ -91,16 +123,11 @@ impl DST { "The ratio of desired output to the output size of hash function is too large!" ); - // 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 IrtfH2F(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()); - let mut hasher = H::default(); - hasher.update(&Z_PAD[0..block_size]); - hasher.update(msg); - hasher.update(&lib_str); hasher.update(&[0u8]); dst.update(& mut hasher); let b0 = hasher.finalize_fixed_reset(); @@ -127,8 +154,6 @@ impl DST { } } -static Z_PAD: [u8; 256] = [0u8; 256]; - pub struct XofVec { bytes: Vec, pos: usize, diff --git a/ff/src/fields/field_hashers/expander/tests.rs b/ff/src/fields/field_hashers/expander/tests.rs index 75675a438..8a60ad23c 100644 --- a/ff/src/fields/field_hashers/expander/tests.rs +++ b/ff/src/fields/field_hashers/expander/tests.rs @@ -1,13 +1,13 @@ use libtest_mimic::{run, Arguments, Failed, Trial}; use sha2::{Sha256, Sha384, Sha512}; -use sha3::{Shake128, Shake256, digest::XofReader}; +use sha3::{Shake128, Shake256, digest::{Update,XofReader}}; use std::{ fs::{read_dir, File}, io::BufReader, }; -use super::{DST}; +use super::{DST,IrtfH2F}; #[derive(Debug, serde_derive::Serialize, serde_derive::Deserialize)] pub struct ExpanderVector { @@ -62,11 +62,11 @@ fn do_test(data: ExpanderVector) -> Result<(), Failed> { for v in data.vectors.iter() { let len = usize::from_str_radix(v.len_in_bytes.trim_start_matches("0x"), 16).unwrap(); let got = match data.hash.as_str() { - "SHA256" => dst.expand_xmd::(64, v.msg.as_bytes(), len).read_boxed(len), - "SHA384" => dst.expand_xmd::(128, v.msg.as_bytes(), len).read_boxed(len), - "SHA512" => dst.expand_xmd::(128, v.msg.as_bytes(), len).read_boxed(len), - "SHAKE128" => dst.expand_xof::(v.msg.as_bytes(), len).read_boxed(len), - "SHAKE256" => dst.expand_xof::(v.msg.as_bytes(), len).read_boxed(len), + "SHA256" => IrtfH2F::::new_xmd().chain(v.msg.as_bytes()).expand_xmd(&dst,len).read_boxed(len), + "SHA384" => IrtfH2F::::new_xmd().chain(v.msg.as_bytes()).expand_xmd(&dst,len).read_boxed(len), + "SHA512" => IrtfH2F::::new_xmd().chain(v.msg.as_bytes()).expand_xmd(&dst,len).read_boxed(len), + "SHAKE128" => IrtfH2F::::new_xof().chain(v.msg.as_bytes()).expand_xof(&dst,len).read_boxed(len), + "SHAKE256" => IrtfH2F::::new_xof().chain(v.msg.as_bytes()).expand_xof(&dst,len).read_boxed(len), _ => unimplemented!(), }; let want = hex::decode(&v.uniform_bytes).unwrap(); diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index d541ed65e..d98d93b81 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -3,8 +3,8 @@ mod expander; use crate::{Field, PrimeField}; use ark_std::vec::Vec; -use digest::{ExtendableOutput,FixedOutputReset,XofReader}; -pub use expander::DST; +use digest::{Update,ExtendableOutput,FixedOutputReset,XofReader}; +pub use expander::{DST, IrtfH2F}; // pub trait HashToField: Field { @@ -40,20 +40,20 @@ pub trait HashToField: Sized { /// assert_eq!(field_elements.len(), 2); /// ``` pub fn xmd_hash_to_field(dst: &[u8], msg: &[u8]) -> [F; N] -where F: Field, H: FixedOutputReset+Default, +where F: Field, H: FixedOutputReset+digest::core_api::BlockSizeUser+Default, { let dst = DST::new_xmd::(dst); let len_per_base_elem = get_len_per_elem::(); + debug_assert_eq!(H::block_size(), len_per_base_elem); let m = F::extension_degree() as usize; let total_length = N * m * len_per_base_elem; - let mut xmd = dst.expand_xmd::(len_per_base_elem, msg, total_length); + let mut xmd = IrtfH2F::::new_xmd().chain(msg).expand_xmd(&dst,total_length); let h2f = |_| hash_to_field::(&mut xmd); ark_std::array::from_fn::(h2f) } - pub fn xof_hash_to_field(dst: &[u8], msg: &[u8]) -> [F; N] where F: Field, H: ExtendableOutput+Default, { @@ -62,7 +62,7 @@ where F: Field, H: ExtendableOutput+Default, let len_per_base_elem = get_len_per_elem::(); let m = F::extension_degree() as usize; let total_length = N * m * len_per_base_elem; - let mut xof = dst.expand_xof::(msg, total_length); + let mut xof = IrtfH2F::::new_xof().chain(msg).expand_xof(&dst,total_length); let h2f = |_| hash_to_field::(&mut xof); ark_std::array::from_fn::(h2f) From 75a00a0eab4a6c9e7869a844de2d9dbdcfd45859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Wed, 19 Apr 2023 13:08:05 +0200 Subject: [PATCH 35/47] Expose more --- ff/src/fields/field_hashers/expander/mod.rs | 47 +++++++++---------- ff/src/fields/field_hashers/expander/tests.rs | 12 ++--- ff/src/fields/field_hashers/mod.rs | 6 +-- 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 73ac1cd97..b16769c8b 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -74,43 +74,38 @@ impl DST { } } +pub fn expand_xof(mut h: H, dst: &DST, n: usize) -> impl XofReader +where H: ExtendableOutput +{ + assert!(n < (1 << 16), "Length should be smaller than 2^16"); + // I2OSP(len,2) https://www.rfc-editor.org/rfc/rfc8017.txt + h.update(& (n as u16).to_be_bytes()); + + // DST::new_xof::(self.dst.as_ref(), self.k) + dst.update(&mut h); + h.finalize_xof() +} + + static Z_PAD: [u8; 256] = [0u8; 256]; -pub struct IrtfH2F(H); +pub struct Zpad(pub H); -impl Update for IrtfH2F { +impl Update for Zpad { fn update(&mut self, data: &[u8]) { self.0.update(data); } } -impl IrtfH2F { - pub fn new_xof() -> IrtfH2F - where H: ExtendableOutput - { - IrtfH2F(H::default()) - } - - pub fn expand_xof(mut self, dst: &DST, n: usize) -> impl XofReader - where H: ExtendableOutput - { - assert!(n < (1 << 16), "Length should be smaller than 2^16"); - // I2OSP(len,2) https://www.rfc-editor.org/rfc/rfc8017.txt - self.0.update(& (n as u16).to_be_bytes()); - - // DST::new_xof::(self.dst.as_ref(), self.k) - dst.update(&mut self.0); - self.0.finalize_xof() - } - - pub fn new_xmd() -> IrtfH2F - where H: FixedOutputReset+BlockSizeUser - { +impl Default for Zpad { + fn default() -> Zpad { let mut hasher = H::default(); hasher.update(&Z_PAD[0 .. H::block_size()]); - IrtfH2F(hasher) + Zpad(hasher) } +} +impl Zpad { pub fn expand_xmd(self, dst: &DST, n: usize) -> impl XofReader where H: FixedOutputReset { @@ -123,7 +118,7 @@ impl IrtfH2F { "The ratio of desired output to the output size of hash function is too large!" ); - let IrtfH2F(mut hasher) = self; + let Zpad(mut hasher) = self; assert!(n < (1 << 16), "Length should be smaller than 2^16"); // I2OSP(len,2) https://www.rfc-editor.org/rfc/rfc8017.txt hasher.update(& (n as u16).to_be_bytes()); diff --git a/ff/src/fields/field_hashers/expander/tests.rs b/ff/src/fields/field_hashers/expander/tests.rs index 8a60ad23c..e6f47c66f 100644 --- a/ff/src/fields/field_hashers/expander/tests.rs +++ b/ff/src/fields/field_hashers/expander/tests.rs @@ -7,7 +7,7 @@ use std::{ io::BufReader, }; -use super::{DST,IrtfH2F}; +use super::{DST,Zpad,expand_xof}; #[derive(Debug, serde_derive::Serialize, serde_derive::Deserialize)] pub struct ExpanderVector { @@ -62,11 +62,11 @@ fn do_test(data: ExpanderVector) -> Result<(), Failed> { for v in data.vectors.iter() { let len = usize::from_str_radix(v.len_in_bytes.trim_start_matches("0x"), 16).unwrap(); let got = match data.hash.as_str() { - "SHA256" => IrtfH2F::::new_xmd().chain(v.msg.as_bytes()).expand_xmd(&dst,len).read_boxed(len), - "SHA384" => IrtfH2F::::new_xmd().chain(v.msg.as_bytes()).expand_xmd(&dst,len).read_boxed(len), - "SHA512" => IrtfH2F::::new_xmd().chain(v.msg.as_bytes()).expand_xmd(&dst,len).read_boxed(len), - "SHAKE128" => IrtfH2F::::new_xof().chain(v.msg.as_bytes()).expand_xof(&dst,len).read_boxed(len), - "SHAKE256" => IrtfH2F::::new_xof().chain(v.msg.as_bytes()).expand_xof(&dst,len).read_boxed(len), + "SHA256" => Zpad::::default().chain(v.msg.as_bytes()).expand_xmd(&dst,len).read_boxed(len), + "SHA384" => Zpad::::default().chain(v.msg.as_bytes()).expand_xmd(&dst,len).read_boxed(len), + "SHA512" => Zpad::::default().chain(v.msg.as_bytes()).expand_xmd(&dst,len).read_boxed(len), + "SHAKE128" => expand_xof(Shake128::default().chain(v.msg.as_bytes()), &dst, len).read_boxed(len), + "SHAKE256" => expand_xof(Shake256::default().chain(v.msg.as_bytes()), &dst, len).read_boxed(len), _ => unimplemented!(), }; let want = hex::decode(&v.uniform_bytes).unwrap(); diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index d98d93b81..723af89f2 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -4,7 +4,7 @@ use crate::{Field, PrimeField}; use ark_std::vec::Vec; use digest::{Update,ExtendableOutput,FixedOutputReset,XofReader}; -pub use expander::{DST, IrtfH2F}; +pub use expander::{DST, Zpad, expand_xof}; // pub trait HashToField: Field { @@ -48,7 +48,7 @@ where F: Field, H: FixedOutputReset+digest::core_api::BlockSizeUser+Default, debug_assert_eq!(H::block_size(), len_per_base_elem); let m = F::extension_degree() as usize; let total_length = N * m * len_per_base_elem; - let mut xmd = IrtfH2F::::new_xmd().chain(msg).expand_xmd(&dst,total_length); + let mut xmd = Zpad::::default().chain(msg).expand_xmd(&dst,total_length); let h2f = |_| hash_to_field::(&mut xmd); ark_std::array::from_fn::(h2f) @@ -62,7 +62,7 @@ where F: Field, H: ExtendableOutput+Default, let len_per_base_elem = get_len_per_elem::(); let m = F::extension_degree() as usize; let total_length = N * m * len_per_base_elem; - let mut xof = IrtfH2F::::new_xof().chain(msg).expand_xof(&dst,total_length); + let mut xof = expand_xof(H::default().chain(msg), &dst, total_length); let h2f = |_| hash_to_field::(&mut xof); ark_std::array::from_fn::(h2f) From 567cf5fc513fb9dd5d9a287df6ce6d70546875d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Wed, 19 Apr 2023 13:19:50 +0200 Subject: [PATCH 36/47] Add Expander trait. I'd prefer free fns here myself, but likely everyone else prefers some trait, and it does make the interface more consistent. --- ff/src/fields/field_hashers/expander/mod.rs | 35 ++++++++++--------- ff/src/fields/field_hashers/expander/tests.rs | 12 +++---- ff/src/fields/field_hashers/mod.rs | 6 ++-- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index b16769c8b..8f4c83a4e 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -7,10 +7,6 @@ use digest::{FixedOutputReset, ExtendableOutput, XofReader, Update, core_api::Bl use arrayvec::ArrayVec; -pub trait Expander { - type R: XofReader; - fn expand(&self, msg: &[u8], length: usize) -> Self::R; -} const MAX_DST_LENGTH: usize = 255; const LONG_DST_PREFIX: &[u8; 17] = b"H2C-OVERSIZE-DST-"; @@ -74,18 +70,23 @@ impl DST { } } -pub fn expand_xof(mut h: H, dst: &DST, n: usize) -> impl XofReader -where H: ExtendableOutput -{ - assert!(n < (1 << 16), "Length should be smaller than 2^16"); - // I2OSP(len,2) https://www.rfc-editor.org/rfc/rfc8017.txt - h.update(& (n as u16).to_be_bytes()); - - // DST::new_xof::(self.dst.as_ref(), self.k) - dst.update(&mut h); - h.finalize_xof() +pub trait Expander { + type R: XofReader; + fn expand(self, dst: &DST, length: usize) -> Self::R; } +impl Expander for H { + type R = ::Reader; + fn expand(mut self, dst: &DST, 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(&mut self); + self.finalize_xof() + } +} static Z_PAD: [u8; 256] = [0u8; 256]; @@ -105,9 +106,9 @@ impl Default for Zpad { } } -impl Zpad { - pub fn expand_xmd(self, dst: &DST, n: usize) -> impl XofReader - where H: FixedOutputReset +impl Expander for Zpad { + type R = XofVec; + fn expand(self, dst: &DST, n: usize) -> XofVec { use digest::typenum::Unsigned; // output size of the hash function, e.g. 32 bytes = 256 bits for sha2::Sha256 diff --git a/ff/src/fields/field_hashers/expander/tests.rs b/ff/src/fields/field_hashers/expander/tests.rs index e6f47c66f..a02073fb1 100644 --- a/ff/src/fields/field_hashers/expander/tests.rs +++ b/ff/src/fields/field_hashers/expander/tests.rs @@ -7,7 +7,7 @@ use std::{ io::BufReader, }; -use super::{DST,Zpad,expand_xof}; +use super::{DST,Zpad,Expander}; #[derive(Debug, serde_derive::Serialize, serde_derive::Deserialize)] pub struct ExpanderVector { @@ -62,11 +62,11 @@ fn do_test(data: ExpanderVector) -> Result<(), Failed> { for v in data.vectors.iter() { let len = usize::from_str_radix(v.len_in_bytes.trim_start_matches("0x"), 16).unwrap(); let got = match data.hash.as_str() { - "SHA256" => Zpad::::default().chain(v.msg.as_bytes()).expand_xmd(&dst,len).read_boxed(len), - "SHA384" => Zpad::::default().chain(v.msg.as_bytes()).expand_xmd(&dst,len).read_boxed(len), - "SHA512" => Zpad::::default().chain(v.msg.as_bytes()).expand_xmd(&dst,len).read_boxed(len), - "SHAKE128" => expand_xof(Shake128::default().chain(v.msg.as_bytes()), &dst, len).read_boxed(len), - "SHAKE256" => expand_xof(Shake256::default().chain(v.msg.as_bytes()), &dst, len).read_boxed(len), + "SHA256" => Zpad::::default().chain(v.msg.as_bytes()).expand(&dst,len).read_boxed(len), + "SHA384" => Zpad::::default().chain(v.msg.as_bytes()).expand(&dst,len).read_boxed(len), + "SHA512" => Zpad::::default().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(); diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index 723af89f2..600bbc4aa 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -4,7 +4,7 @@ use crate::{Field, PrimeField}; use ark_std::vec::Vec; use digest::{Update,ExtendableOutput,FixedOutputReset,XofReader}; -pub use expander::{DST, Zpad, expand_xof}; +pub use expander::{DST, Zpad, Expander}; // pub trait HashToField: Field { @@ -48,7 +48,7 @@ where F: Field, H: FixedOutputReset+digest::core_api::BlockSizeUser+Default, debug_assert_eq!(H::block_size(), len_per_base_elem); let m = F::extension_degree() as usize; let total_length = N * m * len_per_base_elem; - let mut xmd = Zpad::::default().chain(msg).expand_xmd(&dst,total_length); + let mut xmd = Zpad::::default().chain(msg).expand(&dst, total_length); let h2f = |_| hash_to_field::(&mut xmd); ark_std::array::from_fn::(h2f) @@ -62,7 +62,7 @@ where F: Field, H: ExtendableOutput+Default, let len_per_base_elem = get_len_per_elem::(); let m = F::extension_degree() as usize; let total_length = N * m * len_per_base_elem; - let mut xof = expand_xof(H::default().chain(msg), &dst, total_length); + let mut xof = H::default().chain(msg).expand(&dst, total_length); let h2f = |_| hash_to_field::(&mut xof); ark_std::array::from_fn::(h2f) From 9d6148deb190f872c83e596bd995a7a9f6c2c19b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Wed, 19 Apr 2023 13:40:47 +0200 Subject: [PATCH 37/47] Oops, this would never have worked. Artifact of bad test vectors. --- ff/Cargo.toml | 2 +- ff/src/fields/field_hashers/expander/mod.rs | 14 +++++++------- ff/src/fields/field_hashers/expander/tests.rs | 6 +++--- ff/src/fields/field_hashers/mod.rs | 5 ++--- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/ff/Cargo.toml b/ff/Cargo.toml index 4efc2e2da..de8fe2986 100644 --- a/ff/Cargo.toml +++ b/ff/Cargo.toml @@ -25,7 +25,7 @@ paste = "1.0" rayon = { version = "1", optional = true } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } num-bigint = { version = "0.4", default-features = false } -digest = { version = "0.10", default-features = false, features = ["alloc", "core-api"] } +digest = { version = "0.10", default-features = false, features = ["alloc"] } itertools = { version = "0.10", default-features = false } [dev-dependencies] diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 8f4c83a4e..f00860681 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -3,7 +3,7 @@ use ark_std::vec::Vec; -use digest::{FixedOutputReset, ExtendableOutput, XofReader, Update, core_api::BlockSizeUser}; +use digest::{FixedOutputReset, ExtendableOutput, XofReader, Update}; use arrayvec::ArrayVec; @@ -90,23 +90,23 @@ impl Expander for H { static Z_PAD: [u8; 256] = [0u8; 256]; -pub struct Zpad(pub H); +pub struct Zpad(pub H); -impl Update for Zpad { +impl Update for Zpad { fn update(&mut self, data: &[u8]) { self.0.update(data); } } -impl Default for Zpad { - fn default() -> Zpad { +impl Zpad { + pub fn new(block_size: usize) -> Zpad { let mut hasher = H::default(); - hasher.update(&Z_PAD[0 .. H::block_size()]); + hasher.update(&Z_PAD[0..block_size]); Zpad(hasher) } } -impl Expander for Zpad { +impl Expander for Zpad { type R = XofVec; fn expand(self, dst: &DST, n: usize) -> XofVec { diff --git a/ff/src/fields/field_hashers/expander/tests.rs b/ff/src/fields/field_hashers/expander/tests.rs index a02073fb1..82357fc2d 100644 --- a/ff/src/fields/field_hashers/expander/tests.rs +++ b/ff/src/fields/field_hashers/expander/tests.rs @@ -62,9 +62,9 @@ fn do_test(data: ExpanderVector) -> Result<(), Failed> { for v in data.vectors.iter() { let len = usize::from_str_radix(v.len_in_bytes.trim_start_matches("0x"), 16).unwrap(); let got = match data.hash.as_str() { - "SHA256" => Zpad::::default().chain(v.msg.as_bytes()).expand(&dst,len).read_boxed(len), - "SHA384" => Zpad::::default().chain(v.msg.as_bytes()).expand(&dst,len).read_boxed(len), - "SHA512" => Zpad::::default().chain(v.msg.as_bytes()).expand(&dst,len).read_boxed(len), + "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!(), diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index 600bbc4aa..c64afbeb8 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -40,15 +40,14 @@ pub trait HashToField: Sized { /// assert_eq!(field_elements.len(), 2); /// ``` pub fn xmd_hash_to_field(dst: &[u8], msg: &[u8]) -> [F; N] -where F: Field, H: FixedOutputReset+digest::core_api::BlockSizeUser+Default, +where F: Field, H: FixedOutputReset+Default, { let dst = DST::new_xmd::(dst); let len_per_base_elem = get_len_per_elem::(); - debug_assert_eq!(H::block_size(), len_per_base_elem); let m = F::extension_degree() as usize; let total_length = N * m * len_per_base_elem; - let mut xmd = Zpad::::default().chain(msg).expand(&dst, total_length); + let mut xmd = Zpad::::new(len_per_base_elem).chain(msg).expand(&dst, total_length); let h2f = |_| hash_to_field::(&mut xmd); ark_std::array::from_fn::(h2f) From 2e4629ddb34d8d353e88ba7159b54c5db4626531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Fri, 21 Apr 2023 15:00:35 +0200 Subject: [PATCH 38/47] expand_for_field --- ff/src/fields/field_hashers/expander/mod.rs | 35 ++++++++++++++++++++- ff/src/fields/field_hashers/mod.rs | 17 +++------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index f00860681..a014d9e82 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -2,6 +2,7 @@ // With some optimisations use ark_std::vec::Vec; +use crate::{Field}; use digest::{FixedOutputReset, ExtendableOutput, XofReader, Update}; use arrayvec::ArrayVec; @@ -70,10 +71,38 @@ impl DST { } } -pub trait Expander { +pub trait Expander: Sized { type R: XofReader; fn expand(self, dst: &DST, length: usize) -> Self::R; + fn expand_for_field(self, dst: &DST) -> Self::R { + let len_per_base_elem = super::get_len_per_elem::(); + let m = F::extension_degree() as usize; + let total_length = N * m * len_per_base_elem; + self.expand(&dst, total_length) + } +} + +/* +/// An Expander for curves with no defined hash-to-curve +pub struct PanicExpander; + +impl Update for Zpad { + fn update(&mut self, data: &[u8]) { + panic!("Undefined hash to curve"); + } } +impl XofReader for PanicExpander { + fn read(&mut self, buffer: &mut [u8]) { + panic!("Undefined hash to curve"); + } +} +impl Expander for PanicExpander { + type R = PanicExpander; + fn expand(self, dst: &DST, length: usize) { + panic!("Undefined hash to curve"); + } +} +*/ impl Expander for H { type R = ::Reader; @@ -104,6 +133,10 @@ impl Zpad { hasher.update(&Z_PAD[0..block_size]); Zpad(hasher) } + pub fn new_for_field() -> Zpad { + let len_per_base_elem = super::get_len_per_elem::(); + Self::new(len_per_base_elem) + } } impl Expander for Zpad { diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index c64afbeb8..7ec7c4e7e 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -3,7 +3,8 @@ mod expander; use crate::{Field, PrimeField}; use ark_std::vec::Vec; -use digest::{Update,ExtendableOutput,FixedOutputReset,XofReader}; +pub use digest::{self, Update}; +use digest::{ExtendableOutput,FixedOutputReset,XofReader}; pub use expander::{DST, Zpad, Expander}; @@ -43,12 +44,7 @@ pub fn xmd_hash_to_field(dst: &[u8], where F: Field, H: FixedOutputReset+Default, { let dst = DST::new_xmd::(dst); - - let len_per_base_elem = get_len_per_elem::(); - let m = F::extension_degree() as usize; - let total_length = N * m * len_per_base_elem; - let mut xmd = Zpad::::new(len_per_base_elem).chain(msg).expand(&dst, total_length); - + let mut xmd = Zpad::::new_for_field::().chain(msg).expand_for_field::(&dst); let h2f = |_| hash_to_field::(&mut xmd); ark_std::array::from_fn::(h2f) } @@ -57,12 +53,7 @@ pub fn xof_hash_to_field(dst: &[u8], where F: Field, H: ExtendableOutput+Default, { let dst = DST::new_xof::(dst,Some(SEC_PARAM)); - - let len_per_base_elem = get_len_per_elem::(); - let m = F::extension_degree() as usize; - let total_length = N * m * len_per_base_elem; - let mut xof = H::default().chain(msg).expand(&dst, total_length); - + let mut xof = H::default().chain(msg).expand_for_field::(&dst); let h2f = |_| hash_to_field::(&mut xof); ark_std::array::from_fn::(h2f) } From 3f4c13781074f4308fc75b030c697199876cc92c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Fri, 21 Apr 2023 15:16:46 +0200 Subject: [PATCH 39/47] PanicExpander is unecessary --- ff/src/fields/field_hashers/expander/mod.rs | 22 --------------------- 1 file changed, 22 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index a014d9e82..44fe11c08 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -82,28 +82,6 @@ pub trait Expander: Sized { } } -/* -/// An Expander for curves with no defined hash-to-curve -pub struct PanicExpander; - -impl Update for Zpad { - fn update(&mut self, data: &[u8]) { - panic!("Undefined hash to curve"); - } -} -impl XofReader for PanicExpander { - fn read(&mut self, buffer: &mut [u8]) { - panic!("Undefined hash to curve"); - } -} -impl Expander for PanicExpander { - type R = PanicExpander; - fn expand(self, dst: &DST, length: usize) { - panic!("Undefined hash to curve"); - } -} -*/ - impl Expander for H { type R = ::Reader; fn expand(mut self, dst: &DST, n: usize) -> Self::R From 22858b2ed4592538dc04c92bdb4b76c73e26c0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Fri, 21 Apr 2023 15:17:45 +0200 Subject: [PATCH 40/47] Attempt at new hash-to-curve traits --- ec/src/hashing/curve_maps/mod.rs | 2 - ec/src/hashing/curve_maps/swu.rs | 20 ++++- ec/src/hashing/curve_maps/wb.rs | 18 ++++- ec/src/hashing/map_to_curve_hasher.rs | 67 ----------------- ec/src/hashing/mod.rs | 101 +++++++++++++++++++++++--- 5 files changed, 121 insertions(+), 87 deletions(-) diff --git a/ec/src/hashing/curve_maps/mod.rs b/ec/src/hashing/curve_maps/mod.rs index 7b0c3911d..e69de29bb 100644 --- a/ec/src/hashing/curve_maps/mod.rs +++ b/ec/src/hashing/curve_maps/mod.rs @@ -1,2 +0,0 @@ -pub mod swu; -pub mod wb; diff --git a/ec/src/hashing/curve_maps/swu.rs b/ec/src/hashing/curve_maps/swu.rs index c989ece48..54dab529b 100644 --- a/ec/src/hashing/curve_maps/swu.rs +++ b/ec/src/hashing/curve_maps/swu.rs @@ -1,10 +1,16 @@ 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; +use digest::{Update,XofReader}; + +pub use super::MapToCurve; + use crate::{ - hashing::{map_to_curve_hasher::MapToCurve, HashToCurveError}, + hashing::{HashToCurveError}, models::short_weierstrass::{Affine, Projective}, }; @@ -19,6 +25,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,6 +45,10 @@ pub fn parity(element: &F) -> bool { } impl MapToCurve> for SWUMap

{ + /// 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 @@ -152,7 +166,7 @@ impl MapToCurve> for SWUMap

{ #[cfg(test)] mod test { use crate::{ - hashing::{map_to_curve_hasher::MapToCurveBasedHasher, HashToCurve}, + hashing::{HashToCurve}, CurveConfig, }; use ark_ff::field_hashers::DefaultFieldHasher; diff --git a/ec/src/hashing/curve_maps/wb.rs b/ec/src/hashing/curve_maps/wb.rs index 46b635b1c..f58e20564 100644 --- a/ec/src/hashing/curve_maps/wb.rs +++ b/ec/src/hashing/curve_maps/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,6 +78,10 @@ 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 { @@ -84,6 +90,10 @@ pub struct WBMap { } impl MapToCurve> for WBMap

{ + /// 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) { @@ -118,8 +128,8 @@ mod test { curve_maps::{ swu::SWUConfig, wb::{IsogenyMap, WBConfig, WBMap}, + MapToCurveBasedHasher, }, - map_to_curve_hasher::MapToCurveBasedHasher, HashToCurve, }, models::short_weierstrass::SWCurveConfig, diff --git a/ec/src/hashing/map_to_curve_hasher.rs b/ec/src/hashing/map_to_curve_hasher.rs index ed9713c10..8b1378917 100644 --- a/ec/src/hashing/map_to_curve_hasher.rs +++ b/ec/src/hashing/map_to_curve_hasher.rs @@ -1,68 +1 @@ -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 { - /// 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: 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: PhantomData, - _params_t: PhantomData, -} - -impl HashToCurve for MapToCurveBasedHasher -where - T: CurveGroup, - H2F: HashToField, - M2C: MapToCurve, -{ - fn new(domain: &[u8]) -> Result { - #[cfg(test)] - M2C::check_parameters()?; - Ok(MapToCurveBasedHasher { - field_hasher: H2F::new(domain), - _curve_mapper: PhantomData, - _params_t: PhantomData, - }) - } - - /// 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::<2>(msg); - - let rand_curve_elem_0 = M2C::map_to_curve(rand_field_elems[0])?; - let rand_curve_elem_1 = M2C::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..82ea58e17 100644 --- a/ec/src/hashing/mod.rs +++ b/ec/src/hashing/mod.rs @@ -1,21 +1,99 @@ -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, digest, expander}; +use digest::{Update,XofReader}; +use expander::{DST,Expander}; + +use crate::CurveGroup; + + +/// 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; + + /// 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 f = || field_hashers::hash_to_field::<>::SEC_PARAM,C::BaseField,H>(xof); + let p0 = Self::map_to_curve(f())?; + let p1 = Self::map_to_curve(f())?; + + // We've no projective clear_cofactor metho so normalize twice. + Ok( (p0 + p1).into_affine().clear_cofactor_to_group() ) +} -/// 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; +/// 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: &DST) -> Result +where C: CurveGroup, M: MapToCurve +{ + #[cfg(debug-assertions)] + M2C::check_parameters()?; + let mut xof = exp.expand_for_field::<>::SEC_PARAM,F,2>(dst); + xof_map_to_curve::(&mut xof) +} - /// 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; +/// 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: digest::FixedOutputReset+Default, +{ + expander::Zpad::::new_for_field::<>::SEC_PARAM,C::BaseField>() } +/// [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: &DST) -> Result { + expand_to_curve::(exp,dst) + } + + fn hash_to_curve(dst: &DST, msg: &[u8]) -> Result { + let exp = Self::expander().update(msg); + Self::finalize_to_curve(exp, dst) + } +} + + + + /// This is an error that could occur during the hash to curve process #[derive(Clone, Debug)] pub enum HashToCurveError { @@ -37,5 +115,6 @@ impl fmt::Display for HashToCurveError { } } + #[cfg(test)] mod tests; From db6feb0311a1d2d85c48080f34423f13e63d7a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Sat, 22 Apr 2023 00:13:42 +0200 Subject: [PATCH 41/47] cargo check psses, ugly hack around const-generic being broken --- ec/src/hashing/curve_maps/mod.rs | 0 ec/src/hashing/map_to_curve_hasher.rs | 1 - ec/src/hashing/mod.rs | 49 +++++++++++++-------- ec/src/hashing/{curve_maps => }/swu.rs | 2 - ec/src/hashing/{curve_maps => }/wb.rs | 0 ff/src/fields/field_hashers/expander/mod.rs | 8 ++-- ff/src/fields/field_hashers/mod.rs | 14 +++--- 7 files changed, 42 insertions(+), 32 deletions(-) delete mode 100644 ec/src/hashing/curve_maps/mod.rs delete mode 100644 ec/src/hashing/map_to_curve_hasher.rs rename ec/src/hashing/{curve_maps => }/swu.rs (99%) rename ec/src/hashing/{curve_maps => }/wb.rs (100%) diff --git a/ec/src/hashing/curve_maps/mod.rs b/ec/src/hashing/curve_maps/mod.rs deleted file mode 100644 index e69de29bb..000000000 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 8b1378917..000000000 --- a/ec/src/hashing/map_to_curve_hasher.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ec/src/hashing/mod.rs b/ec/src/hashing/mod.rs index 82ea58e17..2fdb593b6 100644 --- a/ec/src/hashing/mod.rs +++ b/ec/src/hashing/mod.rs @@ -10,11 +10,12 @@ pub mod wb; use ark_std::string::String; use core::fmt; -pub use ark_ff::field_hashers::{self, digest, expander}; -use digest::{Update,XofReader}; -use expander::{DST,Expander}; +pub use ark_ff::field_hashers::{ + self, DST,Expander,Zpad, + digest::{self,FixedOutputReset,Update,XofReader} +}; -use crate::CurveGroup; +use crate::{CurveGroup,AffineRepr}; /// Trait for mapping a random field element to a random curve point. @@ -36,24 +37,32 @@ pub trait MapToCurve: Sized { pub fn xof_map_to_curve(xof: &mut H) -> Result where C: CurveGroup, M: MapToCurve, H: XofReader, { - let f = || field_hashers::hash_to_field::<>::SEC_PARAM,C::BaseField,H>(xof); - let p0 = Self::map_to_curve(f())?; - let p1 = Self::map_to_curve(f())?; + let mut f = || match >::SEC_PARAM { + 128 => field_hashers::hash_to_field::<128u16,C::BaseField,H>(xof), + 256 => field_hashers::hash_to_field::<256u16,C::BaseField,H>(xof), + _ => panic!("const-generics"), + }; + 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_to_group() ) + Ok( (p0 + p1).into_affine().clear_cofactor().into_group() ) } /// 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: &DST) -> Result +pub fn expand_to_curve(exp: impl Expander, dst: &DST) -> Result where C: CurveGroup, M: MapToCurve { - #[cfg(debug-assertions)] - M2C::check_parameters()?; - let mut xof = exp.expand_for_field::<>::SEC_PARAM,F,2>(dst); - xof_map_to_curve::(&mut xof) + #[cfg(debug_assertions)] + >::check_parameters()?; + let mut xof = match >::SEC_PARAM { + 128 => exp.expand_for_field::<128u16,C::BaseField,2>(dst), + 256 => exp.expand_for_field::<256u16,C::BaseField,2>(dst), + _ => panic!("const-generics"), + }; + xof_map_to_curve::(&mut xof) } /// Hash-to-curves need an extendable output hash function (XoF). @@ -64,9 +73,13 @@ where C: CurveGroup, M: MapToCurve /// 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: digest::FixedOutputReset+Default, +where C: CurveGroup, M: MapToCurve, H: FixedOutputReset+Default, { - expander::Zpad::::new_for_field::<>::SEC_PARAM,C::BaseField>() + match >::SEC_PARAM { + 128 => Zpad::::new_for_field::<128u16,C::BaseField>(), + 256 => Zpad::::new_for_field::<256u16,C::BaseField>(), + _ => panic!("const-generics"), + } } /// [IRTF CFRG hash-to-curve draft](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16) @@ -86,14 +99,12 @@ pub trait HashToCurve: CurveGroup { } fn hash_to_curve(dst: &DST, msg: &[u8]) -> Result { - let exp = Self::expander().update(msg); + 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 #[derive(Clone, Debug)] pub enum HashToCurveError { diff --git a/ec/src/hashing/curve_maps/swu.rs b/ec/src/hashing/swu.rs similarity index 99% rename from ec/src/hashing/curve_maps/swu.rs rename to ec/src/hashing/swu.rs index 54dab529b..37644a2f6 100644 --- a/ec/src/hashing/curve_maps/swu.rs +++ b/ec/src/hashing/swu.rs @@ -5,8 +5,6 @@ use ark_ff::{ use ark_std::string::ToString; use core::marker::PhantomData; -use digest::{Update,XofReader}; - pub use super::MapToCurve; use crate::{ diff --git a/ec/src/hashing/curve_maps/wb.rs b/ec/src/hashing/wb.rs similarity index 100% rename from ec/src/hashing/curve_maps/wb.rs rename to ec/src/hashing/wb.rs diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 44fe11c08..a1dd5a433 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -50,9 +50,9 @@ impl DST { // } // } - pub fn new_xof(dst: &[u8], sec_param: Option) -> DST { + pub fn new_xof(dst: &[u8], sec_param: Option) -> DST { 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."); + 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); @@ -74,7 +74,7 @@ impl DST { pub trait Expander: Sized { type R: XofReader; fn expand(self, dst: &DST, length: usize) -> Self::R; - fn expand_for_field(self, dst: &DST) -> Self::R { + fn expand_for_field(self, dst: &DST) -> Self::R { let len_per_base_elem = super::get_len_per_elem::(); let m = F::extension_degree() as usize; let total_length = N * m * len_per_base_elem; @@ -111,7 +111,7 @@ impl Zpad { hasher.update(&Z_PAD[0..block_size]); Zpad(hasher) } - pub fn new_for_field() -> Zpad { + pub fn new_for_field() -> Zpad { let len_per_base_elem = super::get_len_per_elem::(); Self::new(len_per_base_elem) } diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index 7ec7c4e7e..58405d658 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -3,7 +3,7 @@ mod expander; use crate::{Field, PrimeField}; use ark_std::vec::Vec; -pub use digest::{self, Update}; +pub use digest::{self,Update}; use digest::{ExtendableOutput,FixedOutputReset,XofReader}; pub use expander::{DST, Zpad, Expander}; @@ -12,6 +12,7 @@ pub use expander::{DST, Zpad, Expander}; // fn hash_to_field() -> Self; // } +/* /// Trait for hashing messages to field elements. pub trait HashToField: Sized { /// Initialises a new hash-to-field helper struct. @@ -24,6 +25,7 @@ pub trait HashToField: Sized { /// Hash an arbitrary `msg` to #`count` elements from field `F`. 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. @@ -40,7 +42,7 @@ pub trait HashToField: Sized { /// /// assert_eq!(field_elements.len(), 2); /// ``` -pub fn xmd_hash_to_field(dst: &[u8], msg: &[u8]) -> [F; N] +pub fn xmd_hash_to_field(dst: &[u8], msg: &[u8]) -> [F; N] where F: Field, H: FixedOutputReset+Default, { let dst = DST::new_xmd::(dst); @@ -49,7 +51,7 @@ where F: Field, H: FixedOutputReset+Default, ark_std::array::from_fn::(h2f) } -pub fn xof_hash_to_field(dst: &[u8], msg: &[u8]) -> [F; N] +pub fn xof_hash_to_field(dst: &[u8], msg: &[u8]) -> [F; N] where F: Field, H: ExtendableOutput+Default, { let dst = DST::new_xof::(dst,Some(SEC_PARAM)); @@ -58,7 +60,7 @@ where F: Field, H: ExtendableOutput+Default, ark_std::array::from_fn::(h2f) } -pub fn hash_to_field(h: &mut H) -> F { +pub fn hash_to_field(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::(); @@ -86,11 +88,11 @@ pub fn hash_to_field(h: &mut H) -> /// 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/) -const fn get_len_per_elem() -> usize { +const fn get_len_per_elem() -> 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; From b8dda7cfe14e965d15713c304221a7320dd21d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Sun, 23 Apr 2023 04:28:47 +0200 Subject: [PATCH 42/47] Remove curve_maps:: everywhere to shrink later diffs --- ec/src/hashing/tests.rs | 2 +- ec/src/hashing/wb.rs | 9 ++++----- test-curves/src/bls12_381/g1.rs | 2 +- test-curves/src/bls12_381/g1_swu_iso.rs | 2 +- test-curves/src/bls12_381/g2.rs | 2 +- test-curves/src/bls12_381/g2_swu_iso.rs | 2 +- test-templates/src/h2c/mod.rs | 2 +- 7 files changed, 10 insertions(+), 11 deletions(-) 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/wb.rs b/ec/src/hashing/wb.rs index f58e20564..77b11afc1 100644 --- a/ec/src/hashing/wb.rs +++ b/ec/src/hashing/wb.rs @@ -125,11 +125,9 @@ impl MapToCurve> for WBMap

{ mod test { use crate::{ hashing::{ - curve_maps::{ - swu::SWUConfig, - wb::{IsogenyMap, WBConfig, WBMap}, - MapToCurveBasedHasher, - }, + swu::SWUConfig, + wb::{IsogenyMap, WBConfig, WBMap}, + MapToCurveBasedHasher, HashToCurve, }, models::short_weierstrass::SWCurveConfig, @@ -317,6 +315,7 @@ mod test { ISOGENY_MAP_TESTWBF127; } + /// The point of the test is to get a simple WB compatible curve /// and make simple hash #[test] 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 378aed971..439f8a78a 100644 --- a/test-templates/src/h2c/mod.rs +++ b/test-templates/src/h2c/mod.rs @@ -15,7 +15,7 @@ macro_rules! test_h2c { extern crate std; use ark_ec::{ hashing::{ - curve_maps::wb::WBMap, map_to_curve_hasher::MapToCurveBasedHasher, HashToCurve, + wb::WBMap, map_to_curve_hasher::MapToCurveBasedHasher, HashToCurve, }, short_weierstrass::{Affine, Projective}, }; From ba26f3c6412b824add878dc6c0fa51239da60f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Mon, 24 Apr 2023 00:34:05 +0200 Subject: [PATCH 43/47] Allow slice DSTs --- ec/src/hashing/mod.rs | 8 +-- ff/src/fields/field_hashers/expander/mod.rs | 61 ++++++++++--------- ff/src/fields/field_hashers/expander/tests.rs | 4 +- ff/src/fields/field_hashers/mod.rs | 2 +- 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/ec/src/hashing/mod.rs b/ec/src/hashing/mod.rs index 2fdb593b6..63e9b2814 100644 --- a/ec/src/hashing/mod.rs +++ b/ec/src/hashing/mod.rs @@ -11,7 +11,7 @@ use ark_std::string::String; use core::fmt; pub use ark_ff::field_hashers::{ - self, DST,Expander,Zpad, + self,AsDST,Expander,Zpad, digest::{self,FixedOutputReset,Update,XofReader} }; @@ -52,7 +52,7 @@ where C: CurveGroup, M: MapToCurve, H: XofReader, /// 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: &DST) -> Result +pub fn expand_to_curve(exp: impl Expander, dst: impl AsDST) -> Result where C: CurveGroup, M: MapToCurve { #[cfg(debug_assertions)] @@ -94,11 +94,11 @@ pub trait HashToCurve: CurveGroup { /// Initalize the standard hasher for this hash-to-curve. fn expander() -> Self::Expand; - fn finalize_to_curve(exp: impl Expander, dst: &DST) -> Result { + fn finalize_to_curve(exp: impl Expander, dst: impl AsDST) -> Result { expand_to_curve::(exp,dst) } - fn hash_to_curve(dst: &DST, msg: &[u8]) -> Result { + fn hash_to_curve(dst: impl AsDST, msg: &[u8]) -> Result { let mut exp = Self::expander(); exp.update(msg); Self::finalize_to_curve(exp, dst) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index a1dd5a433..3bbafad08 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -12,26 +12,34 @@ const MAX_DST_LENGTH: usize = 255; 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 &'static [u8] { + fn as_dst(&self) -> &[u8] { + assert!(self.len() < MAX_DST_LENGTH); + self + } +} + /// 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); -impl TryFrom<&'static [u8]> for DST { - type Error = &'static str; - fn try_from(dst: &'static [u8]) -> Result { - Ok(DST(ArrayVec::try_from(dst).map_err(|_| "DST longer than 255 bytes!") ?)) +impl AsDST for &DST { + fn as_dst(&self) -> &[u8] { + self.0.as_ref() } } -/* -impl From<&'static [u8]> for DST { - fn from(dst: &'static [u8]) -> Self { - ArrayVec::try_from(dst).expect("DST longer than 255 bytes!"); - } -} -*/ - impl DST { pub fn new_xmd(dst: &[u8]) -> DST { DST(ArrayVec::try_from(dst).unwrap_or_else( |_| { @@ -51,6 +59,9 @@ impl DST { // } 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(); @@ -63,34 +74,28 @@ impl DST { ArrayVec::try_from( &*new_dst ).unwrap() } )) } - - pub fn update(&self, h: &mut H) { - h.update(self.0.as_ref()); - // I2OSP(len,1) https://www.rfc-editor.org/rfc/rfc8017.txt - h.update(&[self.0.len() as u8]); - } } - + pub trait Expander: Sized { type R: XofReader; - fn expand(self, dst: &DST, length: usize) -> Self::R; - fn expand_for_field(self, dst: &DST) -> Self::R { + fn expand(self, dst: impl AsDST, length: usize) -> Self::R; + fn expand_for_field(self, dst: impl AsDST) -> Self::R { let len_per_base_elem = super::get_len_per_elem::(); let m = F::extension_degree() as usize; let total_length = N * m * len_per_base_elem; - self.expand(&dst, total_length) + self.expand(dst, total_length) } } impl Expander for H { type R = ::Reader; - fn expand(mut self, dst: &DST, n: usize) -> Self::R + 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(&mut self); + dst.update_digest(&mut self); self.finalize_xof() } } @@ -119,7 +124,7 @@ impl Zpad { impl Expander for Zpad { type R = XofVec; - fn expand(self, dst: &DST, n: usize) -> 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 @@ -136,12 +141,12 @@ impl Expander for Zpad { hasher.update(& (n as u16).to_be_bytes()); hasher.update(&[0u8]); - dst.update(& mut hasher); + dst.update_digest(& mut hasher); let b0 = hasher.finalize_fixed_reset(); hasher.update(&b0); hasher.update(&[1u8]); - dst.update(& mut hasher); + dst.update_digest(& mut hasher); let mut bi = hasher.finalize_fixed_reset(); let mut bytes: Vec = Vec::with_capacity(n); @@ -152,7 +157,7 @@ impl Expander for Zpad { hasher.update(&[*l ^ *r]); } hasher.update(&[i as u8]); - dst.update(& mut hasher); + dst.update_digest(& mut hasher); bi = hasher.finalize_fixed_reset(); bytes.extend_from_slice(&bi); } diff --git a/ff/src/fields/field_hashers/expander/tests.rs b/ff/src/fields/field_hashers/expander/tests.rs index 82357fc2d..7aba547d6 100644 --- a/ff/src/fields/field_hashers/expander/tests.rs +++ b/ff/src/fields/field_hashers/expander/tests.rs @@ -54,8 +54,8 @@ fn do_test(data: ExpanderVector) -> Result<(), Failed> { "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)), - "SHAKE256" => DST::new_xof::(data.dst.as_bytes(), Some(data.k)), + "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!(), }; diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index 58405d658..f210d48fb 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -5,7 +5,7 @@ use crate::{Field, PrimeField}; use ark_std::vec::Vec; pub use digest::{self,Update}; use digest::{ExtendableOutput,FixedOutputReset,XofReader}; -pub use expander::{DST, Zpad, Expander}; +pub use expander::{AsDST, DST, Zpad, Expander}; // pub trait HashToField: Field { From db8fea5a45d931c1e09dd940a72273a49a4874db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Tue, 25 Apr 2023 02:48:19 +0200 Subject: [PATCH 44/47] Move sec_param from type or value level, given const generics does not work quite right yet --- ec/src/hashing/mod.rs | 18 +++------------- ff/src/fields/field_hashers/expander/mod.rs | 8 +++---- ff/src/fields/field_hashers/mod.rs | 24 ++++++++++----------- 3 files changed, 19 insertions(+), 31 deletions(-) diff --git a/ec/src/hashing/mod.rs b/ec/src/hashing/mod.rs index 63e9b2814..2dfd78dff 100644 --- a/ec/src/hashing/mod.rs +++ b/ec/src/hashing/mod.rs @@ -37,11 +37,7 @@ pub trait MapToCurve: Sized { pub fn xof_map_to_curve(xof: &mut H) -> Result where C: CurveGroup, M: MapToCurve, H: XofReader, { - let mut f = || match >::SEC_PARAM { - 128 => field_hashers::hash_to_field::<128u16,C::BaseField,H>(xof), - 256 => field_hashers::hash_to_field::<256u16,C::BaseField,H>(xof), - _ => panic!("const-generics"), - }; + let mut f = || field_hashers::hash_to_field::(>::SEC_PARAM, xof); let p0 = >::map_to_curve(f())?; let p1 = >::map_to_curve(f())?; @@ -57,11 +53,7 @@ where C: CurveGroup, M: MapToCurve { #[cfg(debug_assertions)] >::check_parameters()?; - let mut xof = match >::SEC_PARAM { - 128 => exp.expand_for_field::<128u16,C::BaseField,2>(dst), - 256 => exp.expand_for_field::<256u16,C::BaseField,2>(dst), - _ => panic!("const-generics"), - }; + let mut xof = exp.expand_for_field::(>::SEC_PARAM, dst); xof_map_to_curve::(&mut xof) } @@ -75,11 +67,7 @@ where C: CurveGroup, M: MapToCurve pub fn zpad_expander() -> Zpad where C: CurveGroup, M: MapToCurve, H: FixedOutputReset+Default, { - match >::SEC_PARAM { - 128 => Zpad::::new_for_field::<128u16,C::BaseField>(), - 256 => Zpad::::new_for_field::<256u16,C::BaseField>(), - _ => panic!("const-generics"), - } + 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) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 3bbafad08..ec8167f9f 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -79,8 +79,8 @@ impl DST { pub trait Expander: Sized { type R: XofReader; fn expand(self, dst: impl AsDST, length: usize) -> Self::R; - fn expand_for_field(self, dst: impl AsDST) -> Self::R { - let len_per_base_elem = super::get_len_per_elem::(); + 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) @@ -116,8 +116,8 @@ impl Zpad { hasher.update(&Z_PAD[0..block_size]); Zpad(hasher) } - pub fn new_for_field() -> Zpad { - let len_per_base_elem = super::get_len_per_elem::(); + 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) } } diff --git a/ff/src/fields/field_hashers/mod.rs b/ff/src/fields/field_hashers/mod.rs index f210d48fb..1de66a46e 100644 --- a/ff/src/fields/field_hashers/mod.rs +++ b/ff/src/fields/field_hashers/mod.rs @@ -38,32 +38,32 @@ pub trait HashToField: Sized { /// use ark_test_curves::bls12_381::Fq; /// use sha2::Sha256; /// -/// let field_elements: [Fq; 2] = xmd_hash_to_field::(b"Application",b"Hello, World!"); +/// let field_elements: [Fq; 2] = xmd_hash_to_field::(128,b"Application",b"Hello, World!"); /// /// assert_eq!(field_elements.len(), 2); /// ``` -pub fn xmd_hash_to_field(dst: &[u8], msg: &[u8]) -> [F; N] +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::().chain(msg).expand_for_field::(&dst); - let h2f = |_| hash_to_field::(&mut xmd); + 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) } -pub fn xof_hash_to_field(dst: &[u8], msg: &[u8]) -> [F; N] +pub fn xof_hash_to_field(sec_param: u16, dst: &[u8], msg: &[u8]) -> [F; N] where F: Field, H: ExtendableOutput+Default, { - let dst = DST::new_xof::(dst,Some(SEC_PARAM)); - let mut xof = H::default().chain(msg).expand_for_field::(&dst); - let h2f = |_| hash_to_field::(&mut xof); + 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) } -pub fn hash_to_field(h: &mut H) -> F { +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::(); + 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(); @@ -88,11 +88,11 @@ pub fn hash_to_field(h: &mut H) -> F /// 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/) -const 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 as usize); + 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; From 7705b7232424081a850dc988f7ad8a53d26fcfa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Tue, 25 Apr 2023 04:13:31 +0200 Subject: [PATCH 45/47] Fix ark_ec tests --- ec/src/hashing/swu.rs | 19 +++++++------------ ec/src/hashing/wb.rs | 21 +++++++++------------ ff/src/fields/field_hashers/expander/mod.rs | 6 ++++++ 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/ec/src/hashing/swu.rs b/ec/src/hashing/swu.rs index 37644a2f6..6beed75de 100644 --- a/ec/src/hashing/swu.rs +++ b/ec/src/hashing/swu.rs @@ -164,10 +164,10 @@ impl MapToCurve> for SWUMap

{ #[cfg(test)] mod test { use crate::{ - hashing::{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::*; @@ -248,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" ); } diff --git a/ec/src/hashing/wb.rs b/ec/src/hashing/wb.rs index 77b11afc1..7dd820519 100644 --- a/ec/src/hashing/wb.rs +++ b/ec/src/hashing/wb.rs @@ -125,16 +125,15 @@ impl MapToCurve> for WBMap

{ mod test { use crate::{ hashing::{ + zpad_expander, Update, expand_to_curve, swu::SWUConfig, wb::{IsogenyMap, WBConfig, WBMap}, - MapToCurveBasedHasher, - HashToCurve, }, 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"] @@ -321,14 +320,12 @@ mod test { #[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/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index ec8167f9f..36bec02fa 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -28,6 +28,12 @@ impl AsDST for &'static [u8] { self } } +impl AsDST for &'static [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 From 95cbf11b0d05110ddafa5573a270123ad22ed128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Tue, 25 Apr 2023 04:41:47 +0200 Subject: [PATCH 46/47] Ok don't make DSTs be 'static --- ff/src/fields/field_hashers/expander/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ff/src/fields/field_hashers/expander/mod.rs b/ff/src/fields/field_hashers/expander/mod.rs index 36bec02fa..8e03b6298 100644 --- a/ff/src/fields/field_hashers/expander/mod.rs +++ b/ff/src/fields/field_hashers/expander/mod.rs @@ -22,13 +22,13 @@ pub trait AsDST { } } -impl AsDST for &'static [u8] { +impl AsDST for &[u8] { fn as_dst(&self) -> &[u8] { assert!(self.len() < MAX_DST_LENGTH); self } } -impl AsDST for &'static [u8; N] { +impl AsDST for &[u8; N] { fn as_dst(&self) -> &[u8] { assert!(self.len() < MAX_DST_LENGTH); self.as_ref() From 3d32841bb11ebb9b95e9dad474f9c8b3ad4835f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jeff?= <¨burdges@gnunet.org¨> Date: Tue, 25 Apr 2023 04:42:24 +0200 Subject: [PATCH 47/47] Tentative fix to test-curves --- test-templates/src/h2c/mod.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/test-templates/src/h2c/mod.rs b/test-templates/src/h2c/mod.rs index 439f8a78a..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::{ - 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,19 +42,12 @@ 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: [$base_prime_field; { 2* $m } ] = - hasher.hash_to_field(&v.msg.as_bytes()); + 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); @@ -61,7 +55,7 @@ macro_rules! test_h2c { // 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(),