Skip to content

Commit

Permalink
feat: add from_var_bytes to Fr
Browse files Browse the repository at this point in the history
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
  • Loading branch information
artifex11 authored and vlopes11 committed Nov 24, 2023
1 parent ee84593 commit d04a333
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
Expand Down
57 changes: 57 additions & 0 deletions src/fr/dusk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> for any integer type smaller than 128-bit
Expand Down Expand Up @@ -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<u8>) -> bool {
let fr = Fr::from_var_bytes(&bytes);

is_fr_in_range(&fr)
}
}
}

0 comments on commit d04a333

Please sign in to comment.