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:
Mamy Ratsimbazafy 2020-04-13 19:25:59 +02:00 committed by GitHub
parent 1889fc4eeb
commit 2f839cb1bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1358 additions and 398 deletions

View File

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

View File

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

View File

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

View File

@ -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 𝔽
## i.e. a number that is not a cube in 𝔽
##
## 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 𝔽
## 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

View 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 𝔽
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 𝔽
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 𝔽
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

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

View File

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

View File

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

View 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 𝔽".}
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

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

View File

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

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