Skip to content

Commit

Permalink
feat(gt-exp): public API + zkalc bench #425
Browse files Browse the repository at this point in the history
  • Loading branch information
mratsim committed Jul 15, 2024
1 parent 4f3eb84 commit 85aa615
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 7 deletions.
59 changes: 58 additions & 1 deletion benchmarks/zkalc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type
hash_G2: ZkalcBenchDetails

mul_Gt: ZkalcBenchDetails
exp_Gt: ZkalcBenchDetails
multiexp_Gt: ZkalcBenchDetails

pairing: ZkalcBenchDetails
Expand Down Expand Up @@ -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
# -------------------------------------------------------------------------------------

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

Expand Down Expand Up @@ -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() =
Expand Down
16 changes: 15 additions & 1 deletion constantine/lowlevel_pairing_curves.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.`~^`
7 changes: 5 additions & 2 deletions constantine/math/elliptic/ec_scalar_mul.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions constantine/math/elliptic/ec_scalar_mul_vartime.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
##
Expand Down Expand Up @@ -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)

Expand Down
68 changes: 68 additions & 0 deletions constantine/math/pairings/gt_exponentiations.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
96 changes: 95 additions & 1 deletion constantine/math/pairings/gt_exponentiations_vartime.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)

0 comments on commit 85aa615

Please sign in to comment.