Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor hash-to-curve around digest::XofReader trait #643

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
d652bf3
Avoid unused subdirectories
Mar 20, 2023
c17b654
Pseduo-remove MapToCurve::new, renamed to test_parameters
Mar 20, 2023
b4633c9
Update ec/src/hashing/curve_maps/wb.rs
burdges Mar 21, 2023
6c1a355
Update ec/src/hashing/curve_maps/swu.rs
burdges Mar 21, 2023
d211861
Update ec/src/hashing/map_to_curve_hasher.rs
burdges Mar 21, 2023
53d9cda
Update ec/src/hashing/curve_maps/swu.rs
burdges Mar 21, 2023
10d2d6d
Two more renames
Mar 21, 2023
6d8fd58
make rustfmt happier
Mar 21, 2023
c7ffca1
Again rustfmt
Mar 21, 2023
80f889b
rustfmt is wrong on this one but whatever
burdges Mar 21, 2023
5d149da
Z_pad without Vec
Mar 22, 2023
b3eaa74
inherent method for construct_dst_prime
Mar 22, 2023
65f2229
Use update directly for ExpanderXoF
Mar 23, 2023
90ed308
More idiomatic lib_str
Mar 23, 2023
f311cc2
DST abstraction
Mar 23, 2023
6c8eeff
Test that DST works
Mar 23, 2023
343a8b7
Remove inherent method in favor of DST
Mar 23, 2023
e9ce287
defalt instead of clone
Mar 23, 2023
23f1772
Default insead of Clone
Mar 23, 2023
15fc1a5
Field::from_base_prime_field_elems takes Iterator, not slice.
Apr 13, 2023
f19976e
use const len in hash_to_field
Apr 14, 2023
99299ce
Ugh Vec -> Vec here was just stupid
Apr 14, 2023
20b4111
Kill DynDigest
Apr 14, 2023
fd0d50f
Idiomatic const
Apr 14, 2023
727daaf
semi-merge dst_prime
Apr 14, 2023
e0f1ab7
FixedOutputReset instead of FixedOutput
Apr 14, 2023
25af4a6
More double Vec stupidity
Apr 14, 2023
9982b4d
Initial XofReader based hash_to_field
Apr 14, 2023
5fa83ef
Expose DST
Apr 17, 2023
6573052
user reset
Apr 17, 2023
818e821
Switch to XofReader
Apr 17, 2023
a1fdce4
Sergey complained. lol
Apr 18, 2023
a697434
Switch to fn interface
Apr 18, 2023
25c93fb
Compute block_size and use Update
Apr 19, 2023
75a00a0
Expose more
Apr 19, 2023
567cf5f
Add Expander trait.
Apr 19, 2023
9d6148d
Oops, this would never have worked. Artifact of bad test vectors.
Apr 19, 2023
2e4629d
expand_for_field
Apr 21, 2023
3f4c137
PanicExpander is unecessary
Apr 21, 2023
22858b2
Attempt at new hash-to-curve traits
Apr 21, 2023
db6feb0
cargo check psses, ugly hack around const-generic being broken
Apr 21, 2023
b8dda7c
Remove curve_maps:: everywhere to shrink later diffs
Apr 23, 2023
ba26f3c
Allow slice DSTs
Apr 23, 2023
db8fea5
Move sec_param from type or value level, given const generics does not
Apr 25, 2023
7705b72
Fix ark_ec tests
Apr 25, 2023
95cbf11
Ok don't make DSTs be 'static
Apr 25, 2023
3d32841
Tentative fix to test-curves
Apr 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions ec/src/hashing/curve_maps/mod.rs

This file was deleted.

69 changes: 0 additions & 69 deletions ec/src/hashing/map_to_curve_hasher.rs

This file was deleted.

100 changes: 89 additions & 11 deletions ec/src/hashing/mod.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,96 @@
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,AsDST,Expander,Zpad,
digest::{self,FixedOutputReset,Update,XofReader}
};

use crate::{CurveGroup,AffineRepr};


/// Trait for mapping a random field element to a random curve point.
pub trait MapToCurve<C: CurveGroup>: Sized {
/// Security parameters used by symetric components.
/// Almost always 128 bits, unsued if merely supporting WB.
const SEC_PARAM: u16 = 128;

/// Trait for hashing arbitrary data to a group element on an elliptic curve
pub trait HashToCurve<T: CurveGroup>: Sized {
/// Create a new hash to curve instance, with a given domain.
fn new(domain: &[u8]) -> Result<Self, HashToCurveError>;
/// 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<C::Affine, HashToCurveError>;
}

/// 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<C,M,H>(xof: &mut H) -> Result<C, HashToCurveError>
where C: CurveGroup, M: MapToCurve<C>, H: XofReader,
{
let mut f = || field_hashers::hash_to_field::<C::BaseField,H>(<M as MapToCurve<C>>::SEC_PARAM, xof);
let p0 = <M as MapToCurve<C>>::map_to_curve(f())?;
let p1 = <M as MapToCurve<C>>::map_to_curve(f())?;

// We've no projective clear_cofactor metho so normalize twice.
Ok( (p0 + p1).into_affine().clear_cofactor().into_group() )
}

/// 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<T::Affine, HashToCurveError>;
/// 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<C,M>(exp: impl Expander, dst: impl AsDST) -> Result<C, HashToCurveError>
where C: CurveGroup, M: MapToCurve<C>
{
#[cfg(debug_assertions)]
<M as MapToCurve<C>>::check_parameters()?;
let mut xof = exp.expand_for_field::<C::BaseField,2>(<M as MapToCurve<C>>::SEC_PARAM, dst);
xof_map_to_curve::<C,M,_>(&mut xof)
}

/// 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<C,M,H>() -> Zpad<H>
where C: CurveGroup, M: MapToCurve<C>, H: FixedOutputReset+Default,
{
Zpad::<H>::new_for_field::<C::BaseField>(<M as MapToCurve<C>>::SEC_PARAM)
}

/// [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<Self>;
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: impl AsDST) -> Result<Self, HashToCurveError> {
expand_to_curve::<Self,Self::Map>(exp,dst)
}

fn hash_to_curve(dst: impl AsDST, msg: &[u8]) -> Result<Self, HashToCurveError> {
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
Expand All @@ -37,5 +114,6 @@ impl fmt::Display for HashToCurveError {
}
}


#[cfg(test)]
mod tests;
52 changes: 28 additions & 24 deletions ec/src/hashing/curve_maps/swu/mod.rs → ec/src/hashing/swu.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
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;

pub use super::MapToCurve;

use crate::{
hashing::{map_to_curve_hasher::MapToCurve, HashToCurveError},
hashing::{HashToCurveError},
models::short_weierstrass::{Affine, Projective},
};

Expand All @@ -19,6 +23,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`.
Expand All @@ -35,8 +43,12 @@ pub fn parity<F: Field>(element: &F) -> bool {
}

impl<P: SWUConfig> MapToCurve<Projective<P>> for SWUMap<P> {
/// Constructs a new map if `P` represents a valid map.
fn new() -> Result<Self, HashToCurveError> {
/// Security parameters used by symetric components.
/// Almost always 128 bits.
const SEC_PARAM: u16 = <P as SWUConfig>::SEC_PARAM;

/// Checks if `P` represents a valid map.
fn check_parameters() -> Result<(), HashToCurveError> {
// Verifying that ZETA is a non-square
if P::ZETA.legendre().is_qr() {
return Err(HashToCurveError::MapToCurveError(
Expand All @@ -49,13 +61,13 @@ impl<P: SWUConfig> MapToCurve<Projective<P>> for SWUMap<P> {
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
/// <https://github.com/zcash/pasta_curves/blob/main/src/hashtocurve.rs>.
fn map_to_curve(&self, point: P::BaseField) -> Result<Affine<P>, HashToCurveError> {
fn map_to_curve(point: P::BaseField) -> Result<Affine<P>, 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)
Expand Down Expand Up @@ -152,10 +164,10 @@ impl<P: SWUConfig> MapToCurve<Projective<P>> for SWUMap<P> {
#[cfg(test)]
mod test {
use crate::{
hashing::{map_to_curve_hasher::MapToCurveBasedHasher, 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::*;
Expand Down Expand Up @@ -236,17 +248,12 @@ mod test {
/// simple hash
#[test]
fn hash_arbitary_string_to_curve_swu() {
let test_swu_to_curve_hasher = MapToCurveBasedHasher::<
Projective<TestSWUMapToCurveConfig>,
DefaultFieldHasher<Sha256, 128>,
SWUMap<TestSWUMapToCurveConfig>,
>::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::<Projective<TestSWUMapToCurveConfig>,SWUMap<TestSWUMapToCurveConfig>,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<TestSWUMapToCurveConfig> = expand_to_curve::<Projective<TestSWUMapToCurveConfig>,SWUMap<TestSWUMapToCurveConfig>>(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"
);
}
Expand All @@ -256,15 +263,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::<TestSWUMapToCurveConfig>::new().unwrap();
SWUMap::<TestSWUMapToCurveConfig>::check_parameters().unwrap();

let mut map_range: Vec<Affine<TestSWUMapToCurveConfig>> = 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(),
);
let point = F127::from(current_field_element as u64);
map_range.push(SWUMap::<TestSWUMapToCurveConfig>::map_to_curve(point).unwrap());
}

let mut counts = HashMap::new();
Expand Down
2 changes: 1 addition & 1 deletion ec/src/hashing/tests.rs
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
Loading