Fuzz Fix - Hash-To-Curve - Isogeny EC add non-fully-reduced input (#250)

* H2C: fix fuzz failure 2, non-fully reduced in isogeny EC addition

* faster hashToG2 by using sparsity
This commit is contained in:
Mamy Ratsimbazafy 2023-07-03 06:57:22 +02:00 committed by GitHub
parent b7687ddc4a
commit d69c7bf8e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 81 additions and 50 deletions

View File

@ -483,7 +483,7 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
# Hashing to elliptic curves
# ----------------------------------------------------------
("tests/t_hash_to_field.nim", false),
# ("tests/t_hash_to_curve_random.nim", false),
("tests/t_hash_to_curve_random.nim", false),
("tests/t_hash_to_curve.nim", false),
# Protocols

View File

@ -133,12 +133,12 @@ func mapToCurve_svdw_fusedAdd[F; G: static Subgroup](
## finite or extension field F
## to an elliptic curve E
## and add them
var P0{.noInit.}, P1{.noInit.}: ECP_ShortW_Aff[F, G]
P0.mapToCurve_svdw(u0)
P1.mapToCurve_svdw(u1)
var Q0{.noInit.}, Q1{.noInit.}: ECP_ShortW_Aff[F, G]
Q0.mapToCurve_svdw(u0)
Q1.mapToCurve_svdw(u1)
r.fromAffine(P0)
r += P1
r.fromAffine(Q0)
r += Q1
func mapToCurve_sswu_fusedAdd[F; G: static Subgroup](
r: var ECP_ShortW_Jac[F, G],
@ -164,25 +164,24 @@ func mapToCurve_sswu_fusedAdd[F; G: static Subgroup](
# 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]
var Q0{.noInit.}, Q1{.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, sswu, G1, Aprime_E1))
Q0.mapToIsoCurve_sswuG1_opt3mod4(u0)
Q1.mapToIsoCurve_sswuG1_opt3mod4(u1)
Q0.sum(Q0, Q1, 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, sswu, G2, Aprime_E2))
Q0.mapToIsoCurve_sswuG2_opt9mod16(u0)
Q1.mapToIsoCurve_sswuG2_opt9mod16(u1)
Q0.sum(Q0, Q1, h2CConst(F.C, sswu, G2, Aprime_E2))
else:
{.error: "Not implemented".}
# 2. Map from E'2 to E2
r.h2c_isogeny_map(P0)
r.h2c_isogeny_map(Q0)
else:
{.error: "Not implemented".}

View File

@ -43,7 +43,7 @@ func isInf*(P: ECP_ShortW_Jac): SecretBool {.inline.} =
##
## Note: the jacobian coordinates equation is
## Y² = X³ + aXZ⁴ + bZ⁶
##
##
## When Z = 0 in the equation, it reduces to
## Y² = X³
## (yZ³)² = (xZ²)³ which is true for any x, y coordinates
@ -209,6 +209,15 @@ template sumImpl[F; G: static Subgroup](
# | Y₃ = R*(V-X₃)-S₁*HHH | Y₃ = M*(S-X₃)-YY*YY | | |
# | Z₃ = Z₁*Z₂*H | Z₃ = Y₁*Z₁ | | |
# "when" static evaluation doesn't shortcut booleans :/
# which causes issues when CoefA isn't an int but Fp or Fp2
when CoefA is int:
const CoefA_eq_zero = CoefA == 0
const CoefA_eq_minus3 {.used.} = CoefA == -3
else:
const CoefA_eq_zero = false
const CoefA_eq_minus3 = false
var Z1Z1 {.noInit.}, U1 {.noInit.}, S1 {.noInit.}, H {.noInit.}, R {.noinit.}: F
block: # Addition-only, check for exceptional cases
@ -218,7 +227,7 @@ template sumImpl[F; G: static Subgroup](
S1 *= P.y # S₁ = Y₁*Z₂³
U1.prod(P.x, Z2Z2) # U₁ = X₁*Z₂²
Z1Z1.square(P.z, skipFinalSub = true)
Z1Z1.square(P.z, skipFinalSub = not CoefA_eq_minus3)
S2.prod(P.z, Z1Z1, skipFinalSub = true)
S2 *= Q.y # S₂ = Y₂*Z₁³
U2.prod(Q.x, Z1Z1) # U₂ = X₂*Z₁²
@ -248,15 +257,6 @@ template sumImpl[F; G: static Subgroup](
V_or_S *= HH_or_YY # V = U₁*HH (add) or S = X₁*YY (dbl)
block: # Compute M for doubling
# "when" static evaluation doesn't shortcut booleans :/
# which causes issues when CoefA isn't an int but Fp or Fp2
when CoefA is int:
const CoefA_eq_zero = CoefA == 0
const CoefA_eq_minus3 {.used.} = CoefA == -3
else:
const CoefA_eq_zero = false
const CoefA_eq_minus3 = false
when CoefA_eq_zero:
var a {.noInit.} = H
var b {.noInit.} = HH_or_YY
@ -289,14 +289,13 @@ template sumImpl[F; G: static Subgroup](
var b{.noInit.} = HH_or_YY
a.ccopy(P.x, isDbl)
b.ccopy(P.x, isDbl)
HHH_or_Mpre.prod(a, b, true) # HHH or X₁²
HHH_or_Mpre.prod(a, b) # HHH or X₁²
# Assuming doubling path
a.square(HHH_or_Mpre, skipFinalSub = true)
a *= HHH_or_Mpre # a = 3X₁²
b.square(Z1Z1)
# b.mulCheckSparse(CoefA) # TODO: broken static compile-time type inference
b *= CoefA # b = αZZ, with α the "a" coefficient of the curve
b.mulCheckSparse(CoefA) # b = αZZ, with α the "a" coefficient of the curve
a += b
a.div2()
@ -430,6 +429,17 @@ func madd*[F; G: static Subgroup](
# | Z₃ = Z₁*Z₂*H | Z₃ = Y₁*Z₁ | | |
#
# For mixed adddition we just set Z₂ = 1
# "when" static evaluation doesn't shortcut booleans :/
# which causes issues when CoefA isn't an int but Fp or Fp2
const CoefA = F.C.getCoefA()
when CoefA is int:
const CoefA_eq_zero = CoefA == 0
const CoefA_eq_minus3 {.used.} = CoefA == -3
else:
const CoefA_eq_zero = false
const CoefA_eq_minus3 = false
var Z1Z1 {.noInit.}, U1 {.noInit.}, S1 {.noInit.}, H {.noInit.}, R {.noinit.}: F
block: # Addition-only, check for exceptional cases
@ -437,7 +447,7 @@ func madd*[F; G: static Subgroup](
U1 = P.x
S1 = P.y
Z1Z1.square(P.z, skipFinalSub = true)
Z1Z1.square(P.z, skipFinalSub = not CoefA_eq_minus3)
S2.prod(P.z, Z1Z1, skipFinalSub = true)
S2 *= Q.y # S₂ = Y₂*Z₁³
U2.prod(Q.x, Z1Z1) # U₂ = X₂*Z₁²
@ -467,16 +477,6 @@ func madd*[F; G: static Subgroup](
V_or_S *= HH_or_YY # V = U₁*HH (add) or S = X₁*YY (dbl)
block: # Compute M for doubling
# "when" static evaluation doesn't shortcut booleans :/
# which causes issues when CoefA isn't an int but Fp or Fp2
const CoefA = F.C.getCoefA()
when CoefA is int:
const CoefA_eq_zero = CoefA == 0
const CoefA_eq_minus3 {.used.} = CoefA == -3
else:
const CoefA_eq_zero = false
const CoefA_eq_minus3 = false
when CoefA_eq_zero:
var a {.noInit.} = H
var b {.noInit.} = HH_or_YY
@ -509,14 +509,13 @@ func madd*[F; G: static Subgroup](
var b{.noInit.} = HH_or_YY
a.ccopy(P.x, isDbl)
b.ccopy(P.x, isDbl)
HHH_or_Mpre.prod(a, b, true) # HHH or X₁²
HHH_or_Mpre.prod(a, b) # HHH or X₁²
# Assuming doubling path
a.square(HHH_or_Mpre, skipFinalSub = true)
a *= HHH_or_Mpre # a = 3X₁²
b.square(Z1Z1)
# b.mulCheckSparse(CoefA) # TODO: broken static compile-time type inference
b *= CoefA # b = αZZ, with α the "a" coefficient of the curve
b.mulCheckSparse(CoefA) # b = αZZ, with α the "a" coefficient of the curve
a += b
a.div2()

View File

@ -1440,11 +1440,11 @@ func mul_sparse_by_x0*(a: var QuadraticExt, sparseB: QuadraticExt) =
a.mul_sparse_by_x0(a, sparseB)
template mulCheckSparse*(a: var QuadraticExt, b: QuadraticExt) =
when b.isOne().bool:
when isOne(b).bool:
discard
elif b.isMinusOne().bool:
elif isMinusOne(b).bool:
a.neg()
elif b.c0.isZero().bool and b.c1.isOne().bool:
elif isZero(c0(b)).bool and isOne(c1(b)).bool:
var t {.noInit.}: type(a.c0)
when fromComplexExtension(b):
t.neg(a.c1)
@ -1454,7 +1454,7 @@ template mulCheckSparse*(a: var QuadraticExt, b: QuadraticExt) =
t.prod(a.c1, NonResidue)
a.c1 = a.c0
a.c0 = t
elif b.c0.isZero().bool and b.c1.isMinusOne().bool:
elif isZero(c0(b)).bool and isMinusOne(c1(b)).bool:
var t {.noInit.}: type(a.c0)
when fromComplexExtension(b):
t = a.c1
@ -1464,9 +1464,9 @@ template mulCheckSparse*(a: var QuadraticExt, b: QuadraticExt) =
t.prod(a.c1, NonResidue)
a.c1.neg(a.c0)
a.c0.neg(t)
elif b.c0.isZero().bool:
elif isZero(c0(b)).bool:
a.mul_sparse_by_0y(b)
elif b.c1.isZero().bool:
elif isZero(c1(b)).bool:
a.mul_sparse_by_x0(b)
else:
a *= b

View File

@ -16,6 +16,7 @@ import
../constantine/hash_to_curve/hash_to_curve,
../constantine/hashes,
../constantine/math/constants/zoo_subgroups,
../constantine/math/io/io_ec,
# Test utilities
../helpers/prng_unsafe
@ -54,4 +55,36 @@ suite "Hash-to-curve produces points on curve and in correct subgroup":
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])
testH2C_consistency(ECP_ShortW_Aff[Fp2[BN254_Snarks], G2])
proc testH2C_guidovranken_fuzz_failure_2() =
# From Guido Vranken differential fuzzing
# Summing elliptic curve on an isogeny was mistakenly not fully reducing HHH_or_Mpre
let msg = [
uint8 0xa7, 0x1b, 0x0a, 0x38, 0xd4, 0x09, 0x2b, 0x3b,
0xdc, 0x9e, 0x75, 0x0a, 0x27, 0x0a, 0xd5, 0xdd,
0x16, 0x6f, 0x32, 0x5c, 0x16, 0xf5, 0x6d, 0x2f,
0x87, 0xbb, 0x6b, 0xf5, 0xd2]
let dst = [
uint8 0x5a, 0x59, 0xae, 0x59, 0x04, 0x8a, 0x29, 0x0f,
0x9a, 0xc1, 0x80, 0x26, 0xba, 0x6d, 0xd8, 0x7f,
0x54, 0xf0, 0x5a, 0x01, 0x49, 0xad, 0x2b, 0x95,
0xfe]
let aug = [
uint8 0x70, 0x20, 0xde, 0x7c, 0x51, 0x88, 0x88, 0x54,
0xf1, 0xaf, 0xa5, 0x06, 0x78, 0x80, 0xde, 0xf0,
0x0d, 0xdf]
var r{.noInit}: ECP_ShortW_Jac[Fp[BLS12_381], G1]
sha256.hashToCurve(128, r, aug, msg, dst)
let expected = ECP_ShortW_Jac[Fp[BLS12_381], G1].fromHex(
x = "0x48f2bbee30aa236feaa7fb924d8a3de3090ff160f9972a8afda302bd248248527dcc59ce195cd5f5a1488417cfc64cc",
y = "0xe91b0a3cdea4981741791c8e9b4287d2f693c6626d8e4408ecaaa473e6ff2f691f5f23f8b7b46bdf3560e7cca67e5bc"
)
doAssert bool(r == expected)
suite "Hash-to-curve anti-regression":
test "BLS12-381 G1 - Fuzzing Failure 2":
testH2C_guidovranken_fuzz_failure_2()