From e23eef1a805446bb975d7353d00f6eda1a3cadac Mon Sep 17 00:00:00 2001 From: moana Date: Fri, 8 Dec 2023 15:08:23 +0100 Subject: [PATCH] Add `hash_to_point` to `ExtendedPoint` Resolves #129 --- CHANGELOG.md | 1 + src/dusk.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73573db..c802242 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add `from_var_bytes` to fr [#126] and refactor and rename to `hash_to_scalar` [#129] +- Add `hash_to_point` to `ExtendedPoint` [#129] ## [0.13.1] - 2023-10-11 diff --git a/src/dusk.rs b/src/dusk.rs index 28a1705..dd2276a 100644 --- a/src/dusk.rs +++ b/src/dusk.rs @@ -213,6 +213,44 @@ impl JubJubExtended { let p = JubJubAffine::from(self); [p.u, p.v] } + + /// Hash an arbitrary slice of bytes to a point on the elliptic curve and + /// in the prime order subgroup. + /// + /// This algorithm uses rejection sampling to hash to a point on the curve: + /// The input together with a counter are hashed into an array of 32 bytes. + /// If the hash is a canonical representation of a point on the curve and + /// a member of the prime-order subgroup, we return it. If not, we increment + /// the counter and try to de-serialize again. + /// + /// **Note:** This implementation of `hash_to_point` is not ideal, in the + /// long run we want to implement an algorithm outlined + /// [here](https://datatracker.ietf.org/doc/html/rfc9380), but we start with + /// this implementation in order to be able to use the API already. + pub fn hash_to_point(input: &[u8]) -> Self { + let mut counter = 0u64; + let mut array = [0u8; 32]; + loop { + let state = blake2b_simd::Params::new() + .hash_length(32) + .to_state() + .update(input) + .update(&counter.to_le_bytes()) + .finalize(); + let bytes = state.as_bytes(); + + array.copy_from_slice(&bytes[..32]); + + if let Ok(point) = + >::from_bytes(&array) + { + if point.is_prime_order().into() { + return point.into(); + } + } + counter += 1 + } + } } #[test] @@ -254,7 +292,6 @@ fn test_affine_point_generator_nums_is_not_identity() { ); } -#[ignore] #[test] fn second_gen_nums() { use blake2::{Blake2b, Digest}; @@ -279,7 +316,24 @@ fn second_gen_nums() { == >::from_bytes(&array) .unwrap() ); + break; } counter += 1; } + assert_eq!(counter, 18); +} + +#[cfg(all(test, feature = "alloc"))] +mod fuzz { + use alloc::vec::Vec; + + use crate::ExtendedPoint; + + quickcheck::quickcheck! { + fn prop_hash_to_point(bytes: Vec) -> bool { + let point = ExtendedPoint::hash_to_point(&bytes); + + point.is_on_curve_vartime() && point.is_prime_order().into() + } + } }