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

Map-to-curve #430

Merged
merged 76 commits into from
Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from 74 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
241cb41
Add IETF-compatible hasher
mmagician Feb 16, 2022
543d546
Merge branch 'arkworks-rs:master' into ietf-hasher
mmagician Mar 9, 2022
6b39930
Add empty test suites
mmagician Mar 10, 2022
72b67a6
Include the suites in testing
mmagician Mar 10, 2022
5f1ece7
Complete the hash-to-field suites
mmagician Mar 10, 2022
25a435d
swap g1 > g2
mmagician Mar 10, 2022
21d1f9d
Rename IETFHasher back to DefaultHasher
mmagician Mar 10, 2022
59daf69
Rename DefaultHasher -> DefaultFieldHasher
mmagician Mar 10, 2022
2787667
Read the test vectors as hex, not as raw bytes
mmagician Mar 11, 2022
a3b5c69
Simplify the structure of tests
mmagician Mar 24, 2022
4cd918f
Update ec/Cargo.toml
mmagician Mar 24, 2022
605b96b
Replace lazy_static by an array
mmagician Mar 24, 2022
9d3b013
Merge branch 'ietf-hasher' of github.com:w3f/arkworks-algebra into ie…
mmagician Mar 24, 2022
cb4311b
Merge branch 'master' into ietf-hasher
mmagician Mar 24, 2022
552ca74
Clean up a comment
mmagician Mar 24, 2022
babfe6b
disables default features in sha2 and sha3 and enabling alloc for
drskalman Apr 6, 2022
e05fd6d
Merge branch 'master' into ietf-hash-to-field
mmagician Apr 24, 2022
205625a
Move WB tests to the curve_maps
mmagician May 4, 2022
8f7dce8
Move small field SWU tests to curve_maps
mmagician May 4, 2022
0942860
Add test suites for g1_iso
mmagician May 4, 2022
1c1afcc
Read the serialised point as on the original curve
mmagician May 5, 2022
6e5eabe
Merge branch 'master' into ietf-hash-to-field
mmagician May 6, 2022
190a5f9
Merge branch 'master' into ietf-hash-to-field
mmagician May 6, 2022
61e5d0d
Apply suggestions from code review
mmagician May 6, 2022
37dae2e
import the correct crate: sha2
mmagician May 6, 2022
efe7a10
Rename hasher/mapper constructors to just `new`
mmagician May 6, 2022
833073e
Add a default SEC_PARAM value of 128
mmagician May 6, 2022
c2653d2
Remove empty line
mmagician May 6, 2022
ac2aa67
Move hash_to_field into ff crate
mmagician May 9, 2022
dd494c6
Update doc comment for `get_len_per_elem`
mmagician May 9, 2022
e8f91d4
Remove gen_len_per_elem test
mmagician May 9, 2022
ed85740
Merge branch 'master' into ietf-hash-to-field
mmagician May 9, 2022
ef5b411
Remove unused imports from ec
mmagician May 9, 2022
ab22fc2
Merge branch 'ietf-hash-to-field' of github.com:mmagician/algebra int…
mmagician May 9, 2022
75585f9
Merge branch 'ietf-hash-to-field' into map-to-curve-tests
mmagician May 10, 2022
f618bc6
assert the iso generator is actually on curve
mmagician May 10, 2022
52cd898
Use a correct generator and curve params
mmagician May 10, 2022
6e23a99
Update the isogeny map params to match the draft
mmagician May 11, 2022
2281ccf
only negate `y` if parity(u) != parity(y)
mmagician May 11, 2022
4fdba02
Adapt parity method to be compatible for all `m`
mmagician May 11, 2022
9a4077c
Simplify reading an fq_vec with a helper method
mmagician May 11, 2022
9dbdc40
Use a COFACTOR param from the draft for G1
mmagician May 11, 2022
6bfaf15
Merge branch 'master' into map-to-curve-tests
mmagician May 11, 2022
572f16c
Bring fq12 and g2 from bls12-381 to test-curves
mmagician May 11, 2022
00cb0cb
Implement the isogeny traits for g2
mmagician May 11, 2022
a48b61e
Adapt the test suites to run for both g1 & g2
mmagician May 11, 2022
1434435
format
mmagician May 11, 2022
fc4e25d
Update the cofactor for g2
mmagician May 11, 2022
9fa58bd
Make the test suites more explicit
mmagician May 16, 2022
cf55317
Merge branch 'master' into map-to-curve-tests
mmagician May 24, 2022
d56aa8b
Merge branch 'master' into map-to-curve-tests
mmagician Jun 22, 2022
2b9fd7e
Rename xi -> zeta
mmagician Jun 22, 2022
34ecae3
update cofactor to h_eff
mmagician Jun 22, 2022
7945d11
update cofactor: should be in BE
mmagician Jun 22, 2022
bb8f611
Remove redundant code
mmagician Jun 22, 2022
0ce9f81
negate the Y-isogeny for g2 BLS12-381
mmagician Jun 22, 2022
5a94ff0
Remove XI, ZETA_ON_XI as they're no longer used
mmagician Jun 23, 2022
7174d78
Fix formatting
mmagician Jun 23, 2022
2f2e7ad
Limit the output of `hash` to just one, final elem
mmagician Jun 23, 2022
83666cb
no need to parse q0, q1 when reading json
mmagician Jun 23, 2022
84a4d56
Double-ended iterator is not needed at bonud
mmagician Jun 23, 2022
79547d1
Merge branch 'master' into map-to-curve-tests
mmagician Jun 30, 2022
1b751c5
Use clear_cofactor instead of mul_by_cofactor
mmagician Jun 24, 2022
d5f05d1
Merge branch 'master' into map-to-curve-tests
mmagician Jul 1, 2022
6510e2e
Merge branch 'master' into map-to-curve-tests
mmagician Jul 6, 2022
1a26b0d
Merge branch 'master' into map-to-curve-tests
Pratyush Jul 9, 2022
2741a55
Merge branch 'master' into map-to-curve-tests
mmagician Jul 14, 2022
093e69e
g2 & g2_iso should have the same cofactor
mmagician Jul 16, 2022
cffad6d
Add source for the WB-map parameters
mmagician Jul 16, 2022
c8723ab
Use the more efficient g2 cofactor clearing
mmagician Jul 16, 2022
618739e
Merge branch 'master' into map-to-curve-tests
mmagician Jul 16, 2022
7fea6c9
Define generators as per method in zcash
mmagician Jul 19, 2022
9fbd150
Update comments
mmagician Jul 19, 2022
007621b
Add explicit tests for G1, G2 generators
mmagician Jul 19, 2022
0183527
Merge branch 'master' into map-to-curve-tests
Pratyush Jul 27, 2022
540690a
Merge branch 'master' into map-to-curve-tests
Pratyush Jul 27, 2022
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
189 changes: 152 additions & 37 deletions ec/src/hashing/curve_maps/swu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,10 @@ use crate::{
/// - [\[WB2019\]] <https://eprint.iacr.org/2019/403>
pub trait SWUParams: SWCurveConfig {
/// An element of the base field that is not a square root see \[WB2019, Section 4\].
/// It is also convenient to have $g(b/xi * a)$ to be square. In general
/// we use a `XI` with low absolute value coefficients when they are
/// It is also convenient to have $g(b/ZETA * a)$ to be square. In general
/// we use a `ZETA` with low absolute value coefficients when they are
/// represented as integers.
const XI: Self::BaseField;
/// An arbitrary nonsquare conveniently chosen to be a primitive element of the base field
const ZETA: Self::BaseField;
/// Square root of `THETA = Self::XI/Self::ZETA`.
const XI_ON_ZETA_SQRT: Self::BaseField;
}

/// Represents the SWU hash-to-curve map defined by `P`.
Expand All @@ -43,31 +39,13 @@ pub fn parity<F: Field>(element: &F) -> bool {
impl<P: SWUParams> MapToCurve<Affine<P>> for SWUMap<P> {
/// Constructs a new map if `P` represents a valid map.
fn new() -> Result<Self, HashToCurveError> {
// Verifying that both XI and ZETA are non-squares
if P::XI.legendre().is_qr() || P::ZETA.legendre().is_qr() {
// Verifying that ZETA is a non-square
if P::ZETA.legendre().is_qr() {
return Err(HashToCurveError::MapToCurveError(
"both xi and zeta should be quadratic non-residues for the SWU map".to_string(),
"ZETA should be a quadratic non-residue for the SWU map".to_string(),
));
}

// Verifying precomupted values
let xi_on_zeta = P::XI / P::ZETA;
match xi_on_zeta.sqrt() {
Some(xi_on_zeta_sqrt) => {
if xi_on_zeta_sqrt != P::XI_ON_ZETA_SQRT && xi_on_zeta_sqrt != -P::XI_ON_ZETA_SQRT {
return Err(HashToCurveError::MapToCurveError(
"precomputed P::XI_ON_ZETA_SQRT is not what it is supposed to be"
.to_string(),
));
}
},
None => {
panic!(
"`xi_on_zeta` was expected to have a sqrt, since the numerator and denominator are non-residues and Legendre symbol is multiplicative. Q.E.D"
);
},
}

// Verifying the prerequisite for applicability of SWU map
if P::COEFF_A.is_zero() || P::COEFF_B.is_zero() {
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()));
Expand Down Expand Up @@ -105,40 +83,41 @@ impl<P: SWUParams> MapToCurve<Affine<P>> for SWUMap<P> {
// gx1 = num_gx1/div_gx1 = [num_x1^3 + A * num_x1 * div^2 + B * div^3] / div^3
let a = P::COEFF_A;
let b = P::COEFF_B;
let xi_t2 = P::XI * point.square();
let ta = xi_t2.square() + xi_t2;

let zeta_u2 = P::ZETA * point.square();
let ta = zeta_u2.square() + zeta_u2;
let num_x1 = b * (ta + <P::BaseField as One>::one());
let div = a * if ta.is_zero() { P::XI } else { -ta };
let div = a * if ta.is_zero() { P::ZETA } else { -ta };

let num2_x1 = num_x1.square();
let div2 = div.square();
let div3 = div2 * div;
let num_gx1 = (num2_x1 + a * div2) * num_x1 + b * div3;

// 5. x2 = Z * u^2 * x1
let num_x2 = xi_t2 * num_x1; // same div
let num_x2 = zeta_u2 * num_x1; // same div

// 6. gx2 = x2^3 + A * x2 + B [optimized out; see below]
// 7. If is_square(gx1), set x = x1 and y = sqrt(gx1)
// 8. Else set x = x2 and y = sqrt(gx2)
let gx1_square;
let gx1;
let zeta_gx1;

assert!(
!div3.is_zero(),
"we have checked that neither a or xi are zero. Q.E.D."
"we have checked that neither a or ZETA are zero. Q.E.D."
);
let y1: P::BaseField = {
gx1 = num_gx1 / div3;
zeta_gx1 = P::ZETA * gx1;
if gx1.legendre().is_qr() {
gx1_square = true;
gx1.sqrt()
.expect("We have checked that gx1 is a quadratic residue. Q.E.D")
} else {
let zeta_gx1 = P::ZETA * gx1;
gx1_square = false;
zeta_gx1.sqrt().expect(
"zeta * gx1 is a quadratic residue because legard is multiplicative. Q.E.D",
"ZETA * gx1 is a quadratic residue because legard is multiplicative. Q.E.D",
)
}
};
Expand All @@ -159,12 +138,12 @@ impl<P: SWUParams> MapToCurve<Affine<P>> for SWUMap<P> {
// u^3 * y1 is a square root of gx2. Note that we don't actually need to
// compute gx2.

let y2 = P::XI_ON_ZETA_SQRT * xi_t2 * point * y1;
let y2 = zeta_u2 * point * y1;
let num_x = if gx1_square { num_x1 } else { num_x2 };
let y = if gx1_square { y1 } else { y2 };

let x_affine = num_x / div;
let y_affine = if parity(&y) { -y } else { y };
let y_affine = if parity(&y) != parity(&point) { -y } else { y };
let point_on_curve = Affine::<P>::new_unchecked(x_affine, y_affine);
assert!(
point_on_curve.is_on_curve(),
Expand All @@ -173,3 +152,139 @@ impl<P: SWUParams> MapToCurve<Affine<P>> for SWUMap<P> {
Ok(point_on_curve)
}
}

#[cfg(test)]
mod test {
use crate::hashing::map_to_curve_hasher::MapToCurveBasedHasher;
use crate::hashing::HashToCurve;
use crate::CurveConfig;
use ark_ff::field_hashers::DefaultFieldHasher;
use ark_std::vec::Vec;

use super::*;
use ark_ff::{fields::Fp64, MontBackend, MontFp};
use hashbrown::HashMap;
use sha2::Sha256;

#[derive(ark_ff::MontConfig)]
#[modulus = "127"]
#[generator = "6"]
pub struct F127Config;
pub type F127 = Fp64<MontBackend<F127Config, 1>>;

const F127_ONE: F127 = MontFp!("1");

struct TestSWUMapToCurveParams;

impl CurveConfig for TestSWUMapToCurveParams {
const COFACTOR: &'static [u64] = &[1];

#[rustfmt::skip]
const COFACTOR_INV: F127 = F127_ONE;

type BaseField = F127;
type ScalarField = F127;
}
/// just because not defining another field
///
/// from itertools import product
/// p = 127
/// FF = GF(p)
/// for a,b in product(range(0,p), range(0,p)):
/// try:
/// E = EllipticCurve([FF(a),FF(b)])
/// if E.order() == p:
/// print(E)
/// except:
/// pass
///
/// y^2 = x^3 + x + 63
impl SWCurveConfig for TestSWUMapToCurveParams {
/// COEFF_A = 1
const COEFF_A: F127 = F127_ONE;

/// COEFF_B = 1
#[rustfmt::skip]
const COEFF_B: F127 = MontFp!("63");

/// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y)
const GENERATOR: Affine<Self> = Affine::new_unchecked(MontFp!("62"), MontFp!("70"));
}

impl SWUParams for TestSWUMapToCurveParams {
const ZETA: F127 = MontFp!("-1");
}

/// test that MontFp make a none zero element out of 1
#[test]
fn test_field_element_construction() {
let a1 = F127::from(1);
let a2 = F127::from(2);
let a3 = F127::from(125);

assert!(F127::from(0) == a2 + a3);
assert!(F127::from(0) == a2 * a1 + a3);
}

#[test]
fn test_field_division() {
let num = F127::from(0x3d);
let den = F127::from(0x7b);
let num_on_den = F127::from(0x50);

assert!(num / den == num_on_den);
}

/// The point of the test is to get a simple SWU compatible curve and make
/// simple hash
#[test]
fn hash_arbitary_string_to_curve_swu() {
let test_swu_to_curve_hasher = MapToCurveBasedHasher::<
Affine<TestSWUMapToCurveParams>,
DefaultFieldHasher<Sha256, 128>,
SWUMap<TestSWUMapToCurveParams>,
>::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");

assert!(
hash_result.is_on_curve(),
"hash results into a point off the curve"
);
}

/// Use a simple SWU compatible curve and map the whole field to it. We observe
/// the map behaviour. Specifically, the map should be non-constant, all
/// elements should be mapped to curve successfully. everything can be mapped
#[test]
fn map_field_to_curve_swu() {
let test_map_to_curve = SWUMap::<TestSWUMapToCurveParams>::new().unwrap();

let mut map_range: Vec<Affine<TestSWUMapToCurveParams>> = 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 mut counts = HashMap::new();

let mode = map_range
.iter()
.copied()
.max_by_key(|&n| {
let count = counts.entry(n).or_insert(0);
*count += 1;
*count
})
.unwrap();

assert!(
*counts.get(&mode).unwrap() != 127,
"a constant hash function is not good."
);
}
}
Loading