Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion constantine.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 13 additions & 14 deletions constantine/hash_to_curve/hash_to_curve.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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".}

Expand Down
55 changes: 27 additions & 28 deletions constantine/math/elliptic/ec_shortweierstrass_jacobian.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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₁²
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -430,14 +429,25 @@ 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
var U2 {.noInit.}, S2 {.noInit.}: F
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₁²
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
12 changes: 6 additions & 6 deletions constantine/math/extension_fields/towers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand Down
35 changes: 34 additions & 1 deletion tests/t_hash_to_curve_random.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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()