From d04a33369cc2dbe8a2f582c5cf18b2bcfa1218bc Mon Sep 17 00:00:00 2001 From: Artifex Date: Fri, 24 Nov 2023 03:46:55 +0100 Subject: [PATCH] feat: add `from_var_bytes` to `Fr` This commit introdues a function that will take arbitrary slices, hash with BLAKE2b into 256-bit numbers, and perform a modulus multiplication to produce valid scalars. Resolves #126 --- CHANGELOG.md | 5 +++++ Cargo.toml | 5 +++++ src/fr/dusk.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6bedee..93293ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `from_var_bytes` to scalar [#126] + ## [0.13.1] - 2023-10-11 ### Changed @@ -198,6 +202,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Initial fork from [`zkcrypto/jubjub`] +[#126]: https://github.com/dusk-network/jubjub/issues/126 [#115]: https://github.com/dusk-network/jubjub/issues/115 [#109]: https://github.com/dusk-network/jubjub/issues/109 [#104]: https://github.com/dusk-network/jubjub/issues/104 diff --git a/Cargo.toml b/Cargo.toml index 69749f2..84a944d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,10 @@ version = "2" default-features = false # Begin Dusk dependencies +[dependencies.blake2b_simd] +version = "1.0" +default-features = false + [dependencies.bytecheck] version = "0.6" optional = true @@ -62,6 +66,7 @@ default-features = false [dev-dependencies] criterion = "0.3" csv = ">= 1.0, < 1.2" # csv 1.2 has MSRV 1.60 +quickcheck = "1" [dev-dependencies.rand_xorshift] version = "0.3" diff --git a/src/fr/dusk.rs b/src/fr/dusk.rs index df227c3..e367174 100644 --- a/src/fr/dusk.rs +++ b/src/fr/dusk.rs @@ -129,6 +129,37 @@ impl Fr { } res } + + /// Creates a `Fr` from arbitrary bytes by hashing the input with BLAKE2b + /// into a 256-bits number, and then converting it into its `Fr` + /// representation. + pub fn from_var_bytes(input: &[u8]) -> Self { + let state = blake2b_simd::Params::new() + .hash_length(32) + .to_state() + .update(input) + .finalize(); + + let h = state.as_bytes(); + let mut r = [0u64; 4]; + + // will be optmized by the compiler, depending on the available target + for i in 0..4 { + r[i] = u64::from_le_bytes([ + h[i * 8], + h[i * 8 + 1], + h[i * 8 + 2], + h[i * 8 + 3], + h[i * 8 + 4], + h[i * 8 + 5], + h[i * 8 + 6], + h[i * 8 + 7], + ]); + } + + // `from_raw` converts from arbitrary to congruent scalar + Self::from_raw(r) + } } // TODO implement From for any integer type smaller than 128-bit @@ -303,3 +334,29 @@ fn w_naf_2() { }); assert_eq!(scalar, recomputed); } + +#[cfg(all(test, feature = "alloc"))] +mod fuzz { + use alloc::vec::Vec; + + use crate::fr::{Fr, MODULUS}; + use crate::util::sbb; + + fn is_fr_in_range(fr: &Fr) -> bool { + // subtraction against modulus must underflow + let borrow = + fr.0.iter() + .zip(MODULUS.0.iter()) + .fold(0, |borrow, (&s, &m)| sbb(s, m, borrow).1); + + borrow == u64::MAX + } + + quickcheck::quickcheck! { + fn prop_fr_from_raw_bytes(bytes: Vec) -> bool { + let fr = Fr::from_var_bytes(&bytes); + + is_fr_in_range(&fr) + } + } +}