Try to use hash-to-curve for BN254_Snarks but no low-degree isogeny [skip ci]
This commit is contained in:
parent
65eedd1cf7
commit
062ae56867
|
@ -132,9 +132,9 @@ proc sqrtBench*(T: typedesc, iters: int) =
|
|||
let x = rng.random_unsafe(T)
|
||||
|
||||
const algoType = block:
|
||||
when T.C.hasP3mod4_primeModulus():
|
||||
when T.C.has_P_3mod4_primeModulus():
|
||||
"p ≡ 3 (mod 4)"
|
||||
elif T.C.hasP5mod8_primeModulus():
|
||||
elif T.C.has_P_5mod8_primeModulus():
|
||||
"p ≡ 5 (mod 8)"
|
||||
else:
|
||||
"Tonelli-Shanks"
|
||||
|
|
|
@ -84,13 +84,13 @@ func mapToCurve[F; G: static Subgroup](
|
|||
# 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():
|
||||
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.hasP3mod4_primeModulus():
|
||||
elif F is Fp2 and F.C.has_Psquare_9mod16_primePower():
|
||||
# p ≡ 3 (mod 4) => p² ≡ 9 (mod 16)
|
||||
mapToIsoCurve_sswuG2_opt9mod16(
|
||||
xn, xd,
|
||||
|
@ -131,12 +131,12 @@ func mapToCurve_fusedAdd[F; G: static Subgroup](
|
|||
# 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():
|
||||
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))
|
||||
elif F is Fp2 and F.C.hasP3mod4_primeModulus():
|
||||
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)
|
||||
|
|
|
@ -44,7 +44,7 @@ func invsqrt_p3mod4(r: var Fp, a: Fp) =
|
|||
# a^((p-1)/2)) * a^-1 ≡ 1/a (mod p)
|
||||
# a^((p-3)/2)) ≡ 1/a (mod p)
|
||||
# a^((p-3)/4)) ≡ 1/√a (mod p) # Requires p ≡ 3 (mod 4)
|
||||
static: doAssert Fp.C.hasP3mod4_primeModulus()
|
||||
static: doAssert Fp.C.has_P_3mod4_primeModulus()
|
||||
when FP.C.hasSqrtAddchain():
|
||||
r.invsqrt_addchain(a)
|
||||
else:
|
||||
|
@ -104,7 +104,7 @@ func invsqrt_p5mod8(r: var Fp, a: Fp) =
|
|||
#
|
||||
# Hence we set β = (2a)^((p-1)/4)
|
||||
# and α = (β/2a)⁽¹⸍²⁾= (2a)^(((p-1)/4 - 1)/2) = (2a)^((p-5)/8)
|
||||
static: doAssert Fp.C.hasP5mod8_primeModulus()
|
||||
static: doAssert Fp.C.has_P_5mod8_primeModulus()
|
||||
var alpha{.noInit.}, beta{.noInit.}: Fp
|
||||
|
||||
# α = (2a)^((p-5)/8)
|
||||
|
@ -234,9 +234,9 @@ func invsqrt*[C](r: var Fp[C], a: Fp[C]) =
|
|||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
## This procedure is constant-time
|
||||
when C.hasP3mod4_primeModulus():
|
||||
when C.has_P_3mod4_primeModulus():
|
||||
r.invsqrt_p3mod4(a)
|
||||
elif C.hasP5mod8_primeModulus():
|
||||
elif C.has_P_5mod8_primeModulus():
|
||||
r.invsqrt_p5mod8(a)
|
||||
else:
|
||||
r.invsqrt_tonelli_shanks(a)
|
||||
|
@ -334,7 +334,7 @@ func isSquare*(a: Fp): SecretBool =
|
|||
)
|
||||
else:
|
||||
# We reuse the optimized addition chains instead of exponentiation by (p-1)/2
|
||||
when Fp.C.hasP3mod4_primeModulus() or Fp.C.hasP5mod8_primeModulus():
|
||||
when Fp.C.has_P_3mod4_primeModulus() or Fp.C.has_P_5mod8_primeModulus():
|
||||
var sqrt{.noInit.}, invsqrt{.noInit.}: Fp
|
||||
return sqrt_invsqrt_if_square(sqrt, invsqrt, a)
|
||||
else:
|
||||
|
|
|
@ -39,14 +39,18 @@ 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
|
||||
|
||||
func hasP3mod4_primeModulus*(C: static Curve): static bool =
|
||||
func has_P_3mod4_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 =
|
||||
func has_P_5mod8_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 =
|
||||
func has_P_9mod16_primeModulus*(C: static Curve): static bool =
|
||||
## Returns true iff p ≡ 9 (mod 16)
|
||||
(BaseType(C.Mod.limbs[0]) and 15) == 9
|
||||
|
||||
func has_Psquare_9mod16_primePower*(C: static Curve): static bool =
|
||||
## Returns true iff p² ≡ 9 (mod 16)
|
||||
((BaseType(C.Mod.limbs[0]) * BaseType(C.Mod.limbs[0])) and 15) == 9
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
import os
|
||||
import inspect, textwrap
|
||||
import sage.schemes.elliptic_curves.isogeny_small_degree as isd
|
||||
|
||||
# Working directory
|
||||
# ---------------------------------------------------------
|
||||
|
@ -86,9 +87,24 @@ def dump_poly(name, poly, field, curve):
|
|||
result += ']'
|
||||
return result
|
||||
|
||||
# Unused
|
||||
# Isogenies
|
||||
# ---------------------------------------------------------
|
||||
|
||||
def find_iso(E):
|
||||
"""
|
||||
Find an isogenous curve with j-invariant not in {0, 1728} so that
|
||||
Simplified Shallue-van de Woestijne method is directly applicable
|
||||
(i.e the Elliptic Curve coefficient y² = x³ + A*x + B have AB != 0)
|
||||
"""
|
||||
for p_test in primes(30):
|
||||
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}')
|
||||
return isos[0].dual()
|
||||
print(f'Found no isogenies')
|
||||
return None
|
||||
|
||||
def find_z_sswu(F, A, B):
|
||||
"""
|
||||
https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#ref-SAGE
|
||||
|
@ -115,6 +131,86 @@ def find_z_sswu(F, A, B):
|
|||
return Z_cand
|
||||
ctr += 1
|
||||
|
||||
def search_isogeny(curve_name, curve_config):
|
||||
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 - E2
|
||||
embedding_degree = curve_config[curve_name]['tower']['embedding_degree']
|
||||
twist_degree = curve_config[curve_name]['tower']['twist_degree']
|
||||
twist = curve_config[curve_name]['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_name]['tower']['QNR_Fp']
|
||||
elif G2_field_degree == 1:
|
||||
if twist_degree == 6:
|
||||
# Only for complete serialization
|
||||
non_residue_fp = curve_config[curve_name]['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_name]['tower']['SNR_Fp2']
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
elif G2_field == 'Fp':
|
||||
G2F = Fp
|
||||
if twist_degree == 6:
|
||||
non_residue_twist = curve_config[curve_name]['tower']['SNR_Fp']
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
if twist == 'D_Twist':
|
||||
G2B = B/G2F(non_residue_twist)
|
||||
E2 = EllipticCurve(G2F, [0, G2B])
|
||||
elif twist == 'M_Twist':
|
||||
G2B = B*G2F(non_residue_twist)
|
||||
E2 = EllipticCurve(G2F, [0, G2B])
|
||||
else:
|
||||
raise ValueError('E2 must be a D_Twist or M_Twist but found ' + twist)
|
||||
|
||||
|
||||
# Isogenies:
|
||||
iso_G1 = find_iso(E1)
|
||||
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()
|
||||
|
||||
# Z
|
||||
Z_G1 = find_z_sswu(Fp, a_G1, b_G1)
|
||||
Z_G2 = find_z_sswu(Fp2, a_G2, b_G2)
|
||||
|
||||
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"{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)}")
|
||||
|
||||
# BLS12-381 G1
|
||||
# ---------------------------------------------------------
|
||||
# Hardcoding from spec:
|
||||
|
@ -192,8 +288,8 @@ def genBLS12381G1_H2C_isogeny_map(curve_config):
|
|||
# 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)
|
||||
# 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)
|
||||
|
||||
""")
|
||||
buf += '\n\n'
|
||||
|
@ -374,7 +470,7 @@ def genBLS12381G2_H2C_isogeny_map(curve_config):
|
|||
else:
|
||||
Btwist = B / Fp2(SNR_Fp2)
|
||||
|
||||
E2 = EllipticCurve(Fp2, [A, B * Fp2(SNR_Fp2)])
|
||||
E2 = EllipticCurve(Fp2, [A, Btwist])
|
||||
|
||||
# Base constants - Isogenous curve E'2, degree 3
|
||||
Aprime_E2 = Fp2([0, 240])
|
||||
|
@ -413,7 +509,11 @@ def genBLS12381G2_H2C_isogeny_map(curve_config):
|
|||
if __name__ == "__main__":
|
||||
# Usage
|
||||
# BLS12-381
|
||||
# sage sage/derive_hash_to_curve.sage BLS12_381 G2
|
||||
# sage sage/derive_hash_to_curve.sage BLS12_381 G2
|
||||
# for Hash-to-Curve
|
||||
# or
|
||||
# sage sage/derive_hash_to_curve.sage BLS12_381 iso
|
||||
# to search for a suitable isogeny
|
||||
|
||||
from argparse import ArgumentParser
|
||||
|
||||
|
@ -422,9 +522,12 @@ if __name__ == "__main__":
|
|||
args = parser.parse_args()
|
||||
|
||||
curve = args.curve[0]
|
||||
group = args.curve[1]
|
||||
group_or_iso = args.curve[1]
|
||||
|
||||
if curve == 'BLS12_381' and group == 'G1':
|
||||
if group_or_iso == 'iso':
|
||||
search_isogeny(curve, Curves)
|
||||
|
||||
elif curve == 'BLS12_381' and group_or_iso == 'G1':
|
||||
h2c = genBLS12381G1_H2C_constants(Curves)
|
||||
h2c += '\n\n'
|
||||
h2c += genBLS12381G1_H2C_isogeny_map(Curves)
|
||||
|
@ -444,7 +547,7 @@ if __name__ == "__main__":
|
|||
|
||||
print(f'Successfully created {curve.lower()}_hash_to_curve_g1.nim')
|
||||
|
||||
elif curve == 'BLS12_381' and group == 'G2':
|
||||
elif curve == 'BLS12_381' and group_or_iso == 'G2':
|
||||
h2c = genBLS12381G2_H2C_constants(Curves)
|
||||
h2c += '\n\n'
|
||||
h2c += genBLS12381G2_H2C_isogeny_map(Curves)
|
||||
|
@ -465,6 +568,6 @@ if __name__ == "__main__":
|
|||
print(f'Successfully created {curve.lower()}_hash_to_curve_g2.nim')
|
||||
else:
|
||||
raise ValueError(
|
||||
curve + group +
|
||||
curve + group_or_iso +
|
||||
' is not configured '
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue