diff --git a/benchmarks/bench_hash_to_curve.nim b/benchmarks/bench_hash_to_curve.nim index 988dc7562..37db7ca49 100644 --- a/benchmarks/bench_hash_to_curve.nim +++ b/benchmarks/bench_hash_to_curve.nim @@ -12,9 +12,7 @@ import ../constantine/math/config/curves, ../constantine/math/extension_fields, ../constantine/math/io/[io_bigints, io_ec], - ../constantine/math/elliptic/[ - ec_shortweierstrass_affine, - ec_shortweierstrass_projective], + ../constantine/math/ec_shortweierstrass, ../constantine/hash_to_curve/hash_to_curve, ../constantine/hashes, # Helpers @@ -39,9 +37,9 @@ proc bench_BLS12_381_hash_to_G1(iters: int) = const dst = "BLS_SIG_BLS12381G1-SHA256-SSWU-RO_POP_" let msg = "Mr F was here" - var P: ECP_ShortW_Prj[Fp[BLS12_381], G1] + var P: ECP_ShortW_Jac[Fp[BLS12_381], G1] - bench("Hash to G1 (Draft #11)", BLS12_381, iters): + bench("Hash to G1 (SSWU method - Draft #14)", BLS12_381, iters): sha256.hashToCurve( k = 128, output = P, @@ -54,9 +52,9 @@ proc bench_BLS12_381_hash_to_G2(iters: int) = const dst = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_" let msg = "Mr F was here" - var P: ECP_ShortW_Prj[Fp2[BLS12_381], G2] + var P: ECP_ShortW_Jac[Fp2[BLS12_381], G2] - bench("Hash to G2 (Draft #11)", BLS12_381, iters): + bench("Hash to G2 (SSWU method - Draft #14)", BLS12_381, iters): sha256.hashToCurve( k = 128, output = P, @@ -65,11 +63,72 @@ proc bench_BLS12_381_hash_to_G2(iters: int) = domainSepTag = dst ) -proc bench_BLS12_381_G1_proj_aff_conversion(iters: int) = +proc bench_BLS12_381_hash_to_G1_SVDW(iters: int) = + const dst = "BLS_SIG_BLS12381G1-SHA256-SVDW-RO_POP_" + let msg = "Mr F was here" + + var P: ECP_ShortW_Jac[Fp[BLS12_381], G1] + + bench("Hash to G1 (SVDW method)", BLS12_381, iters): + sha256.hashToCurve_svdw( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = dst + ) + +proc bench_BLS12_381_hash_to_G2_SVDW(iters: int) = + const dst = "BLS_SIG_BLS12381G2-SHA256-SVDW-RO_POP_" + let msg = "Mr F was here" + + var P: ECP_ShortW_Jac[Fp2[BLS12_381], G2] + + bench("Hash to G2 (SVDW method)", BLS12_381, iters): + sha256.hashToCurve_svdw( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = dst + ) + +proc bench_BN254_Snarks_hash_to_G1(iters: int) = + const dst = "BLS_SIG_BN254SNARKSG1-SHA256-SVDW-RO_POP_" + let msg = "Mr F was here" + + var P: ECP_ShortW_Jac[Fp[BN254_Snarks], G1] + + bench("Hash to G1 (SVDW method)", BN254_Snarks, iters): + sha256.hashToCurve( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = dst + ) + +proc bench_BN254_Snarks_hash_to_G2(iters: int) = + const dst = "BLS_SIG_BN254SNARKSG2-SHA256-SVDW-RO_POP_" + let msg = "Mr F was here" + + var P: ECP_ShortW_Jac[Fp2[BN254_Snarks], G2] + + bench("Hash to G2 (SVDW method)", BN254_Snarks, iters): + sha256.hashToCurve( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = dst + ) + + +proc bench_BLS12_381_G1_jac_aff_conversion(iters: int) = const dst = "BLS_SIG_BLS12381G1-SHA256-SSWU-RO_POP_" let msg = "Mr F was here" - var P: ECP_ShortW_Prj[Fp[BLS12_381], G1] + var P: ECP_ShortW_Jac[Fp[BLS12_381], G1] var Paff: ECP_ShortW_Aff[Fp[BLS12_381], G1] sha256.hashToCurve( @@ -80,14 +139,14 @@ proc bench_BLS12_381_G1_proj_aff_conversion(iters: int) = domainSepTag = dst ) - bench("G1 Proj->Affine conversion (for pairing)", BLS12_381, iters): + bench("G1 Jac->Affine conversion (for pairing)", BLS12_381, iters): Paff.affine(P) -proc bench_BLS12_381_G2_proj_aff_conversion(iters: int) = +proc bench_BLS12_381_G2_jac_aff_conversion(iters: int) = const dst = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_" let msg = "Mr F was here" - var P: ECP_ShortW_Prj[Fp2[BLS12_381], G2] + var P: ECP_ShortW_Jac[Fp2[BLS12_381], G2] var Paff: ECP_ShortW_Aff[Fp2[BLS12_381], G2] sha256.hashToCurve( @@ -98,7 +157,7 @@ proc bench_BLS12_381_G2_proj_aff_conversion(iters: int) = domainSepTag = dst ) - bench("G2 Proj->Affine conversion (for pairing)", BLS12_381, iters): + bench("G2 Jac->Affine conversion (for pairing)", BLS12_381, iters): Paff.affine(P) const Iters = 1000 @@ -107,8 +166,12 @@ proc main() = separator() bench_BLS12_381_hash_to_G1(Iters) bench_BLS12_381_hash_to_G2(Iters) - bench_BLS12_381_G1_proj_aff_conversion(Iters) - bench_BLS12_381_G2_proj_aff_conversion(Iters) + bench_BLS12_381_hash_to_G1_SVDW(Iters) + bench_BLS12_381_hash_to_G2_SVDW(Iters) + bench_BN254_Snarks_hash_to_G1(Iters) + bench_BN254_Snarks_hash_to_G2(Iters) + bench_BLS12_381_G1_jac_aff_conversion(Iters) + bench_BLS12_381_G2_jac_aff_conversion(Iters) separator() main() diff --git a/benchmarks/bench_summary_bls12_381.nim b/benchmarks/bench_summary_bls12_381.nim index 7e7abf63b..4362771d2 100644 --- a/benchmarks/bench_summary_bls12_381.nim +++ b/benchmarks/bench_summary_bls12_381.nim @@ -79,6 +79,7 @@ proc main() = finalExpBLS12Bench(curve, Iters) pairingBLS12Bench(curve, Iters) separator() + hashToCurveBLS12381G1Bench(Iters) hashToCurveBLS12381G2Bench(Iters) separator() diff --git a/benchmarks/bench_summary_bn254_snarks.nim b/benchmarks/bench_summary_bn254_snarks.nim index 8582b30aa..86f307a1b 100644 --- a/benchmarks/bench_summary_bn254_snarks.nim +++ b/benchmarks/bench_summary_bn254_snarks.nim @@ -78,6 +78,9 @@ proc main() = finalExpBNBench(curve, Iters) pairingBNBench(curve, Iters) separator() + hashToCurveBN254SnarksG1Bench(Iters) + hashToCurveBN254SnarksG2Bench(Iters) + separator() main() notes() diff --git a/benchmarks/bench_summary_template.nim b/benchmarks/bench_summary_template.nim index 6d13176a8..6b71e3366 100644 --- a/benchmarks/bench_summary_template.nim +++ b/benchmarks/bench_summary_template.nim @@ -212,7 +212,24 @@ proc pairingBNBench*(C: static Curve, iters: int) = bench("Pairing BN", C, iters): f.pairing_bn(P, Q) -proc hashToCurveBLS12_381G2Bench*(iters: int) = +proc hashToCurveBLS12381G1Bench*(iters: int) = + # Hardcode BLS12_381 + # otherwise concept symbol + # 'CryptoHash' resolution issue + const dst = "BLS_SIG_BLS12381G1-SHA256-SSWU-RO_POP_" + let msg = "Mr F was here" + var P: ECP_ShortW_Prj[Fp[BLS12_381], G1] + + bench("Hash to G1 (SSWU - Draft #14)", BLS12_381, iters): + sha256.hashToCurve( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = dst + ) + +proc hashToCurveBLS12381G2Bench*(iters: int) = # Hardcode BLS12_381 # otherwise concept symbol # 'CryptoHash' resolution issue @@ -220,7 +237,42 @@ proc hashToCurveBLS12_381G2Bench*(iters: int) = let msg = "Mr F was here" var P: ECP_ShortW_Prj[Fp2[BLS12_381], G2] - bench("Hash to G2 (Draft #11)", BLS12_381, iters): + bench("Hash to G2 (SSWU - Draft #14)", BLS12_381, iters): + sha256.hashToCurve( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = dst + ) + + +proc hashToCurveBN254SnarksG1Bench*(iters: int) = + # Hardcode BN254_Snarks + # otherwise concept symbol + # 'CryptoHash' resolution issue + const dst = "BLS_SIG_BN254SNARKSG1-SHA256-SVDW-RO_POP_" + let msg = "Mr F was here" + var P: ECP_ShortW_Prj[Fp[BN254_Snarks], G1] + + bench("Hash to G1 (SVDW - Draft #14)", BN254_Snarks, iters): + sha256.hashToCurve( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = dst + ) + +proc hashToCurveBN254SnarksG2Bench*(iters: int) = + # Hardcode BN254_Snarks + # otherwise concept symbol + # 'CryptoHash' resolution issue + const dst = "BLS_SIG_BN254SNARKSG2-SHA256-SVDW-RO_POP_" + let msg = "Mr F was here" + var P: ECP_ShortW_Prj[Fp2[BN254_Snarks], G2] + + bench("Hash to G2 (SVDW - Draft #14)", BN254_Snarks, iters): sha256.hashToCurve( k = 128, output = P, diff --git a/constantine.nimble b/constantine.nimble index 4dd3f95e6..a06d496a0 100644 --- a/constantine.nimble +++ b/constantine.nimble @@ -175,11 +175,6 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[ ("tests/math/t_pairing_bls12_381_optate.nim", false), ("tests/math/t_pairing_bls12_381_multi.nim", false), - # Hashing to elliptic curves - # ---------------------------------------------------------- - ("tests/math/t_hash_to_field.nim", false), - ("tests/math/t_hash_to_curve.nim", false), - # Prime order fields # ---------------------------------------------------------- ("tests/math/t_fr.nim", false), @@ -188,6 +183,12 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[ # ---------------------------------------------------------- ("tests/t_hash_sha256_vs_openssl.nim", true), # skip OpenSSL tests on Windows + # Hashing to elliptic curves + # ---------------------------------------------------------- + ("tests/t_hash_to_field.nim", false), + ("tests/t_hash_to_curve_random.nim", false), + ("tests/t_hash_to_curve.nim", false), + # Ciphers # ---------------------------------------------------------- ("tests/t_cipher_chacha20.nim", false), @@ -216,8 +217,9 @@ const skipSanitizers = [ "tests/math/t_ec_sage_bn254_snarks.nim", "tests/math/t_ec_sage_bls12_377.nim", "tests/math/t_ec_sage_bls12_381.nim", - "tests/math/t_hash_to_field.nim", - "tests/math/t_hash_to_curve.nim" + "tests/t_hash_to_field.nim", + "tests/t_hash_to_curve.nim", + "tests/t_hash_to_curve_random.nim" ] when defined(windows): diff --git a/constantine/hash_to_curve/h2c_isogeny_maps.nim b/constantine/hash_to_curve/h2c_isogeny_maps.nim index addb46629..3b083a0df 100644 --- a/constantine/hash_to_curve/h2c_isogeny_maps.nim +++ b/constantine/hash_to_curve/h2c_isogeny_maps.nim @@ -92,10 +92,10 @@ func h2c_isogeny_map[F]( # xd^e with e in [1, N], for example [xd, xd², xd³] const maxdegree = max([ - h2cIsomapPoly(F.C, G, xnum).len, - h2cIsomapPoly(F.C, G, xden).len, - h2cIsomapPoly(F.C, G, ynum).len, - h2cIsomapPoly(F.C, G, yden).len, + h2cIsomapPoly(F.C, sswu, G, xnum).len, + h2cIsomapPoly(F.C, sswu, G, xden).len, + h2cIsomapPoly(F.C, sswu, G, ynum).len, + h2cIsomapPoly(F.C, sswu, G, yden).len, ]) var xd_pow{.noInit.}: array[maxdegree, F] xd_pow[0] = xd @@ -103,28 +103,28 @@ func h2c_isogeny_map[F]( for i in 2 ..< xd_pow.len: xd_pow[i].prod(xd_pow[i-1], xd_pow[0]) - const xnLen = h2cIsomapPoly(F.C, G, xnum).len - const ynLen = h2cIsomapPoly(F.C, G, ynum).len + const xnLen = h2cIsomapPoly(F.C, sswu, G, xnum).len + const ynLen = h2cIsomapPoly(F.C, sswu, G, ynum).len rxn.poly_eval_horner_scaled( xn, xd_pow, - h2cIsomapPoly(F.C, G, xnum), + h2cIsomapPoly(F.C, sswu, G, xnum), xnLen ) rxd.poly_eval_horner_scaled( xn, xd_pow, - h2cIsomapPoly(F.C, G, xden), + h2cIsomapPoly(F.C, sswu, G, xden), xnLen ) ryn.poly_eval_horner_scaled( xn, xd_pow, - h2cIsomapPoly(F.C, G, ynum), + h2cIsomapPoly(F.C, sswu, G, ynum), ynLen ) ryd.poly_eval_horner_scaled( xn, xd_pow, - h2cIsomapPoly(F.C, G, yden), + h2cIsomapPoly(F.C, sswu, G, yden), ynLen ) @@ -224,10 +224,10 @@ func h2c_isogeny_map*[F; G: static Subgroup]( # Z²^e with e in [1, N], for example [Z², Z⁴, Z⁶] const maxdegree = max([ - h2cIsomapPoly(F.C, G, xnum).len, - h2cIsomapPoly(F.C, G, xden).len, - h2cIsomapPoly(F.C, G, ynum).len, - h2cIsomapPoly(F.C, G, yden).len, + h2cIsomapPoly(F.C, sswu, G, xnum).len, + h2cIsomapPoly(F.C, sswu, G, xden).len, + h2cIsomapPoly(F.C, sswu, G, ynum).len, + h2cIsomapPoly(F.C, sswu, G, yden).len, ]) var ZZpow{.noInit.}: array[maxdegree, F] ZZpow[0].square(P.z) @@ -241,28 +241,28 @@ func h2c_isogeny_map*[F; G: static Subgroup]( else: ZZpow[i].prod(ZZpow[(i-1) shr 1], ZZpow[((i-1) shr 1) + 1]) - const xnLen = h2cIsomapPoly(F.C, G, xnum).len - const ynLen = h2cIsomapPoly(F.C, G, ynum).len + const xnLen = h2cIsomapPoly(F.C, sswu, G, xnum).len + const ynLen = h2cIsomapPoly(F.C, sswu, G, ynum).len xn.poly_eval_horner_scaled( P.x, ZZpow, - h2cIsomapPoly(F.C, G, xnum), + h2cIsomapPoly(F.C, sswu, G, xnum), xnLen ) xd.poly_eval_horner_scaled( P.x, ZZpow, - h2cIsomapPoly(F.C, G, xden), + h2cIsomapPoly(F.C, sswu, G, xden), xnLen ) yn.poly_eval_horner_scaled( P.x, ZZpow, - h2cIsomapPoly(F.C, G, ynum), + h2cIsomapPoly(F.C, sswu, G, ynum), ynLen ) yd.poly_eval_horner_scaled( P.x, ZZpow, - h2cIsomapPoly(F.C, G, yden), + h2cIsomapPoly(F.C, sswu, G, yden), ynLen ) diff --git a/constantine/hash_to_curve/h2c_map_to_isocurve_swu.nim b/constantine/hash_to_curve/h2c_map_to_isocurve_swu.nim index 64df705f3..0d3e987f4 100644 --- a/constantine/hash_to_curve/h2c_map_to_isocurve_swu.nim +++ b/constantine/hash_to_curve/h2c_map_to_isocurve_swu.nim @@ -11,7 +11,8 @@ import ../platforms/abstractions, ../math/config/curves, ../math/[arithmetic, extension_fields], - ../math/curves/zoo_hash_to_curve + ../math/curves/zoo_hash_to_curve, + ./h2c_utilities # ############################################################ # @@ -39,43 +40,6 @@ import # Test vector generator # - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/f7dd3761/poc/sswu_generic.sage -func sgn0(x: Fp): SecretBool = - ## Returns a conventional "sign" for a field element. - ## Even numbers are considered positive by convention - ## and odd negative. - ## - ## https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4.1 - # - # In Montgomery representation - # each number a is represented as aR (mod M) - # with R a Montgomery constant - # hence the LSB of the Montgomery representation - # cannot be used for this use-case. - # - # Another angle is that if M is odd, - # a+M and a have different parity even though they are - # the same modulo M. - let canonical {.noInit.} = x.toBig() - result = canonical.isOdd() - -func sgn0(x: Fp2): SecretBool = - # https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4.1 - # sgn0_m_eq_2(x) - # - # Input: x, an element of GF(p^2). - # Output: 0 or 1. - # - # Steps: - # 1. sign_0 = x_0 mod 2 - # 2. zero_0 = x_0 == 0 - # 3. sign_1 = x_1 mod 2 - # 4. return sign_0 OR (zero_0 AND sign_1) # Avoid short-circuit logic ops - - result = x.c0.sgn0() - let z0 = x.c0.isZero() - let s1 = x.c1.sgn0() - result = result or (z0 and s1) - func invsqrt_if_square[C: static Curve]( r: var Fp2[C], a: Fp2[C]): SecretBool = ## If ``a`` is a square, compute the inverse square root of ``a`` @@ -113,13 +77,13 @@ func invsqrt_if_square[C: static Curve]( result = t3.invsqrt_if_square(t1) # 1/sqrt(a0² - β a1²) # If input is not a square in Fp2, multiply by 1/Z³ - inp.prod(a, h2cConst(C, G2, inv_Z3)) # inp = a / Z³ + inp.prod(a, h2cConst(C, sswu, G2, inv_Z3)) # inp = a / Z³ block: # Adjust t1 and t3 accordingly var t0{.noInit.}: Fp[C] - t0.prod(t1, h2cConst(C, G2, squared_norm_inv_Z3)) # (a0² - β a1²) * ||1/Z³||² + t0.prod(t1, h2cConst(C, sswu, G2, squared_norm_inv_Z3)) # (a0² - β a1²) * ||1/Z³||² t1.ccopy(t0, not result) - t0.prod(t3, h2cConst(C, G2, inv_norm_inv_Z3)) # 1/sqrt(a0² - β a1²) * 1/||1/Z³|| + t0.prod(t3, h2cConst(C, sswu, G2, inv_norm_inv_Z3)) # 1/sqrt(a0² - β a1²) * 1/||1/Z³|| t3.ccopy(t0, not result) inp.ccopy(a, result) @@ -188,42 +152,42 @@ func mapToIsoCurve_sswuG2_opt9mod16*[C: static Curve]( template gxd: untyped = xd3 # x numerators - uu.square(u) # uu = u² - Zuu.prod(uu, h2cConst(C, G2, Z)) # Zuu = Z * uu - tv2.square(Zuu) # tv2 = Zuu² - tv2 += Zuu # tv2 = tv2 + Zuu + uu.square(u) # uu = u² + Zuu.prod(uu, h2cConst(C, sswu, G2, Z)) # Zuu = Z * uu + tv2.square(Zuu) # tv2 = Zuu² + tv2 += Zuu # tv2 = tv2 + Zuu x1n.setOne() - x1n += tv2 # x1n = tv2 + 1 - x1n *= h2cConst(C, G2, Bprime_E2) # x1n = x1n * B' - x2n.prod(Zuu, x1n) # x2n = Zuu * x1n + x1n += tv2 # x1n = tv2 + 1 + x1n *= h2cConst(C, sswu, G2, Bprime_E2) # x1n = x1n * B' + x2n.prod(Zuu, x1n) # x2n = Zuu * x1n # x denumerator - xd.prod(tv2, h2cConst(C, G2, minus_A)) # xd = -A * tv2 - e1 = xd.isZero() # e1 = xd == 0 - xd.ccopy(h2cConst(C, G2, ZmulA), e1) # If xd == 0, set xd = Z*A + xd.prod(tv2, h2cConst(C, sswu, G2, minus_A)) # xd = -A * tv2 + e1 = xd.isZero() # e1 = xd == 0 + xd.ccopy(h2cConst(C, sswu, G2, ZmulA), e1) # If xd == 0, set xd = Z*A # y numerators tv2.square(xd) - gxd.prod(xd, tv2) # gxd = xd³ - tv2.mulCheckSparse(h2CConst(C, G2, Aprime_E2)) + gxd.prod(xd, tv2) # gxd = xd³ + tv2.mulCheckSparse(h2CConst(C, sswu, G2, Aprime_E2)) gx1.square(x1n) - gx1 += tv2 # x1n² + A * xd² - gx1 *= x1n # x1n³ + A * x1n * xd² - tv2.prod(gxd, h2cConst(C, G2, Bprime_E2)) - gx1 += tv2 # gx1 = x1n³ + A * x1n * xd² + B * xd³ - tv4.square(gxd) # tv4 = gxd² - tv2.prod(gx1, gxd) # tv2 = gx1 * gxd - tv4 *= tv2 # tv4 = gx1 * gxd³ + gx1 += tv2 # x1n² + A * xd² + gx1 *= x1n # x1n³ + A * x1n * xd² + tv2.prod(gxd, h2cConst(C, sswu, G2, Bprime_E2)) + gx1 += tv2 # gx1 = x1n³ + A * x1n * xd² + B * xd³ + tv4.square(gxd) # tv4 = gxd² + tv2.prod(gx1, gxd) # tv2 = gx1 * gxd + tv4 *= tv2 # tv4 = gx1 * gxd³ # Start searching for sqrt(gx1) - e2 = y1.invsqrt_if_square(tv4) # y1 = tv4^c1 = (gx1 * gxd³)^((p²-9)/16) - y1 *= tv2 # y1 *= gx1*gxd + e2 = y1.invsqrt_if_square(tv4) # y1 = tv4^c1 = (gx1 * gxd³)^((p²-9)/16) + y1 *= tv2 # y1 *= gx1*gxd y2.prod(y1, uu) y2 *= u # Choose numerators - xn.ccopy(x2n, not e2) # xn = e2 ? x1n : x2n - yn.ccopy(y2, not e2) # yn = e2 ? y1 : y2 + xn.ccopy(x2n, not e2) # xn = e2 ? x1n : x2n + yn.ccopy(y2, not e2) # yn = e2 ? y1 : y2 e1 = sgn0(u) e2 = sgn0(y) @@ -270,28 +234,28 @@ func mapToIsoCurve_sswuG1_opt3mod4*[C: static Curve]( template gxd: untyped = xd3 # x numerators - uu.square(u) # uu = u² - Zuu.prod(uu, h2cConst(C, G1, Z)) # Zuu = Z * uu - tv2.square(Zuu) # tv2 = Zuu² - tv2 += Zuu # tv2 = tv2 + Zuu + uu.square(u) # uu = u² + Zuu.prod(uu, h2cConst(C, sswu, G1, Z)) # Zuu = Z * uu + tv2.square(Zuu) # tv2 = Zuu² + tv2 += Zuu # tv2 = tv2 + Zuu x1n.setOne() - x1n += tv2 # x1n = tv2 + 1 - x1n *= h2cConst(C, G1, Bprime_E1) # x1n = x1n * B' - x2n.prod(Zuu, x1n) # x2n = Zuu * x1n + x1n += tv2 # x1n = tv2 + 1 + x1n *= h2cConst(C, sswu, G1, Bprime_E1) # x1n = x1n * B' + x2n.prod(Zuu, x1n) # x2n = Zuu * x1n # x denumerator - xd.prod(tv2, h2cConst(C, G1, minus_A)) # xd = -A * tv2 - e1 = xd.isZero() # e1 = xd == 0 - xd.ccopy(h2cConst(C, G1, ZmulA), e1) # If xd == 0, set xd = Z*A + xd.prod(tv2, h2cConst(C, sswu, G1, minus_A)) # xd = -A * tv2 + e1 = xd.isZero() # e1 = xd == 0 + xd.ccopy(h2cConst(C, sswu, G1, ZmulA), e1) # If xd == 0, set xd = Z*A # y numerators tv2.square(xd) gxd.prod(xd, tv2) # gxd = xd³ - tv2 *= h2CConst(C, G1, Aprime_E1) + tv2 *= h2cConst(C, sswu, G1, Aprime_E1) gx1.square(x1n) gx1 += tv2 # x1n² + A * xd² gx1 *= x1n # x1n³ + A * x1n * xd² - tv2.prod(gxd, h2cConst(C, G1, Bprime_E1)) + tv2.prod(gxd, h2cConst(C, sswu, G1, Bprime_E1)) gx1 += tv2 # gx1 = x1n³ + A * x1n * xd² + B * xd³ tv4.square(gxd) # tv4 = gxd² tv2.prod(gx1, gxd) # tv2 = gx1 * gxd @@ -300,7 +264,7 @@ func mapToIsoCurve_sswuG1_opt3mod4*[C: static Curve]( # Start searching for sqrt(gx1) e2 = y1.invsqrt_if_square(tv4) # y1 = tv4^c1 = (gx1 * gxd³)^((p²-9)/16) y1 *= tv2 # y1 *= gx1*gxd - y2.prod(y1, h2cConst(C, G1, sqrt_minus_Z3)) + y2.prod(y1, h2cConst(C, sswu, G1, sqrt_minus_Z3)) y2 *= uu y2 *= u diff --git a/constantine/hash_to_curve/h2c_utilities.nim b/constantine/hash_to_curve/h2c_utilities.nim new file mode 100644 index 000000000..7ad9a555b --- /dev/null +++ b/constantine/hash_to_curve/h2c_utilities.nim @@ -0,0 +1,51 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../platforms/abstractions, + ../math/[arithmetic, extension_fields] + +# https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#section-4.1 + +func sgn0*(x: Fp): SecretBool = + ## Returns a conventional "sign" for a field element. + ## Even numbers are considered positive by convention + ## and odd negative. + ## + ## https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4.1 + # + # In Montgomery representation + # each number a is represented as aR (mod M) + # with R a Montgomery constant + # hence the LSB of the Montgomery representation + # cannot be used for this use-case. + # + # Another angle is that if M is odd, + # a+M and a have different parity even though they are + # the same modulo M. + let canonical {.noInit.} = x.toBig() + result = canonical.isOdd() + +func sgn0*(x: Fp2): SecretBool = + # https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4.1 + # sgn0_m_eq_2(x) + # + # Input: x, an element of GF(p^2). + # Output: 0 or 1. + # + # Steps: + # 1. sign_0 = x_0 mod 2 + # 2. zero_0 = x_0 == 0 + # 3. sign_1 = x_1 mod 2 + # 4. return sign_0 OR (zero_0 AND sign_1) # Avoid short-circuit logic ops + + result = x.c0.sgn0() + let z0 = x.c0.isZero() + let s1 = x.c1.sgn0() + result = result or (z0 and s1) \ No newline at end of file diff --git a/constantine/hash_to_curve/hash_to_curve.nim b/constantine/hash_to_curve/hash_to_curve.nim index 1f4f3cafd..baa75d5ad 100644 --- a/constantine/hash_to_curve/hash_to_curve.nim +++ b/constantine/hash_to_curve/hash_to_curve.nim @@ -16,6 +16,7 @@ import ./h2c_hash_to_field, ./h2c_map_to_isocurve_swu, ./h2c_isogeny_maps, + ./h2c_utilities, ../hashes # ############################################################ @@ -34,6 +35,63 @@ import # Map to curve # ---------------------------------------------------------------- +func mapToCurve_svdw[F, G]( + r: var ECP_ShortW_Aff[F, G], + u: F) = + ## Deterministically map a field element u + ## to an elliptic curve point `r` + ## https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#section-6.6.1 + + var + tv1 {.noInit.}, tv2{.noInit.}, tv3{.noInit.}: F + tv4{.noInit.}: F + x1{.noInit.}, x2{.noInit.}: F + gx1{.noInit.}, gx2{.noInit.}: F + + tv1.square(u) + tv1 *= h2cConst(F.C, svdw, G, curve_eq_rhs_Z) + tv2 = tv1 + when F is Fp: + tv2 += F(mres: F.getMontyOne()) + tv1.diff(F(mres: F.getMontyOne()), tv1) + else: + tv2.c0 += Fp[F.F.C](mres: Fp[F.F.C].getMontyOne()) + tv1.c0.diff(Fp[F.F.C](mres: Fp[F.F.C].getMontyOne()), tv1.c0) + tv1.c1.neg() + tv3.prod(tv1, tv2) + tv3.inv() + + tv4.prod(u, tv1) + tv4 *= tv3 + tv4.mulCheckSparse(h2cConst(F.C, svdw, G, z3)) + + x1.diff(h2cConst(F.C, svdw, G, minus_Z_div_2), tv4) + x2.sum(h2cConst(F.C, svdw, G, minus_Z_div_2), tv4) + r.x.square(tv2) + r.x *= tv3 + r.x.square() + r.x *= h2cConst(F.C, svdw, G, z4) + r.x += h2cConst(F.C, svdw, G, Z) + + # x³+ax+b + gx1.curve_eq_rhs(x1, G) + gx2.curve_eq_rhs(x2, G) + + # TODO: faster Legendre symbol. + # We can optimize the 2 legendre symbols + 3 sqrt to + # - either 2 legendre and 1 sqrt + # - or 3 fused legendre+sqrt + let e1 = gx1.isSquare() + let e2 = gx2.isSquare() and not e1 + + r.x.ccopy(x1, e1) + r.x.ccopy(x2, e2) + + r.y.curve_eq_rhs(r.x, G) + r.y.sqrt() + + r.y.cneg(sgn0(u) xor sgn0(r.y)) + func mapToIsoCurve_sswuG1_opt3mod4[F]( r: var ECP_ShortW_Jac[F, G1], u: F) = @@ -72,40 +130,21 @@ func mapToIsoCurve_sswuG2_opt9mod16[F]( r.x.prod(xn, xd) # X = xZ² = xn/xd * xd² = xn*xd r.y.prod(yn, xd3) # Y = yZ³ = yn * xd³ -func mapToCurve[F; G: static Subgroup]( - r: var (ECP_ShortW_Prj[F, G] or ECP_ShortW_Jac[F, G]), - u: F) = - ## Map an element of the +func mapToCurve_svdw_fusedAdd[F; G: static Subgroup]( + r: var ECP_ShortW_Jac[F, G], + u0, u1: F) = + ## Map 2 elements of the ## finite or extension field F ## to an elliptic curve E - - when F.C.getCoefA() * F.C.getCoefB() == 0: - # https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-6.6.3 - # Simplified Shallue-van de Woestijne-Ulas method for AB == 0 - - # 1. Map to E' isogenous to E - when F is Fp and F.C.has_P_3mod4_primeModulus(): - mapToIsoCurve_sswuG1_opt3mod4( - xn, xd, - yn, - u, xd3 - ) - elif F is Fp2 and F.C.has_Psquare_9mod16_primePower(): - # p ≡ 3 (mod 4) => p² ≡ 9 (mod 16) - mapToIsoCurve_sswuG2_opt9mod16( - xn, xd, - yn, - u, xd3 - ) - else: - {.error: "Not implemented".} + ## and add them + var P0{.noInit.}, P1{.noInit.}: ECP_ShortW_Aff[F, G] + P0.mapToCurve_svdw(u0) + P1.mapToCurve_svdw(u1) - # 2. Map from E'1 to E1 - r.h2c_isogeny_map(xn, xd, yn) - else: - {.error: "Not implemented".} + r.fromAffine(P0) + r += P1 -func mapToCurve_fusedAdd[F; G: static Subgroup]( +func mapToCurve_sswu_fusedAdd[F; G: static Subgroup]( r: var ECP_ShortW_Jac[F, G], u0, u1: F) = ## Map 2 elements of the @@ -125,23 +164,24 @@ func mapToCurve_fusedAdd[F; G: static Subgroup]( # unlike the complete projective formulae which heavily depends on it # So we use jacobian coordinates for computation on isogenies. - var P0{.noInit.}, P1{.noInit.}: ECP_ShortW_Jac[F, G] when F.C.getCoefA() * F.C.getCoefB() == 0: # https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-6.6.3 # Simplified Shallue-van de Woestijne-Ulas method for AB == 0 + + var P0{.noInit.}, P1{.noInit.}: ECP_ShortW_Jac[F, G] # 1. Map to E' isogenous to E when F is Fp and F.C.has_P_3mod4_primeModulus(): # 1. Map to E'1 isogenous to E1 P0.mapToIsoCurve_sswuG1_opt3mod4(u0) P1.mapToIsoCurve_sswuG1_opt3mod4(u1) - P0.sum(P0, P1, h2CConst(F.C, G1, Aprime_E1)) + P0.sum(P0, P1, h2CConst(F.C, sswu, G1, Aprime_E1)) elif F is Fp2 and F.C.has_Psquare_9mod16_primePower(): # p ≡ 3 (mod 4) => p² ≡ 9 (mod 16) # 1. Map to E'2 isogenous to E2 P0.mapToIsoCurve_sswuG2_opt9mod16(u0) P1.mapToIsoCurve_sswuG2_opt9mod16(u1) - P0.sum(P0, P1, h2CConst(F.C, G2, Aprime_E2)) + P0.sum(P0, P1, h2CConst(F.C, sswu, G2, Aprime_E2)) else: {.error: "Not implemented".} @@ -153,7 +193,7 @@ func mapToCurve_fusedAdd[F; G: static Subgroup]( # Hash to curve # ---------------------------------------------------------------- -func hashToCurve*[ +func hashToCurve_svdw*[ F; G: static Subgroup; B1, B2, B3: byte|char]( H: type CryptoHash, @@ -192,9 +232,89 @@ func hashToCurve*[ H.shortDomainSepTag(dst, domainSepTag) H.hashToField(k, u, augmentation, message, dst) - output.mapToCurve_fusedAdd(u[0], u[1]) + output.mapToCurve_svdw_fusedAdd(u[0], u[1]) output.clearCofactor() +func hashToCurve_sswu*[ + F; G: static Subgroup; + B1, B2, B3: byte|char]( + H: type CryptoHash, + k: static int, + output: var ECP_ShortW_Jac[F, G], + augmentation: openarray[B1], + message: openarray[B2], + domainSepTag: openarray[B3] + ) = + ## Hash a message to an elliptic curve + ## + ## Arguments: + ## - `Hash` a cryptographic hash function. + ## - `Hash` MAY be a Merkle-Damgaard hash function like SHA-2 + ## - `Hash` MAY be a sponge-based hash function like SHA-3 or BLAKE2 + ## - Otherwise, H MUST be a hash function that has been proved + ## indifferentiable from a random oracle [MRH04] under a reasonable + ## cryptographic assumption. + ## - k the security parameter of the suite in bits (for example 128) + ## - `output`, an elliptic curve point that will be overwritten. + ## - `augmentation`, an optional augmentation to the message. This will be prepended, + ## prior to hashing. + ## This is used for building the "message augmentation" variant of BLS signatures + ## https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-3.2 + ## which requires `CoreSign(SK, PK || message)` + ## and `CoreVerify(PK, PK || message, signature)` + ## - `message` is the message to hash + ## - `domainSepTag` is the protocol domain separation tag (DST). + + var u{.noInit.}: array[2, F] + if domainSepTag.len <= 255: + H.hashToField(k, u, augmentation, message, domainSepTag) + else: + const N = H.type.digestSize() + var dst {.noInit.}: array[N, byte] + H.shortDomainSepTag(dst, domainSepTag) + H.hashToField(k, u, augmentation, message, dst) + + output.mapToCurve_sswu_fusedAdd(u[0], u[1]) + output.clearCofactor() + +func hashToCurve*[ + F; G: static Subgroup; + B1, B2, B3: byte|char]( + H: type CryptoHash, + k: static int, + output: var ECP_ShortW_Jac[F, G], + augmentation: openarray[B1], + message: openarray[B2], + domainSepTag: openarray[B3] + ) {.inline.} = + ## Hash a message to an elliptic curve + ## + ## Arguments: + ## - `Hash` a cryptographic hash function. + ## - `Hash` MAY be a Merkle-Damgaard hash function like SHA-2 + ## - `Hash` MAY be a sponge-based hash function like SHA-3 or BLAKE2 + ## - Otherwise, H MUST be a hash function that has been proved + ## indifferentiable from a random oracle [MRH04] under a reasonable + ## cryptographic assumption. + ## - k the security parameter of the suite in bits (for example 128) + ## - `output`, an elliptic curve point that will be overwritten. + ## - `augmentation`, an optional augmentation to the message. This will be prepended, + ## prior to hashing. + ## This is used for building the "message augmentation" variant of BLS signatures + ## https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-3.2 + ## which requires `CoreSign(SK, PK || message)` + ## and `CoreVerify(PK, PK || message, signature)` + ## - `message` is the message to hash + ## - `domainSepTag` is the protocol domain separation tag (DST). + when F.C == BLS12_381: + hashToCurve_sswu(H, k, output, + augmentation, message, domainSepTag) + elif F.C == BN254_Snarks: + hashToCurve_svdw(H, k, output, + augmentation, message, domainSepTag) + else: + {.error: "Not implemented".} + func hashToCurve*[ F; G: static Subgroup; B1, B2, B3: byte|char]( @@ -204,7 +324,7 @@ func hashToCurve*[ augmentation: openarray[B1], message: openarray[B2], domainSepTag: openarray[B3] - ) = + ) {.inline.} = ## Hash a message to an elliptic curve ## ## Arguments: diff --git a/constantine/math/arithmetic/finite_fields.nim b/constantine/math/arithmetic/finite_fields.nim index aea31d03e..8406c01f7 100644 --- a/constantine/math/arithmetic/finite_fields.nim +++ b/constantine/math/arithmetic/finite_fields.nim @@ -524,11 +524,11 @@ func prod*(r: var FF, a: FF, b: static int) = template mulCheckSparse*(a: var Fp, b: Fp) = ## Multiplication with optimization for sparse inputs - when b.isOne().bool: + when isOne(b).bool: discard - elif b.isZero().bool: + elif isZero(b).bool: a.setZero() - elif b.isMinusOne().bool: + elif isMinusOne(b).bool: a.neg() else: a *= b diff --git a/constantine/math/curves/bls12_381_hash_to_curve_g1.nim b/constantine/math/curves/bls12_381_hash_to_curve_g1.nim index d3360b84b..639e110d4 100644 --- a/constantine/math/curves/bls12_381_hash_to_curve_g1.nim +++ b/constantine/math/curves/bls12_381_hash_to_curve_g1.nim @@ -10,6 +10,26 @@ import ../config/curves, ../io/io_fields +# Hash-to-Curve Shallue-van de Woestijne (SVDW) BLS12_381 G1 map +# ----------------------------------------------------------------- +# Spec: +# - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-F.1 +# This map is slower than SSWU + +const BLS12_381_h2c_svdw_G1_Z* = Fp[BLS12_381].fromHex( + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa8") +const BLS12_381_h2c_svdw_G1_curve_eq_rhs_Z* = Fp[BLS12_381].fromHex( + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa94") +const BLS12_381_h2c_svdw_G1_minus_Z_div_2* = Fp[BLS12_381].fromHex( + "0xd0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd557") +const BLS12_381_h2c_svdw_G1_z3* = Fp[BLS12_381].fromHex( + "0xc855b97020fc106fa9c2de78c9f5d835bbc17c0487afe401c9cd0dcff9fd40bdf8033cd3095a4cd124d794808f153aa") +const BLS12_381_h2c_svdw_G1_z4* = Fp[BLS12_381].fromHex( + "0x6bde8333e50911e85400f0953d13fc61090b4c3a76e5a319f7e6f7f108cb197962c97b3cf28bda11d421c71c71c5bab") + +# Hash-to-Curve Simplified Shallue-van de Woestijne-Ulas (SSWU) map +# ----------------------------------------------------------------- + # Hash-to-Curve map to isogenous BLS12-381 E'1 constants # ----------------------------------------------------------------- # @@ -19,17 +39,17 @@ import # - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#section-8.8.2 # - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/f7dd3761/poc/sswu_opt_3mod4.sage#L126-L132 -const BLS12_381_h2c_G1_Aprime_E1* = Fp[BLS12_381].fromHex( +const BLS12_381_h2c_sswu_G1_Aprime_E1* = Fp[BLS12_381].fromHex( "0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d") -const BLS12_381_h2c_G1_Bprime_E1* = Fp[BLS12_381].fromHex( +const BLS12_381_h2c_sswu_G1_Bprime_E1* = Fp[BLS12_381].fromHex( "0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0") -const BLS12_381_h2c_G1_Z* = Fp[BLS12_381].fromHex( +const BLS12_381_h2c_sswu_G1_Z* = Fp[BLS12_381].fromHex( "0xb") -const BLS12_381_h2c_G1_minus_A* = Fp[BLS12_381].fromHex( +const BLS12_381_h2c_sswu_G1_minus_A* = Fp[BLS12_381].fromHex( "0x19eccb5195c6fd570db26db379de6354b38cb3316f96ac168e483a8606d8747786189071107306805d0ad7f7d2a75e8e") -const BLS12_381_h2c_G1_ZmulA* = Fp[BLS12_381].fromHex( +const BLS12_381_h2c_sswu_G1_ZmulA* = Fp[BLS12_381].fromHex( "0xdf088f08f205e3a3857e1ea7b2289d9a148b96ab3e694151fe89284e4d926a8e55cb15e9aab878fe7db859f2cb453f") -const BLS12_381_h2c_G1_sqrt_minus_Z3* = Fp[BLS12_381].fromHex( +const BLS12_381_h2c_sswu_G1_sqrt_minus_Z3* = Fp[BLS12_381].fromHex( "0x3d689d1e0e762cef9f2bec6130316806b4c80eda6fc10ce77ae83eab1ea8b8b8a407c9c6db195e06f2dbeabc2baeff5") # Hash-to-Curve 11-isogeny map BLS12-381 E'1 constants @@ -38,7 +58,7 @@ const BLS12_381_h2c_G1_sqrt_minus_Z3* = Fp[BLS12_381].fromHex( # The polynomials map a point (x', y') on the isogenous curve E'1 # to (x, y) on E1, represented as (xnum/xden, y' * ynum/yden) -const BLS12_381_h2c_G1_isogeny_map_xnum* = [ +const BLS12_381_h2c_sswu_G1_isogeny_map_xnum* = [ # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ @@ -91,7 +111,7 @@ const BLS12_381_h2c_G1_isogeny_map_xnum* = [ "0x6e08c248e260e70bd1e962381edee3d31d79d7e22c837bc23c0bf1bc24c6b68c24b1b80b64d391fa9c8ba2e8ba2d229" ) ] -const BLS12_381_h2c_G1_isogeny_map_xden* = [ +const BLS12_381_h2c_sswu_G1_isogeny_map_xden* = [ # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ @@ -140,7 +160,7 @@ const BLS12_381_h2c_G1_isogeny_map_xden* = [ "0x1" ) ] -const BLS12_381_h2c_G1_isogeny_map_ynum* = [ +const BLS12_381_h2c_sswu_G1_isogeny_map_ynum* = [ # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ @@ -209,7 +229,7 @@ const BLS12_381_h2c_G1_isogeny_map_ynum* = [ "0x15e6be4e990f03ce4ea50b3b42df2eb5cb181d8f84965a3957add4fa95af01b2b665027efec01c7704b456be69c8b604" ), ] -const BLS12_381_h2c_G1_isogeny_map_yden* = [ +const BLS12_381_h2c_sswu_G1_isogeny_map_yden* = [ # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ diff --git a/constantine/math/curves/bls12_381_hash_to_curve_g2.nim b/constantine/math/curves/bls12_381_hash_to_curve_g2.nim index 4eae91f57..e40385743 100644 --- a/constantine/math/curves/bls12_381_hash_to_curve_g2.nim +++ b/constantine/math/curves/bls12_381_hash_to_curve_g2.nim @@ -10,6 +10,37 @@ import ../config/curves, ../io/[io_fields, io_extfields] +# Hash-to-Curve Shallue-van de Woestijne (SVDW) BLS12_381 G2 map +# ----------------------------------------------------------------- +# Spec: +# - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-F.1 +# This map is slower than SSWU + +const BLS12_381_h2c_svdw_G2_Z* = Fp2[BLS12_381].fromHex( + "0x0", + "0x1" +) +const BLS12_381_h2c_svdw_G2_curve_eq_rhs_Z* = Fp2[BLS12_381].fromHex( + "0x4", + "0x3" +) +const BLS12_381_h2c_svdw_G2_minus_Z_div_2* = Fp2[BLS12_381].fromHex( + "0x0", + "0xd0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd555" +) +const BLS12_381_h2c_svdw_G2_z3* = Fp2[BLS12_381].fromHex( + "0xbdd5ce0da1f67a74801737ad294eb2e8792dfaff3b97d438795e114a0bf9b0d448554f8291ae6ae6f9aad7ac97e0842", + "0x154a803c6f0a66f3f4bd964d1db96c49c5807ce89e413640c752821cda0b2d1c809f1c51d940f78f4bdd8f28edd47488" +) +const BLS12_381_h2c_svdw_G2_z4* = Fp2[BLS12_381].fromHex( + "0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc722", + "0x4" +) + + +# Hash-to-Curve Simplified Shallue-van de Woestijne-Ulas (SSWU) map +# ----------------------------------------------------------------- + # Hash-to-Curve map to isogenous BLS12-381 E'2 constants # ----------------------------------------------------------------- # @@ -19,33 +50,33 @@ import # - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.8.2 # - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/f7dd3761/poc/sswu_opt_9mod16.sage#L142-L148 -const BLS12_381_h2c_G2_Aprime_E2* = Fp2[BLS12_381].fromHex( # 240𝑖 +const BLS12_381_h2c_sswu_G2_Aprime_E2* = Fp2[BLS12_381].fromHex( # 240𝑖 "0x0", "0xf0" ) -const BLS12_381_h2c_G2_Bprime_E2* = Fp2[BLS12_381].fromHex( # 1012 * (1 + 𝑖) +const BLS12_381_h2c_sswu_G2_Bprime_E2* = Fp2[BLS12_381].fromHex( # 1012 * (1 + 𝑖) "0x3f4", "0x3f4" ) -const BLS12_381_h2c_G2_Z* = Fp2[BLS12_381].fromHex( # -(2 + 𝑖) +const BLS12_381_h2c_sswu_G2_Z* = Fp2[BLS12_381].fromHex( # -(2 + 𝑖) "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9", "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa" ) -const BLS12_381_h2c_G2_minus_A* = Fp2[BLS12_381].fromHex( # -240𝑖 +const BLS12_381_h2c_sswu_G2_minus_A* = Fp2[BLS12_381].fromHex( # -240𝑖 "0x0", "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9bb" ) -const BLS12_381_h2c_G2_ZmulA* = Fp2[BLS12_381].fromHex( # Z*A = 240-480𝑖 +const BLS12_381_h2c_sswu_G2_ZmulA* = Fp2[BLS12_381].fromHex( # Z*A = 240-480𝑖 "0xf0", "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8cb" ) -const BLS12_381_h2c_G2_inv_Z3* = Fp2[BLS12_381].fromHex( # 1/Z³ +const BLS12_381_h2c_sswu_G2_inv_Z3* = Fp2[BLS12_381].fromHex( # 1/Z³ "0xec5373b4fc387140dfd46af348f55e2ca7901ef5b371b085d6da6bdbb39819171ad78d43fdbbe76a0f1189374bc3a07", "0x9c70eed928c402db9efc63a4a7484928607fdacdea2acefe74fcc1fd9af14de7a1fe76c0d6d687295ce78d4fdf39630" ) -const BLS12_381_h2c_G2_squared_norm_inv_Z3* = Fp[BLS12_381].fromHex( # ||1/Z³||² +const BLS12_381_h2c_sswu_G2_squared_norm_inv_Z3* = Fp[BLS12_381].fromHex( # ||1/Z³||² "0x59ded5774de2fc31e8f3083875e2b7a4cff24cacc26fbdb84e195f19dbbba49567f439538bc20c48c86f3b645a1b852") -const BLS12_381_h2c_G2_inv_norm_inv_Z3* = Fp[BLS12_381].fromHex( # 1/||1/Z³|| +const BLS12_381_h2c_sswu_G2_inv_norm_inv_Z3* = Fp[BLS12_381].fromHex( # 1/||1/Z³|| "0x810e5a23cbb86fd12ded1af502287a397ed25c1d6fe0444e38c48e9c7ddb3c27cfebdd464e90f201fda0eb6983f2533") @@ -55,7 +86,7 @@ const BLS12_381_h2c_G2_inv_norm_inv_Z3* = Fp[BLS12_381].fromHex( # 1/||1/Z³|| # The polynomials map a point (x', y') on the isogenous curve E'2 # to (x, y) on E2, represented as (xnum/xden, y' * ynum/yden) -const BLS12_381_h2c_G2_isogeny_map_xnum* = [ +const BLS12_381_h2c_sswu_G2_isogeny_map_xnum* = [ # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ @@ -80,7 +111,7 @@ const BLS12_381_h2c_G2_isogeny_map_xnum* = [ "0x0" ) ] -const BLS12_381_h2c_G2_isogeny_map_xden* = [ +const BLS12_381_h2c_sswu_G2_isogeny_map_xden* = [ # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ @@ -100,7 +131,7 @@ const BLS12_381_h2c_G2_isogeny_map_xden* = [ "0x0" ) ] -const BLS12_381_h2c_G2_isogeny_map_ynum* = [ +const BLS12_381_h2c_sswu_G2_isogeny_map_ynum* = [ # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ @@ -125,7 +156,7 @@ const BLS12_381_h2c_G2_isogeny_map_ynum* = [ "0x0" ) ] -const BLS12_381_h2c_G2_isogeny_map_yden* = [ +const BLS12_381_h2c_sswu_G2_isogeny_map_yden* = [ # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ diff --git a/constantine/math/curves/bn254_nogami_subgroups.nim b/constantine/math/curves/bn254_nogami_subgroups.nim index 346c29147..2f5050438 100644 --- a/constantine/math/curves/bn254_nogami_subgroups.nim +++ b/constantine/math/curves/bn254_nogami_subgroups.nim @@ -81,7 +81,7 @@ func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp2[BN254_Nogami], G2]) {.inl # BN G1 # ------------------------------------------------------------ -func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp[BN254_Nogami], G1]) {.inline.} = +func clearCofactorFast*(P: var ECP_ShortW[Fp[BN254_Nogami], G1]) {.inline.} = ## Clear the cofactor of BN254_Nogami G1 ## BN curves have a prime order r hence all points on curve are in G1 ## Hence this is a no-op @@ -93,7 +93,7 @@ func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp[BN254_Nogami], G1]) {.inline.} # Implementation # Fuentes-Castaneda et al, "Fast Hashing to G2 on Pairing-Friendly Curves", https://doi.org/10.1007/978-3-642-28496-0_25* -func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp2[BN254_Nogami], G2]) {.inline.} = +func clearCofactorFast*(P: var ECP_ShortW[Fp2[BN254_Nogami], G2]) {.inline.} = ## Clear the cofactor of BN254_Nogami G2 ## Optimized using endomorphisms ## P' → [x]P + [3x]ψ(P) + [x]ψ²(P) + ψ³(P) @@ -115,7 +115,7 @@ func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp2[BN254_Nogami], G2]) {.inline.} # # ############################################################ -func isInSubgroup*(P: ECP_ShortW_Prj[Fp[BN254_Nogami], G1]): SecretBool {.inline.} = +func isInSubgroup*(P: ECP_ShortW[Fp[BN254_Nogami], G1]): SecretBool {.inline.} = ## Returns true if P is in G1 subgroup, i.e. P is a point of order r. ## A point may be on a curve but not on the prime order r subgroup. ## Not checking subgroup exposes a protocol to small subgroup attacks. @@ -124,7 +124,7 @@ func isInSubgroup*(P: ECP_ShortW_Prj[Fp[BN254_Nogami], G1]): SecretBool {.inline ## Warning ⚠: Assumes that P is on curve return CtTrue -func isInSubgroup*(P: ECP_ShortW_Prj[Fp2[BN254_Nogami], G2]): SecretBool = +func isInSubgroup*(P: ECP_ShortW_Jac[Fp2[BN254_Nogami], G2] or ECP_ShortW_Prj[Fp2[BN254_Nogami], G2]): SecretBool = ## Returns true if P is in G2 subgroup, i.e. P is a point of order r. ## A point may be on a curve but not on the prime order r subgroup. ## Not checking subgroup exposes a protocol to small subgroup attacks. @@ -140,7 +140,7 @@ func isInSubgroup*(P: ECP_ShortW_Prj[Fp2[BN254_Nogami], G2]): SecretBool = # p the prime modulus: 36u⁴ + 36u³ + 24u² + 6u + 1 # r the prime order: 36u⁴ + 36u³ + 18u² + 6u + 1 # t the trace: 6u² + 1 - var t0{.noInit.}, t1{.noInit.}: ECP_ShortW_Prj[Fp2[BN254_Nogami], G2] + var t0{.noInit.}, t1{.noInit.}: typeof(P) t0.pow_BN254_Nogami_u(P) # [u]P t1.pow_BN254_Nogami_u(t0) # [u²]P @@ -150,4 +150,14 @@ func isInSubgroup*(P: ECP_ShortW_Prj[Fp2[BN254_Nogami], G2]): SecretBool = t1.frobenius_psi(P) # ψ(P) - return t0 == t1 \ No newline at end of file + return t0 == t1 + +func isInSubgroup*(P: ECP_ShortW_Aff[Fp2[BN254_Nogami], G2]): SecretBool = + ## Returns true if P is in 𝔾2 subgroup, i.e. P is a point of order r. + ## A point may be on a curve but not on the prime order r subgroup. + ## Not checking subgroup exposes a protocol to small subgroup attacks. + ## + ## Warning ⚠: Assumes that P is on curve + var t{.noInit.}: ECP_ShortW_Jac[Fp2[BN254_Nogami], G2] + t.fromAffine(P) + return t.isInSubgroup() \ No newline at end of file diff --git a/constantine/math/curves/bn254_snarks_hash_to_curve_g1.nim b/constantine/math/curves/bn254_snarks_hash_to_curve_g1.nim new file mode 100644 index 000000000..af6892779 --- /dev/null +++ b/constantine/math/curves/bn254_snarks_hash_to_curve_g1.nim @@ -0,0 +1,27 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + ../config/curves, + ../io/io_fields + +# Hash-to-Curve Shallue-van de Woestijne BN254_Snarks G1 map +# ----------------------------------------------------------------- +# Spec: +# - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-F.1 + +const BN254_Snarks_h2c_svdw_G1_Z* = Fp[BN254_Snarks].fromHex( + "0x1") +const BN254_Snarks_h2c_svdw_G1_curve_eq_rhs_Z* = Fp[BN254_Snarks].fromHex( + "0x4") +const BN254_Snarks_h2c_svdw_G1_minus_Z_div_2* = Fp[BN254_Snarks].fromHex( + "0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3") +const BN254_Snarks_h2c_svdw_G1_z3* = Fp[BN254_Snarks].fromHex( + "0x16789af3a83522eb353c98fc6b36d713d5d8d1cc5dffffffa") +const BN254_Snarks_h2c_svdw_G1_z4* = Fp[BN254_Snarks].fromHex( + "0x10216f7ba065e00de81ac1e7808072c9dd2b2385cd7b438469602eb24829a9bd") diff --git a/constantine/math/curves/bn254_snarks_hash_to_curve_g2.nim b/constantine/math/curves/bn254_snarks_hash_to_curve_g2.nim new file mode 100644 index 000000000..da235b623 --- /dev/null +++ b/constantine/math/curves/bn254_snarks_hash_to_curve_g2.nim @@ -0,0 +1,37 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + ../config/curves, + ../io/[io_fields, io_extfields] + +# Hash-to-Curve Shallue-van de Woestijne BN254_Snarks G2 map +# ----------------------------------------------------------------- +# Spec: +# - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-F.1 + +const BN254_Snarks_h2c_svdw_G2_Z* = Fp2[BN254_Snarks].fromHex( + "0x0", + "0x1" +) +const BN254_Snarks_h2c_svdw_G2_curve_eq_rhs_Z* = Fp2[BN254_Snarks].fromHex( + "0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5", + "0x9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d1" +) +const BN254_Snarks_h2c_svdw_G2_minus_Z_div_2* = Fp2[BN254_Snarks].fromHex( + "0x0", + "0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3" +) +const BN254_Snarks_h2c_svdw_G2_z3* = Fp2[BN254_Snarks].fromHex( + "0x1248cccf0e2a72383dec3a1621130a65c0eb5d826ca664d3f4fce46f983efce6", + "0x220de2a91cc408cf05ff76bf76fb88febaac1173cab9c8ebc03c7f9dc5569f10" +) +const BN254_Snarks_h2c_svdw_G2_z4* = Fp2[BN254_Snarks].fromHex( + "0x294f62301de5ae301a38098f4f5570e5bfc5e456aa54a6aa847fafc89357f76f", + "0xc96f95a3ebfe711190ea3d3e76a7f0df14d60686e6cb1930d8fc08b259726c" +) diff --git a/constantine/math/curves/bn254_snarks_subgroups.nim b/constantine/math/curves/bn254_snarks_subgroups.nim index 60fdd4ca9..1c41bbdea 100644 --- a/constantine/math/curves/bn254_snarks_subgroups.nim +++ b/constantine/math/curves/bn254_snarks_subgroups.nim @@ -146,7 +146,7 @@ func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp2[BN254_Snarks], G2]) {.inl # BN G1 # ------------------------------------------------------------ -func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp[BN254_Snarks], G1]) {.inline.} = +func clearCofactorFast*(P: var ECP_ShortW[Fp[BN254_Snarks], G1]) {.inline.} = ## Clear the cofactor of BN254_Snarks G1 ## BN curves have a prime order r hence all points on curve are in G1 ## Hence this is a no-op @@ -158,7 +158,7 @@ func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp[BN254_Snarks], G1]) {.inline.} # Implementation # Fuentes-Castaneda et al, "Fast Hashing to G2 on Pairing-Friendly Curves", https://doi.org/10.1007/978-3-642-28496-0_25* -func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp2[BN254_Snarks], G2]) {.inline.} = +func clearCofactorFast*(P: var ECP_ShortW[Fp2[BN254_Snarks], G2]) {.inline.} = ## Clear the cofactor of BN254_Snarks G2 ## Optimized using endomorphisms ## P' → [x]P + [3x]ψ(P) + [x]ψ²(P) + ψ³(P) @@ -180,7 +180,7 @@ func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp2[BN254_Snarks], G2]) {.inline.} # # ############################################################ -func isInSubgroup*(P: ECP_ShortW_Prj[Fp[BN254_Snarks], G1]): SecretBool {.inline.} = +func isInSubgroup*(P: ECP_ShortW[Fp[BN254_Snarks], G1]): SecretBool {.inline.} = ## Returns true if P is in G1 subgroup, i.e. P is a point of order r. ## A point may be on a curve but not on the prime order r subgroup. ## Not checking subgroup exposes a protocol to small subgroup attacks. @@ -189,7 +189,7 @@ func isInSubgroup*(P: ECP_ShortW_Prj[Fp[BN254_Snarks], G1]): SecretBool {.inline ## Warning ⚠: Assumes that P is on curve return CtTrue -func isInSubgroup*(P: ECP_ShortW_Prj[Fp2[BN254_Snarks], G2]): SecretBool = +func isInSubgroup*(P: ECP_ShortW_Jac[Fp2[BN254_Snarks], G2] or ECP_ShortW_Prj[Fp2[BN254_Snarks], G2]): SecretBool = ## Returns true if P is in G2 subgroup, i.e. P is a point of order r. ## A point may be on a curve but not on the prime order r subgroup. ## Not checking subgroup exposes a protocol to small subgroup attacks. @@ -205,7 +205,7 @@ func isInSubgroup*(P: ECP_ShortW_Prj[Fp2[BN254_Snarks], G2]): SecretBool = # p the prime modulus: 36u⁴ + 36u³ + 24u² + 6u + 1 # r the prime order: 36u⁴ + 36u³ + 18u² + 6u + 1 # t the trace: 6u² + 1 - var t0{.noInit.}, t1{.noInit.}: ECP_ShortW_Prj[Fp2[BN254_Snarks], G2] + var t0{.noInit.}, t1{.noInit.}: typeof(P) t0.pow_bn254_snarks_u(P) # [u]P t1.pow_bn254_snarks_u(t0) # [u²]P @@ -215,4 +215,14 @@ func isInSubgroup*(P: ECP_ShortW_Prj[Fp2[BN254_Snarks], G2]): SecretBool = t1.frobenius_psi(P) # ψ(P) - return t0 == t1 \ No newline at end of file + return t0 == t1 + +func isInSubgroup*(P: ECP_ShortW_Aff[Fp2[BN254_Snarks], G2]): SecretBool = + ## Returns true if P is in 𝔾2 subgroup, i.e. P is a point of order r. + ## A point may be on a curve but not on the prime order r subgroup. + ## Not checking subgroup exposes a protocol to small subgroup attacks. + ## + ## Warning ⚠: Assumes that P is on curve + var t{.noInit.}: ECP_ShortW_Jac[Fp2[BN254_Snarks], G2] + t.fromAffine(P) + return t.isInSubgroup() \ No newline at end of file diff --git a/constantine/math/curves/zoo_hash_to_curve.nim b/constantine/math/curves/zoo_hash_to_curve.nim index 04913b219..d5e27c238 100644 --- a/constantine/math/curves/zoo_hash_to_curve.nim +++ b/constantine/math/curves/zoo_hash_to_curve.nim @@ -11,19 +11,23 @@ import ../config/curves, ../elliptic/ec_shortweierstrass_affine, ./bls12_381_hash_to_curve_g1, - ./bls12_381_hash_to_curve_g2 + ./bls12_381_hash_to_curve_g2, + ./bn254_snarks_hash_to_curve_g1, + ./bn254_snarks_hash_to_curve_g2 {.experimental: "dynamicBindSym".} -macro h2cConst*(C: static Curve, group, value: untyped): untyped = +macro h2cConst*(C: static Curve, mapping: untyped, group: static Subgroup, value: untyped): untyped = ## Get a Hash-to-Curve constant ## for mapping to a elliptic curve group (G1 or G2) - return bindSym($C & "_h2c_" & $group & "_" & $value) + return bindSym($C & "_h2c_" & $mapping & "_" & $group & "_" & $value) macro h2cIsomapPoly*(C: static Curve, + mapping: untyped, group: static Subgroup, value: untyped): untyped = ## Get an isogeny map polynomial ## for mapping to a elliptic curve group (G1 or G2) return bindSym($C & "_h2c_" & + $mapping & "_" & $group & "_isogeny_map_" & $value) diff --git a/constantine/math/elliptic/ec_shortweierstrass_affine.nim b/constantine/math/elliptic/ec_shortweierstrass_affine.nim index 155498c2f..c3b958603 100644 --- a/constantine/math/elliptic/ec_shortweierstrass_affine.nim +++ b/constantine/math/elliptic/ec_shortweierstrass_affine.nim @@ -66,6 +66,8 @@ func curve_eq_rhs*[F](y2: var F, x: F, G: static Subgroup) = var t{.noInit.}: F t.square(x) + when F.C.getCoefA() != 0: + t += F.C.getCoefA() t *= x when G == G1: @@ -78,11 +80,6 @@ func curve_eq_rhs*[F](y2: var F, x: F, G: static Subgroup) = else: y2.sum(F.C.getCoefB_G2(), t) - when F.C.getCoefA() != 0: - t = x - t *= F.C.getCoefA() - y2 += t - func isOnCurve*[F](x, y: F, G: static Subgroup): SecretBool = ## Returns true if the (x, y) coordinates ## represents a point of the elliptic curve diff --git a/constantine/math/extension_fields/square_root_fp2.nim b/constantine/math/extension_fields/square_root_fp2.nim index 88e1984c8..3e5b4b2fe 100644 --- a/constantine/math/extension_fields/square_root_fp2.nim +++ b/constantine/math/extension_fields/square_root_fp2.nim @@ -202,3 +202,11 @@ func sqrt_if_square*(a: var Fp2): SecretBool = result = a.sqrt_if_square_generic() else: result = a.sqrt_if_square_opt() + +func sqrt*(a: var Fp2) = + ## If ``a`` is a square, compute the square root of ``a`` + ## if not, ``a`` is undefined. + ## + ## The square root, if it exist is multivalued, + ## i.e. both x² == (-x)² + discard a.sqrt_if_square() \ No newline at end of file diff --git a/sage/derive_hash_to_curve.sage b/sage/derive_hash_to_curve.sage index bbfafa5db..5c20a905a 100644 --- a/sage/derive_hash_to_curve.sage +++ b/sage/derive_hash_to_curve.sage @@ -87,7 +87,62 @@ def dump_poly(name, poly, field, curve): result += ']' return result -# Isogenies +ZZR = PolynomialRing(ZZ, name='XX') +def sgn0(x): + """ + Returns 1 if x is 'negative' (little-endian sense), else 0. + """ + degree = x.parent().degree() + if degree == 1: + # not a field extension + xi_values = (ZZ(x),) + else: + # field extension + xi_values = ZZR(x) # extract vector repr of field element (faster than x._vector_()) + sign = 0 + zero = 1 + # compute the sign in constant time + for i in range(0, degree): + zz_xi = xi_values[i] + # sign of this digit + sign_i = zz_xi % 2 + zero_i = zz_xi == 0 + # update sign and zero + sign = sign | (zero & sign_i) + zero = zero & zero_i + return sign + +# Generic Shallue-van de Woestijne map +# --------------------------------------------------------- + +def find_z_svdw(F, A, B): + """ + https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-H.1 + Arguments: + - F, a field object, e.g., F = GF(2^521 - 1) + - A and B, the coefficients of the curve y^2 = x^3 + A * x + B + """ + g = lambda x: F(x)^3 + F(A) * F(x) + F(B) + h = lambda Z: -(F(3) * Z^2 + F(4) * A) / (F(4) * g(Z)) + ctr = F.gen() + while True: + for Z_cand in (F(ctr), F(-ctr)): + if g(Z_cand) == F(0): + # Criterion 1: g(Z) != 0 in F. + continue + if h(Z_cand) == F(0): + # Criterion 2: -(3 * Z^2 + 4 * A) / (4 * g(Z)) != 0 in F. + continue + if not h(Z_cand).is_square(): + # Criterion 3: -(3 * Z^2 + 4 * A) / (4 * g(Z)) is square in F. + continue + if g(Z_cand).is_square() or g(-Z_cand / F(2)).is_square(): + # Criterion 4: At least one of g(Z) and g(-Z / 2) is square in F. + return Z_cand + ctr += 1 + + +# Isogenies for Simplified Shallue-van de Woestijne-Ulas map # --------------------------------------------------------- def find_iso(E): @@ -100,14 +155,14 @@ def find_iso(E): isos = [i for i in isd.isogenies_prime_degree(E, p_test) if i.codomain().j_invariant() not in (0, 1728) ] if len(isos) > 0: - print(f'Found {len(isos)} isogenous curves of degree {p_test}') + print(f'✔️✔️✔️ Found {len(isos)} isogenous curves of degree {p_test}') return isos[0].dual() - print(f'Found no isogenies') + print(f'⚠️⚠️⚠️ Found no isogenies for {E}') return None def find_z_sswu(F, A, B): """ - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#ref-SAGE + https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-H.2 Arguments: - F, a field object, e.g., F = GF(2^521 - 1) - A and B, the coefficients of the curve equation y² = x³ + A * x + B @@ -187,13 +242,21 @@ def search_isogeny(curve_name, curve_config): else: raise ValueError('E2 must be a D_Twist or M_Twist but found ' + twist) - # Isogenies: iso_G1 = find_iso(E1) + iso_G2 = find_iso(E2) + + if iso_G1 == None or iso_G2 == None: + # TODO: case when G1 has a cheap isogeny but G2 does not + Z_G1 = find_z_svdw(Fp, A, B) + print(f"Z G1 (svdw): {Z_G1}") + Z_G2 = find_z_svdw(Fp2, A, G2B) + print(f"Z G2 (svdw): {fp2_to_hex(Z_G2)}") + return + a_G1 = iso_G1.domain().a4() b_G1 = iso_G1.domain().a6() - iso_G2 = find_iso(E2) a_G2 = iso_G2.domain().a4() b_G2 = iso_G2.domain().a6() @@ -204,12 +267,12 @@ def search_isogeny(curve_name, curve_config): print(f"{curve_name} G1 - isogeny of degree {iso_G1.degree()} with eq y² = x³ + A'x + B':") print(f" A': 0x{Integer(a_G1).hex()}") print(f" B': 0x{Integer(b_G1).hex()}") - print(f" Z: {Z_G1}") + print(f" Z (sswu): {Z_G1}") print(f"{curve_name} G2 - isogeny of degree {iso_G2.degree()} with eq y² = x³ + A'x + B':") print(f" A': {fp2_to_hex(a_G2)}") print(f" B': {fp2_to_hex(b_G2)}") - print(f" Z: {fp2_to_hex(Z_G2)}") + print(f" Z (sswu): {fp2_to_hex(Z_G2)}") # BLS12-381 G1 # --------------------------------------------------------- @@ -223,7 +286,6 @@ def genBLS12381G1_H2C_constants(curve_config): # ------------------------------------------ p = curve_config[curve_name]['field']['modulus'] Fp = GF(p) - K. = PolynomialRing(Fp) # ------------------------------------------ # Hash to curve isogenous curve parameters @@ -503,6 +565,140 @@ def genBLS12381G2_H2C_isogeny_map(curve_config): return buf +def genSVDW_H2C_G1_constants(curve, curve_config, Z): + p = curve_config[curve]['field']['modulus'] + a = curve_config[curve]['curve']['a'] + b = curve_config[curve]['curve']['b'] + + Fp = GF(p) + + print(f'\n----> Hash-to-Curve Shallue-van de Woestijne {curve} G1 map <----\n') + buf = inspect.cleandoc(f""" + # Hash-to-Curve Shallue-van de Woestijne {curve} G1 map + # ----------------------------------------------------------------- + # Spec: + # - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-F.1 + """) + buf += '\n\n' + + c1 = Z^3 + a*Z + b + c2 = -Z/2 + t = 3 * Z^2 + 4 * a + c3 = sqrt(-c1 * t) + if sgn0(c3) == 1: + c3 = -c3 + c4 = -4 * c1 / t + + buf += f'const {curve}_h2c_svdw_G1_Z* = ' + buf += field_to_nim(Z, 'Fp', curve) + buf += '\n' + + buf += f'const {curve}_h2c_svdw_G1_curve_eq_rhs_Z* = ' + buf += field_to_nim(c1, 'Fp', curve) + buf += '\n' + + buf += f'const {curve}_h2c_svdw_G1_minus_Z_div_2* = ' + buf += field_to_nim(c2, 'Fp', curve) + buf += '\n' + + buf += f'const {curve}_h2c_svdw_G1_z3* = ' + buf += field_to_nim(c3, 'Fp', curve) + buf += '\n' + + buf += f'const {curve}_h2c_svdw_G1_z4* = ' + buf += field_to_nim(c4, 'Fp', curve) + buf += '\n' + + return buf + +def genSVDW_H2C_G2_constants(curve, curve_config, Z): + p = curve_config[curve]['field']['modulus'] + a = curve_config[curve]['curve']['a'] + b = curve_config[curve]['curve']['b'] + + embedding_degree = curve_config[curve]['tower']['embedding_degree'] + twist_degree = curve_config[curve]['tower']['twist_degree'] + twist = curve_config[curve]['tower']['twist'] + + G2_field_degree = embedding_degree // twist_degree + G2_field = f'Fp{G2_field_degree}' if G2_field_degree > 1 else 'Fp' + + if G2_field_degree == 2: + non_residue_fp = curve_config[curve]['tower']['QNR_Fp'] + elif G2_field_degree == 1: + if twist_degree == 6: + # Only for complete serialization + non_residue_fp = curve_config[curve]['tower']['SNR_Fp'] + else: + raise NotImplementedError() + else: + raise NotImplementedError() + + Fp = GF(p) + K. = PolynomialRing(Fp) + + if G2_field == 'Fp2': + Fp2. = Fp.extension(u^2 - non_residue_fp) + G2F = Fp2 + if twist_degree == 6: + non_residue_twist = curve_config[curve]['tower']['SNR_Fp2'] + else: + raise NotImplementedError() + elif G2_field == 'Fp': + G2F = Fp + if twist_degree == 6: + non_residue_twist = curve_config[curve]['tower']['SNR_Fp'] + else: + raise NotImplementedError() + else: + raise NotImplementedError() + + if twist == 'D_Twist': + G2B = b/G2F(non_residue_twist) + elif twist == 'M_Twist': + G2B = b*G2F(non_residue_twist) + else: + raise ValueError('E2 must be a D_Twist or M_Twist but found ' + twist) + + print(f'\n----> Hash-to-Curve Shallue-van de Woestijne {curve} G2 map <----\n') + buf = inspect.cleandoc(f""" + # Hash-to-Curve Shallue-van de Woestijne {curve} G2 map + # ----------------------------------------------------------------- + # Spec: + # - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-F.1 + """) + buf += '\n\n' + + c1 = Z^3 + a*Z + G2B + c2 = -Z/2 + t = 3 * Z^2 + 4 * a + c3 = sqrt(-c1 * t) + if sgn0(c3) == 1: + c3 = -c3 + c4 = -4 * c1 / t + + buf += f'const {curve}_h2c_svdw_G2_Z* = ' + buf += field_to_nim(Z, G2_field, curve) + buf += '\n' + + buf += f'const {curve}_h2c_svdw_G2_curve_eq_rhs_Z* = ' + buf += field_to_nim(c1, G2_field, curve) + buf += '\n' + + buf += f'const {curve}_h2c_svdw_G2_minus_Z_div_2* = ' + buf += field_to_nim(c2, G2_field, curve) + buf += '\n' + + buf += f'const {curve}_h2c_svdw_G2_z3* = ' + buf += field_to_nim(c3, G2_field, curve) + buf += '\n' + + buf += f'const {curve}_h2c_svdw_G2_z4* = ' + buf += field_to_nim(c4, G2_field, curve) + buf += '\n' + + return buf + # CLI # --------------------------------------------------------- @@ -552,6 +748,52 @@ if __name__ == "__main__": h2c += '\n\n' h2c += genBLS12381G2_H2C_isogeny_map(Curves) + with open(f'{curve.lower()}_hash_to_curve_g2.nim', 'w') as f: + f.write(copyright()) + f.write('\n\n') + + f.write(inspect.cleandoc(""" + import + ../config/curves, + ../io/[io_fields, io_extfields] + """)) + + f.write('\n\n') + f.write(h2c) + + print(f'Successfully created {curve.lower()}_hash_to_curve_g2.nim') + + elif curve == 'BN254_Snarks' and group_or_iso == 'G1': + p = Curves['BN254_Snarks']['field']['modulus'] + + Z = GF(p)(1) + h2c = genSVDW_H2C_G1_constants('BN254_Snarks', Curves, Z) + + with open(f'{curve.lower()}_hash_to_curve_g1.nim', 'w') as f: + f.write(copyright()) + f.write('\n\n') + + f.write(inspect.cleandoc(""" + import + ../config/curves, + ../io/io_fields + """)) + + f.write('\n\n') + f.write(h2c) + + print(f'Successfully created {curve.lower()}_hash_to_curve_g1.nim') + + elif curve == 'BN254_Snarks' and group_or_iso == 'G2': + p = Curves['BN254_Snarks']['field']['modulus'] + non_residue_fp = Curves['BN254_Snarks']['tower']['QNR_Fp'] + Fp = GF(p) + K. = PolynomialRing(Fp) + Fp2. = Fp.extension(u^2 - non_residue_fp) + + Z = Fp2([0, 1]) + h2c = genSVDW_H2C_G2_constants('BN254_Snarks', Curves, Z) + with open(f'{curve.lower()}_hash_to_curve_g2.nim', 'w') as f: f.write(copyright()) f.write('\n\n') diff --git a/tests/math/t_ec_template.nim b/tests/math/t_ec_template.nim index 6b59c62bd..c97cdbadf 100644 --- a/tests/math/t_ec_template.nim +++ b/tests/math/t_ec_template.nim @@ -72,7 +72,7 @@ proc run_EC_addition_tests*( moduleName: string ) = - # Random seed for reproducibility + var rng: RngState let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 rng.seed(seed) diff --git a/tests/math/vectors/tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json b/tests/protocol_hash_to_curve/tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json similarity index 100% rename from tests/math/vectors/tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json rename to tests/protocol_hash_to_curve/tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json diff --git a/tests/protocol_hash_to_curve/tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SVDW_RO.json b/tests/protocol_hash_to_curve/tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SVDW_RO.json new file mode 100644 index 000000000..888d0afc6 --- /dev/null +++ b/tests/protocol_hash_to_curve/tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SVDW_RO.json @@ -0,0 +1,96 @@ +{ + "L": "0x40", + "Z": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa8", + "ciphersuite": "BLS12381G1_XMD:SHA-256_SVDW_RO_", + "curve": "BLS12381G1", + "dst": "BLS12381G1_XMD:SHA-256_SVDW_RO_TESTGEN", + "expand": "XMD", + "field": { + "m": "0x1", + "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" + }, + "hash": "sha256", + "k": "0x80", + "map": { + "name": "SVDW" + }, + "randomOracle": true, + "vectors": [ + { + "P": { + "x": "0x163505b44d4a47de22946139337d787f93f4356075c55401f4fdbeb3ede4f3138684e2437e50175f94eac511d7c673d6", + "y": "0x0d09a4540a792daf4d0368f20afb5bd859e537e362edfc9b6f35290b6d05df90937ea91e6277d1cb91638c1abaec4eca" + }, + "Q0": { + "x": "0x04c5db8bf2c09bda41eab080eb6ffed00f30d4c650a0c0c8cb136d9041a9e9b420aa5737fc8dcd1ec2eda971b4bbf7e1", + "y": "0x05b8cc1054ff8ec690a4c073649b474b7b96f2b26ebf83cdd354197ab89f66d78f120407a934d5aac78913d16a8092a5" + }, + "Q1": { + "x": "0x0ace795fe3b2f40cceda72f6fc9086009db87b1e63e906df2eeca748df8f961ae3c83e83bf6c11b5d833e7b93c8911f6", + "y": "0x02be94da9204c38c42828e677cc941184b62ab9aaebe408f90035c50afd732611550dcd31cec9625874596c8d45dd2f5" + }, + "msg": "", + "u": [ + "0x10baa9e54a51ad63c6d8675830a2d74c43abd00a4e56432e90aa77ccf95997286d3b7bf1f61059b76663878995426b9d", + "0x0acf2dcf2bf547d48c01b96379801bd7e2b9f815f22132a07d1ae1ffe49040825ba7b28082696a67b3de2cf9de5f886f" + ] + }, + { + "P": { + "x": "0x02967a7955df3c43807263e9d33c28912daebcdf915ada74ea6ec131210a3f304d97a59d6e3b7bcd98d84f61efb2f659", + "y": "0x13dab558fae67943edd68f571bdc82ad6944f2d80285d9f578d4b1be0d7b4641c653ca52f13eb3474922617c00e4eab5" + }, + "Q0": { + "x": "0x118e12a015459f171fa12049d2bf7a43aa2d07b10709c2e21757f191cc60a3313e447c4ceefb3dc89e05893af65b133d", + "y": "0x0c02fa378304433efcf458c0cc531b72b8613e425cb8404be8612aee0f7850724ed2a6c6d02a8310cf381b99c85293c6" + }, + "Q1": { + "x": "0x107450b1fd1a5e7b8fdf96cd40da9c1919ba86c124160ab6d5d2ed9f83ee8f97c1530437f6f811fa4f4cd1d0a7d69c42", + "y": "0x039185c9fb255a0ac30d091675f02d29bfcf6045b1532384056876a389819d48da77fd71669516e7c98e12da273cddd7" + }, + "msg": "abc", + "u": [ + "0x12b51a783412cbfae1f1b20d7000184c74c889382dc65762ad98a8d6d654aec9a05fa4621693072c67e41987e36b3998", + "0x150a735bb2fb20d4b6221a2d20cfbe275826a63a38f04f757ac63a35dc0a26041d842e9d7f27e3394a007bbeee92fe85" + ] + }, + { + "P": { + "x": "0x084b64b095e373b86441e9fe737a3f8e56fe7f0016a973ed9938db9958505a8927ca220d8e8235901d4bc1dccd4362bb", + "y": "0x0fd10114cded64bf037d3ba00b4a85e5f19847535dbc960bdaaa8a92bbb9d8a845dc355370ed0edb9d4403afc39bd30c" + }, + "Q0": { + "x": "0x01bbafd66bbe5955cf186e1b050f962f7a2efa4f03ff50353dbda78568f5541aa8582c25057a9fe414f9a1bd82eccf0a", + "y": "0x0c96fd01df526fda79643a45b1ec5d435e63a3af5cf4088f30c07302ff8e0a271aff59e5da618ab9b89326fe8b4338fa" + }, + "Q1": { + "x": "0x086bb205df6314011dedc477b201229f383c1e2585b5aaf269fc6762ccb2397690064429dddbf9f304367508aac0fe83", + "y": "0x02f80d27fd2981db284b0f66cf3d8e06f751464e610009b9f3fbb818f526ff85f7f1a38ea3c85008c5814ff19d05a1ee" + }, + "msg": "abcdef0123456789", + "u": [ + "0x163831497fddb01bbad44e3f592ad34635579e055d36be71d53bec8b22c27525a471ea058bea4a188a857861fd1af802", + "0x0cf8d175460236795d8163d01d5068d82b218295a1f8a8eca094ce27ff208ab5c9c3f4212e3fcdef3980a267598f6830" + ] + }, + { + "P": { + "x": "0x1362872938edd5f0f648d3f8f71225b38a9bbf6c28dee168d76b274125ef4596d1959dfa622861848b2bf713fda12601", + "y": "0x0308f950d4321be5e06a751aee088d09da8fdb36c4bc6b801d529e43250f3e90d55c8d9fea6d3762912f92866bd89d02" + }, + "Q0": { + "x": "0x17f78156bb227c0fefb04a3d58f7315cd7eb696e52b7efeef9fa82171e8c6d8dd265f7fcffa8e6e734c6cc2611363e80", + "y": "0x000a6e05625536b8cc8fff02ebe2ff0a6f137d5f0b644afae033fdbec60e41e281383e97333a342e3492baeddf637e36" + }, + "Q1": { + "x": "0x191f209664bd324339cbbc4e3afe7e713fd47e57c476ff8c224bef6b59745384ec6f15edd912c56336299733b9650f3a", + "y": "0x108c7a3b7d2e6c83e1e9bbcb03960a3b9115d871cce130d8622576339bbb3fd02565de45b06620d557e9244c4a4944d8" + }, + "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "u": [ + "0x130bc594dc2a536afba69280db5d4675051f5eb3a5d147290874f53fd275acddf4faca77145fb23aa6d9357a00e0a4f4", + "0x02e5a19a37149c402114906448c3f7558125edbfce8c5241056d32bac7980d95d820ecf23dbfa5eafa9c4f8c3dbb4b70" + ] + } + ] +} diff --git a/tests/math/vectors/tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json b/tests/protocol_hash_to_curve/tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json similarity index 100% rename from tests/math/vectors/tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json rename to tests/protocol_hash_to_curve/tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json diff --git a/tests/protocol_hash_to_curve/tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SVDW_RO.json b/tests/protocol_hash_to_curve/tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SVDW_RO.json new file mode 100644 index 000000000..2671e51bd --- /dev/null +++ b/tests/protocol_hash_to_curve/tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SVDW_RO.json @@ -0,0 +1,96 @@ +{ + "L": "0x40", + "Z": "0x0,0x1", + "ciphersuite": "BLS12381G2_XMD:SHA-256_SVDW_RO_", + "curve": "BLS12381G2", + "dst": "BLS12381G2_XMD:SHA-256_SVDW_RO_TESTGEN", + "expand": "XMD", + "field": { + "m": "0x2", + "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" + }, + "hash": "sha256", + "k": "0x80", + "map": { + "name": "SVDW" + }, + "randomOracle": true, + "vectors": [ + { + "P": { + "x": "0x1247e409b2a18aa704b37c891ad429af1e81a7d03d97c1651d072a63acc5585967939adfe6b41e608c4cf1444be4ebaf,0x1379e10a574e45e6e8ffc3d403bf975984237d4e0dd350a2209ee5025b738827620301e71bfe4449af7b9ea2a8fc0464", + "y": "0x0f171ce60457b1bf85847e48469e8a50e28b26e126201664b2eb962abe38c0eb2e1e84c953521c6e8cd82fe25fca520a,0x02663ec2937ebd48810bbd17d88f03e9abea73025d7a59c5b7a0db80ac3bde5b1b6fb55f825686d170702f9a0b354e7b" + }, + "Q0": { + "x": "0x0c518ce5d2e9e96a19ce4737e4f60ef6273231e706540bca662697e16303f0066ace3544d272492b5c01ab2a20d08d0f,0x132f304e07b3a827b26eacbb0e3af39a32b260d95ca86e63aa2f8d0ba99b6f6ee5f15cbcd3afee9f9081879d5709c565", + "y": "0x021575725c8fb5107d5ad64c1f178d923e19203590dcb1b509af82ffbf367b9c4cccb89301f0aea987dc788d44547f55,0x00a2ba2f88f654fcde8197b94d1605c24515a5cb7823288d13760cc23e49c424db9a08d37e1e55c425f1334894ce0125" + }, + "Q1": { + "x": "0x0fe40888ce7a67c99ba37194b2e62c77c9d2fb5d18d71faf81a4084b8edc805c30776a46dbe6b749ea170868bf4faadc,0x1918a1f78646dc0451cfdd37725fa080cfb9d9ea473b0f58a9929b7b31ab8da8547346a091a7e2b3d073e3cc38ce0b12", + "y": "0x152eebe67adbeb41ccc8a03926448c9dbe6692254203c6378f00a7e5bd56acdbde4ab9e8d1809aaf405bbe5ab395e385,0x070cc5025c561c1dfcca06ba88853300d4314db466e187e60cd1915c735bf2c6dc3bd0903d988d7d9b3b9e46dbe3ee1d" + }, + "msg": "", + "u": [ + "0x011a69c0b78b463729c1e9e6c7a543d4c10e1880a6a5c92748db6708c7492b170912c46abf43a50d6af40345ceeb16db,0x1216c67865d83536ff57ed9480ac3ee581b7edc25860f73e2ed967c40f5647a4bc25d54538e14a0404d550acdbe8056f", + "0x190bfaa5cd1654a6f71bcff0265236fc73a1a8c72f637b13fa9e3b7c69ad7a82de6419893f1a4f46b0aefeee08a5f877,0x001af2852d68aefb42258ae84e63fa04f45e0603a54d245882a2e2373484d49277d885983e300dcc8a7632c476f6654e" + ] + }, + { + "P": { + "x": "0x14dc75415d6482bae5db292316e9fce2a9ac7c9591222ba12ef70fe4ad5ddd2a95b696857234d9114812394b60f8871a,0x121be08f8e5ebfd6bd080250aac7cde4ceb5672b5fd470bd8a92164e7b7fef93631da6bb514892cf913e5ba6bba00adc", + "y": "0x024233ce610d08957a91a989495af524ca3c4409e7f2fd38164ee7e76fc9694338370d90d2150f2e602bec35eda9e22d,0x00bd976560d390194db01606af280f74b6b4282d4bf3f6ed2718ac67ebe1507e52eab74fbdfdfdf0092c0d9a106969e5" + }, + "Q0": { + "x": "0x0d0c596608ea6ae9b18b6e55d21663933db8256234f5c2b3b3479b7bfc490f7828c98f883e1a63c58cdc0fc028d4661a,0x153ca080b13248f5f6b863a409ff2df352041c19f353f72dd0f3233a1ae2fdeea6e31a93a7a290a99a56a7cfe8448ff1", + "y": "0x012454a1f725b9b478ebdfaf0e56870fa99a15ff3e003c21a155042b44a39cc3a36421b7e11112da0a43e8528b15f820,0x0116d64afa9e422e1d966a93b0e3a0e40c4e34dc34a4c7b2f71e1520812f401baa979c5c62e67e4adf15ce4a507f7db1" + }, + "Q1": { + "x": "0x0083e95f0c761f7303c593bfacddf994635f12704cec7d2a1e8ce9ce3186b20acad0a8f63ff94cefd67400c72751fa29,0x198853939c488f8d8e5bf0839aff9aee5513205a775a541ea3fbce3ebc27395f811147593a7b51e4b1874cf14c8a5c59", + "y": "0x145f9d0d73567c83a5fccb0b05afc9585d302e1fe2e60fa91c673e12a2e760e74c64eb88c7747030b1b50ec8b8111d7f,0x102641fb2d3f4866926783d40d04d3b0f5a6ec4f4aab132053499b50922a29dce7f535502e4038b1a8ce7ae79d2faacd" + }, + "msg": "abc", + "u": [ + "0x08dde2c4f9306a44a460f832d9153c2eccbb8588a7ac35b8476b006be691ce1b340486a56f180be2a022f3cee642ea2a,0x05141fedc77647e15b94c7dd50010f0ab062f44dfb25bc4a127f94aeede48e4f5782c83a29c98295d27210c7ff74ba6c", + "0x0f6d3efdc539af5edd8b806160272c9699ab924f37d161509d4cfa5a589d796d7b05b78fe5994bd082aa6765ef468421,0x00ba433ee89bbbb64364644c770149bba3d8fc6de84e2c057fe7ea027fab92261a40a20214f4da43375f5d55d5c74361" + ] + }, + { + "P": { + "x": "0x185cad2867dc5c11b0f42842adc2bc16ebaf95c761b5d26541a089c19f54e5039ad911b2667de073c26d01911d203d58,0x05e0ac4290a0620d1b5d20b8b98ace6d1b198273e05a36daa18b3c591ded4bfebbff7a8bb3bc2b9de009d5e9d0a78a12", + "y": "0x025525d91f16601d32379f4d5cba4a256ad84d40423ef294dabdf73e351f86b583c848f974899815a40c569465c68c7f,0x09aa43a981af5885a0f86a1b9a143abbc1570d1bf342f8b506ff9f9bcc15dbb12a8ac062f7e37e06b90881b8cbc1f4e9" + }, + "Q0": { + "x": "0x10319d966d19e9017cd0be3c551e4001c3e9d75a25e1700c3443b7756ae94fe1d442d6b3839d505682fa82269c7e910e,0x1860d1ab41d2ffe81a587e6919b21348bb437a8ea88e932525c803d5c1ee68bf47f515c445ae102cde5cf5569de63970", + "y": "0x133af9a9ac415000202b1a1fc258aa773902a937fa09af32267cfcf32baaa002eb80b1aefa92ee759867cd9222c55ea6,0x07abb300d3f1a89f91e55b1abf628f83dd967a97d81d8938c876060455abb77dfc9ce41c5a0beb99e9feb684b9f18d4e" + }, + "Q1": { + "x": "0x14f0b1e5f3d5b113514a1ce6eb17de28d84fb686ed72b9a3091a9be545977acaae253b65268c1057abccb2c47381a7b2,0x136ba736cfc5c767b74be4814e647516ef71e512623d481ec17c6d89d65f536ed679900bf8050bed93a5833df4c032f5", + "y": "0x10e035eb449a1ebbc06431ee73251c7a94b96f83102bacb8c3a6bbacf32486bd6715dcfdd1c1a5ac0058f4fd7930e22b,0x06e89c91fe9ed6c5f6184c5e3766d7393002d06cdffe4b084a5cb4fd4ed71fa5f82590927ff983ea84cb7229a688d071" + }, + "msg": "abcdef0123456789", + "u": [ + "0x11cb86b80f5715c34ebbbfd2b535a765de4e5e6de719f804b86e5d0e7415f00a2123397e06a9fe969ef09cd84c4d0146,0x10c642ecf452b47f5dcff39e3ab10d9b240d9511837f2d6eb152f86b2960a2fb8350f9d0387f8a8899a58bd2c5691b92", + "0x05c8d547790510132e87b818de01994a7dcccbafcccffbfe38ada5ba2573eda9308f4bad0fb1beb79b12c7329c18766b,0x11931693de41195bec4acae1beea0b9b2bf2e55902bebb8f08da8036989efacda79b275d0a5b06d42905826f99e05dfa" + ] + }, + { + "P": { + "x": "0x19925c3d247512c84e3ced29498eada349e82ee96689d5977e627c5b75a904800d789d1848a0551fd1d8b300efef8663,0x07a1af1c92c0723656bb94b0ebae40d4c3e952b32473964710b9b9602b8dbd4e80a8950208fc2cb449daea25af54f138", + "y": "0x179c7387bd605a594a778eb6305ba2d96d69383104c41fd6c5b1517b1728959e78fef47260863497292e056b745d847a,0x174fd14a9224fc3a7fa9019b7c344557f67b98f40c4fe7cefffd99a57aac611de1456988947cbfa37728426d9eeaea52" + }, + "Q0": { + "x": "0x15e925b755b1d7ffadb242ae97d8f3826a46b6cebb87fc5afafa75a041dce9dab92daff7c76a27fdaabee83b58302e7c,0x198976a1d24b585148aa5331536a8d9747584d2cd86bb424426c4ae7234aed4afb060f1e09d1ab36bbf7a439fc8da6d1", + "y": "0x16cda73b5ec6993795f393f19129e0f4e3de82f803ca55f21962162ac599e48e39f0ff78e8159a05d0eceabc6cf01291,0x05b52d7a1673bb8168c3bbad3d3652abca5d7e0027a667914668772ca6ca7e130b07ae7ea6b16695ed53e83789ca3a50" + }, + "Q1": { + "x": "0x031241efa254ea61dd01d9ac138fa90b5b5a60b1d37bdac7984f1a038e867acb39f10d616b012b2d64f2a907ccf7a2c5,0x10e01a11fd5e4af0c2f7d373dfd0b565055680087068bfe3d9694b443a76a472c409fd57e86647848615ed54bf85fac6", + "y": "0x173cec16ebaf7dc61a0c3fadd3a50bc19362dddfe166c6ff7a84443c04659c096be4982b3db1d7bb26ae16af4bfb532f,0x13e3154a7eee424063ec3826c681675942bd36b9f22898547414420bd69ba4e273090d4722297f6e5ba2729fba8cabd9" + }, + "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "u": [ + "0x1412f7dcbbd4b37d2818e3174f2e51c6767b4d93501208d905d625a6decfd1253c626575c44b838cd8801653909fa3a1,0x17bb0ac3a973bf8d3663102459b8dca358d9b971ebc700126f11110c83f644f0ac5a86f1c7cd6babceb1f3705447665a", + "0x0eaa03f98f9aa539262275d8862ce341537228572ea46dcd6e9e8662bd7f986948ddab6cf0594faa7b417b772ec96177,0x0fdb46860616bdbd8faff9859ae8d92f282f9633a4b5050c924d711f1b32a6cc20f431c207d47d66dd27fb6604c871b0" + ] + } + ] +} diff --git a/tests/math/vectors/tv_h2c_v8_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json b/tests/protocol_hash_to_curve/tv_h2c_v8_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json similarity index 100% rename from tests/math/vectors/tv_h2c_v8_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json rename to tests/protocol_hash_to_curve/tv_h2c_v8_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json diff --git a/tests/math/vectors/tv_h2c_v8_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json b/tests/protocol_hash_to_curve/tv_h2c_v8_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json similarity index 100% rename from tests/math/vectors/tv_h2c_v8_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json rename to tests/protocol_hash_to_curve/tv_h2c_v8_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json diff --git a/tests/math/t_hash_to_curve.nim b/tests/t_hash_to_curve.nim similarity index 73% rename from tests/math/t_hash_to_curve.nim rename to tests/t_hash_to_curve.nim index 2f276edec..f22e071eb 100644 --- a/tests/math/t_hash_to_curve.nim +++ b/tests/t_hash_to_curve.nim @@ -12,15 +12,13 @@ import # 3rd party pkg/jsony, # Internals - ../../constantine/platforms/abstractions, - ../../constantine/math/config/curves, - ../../constantine/math/extension_fields, - ../../constantine/math/io/[io_bigints, io_ec], - ../../constantine/math/elliptic/[ - ec_shortweierstrass_affine, - ec_shortweierstrass_projective], - ../../constantine/hash_to_curve/hash_to_curve, - ../../constantine/hashes + ../constantine/platforms/abstractions, + ../constantine/math/config/curves, + ../constantine/math/extension_fields, + ../constantine/math/io/[io_bigints, io_ec], + ../constantine/math/ec_shortweierstrass, + ../constantine/hash_to_curve/hash_to_curve, + ../constantine/hashes # Serialization # -------------------------------------------------------------------------- @@ -65,7 +63,7 @@ type const TestVectorsDir* = - currentSourcePath.rsplit(DirSep, 1)[0] / "vectors" + currentSourcePath.rsplit(DirSep, 1)[0] / "protocol_hash_to_curve" proc parseHook*(src: string, pos: var int, value: var ECP_ShortW_Aff) = # Note when nim-serialization was used: @@ -138,6 +136,41 @@ proc run_hash_to_curve_test( doAssert: bool(P == P_ref) +proc run_hash_to_curve_svdw_test( + EC: typedesc, + spec_version: string, + filename: string + ) = + + when EC.G == G1: + const G1_or_G2 = "G1" + else: + const G1_or_G2 = "G2" + let vec = loadVectors(HashToCurveTest[ECP_ShortW_Aff[EC.F, EC.G]], filename) + + let testSuiteDesc = "Hash to Curve " & $EC.F.C & " " & G1_or_G2 & " - official specs " & spec_version & " test vectors" + + suite testSuiteDesc & " [" & $WordBitwidth & "-bit mode]": + + doAssert vec.hash == "sha256" + doAssert vec.k == "0x80" # 128 + + for i in 0 ..< vec.vectors.len: + test "test " & $i & " - msg: \'" & vec.vectors[i].msg & "\'": + var P{.noInit.}: EC + sha256.hashToCurve_svdw( + k = 128, + output = P, + augmentation = "", + message = vec.vectors[i].msg, + domainSepTag = vec.dst + ) + + var P_ref: EC + P_ref.fromAffine(vec.vectors[i].P) + + doAssert: bool(P == P_ref) + echo "\n------------------------------------------------------\n" echo "Hash-to-curve" & '\n' @@ -168,3 +201,16 @@ run_hash_to_curve_test( "v7", "tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SSWU_RO.json" ) + +# With the slower universal SVDW mapping instead of SSWU +run_hash_to_curve_svdw_test( + ECP_ShortW_Jac[Fp[BLS12_381], G1], + "v7 (SVDW)", + "tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SVDW_RO.json" +) + +run_hash_to_curve_svdw_test( + ECP_ShortW_Jac[Fp2[BLS12_381], G2], + "v7 (SVDW)", + "tv_h2c_v7_BLS12_381_hash_to_G2_SHA256_SVDW_RO.json" +) diff --git a/tests/t_hash_to_curve_random.nim b/tests/t_hash_to_curve_random.nim new file mode 100644 index 000000000..b52912025 --- /dev/null +++ b/tests/t_hash_to_curve_random.nim @@ -0,0 +1,57 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times, os, strutils], + # Internals + ../constantine/math/config/curves, + ../constantine/math/extension_fields, + ../constantine/math/ec_shortweierstrass, + ../constantine/hash_to_curve/hash_to_curve, + ../constantine/hashes, + ../constantine/math/curves/zoo_subgroups, + # Test utilities + ../helpers/prng_unsafe + +const Iters = 6 + +# Random seed for reproducibility +var rng: RngState +let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 +rng.seed(seed) +echo "\n------------------------------------------------------\n" +echo "Hash-to-curve (randomized) xoshiro512** seed: ", seed + +proc testH2C_consistency[EC: ECP_ShortW](curve: typedesc[EC]) = + var P{.noInit.}: EC + let msg = rng.random_byte_seq(32) + sha256.hashToCurve( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = "H2C-CONSTANTINE-TESTSUITE" + ) + + doAssert bool isOnCurve(P.x, P.y, EC.G) + doAssert bool isInSubgroup(P) + +suite "Hash-to-curve produces points on curve and in correct subgroup": + test "BLS12-381 G1": + for i in 0 ..< Iters: + testH2C_consistency(ECP_ShortW_Aff[Fp[BLS12_381], G1]) + test "BLS12-381 G2": + for i in 0 ..< Iters: + testH2C_consistency(ECP_ShortW_Aff[Fp2[BLS12_381], G2]) + test "BN254_Snarks G1": + for i in 0 ..< Iters: + testH2C_consistency(ECP_ShortW_Aff[Fp[BN254_Snarks], G1]) + test "BN254_Snarks G2": + for i in 0 ..< Iters: + testH2C_consistency(ECP_ShortW_Aff[Fp2[BN254_Snarks], G2]) \ No newline at end of file diff --git a/tests/math/t_hash_to_field.nim b/tests/t_hash_to_field.nim similarity index 97% rename from tests/math/t_hash_to_field.nim rename to tests/t_hash_to_field.nim index 7ca73671e..dfd33e135 100644 --- a/tests/math/t_hash_to_field.nim +++ b/tests/t_hash_to_field.nim @@ -7,12 +7,11 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../../constantine/hashes, - ../../constantine/hash_to_curve/h2c_hash_to_field, - ../../constantine/math/config/[curves_declaration, type_ff], - ../../constantine/math/extension_fields/towers, - - ../../constantine/math/io/[io_fields, io_extfields], + ../constantine/hashes, + ../constantine/hash_to_curve/h2c_hash_to_field, + ../constantine/math/config/[curves_declaration, type_ff], + ../constantine/math/extension_fields/towers, + ../constantine/math/io/[io_fields, io_extfields], # Third-party stew/byteutils