From 85aa61508cf1ef8a6fa640a0f883de8e53d77fc7 Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Mon, 15 Jul 2024 22:36:31 +0200 Subject: [PATCH] feat(gt-exp): public API + zkalc bench #425 --- benchmarks/zkalc.nim | 59 +++++++++++- constantine/lowlevel_pairing_curves.nim | 16 +++- constantine/math/elliptic/ec_scalar_mul.nim | 7 +- .../math/elliptic/ec_scalar_mul_vartime.nim | 10 +- .../math/pairings/gt_exponentiations.nim | 68 +++++++++++++ .../pairings/gt_exponentiations_vartime.nim | 96 ++++++++++++++++++- 6 files changed, 249 insertions(+), 7 deletions(-) diff --git a/benchmarks/zkalc.nim b/benchmarks/zkalc.nim index fb452d66..7448f72a 100644 --- a/benchmarks/zkalc.nim +++ b/benchmarks/zkalc.nim @@ -55,6 +55,7 @@ type hash_G2: ZkalcBenchDetails mul_Gt: ZkalcBenchDetails + exp_Gt: ZkalcBenchDetails multiexp_Gt: ZkalcBenchDetails pairing: ZkalcBenchDetails @@ -394,6 +395,57 @@ proc benchEcHashToCurve(rng: var RngState, EC: type): ZkalcBenchDetails = report(G & " Hash-to-Curve", curve, stats) stats.toZkalc() +# ๐”พโ‚œ benches +# ------------------------------------------------------------------------------------- + +func random_gt*(rng: var RngState, F: typedesc): F {.inline, noInit.} = + result = rng.random_unsafe(F) + result.finalExp() + +proc benchGtMul(rng: var RngState, curve: static Algebra): ZkalcBenchDetails = + when curve in {BN254_Snarks, BLS12_377, BLS12_381}: + type Gt = Fp12[curve] + else: + {.error: "๐”พโ‚œ multiplication is not configured for " & $curve.} + + var x = rng.random_gt(Gt) + let y = rng.random_gt(Gt) + + preventOptimAway(x) + preventOptimAway(y) + + let stats = bench(): + x *= y + + report("๐”พโ‚œ Multiplication", curve, stats) + stats.toZkalc() + +proc benchGtExp(rng: var RngState, curve: static Algebra, useVartime: bool): ZkalcBenchDetails = + when curve in {BN254_Snarks, BLS12_377, BLS12_381}: + type Gt = Fp12[curve] + else: + {.error: "๐”พโ‚œ exponentiation is not configured for " & $curve.} + + var r {.noInit.}: Gt + let a = rng.random_gt(Gt) + let k = rng.random_unsafe(Fr[curve].getBigInt()) + + preventOptimAway(r) + preventOptimAway(a) + + if useVartime: + let stats = bench(): + r.gtExp_vartime(a, k) + + report("๐”พโ‚œ exponentiation" & align("| vartime", 16), curve, stats) + stats.toZkalc() + else: + let stats = bench(): + r.gtExp(a, k) + + report("๐”พโ‚œ exponentiation" & align("| constant-time", 16), curve, stats) + stats.toZkalc() + # Pairing benches # ------------------------------------------------------------------------------------- @@ -405,7 +457,7 @@ func clearCofactor[F; G: static Subgroup]( t.clearCofactor() ec.affine(t) -func random_point*(rng: var RngState, EC: typedesc): EC {.noInit.} = +func random_point*(rng: var RngState, EC: typedesc): EC {.inline, noInit.} = result = rng.random_unsafe(EC) result.clearCofactor() @@ -514,6 +566,11 @@ proc runBenches(curve: static Algebra, useVartime: bool): ZkalcBenchResult = zkalc.multipairing = rng.benchMultiPairing(curve, maxNumInputs = 1024) separator() + # Target group ๐”พโ‚œ + # -------------------------------------------------------------------- + zkalc.mul_Gt = rng.benchGtMul(curve) + zkalc.exp_Gt = rng.benchGtExp(curve, useVartime) + return zkalc proc main() = diff --git a/constantine/lowlevel_pairing_curves.nim b/constantine/lowlevel_pairing_curves.nim index f9273066..19c53ff2 100644 --- a/constantine/lowlevel_pairing_curves.nim +++ b/constantine/lowlevel_pairing_curves.nim @@ -14,7 +14,9 @@ import ./math/pairings/[ cyclotomic_subgroups, lines_eval, - pairings_generic] + pairings_generic, + gt_exponentiations, + gt_exponentiations_vartime] # ############################################################ # # Low-level named Pairing-Friendly Curve API @@ -64,3 +66,15 @@ export zoo_pairings.isInPairingSubgroup export pairings_generic.pairing export pairings_generic.millerLoop export pairings_generic.finalExp + +export gt_exponentiations.gtExp +export gt_exponentiations_vartime.gtExp_vartime + +# Out-of-place functions SHOULD NOT be used in performance-critical subroutines as compilers +# tend to generate useless memory moves or have difficulties to minimize stack allocation +# and our types might be large (Fp12 ...) +# See: https://github.com/mratsim/constantine/issues/145 +# +# They are intended for rapid prototyping, testing and debugging. +export gt_exponentiations.`^` +export gt_exponentiations_vartime.`~^` diff --git a/constantine/math/elliptic/ec_scalar_mul.nim b/constantine/math/elliptic/ec_scalar_mul.nim index 10b0094b..76acedae 100644 --- a/constantine/math/elliptic/ec_scalar_mul.nim +++ b/constantine/math/elliptic/ec_scalar_mul.nim @@ -422,8 +422,11 @@ func scalarMulGLV_m2w2*[scalBits; EC](P0: var EC, scalar: BigInt[scalBits]) {.me P0.diff(Q, P0) P0.ccopy(Q, k0isOdd) -# Public API -# -------------------------------------------------------------------------------------- +# ############################################################ +# +# Public API +# +# ############################################################ func scalarMul*[EC](P: var EC, scalar: BigInt) {.inline, meter.} = ## Elliptic Curve Scalar Multiplication diff --git a/constantine/math/elliptic/ec_scalar_mul_vartime.nim b/constantine/math/elliptic/ec_scalar_mul_vartime.nim index 4f4c719b..dbff3a47 100644 --- a/constantine/math/elliptic/ec_scalar_mul_vartime.nim +++ b/constantine/math/elliptic/ec_scalar_mul_vartime.nim @@ -330,12 +330,18 @@ func scalarMulEndo_wNAF_vartime*[scalBits: static int; EC]( else: isInit = P.initNAF(tab[m], tabNaf[m], NafLen, i) +# ############################################################ +# +# Public API +# +# ############################################################ + func scalarMul_vartime*[scalBits; EC](P: var EC, scalar: BigInt[scalBits]) {.meter.} = ## Elliptic Curve Scalar Multiplication ## ## P <- [k] P ## - ## This select the best algorithm depending on heuristics + ## This selects the best algorithm depending on heuristics ## and the scalar being multiplied. ## The scalar MUST NOT be a secret as this does not use side-channel countermeasures ## @@ -372,7 +378,7 @@ func scalarMul_vartime*[scalBits; EC](P: var EC, scalar: BigInt[scalBits]) {.met # With a window of 3, we precompute 2^1 = 2 points P.scalarMul_wNAF_vartime(scalar, window = 3) elif 4 < usedBits: - P.scalarMul_doubleAdd_vartime(scalar) + P.scalarMul_jy00_vartime(scalar) else: P.scalarMul_addchain_4bit_vartime(scalar) diff --git a/constantine/math/pairings/gt_exponentiations.nim b/constantine/math/pairings/gt_exponentiations.nim index 901bf14a..3c1f9b2c 100644 --- a/constantine/math/pairings/gt_exponentiations.nim +++ b/constantine/math/pairings/gt_exponentiations.nim @@ -108,3 +108,71 @@ func gtExpEndo*[Gt: ExtensionField, scalBits: static int]( # Now we need to correct if the sign miniscalar was not odd r.quot(Q, r) r.ccopy(Q, k0isOdd) + +# ############################################################ +# +# Public API +# +# ############################################################ + + +func gtExp*[Gt](r: var Gt, a: Gt, scalar: BigInt) {.inline, meter.} = + ## Exponentiation in ๐”พโ‚œ + ## + ## r <- aแต + ## + ## This use endomorphism acceleration by default if available + ## Endomorphism acceleration requires: + ## - Cofactor to be cleared + ## - 0 <= scalar < curve order + ## Those will be assumed to maintain constant-time property + when Gt.Name.hasEndomorphismAcceleration() and + BigInt.bits >= EndomorphismThreshold: + when Gt is Fp6: + r.gtExpEndo(a, scalar) # TODO: window method + elif Gt is Fp12: + r.gtExpEndo(a, scalar) + else: # Curves defined on Fp^m with m > 2 + {.error: "Unconfigured".} + else: + {.error: "Unimplemented".} + +func gtExp*[EC](r: var EC, a: EC, scalar: Fr) {.inline.} = + ## Exponentiation in ๐”พโ‚œ + ## + ## r <- aแต + r.gtExp(a, scalar.toBig()) + +func gtExp*[EC](a: var EC, scalar: Fr or BigInt) {.inline.} = + ## Exponentiation in ๐”พโ‚œ + ## + ## r <- aแต + ## + ## This use endomorphism acceleration by default if available + ## Endomorphism acceleration requires: + ## - Cofactor to be cleared + ## - 0 <= scalar < curve order + ## Those will be assumed to maintain constant-time property + a.gtExp(a, scalar) + +# ############################################################ +# +# Out-of-Place functions +# +# ############################################################ +# +# Out-of-place functions SHOULD NOT be used in performance-critical subroutines as compilers +# tend to generate useless memory moves or have difficulties to minimize stack allocation +# and our types might be large (Fp12 ...) +# See: https://github.com/mratsim/constantine/issues/145 + +func `^`*[Gt: ExtensionField](a: Gt, scalar: Fr or BigInt): Gt {.noInit, inline.} = + ## Exponentiation in ๐”พโ‚œ + ## + ## r <- aแต + ## + ## Out-of-place functions SHOULD NOT be used in performance-critical subroutines as compilers + ## tend to generate useless memory moves or have difficulties to minimize stack allocation + ## and our types might be large (Fp12 ...) + ## See: https://github.com/mratsim/constantine/issues/145 + result.gtExp(a, scalar) diff --git a/constantine/math/pairings/gt_exponentiations_vartime.nim b/constantine/math/pairings/gt_exponentiations_vartime.nim index a307bd6e..29cad331 100644 --- a/constantine/math/pairings/gt_exponentiations_vartime.nim +++ b/constantine/math/pairings/gt_exponentiations_vartime.nim @@ -69,7 +69,7 @@ func gtExp_addchain_4bit_vartime[Gt: ExtensionField](r: var Gt, a: Gt, scalar: B case s of 0: - r.setNeutral() + r.setOne() of 1: discard of 2: @@ -323,3 +323,97 @@ func gtExpEndo_wNAF_vartime*[Gt: ExtensionField, scalBits: static int]( r.accumNAF(tab[m], tabNaf[m], NafLen, i) else: isInit = r.initNAF(tab[m], tabNaf[m], NafLen, i) + +# ############################################################ +# +# Public API +# +# ############################################################ + +func gtExp_vartime*[Gt: ExtensionField, scalBits: static int]( + r: var Gt, a: Gt, scalar: BigInt[scalBits]) {.tags:[VarTime], meter.} = + ## Exponentiation in ๐”พโ‚œ + ## + ## r <- aแต + ## + ## This selects the best algorithm depending on heuristics + ## and the scalar being multiplied. + ## The scalar MUST NOT be a secret as this does not use side-channel countermeasures + ## + ## This may use endomorphism acceleration. + ## As endomorphism acceleration requires: + ## - Cofactor to be cleared + ## - 0 <= scalar < curve order + ## Those conditions will be assumed. + + let usedBits = scalar.limbs.getBits_LE_vartime() + + when Gt.Name.hasEndomorphismAcceleration(): + when scalBits >= EndomorphismThreshold: # Skip static: doAssert when multiplying by intentionally small scalars. + if usedBits >= EndomorphismThreshold: + when Gt is Fp6: + r.gtExpEndo_wNAF_vartime(a, scalar, window = 4) + elif Gt is Fp12: + r.gtExpEndo_wNAF_vartime(a, scalar, window = 3) + else: # Curves defined on Fp^m with m > 2 + {.error: "Unconfigured".} + return + + if 64 < usedBits: + # With a window of 4, we precompute 2^4 = 4 elements + r.gtExp_wNAF_vartime(a, scalar, window = 4) + elif 16 < usedBits: + # With a window of 3, we precompute 2^1 = 2 elements + r.gtExp_wNAF_vartime(a, scalar, window = 3) + elif 4 < usedBits: + r.gtExp_jy00_vartime(a, scalar) + else: + r.gtExp_addchain_4bit_vartime(a, scalar) + +func gtExp_vartime*[Gt](r: var Gt, a: Gt, scalar: Fr) {.inline.} = + ## Exponentiation in ๐”พโ‚œ + ## + ## r <- aแต + ## + ## This select the best algorithm depending on heuristics + ## and the scalar being multiplied. + ## The scalar MUST NOT be a secret as this does not use side-channel countermeasures + r.gtExp_vartime(a, scalar.toBig()) + +func gtExp_vartime*[Gt](a: var Gt, scalar: Fr or BigInt) {.inline.} = + ## Exponentiation in ๐”พโ‚œ + ## + ## r <- aแต + ## + ## This selects the best algorithm depending on heuristics + ## and the scalar being multiplied. + ## The scalar MUST NOT be a secret as this does not use side-channel countermeasures + ## + ## This may use endomorphism acceleration. + ## As endomorphism acceleration requires: + ## - Cofactor to be cleared + ## - 0 <= scalar < curve order + ## Those conditions will be assumed. + a.gtExp_vartime(a, scalar) + +# ############################################################ +# +# Out-of-Place functions +# +# ############################################################ +# +# Out-of-place functions SHOULD NOT be used in performance-critical subroutines as compilers +# tend to generate useless memory moves or have difficulties to minimize stack allocation +# and our types might be large (Fp12 ...) +# See: https://github.com/mratsim/constantine/issues/145 + +func `~^`*[Gt: ExtensionField](a: Gt, scalar: Fr or BigInt): Gt {.noInit, inline.} = + ## Elliptic Curve variable-time Scalar Multiplication + ## + ## r <- aแต + ## + ## Out-of-place functions SHOULD NOT be used in performance-critical subroutines as compilers + ## tend to generate useless memory moves or have difficulties to minimize stack allocation + ## and our types might be large (Fp12 ...) + ## See: https://github.com/mratsim/constantine/issues/145 + result.gtExp_vartime(a, scalar)