mirror of
https://github.com/status-im/constantine.git
synced 2025-02-23 09:28:07 +00:00
Initial support for Elliptic Curve (#24)
* Elliptic curve and Twisted curve templates - initial commit * Support EC Add on G2 (Sextic Twisted curve for BN and BLS12 families) * Refactor the config parser to prepare for elliptic coefficient support * Add elliptic curve parameter for BN254 (Snarks), BLS12-381 and Zexe curve BLS12-377 * Add accessors to curve parameters * Allow computing the right-hand-side of of Weierstrass equation "y² = x³ + a x + b" * Randomized test infrastructure for elliptic curves * Start a testing suite on ellptic curve addition (failing) * detail projective addition * Fix EC addition test (forgot initializing Z=1 and that there ar emultiple infinity points) * Test with random Z coordinate + add elliptic curve test to test suite * fix reference to the (deactivated) addchain inversion for BN curves [skip ci] * .nims file leftover [skip ci]
This commit is contained in:
parent
1889fc4eeb
commit
2f839cb1bf
@ -54,7 +54,10 @@ task test, "Run all tests":
|
||||
test "", "tests/test_fp6.nim"
|
||||
test "", "tests/test_fp12.nim"
|
||||
|
||||
if sizeof(int) == 8: # 32-bit tests
|
||||
# Elliptic curve arithmetic
|
||||
test "", "tests/test_ec_weierstrass_projective_g1.nim"
|
||||
|
||||
if sizeof(int) == 8: # 32-bit tests on 64-bit arch
|
||||
# Primitives
|
||||
test "-d:Constantine32", "tests/test_primitives.nim"
|
||||
|
||||
@ -79,6 +82,9 @@ task test, "Run all tests":
|
||||
test "-d:Constantine32", "tests/test_fp6.nim"
|
||||
test "-d:Constantine32", "tests/test_fp12.nim"
|
||||
|
||||
# Elliptic curve arithmetic
|
||||
test "-d:Constantine32", "tests/test_ec_weierstrass_projective_g1.nim"
|
||||
|
||||
task test_no_gmp, "Run tests that don't require GMP":
|
||||
# -d:testingCurves is configured in a *.nim.cfg for convenience
|
||||
|
||||
@ -102,6 +108,9 @@ task test_no_gmp, "Run tests that don't require GMP":
|
||||
test "", "tests/test_fp6.nim"
|
||||
test "", "tests/test_fp12.nim"
|
||||
|
||||
# Elliptic curve arithmetic
|
||||
test "", "tests/test_ec_weierstrass_projective_g1.nim"
|
||||
|
||||
if sizeof(int) == 8: # 32-bit tests
|
||||
# Primitives
|
||||
test "-d:Constantine32", "tests/test_primitives.nim"
|
||||
@ -123,6 +132,9 @@ task test_no_gmp, "Run tests that don't require GMP":
|
||||
test "-d:Constantine32", "tests/test_fp6.nim"
|
||||
test "-d:Constantine32", "tests/test_fp12.nim"
|
||||
|
||||
# Elliptic curve arithmetic
|
||||
test "-d:Constantine32", "tests/test_ec_weierstrass_projective_g1.nim"
|
||||
|
||||
proc runBench(benchName: string, compiler = "") =
|
||||
if not dirExists "build":
|
||||
mkDir "build"
|
||||
|
@ -334,3 +334,85 @@ func `*=`*(a: var Fp, b: Fp) =
|
||||
func square*(a: var Fp) =
|
||||
## Squaring modulo p
|
||||
a.mres.montySquare(a.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare())
|
||||
|
||||
func `*=`*(a: var Fp, b: static int) {.inline.} =
|
||||
## Multiplication by a small integer known at compile-time
|
||||
# Implementation:
|
||||
# We don't want to go convert the integer to the Montgomery domain (O(n²))
|
||||
# and then multiply by ``b`` (another O(n²)
|
||||
#
|
||||
# So we hardcode addition chains for small integer
|
||||
#
|
||||
# In terms of cost a doubling/addition is 3 passes over the data:
|
||||
# - addition + check if > prime + conditional substraction
|
||||
# A full multiplication, assuming b is projected to Montgomery domain beforehand is:
|
||||
# - n² passes over the data, each of 5~6 elementary addition/multiplication
|
||||
# - a conditional substraction
|
||||
#
|
||||
const negate = b < 0
|
||||
const b = if negate: -b
|
||||
else: b
|
||||
when negate:
|
||||
a.neg(a)
|
||||
when b == 0:
|
||||
a.setZero()
|
||||
elif b == 1:
|
||||
return
|
||||
elif b == 2:
|
||||
a.double()
|
||||
elif b == 3:
|
||||
let t1 = a
|
||||
a.double()
|
||||
a += t1
|
||||
elif b == 4:
|
||||
a.double()
|
||||
a.double()
|
||||
elif b == 5:
|
||||
let t1 = a
|
||||
a.double()
|
||||
a.double()
|
||||
a += t1
|
||||
elif b == 6:
|
||||
a.double()
|
||||
let t2 = a
|
||||
a.double() # 4
|
||||
a += t2
|
||||
elif b == 7:
|
||||
let t1 = a
|
||||
a.double()
|
||||
let t2 = a
|
||||
a.double() # 4
|
||||
a += t2
|
||||
a += t1
|
||||
elif b == 8:
|
||||
a.double()
|
||||
a.double()
|
||||
a.double()
|
||||
elif b == 9:
|
||||
let t1 = a
|
||||
a.double()
|
||||
a.double()
|
||||
a.double() # 8
|
||||
a += t1
|
||||
elif b == 10:
|
||||
a.double()
|
||||
let t2 = a
|
||||
a.double()
|
||||
a.double() # 8
|
||||
a += t2
|
||||
elif b == 11:
|
||||
let t1 = a
|
||||
a.double()
|
||||
let t2 = a
|
||||
a.double()
|
||||
a.double() # 8
|
||||
a += t2
|
||||
a += t1
|
||||
elif b == 12:
|
||||
a.double()
|
||||
a.double() # 4
|
||||
let t4 = a
|
||||
a.double() # 8
|
||||
a += t4
|
||||
else:
|
||||
{.error: "Multiplication by this small int not implemented".}
|
||||
|
@ -127,7 +127,7 @@ func invmod_addchain_bn[C](r: var Fp[C], a: Fp[C]) =
|
||||
## Requires a `bn` curve with a positive parameter `u`
|
||||
# TODO: debug for input "0x0d2007d8aaface1b8501bfbe792974166e8f9ad6106e5b563604f0aea9ab06f6"
|
||||
# see test suite
|
||||
static: doAssert C.canUseFast_BN_Inversion()
|
||||
static: doAssert C.canUse_BN_AddchainInversion()
|
||||
|
||||
var v0 {.noInit.}, v1 {.noInit.}: Fp[C]
|
||||
|
||||
|
@ -10,167 +10,14 @@ import
|
||||
# Standard library
|
||||
macros,
|
||||
# Internal
|
||||
./curves_parser, ./common,
|
||||
../arithmetic/[precomputed, bigints]
|
||||
./curves_declaration, ./curves_derived, ./curves_parser,
|
||||
../arithmetic/bigints
|
||||
|
||||
export CurveFamily, Curve, SexticTwist
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Configuration of finite fields
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
# Curves & their corresponding finite fields are preconfigured in this file
|
||||
|
||||
# Note, in the past the convention was to name a curve by its conjectured security level.
|
||||
# as this might change with advances in research, the new convention is
|
||||
# to name curves according to the length of the prime bit length.
|
||||
# i.e. the BN254 was previously named BN128.
|
||||
|
||||
# Curves security level were significantly impacted by
|
||||
# advances in the Tower Number Field Sieve.
|
||||
# in particular BN254 curve security dropped
|
||||
# from estimated 128-bit to estimated 100-bit
|
||||
# Barbulescu, R. and S. Duquesne, "Updating Key Size Estimations for Pairings",
|
||||
# Journal of Cryptology, DOI 10.1007/s00145-018-9280-5, January 2018.
|
||||
|
||||
# Generates public:
|
||||
# - type Curve* = enum
|
||||
# - proc Mod*(curve: static Curve): auto
|
||||
# which returns the field modulus of the curve
|
||||
# - proc Family*(curve: static Curve): CurveFamily
|
||||
# which returns the curve family
|
||||
# - proc get_BN_param_u_BE*(curve: static Curve): array[N, byte]
|
||||
# which returns the "u" parameter of a BN curve
|
||||
# as a big-endian canonical integer representation
|
||||
# if it's a BN curve and u is positive
|
||||
# - proc get_BN_param_6u_minus1_BE*(curve: static Curve): array[N, byte]
|
||||
# which returns the "6u-1" parameter of a BN curve
|
||||
# as a big-endian canonical integer representation
|
||||
# if it's a BN curve and u is positive.
|
||||
# This is used for optimized field inversion for BN curves
|
||||
|
||||
type
|
||||
CurveFamily* = enum
|
||||
NoFamily
|
||||
BarretoNaehrig # BN curve
|
||||
|
||||
declareCurves:
|
||||
# -----------------------------------------------------------------------------
|
||||
# Curves added when passed "-d:testingCurves"
|
||||
curve Fake101:
|
||||
testingCurve: true
|
||||
bitsize: 7
|
||||
modulus: "0x65" # 101 in hex
|
||||
curve Fake103: # 103 ≡ 3 (mod 4)
|
||||
testingCurve: true
|
||||
bitsize: 7
|
||||
modulus: "0x67" # 103 in hex
|
||||
curve Fake10007: # 10007 ≡ 3 (mod 4)
|
||||
testingCurve: true
|
||||
bitsize: 14
|
||||
modulus: "0x2717" # 10007 in hex
|
||||
curve Fake65519: # 65519 ≡ 3 (mod 4)
|
||||
testingCurve: true
|
||||
bitsize: 16
|
||||
modulus: "0xFFEF" # 65519 in hex
|
||||
curve Mersenne61:
|
||||
testingCurve: true
|
||||
bitsize: 61
|
||||
modulus: "0x1fffffffffffffff" # 2^61 - 1
|
||||
curve Mersenne127:
|
||||
testingCurve: true
|
||||
bitsize: 127
|
||||
modulus: "0x7fffffffffffffffffffffffffffffff" # 2^127 - 1
|
||||
# -----------------------------------------------------------------------------
|
||||
curve P224: # NIST P-224
|
||||
bitsize: 224
|
||||
modulus: "0xffffffff_ffffffff_ffffffff_ffffffff_00000000_00000000_00000001"
|
||||
curve BN254_Nogami: # Integer Variable χ–Based Ate Pairing, 2008, Nogami et al
|
||||
bitsize: 254
|
||||
modulus: "0x2523648240000001ba344d80000000086121000000000013a700000000000013"
|
||||
family: BarretoNaehrig
|
||||
# Equation: Y^2 = X^3 + 2
|
||||
# u: -(2^62 + 2^55 + 1)
|
||||
curve BN254_Snarks: # Zero-Knowledge proofs curve (SNARKS, STARKS, Ethereum)
|
||||
bitsize: 254
|
||||
modulus: "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"
|
||||
family: BarretoNaehrig
|
||||
bn_u_bitwidth: 63
|
||||
bn_u: "0x44E992B44A6909F1"
|
||||
# Equation: Y^2 = X^3 + 3
|
||||
# u: 4965661367192848881
|
||||
curve Curve25519: # Bernstein curve
|
||||
bitsize: 255
|
||||
modulus: "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"
|
||||
curve P256: # secp256r1 / NIST P-256
|
||||
bitsize: 256
|
||||
modulus: "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
|
||||
curve Secp256k1: # Bitcoin curve
|
||||
bitsize: 256
|
||||
modulus: "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F"
|
||||
curve BLS12_377:
|
||||
# Zexe curve
|
||||
# (p41) https://eprint.iacr.org/2018/962.pdf
|
||||
# https://github.com/ethereum/EIPs/blob/41dea9615/EIPS/eip-2539.md
|
||||
bitsize: 377
|
||||
modulus: "0x01ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000001"
|
||||
# u: 3 * 2^46 * (7 * 13 * 499) + 1
|
||||
# u: 0x8508c00000000001
|
||||
curve BLS12_381:
|
||||
bitsize: 381
|
||||
modulus: "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
|
||||
# Equation: y^2 = x^3 + 4
|
||||
# u: -(2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16)
|
||||
curve BN446:
|
||||
bitsize: 446
|
||||
modulus: "0x2400000000000000002400000002d00000000d800000021c0000001800000000870000000b0400000057c00000015c000000132000000067"
|
||||
family: BarretoNaehrig
|
||||
# u = 2^110 + 2^36 + 1
|
||||
curve FKM12_447: # Fotiadis-Konstantinou-Martindale
|
||||
bitsize: 447
|
||||
modulus: "0x4ce300001338c00001c08180000f20cfffffe5a8bffffd08a000000f228000007e8ffffffaddfffffffdc00000009efffffffca000000007"
|
||||
# TNFS Resistant Families of Pairing-Friendly Elliptic Curves
|
||||
# Georgios Fotiadis and Elisavet Konstantinou, 2018
|
||||
# https://eprint.iacr.org/2018/1017
|
||||
#
|
||||
# Family 17 choice b of
|
||||
# Optimal TNFS-secure pairings on elliptic curves with composite embedding degree
|
||||
# Georgios Fotiadis and Chloe Martindale, 2019
|
||||
# https://eprint.iacr.org/2019/555
|
||||
#
|
||||
# A short-list of pairing-friendly curves resistant toSpecial TNFS at the 128-bit security level
|
||||
# Aurore Guillevic
|
||||
# https://hal.inria.fr/hal-02396352v2/document
|
||||
#
|
||||
# p(x) = 1728x^6 + 2160x^5 + 1548x^4 + 756x^3 + 240x^2 + 54x + 7
|
||||
# t(x) = −6x² + 1, r(x) = 36x^4 + 36x^3 + 18x^2 + 6x + 1.
|
||||
# Choice (b):u=−2^72 − 2^71 − 2^36
|
||||
#
|
||||
# Note the paper mentions 446-bit but it's 447
|
||||
curve BLS12_461:
|
||||
# Updating Key Size Estimations for Pairings
|
||||
# Barbulescu, R. and S. Duquesne, 2018
|
||||
# https://hal.archives-ouvertes.fr/hal-01534101/file/main.pdf
|
||||
bitsize: 461
|
||||
modulus: "0x15555545554d5a555a55d69414935fbd6f1e32d8bacca47b14848b42a8dffa5c1cc00f26aa91557f00400020000555554aaaaaac0000aaaaaaab"
|
||||
# u = −2^77 + 2^50 + 2^33
|
||||
# p = (u - 1)^2 (u^4 - u^2 + 1)/3 + u
|
||||
curve BN462:
|
||||
# Pairing-Friendly Curves
|
||||
# IETF Draft
|
||||
# https://tools.ietf.org/id/draft-irtf-cfrg-pairing-friendly-curves-02.html
|
||||
|
||||
# Updating Key Size Estimations for Pairings
|
||||
# Barbulescu, R. and S. Duquesne, 2018
|
||||
# https://hal.archives-ouvertes.fr/hal-01534101/file/main.pdf
|
||||
bitsize: 462
|
||||
modulus: "0x240480360120023ffffffffff6ff0cf6b7d9bfca0000000000d812908f41c8020ffffffffff6ff66fc6ff687f640000000002401b00840138013"
|
||||
family: BarretoNaehrig
|
||||
# u = 2^114 + 2^101 - 2^14 - 1
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Curve characteristics
|
||||
# Field properties
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
@ -182,143 +29,68 @@ macro Mod*(C: static Curve): untyped =
|
||||
|
||||
func getCurveBitSize*(C: static Curve): static int =
|
||||
## Returns the number of bits taken by the curve modulus
|
||||
result = static(CurveBitSize[C])
|
||||
result = static(CurveBitWidth[C])
|
||||
|
||||
template matchingBigInt*(C: static Curve): untyped =
|
||||
BigInt[CurveBitSize[C]]
|
||||
BigInt[CurveBitWidth[C]]
|
||||
|
||||
func family*(C: static Curve): CurveFamily =
|
||||
result = static(CurveFamilies[C])
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Autogeneration of precomputed constants in ROM
|
||||
# Curve properties
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
macro genConstants(): untyped =
|
||||
## Store
|
||||
## - the Montgomery magic constant "R^2 mod N" in ROM
|
||||
## For each curve under the private symbol "MyCurve_R2modP"
|
||||
## - the Montgomery magic constant -1/P mod 2^WordBitSize
|
||||
## For each curve under the private symbol "MyCurve_NegInvModWord
|
||||
## - ...
|
||||
result = newStmtList()
|
||||
macro getEquationForm*(C: static Curve): untyped =
|
||||
## Returns the equation form
|
||||
result = bindSym($C & "_equation_form")
|
||||
|
||||
template used(name: string): NimNode =
|
||||
nnkPragmaExpr.newTree(
|
||||
ident(name),
|
||||
nnkPragma.newTree(ident"used")
|
||||
)
|
||||
macro getCoefA*(C: static Curve): untyped =
|
||||
## Returns the A coefficient of the curve
|
||||
## The return type is polymorphic, it can be an int
|
||||
## or a bigInt depending on the curve
|
||||
result = bindSym($C & "_coef_A")
|
||||
|
||||
for curve in Curve.low .. Curve.high:
|
||||
# const MyCurve_CanUseNoCarryMontyMul = useNoCarryMontyMul(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used($curve & "_CanUseNoCarryMontyMul"), newCall(
|
||||
bindSym"useNoCarryMontyMul",
|
||||
bindSym($curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
macro getCoefB*(C: static Curve): untyped =
|
||||
## Returns the B coefficient of the curve
|
||||
## The return type is polymorphic, it can be an int
|
||||
## or a bigInt depending on the curve
|
||||
result = bindSym($C & "_coef_B")
|
||||
|
||||
# const MyCurve_CanUseNoCarryMontySquare = useNoCarryMontySquare(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used($curve & "_CanUseNoCarryMontySquare"), newCall(
|
||||
bindSym"useNoCarryMontySquare",
|
||||
bindSym($curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
macro get_QNR_Fp*(C: static Curve): untyped =
|
||||
## Returns the tower extension quadratic non-residue in 𝔽p
|
||||
## i.e. a number that is not a square in 𝔽p
|
||||
result = bindSym($C & "_nonresidue_quad_fp")
|
||||
|
||||
# const MyCurve_R2modP = r2mod(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used($curve & "_R2modP"), newCall(
|
||||
bindSym"r2mod",
|
||||
bindSym($curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
macro get_CNR_Fp2*(C: static Curve): untyped =
|
||||
## Returns the tower extension cubic non-residue 𝔽p²
|
||||
## i.e. a number that is not a cube in 𝔽p²
|
||||
##
|
||||
## The return value is a tuple (a, b)
|
||||
## that corresponds to the number a + b𝑗
|
||||
## with 𝑗 choosen for 𝑗² - QNR_Fp == 0
|
||||
## i.e. if -1 is chosen as a quadratic non-residue 𝑗 = √-1
|
||||
## if -2 is chosen as a quadratic non-residue 𝑗 = √-2
|
||||
result = bindSym($C & "_nonresidue_cube_fp2")
|
||||
|
||||
# const MyCurve_NegInvModWord = negInvModWord(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used($curve & "_NegInvModWord"), newCall(
|
||||
bindSym"negInvModWord",
|
||||
bindSym($curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_montyOne = montyOne(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used($curve & "_MontyOne"), newCall(
|
||||
bindSym"montyOne",
|
||||
bindSym($curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_MontyPrimeMinus1 = montyPrimeMinus1(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used($curve & "_MontyPrimeMinus1"), newCall(
|
||||
bindSym"montyPrimeMinus1",
|
||||
bindSym($curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_InvModExponent = primeMinus2_BE(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used($curve & "_InvModExponent"), newCall(
|
||||
bindSym"primeMinus2_BE",
|
||||
bindSym($curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_PrimePlus1div2 = primePlus1div2(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used($curve & "_PrimePlus1div2"), newCall(
|
||||
bindSym"primePlus1div2",
|
||||
bindSym($curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_PrimeMinus1div2_BE = primeMinus1div2_BE(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used($curve & "_PrimeMinus1div2_BE"), newCall(
|
||||
bindSym"primeMinus1div2_BE",
|
||||
bindSym($curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_PrimeMinus3div4_BE = primeMinus3div4_BE(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used($curve & "_PrimeMinus3div4_BE"), newCall(
|
||||
bindSym"primeMinus3div4_BE",
|
||||
bindSym($curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_PrimePlus1div4_BE = primePlus1div4_BE(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used($curve & "_PrimePlus1div4_BE"), newCall(
|
||||
bindSym"primePlus1div4_BE",
|
||||
bindSym($curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
macro getSexticTwist*(C: static Curve): untyped =
|
||||
result = bindSym($C & "_sexticTwist")
|
||||
|
||||
if CurveFamilies[curve] == BarretoNaehrig:
|
||||
# when declared(MyCurve_BN_param_u):
|
||||
# const MyCurve_BN_u_BE = toCanonicalIntRepr(MyCurve_BN_param_u)
|
||||
# const MyCurve_BN_6u_minus_1_BE = bn_6u_minus_1_BE(MyCurve_BN_param_u)
|
||||
var bnStmts = newStmtList()
|
||||
bnStmts.add newConstStmt(
|
||||
used($curve & "_BN_u_BE"), newCall(
|
||||
bindSym"toCanonicalIntRepr",
|
||||
ident($curve & "_BN_param_u")
|
||||
)
|
||||
)
|
||||
bnStmts.add newConstStmt(
|
||||
used($curve & "_BN_6u_minus_1_BE"), newCall(
|
||||
bindSym"bn_6u_minus_1_BE",
|
||||
ident($curve & "_BN_param_u")
|
||||
)
|
||||
)
|
||||
macro get_SNR_Fp2*(C: static Curve): untyped =
|
||||
## Returns the sextic non-residue in 𝔽p²
|
||||
## choosen to build the twisted curve E'(𝔽p²)
|
||||
## i.e. a number µ so that x⁶ - µ is irreducible
|
||||
result = bindSym($C & "_sexticNonResidue_fp2")
|
||||
|
||||
result.add nnkWhenStmt.newTree(
|
||||
nnkElifBranch.newTree(
|
||||
newCall(ident"declared", ident($curve & "_BN_param_u")),
|
||||
bnStmts
|
||||
)
|
||||
)
|
||||
# ############################################################
|
||||
#
|
||||
# Access precomputed derived constants in ROM
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
genConstants()
|
||||
genDerivedConstants()
|
||||
|
||||
macro canUseNoCarryMontyMul*(C: static Curve): untyped =
|
||||
## Returns true if the Modulus is compatible with a fast
|
||||
@ -335,7 +107,7 @@ macro getR2modP*(C: static Curve): untyped =
|
||||
result = bindSym($C & "_R2modP")
|
||||
|
||||
macro getNegInvModWord*(C: static Curve): untyped =
|
||||
## Get the Montgomery "-1/P[0] mod 2^WordBitSize" constant associated to a curve field modulus
|
||||
## Get the Montgomery "-1/P[0] mod 2^Wordbitwidth" constant associated to a curve field modulus
|
||||
result = bindSym($C & "_NegInvModWord")
|
||||
|
||||
macro getMontyOne*(C: static Curve): untyped =
|
||||
@ -369,11 +141,11 @@ macro getPrimePlus1div4_BE*(C: static Curve): untyped =
|
||||
|
||||
# Family specific
|
||||
# -------------------------------------------------------
|
||||
macro canUseFast_BN_Inversion*(C: static Curve): untyped =
|
||||
macro canUse_BN_AddchainInversion*(C: static Curve): untyped =
|
||||
## A BN curve can use the fast BN inversion if the parameter "u" is positive
|
||||
if CurveFamilies[C] != BarretoNaehrig:
|
||||
return newLit false
|
||||
return bindSym($C & "_BN_can_use_fast_inversion")
|
||||
return bindSym($C & "_BN_can_use_addchain_inversion")
|
||||
|
||||
macro getBN_param_u_BE*(C: static Curve): untyped =
|
||||
## Get the ``u`` parameter of a BN curve in canonical big-endian representation
|
||||
|
201
constantine/config/curves_declaration.nim
Normal file
201
constantine/config/curves_declaration.nim
Normal file
@ -0,0 +1,201 @@
|
||||
# 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
|
||||
# Internal
|
||||
./curves_parser
|
||||
|
||||
export CurveFamily
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Configuration of finite fields
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
# Curves & their corresponding finite fields are preconfigured in this file
|
||||
|
||||
# Note, in the past the convention was to name a curve by its conjectured security level.
|
||||
# as this might change with advances in research, the new convention is
|
||||
# to name curves according to the length of the prime bit length.
|
||||
# i.e. the BN254 was previously named BN128.
|
||||
|
||||
# Curves security level were significantly impacted by
|
||||
# advances in the Tower Number Field Sieve.
|
||||
# in particular BN254 curve security dropped
|
||||
# from estimated 128-bit to estimated 100-bit
|
||||
# Barbulescu, R. and S. Duquesne, "Updating Key Size Estimations for Pairings",
|
||||
# Journal of Cryptology, DOI 10.1007/s00145-018-9280-5, January 2018.
|
||||
|
||||
# Generates public:
|
||||
# - type Curve* = enum
|
||||
# - proc Mod*(curve: static Curve): auto
|
||||
# which returns the field modulus of the curve
|
||||
# - proc Family*(curve: static Curve): CurveFamily
|
||||
# which returns the curve family
|
||||
# - proc get_BN_param_u_BE*(curve: static Curve): array[N, byte]
|
||||
# which returns the "u" parameter of a BN curve
|
||||
# as a big-endian canonical integer representation
|
||||
# if it's a BN curve and u is positive
|
||||
# - proc get_BN_param_6u_minus1_BE*(curve: static Curve): array[N, byte]
|
||||
# which returns the "6u-1" parameter of a BN curve
|
||||
# as a big-endian canonical integer representation
|
||||
# if it's a BN curve and u is positive.
|
||||
# This is used for optimized field inversion for BN curves
|
||||
|
||||
declareCurves:
|
||||
# -----------------------------------------------------------------------------
|
||||
# Curves added when passed "-d:testingCurves"
|
||||
curve Fake101:
|
||||
testingCurve: true
|
||||
bitwidth: 7
|
||||
modulus: "0x65" # 101 in hex
|
||||
curve Fake103: # 103 ≡ 3 (mod 4)
|
||||
testingCurve: true
|
||||
bitwidth: 7
|
||||
modulus: "0x67" # 103 in hex
|
||||
curve Fake10007: # 10007 ≡ 3 (mod 4)
|
||||
testingCurve: true
|
||||
bitwidth: 14
|
||||
modulus: "0x2717" # 10007 in hex
|
||||
curve Fake65519: # 65519 ≡ 3 (mod 4)
|
||||
testingCurve: true
|
||||
bitwidth: 16
|
||||
modulus: "0xFFEF" # 65519 in hex
|
||||
curve Mersenne61:
|
||||
testingCurve: true
|
||||
bitwidth: 61
|
||||
modulus: "0x1fffffffffffffff" # 2^61 - 1
|
||||
curve Mersenne127:
|
||||
testingCurve: true
|
||||
bitwidth: 127
|
||||
modulus: "0x7fffffffffffffffffffffffffffffff" # 2^127 - 1
|
||||
# -----------------------------------------------------------------------------
|
||||
curve P224: # NIST P-224
|
||||
bitwidth: 224
|
||||
modulus: "0xffffffff_ffffffff_ffffffff_ffffffff_00000000_00000000_00000001"
|
||||
curve BN254_Nogami: # Integer Variable χ–Based Ate Pairing, 2008, Nogami et al
|
||||
bitwidth: 254
|
||||
modulus: "0x2523648240000001ba344d80000000086121000000000013a700000000000013"
|
||||
family: BarretoNaehrig
|
||||
# Equation: Y^2 = X^3 + 2
|
||||
# u: -(2^62 + 2^55 + 1)
|
||||
curve BN254_Snarks: # Zero-Knowledge proofs curve (SNARKS, STARKS, Ethereum)
|
||||
bitwidth: 254
|
||||
modulus: "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"
|
||||
family: BarretoNaehrig
|
||||
bn_u_bitwidth: 63
|
||||
bn_u: "0x44E992B44A6909F1" # u: 4965661367192848881
|
||||
|
||||
# G1 Equation: Y^2 = X^3 + 3
|
||||
# G2 Equation: Y^2 = X^3 + 3/(9+𝑖)
|
||||
eq_form: ShortWeierstrass
|
||||
coef_a: 0
|
||||
coef_b: 3
|
||||
nonresidue_quad_fp: -1 # -1 is not a square in 𝔽p
|
||||
nonresidue_cube_fp2: (9, 1) # 9+𝑖 9+𝑖 is not a cube in 𝔽p²
|
||||
|
||||
sexticTwist: D_Twist
|
||||
sexticNonResidue_fp2: (9, 1) # 9+𝑖
|
||||
|
||||
curve Curve25519: # Bernstein curve
|
||||
bitwidth: 255
|
||||
modulus: "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"
|
||||
curve P256: # secp256r1 / NIST P-256
|
||||
bitwidth: 256
|
||||
modulus: "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
|
||||
curve Secp256k1: # Bitcoin curve
|
||||
bitwidth: 256
|
||||
modulus: "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F"
|
||||
curve BLS12_377:
|
||||
# Zexe curve
|
||||
# (p41) https://eprint.iacr.org/2018/962.pdf
|
||||
# https://github.com/ethereum/EIPs/blob/41dea9615/EIPS/eip-2539.md
|
||||
bitwidth: 377
|
||||
modulus: "0x01ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000001"
|
||||
family: BarretoLynnScott
|
||||
# u: 3 * 2^46 * (7 * 13 * 499) + 1
|
||||
# u: 0x8508c00000000001
|
||||
|
||||
# G1 Equation: y² = x³ + 1
|
||||
# G2 Equation: y² = x³ + 1/ with 𝑗 = √-5
|
||||
eq_form: ShortWeierstrass
|
||||
coef_a: 0
|
||||
coef_b: 1
|
||||
nonresidue_quad_fp: -5 # -5 is not a square in 𝔽p
|
||||
nonresidue_cube_fp2: (0, 1) # √-5 √-5 is not a cube in 𝔽p²
|
||||
|
||||
sexticTwist: D_Twist
|
||||
sexticNonResidue_fp2: (0, 1) # √-5
|
||||
|
||||
curve BLS12_381:
|
||||
bitwidth: 381
|
||||
modulus: "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
|
||||
family: BarretoLynnScott
|
||||
# u: -(2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16)
|
||||
|
||||
# G1 Equation: y² = x³ + 4
|
||||
# G2 Equation: y² = x³ + 4 (1+i)
|
||||
eq_form: ShortWeierstrass
|
||||
coef_a: 0
|
||||
coef_b: 4
|
||||
nonresidue_quad_fp: -1 # -1 is not a square in 𝔽p
|
||||
nonresidue_cube_fp2: (1, 1) # 1+𝑖 1+𝑖 is not a cube in 𝔽p²
|
||||
|
||||
sexticTwist: M_Twist
|
||||
sexticNonResidue_fp2: (1, 1) # 1+𝑖
|
||||
|
||||
curve BN446:
|
||||
bitwidth: 446
|
||||
modulus: "0x2400000000000000002400000002d00000000d800000021c0000001800000000870000000b0400000057c00000015c000000132000000067"
|
||||
family: BarretoNaehrig
|
||||
# u = 2^110 + 2^36 + 1
|
||||
curve FKM12_447: # Fotiadis-Konstantinou-Martindale
|
||||
bitwidth: 447
|
||||
modulus: "0x4ce300001338c00001c08180000f20cfffffe5a8bffffd08a000000f228000007e8ffffffaddfffffffdc00000009efffffffca000000007"
|
||||
# TNFS Resistant Families of Pairing-Friendly Elliptic Curves
|
||||
# Georgios Fotiadis and Elisavet Konstantinou, 2018
|
||||
# https://eprint.iacr.org/2018/1017
|
||||
#
|
||||
# Family 17 choice b of
|
||||
# Optimal TNFS-secure pairings on elliptic curves with composite embedding degree
|
||||
# Georgios Fotiadis and Chloe Martindale, 2019
|
||||
# https://eprint.iacr.org/2019/555
|
||||
#
|
||||
# A short-list of pairing-friendly curves resistant toSpecial TNFS at the 128-bit security level
|
||||
# Aurore Guillevic
|
||||
# https://hal.inria.fr/hal-02396352v2/document
|
||||
#
|
||||
# p(x) = 1728x^6 + 2160x^5 + 1548x^4 + 756x^3 + 240x^2 + 54x + 7
|
||||
# t(x) = −6x² + 1, r(x) = 36x^4 + 36x^3 + 18x^2 + 6x + 1.
|
||||
# Choice (b):u=−2^72 − 2^71 − 2^36
|
||||
#
|
||||
# Note the paper mentions 446-bit but it's 447
|
||||
curve BLS12_461:
|
||||
# Updating Key Size Estimations for Pairings
|
||||
# Barbulescu, R. and S. Duquesne, 2018
|
||||
# https://hal.archives-ouvertes.fr/hal-01534101/file/main.pdf
|
||||
bitwidth: 461
|
||||
modulus: "0x15555545554d5a555a55d69414935fbd6f1e32d8bacca47b14848b42a8dffa5c1cc00f26aa91557f00400020000555554aaaaaac0000aaaaaaab"
|
||||
# u = −2^77 + 2^50 + 2^33
|
||||
# p = (u - 1)^2 (u^4 - u^2 + 1)/3 + u
|
||||
|
||||
# Note there is another BLS12-461 proposed here:
|
||||
# https://tools.ietf.org/id/draft-yonezawa-pairing-friendly-curves-00.html#rfc.section.4.2
|
||||
curve BN462:
|
||||
# Pairing-Friendly Curves
|
||||
# IETF Draft
|
||||
# https://tools.ietf.org/id/draft-irtf-cfrg-pairing-friendly-curves-02.html
|
||||
|
||||
# Updating Key Size Estimations for Pairings
|
||||
# Barbulescu, R. and S. Duquesne, 2018
|
||||
# https://hal.archives-ouvertes.fr/hal-01534101/file/main.pdf
|
||||
bitwidth: 462
|
||||
modulus: "0x240480360120023ffffffffff6ff0cf6b7d9bfca0000000000d812908f41c8020ffffffffff6ff66fc6ff687f640000000002401b00840138013"
|
||||
family: BarretoNaehrig
|
||||
# u = 2^114 + 2^101 - 2^14 - 1
|
150
constantine/config/curves_derived.nim
Normal file
150
constantine/config/curves_derived.nim
Normal file
@ -0,0 +1,150 @@
|
||||
# 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
|
||||
macros,
|
||||
# Internal
|
||||
../arithmetic/precomputed,
|
||||
./curves_declaration
|
||||
|
||||
{.experimental: "dynamicBindSym".}
|
||||
|
||||
macro genDerivedConstants*(): untyped =
|
||||
## Generate constants derived from the main constants
|
||||
##
|
||||
## For example
|
||||
## - the Montgomery magic constant "R^2 mod N" in ROM
|
||||
## For each curve under the private symbol "MyCurve_R2modP"
|
||||
## - the Montgomery magic constant -1/P mod 2^Wordbitwidth
|
||||
## For each curve under the private symbol "MyCurve_NegInvModWord
|
||||
## - ...
|
||||
|
||||
# Now typedesc are NimNode and there is no way to translate
|
||||
# NimNode -> typedesc easily so we can't
|
||||
# "for curve in low(Curve) .. high(Curve):"
|
||||
# As an ugly workaround, we count
|
||||
# The item at position 0 is a pragma
|
||||
let curveList = Curve.getType[1].getType
|
||||
|
||||
result = newStmtList()
|
||||
|
||||
template used(name: string): NimNode =
|
||||
nnkPragmaExpr.newTree(
|
||||
ident(name),
|
||||
nnkPragma.newTree(ident"used")
|
||||
)
|
||||
|
||||
for curveSym in low(Curve) .. high(Curve):
|
||||
let curve = $curveSym
|
||||
|
||||
# const MyCurve_CanUseNoCarryMontyMul = useNoCarryMontyMul(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_CanUseNoCarryMontyMul"), newCall(
|
||||
bindSym"useNoCarryMontyMul",
|
||||
bindSym(curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
|
||||
# const MyCurve_CanUseNoCarryMontySquare = useNoCarryMontySquare(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_CanUseNoCarryMontySquare"), newCall(
|
||||
bindSym"useNoCarryMontySquare",
|
||||
bindSym(curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
|
||||
# const MyCurve_R2modP = r2mod(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_R2modP"), newCall(
|
||||
bindSym"r2mod",
|
||||
bindSym(curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
|
||||
# const MyCurve_NegInvModWord = negInvModWord(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_NegInvModWord"), newCall(
|
||||
bindSym"negInvModWord",
|
||||
bindSym(curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_montyOne = montyOne(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_MontyOne"), newCall(
|
||||
bindSym"montyOne",
|
||||
bindSym(curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_MontyPrimeMinus1 = montyPrimeMinus1(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_MontyPrimeMinus1"), newCall(
|
||||
bindSym"montyPrimeMinus1",
|
||||
bindSym(curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_InvModExponent = primeMinus2_BE(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_InvModExponent"), newCall(
|
||||
bindSym"primeMinus2_BE",
|
||||
bindSym(curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_PrimePlus1div2 = primePlus1div2(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_PrimePlus1div2"), newCall(
|
||||
bindSym"primePlus1div2",
|
||||
bindSym(curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_PrimeMinus1div2_BE = primeMinus1div2_BE(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_PrimeMinus1div2_BE"), newCall(
|
||||
bindSym"primeMinus1div2_BE",
|
||||
bindSym(curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_PrimeMinus3div4_BE = primeMinus3div4_BE(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_PrimeMinus3div4_BE"), newCall(
|
||||
bindSym"primeMinus3div4_BE",
|
||||
bindSym(curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
# const MyCurve_PrimePlus1div4_BE = primePlus1div4_BE(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_PrimePlus1div4_BE"), newCall(
|
||||
bindSym"primePlus1div4_BE",
|
||||
bindSym(curve & "_Modulus")
|
||||
)
|
||||
)
|
||||
|
||||
if CurveFamilies[curveSym] == BarretoNaehrig:
|
||||
# when declared(MyCurve_BN_param_u):
|
||||
# const MyCurve_BN_u_BE = toCanonicalIntRepr(MyCurve_BN_param_u)
|
||||
# const MyCurve_BN_6u_minus_1_BE = bn_6u_minus_1_BE(MyCurve_BN_param_u)
|
||||
var bnStmts = newStmtList()
|
||||
bnStmts.add newConstStmt(
|
||||
used(curve & "_BN_u_BE"), newCall(
|
||||
bindSym"toCanonicalIntRepr",
|
||||
ident(curve & "_BN_param_u")
|
||||
)
|
||||
)
|
||||
bnStmts.add newConstStmt(
|
||||
used(curve & "_BN_6u_minus_1_BE"), newCall(
|
||||
bindSym"bn_6u_minus_1_BE",
|
||||
ident(curve & "_BN_param_u")
|
||||
)
|
||||
)
|
||||
|
||||
result.add nnkWhenStmt.newTree(
|
||||
nnkElifBranch.newTree(
|
||||
newCall(ident"declared", ident(curve & "_BN_param_u")),
|
||||
bnStmts
|
||||
)
|
||||
)
|
@ -8,31 +8,114 @@
|
||||
|
||||
import
|
||||
# Standard library
|
||||
macros,
|
||||
std/[macros, strutils],
|
||||
# Internal
|
||||
../io/io_bigints, ../arithmetic/bigints
|
||||
../io/io_bigints, ../arithmetic/[bigints, precomputed]
|
||||
|
||||
# Macro to parse declarative curves configuration.
|
||||
# Parsing is done in 2 steps:
|
||||
# 1. All declared parameters are collected in a {.compileTime.} seq[CurveParams]
|
||||
# 2. Those parameters are assigned to a constant
|
||||
# If needed a macro is defined to retrieve those in a generic way.
|
||||
#
|
||||
# Using a const indirection rather than directly accessing the {.compileTime.} object ensures 2 things:
|
||||
# - properly cross the compile-time -> runtime boundary
|
||||
# - avoid inlining large const arrays at the call site
|
||||
# for example when using the `r2modP` constant in multiple overloads in the same module
|
||||
# TODO: check that those constants use extern const to avoid duplication across modules
|
||||
|
||||
macro declareCurves*(curves: untyped): untyped =
|
||||
## Parse curve configuration and generates
|
||||
##
|
||||
## type Curve = enum
|
||||
## BN254
|
||||
## ...
|
||||
##
|
||||
## const CurveBitSize* = array[
|
||||
## BN254: 254,
|
||||
## ...
|
||||
## ]
|
||||
##
|
||||
## TODO: Ensure that the modulus is not inlined at runtime
|
||||
## to avoid codesize explosion.
|
||||
## const BN254_Modulus = fromHex(BigInt[254], "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47")
|
||||
##
|
||||
## func fieldModulus*(curve: static Curve): auto =
|
||||
## when curve == BN254_Modulus: BN254_Modulus
|
||||
## ...
|
||||
type
|
||||
CurveFamily* = enum
|
||||
NoFamily
|
||||
BarretoNaehrig # BN curve
|
||||
BarretoLynnScott # BLS curve
|
||||
|
||||
CurveCoefKind* = enum
|
||||
## Small coefficients fit in an int64
|
||||
## Large ones require a bigint
|
||||
## Note that some seemingly large coefficients might be small
|
||||
## when represented as a negative modular integer
|
||||
##
|
||||
## NoCoef is used when the curve is not defined (i.e. we are only interest in field arithmetic)
|
||||
## use `Small` to set a coef to ``0``
|
||||
NoCoef
|
||||
Small
|
||||
Large
|
||||
|
||||
CurveCoef* = object
|
||||
case kind: CurveCoefKind
|
||||
of NoCoef: discard
|
||||
of Small: coef: int
|
||||
of Large: coefHex: string
|
||||
|
||||
CurveEquationForm* = enum
|
||||
ShortWeierstrass
|
||||
|
||||
SexticTwist* = enum
|
||||
## The sextic twist type of the current elliptic curve
|
||||
##
|
||||
## Assuming a standard curve `E` over the prime field `𝔽p`
|
||||
## denoted `E(𝔽p)` in Short Weierstrass form
|
||||
## y² = x³ + Ax + B
|
||||
##
|
||||
## If E(𝔽p^k), the elliptic curve defined over the extension field
|
||||
## of degree k, the embedding degree, admits an isomorphism
|
||||
## to a curve E'(Fp^(k/d)), we call E' a twisted curve.
|
||||
##
|
||||
## For pairing they have the following equation
|
||||
## y² = x³ + Ax/µ² + B/µ³ for a D-Twist (Divisor)
|
||||
## or
|
||||
## y² = x³ + µ²Ax + µ³B for a M-Twist (Multiplicand)
|
||||
## with the polynomial x^k - µ being irreducible.
|
||||
##
|
||||
## i.e. if d == 2, E' is a quadratic twist and µ is a quadratic non-residue
|
||||
## if d == 4, E' is a quartic twist
|
||||
## if d == 6, E' is a sextic twist
|
||||
##
|
||||
## References:
|
||||
## - Efficient Pairings on Twisted Elliptic Curve
|
||||
## Yasuyuki Nogami, Masataka Akane, Yumi Sakemi and Yoshitaka Morikawa, 2010
|
||||
## https://www.researchgate.net/publication/221908359_Efficient_Pairings_on_Twisted_Elliptic_Curve
|
||||
##
|
||||
## - A note on twists for pairing friendly curves\
|
||||
## Michael Scott, 2009\
|
||||
## http://indigo.ie/~mscott/twists.pdf
|
||||
NotTwisted
|
||||
D_Twist
|
||||
M_Twist
|
||||
|
||||
CurveParams = object
|
||||
## All the curve parameters that may be defined
|
||||
# Note: we don't use case object here, the transition is annoying
|
||||
# and would force use to scan all "kind" field (eq_form, family, ...)
|
||||
# before instantiating the object.
|
||||
name: NimNode
|
||||
|
||||
# Field parameters
|
||||
bitWidth: NimNode # nnkIntLit
|
||||
modulus: NimNode # nnkStrLit (hex)
|
||||
|
||||
# Towering
|
||||
nonresidue_quad_fp: NimNode # nnkIntLit
|
||||
nonresidue_cube_fp2: NimNode # nnkPar(nnkIntLit, nnkIntLit)
|
||||
|
||||
# Curve parameters
|
||||
eq_form: CurveEquationForm
|
||||
coef_A: CurveCoef
|
||||
coef_B: CurveCoef
|
||||
|
||||
sexticTwist: SexticTwist
|
||||
sexticNonResidue_fp2: NimNode # nnkPar(nnkIntLit, nnkIntLit)
|
||||
|
||||
family: CurveFamily
|
||||
# BN family
|
||||
# ------------------------
|
||||
bn_u_bitwidth: NimNode # nnkIntLit
|
||||
bn_u: NimNode # nnkStrLit (hex)
|
||||
|
||||
var curvesDefinitions {.compileTime.}: seq[CurveParams]
|
||||
|
||||
proc parseCurveDecls(defs: var seq[CurveParams], curves: NimNode) =
|
||||
## Parse the curve declarations and store them in the curve definitions
|
||||
curves.expectKind(nnkStmtList)
|
||||
|
||||
# curve BN254:
|
||||
@ -55,26 +138,19 @@ macro declareCurves*(curves: untyped): untyped =
|
||||
# StmtList
|
||||
# StrLit "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"
|
||||
|
||||
var Curves: seq[NimNode]
|
||||
var MapCurveBitWidth = nnkBracket.newTree()
|
||||
var MapCurveFamily = nnkBracket.newTree()
|
||||
var curveModStmts = newStmtList()
|
||||
var curveExtraStmts = newStmtList()
|
||||
|
||||
for curveDesc in curves:
|
||||
# Checks
|
||||
# -----------------------------------------------
|
||||
curveDesc.expectKind(nnkCommand)
|
||||
doAssert curveDesc[0].eqIdent"curve"
|
||||
curveDesc[1].expectKind(nnkIdent) # Curve name
|
||||
curveDesc[2].expectKind(nnkStmtList)
|
||||
curveDesc[2][0].expectKind(nnkCall)
|
||||
curveDesc[2][1].expectKind(nnkCall)
|
||||
|
||||
# Mandatory fields
|
||||
# -----------------------------------------------
|
||||
let curve = curveDesc[1]
|
||||
let curveParams = curveDesc[2]
|
||||
curve.expectKind(nnkIdent) # Curve name
|
||||
curveParams.expectKind(nnkStmtList) # Curve parameters
|
||||
|
||||
# Skip test curves if not testing
|
||||
# -----------------------------------------------
|
||||
|
||||
var offset = 0
|
||||
var testCurve = false
|
||||
@ -82,86 +158,158 @@ macro declareCurves*(curves: untyped): untyped =
|
||||
offset = 1
|
||||
testCurve = curveParams[0][1].boolVal
|
||||
|
||||
let sizeSection = curveParams[offset]
|
||||
doAssert sizeSection[0].eqIdent"bitsize"
|
||||
sizeSection[1].expectKind(nnkStmtList)
|
||||
let bitSize = sizeSection[1][0]
|
||||
if testCurve and defined(testingCurves):
|
||||
continue
|
||||
|
||||
let modSection = curveParams[offset+1]
|
||||
doAssert modSection[0].eqIdent"modulus"
|
||||
modSection[1].expectKind(nnkStmtList)
|
||||
let modulus = modSection[1][0]
|
||||
|
||||
# Construct the constants
|
||||
# Parameters
|
||||
# -----------------------------------------------
|
||||
if not testCurve or defined(testingCurves):
|
||||
Curves.add curve
|
||||
# "BN254: 254" for array construction
|
||||
MapCurveBitWidth.add nnkExprColonExpr.newTree(
|
||||
curve, bitSize
|
||||
)
|
||||
var params = CurveParams(name: curve)
|
||||
for i in offset ..< curveParams.len:
|
||||
let sectionId = curveParams[i][0]
|
||||
curveParams[i][1].expectKind(nnkStmtList)
|
||||
let sectionVal = curveParams[i][1][0]
|
||||
|
||||
# const BN254_Snarks_Modulus = fromHex(BigInt[254], "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47")
|
||||
let modulusID = ident($curve & "_Modulus")
|
||||
curveModStmts.add newConstStmt(
|
||||
modulusID,
|
||||
newCall(
|
||||
bindSym"fromHex",
|
||||
nnkBracketExpr.newTree(bindSym"BigInt", bitSize),
|
||||
modulus
|
||||
)
|
||||
)
|
||||
|
||||
# Family specific
|
||||
# -----------------------------------------------
|
||||
if offset + 2 < curveParams.len:
|
||||
let familySection = curveParams[offset+2]
|
||||
doAssert familySection[0].eqIdent"family"
|
||||
familySection[1].expectKind(nnkStmtList)
|
||||
let family = familySection[1][0]
|
||||
|
||||
MapCurveFamily.add nnkExprColonExpr.newTree(
|
||||
curve, family
|
||||
)
|
||||
|
||||
# BN curves
|
||||
# -----------------------------------------------
|
||||
if family.eqIdent"BarretoNaehrig":
|
||||
if offset + 5 == curveParams.len:
|
||||
if curveParams[offset+3][0].eqIdent"bn_u_bitwidth" and
|
||||
curveParams[offset+4][0].eqIdent"bn_u":
|
||||
|
||||
let bn_u_bitwidth = curveParams[offset+3][1][0]
|
||||
let bn_u = curveParams[offset+4][1][0]
|
||||
|
||||
# const BN254_Snarks_BN_can_use_fast_inversion = ...
|
||||
curveExtraStmts.add newConstStmt(
|
||||
ident($curve & "_BN_can_use_fast_inversion"),
|
||||
if ($bn_u)[0] == '-': newLit false # negative ``u`` can use the specialized fast inversion
|
||||
else: newLit true
|
||||
)
|
||||
|
||||
# const BN254_Snarks_BN_param_u = fromHex(BigInt[63], "0x44E992B44A6909F1")
|
||||
curveExtraStmts.add newConstStmt(
|
||||
ident($curve & "_BN_param_u"),
|
||||
newCall(
|
||||
bindSym"fromHex",
|
||||
nnkBracketExpr.newTree(bindSym"BigInt", bn_u_bitwidth),
|
||||
bn_u
|
||||
)
|
||||
)
|
||||
if sectionId.eqIdent"bitwidth":
|
||||
params.bitWidth = sectionVal
|
||||
elif sectionId.eqident"modulus":
|
||||
params.modulus = sectionVal
|
||||
elif sectionId.eqIdent"family":
|
||||
params.family = parseEnum[CurveFamily]($sectionVal)
|
||||
elif sectionId.eqIdent"bn_u_bitwidth":
|
||||
params.bn_u_bitwidth = sectionVal
|
||||
elif sectionId.eqIdent"bn_u":
|
||||
params.bn_u = sectionVal
|
||||
elif sectionId.eqIdent"eq_form":
|
||||
params.eq_form = parseEnum[CurveEquationForm]($sectionVal)
|
||||
elif sectionId.eqIdent"coef_a":
|
||||
if sectionVal.kind == nnkIntLit:
|
||||
params.coef_A = CurveCoef(kind: Small, coef: sectionVal.intVal.int)
|
||||
else:
|
||||
# const BN254_Snarks_BN_can_use_fast_inversion = ...
|
||||
curveExtraStmts.add newConstStmt(
|
||||
ident($curve & "_BN_can_use_fast_inversion"),
|
||||
newLit false
|
||||
)
|
||||
params.coef_A = CurveCoef(kind: Large, coefHex: sectionVal.strVal)
|
||||
elif sectionId.eqIdent"coef_b":
|
||||
if sectionVal.kind == nnkIntLit:
|
||||
params.coef_B = CurveCoef(kind: Small, coef: sectionVal.intVal.int)
|
||||
else:
|
||||
params.coef_B = CurveCoef(kind: Large, coefHex: sectionVal.strVal)
|
||||
elif sectionId.eqIdent"nonresidue_quad_fp":
|
||||
params.nonresidue_quad_fp = sectionVal
|
||||
elif sectionId.eqIdent"nonresidue_cube_fp2":
|
||||
params.nonresidue_cube_fp2 = sectionVal
|
||||
elif sectionId.eqIdent"sexticTwist":
|
||||
params.sexticTwist = parseEnum[SexticTwist]($sectionVal)
|
||||
elif sectionId.eqIdent"sexticNonResidue_fp2":
|
||||
params.sexticNonResidue_fp2 = sectionVal
|
||||
else:
|
||||
error "Invalid section: \n", curveParams[i].toStrLit()
|
||||
|
||||
else:
|
||||
MapCurveFamily.add nnkExprColonExpr.newTree(
|
||||
curve, ident"NoFamily"
|
||||
defs.add params
|
||||
|
||||
proc exported(id: string): NimNode =
|
||||
nnkPostfix.newTree(
|
||||
ident"*",
|
||||
ident(id)
|
||||
)
|
||||
|
||||
template getCoef(c: CurveCoef, width: NimNode): untyped {.dirty.}=
|
||||
case c.kind
|
||||
of NoCoef:
|
||||
error "Unreachable"
|
||||
nnkDiscardStmt.newTree(newLit "Dummy")
|
||||
of Small:
|
||||
newLit c.coef
|
||||
of Large:
|
||||
newCall(
|
||||
bindSym"fromHex",
|
||||
nnkBracketExpr.newTree(bindSym"BigInt", width),
|
||||
newLit c.coefHex
|
||||
)
|
||||
|
||||
proc genMainConstants(defs: var seq[CurveParams]): NimNode =
|
||||
## Generate curves and fields main constants
|
||||
|
||||
var Curves: seq[NimNode]
|
||||
var MapCurveBitWidth = nnkBracket.newTree()
|
||||
var MapCurveFamily = nnkBracket.newTree()
|
||||
var curveModStmts = newStmtList()
|
||||
var curveEllipticStmts = newStmtList()
|
||||
var curveExtraStmts = newStmtList()
|
||||
|
||||
for curveDef in defs:
|
||||
curveDef.name.expectKind(nnkIdent)
|
||||
curveDef.bitWidth.expectKind(nnkIntLit)
|
||||
curveDef.modulus.expectKind(nnkStrLit)
|
||||
|
||||
let curve = curveDef.name
|
||||
let bitWidth = curveDef.bitWidth
|
||||
let modulus = curveDef.modulus
|
||||
let family = curveDef.family
|
||||
|
||||
Curves.add curve
|
||||
# "BN254_Snarks: 254" array construction expression
|
||||
MapCurveBitWidth.add nnkExprColonExpr.newTree(
|
||||
curve, bitWidth
|
||||
)
|
||||
|
||||
# const BN254_Snarks_Modulus = fromHex(BigInt[254], "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47")
|
||||
curveModStmts.add newConstStmt(
|
||||
exported($curve & "_Modulus"),
|
||||
newCall(
|
||||
bindSym"fromHex",
|
||||
nnkBracketExpr.newTree(bindSym"BigInt", bitWidth),
|
||||
modulus
|
||||
)
|
||||
)
|
||||
|
||||
MapCurveFamily.add nnkExprColonExpr.newTree(
|
||||
curve, newLit(family)
|
||||
)
|
||||
# Curve equation
|
||||
# -----------------------------------------------
|
||||
curveEllipticStmts.add newConstStmt(
|
||||
exported($curve & "_equation_form"),
|
||||
newLit curveDef.eq_form
|
||||
)
|
||||
if curveDef.coef_A.kind != NoCoef and curveDef.coef_B.kind != NoCoef:
|
||||
curveEllipticStmts.add newConstStmt(
|
||||
exported($curve & "_coef_A"),
|
||||
curveDef.coef_A.getCoef(bitWidth)
|
||||
)
|
||||
curveEllipticStmts.add newConstStmt(
|
||||
exported($curve & "_coef_B"),
|
||||
curveDef.coef_B.getCoef(bitWidth)
|
||||
)
|
||||
curveEllipticStmts.add newConstStmt(
|
||||
exported($curve & "_nonresidue_quad_fp"),
|
||||
curveDef.nonresidue_quad_fp
|
||||
)
|
||||
curveEllipticStmts.add newConstStmt(
|
||||
exported($curve & "_nonresidue_cube_fp2"),
|
||||
curveDef.nonresidue_cube_fp2
|
||||
)
|
||||
curveEllipticStmts.add newConstStmt(
|
||||
exported($curve & "_sexticTwist"),
|
||||
newLit curveDef.sexticTwist
|
||||
)
|
||||
curveEllipticStmts.add newConstStmt(
|
||||
exported($curve & "_sexticNonResidue_fp2"),
|
||||
curveDef.sexticNonResidue_fp2
|
||||
)
|
||||
|
||||
# BN curves
|
||||
# -----------------------------------------------
|
||||
if family == BarretoNaehrig:
|
||||
if not curveDef.bn_u_bitwidth.isNil and
|
||||
not curveDef.bn_u.isNil and
|
||||
($curveDef.bn_u)[0] != '-': # The parameter must be positive
|
||||
curveExtraStmts.add newConstStmt(
|
||||
exported($curve & "_BN_can_use_addchain_inversion"),
|
||||
newLit true
|
||||
)
|
||||
else:
|
||||
curveExtraStmts.add newConstStmt(
|
||||
exported($curve & "_BN_can_use_addchain_inversion"),
|
||||
newLit false
|
||||
)
|
||||
# end for ---------------------------------------------------
|
||||
|
||||
result = newStmtList()
|
||||
@ -177,14 +325,35 @@ macro declareCurves*(curves: untyped): untyped =
|
||||
|
||||
# const CurveBitSize: array[Curve, int] = ...
|
||||
result.add newConstStmt(
|
||||
ident("CurveBitSize"), MapCurveBitWidth
|
||||
exported("CurveBitWidth"), MapCurveBitWidth
|
||||
)
|
||||
# const CurveFamily: array[Curve, CurveFamily] = ...
|
||||
result.add newConstStmt(
|
||||
ident("CurveFamilies"), MapCurveFamily
|
||||
exported("CurveFamilies"), MapCurveFamily
|
||||
)
|
||||
|
||||
result.add curveModStmts
|
||||
result.add curveEllipticStmts
|
||||
result.add curveExtraStmts
|
||||
|
||||
# echo result.toStrLit()
|
||||
|
||||
macro declareCurves*(curves: untyped): untyped =
|
||||
## Parse curve configuration and generates
|
||||
##
|
||||
## type Curve = enum
|
||||
## BN254
|
||||
## ...
|
||||
##
|
||||
## const CurveBitSize* = array[
|
||||
## BN254: 254,
|
||||
## ...
|
||||
## ]
|
||||
##
|
||||
## TODO: Ensure that
|
||||
## 1. the modulus is not inlined at runtime to avoid codesize explosion.
|
||||
## 2. is not duplicated across compilation modules.
|
||||
|
||||
curves.expectKind(nnkStmtList)
|
||||
curvesDefinitions.parseCurveDecls(curves)
|
||||
result = curvesDefinitions.genMainConstants()
|
||||
|
@ -2,8 +2,137 @@
|
||||
|
||||
This folder will hold the implementation of elliptic curves arithmetic
|
||||
|
||||
## Terminology
|
||||
|
||||
### Coordinates system
|
||||
|
||||
The point P of the curve `y² = x³ + ax + b)` have the following coordinate:
|
||||
|
||||
- `(x, y)` in the affine coordinate system
|
||||
- `(X, Y, Z)` with `X = xZ` and `Y = yZ` in the homogeneous projective coordinate system.
|
||||
The homogeneous projective coordinates will be called projective coordinates from now on.
|
||||
- `(X, Y, Z)` with `X = xZ²` and `Y = yZ³` in the jacobian projective coordinate system.
|
||||
The jacobian projective coordinates will be called jacobian coordinates from now on.
|
||||
|
||||
## Operations on a Twist
|
||||
|
||||
Pairings require operation on a twisted curve. Formulas are available
|
||||
in Costello2009 and Ionica2017 including an overview of which coordinate system (affine, homogeneous projective or jacobian) is the most efficient for the Miller loop.
|
||||
|
||||
In particular for sextic twist (applicable to BN and BLS12 families), the projective coordinates are more efficient while for quadratic and quartic twists, jacobian coordinates ar emore efficient.
|
||||
|
||||
When the addition law requires the `a` or `b` parameter from the curve Scott2009 and Nogami2010 give the parameter relevant to the twisted curve for the M-Twist (multiplication by non-residue) or D-Twist (Division by non-residue) cases.
|
||||
|
||||
## Side-Channel resistance
|
||||
|
||||
### Scalar multiplication
|
||||
|
||||
Scalar multiplication of a point `P` by a scalar `k` and denoted `R = [k]P` (or `R = kP`)
|
||||
is a critical operation to make side-channel resistant.
|
||||
|
||||
Elliptic Curve-based signature scheme indeed rely on the fact that computing the inverse of elliptic scalar multiplication is intractable to produce a public key `[k]P` from
|
||||
the secret (integer) key `k`. The problem is called ECDLP, Elliptic Curve Discrete Logarithm Problem in the litterature.
|
||||
|
||||
Scalar multiplication for elliptic curve presents the same constant-time challenge as square-and-multiply, a naive implementation will leak every bit of the secret key:
|
||||
```
|
||||
N ← P
|
||||
R ← 0
|
||||
for i from 0 to log2(k) do
|
||||
if k.bit(i) == 1 then
|
||||
Q ← point_add(Q, N)
|
||||
N ← point_double(N)
|
||||
return Q
|
||||
```
|
||||
|
||||
### Point Addition and Doubling
|
||||
|
||||
#### Exceptions in elliptic curve group laws.
|
||||
|
||||
For an elliptic curve in short Weierstrass form: `y² = x³ + ax + b)`
|
||||
|
||||
The equation for elliptic curve addition is in affine (x, y) coordinates:
|
||||
|
||||
```
|
||||
P + Q = R
|
||||
(Px, Py) + (Qx, Qy) = (Rx, Ry)
|
||||
|
||||
with
|
||||
Rx = λ² - Px - Qx
|
||||
Ry = λ(Px - Rx) - Py
|
||||
```
|
||||
but in the case of addition
|
||||
```
|
||||
λ = (Qy - Py) / (Px - Qx)
|
||||
```
|
||||
which is undefined for P == Q or P == -Q (as `-(x, y) = (x, -y)`)
|
||||
|
||||
the doubling formula uses the slope of the tangent at the limit
|
||||
|
||||
```
|
||||
λ = (3 Px² + a) / (2 Px)
|
||||
```
|
||||
|
||||
So we have to take into account 2 special-cases.
|
||||
|
||||
Furthermore when using (homogeneous) projective or jacobian coordinates, most formulæ
|
||||
needs to special-case the point at infinity.
|
||||
|
||||
#### Dealing with exceptions
|
||||
|
||||
An addition formula that works for both addition and doubling (adding the same point) is called **unified**.
|
||||
An addition formula that works for all inputs including adding infinity point or the same point is called **complete** or **exception-free**.
|
||||
|
||||
Abarúa2019 highlight several attacks, their defenses, counterattacks and counterdefenses
|
||||
on elliptic curve implementations.
|
||||
|
||||
We use the complete addition law from Renes2015 for projective coordinates, note that the prime order requirement can be relaxed to odd order according to the author.
|
||||
|
||||
We use the complete addition law from Bos2014 for Jacobian coordinates, note that there is a prime order requirement.
|
||||
|
||||
## References
|
||||
|
||||
- Pairing-Friendly Curves\
|
||||
(Draft, expires May 4, 2020)\
|
||||
https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-00#section-2.1
|
||||
|
||||
- Survey for Performance & Security Problems of Passive Side-channel Attacks Countermeasures in ECC\
|
||||
Rodrigo Abarúa, Claudio Valencia, and Julio López, 2019\
|
||||
https://eprint.iacr.org/2019/010
|
||||
|
||||
- Completing the Complete ECC Formulae with Countermeasures
|
||||
Łukasz Chmielewski, Pedro Maat Costa Massolino, Jo Vliegen, Lejla Batina and Nele Mentens, 2017\
|
||||
https://www.mdpi.com/2079-9268/7/1/3/pdf
|
||||
|
||||
- Pairings\
|
||||
Chapter 3 of Guide to Pairing-Based Cryptography\
|
||||
Sorina Ionica, Damien Robert, 2017\
|
||||
https://www.math.u-bordeaux.fr/~damienrobert/csi2018/pairings.pdf
|
||||
|
||||
- Complete addition formulas for prime order elliptic curves\
|
||||
Joost Renes and Craig Costello and Lejla Batina, 2015\
|
||||
https://eprint.iacr.org/2015/1060
|
||||
|
||||
- Selecting Elliptic Curves for Cryptography: An Efficiency and Security Analysis\
|
||||
Joppe W. Bos and Craig Costello and Patrick Longa and Michael Naehrig, 2014\
|
||||
https://eprint.iacr.org/2014/130
|
||||
https://www.imsc.res.in/~ecc14/slides/costello.pdf
|
||||
|
||||
- State-of-the-art of secure ECC implementations:a survey on known side-channel attacks and countermeasures\
|
||||
Junfeng Fan,XuGuo, Elke De Mulder, Patrick Schaumont, Bart Preneel and Ingrid Verbauwhede, 2010
|
||||
https://www.esat.kuleuven.be/cosic/publications/article-1461.pdf
|
||||
|
||||
- Efficient Pairings on Twisted Elliptic Curve\
|
||||
Yasuyuki Nogami, Masataka Akane, Yumi Sakemi and Yoshitaka Morikawa, 2010\
|
||||
https://www.researchgate.net/publication/221908359_Efficient_Pairings_on_Twisted_Elliptic_Curve
|
||||
|
||||
- A note on twists for pairing friendly curves\
|
||||
Michael Scott, 2009\
|
||||
http://indigo.ie/~mscott/twists.pdf
|
||||
|
||||
- Faster Pairing Computations on Curves withHigh-Degree Twists\
|
||||
Craig Costello and Tanja Lange and Michael Naehrig, 2009,
|
||||
https://eprint.iacr.org/2009/615
|
||||
|
||||
- Complete systems of Two Addition Laws for Elliptic Curve\
|
||||
Bosma and Lenstra, 1995
|
||||
http://www.mat.uniroma3.it/users/pappa/CORSI/CR510_13_14/BosmaLenstra.pdf
|
||||
|
53
constantine/elliptic/ec_weierstrass_affine.nim
Normal file
53
constantine/elliptic/ec_weierstrass_affine.nim
Normal file
@ -0,0 +1,53 @@
|
||||
# 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
|
||||
../primitives,
|
||||
../config/[common, curves],
|
||||
../arithmetic,
|
||||
../towers,
|
||||
../io/io_bigints
|
||||
|
||||
func curve_eq_rhs*[F](y2: var F, x: F) =
|
||||
## Compute the curve equation right-hand-side from field element `x`
|
||||
## i.e. `y²` in `y² = x³ + a x + b`
|
||||
## or on sextic twists for pairing curves `y² = x³ + b/µ` or `y² = x³ + µ b`
|
||||
## with µ the chosen sextic non-residue
|
||||
|
||||
var t{.noInit.}: F
|
||||
t.square(x)
|
||||
t *= x
|
||||
|
||||
# No need to precompute `b` in 𝔽p or 𝔽p² or `b/µ` `µ b`
|
||||
# This procedure is not use in perf critcal situation like signing/verification
|
||||
# but for testing to quickly create points on a curve.
|
||||
y2 = F.fromBig F.C.matchingBigInt().fromUint F.C.getCoefB()
|
||||
when F is Fp2:
|
||||
when F.C.getSexticTwist() == D_Twist:
|
||||
y2 /= F.C.get_SNR_Fp2()
|
||||
elif F.C.getSexticTwist() == M_Twist:
|
||||
y2 *= F.C.get_SNR_Fp2()
|
||||
else:
|
||||
{.error: "Only twisted curves are supported on extension field 𝔽p²".}
|
||||
|
||||
y2 += t
|
||||
|
||||
when F.C.getCoefA() != 0:
|
||||
t = x
|
||||
t *= F.C.getCoefA()
|
||||
y2 += t
|
||||
|
||||
func isOnCurve*[F](x, y: F): CTBool[Word] =
|
||||
## Returns true if the (x, y) coordinates
|
||||
## represents a point of the elliptic curve
|
||||
|
||||
var y2, rhs {.noInit.}: F
|
||||
y2.square(y)
|
||||
rhs.curve_eq_rhs(x)
|
||||
|
||||
return y2 == rhs
|
193
constantine/elliptic/ec_weierstrass_projective.nim
Normal file
193
constantine/elliptic/ec_weierstrass_projective.nim
Normal file
@ -0,0 +1,193 @@
|
||||
# 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
|
||||
../primitives,
|
||||
../config/[common, curves],
|
||||
../arithmetic,
|
||||
../towers,
|
||||
./ec_weierstrass_affine
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Elliptic Curve in Weierstrass form
|
||||
# with Projective Coordinates
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
type ECP_SWei_Proj*[F] = object
|
||||
## Elliptic curve point for a curve in Short Weierstrass form
|
||||
## y² = x³ + a x + b
|
||||
##
|
||||
## over a field F
|
||||
##
|
||||
## in projective coordinates (X, Y, Z)
|
||||
## corresponding to (x, y) with X = xZ and Y = yZ
|
||||
##
|
||||
## Note that projective coordinates are not unique
|
||||
x, y, z: F
|
||||
|
||||
func `==`*[F](P, Q: ECP_SWei_Proj[F]): CTBool[Word] =
|
||||
## Constant-time equality check
|
||||
# Reminder: the representation is not unique
|
||||
|
||||
var a{.noInit.}, b{.noInit.}: F
|
||||
|
||||
a.prod(P.x, Q.z)
|
||||
b.prod(Q.x, P.z)
|
||||
result = a == b
|
||||
|
||||
a.prod(P.y, Q.z)
|
||||
b.prod(Q.y, P.z)
|
||||
result = result and a == b
|
||||
|
||||
func isInf*(P: ECP_SWei_Proj): CTBool[Word] =
|
||||
## Returns true if P is an infinity point
|
||||
## and false otherwise
|
||||
##
|
||||
## Note: the projective coordinates equation is
|
||||
## Y²Z = X³ + aXZ² + bZ³
|
||||
## A "zero" point is any point with coordinates X and Z = 0
|
||||
## Y can be anything
|
||||
result = P.x.isZero() and P.z.isZero()
|
||||
|
||||
func setInf*(P: var ECP_SWei_Proj) =
|
||||
## Set ``P`` to infinity
|
||||
P.x.setZero()
|
||||
P.y.setOne()
|
||||
P.z.setZero()
|
||||
|
||||
func trySetFromCoordsXandZ*[F](P: var ECP_SWei_Proj[F], x, z: F): CTBool[Word] =
|
||||
## Try to create a point the elliptic curve
|
||||
## Y²Z = X³ + aXZ² + bZ³ (projective coordinates)
|
||||
## y² = x³ + a x + b (affine coordinate)
|
||||
## return true and update `P` if `x` leads to a valid point
|
||||
## return false otherwise, in that case `P` is undefined.
|
||||
##
|
||||
## Note: Dedicated robust procedures for hashing-to-curve
|
||||
## will be provided, this is intended for testing purposes.
|
||||
P.y.curve_eq_rhs(x)
|
||||
# TODO: supports non p ≡ 3 (mod 4) modulus like BLS12-377
|
||||
result = sqrt_if_square_p3mod4(P.y)
|
||||
|
||||
P.x.prod(x, z)
|
||||
P.y *= z
|
||||
P.z = z
|
||||
|
||||
func trySetFromCoordX*[F](P: var ECP_SWei_Proj[F], x: F): CTBool[Word] =
|
||||
## Try to create a point the elliptic curve
|
||||
## y² = x³ + a x + b (affine coordinate)
|
||||
##
|
||||
## The `Z` coordinates is set to 1
|
||||
##
|
||||
## return true and update `P` if `x` leads to a valid point
|
||||
## return false otherwise, in that case `P` is undefined.
|
||||
##
|
||||
## Note: Dedicated robust procedures for hashing-to-curve
|
||||
## will be provided, this is intended for testing purposes.
|
||||
P.y.curve_eq_rhs(x)
|
||||
# TODO: supports non p ≡ 3 (mod 4) modulus like BLS12-377
|
||||
result = sqrt_if_square_p3mod4(P.y)
|
||||
P.x = x
|
||||
P.z.setOne()
|
||||
|
||||
func neg*(P: var ECP_SWei_Proj) =
|
||||
## Negate ``P``
|
||||
P.y.neg(P.y)
|
||||
|
||||
func sum*[F](
|
||||
r: var ECP_SWei_Proj[F],
|
||||
P, Q: ECP_SWei_Proj[F]
|
||||
) =
|
||||
## Elliptic curve point addition for Short Weierstrass curves in projective coordinate
|
||||
## Short Weierstrass curves have the following equation in projective coordinates
|
||||
## Y²Z = X³ + aXZ² + bZ³
|
||||
## from the affine equation
|
||||
## y² = x³ + a x + b
|
||||
##
|
||||
## ``r`` is initialized/overwritten with the sum
|
||||
##
|
||||
## Implementation is constant-time, in particular it will not expose
|
||||
## that P == Q or P == -Q or P or Q are the infinity points
|
||||
## to simple side-channel attacks (SCA)
|
||||
## This is done by using a "complete" or "exception-free" addition law.
|
||||
##
|
||||
## This requires the order of the curve to be odd
|
||||
#
|
||||
# Implementation:
|
||||
# Algorithms 1 (generic case), 4 (a == -3), 7 (a == 0) of
|
||||
# Complete addition formulas for prime order elliptic curves\
|
||||
# Joost Renes and Craig Costello and Lejla Batina, 2015\
|
||||
# https://eprint.iacr.org/2015/1060
|
||||
#
|
||||
# with the indices 1 corresponding to ``P``, 2 to ``Q`` and 3 to the result ``r``
|
||||
#
|
||||
# X3 = (X1 Y2 + X2 Y1)(Y1 Y2 - a(X1 Z2 + X2 Z1) - 3b Z1 Z2)
|
||||
# - (Y1 Z2 + Y2 Z1)(a X1 X2 + 3b(X1 Z2 + X2 Z1) - a² Z1 Z2)
|
||||
# Y3 = (3 X1 X2 + a Z1 Z2)(a X1 X2 + 3b (X1 Z2 + X2 Z1) - a² Z1 Z2)
|
||||
# + (Y1 Y2 + a (X1 Z2 + X2 Z1) + 3b Z1 Z2)(Y1 Y2 - a(X1 Z2 + X2 Z1) - 3b Z1 Z2)
|
||||
# Z3 = (Y1 Z2 + Y2 Z1)(Y1 Y2 + a(X1 Z2 + X2 Z1) + 3b Z1 Z2) + (X1 Y2 + X2 Y1)(3 X1 X2 + a Z1 Z2)
|
||||
|
||||
# TODO: static doAssert odd order
|
||||
var t0 {.noInit.}, t1 {.noInit.}, t2 {.noInit.}, t3 {.noInit.}, t4 {.noInit.}: F
|
||||
const b3 = 3 * F.C.getCoefB()
|
||||
|
||||
when F.C.getCoefA() == 0:
|
||||
# Algorithm 7 for curves: y² = x³ + b
|
||||
# 12M + 2 mul(3b) + 19A
|
||||
# X3 = (X1 Y2 + X2 Y1)(Y1 Y2 − 3b Z1 Z2)
|
||||
# − 3b(Y1 Z2 + Y2 Z1)(X1 Z2 + X2 Z1)
|
||||
# Y3 = (Y1 Y2 + 3b Z1 Z2)(Y1 Y2 − 3b Z1 Z2)
|
||||
# + 9b X1 X2 (X1 Z2 + X2 Z1)
|
||||
# Z3= (Y1 Z2 + Y2 Z1)(Y1 Y2 + 3b Z1 Z2) + 3 X1 X2 (X1 Y2 + X2 Y1)
|
||||
t0.prod(P.x, Q.x) # 1. t0 <- X1 X2
|
||||
t1.prod(P.y, Q.y) # 2. t1 <- Y1 Y2
|
||||
t2.prod(P.z, Q.z) # 3. t2 <- Z1 Z2
|
||||
t3.sum(P.x, P.y) # 4. t3 <- X1 + Y1
|
||||
t4.sum(Q.x, Q.y) # 5. t4 <- X2 + Y2
|
||||
t3 *= t4 # 6. t3 <- t3 * t4
|
||||
t4.sum(t0, t1) # 7. t4 <- t0 + t1
|
||||
t3 -= t4 # 8. t3 <- t3 - t4 t3 = (X1 + Y1)(X2 + Y2) - (X1 X2 + Y1 Y2) = X1.Y2 + X2.Y1
|
||||
when F is Fp2 and F.C.getSexticTwist() == D_Twist:
|
||||
t3 *= F.sexticNonResidue()
|
||||
t4.sum(P.y, P.z) # 9. t4 <- Y1 + Z1
|
||||
r.x.sum(Q.y, Q.z) # 10. X3 <- Y2 + Z2
|
||||
t4 *= r.x # 11. t4 <- t4 X3
|
||||
r.x.sum(t1, t2) # 12. X3 <- t1 + t2 X3 = Y1 Y2 + Z1 Z2
|
||||
t4 -= r.x # 13. t4 <- t4 - X3 t4 = (Y1 + Z1)(Y2 + Z2) - (Y1 Y2 + Z1 Z2) = Y1 Z2 + Y2 Z1
|
||||
when F is Fp2 and F.C.getSexticTwist() == D_Twist:
|
||||
t4 *= F.sexticNonResidue()
|
||||
r.x.sum(P.x, P.z) # 14. X3 <- X1 + Z1
|
||||
r.y.sum(Q.x, Q.z) # 15. Y3 <- X2 + Z2
|
||||
r.x *= r.y # 16. X3 <- X3 Y3 X3 = (X1 Z1)(X2 Z2)
|
||||
r.y.sum(t0, t2) # 17. Y3 <- t0 + t2 Y3 = X1 X2 + Z1 Z2
|
||||
r.y.diff(r.x, r.y) # 18. Y3 <- X3 - Y3 Y3 = (X1 + Z1)(X2 + Z2) - (X1 X2 + Z1 Z2) = X1 Z2 + X2 Z1
|
||||
when F is Fp2 and F.C.getSexticTwist() == D_Twist:
|
||||
t0 *= F.sexticNonResidue()
|
||||
t1 *= F.sexticNonResidue()
|
||||
r.x.double(t0) # 19. X3 <- t0 + t0 X3 = 2 X1 X2
|
||||
t0 += r.x # 20. t0 <- X3 + t0 t0 = 3 X1 X2
|
||||
t2 *= b3 # 21. t2 <- b3 t2 t2 = 3b Z1 Z2
|
||||
when F is Fp2 and F.C.getSexticTwist() == M_Twist:
|
||||
t2 *= F.sexticNonResidue()
|
||||
r.z.sum(t1, t2) # 22. Z3 <- t1 + t2 Z3 = Y1 Y2 + 3b Z1 Z2
|
||||
t1 -= t2 # 23. t1 <- t1 - t2 t1 = Y1 Y2 - 3b Z1 Z2
|
||||
r.y *= b3 # 24. Y3 <- b3 Y3 Y3 = 3b(X1 Z2 + X2 Z1)
|
||||
when F is Fp2 and F.C.getSexticTwist() == M_Twist:
|
||||
r.y *= F.sexticNonResidue()
|
||||
r.x.prod(t4, r.y) # 25. X3 <- t4 Y3 X3 = 3b(Y1 Z2 + Y2 Z1)(X1 Z2 + X2 Z1)
|
||||
t2.prod(t3, t1) # 26. t2 <- t3 t1 t2 = (X1 Y2 + X2 Y1) (Y1 Y2 - 3b Z1 Z2)
|
||||
r.x.diff(t2, r.x) # 27. X3 <- t2 - X3 X3 = (X1 Y2 + X2 Y1) (Y1 Y2 - 3b Z1 Z2) - 3b(Y1 Z2 + Y2 Z1)(X1 Z2 + X2 Z1)
|
||||
r.y *= t0 # 28. Y3 <- Y3 t0 Y3 = 9b X1 X2 (X1 Z2 + X2 Z1)
|
||||
t1 *= r.z # 29. t1 <- t1 Z3 t1 = (Y1 Y2 - 3b Z1 Z2)(Y1 Y2 + 3b Z1 Z2)
|
||||
r.y += t1 # 30. Y3 <- t1 + Y3 Y3 = (Y1 Y2 + 3b Z1 Z2)(Y1 Y2 - 3b Z1 Z2) + 9b X1 X2 (X1 Z2 + X2 Z1)
|
||||
t0 *= t3 # 31. t0 <- t0 t3 t0 = 3 X1 X2 (X1.Y2 + X2.Y1)
|
||||
r.z *= t4 # 32. Z3 <- Z3 t4 Z3 = (Y1 Y2 + 3b Z1 Z2)(Y1 Z2 + Y2 Z1)
|
||||
r.z += t0 # 33. Z3 <- Z3 + t0 Z3 = (Y1 Z2 + Y2 Z1)(Y1 Y2 + 3b Z1 Z2) + 3 X1 X2 (X1.Y2 + X2.Y1)
|
||||
else:
|
||||
{.error: "Not implemented.".}
|
21
constantine/towers.nim
Normal file
21
constantine/towers.nim
Normal file
@ -0,0 +1,21 @@
|
||||
# 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
|
||||
tower_field_extensions/[
|
||||
abelian_groups,
|
||||
fp2_complex,
|
||||
fp6_1_plus_i,
|
||||
fp12_quad_fp6
|
||||
]
|
||||
|
||||
export
|
||||
abelian_groups,
|
||||
fp2_complex,
|
||||
fp6_1_plus_i,
|
||||
fp12_quad_fp6
|
@ -8,7 +8,8 @@
|
||||
|
||||
import
|
||||
../constantine/arithmetic/bigints,
|
||||
../constantine/config/[common, curves]
|
||||
../constantine/config/[common, curves],
|
||||
../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective]
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
@ -75,11 +76,8 @@ func next(rng: var RngState): uint64 =
|
||||
|
||||
rng.s[7] = rotl(rng.s[7], 21);
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Create a random BigInt or Field element
|
||||
#
|
||||
# ############################################################
|
||||
# BigInts and Fields
|
||||
# ------------------------------------------------------------
|
||||
|
||||
func random[T](rng: var RngState, a: var T, C: static Curve) {.noInit.}=
|
||||
## Recursively initialize a BigInt or Field element
|
||||
@ -97,6 +95,44 @@ func random[T](rng: var RngState, a: var T, C: static Curve) {.noInit.}=
|
||||
for field in fields(a):
|
||||
rng.random(field, C)
|
||||
|
||||
# Elliptic curves
|
||||
# ------------------------------------------------------------
|
||||
|
||||
func random[F](rng: var RngState, a: var ECP_SWei_Proj[F]) =
|
||||
## Initialize a random curve point with Z coordinate == 1
|
||||
|
||||
var fieldElem {.noInit.}: F
|
||||
var success = CtFalse
|
||||
|
||||
while not bool(success):
|
||||
# Euler's criterion: there are (p-1)/2 squares in a field with modulus `p`
|
||||
# so we have a probability of ~0.5 to get a good point
|
||||
rng.random(fieldElem, F.C)
|
||||
success = trySetFromCoordX(a, fieldElem)
|
||||
|
||||
func random_with_randZ[F](rng: var RngState, a: var ECP_SWei_Proj[F]) =
|
||||
## Initialize a random curve point with Z coordinate being random
|
||||
|
||||
var Z{.noInit.}: F
|
||||
rng.random(Z, F.C) # If Z is zero, X will be zero and that will be an infinity point
|
||||
|
||||
var fieldElem {.noInit.}: F
|
||||
var success = CtFalse
|
||||
|
||||
while not bool(success):
|
||||
rng.random(fieldElem, F.C)
|
||||
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
||||
|
||||
# Generic over any supported type
|
||||
# ------------------------------------------------------------
|
||||
|
||||
func random*(rng: var RngState, T: typedesc): T =
|
||||
## Create a random Field or Extension Field Element
|
||||
rng.random(result, T.C)
|
||||
## Create a random Field or Extension Field or Curve Element
|
||||
when T is ECP_SWei_Proj:
|
||||
rng.random(result)
|
||||
else:
|
||||
rng.random(result, T.C)
|
||||
|
||||
func random_with_randZ*(rng: var RngState, T: typedesc[ECP_SWei_Proj]): T =
|
||||
## Create a random curve element with a random Z coordinate
|
||||
rng.random_with_randZ(result)
|
||||
|
142
tests/test_ec_weierstrass_projective_g1.nim
Normal file
142
tests/test_ec_weierstrass_projective_g1.nim
Normal file
@ -0,0 +1,142 @@
|
||||
# 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
|
||||
unittest, times, random,
|
||||
# Internals
|
||||
../constantine/config/[common, curves],
|
||||
../constantine/arithmetic,
|
||||
../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective],
|
||||
# Test utilities
|
||||
../helpers/prng
|
||||
|
||||
const Iters = 128
|
||||
|
||||
var rng: RngState
|
||||
let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32
|
||||
rng.seed(seed)
|
||||
echo "test_ec_weierstrass_projective_g1 xoshiro512** seed: ", seed
|
||||
|
||||
# Import: wrap in elliptic curve tests in small procedures
|
||||
# otherwise they will become globals,
|
||||
# and will create binary size issues.
|
||||
# Also due to Nim stack scanning,
|
||||
# having too many elements on the stack (a couple kB)
|
||||
# will significantly slow down testing (100x is possible)
|
||||
|
||||
suite "Elliptic curve in Short Weierstrass form y² = x³ + a x + b with projective coordinates (X, Y, Z): Y²Z = X³ + aXZ² + bZ³ i.e. X = xZ, Y = yZ":
|
||||
test "The infinity point is the neutral element w.r.t. to EC addition":
|
||||
proc test(F: typedesc, randZ: static bool) =
|
||||
var inf {.noInit.}: ECP_SWei_Proj[F]
|
||||
inf.setInf()
|
||||
check: bool inf.isInf()
|
||||
|
||||
for _ in 0 ..< Iters:
|
||||
var r{.noInit.}: ECP_SWei_Proj[F]
|
||||
when randZ:
|
||||
let P = rng.random_with_randZ(ECP_SWei_Proj[F])
|
||||
else:
|
||||
let P = rng.random(ECP_SWei_Proj[F])
|
||||
|
||||
r.sum(P, inf)
|
||||
check: bool(r == P)
|
||||
|
||||
r.sum(inf, P)
|
||||
check: bool(r == P)
|
||||
|
||||
test(Fp[BLS12_381], randZ = false)
|
||||
test(Fp[BLS12_381], randZ = true)
|
||||
|
||||
test "Adding opposites gives an infinity point":
|
||||
proc test(F: typedesc, randZ: static bool) =
|
||||
for _ in 0 ..< Iters:
|
||||
var r{.noInit.}: ECP_SWei_Proj[F]
|
||||
when randZ:
|
||||
let P = rng.random_with_randZ(ECP_SWei_Proj[F])
|
||||
else:
|
||||
let P = rng.random(ECP_SWei_Proj[F])
|
||||
var Q = P
|
||||
Q.neg()
|
||||
|
||||
r.sum(P, Q)
|
||||
check: bool r.isInf()
|
||||
|
||||
r.sum(Q, P)
|
||||
check: bool r.isInf()
|
||||
|
||||
test(Fp[BLS12_381], randZ = false)
|
||||
test(Fp[BLS12_381], randZ = true)
|
||||
|
||||
test "EC add is commutative":
|
||||
proc test(F: typedesc, randZ: static bool) =
|
||||
for _ in 0 ..< Iters:
|
||||
var r0{.noInit.}, r1{.noInit.}: ECP_SWei_Proj[F]
|
||||
when randZ:
|
||||
let P = rng.random_with_randZ(ECP_SWei_Proj[F])
|
||||
let Q = rng.random_with_randZ(ECP_SWei_Proj[F])
|
||||
else:
|
||||
let P = rng.random(ECP_SWei_Proj[F])
|
||||
let Q = rng.random(ECP_SWei_Proj[F])
|
||||
|
||||
r0.sum(P, Q)
|
||||
r1.sum(Q, P)
|
||||
check: bool(r0 == r1)
|
||||
|
||||
test(Fp[BLS12_381], randZ = false)
|
||||
test(Fp[BLS12_381], randZ = true)
|
||||
|
||||
test "EC add is associative":
|
||||
proc test(F: typedesc, randZ: static bool) =
|
||||
for _ in 0 ..< Iters:
|
||||
when randZ:
|
||||
let a = rng.random_with_randZ(ECP_SWei_Proj[F])
|
||||
let b = rng.random_with_randZ(ECP_SWei_Proj[F])
|
||||
let c = rng.random_with_randZ(ECP_SWei_Proj[F])
|
||||
else:
|
||||
let a = rng.random(ECP_SWei_Proj[F])
|
||||
let b = rng.random(ECP_SWei_Proj[F])
|
||||
let c = rng.random(ECP_SWei_Proj[F])
|
||||
|
||||
var tmp1{.noInit.}, tmp2{.noInit.}: ECP_SWei_Proj[F]
|
||||
|
||||
# r0 = (a + b) + c
|
||||
tmp1.sum(a, b)
|
||||
tmp2.sum(tmp1, c)
|
||||
let r0 = tmp2
|
||||
|
||||
# r1 = a + (b + c)
|
||||
tmp1.sum(b, c)
|
||||
tmp2.sum(a, tmp1)
|
||||
let r1 = tmp2
|
||||
|
||||
# r2 = (a + c) + b
|
||||
tmp1.sum(a, c)
|
||||
tmp2.sum(tmp1, b)
|
||||
let r2 = tmp2
|
||||
|
||||
# r3 = a + (c + b)
|
||||
tmp1.sum(c, b)
|
||||
tmp2.sum(a, tmp1)
|
||||
let r3 = tmp2
|
||||
|
||||
# r4 = (c + a) + b
|
||||
tmp1.sum(c, a)
|
||||
tmp2.sum(tmp1, b)
|
||||
let r4 = tmp2
|
||||
|
||||
# ...
|
||||
|
||||
check:
|
||||
bool(r0 == r1)
|
||||
bool(r0 == r2)
|
||||
bool(r0 == r3)
|
||||
bool(r0 == r4)
|
||||
|
||||
test(Fp[BLS12_381], randZ = false)
|
||||
test(Fp[BLS12_381], randZ = true)
|
Loading…
x
Reference in New Issue
Block a user