diff --git a/src/arithmetic/curves.rs b/src/arithmetic/curves.rs index 7243f34..4a633d9 100644 --- a/src/arithmetic/curves.rs +++ b/src/arithmetic/curves.rs @@ -95,7 +95,7 @@ pub trait Curve: /// (g * x + &(h * r)).to_affine() /// } /// ``` - fn hash_to_curve(domain_prefix: &str) -> Box Self + 'static>; + fn hash_to_curve<'a>(domain_prefix: &'a str) -> Box Self + 'a>; /// Returns whether or not this element is on the curve; should /// always be true unless an "unchecked" API was used. diff --git a/src/pasta/curves.rs b/src/pasta/curves.rs index b90e39d..f22a65b 100644 --- a/src/pasta/curves.rs +++ b/src/pasta/curves.rs @@ -677,20 +677,12 @@ macro_rules! new_curve_impl { macro_rules! impl_projective_curve_specific { ($name:ident, $name_affine:ident, $iso_affine:ident, $base:ident, special_a0_b5) => { - fn hash_to_curve(domain_prefix: &str) -> Box Self + 'static> { + fn hash_to_curve<'a>(domain_prefix: &'a str) -> Box Self + 'a> { use super::hashtocurve; - let domain_separation_tag: String = format!( - "{}-{}_{}_{}_RO_", - domain_prefix, - $name_affine::CURVE_ID, - "XMD:BLAKE2b", - "SSWU" - ); - Box::new(move |message| { let mut us = [Field::zero(); 2]; - hashtocurve::hash_to_field(message, domain_separation_tag.as_bytes(), &mut us); + hashtocurve::hash_to_field($name_affine::CURVE_ID, domain_prefix, message, &mut us); let q0 = hashtocurve::map_to_curve_simple_swu::<$base, $name_affine, $iso_affine>( &us[0], $name::THETA, @@ -702,7 +694,7 @@ macro_rules! impl_projective_curve_specific { $name::Z, ); let r = q0 + &q1; - assert!(bool::from(r.is_on_curve())); + debug_assert!(bool::from(r.is_on_curve())); hashtocurve::iso_map::<$base, $name_affine, $iso_affine>( &r, &$name::ISOGENY_CONSTANTS, @@ -766,7 +758,7 @@ macro_rules! impl_projective_curve_specific { }; ($name:ident, $name_affine:ident, $iso_affine:ident, $base:ident, general) => { /// Unimplemented: hashing to this curve is not supported - fn hash_to_curve(_domain_prefix: &str) -> Box Self + 'static> { + fn hash_to_curve<'a>(_domain_prefix: &'a str) -> Box Self + 'a> { unimplemented!() } diff --git a/src/pasta/hashtocurve.rs b/src/pasta/hashtocurve.rs index dc1c165..2388c1e 100644 --- a/src/pasta/hashtocurve.rs +++ b/src/pasta/hashtocurve.rs @@ -1,66 +1,71 @@ //! This module implements "simplified SWU" hashing to short Weierstrass curves //! with a = 0. -use std::convert::TryInto; use subtle::ConstantTimeEq; use crate::arithmetic::{Curve, CurveAffine, FieldExt}; /// Hashes over a message and writes the output to all of `buf`. -pub fn hash_to_field(message: &[u8], domain_separation_tag: &[u8], buf: &mut [F; 2]) { - use blake2b_simd::{Params as Blake2bParams, State as Blake2bState}; - assert!(domain_separation_tag.len() < 256); +pub fn hash_to_field( + curve_id: &str, + domain_prefix: &str, + message: &[u8], + buf: &mut [F; 2], +) { + assert!(domain_prefix.len() < 256); + assert!((22 + curve_id.len() + domain_prefix.len()) < 256); // Assume that the field size is 32 bytes and k is 256, where k is defined in // . const CHUNKLEN: usize = 64; - let mut dst_prime: Vec = domain_separation_tag.into(); - dst_prime.extend_from_slice(&[domain_separation_tag.len() as u8]); - let personal = [0u8; 16]; - let empty_hasher = Blake2bParams::new() + let empty_hasher = blake2b_simd::Params::new() .hash_length(CHUNKLEN) .personal(&personal) .to_state(); - let xor = |left: &[u8; CHUNKLEN], right: &[u8; CHUNKLEN]| -> Vec { - left.iter() - .zip(right.iter()) - .map(|(&l, &r)| l ^ r) - .collect() - }; + let b_0 = empty_hasher + .clone() + .update(&[0; CHUNKLEN]) + .update(message) + .update(&[0, 128, 0]) + .update(domain_prefix.as_bytes()) + .update(b"-") + .update(curve_id.as_bytes()) + .update(b"_XMD:BLAKE2b_SSWU_RO_") + .update(&[(22 + curve_id.len() + domain_prefix.len()) as u8]) + .finalize(); - let finalize = |hasher: &Blake2bState| -> [u8; CHUNKLEN] { - hasher.finalize().as_bytes().try_into().unwrap() - }; + let b_1 = empty_hasher + .clone() + .update(b_0.as_array()) + .update(&[1]) + .update(domain_prefix.as_bytes()) + .update(b"-") + .update(curve_id.as_bytes()) + .update(b"_XMD:BLAKE2b_SSWU_RO_") + .update(&[(22 + curve_id.len() + domain_prefix.len()) as u8]) + .finalize(); - let b_0 = finalize( + let b_2 = { + let mut empty_hasher = empty_hasher; + for (l, r) in b_0.as_array().iter().zip(b_1.as_array().iter()) { + empty_hasher.update(&[*l ^ *r]); + } empty_hasher - .clone() - .update(&[0; CHUNKLEN]) - .update(message) - .update(&[0, 128, 0]) - .update(&dst_prime), - ); - let b_1 = finalize( - empty_hasher - .clone() - .update(&b_0) - .update(&[1]) - .update(&dst_prime), - ); - let mut empty_hasher = empty_hasher; - let b_2 = finalize( - empty_hasher - .update(&xor(&b_0, &b_1)) .update(&[2]) - .update(&dst_prime), - ); + .update(domain_prefix.as_bytes()) + .update(b"-") + .update(curve_id.as_bytes()) + .update(b"_XMD:BLAKE2b_SSWU_RO_") + .update(&[(22 + curve_id.len() + domain_prefix.len()) as u8]) + .finalize() + }; for (big, buf) in [b_1, b_2].iter().zip(buf.iter_mut()) { let mut little = [0u8; CHUNKLEN]; - little.copy_from_slice(big); + little.copy_from_slice(big.as_array()); little.reverse(); *buf = F::from_bytes_wide(&little); }