BN254 - Hash-to-Curve (SVDW method) (#190)

* Hash to BN254-Snarks

* Test SVDW code path with old v7 vectors for BLS12-381

* add benches
This commit is contained in:
Mamy Ratsimbazafy 2022-04-26 21:24:07 +02:00 committed by GitHub
parent 062ae56867
commit e9e7a1809c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1166 additions and 230 deletions

View File

@ -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()

View File

@ -79,6 +79,7 @@ proc main() =
finalExpBLS12Bench(curve, Iters)
pairingBLS12Bench(curve, Iters)
separator()
hashToCurveBLS12381G1Bench(Iters)
hashToCurveBLS12381G2Bench(Iters)
separator()

View File

@ -78,6 +78,9 @@ proc main() =
finalExpBNBench(curve, Iters)
pairingBNBench(curve, Iters)
separator()
hashToCurveBN254SnarksG1Bench(Iters)
hashToCurveBN254SnarksG2Bench(Iters)
separator()
main()
notes()

View File

@ -212,15 +212,67 @@ 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_BLS12381G2-SHA256-SSWU-RO_POP_"
const dst = "BLS_SIG_BLS12381G1-SHA256-SSWU-RO_POP_"
let msg = "Mr F was here"
var P: ECP_ShortW_Prj[Fp2[BLS12_381], G2]
var P: ECP_ShortW_Prj[Fp[BLS12_381], G1]
bench("Hash to G2 (Draft #11)", BLS12_381, iters):
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
const dst = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_"
let msg = "Mr F was here"
var P: ECP_ShortW_Prj[Fp2[BLS12_381], G2]
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,

View File

@ -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):

View File

@ -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
)

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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ₙ

View File

@ -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ₙ

View File

@ -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
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()

View File

@ -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")

View File

@ -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"
)

View File

@ -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
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()

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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 = + 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.<u> = 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.<u> = PolynomialRing(Fp)
if G2_field == 'Fp2':
Fp2.<beta> = 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.<u> = PolynomialRing(Fp)
Fp2.<beta> = 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')

View File

@ -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)

View File

@ -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"
]
}
]
}

View File

@ -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"
]
}
]
}

View File

@ -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"
)

View File

@ -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])

View File

@ -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