From 65eedd1cf70389d188db01fb4f3b6c8e76cc3594 Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Mon, 11 Apr 2022 00:57:16 +0200 Subject: [PATCH] Hash-to-Curve BLS12-381 G1 (#189) * Skeleton of hash to curve for BLS12-381 G1 * Remove isodegree parameter * Fix polynomial evaluation of hashToG1 * Optimize hash_to_curve and add bench for hash to G1 * slight optim of jacobian isomap + v7 test vectors --- benchmarks/bench_hash_to_curve.nim | 41 ++- .../hash_to_curve/h2c_isogeny_maps.nim | 123 ++++---- .../hash_to_curve/h2c_map_to_isocurve_swu.nim | 83 ++++++ constantine/hash_to_curve/hash_to_curve.nim | 90 ++++-- .../arithmetic/finite_fields_square_root.nim | 8 - .../math/config/curves_prop_field_core.nim | 14 +- .../curves/bls12_381_hash_to_curve_g1.nim | 280 ++++++++++++++++++ ...rve.nim => bls12_381_hash_to_curve_g2.nim} | 22 +- constantine/math/curves/zoo_hash_to_curve.nim | 10 +- constantine/math/curves/zoo_subgroups.nim | 2 +- constantine/math/ec_shortweierstrass.nim | 1 + constantine/math/io/io_bigints.nim | 3 + sage/derive_hash_to_curve.sage | 147 ++++++++- tests/math/t_hash_to_curve.nim | 12 + ...7_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json | 96 ++++++ ...8_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json | 115 +++++++ 16 files changed, 936 insertions(+), 111 deletions(-) create mode 100644 constantine/math/curves/bls12_381_hash_to_curve_g1.nim rename constantine/math/curves/{bls12_381_g2_hash_to_curve.nim => bls12_381_hash_to_curve_g2.nim} (95%) create mode 100644 tests/math/vectors/tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json create mode 100644 tests/math/vectors/tv_h2c_v8_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json diff --git a/benchmarks/bench_hash_to_curve.nim b/benchmarks/bench_hash_to_curve.nim index e574072..988dc75 100644 --- a/benchmarks/bench_hash_to_curve.nim +++ b/benchmarks/bench_hash_to_curve.nim @@ -35,6 +35,21 @@ template bench(op: string, C: static Curve, iters: int, body: untyped): untyped measure(iters, startTime, stopTime, startClk, stopClk, body) report(op, $C, startTime, stopTime, startClk, stopClk, iters) +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] + + bench("Hash to G1 (Draft #11)", BLS12_381, iters): + sha256.hashToCurve( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = dst + ) + proc bench_BLS12_381_hash_to_G2(iters: int) = const dst = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_" let msg = "Mr F was here" @@ -50,7 +65,25 @@ proc bench_BLS12_381_hash_to_G2(iters: int) = domainSepTag = dst ) -proc bench_BLS12_381_proj_aff_conversion(iters: int) = +proc bench_BLS12_381_G1_proj_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 Paff: ECP_ShortW_Aff[Fp[BLS12_381], G1] + + sha256.hashToCurve( + k = 128, + output = P, + augmentation = "", + message = msg, + domainSepTag = dst + ) + + bench("G1 Proj->Affine conversion (for pairing)", BLS12_381, iters): + Paff.affine(P) + +proc bench_BLS12_381_G2_proj_aff_conversion(iters: int) = const dst = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_" let msg = "Mr F was here" @@ -65,15 +98,17 @@ proc bench_BLS12_381_proj_aff_conversion(iters: int) = domainSepTag = dst ) - bench("Proj->Affine conversion (for pairing)", BLS12_381, iters): + bench("G2 Proj->Affine conversion (for pairing)", BLS12_381, iters): Paff.affine(P) const Iters = 1000 proc main() = separator() + bench_BLS12_381_hash_to_G1(Iters) bench_BLS12_381_hash_to_G2(Iters) - bench_BLS12_381_proj_aff_conversion(Iters) + bench_BLS12_381_G1_proj_aff_conversion(Iters) + bench_BLS12_381_G2_proj_aff_conversion(Iters) separator() main() diff --git a/constantine/hash_to_curve/h2c_isogeny_maps.nim b/constantine/hash_to_curve/h2c_isogeny_maps.nim index 46bf59a..addb466 100644 --- a/constantine/hash_to_curve/h2c_isogeny_maps.nim +++ b/constantine/hash_to_curve/h2c_isogeny_maps.nim @@ -25,25 +25,9 @@ import # No exceptions allowed {.push raises: [].} -func poly_eval_horner[F](r: var F, x: F, poly: openarray[F]) = - ## Fast polynomial evaluation using Horner's rule - ## The polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ - ## MUST be stored in order - ## [k₀, k₁, k₂, k₃, ..., kₙ] - ## - ## Assuming a degree n = 3 polynomial - ## Horner's rule rewrites its evaluation as - ## ((k₃ x + k₂)x + k₁) x + k₀ - ## which is n additions and n multiplications, - ## the lowest complexity of general polynomial evaluation algorithm. - r = poly[^1] # TODO: optim when poly[^1] == 1 - for i in countdown(poly.len-2, 0): - r *= x - r += poly[i] - func poly_eval_horner_scaled[F; D, N: static int]( r: var F, xn: F, - xd_pow: array[D, F], poly: array[N, F]) = + xd_pow: array[D, F], poly: static array[N, F], numPolyLen: static int) = ## Fast polynomial evaluation using Horner's rule ## Result is scaled by xd^N with N the polynomial degree ## to avoid finite field division @@ -66,25 +50,33 @@ func poly_eval_horner_scaled[F; D, N: static int]( ## ((k₃ xn + k₂ xd) xn + k₁ xd²) xn + k₀ xd³ ## ## avoiding expensive divisions - r = poly[^1] # TODO: optim when poly[^1] == 1 - for i in countdown(N-2, 0): - var t: F + + # i = N-2 + when poly[^1].isOne().bool: + block: + r.prod(poly[N-2], xd_pow[0]) + r += xn + else: + block: + var t{.noInit.}: F + r.prod(poly[N-1], xn) + t.prod(poly[N-2], xd_pow[0]) + r += t + + for i in countdown(N-3, 0): + var t{.noInit.}: F r *= xn - t.prod(poly[i], xd_pow[N-2-i]) + t.prod(poly[i], xd_pow[(N-2-i)]) r += t - const - poly_degree = N-1 # [1, x, x², x³] of length 4 - isodegree = D # Isogeny degree - - static: doAssert isodegree - poly_degree >= 0 - when isodegree - poly_degree > 0: + when N-numPolyLen < 0: # Missing scaling factor - r *= xd_pow[isodegree - poly_degree - 1] + r *= xd_pow[0] func h2c_isogeny_map[F]( rxn, rxd, ryn, ryd: var F, - xn, xd, yn: F, isodegree: static int) = + xn, xd, yn: F, + G: static Subgroup) = ## Given G2, the target prime order subgroup of E2, ## this function maps an element of ## E'2 a curve isogenous to E2 @@ -99,29 +91,41 @@ func h2c_isogeny_map[F]( ## (rx, ry) with rx = rxn/rxd and ry = ryn/ryd # xd^e with e in [1, N], for example [xd, xd², xd³] - static: doAssert isodegree >= 2 - var xd_pow{.noInit.}: array[isodegree, F] + 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, + ]) + var xd_pow{.noInit.}: array[maxdegree, F] xd_pow[0] = xd xd_pow[1].square(xd_pow[0]) 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 + rxn.poly_eval_horner_scaled( xn, xd_pow, - h2cIsomapPoly(F.C, G2, isodegree, xnum) + h2cIsomapPoly(F.C, G, xnum), + xnLen ) rxd.poly_eval_horner_scaled( xn, xd_pow, - h2cIsomapPoly(F.C, G2, isodegree, xden) + h2cIsomapPoly(F.C, G, xden), + xnLen ) ryn.poly_eval_horner_scaled( xn, xd_pow, - h2cIsomapPoly(F.C, G2, isodegree, ynum) + h2cIsomapPoly(F.C, G, ynum), + ynLen ) ryd.poly_eval_horner_scaled( xn, xd_pow, - h2cIsomapPoly(F.C, G2, isodegree, yden) + h2cIsomapPoly(F.C, G, yden), + ynLen ) # y coordinate is y' * poly_yNum(x) @@ -129,7 +133,7 @@ func h2c_isogeny_map[F]( func h2c_isogeny_map*[F; G: static Subgroup]( r: var ECP_ShortW_Prj[F, G], - xn, xd, yn: F, isodegree: static int) = + xn, xd, yn: F) = ## Given G2, the target prime order subgroup of E2, ## this function maps an element of ## E'2 a curve isogenous to E2 @@ -151,7 +155,8 @@ func h2c_isogeny_map*[F; G: static Subgroup]( rxd = r.z, ryn = r.y, ryd = t, - xn, xd, yn, isodegree + xn, xd, yn, + G ) # Now convert to projective coordinates @@ -163,7 +168,7 @@ func h2c_isogeny_map*[F; G: static Subgroup]( func h2c_isogeny_map*[F; G: static Subgroup]( r: var ECP_ShortW_Jac[F, G], - xn, xd, yn: F, isodegree: static int) = + xn, xd, yn: F) = ## Given G2, the target prime order subgroup of E2, ## this function maps an element of ## E'2 a curve isogenous to E2 @@ -184,7 +189,8 @@ func h2c_isogeny_map*[F; G: static Subgroup]( h2c_isogeny_map( rxn, rxd, ryn, ryd, - xn, xd, yn, isodegree + xn, xd, yn, + G ) # Now convert to jacobian coordinates @@ -200,8 +206,7 @@ func h2c_isogeny_map*[F; G: static Subgroup]( func h2c_isogeny_map*[F; G: static Subgroup]( r: var ECP_ShortW_Jac[F, G], - P: ECP_ShortW_Jac[F, G], - isodegree: static int) = + P: ECP_ShortW_Jac[F, G]) = ## Map P in isogenous curve E'2 ## to r in E2 ## @@ -218,29 +223,47 @@ func h2c_isogeny_map*[F; G: static Subgroup]( var yn{.noInit.}, yd{.noInit.}: F # Z²^e with e in [1, N], for example [Z², Z⁴, Z⁶] - static: doAssert isodegree >= 2 - var ZZpow{.noInit.}: array[isodegree, F] + 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, + ]) + var ZZpow{.noInit.}: array[maxdegree, F] ZZpow[0].square(P.z) - ZZpow[1].square(ZZpow[0]) - for i in 2 ..< ZZpow.len: - ZZpow[i].prod(ZZpow[i-1], ZZpow[0]) + + # ZZpow[1].square(ZZpow[0]) + # for i in 2 ..< ZZpow.len: + # ZZpow[i].prod(ZZpow[i-1], ZZpow[0]) + staticFor i, 1, ZZpow.len: + when bool(i and 1): # is odd + ZZpow[i].square(ZZpow[(i-1) shr 1]) + 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 xn.poly_eval_horner_scaled( P.x, ZZpow, - h2cIsomapPoly(F.C, G2, isodegree, xnum) + h2cIsomapPoly(F.C, G, xnum), + xnLen ) xd.poly_eval_horner_scaled( P.x, ZZpow, - h2cIsomapPoly(F.C, G2, isodegree, xden) + h2cIsomapPoly(F.C, G, xden), + xnLen ) yn.poly_eval_horner_scaled( P.x, ZZpow, - h2cIsomapPoly(F.C, G2, isodegree, ynum) + h2cIsomapPoly(F.C, G, ynum), + ynLen ) yd.poly_eval_horner_scaled( P.x, ZZpow, - h2cIsomapPoly(F.C, G2, isodegree, yden) + h2cIsomapPoly(F.C, G, yden), + ynLen ) # yn = y' * poly_yNum(x) = yZ³ * poly_yNum(x) 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 931620d..64df705 100644 --- a/constantine/hash_to_curve/h2c_map_to_isocurve_swu.nim +++ b/constantine/hash_to_curve/h2c_map_to_isocurve_swu.nim @@ -230,3 +230,86 @@ func mapToIsoCurve_sswuG2_opt9mod16*[C: static Curve]( y.cneg(e1 xor e2) # yd.setOne() + +func mapToIsoCurve_sswuG1_opt3mod4*[C: static Curve]( + xn, xd, yn: var Fp[C], + u: Fp[C], xd3: var Fp[C]) = + ## Given G1, the target prime order subgroup of E1 we want to hash to, + ## this function maps any field element of Fp to E'1 + ## a curve isogenous to E1 using the Simplified Shallue-van de Woestijne method. + ## + ## This requires p² ≡ 3 (mod 4). + ## + ## Input: + ## - u, an Fp element + ## Output: + ## - (xn, xd, yn, yd) such that (x', y') = (xn/xd, yn/yd) + ## is a point of E'1 + ## - yd is implied to be 1 + ## Scratchspace: + ## - xd3 is temporary scratchspace that will hold xd³ + ## after execution (which might be useful for Jacobian coordinate conversion) + # + # Paper: https://eprint.iacr.org/2019/403 + # Spec: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-G.2.1 + # Sage: https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/f7dd3761/poc/sswu_opt_3mod4.sage#L33-L76 + # BLST: https://github.com/supranational/blst/blob/v0.3.4/src/map_to_g1.c#L322-L365 + # Formal verification: https://github.com/GaloisInc/BLST-Verification/blob/8e2efde4/spec/implementation/HashToG1.cry + + var + uu {.noInit.}, tv2 {.noInit.}: Fp[C] + tv4 {.noInit.}, x2n {.noInit.}, gx1 {.noInit.}: Fp[C] + y2 {.noInit.}: Fp[C] + e1, e2: SecretBool + + # Aliases + template y: untyped = yn + template x1n: untyped = xn + template y1: untyped = yn + template Zuu: untyped = x2n + 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 + x1n.setOne() + x1n += tv2 # x1n = tv2 + 1 + x1n *= h2cConst(C, 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 + + # y numerators + tv2.square(xd) + gxd.prod(xd, tv2) # gxd = xd³ + tv2 *= h2CConst(C, 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)) + 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 + y2.prod(y1, h2cConst(C, G1, sqrt_minus_Z3)) + y2 *= uu + y2 *= u + + # Choose numerators + xn.ccopy(x2n, not e2) # xn = e2 ? x1n : x2n + yn.ccopy(y2, not e2) # yn = e2 ? y1 : y2 + + e1 = sgn0(u) + e2 = sgn0(y) + y.cneg(e1 xor e2) + + # yd.setOne() diff --git a/constantine/hash_to_curve/hash_to_curve.nim b/constantine/hash_to_curve/hash_to_curve.nim index 50698b4..ddd9d24 100644 --- a/constantine/hash_to_curve/hash_to_curve.nim +++ b/constantine/hash_to_curve/hash_to_curve.nim @@ -34,8 +34,27 @@ import # Map to curve # ---------------------------------------------------------------- -func mapToIsoCurve_sswuG2_opt9mod16[F; G: static Subgroup]( - r: var ECP_ShortW_Jac[F, G], +func mapToIsoCurve_sswuG1_opt3mod4[F]( + r: var ECP_ShortW_Jac[F, G1], + u: F) = + var + xn{.noInit.}, xd{.noInit.}: F + yn{.noInit.}: F + xd3{.noInit.}: F + + mapToIsoCurve_sswuG1_opt3mod4( + xn, xd, + yn, + u, xd3 + ) + + # Convert to Jacobian + r.z = xd # Z = xd + r.x.prod(xn, xd) # X = xZ² = xn/xd * xd² = xn*xd + r.y.prod(yn, xd3) # Y = yZ³ = yn * xd³ + +func mapToIsoCurve_sswuG2_opt9mod16[F]( + r: var ECP_ShortW_Jac[F, G2], u: F) = var xn{.noInit.}, xd{.noInit.}: F @@ -59,29 +78,30 @@ func mapToCurve[F; G: static Subgroup]( ## Map an element of the ## finite or extension field F ## to an elliptic curve E - - when F.C == BLS12_381 and F is Fp2: + + 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.hasP3mod4_primeModulus(): + mapToIsoCurve_sswuG1_opt3mod4( + xn, xd, + yn, + u, xd3 + ) + elif F is Fp2 and F.C.hasP3mod4_primeModulus(): + # p ≡ 3 (mod 4) => p² ≡ 9 (mod 16) + mapToIsoCurve_sswuG2_opt9mod16( + xn, xd, + yn, + u, xd3 + ) + else: + {.error: "Not implemented".} - # 1. Map to E'2 isogenous to E2 - var - xn{.noInit.}, xd{.noInit.}: F - yn{.noInit.}: F - xd3{.noInit.}: F - - mapToIsoCurve_sswuG2_opt9mod16( - xn, xd, - yn, - u, xd3 - ) - - # 2. Map from E'2 to E2 - r.h2c_isogeny_map( - xn, xd, - yn, - isodegree = 3 - ) + # 2. Map from E'1 to E1 + r.h2c_isogeny_map(xn, xd, yn) else: {.error: "Not implemented".} @@ -106,15 +126,27 @@ func mapToCurve_fusedAdd[F; G: static Subgroup]( # So we use jacobian coordinates for computation on isogenies. var P0{.noInit.}, P1{.noInit.}: ECP_ShortW_Jac[F, G] - when F.C == BLS12_381 and F is Fp2: - # 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)) + 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.hasP3mod4_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)) + elif F is Fp2 and F.C.hasP3mod4_primeModulus(): + # 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)) + else: + {.error: "Not implemented".} # 2. Map from E'2 to E2 - r.h2c_isogeny_map(P0, isodegree = 3) + r.h2c_isogeny_map(P0) else: {.error: "Not implemented".} diff --git a/constantine/math/arithmetic/finite_fields_square_root.nim b/constantine/math/arithmetic/finite_fields_square_root.nim index 9347f68..7b11e29 100644 --- a/constantine/math/arithmetic/finite_fields_square_root.nim +++ b/constantine/math/arithmetic/finite_fields_square_root.nim @@ -25,10 +25,6 @@ import # Specialized routine for p ≡ 3 (mod 4) # ------------------------------------------------------------ -func hasP3mod4_primeModulus*(C: static Curve): static bool = - ## Returns true iff p ≡ 3 (mod 4) - (BaseType(C.Mod.limbs[0]) and 3) == 3 - func invsqrt_p3mod4(r: var Fp, a: Fp) = ## Compute the inverse square root of ``a`` ## @@ -58,10 +54,6 @@ func invsqrt_p3mod4(r: var Fp, a: Fp) = # Specialized routine for p ≡ 5 (mod 8) # ------------------------------------------------------------ -func hasP5mod8_primeModulus*(C: static Curve): static bool = - ## Returns true iff p ≡ 5 (mod 8) - (BaseType(C.Mod.limbs[0]) and 7) == 5 - func invsqrt_p5mod8(r: var Fp, a: Fp) = ## Compute the inverse square root of ``a`` ## diff --git a/constantine/math/config/curves_prop_field_core.nim b/constantine/math/config/curves_prop_field_core.nim index 23155a4..768029f 100644 --- a/constantine/math/config/curves_prop_field_core.nim +++ b/constantine/math/config/curves_prop_field_core.nim @@ -37,4 +37,16 @@ template matchingBigInt*(C: static Curve): untyped = template matchingLimbs2x*(C: Curve): untyped = const N2 = wordsRequired(getCurveBitwidth(C)) * 2 # TODO upstream, not precomputing N2 breaks semcheck - array[N2, SecretWord] # TODO upstream, using Limbs[N2] breaks semcheck \ No newline at end of file + array[N2, SecretWord] # TODO upstream, using Limbs[N2] breaks semcheck + +func hasP3mod4_primeModulus*(C: static Curve): static bool = + ## Returns true iff p ≡ 3 (mod 4) + (BaseType(C.Mod.limbs[0]) and 3) == 3 + +func hasP5mod8_primeModulus*(C: static Curve): static bool = + ## Returns true iff p ≡ 5 (mod 8) + (BaseType(C.Mod.limbs[0]) and 7) == 5 + +func hasP9mod16_primeModulus*(C: static Curve): static bool = + ## Returns true iff p ≡ 9 (mod 16) + (BaseType(C.Mod.limbs[0]) and 15) == 9 \ No newline at end of file diff --git a/constantine/math/curves/bls12_381_hash_to_curve_g1.nim b/constantine/math/curves/bls12_381_hash_to_curve_g1.nim new file mode 100644 index 0000000..d3360b8 --- /dev/null +++ b/constantine/math/curves/bls12_381_hash_to_curve_g1.nim @@ -0,0 +1,280 @@ +# 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 map to isogenous BLS12-381 E'1 constants +# ----------------------------------------------------------------- +# +# y² = x³ + A'*x + B' with p ≡ 3 (mod 4) the BLS12-381 characteristic (base modulus) +# +# Hardcoding from spec: +# - 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( + "0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d") +const BLS12_381_h2c_G1_Bprime_E1* = Fp[BLS12_381].fromHex( + "0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0") +const BLS12_381_h2c_G1_Z* = Fp[BLS12_381].fromHex( + "0xb") +const BLS12_381_h2c_G1_minus_A* = Fp[BLS12_381].fromHex( + "0x19eccb5195c6fd570db26db379de6354b38cb3316f96ac168e483a8606d8747786189071107306805d0ad7f7d2a75e8e") +const BLS12_381_h2c_G1_ZmulA* = Fp[BLS12_381].fromHex( + "0xdf088f08f205e3a3857e1ea7b2289d9a148b96ab3e694151fe89284e4d926a8e55cb15e9aab878fe7db859f2cb453f") +const BLS12_381_h2c_G1_sqrt_minus_Z3* = Fp[BLS12_381].fromHex( + "0x3d689d1e0e762cef9f2bec6130316806b4c80eda6fc10ce77ae83eab1ea8b8b8a407c9c6db195e06f2dbeabc2baeff5") + +# Hash-to-Curve 11-isogeny map BLS12-381 E'1 constants +# ----------------------------------------------------------------- +# +# 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* = [ + # Polynomial k₀ + k₁ x + k₂ x² + k₃ x³ + ... + kₙ xⁿ + # The polynomial is stored as an array of coefficients ordered from k₀ to kₙ + + # 1 + Fp[BLS12_381].fromHex( + "0x11a05f2b1e833340b809101dd99815856b303e88a2d7005ff2627b56cdb4e2c85610c2d5f2e62d6eaeac1662734649b7" + ), + # x + Fp[BLS12_381].fromHex( + "0x17294ed3e943ab2f0588bab22147a81c7c17e75b2f6a8417f565e33c70d1e86b4838f2a6f318c356e834eef1b3cb83bb" + ), + # x² + Fp[BLS12_381].fromHex( + "0xd54005db97678ec1d1048c5d10a9a1bce032473295983e56878e501ec68e25c958c3e3d2a09729fe0179f9dac9edcb0" + ), + # x³ + Fp[BLS12_381].fromHex( + "0x1778e7166fcc6db74e0609d307e55412d7f5e4656a8dbf25f1b33289f1b330835336e25ce3107193c5b388641d9b6861" + ), + # x⁴ + Fp[BLS12_381].fromHex( + "0xe99726a3199f4436642b4b3e4118e5499db995a1257fb3f086eeb65982fac18985a286f301e77c451154ce9ac8895d9" + ), + # x⁵ + Fp[BLS12_381].fromHex( + "0x1630c3250d7313ff01d1201bf7a74ab5db3cb17dd952799b9ed3ab9097e68f90a0870d2dcae73d19cd13c1c66f652983" + ), + # x⁶ + Fp[BLS12_381].fromHex( + "0xd6ed6553fe44d296a3726c38ae652bfb11586264f0f8ce19008e218f9c86b2a8da25128c1052ecaddd7f225a139ed84" + ), + # x⁷ + Fp[BLS12_381].fromHex( + "0x17b81e7701abdbe2e8743884d1117e53356de5ab275b4db1a682c62ef0f2753339b7c8f8c8f475af9ccb5618e3f0c88e" + ), + # x⁸ + Fp[BLS12_381].fromHex( + "0x80d3cf1f9a78fc47b90b33563be990dc43b756ce79f5574a2c596c928c5d1de4fa295f296b74e956d71986a8497e317" + ), + # x⁹ + Fp[BLS12_381].fromHex( + "0x169b1f8e1bcfa7c42e0c37515d138f22dd2ecb803a0c5c99676314baf4bb1b7fa3190b2edc0327797f241067be390c9e" + ), + # x¹⁰ + Fp[BLS12_381].fromHex( + "0x10321da079ce07e272d8ec09d2565b0dfa7dccdde6787f96d50af36003b14866f69b771f8c285decca67df3f1605fb7b" + ), + # x¹¹ + Fp[BLS12_381].fromHex( + "0x6e08c248e260e70bd1e962381edee3d31d79d7e22c837bc23c0bf1bc24c6b68c24b1b80b64d391fa9c8ba2e8ba2d229" + ) +] +const BLS12_381_h2c_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ₙ + + # 1 + Fp[BLS12_381].fromHex( + "0x8ca8d548cff19ae18b2e62f4bd3fa6f01d5ef4ba35b48ba9c9588617fc8ac62b558d681be343df8993cf9fa40d21b1c" + ), + # x + Fp[BLS12_381].fromHex( + "0x12561a5deb559c4348b4711298e536367041e8ca0cf0800c0126c2588c48bf5713daa8846cb026e9e5c8276ec82b3bff" + ), + # x² + Fp[BLS12_381].fromHex( + "0xb2962fe57a3225e8137e629bff2991f6f89416f5a718cd1fca64e00b11aceacd6a3d0967c94fedcfcc239ba5cb83e19" + ), + # x³ + Fp[BLS12_381].fromHex( + "0x3425581a58ae2fec83aafef7c40eb545b08243f16b1655154cca8abc28d6fd04976d5243eecf5c4130de8938dc62cd8" + ), + # x⁴ + Fp[BLS12_381].fromHex( + "0x13a8e162022914a80a6f1d5f43e7a07dffdfc759a12062bb8d6b44e833b306da9bd29ba81f35781d539d395b3532a21e" + ), + # x⁵ + Fp[BLS12_381].fromHex( + "0xe7355f8e4e667b955390f7f0506c6e9395735e9ce9cad4d0a43bcef24b8982f7400d24bc4228f11c02df9a29f6304a5" + ), + # x⁶ + Fp[BLS12_381].fromHex( + "0x772caacf16936190f3e0c63e0596721570f5799af53a1894e2e073062aede9cea73b3538f0de06cec2574496ee84a3a" + ), + # x⁷ + Fp[BLS12_381].fromHex( + "0x14a7ac2a9d64a8b230b3f5b074cf01996e7f63c21bca68a81996e1cdf9822c580fa5b9489d11e2d311f7d99bbdcc5a5e" + ), + # x⁸ + Fp[BLS12_381].fromHex( + "0xa10ecf6ada54f825e920b3dafc7a3cce07f8d1d7161366b74100da67f39883503826692abba43704776ec3a79a1d641" + ), + # x⁹ + Fp[BLS12_381].fromHex( + "0x95fc13ab9e92ad4476d6e3eb3a56680f682b4ee96f7d03776df533978f31c1593174e4b4b7865002d6384d168ecdd0a" + ), + # x¹⁰ + Fp[BLS12_381].fromHex( + "0x1" + ) +] +const BLS12_381_h2c_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ₙ + + # y + Fp[BLS12_381].fromHex( + "0x90d97c81ba24ee0259d1f094980dcfa11ad138e48a869522b52af6c956543d3cd0c7aee9b3ba3c2be9845719707bb33" + ), + # x*y + Fp[BLS12_381].fromHex( + "0x134996a104ee5811d51036d776fb46831223e96c254f383d0f906343eb67ad34d6c56711962fa8bfe097e75a2e41c696" + ), + # x²*y + Fp[BLS12_381].fromHex( + "0xcc786baa966e66f4a384c86a3b49942552e2d658a31ce2c344be4b91400da7d26d521628b00523b8dfe240c72de1f6" + ), + # x³*y + Fp[BLS12_381].fromHex( + "0x1f86376e8981c217898751ad8746757d42aa7b90eeb791c09e4a3ec03251cf9de405aba9ec61deca6355c77b0e5f4cb" + ), + # x⁴*y + Fp[BLS12_381].fromHex( + "0x8cc03fdefe0ff135caf4fe2a21529c4195536fbe3ce50b879833fd221351adc2ee7f8dc099040a841b6daecf2e8fedb" + ), + # x⁵*y + Fp[BLS12_381].fromHex( + "0x16603fca40634b6a2211e11db8f0a6a074a7d0d4afadb7bd76505c3d3ad5544e203f6326c95a807299b23ab13633a5f0" + ), + # x⁶*y + Fp[BLS12_381].fromHex( + "0x4ab0b9bcfac1bbcb2c977d027796b3ce75bb8ca2be184cb5231413c4d634f3747a87ac2460f415ec961f8855fe9d6f2" + ), + # x⁷*y + Fp[BLS12_381].fromHex( + "0x987c8d5333ab86fde9926bd2ca6c674170a05bfe3bdd81ffd038da6c26c842642f64550fedfe935a15e4ca31870fb29" + ), + # x⁸*y + Fp[BLS12_381].fromHex( + "0x9fc4018bd96684be88c9e221e4da1bb8f3abd16679dc26c1e8b6e6a1f20cabe69d65201c78607a360370e577bdba587" + ), + # x⁹*y + Fp[BLS12_381].fromHex( + "0xe1bba7a1186bdb5223abde7ada14a23c42a0ca7915af6fe06985e7ed1e4d43b9b3f7055dd4eba6f2bafaaebca731c30" + ), + # x¹⁰*y + Fp[BLS12_381].fromHex( + "0x19713e47937cd1be0dfd0b8f1d43fb93cd2fcbcb6caf493fd1183e416389e61031bf3a5cce3fbafce813711ad011c132" + ), + # x¹¹*y + Fp[BLS12_381].fromHex( + "0x18b46a908f36f6deb918c143fed2edcc523559b8aaf0c2462e6bfe7f911f643249d9cdf41b44d606ce07c8a4d0074d8e" + ), + # x¹²*y + Fp[BLS12_381].fromHex( + "0xb182cac101b9399d155096004f53f447aa7b12a3426b08ec02710e807b4633f06c851c1919211f20d4c04f00b971ef8" + ), + # x¹³*y + Fp[BLS12_381].fromHex( + "0x245a394ad1eca9b72fc00ae7be315dc757b3b080d4c158013e6632d3c40659cc6cf90ad1c232a6442d9d3f5db980133" + ), + # x¹⁴*y + Fp[BLS12_381].fromHex( + "0x5c129645e44cf1102a159f748c4a3fc5e673d81d7e86568d9ab0f5d396a7ce46ba1049b6579afb7866b1e715475224b" + ), + # x¹⁵*y + Fp[BLS12_381].fromHex( + "0x15e6be4e990f03ce4ea50b3b42df2eb5cb181d8f84965a3957add4fa95af01b2b665027efec01c7704b456be69c8b604" + ), +] +const BLS12_381_h2c_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ₙ + + # 1 + Fp[BLS12_381].fromHex( + "0x16112c4c3a9c98b252181140fad0eae9601a6de578980be6eec3232b5be72e7a07f3688ef60c206d01479253b03663c1" + ), + # x + Fp[BLS12_381].fromHex( + "0x1962d75c2381201e1a0cbd6c43c348b885c84ff731c4d59ca4a10356f453e01f78a4260763529e3532f6102c2e49a03d" + ), + # x² + Fp[BLS12_381].fromHex( + "0x58df3306640da276faaae7d6e8eb15778c4855551ae7f310c35a5dd279cd2eca6757cd636f96f891e2538b53dbf67f2" + ), + # x³ + Fp[BLS12_381].fromHex( + "0x16b7d288798e5395f20d23bf89edb4d1d115c5dbddbcd30e123da489e726af41727364f2c28297ada8d26d98445f5416" + ), + # x⁴ + Fp[BLS12_381].fromHex( + "0xbe0e079545f43e4b00cc912f8228ddcc6d19c9f0f69bbb0542eda0fc9dec916a20b15dc0fd2ededda39142311a5001d" + ), + # x⁵ + Fp[BLS12_381].fromHex( + "0x8d9e5297186db2d9fb266eaac783182b70152c65550d881c5ecd87b6f0f5a6449f38db9dfa9cce202c6477faaf9b7ac" + ), + # x⁶ + Fp[BLS12_381].fromHex( + "0x166007c08a99db2fc3ba8734ace9824b5eecfdfa8d0cf8ef5dd365bc400a0051d5fa9c01a58b1fb93d1a1399126a775c" + ), + # x⁷ + Fp[BLS12_381].fromHex( + "0x16a3ef08be3ea7ea03bcddfabba6ff6ee5a4375efa1f4fd7feb34fd206357132b920f5b00801dee460ee415a15812ed9" + ), + # x⁸ + Fp[BLS12_381].fromHex( + "0x1866c8ed336c61231a1be54fd1d74cc4f9fb0ce4c6af5920abc5750c4bf39b4852cfe2f7bb9248836b233d9d55535d4a" + ), + # x⁹ + Fp[BLS12_381].fromHex( + "0x167a55cda70a6e1cea820597d94a84903216f763e13d87bb5308592e7ea7d4fbc7385ea3d529b35e346ef48bb8913f55" + ), + # x¹⁰ + Fp[BLS12_381].fromHex( + "0x4d2f259eea405bd48f010a01ad2911d9c6dd039bb61a6290e591b36e636a5c871a5c29f4f83060400f8b49cba8f6aa8" + ), + # x¹¹ + Fp[BLS12_381].fromHex( + "0xaccbb67481d033ff5852c1e48c50c477f94ff8aefce42d28c0f9a88cea7913516f968986f7ebbea9684b529e2561092" + ), + # x¹² + Fp[BLS12_381].fromHex( + "0xad6b9514c767fe3c3613144b45f1496543346d98adf02267d5ceef9a00d9b8693000763e3b90ac11e99b138573345cc" + ), + # x¹³ + Fp[BLS12_381].fromHex( + "0x2660400eb2e4f3b628bdd0d53cd76f2bf565b94e72927c1cb748df27942480e420517bd8714cc80d1fadc1326ed06f7" + ), + # x¹⁴ + Fp[BLS12_381].fromHex( + "0xe0fa1d816ddc03e6b24255e0d7819c171c40f65e273b853324efcd6356caa205ca2f570f13497804415473a1d634b8f" + ), + # x¹⁵ + Fp[BLS12_381].fromHex( + "0x1" + ) +] diff --git a/constantine/math/curves/bls12_381_g2_hash_to_curve.nim b/constantine/math/curves/bls12_381_hash_to_curve_g2.nim similarity index 95% rename from constantine/math/curves/bls12_381_g2_hash_to_curve.nim rename to constantine/math/curves/bls12_381_hash_to_curve_g2.nim index ab10ad9..4eae91f 100644 --- a/constantine/math/curves/bls12_381_g2_hash_to_curve.nim +++ b/constantine/math/curves/bls12_381_hash_to_curve_g2.nim @@ -55,7 +55,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_3_isogeny_map_xnum* = [ +const BLS12_381_h2c_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ₙ @@ -69,18 +69,18 @@ const BLS12_381_h2c_G2_3_isogeny_map_xnum* = [ "0x0", "0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71a" ), - # x^2 + # x² Fp2[BLS12_381].fromHex( "0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71e", "0x8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38d" ), - # x^3 + # x³ Fp2[BLS12_381].fromHex( "0x171d6541fa38ccfaed6dea691f5fb614cb14b4e7f4e810aa22d6108f142b85757098e38d0f671c7188e2aaaaaaaa5ed1", "0x0" ) ] -const BLS12_381_h2c_G2_3_isogeny_map_xden* = [ +const BLS12_381_h2c_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ₙ @@ -94,13 +94,13 @@ const BLS12_381_h2c_G2_3_isogeny_map_xden* = [ "0xc", "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa9f" ), - # x^2 + # x² Fp2[BLS12_381].fromHex( "0x1", "0x0" ) ] -const BLS12_381_h2c_G2_3_isogeny_map_ynum* = [ +const BLS12_381_h2c_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ₙ @@ -114,18 +114,18 @@ const BLS12_381_h2c_G2_3_isogeny_map_ynum* = [ "0x0", "0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97be" ), - # x^2*y + # x²*y Fp2[BLS12_381].fromHex( "0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71c", "0x8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38f" ), - # x^3*y + # x³*y Fp2[BLS12_381].fromHex( "0x124c9ad43b6cf79bfbf7043de3811ad0761b0f37a1e26286b0e977c69aa274524e79097a56dc4bd9e1b371c71c718b10", "0x0" ) ] -const BLS12_381_h2c_G2_3_isogeny_map_yden* = [ +const BLS12_381_h2c_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ₙ @@ -139,12 +139,12 @@ const BLS12_381_h2c_G2_3_isogeny_map_yden* = [ "0x0", "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9d3" ), - # x^2 + # x² Fp2[BLS12_381].fromHex( "0x12", "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa99" ), - # x^3 + # x³ Fp2[BLS12_381].fromHex( "0x1", "0x0" diff --git a/constantine/math/curves/zoo_hash_to_curve.nim b/constantine/math/curves/zoo_hash_to_curve.nim index 1dd8a81..04913b2 100644 --- a/constantine/math/curves/zoo_hash_to_curve.nim +++ b/constantine/math/curves/zoo_hash_to_curve.nim @@ -9,7 +9,9 @@ import std/macros, ../config/curves, - ./bls12_381_g2_hash_to_curve + ../elliptic/ec_shortweierstrass_affine, + ./bls12_381_hash_to_curve_g1, + ./bls12_381_hash_to_curve_g2 {.experimental: "dynamicBindSym".} @@ -19,11 +21,9 @@ macro h2cConst*(C: static Curve, group, value: untyped): untyped = return bindSym($C & "_h2c_" & $group & "_" & $value) macro h2cIsomapPoly*(C: static Curve, - group: untyped, - isodegree: static int, + 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_" & - $group & "_" & $isodegree & - "_isogeny_map_" & $value) + $group & "_isogeny_map_" & $value) diff --git a/constantine/math/curves/zoo_subgroups.nim b/constantine/math/curves/zoo_subgroups.nim index 99e4f47..a915bb2 100644 --- a/constantine/math/curves/zoo_subgroups.nim +++ b/constantine/math/curves/zoo_subgroups.nim @@ -25,7 +25,7 @@ export func clearCofactor*[ECP](P: var ECP) {.inline.} = ## Clear the cofactor of a point on the curve ## From a point on the curve, returns a point on the subgroup of order r - when ECP.F.C in {BN254_Nogami, BN254_SNarks, BLS12_377, BLS12_381}: + when ECP.F.C in {BN254_Nogami, BN254_Snarks, BLS12_377, BLS12_381}: P.clearCofactorFast() else: P.clearCofactorReference() diff --git a/constantine/math/ec_shortweierstrass.nim b/constantine/math/ec_shortweierstrass.nim index ba3fc97..1b54f80 100644 --- a/constantine/math/ec_shortweierstrass.nim +++ b/constantine/math/ec_shortweierstrass.nim @@ -13,6 +13,7 @@ # ############################################################ import + ./arithmetic, elliptic/[ ec_shortweierstrass_affine, ec_shortweierstrass_jacobian, diff --git a/constantine/math/io/io_bigints.nim b/constantine/math/io/io_bigints.nim index 7d737ed..7634804 100644 --- a/constantine/math/io/io_bigints.nim +++ b/constantine/math/io/io_bigints.nim @@ -465,6 +465,9 @@ func appendHex*(dst: var string, big: BigInt, order: static Endianness = bigEndi # 2 Convert canonical uint to hex dst.add bytes.nativeEndianToHex(order) +func toHex*(a: openArray[byte]): string = + nativeEndianToHex(a, system.cpuEndian) + func toHex*(big: BigInt, order: static Endianness = bigEndian): string = ## Stringify an int to hex. ## Note. Leading zeros are not removed. diff --git a/sage/derive_hash_to_curve.sage b/sage/derive_hash_to_curve.sage index 0567386..d627d54 100644 --- a/sage/derive_hash_to_curve.sage +++ b/sage/derive_hash_to_curve.sage @@ -115,6 +115,127 @@ def find_z_sswu(F, A, B): return Z_cand ctr += 1 +# BLS12-381 G1 +# --------------------------------------------------------- +# Hardcoding from spec: +# - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.8.1 +# - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/f7dd3761/poc/sswu_opt_3mod4.sage#L126-L132 + +def genBLS12381G1_H2C_constants(curve_config): + curve_name = 'BLS12_381' + + # ------------------------------------------ + p = curve_config[curve_name]['field']['modulus'] + Fp = GF(p) + K. = PolynomialRing(Fp) + # ------------------------------------------ + + # Hash to curve isogenous curve parameters + # y² = x³ + A'*x + B' + + print('\n----> Hash-to-Curve map to isogenous BLS12-381 E\'1 <----\n') + buf = inspect.cleandoc(f""" + # Hash-to-Curve map to isogenous BLS12-381 E'1 constants + # ----------------------------------------------------------------- + # + # y² = x³ + A'*x + B' with p ≡ 3 (mod 4) the BLS12-381 characteristic (base modulus) + # + # Hardcoding from spec: + # - https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.8.1 + # - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/f7dd3761/poc/sswu_opt_3mod4.sage#L126-L132 + """) + buf += '\n\n' + + # Base constants + Aprime_E1 = Fp('0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d') + Bprime_E1 = Fp('0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0') + Z = Fp(11) + # Extra + minus_A = -Aprime_E1 + ZmulA = Z * Aprime_E1 + sqrt_minus_Z3 = sqrt(-Z^3) + + buf += f'const {curve_name}_h2c_G1_Aprime_E1* = ' + buf += field_to_nim(Aprime_E1, 'Fp', curve_name) + buf += '\n' + + buf += f'const {curve_name}_h2c_G1_Bprime_E1* = ' + buf += field_to_nim(Bprime_E1, 'Fp', curve_name) + buf += '\n' + + buf += f'const {curve_name}_h2c_G1_Z* = ' + buf += field_to_nim(Z, 'Fp', curve_name) + buf += '\n' + + buf += f'const {curve_name}_h2c_G1_minus_A* = ' + buf += field_to_nim(minus_A, 'Fp', curve_name) + buf += '\n' + + buf += f'const {curve_name}_h2c_G1_ZmulA* = ' + buf += field_to_nim(ZmulA, 'Fp', curve_name) + buf += '\n' + + buf += f'const {curve_name}_h2c_G1_sqrt_minus_Z3* = ' + buf += field_to_nim(sqrt_minus_Z3, 'Fp', curve_name) + buf += '\n' + + return buf + +def genBLS12381G1_H2C_isogeny_map(curve_config): + curve_name = 'BLS12_381' + + # Hash to curve isogenous curve parameters + # y² = x³ + A'*x + B' + + print('\n----> Hash-to-Curve 3-isogeny map BLS12-381 E\'1 constants <----\n') + buf = inspect.cleandoc(f""" + # Hash-to-Curve 11-isogeny map BLS12-381 E'1 constants + # ----------------------------------------------------------------- + # + # 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) + + """) + buf += '\n\n' + + p = curve_config[curve_name]['field']['modulus'] + Fp = GF(p) + + # Base constants - E1 + A = curve_config[curve_name]['curve']['a'] + B = curve_config[curve_name]['curve']['b'] + E1 = EllipticCurve(Fp, [A, B]) + + # Base constants - Isogenous curve E'1, degree 11 + Aprime_E1 = Fp('0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d') + Bprime_E1 = Fp('0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0') + Eprime1 = EllipticCurve(Fp, [Aprime_E1, Bprime_E1]) + + iso = EllipticCurveIsogeny(E=E1, kernel=None, codomain=Eprime1, degree=11).dual() + if (- iso.rational_maps()[1])(1, 1) > iso.rational_maps()[1](1, 1): + iso.switch_sign() + + (xm, ym) = iso.rational_maps() + maps = (xm.numerator(), xm.denominator(), ym.numerator(), ym.denominator()) + + buf += dump_poly( + 'BLS12_381_h2c_G1_11_isogeny_map_xnum', + xm.numerator(), 'Fp', curve_name) + buf += '\n' + buf += dump_poly( + 'BLS12_381_h2c_G1_11_isogeny_map_xden', + xm.denominator(), 'Fp', curve_name) + buf += '\n' + buf += dump_poly( + 'BLS12_381_h2c_G1_11_isogeny_map_ynum', + ym.numerator(), 'Fp', curve_name) + buf += '\n' + buf += dump_poly( + 'BLS12_381_h2c_G1_11_isogeny_map_yden', + ym.denominator(), 'Fp', curve_name) + + return buf + # BLS12-381 G2 # --------------------------------------------------------- # Hardcoding from spec: @@ -303,12 +424,32 @@ if __name__ == "__main__": curve = args.curve[0] group = args.curve[1] - if curve == 'BLS12_381' and group == 'G2': + if curve == 'BLS12_381' and group == 'G1': + h2c = genBLS12381G1_H2C_constants(Curves) + h2c += '\n\n' + h2c += genBLS12381G1_H2C_isogeny_map(Curves) + + 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 == 'BLS12_381' and group == 'G2': h2c = genBLS12381G2_H2C_constants(Curves) h2c += '\n\n' h2c += genBLS12381G2_H2C_isogeny_map(Curves) - with open(f'{curve.lower()}_g2_hash_to_curve.nim', 'w') as f: + with open(f'{curve.lower()}_hash_to_curve_g2.nim', 'w') as f: f.write(copyright()) f.write('\n\n') @@ -321,7 +462,7 @@ if __name__ == "__main__": f.write('\n\n') f.write(h2c) - print(f'Successfully created {curve.lower()}_g2_hash_to_curve.nim') + print(f'Successfully created {curve.lower()}_hash_to_curve_g2.nim') else: raise ValueError( curve + group + diff --git a/tests/math/t_hash_to_curve.nim b/tests/math/t_hash_to_curve.nim index 1313249..2f276ed 100644 --- a/tests/math/t_hash_to_curve.nim +++ b/tests/math/t_hash_to_curve.nim @@ -143,6 +143,12 @@ echo "Hash-to-curve" & '\n' # Hash-to-curve v8 to latest # https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/draft-irtf-cfrg-hash-to-curve-10/poc/vectors/BLS12381G2_XMD:SHA-256_SSWU_RO_.json +run_hash_to_curve_test( + ECP_ShortW_Prj[Fp[BLS12_381], G1], + "v8", + "tv_h2c_v8_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json" +) + run_hash_to_curve_test( ECP_ShortW_Prj[Fp2[BLS12_381], G2], "v8", @@ -151,6 +157,12 @@ run_hash_to_curve_test( # Hash-to-curve v7 (different domain separation tag) # https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/draft-irtf-cfrg-hash-to-curve-07/poc/vectors/BLS12381G2_XMD:SHA-256_SSWU_RO_.json +run_hash_to_curve_test( + ECP_ShortW_Prj[Fp[BLS12_381], G1], + "v7", + "tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json" +) + run_hash_to_curve_test( ECP_ShortW_Prj[Fp2[BLS12_381], G2], "v7", diff --git a/tests/math/vectors/tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json b/tests/math/vectors/tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json new file mode 100644 index 0000000..bef437d --- /dev/null +++ b/tests/math/vectors/tv_h2c_v7_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json @@ -0,0 +1,96 @@ +{ + "L": "0x40", + "Z": "0xb", + "ciphersuite": "BLS12381G1_XMD:SHA-256_SSWU_RO_", + "curve": "BLS12381G1", + "dst": "BLS12381G1_XMD:SHA-256_SSWU_RO_TESTGEN", + "expand": "XMD", + "field": { + "m": "0x1", + "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" + }, + "hash": "sha256", + "k": "0x80", + "map": { + "name": "SSWU" + }, + "randomOracle": true, + "vectors": [ + { + "P": { + "x": "0x0576730ab036cbac1d95b38dca905586f28d0a59048db4e8778782d89bff856ddef89277ead5a21e2975c4a6e3d8c79e", + "y": "0x1273e568bebf1864393c517f999b87c1eaa1b8432f95aea8160cd981b5b05d8cd4a7cf00103b6ef87f728e4b547dd7ae" + }, + "Q0": { + "x": "0x0b63f31bcc08df890f35ee362c8538fac22cf22637aa2ba22d9c85bc1bda995926ab690d86830bf8ae06f4d537ccf6d7", + "y": "0x0666f3763cc7b223ab237e313f6474c9a3c2f5ed985ee8d1faa0928b4b428ec1a366226125ce8f415edb3f706e71d80e" + }, + "Q1": { + "x": "0x0362c0f9d6cf4b73309a16b439d096b3ead588ab03cff57daf56fe747ab6d7774d5bfc0bd0a55bbeb0f05ec25cc191f6", + "y": "0x18d279b38babbd69aa176031655d138a731c049385aeef6eff3bf80e45ebcad0a941cdfc135e9ea1690a25eb6eac38e5" + }, + "msg": "", + "u": [ + "0x0633af2b38973d1cfb6e905292c41f209fe52e5be989b5e0d32c06a0e3c23e4843927cb8289b440f3cde0da46dc9ba0d", + "0x022474974e47d74c495de648eff1c8e4fabbae0d8ce3e30e3d1a5f9386cdf2582f78df056342d59ccca34321d93ef13d" + ] + }, + { + "P": { + "x": "0x061daf0cc00d8912dac1d4cf5a7c32fca97f8b3bf3f805121888e5eb89f77f9a9f406569027ac6d0e61b1229f42c43d6", + "y": "0x0de1601e5ba02cb637c1d35266f5700acee9850796dc88e860d022d7b9e7e3dce5950952e97861e5bb16d215c87f030d" + }, + "Q0": { + "x": "0x0e8334d819ca7fad50979a487e0bc95cb1410914f1d760f842fc3dd0102755e7ca81b0356da7b9771ab11bf50efbca67", + "y": "0x120397edf7002610f907c2d4ecfcc4e817f1f8915becb5919510796bf595d854048461662ad960347216b00dfc79db38" + }, + "Q1": { + "x": "0x013e1240e4da2abda009e263089cb8e57f1b24d0d1df09f644cc9c9a8b3fde7d154c7f1b0895a0af22b902a8140fb3ce", + "y": "0x0d6a9f75f2088dcac8f8ec0ab94bf2dac23b7b832bf23c91f9241f753c5831054b058192351a972347cb19806e78477d" + }, + "msg": "abc", + "u": [ + "0x07df547923a0c77ddc4fea1a8a2eb156aef1746d5452239a55a378c5d3590e0b75cddff0eef2a9214a41923f2be27b55", + "0x0f95fd8f00e25c3073ec07f249a7d527e580f01a6986158aeb064ed831d544fb9c5dbceb6604c908db5430d8f3d1c4f3" + ] + }, + { + "P": { + "x": "0x0fb3455436843e76079c7cf3dfef75e5a104dfe257a29a850c145568d500ad31ccfe79be9ae0ea31a722548070cf98cd", + "y": "0x177989f7e2c751658df1b26943ee829d3ebcf131d8f805571712f3a7527ee5334ecff8a97fc2a50cea86f5e6212e9a57" + }, + "Q0": { + "x": "0x03ff794b445b926906b2fa710ba5db9f7b8689429a1630ab672854b5ba1a7c59bf3667d64aa63824a8798dcb631bfa9a", + "y": "0x1581711cffadabb6136f4bf57749e04b92787c7486da6b6da1fa758655c9af275b23540370d9f3987a100f0d3dc8e6db" + }, + "Q1": { + "x": "0x133bdea6715b4ef780693cd0055025b221becc8e04506a776484590df9b43af62ef402778a9c98ec540bc293e9741565", + "y": "0x0d953a5bdb2d16e62bfeab742e70ea64fddd83e8210b2416d40a02b0d90986fd0d00a3d77751ac467964ecc037dc284a" + }, + "msg": "abcdef0123456789", + "u": [ + "0x0a5d2ed6108aa08d652ab61af11c12d8750ed179cb962779c7b5393f219ad4b78b7b252a2896a341ad451e93f1904fb0", + "0x17d6cd69f4bd29b85c550539a296c76ced075d9d39a81f4cdc2804a7184ff9ea4a5a85dac4a2a61e317894d0fba55740" + ] + }, + { + "P": { + "x": "0x0514af2137c1ae1d78d5cb97ee606ea142824c199f0f25ac463a0c78200de57640d34686521d3e9cf6b3721834f8a038", + "y": "0x047a85d6898416a0899e26219bca7c4f0fa682717199de196b02b95eaf9fb55456ac3b810e78571a1b7f5692b7c58ab6" + }, + "Q0": { + "x": "0x17dc55e956f1e24c800fa6a61ccba179fbba6bbe27e96ab16b862defa4782d567f8733f1ee39acd7b665ba0204318d7f", + "y": "0x0252510413c32677817c4ee5f84e4c8f66721e489913a50eac550f41ad48b01b763ec9eed5bd68bcac76131ca9ebd741" + }, + "Q1": { + "x": "0x03d2f5444d39ed19b6087cd684d2a72795038d1ffe9ab120f7e5ce41fb48af76bba3eb4efc8a696418fcb3c5cfdbfd94", + "y": "0x061a1fe191cbf373b74d5f642148722160b5524dd2c06a49c5e4d5480966de5b0854d53cbea144b482fa687eaf9fdc66" + }, + "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "u": [ + "0x111e524a9da7ae49a1cf6b03f5bc9d374f16951dcba59d03529a94afd4e5ba171fb1dffa373d13993503d594abd1b5ed", + "0x006ad90d8d5101c88db3923376f2a33ff922ba39342d1a5462785796b6ebdec10dedb5e9cd9ff8e611af939f7f617844" + ] + } + ] +} diff --git a/tests/math/vectors/tv_h2c_v8_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json b/tests/math/vectors/tv_h2c_v8_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json new file mode 100644 index 0000000..46c7574 --- /dev/null +++ b/tests/math/vectors/tv_h2c_v8_BLS12_381_hash_to_G1_SHA256_SSWU_RO.json @@ -0,0 +1,115 @@ +{ + "L": "0x40", + "Z": "0xb", + "ciphersuite": "BLS12381G1_XMD:SHA-256_SSWU_RO_", + "curve": "BLS12-381 G1", + "dst": "QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_", + "expand": "XMD", + "field": { + "m": "0x1", + "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" + }, + "hash": "sha256", + "k": "0x80", + "map": { + "name": "SSWU" + }, + "randomOracle": true, + "vectors": [ + { + "P": { + "x": "0x052926add2207b76ca4fa57a8734416c8dc95e24501772c814278700eed6d1e4e8cf62d9c09db0fac349612b759e79a1", + "y": "0x08ba738453bfed09cb546dbb0783dbb3a5f1f566ed67bb6be0e8c67e2e81a4cc68ee29813bb7994998f3eae0c9c6a265" + }, + "Q0": { + "x": "0x11a3cce7e1d90975990066b2f2643b9540fa40d6137780df4e753a8054d07580db3b7f1f03396333d4a359d1fe3766fe", + "y": "0x0eeaf6d794e479e270da10fdaf768db4c96b650a74518fc67b04b03927754bac66f3ac720404f339ecdcc028afa091b7" + }, + "Q1": { + "x": "0x160003aaf1632b13396dbad518effa00fff532f604de1a7fc2082ff4cb0afa2d63b2c32da1bef2bf6c5ca62dc6b72f9c", + "y": "0x0d8bb2d14e20cf9f6036152ed386d79189415b6d015a20133acb4e019139b94e9c146aaad5817f866c95d609a361735e" + }, + "msg": "", + "u": [ + "0x0ba14bd907ad64a016293ee7c2d276b8eae71f25a4b941eece7b0d89f17f75cb3ae5438a614fb61d6835ad59f29c564f", + "0x019b9bd7979f12657976de2884c7cce192b82c177c80e0ec604436a7f538d231552f0d96d9f7babe5fa3b19b3ff25ac9" + ] + }, + { + "P": { + "x": "0x03567bc5ef9c690c2ab2ecdf6a96ef1c139cc0b2f284dca0a9a7943388a49a3aee664ba5379a7655d3c68900be2f6903", + "y": "0x0b9c15f3fe6e5cf4211f346271d7b01c8f3b28be689c8429c85b67af215533311f0b8dfaaa154fa6b88176c229f2885d" + }, + "Q0": { + "x": "0x125435adce8e1cbd1c803e7123f45392dc6e326d292499c2c45c5865985fd74fe8f042ecdeeec5ecac80680d04317d80", + "y": "0x0e8828948c989126595ee30e4f7c931cbd6f4570735624fd25aef2fa41d3f79cfb4b4ee7b7e55a8ce013af2a5ba20bf2" + }, + "Q1": { + "x": "0x11def93719829ecda3b46aa8c31fc3ac9c34b428982b898369608e4f042babee6c77ab9218aad5c87ba785481eff8ae4", + "y": "0x0007c9cef122ccf2efd233d6eb9bfc680aa276652b0661f4f820a653cec1db7ff69899f8e52b8e92b025a12c822a6ce6" + }, + "msg": "abc", + "u": [ + "0x0d921c33f2bad966478a03ca35d05719bdf92d347557ea166e5bba579eea9b83e9afa5c088573c2281410369fbd32951", + "0x003574a00b109ada2f26a37a91f9d1e740dffd8d69ec0c35e1e9f4652c7dba61123e9dd2e76c655d956e2b3462611139" + ] + }, + { + "P": { + "x": "0x11e0b079dea29a68f0383ee94fed1b940995272407e3bb916bbf268c263ddd57a6a27200a784cbc248e84f357ce82d98", + "y": "0x03a87ae2caf14e8ee52e51fa2ed8eefe80f02457004ba4d486d6aa1f517c0889501dc7413753f9599b099ebcbbd2d709" + }, + "Q0": { + "x": "0x08834484878c217682f6d09a4b51444802fdba3d7f2df9903a0ddadb92130ebbfa807fffa0eabf257d7b48272410afff", + "y": "0x0b318f7ecf77f45a0f038e62d7098221d2dbbca2a394164e2e3fe953dc714ac2cde412d8f2d7f0c03b259e6795a2508e" + }, + "Q1": { + "x": "0x158418ed6b27e2549f05531a8281b5822b31c3bf3144277fbb977f8d6e2694fedceb7011b3c2b192f23e2a44b2bd106e", + "y": "0x1879074f344471fac5f839e2b4920789643c075792bec5af4282c73f7941cda5aa77b00085eb10e206171b9787c4169f" + }, + "msg": "abcdef0123456789", + "u": [ + "0x062d1865eb80ebfa73dcfc45db1ad4266b9f3a93219976a3790ab8d52d3e5f1e62f3b01795e36834b17b70e7b76246d4", + "0x0cdc3e2f271f29c4ff75020857ce6c5d36008c9b48385ea2f2bf6f96f428a3deb798aa033cd482d1cdc8b30178b08e3a" + ] + }, + { + "P": { + "x": "0x15f68eaa693b95ccb85215dc65fa81038d69629f70aeee0d0f677cf22285e7bf58d7cb86eefe8f2e9bc3f8cb84fac488", + "y": "0x1807a1d50c29f430b8cafc4f8638dfeeadf51211e1602a5f184443076715f91bb90a48ba1e370edce6ae1062f5e6dd38" + }, + "Q0": { + "x": "0x0cbd7f84ad2c99643fea7a7ac8f52d63d66cefa06d9a56148e58b984b3dd25e1f41ff47154543343949c64f88d48a710", + "y": "0x052c00e4ed52d000d94881a5638ae9274d3efc8bc77bc0e5c650de04a000b2c334a9e80b85282a00f3148dfdface0865" + }, + "Q1": { + "x": "0x06493fb68f0d513af08be0372f849436a787e7b701ae31cb964d968021d6ba6bd7d26a38aaa5a68e8c21a6b17dc8b579", + "y": "0x02e98f2ccf5802b05ffaac7c20018bc0c0b2fd580216c4aa2275d2909dc0c92d0d0bdc979226adeb57a29933536b6bb4" + }, + "msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "u": [ + "0x010476f6a060453c0b1ad0b628f3e57c23039ee16eea5e71bb87c3b5419b1255dc0e5883322e563b84a29543823c0e86", + "0x0b1a912064fb0554b180e07af7e787f1f883a0470759c03c1b6509eb8ce980d1670305ae7b928226bb58fdc0a419f46e" + ] + }, + { + "P": { + "x": "0x082aabae8b7dedb0e78aeb619ad3bfd9277a2f77ba7fad20ef6aabdc6c31d19ba5a6d12283553294c1825c4b3ca2dcfe", + "y": "0x05b84ae5a942248eea39e1d91030458c40153f3b654ab7872d779ad1e942856a20c438e8d99bc8abfbf74729ce1f7ac8" + }, + "Q0": { + "x": "0x0cf97e6dbd0947857f3e578231d07b309c622ade08f2c08b32ff372bd90db19467b2563cc997d4407968d4ac80e154f8", + "y": "0x127f0cddf2613058101a5701f4cb9d0861fd6c2a1b8e0afe194fccf586a3201a53874a2761a9ab6d7220c68661a35ab3" + }, + "Q1": { + "x": "0x092f1acfa62b05f95884c6791fba989bbe58044ee6355d100973bf9553ade52b47929264e6ae770fb264582d8dce512a", + "y": "0x028e6d0169a72cfedb737be45db6c401d3adfb12c58c619c82b93a5dfcccef12290de530b0480575ddc8397cda0bbebf" + }, + "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "u": [ + "0x0a8ffa7447f6be1c5a2ea4b959c9454b431e29ccc0802bc052413a9c5b4f9aac67a93431bd480d15be1e057c8a08e8c6", + "0x05d487032f602c90fa7625dbafe0f4a49ef4a6b0b33d7bb349ff4cf5410d297fd6241876e3e77b651cfc8191e40a68b7" + ] + } + ] +}