Skip to content

Commit

Permalink
Merge pull request #1 from huitseeker/feature/hash-to-curve-with-tests
Browse files Browse the repository at this point in the history
Add tests for map_to_curve from gnark-crypto
  • Loading branch information
han0110 authored Jun 13, 2023
2 parents 3ba1790 + cfc6ec2 commit 1df1839
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 77 deletions.
124 changes: 121 additions & 3 deletions src/bn256/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,18 +211,136 @@ impl CofactorGroup for G2 {
mod tests {

use crate::arithmetic::CurveEndo;
use crate::bn256::{Fr, G1, G2};
use crate::bn256::{Fq, Fr, G1Affine, G1, G2};
use crate::hash_to_curve::map_to_curve;
use crate::serde::SerdeObject;
use crate::CurveExt;
use ff::Field;
use ff::PrimeField;
use ff::WithSmallOrderMulGroup;
use ff::{PrimeField, WithSmallOrderMulGroup};
use group::Curve;
use num_bigint::BigUint;
use num_traits::Num;
use pasta_curves::arithmetic::CurveAffine;
use rand_core::OsRng;

#[test]
fn test_hash_to_curve() {
crate::tests::curve::hash_to_curve_test::<G1>();
}

#[test]
fn test_map_to_curve_bn256() {
// from https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/bn254/hash_vectors_test.go
let encode_tests = vec![
(
//u
"0xcb81538a98a2e3580076eed495256611813f6dae9e16d3d4f8de7af0e9833e1",
// Q
(
"0x1bb8810e2ceaf04786d4efd216fc2820ddd9363712efc736ada11049d8af5925",
"0x1efbf8d54c60d865cce08437668ea30f5bf90d287dbd9b5af31da852915e8f11",
),
),
(
//u
"0xba35e127276e9000b33011860904ddee28f1d48ddd3577e2a797ef4a5e62319",
// Q
(
"0xda4a96147df1f35b0f820bd35c6fac3b80e8e320de7c536b1e054667b22c332",
"0x189bd3fbffe4c8740d6543754d95c790e44cd2d162858e3b733d2b8387983bb7",
),
),
(
//u
"0x11852286660cd970e9d7f46f99c7cca2b75554245e91b9b19d537aa6147c28fc",
// Q
(
"0x2ff727cfaaadb3acab713fa22d91f5fddab3ed77948f3ef6233d7ea9b03f4da1",
"0x304080768fd2f87a852155b727f97db84b191e41970506f0326ed4046d1141aa",
),
),
(
//u
"0x174d1c85d8a690a876cc1deba0166d30569fafdb49cb3ed28405bd1c5357a1cc",
// Q
(
"0x11a2eaa8e3e89de056d1b3a288a7f733c8a1282efa41d28e71af065ab245df9b",
"0x60f37c447ac29fd97b9bb83be98ddccf15e34831a9cdf5493b7fede0777ae06",
),
),
(
//u
"0x73b81432b4cf3a8a9076201500d1b94159539f052a6e0928db7f2df74bff672",
// Q
(
"0x27409dccc6ee4ce90e24744fda8d72c0bc64e79766f778da0c1c0ef1c186ea84",
"0x1ac201a542feca15e77f30370da183514dc99d8a0b2c136d64ede35cd0b51dc0",
),
),
];

// inspired by TestMapToCurve1 in
// https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/bn254/hash_to_g1_test.go
for (u, pt_q) in encode_tests {
let big_u = BigUint::from_str_radix(&u.strip_prefix("0x").unwrap(), 16)
.unwrap()
.to_string();
let u = Fq::from_str_vartime(&big_u).unwrap();

let to_fq = |arg: [u64; 4]| {
let arg_bytes: [u8; 32] = unsafe { ::std::mem::transmute(arg) };
Fq::from_raw_bytes_unchecked(&arg_bytes)
};

// from https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/bn254/hash_to_g1.go
let z = to_fq([
15230403791020821917,
754611498739239741,
7381016538464732716,
1011752739694698287,
]);
let c1 = to_fq([
1248766071674976557,
10548065924188627562,
16242874202584236114,
560012691975822483,
]);
let c2 = to_fq([
12997850613838968789,
14304628359724097447,
2950087706404981016,
1237622763554136189,
]);
let c3 = to_fq([
8972444824031832946,
5898165201680709844,
10690697896010808308,
824354360198587078,
]);
let c4 = to_fq([
12077013577332951089,
1872782865047492001,
13514471836495169457,
415649166299893576,
]);

let g: G1 = map_to_curve(u, &c1, c2, &c3, &c4, &G1::a(), &G1::b(), &z);
let g_aff = g.to_affine();

let big_x = BigUint::from_str_radix(&pt_q.0.strip_prefix("0x").unwrap(), 16)
.unwrap()
.to_string();
let big_y = BigUint::from_str_radix(&pt_q.1.strip_prefix("0x").unwrap(), 16)
.unwrap()
.to_string();
let x = Fq::from_str_vartime(&big_x).unwrap();
let y = Fq::from_str_vartime(&big_y).unwrap();
let expected_g = G1Affine::from_xy(x, y).unwrap();

assert_eq!(g_aff, expected_g);
}
}

#[test]
fn test_curve() {
crate::tests::curve::curve_tests::<G1>();
Expand Down
166 changes: 92 additions & 74 deletions src/hash_to_curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,97 @@ fn hash_to_field<F: FromUniformBytes<64>>(
}
}

#[allow(clippy::too_many_arguments)]
pub(crate) fn map_to_curve<C>(
u: C::Base,
c1: &C::Base,
c2: C::Base,
c3: &C::Base,
c4: &C::Base,
a: &C::Base,
b: &C::Base,
z: &C::Base,
) -> C
where
C: CurveExt,
C::Base: FromUniformBytes<64>,
{
let one = C::Base::ONE;

// 1. tv1 = u^2
let tv1 = u.square();
// 2. tv1 = tv1 * c1
let tv1 = tv1 * c1;
// 3. tv2 = 1 + tv1
let tv2 = one + tv1;
// 4. tv1 = 1 - tv1
let tv1 = one - tv1;
// 5. tv3 = tv1 * tv2
let tv3 = tv1 * tv2;
// 6. tv3 = inv0(tv3)
let tv3 = tv3.invert().unwrap_or(C::Base::ZERO);
// 7. tv4 = u * tv1
let tv4 = u * tv1;
// 8. tv4 = tv4 * tv3
let tv4 = tv4 * tv3;
// 9. tv4 = tv4 * c3
let tv4 = tv4 * c3;
// 10. x1 = c2 - tv4
let x1 = c2 - tv4;
// 11. gx1 = x1^2
let gx1 = x1.square();
// 12. gx1 = gx1 + A
let gx1 = gx1 + a;
// 13. gx1 = gx1 * x1
let gx1 = gx1 * x1;
// 14. gx1 = gx1 + B
let gx1 = gx1 + b;
// 15. e1 = is_square(gx1)
let e1 = gx1.sqrt().is_some();
// 16. x2 = c2 + tv4
let x2 = c2 + tv4;
// 17. gx2 = x2^2
let gx2 = x2.square();
// 18. gx2 = gx2 + A
let gx2 = gx2 + a;
// 19. gx2 = gx2 * x2
let gx2 = gx2 * x2;
// 20. gx2 = gx2 + B
let gx2 = gx2 + b;
// 21. e2 = is_square(gx2) AND NOT e1 # Avoid short-circuit logic ops
let e2 = gx2.sqrt().is_some() & (!e1);
// 22. x3 = tv2^2
let x3 = tv2.square();
// 23. x3 = x3 * tv3
let x3 = x3 * tv3;
// 24. x3 = x3^2
let x3 = x3.square();
// 25. x3 = x3 * c4
let x3 = x3 * c4;
// 26. x3 = x3 + Z
let x3 = x3 + z;
// 27. x = CMOV(x3, x1, e1) # x = x1 if gx1 is square, else x = x3
let x = C::Base::conditional_select(&x3, &x1, e1);
// 28. x = CMOV(x, x2, e2) # x = x2 if gx2 is square and gx1 is not
let x = C::Base::conditional_select(&x, &x2, e2);
// 29. gx = x^2
let gx = x.square();
// 30. gx = gx + A
let gx = gx + a;
// 31. gx = gx * x
let gx = gx * x;
// 32. gx = gx + B
let gx = gx + b;
// 33. y = sqrt(gx)
let y = gx.sqrt().unwrap();
// 34. e3 = sgn0(u) == sgn0(y)
let e3 = u.is_odd().ct_eq(&y.is_odd());
// 35. y = CMOV(-y, y, e3) # Select correct sign of y
let y = C::Base::conditional_select(&-y, &y, e3);
// 36. return (x, y)
C::new_jacobian(x, y, one).unwrap()
}

/// Implementation of https://www.ietf.org/id/draft-irtf-cfrg-hash-to-curve-16.html#name-shallue-van-de-woestijne-met
#[allow(clippy::type_complexity)]
pub(crate) fn svdw_map_to_curve<'a, C>(
Expand Down Expand Up @@ -116,80 +207,7 @@ where
let mut us = [C::Base::ZERO; 2];
hash_to_field("SVDW", curve_id, domain_prefix, message, &mut us);

let [q0, q1] = us.map(|u| {
// 1. tv1 = u^2
let tv1 = u.square();
// 2. tv1 = tv1 * c1
let tv1 = tv1 * c1;
// 3. tv2 = 1 + tv1
let tv2 = one + tv1;
// 4. tv1 = 1 - tv1
let tv1 = one - tv1;
// 5. tv3 = tv1 * tv2
let tv3 = tv1 * tv2;
// 6. tv3 = inv0(tv3)
let tv3 = tv3.invert().unwrap_or(C::Base::ZERO);
// 7. tv4 = u * tv1
let tv4 = u * tv1;
// 8. tv4 = tv4 * tv3
let tv4 = tv4 * tv3;
// 9. tv4 = tv4 * c3
let tv4 = tv4 * c3;
// 10. x1 = c2 - tv4
let x1 = c2 - tv4;
// 11. gx1 = x1^2
let gx1 = x1.square();
// 12. gx1 = gx1 + A
let gx1 = gx1 + a;
// 13. gx1 = gx1 * x1
let gx1 = gx1 * x1;
// 14. gx1 = gx1 + B
let gx1 = gx1 + b;
// 15. e1 = is_square(gx1)
let e1 = gx1.sqrt().is_some();
// 16. x2 = c2 + tv4
let x2 = c2 + tv4;
// 17. gx2 = x2^2
let gx2 = x2.square();
// 18. gx2 = gx2 + A
let gx2 = gx2 + a;
// 19. gx2 = gx2 * x2
let gx2 = gx2 * x2;
// 20. gx2 = gx2 + B
let gx2 = gx2 + b;
// 21. e2 = is_square(gx2) AND NOT e1 # Avoid short-circuit logic ops
let e2 = gx2.sqrt().is_some() & (!e1);
// 22. x3 = tv2^2
let x3 = tv2.square();
// 23. x3 = x3 * tv3
let x3 = x3 * tv3;
// 24. x3 = x3^2
let x3 = x3.square();
// 25. x3 = x3 * c4
let x3 = x3 * c4;
// 26. x3 = x3 + Z
let x3 = x3 + z;
// 27. x = CMOV(x3, x1, e1) # x = x1 if gx1 is square, else x = x3
let x = C::Base::conditional_select(&x3, &x1, e1);
// 28. x = CMOV(x, x2, e2) # x = x2 if gx2 is square and gx1 is not
let x = C::Base::conditional_select(&x, &x2, e2);
// 29. gx = x^2
let gx = x.square();
// 30. gx = gx + A
let gx = gx + a;
// 31. gx = gx * x
let gx = gx * x;
// 32. gx = gx + B
let gx = gx + b;
// 33. y = sqrt(gx)
let y = gx.sqrt().unwrap();
// 34. e3 = sgn0(u) == sgn0(y)
let e3 = u.is_odd().ct_eq(&y.is_odd());
// 35. y = CMOV(-y, y, e3) # Select correct sign of y
let y = C::Base::conditional_select(&-y, &y, e3);
// 36. return (x, y)
C::new_jacobian(x, y, one).unwrap()
});
let [q0, q1]: [C; 2] = us.map(|u| map_to_curve(u, &c1, c2, &c3, &c4, &a, &b, &z));

let r = q0 + &q1;
debug_assert!(bool::from(r.is_on_curve()));
Expand Down

0 comments on commit 1df1839

Please sign in to comment.