Accelerate FFT - endomorphism + wNAF vartime scalar mul (#258)
* accel FFT by 30+% with vartime endomorphism support * silly error fix * endomorphism + wNAF, closes #253, FFT 20% speedup * vartime EC addition for all repr * implement vartime EC add * finishing touches, renam to fft_vartime
This commit is contained in:
parent
4981c383bb
commit
b9c911ba37
|
@ -45,11 +45,11 @@ proc main() =
|
|||
staticFor i, 0, AvailableCurves.len:
|
||||
const curve = AvailableCurves[i]
|
||||
const bits = curve.getCurveOrderBitwidth()
|
||||
scalarMulUnsafeDoubleAddBench(ECP_ShortW_Prj[Fp[curve], G1], bits, MulIters)
|
||||
scalarMulUnsafeDoubleAddBench(ECP_ShortW_Jac[Fp[curve], G1], bits, MulIters)
|
||||
scalarMulVartimeDoubleAddBench(ECP_ShortW_Prj[Fp[curve], G1], bits, MulIters)
|
||||
scalarMulVartimeDoubleAddBench(ECP_ShortW_Jac[Fp[curve], G1], bits, MulIters)
|
||||
separator()
|
||||
scalarMulUnsafeMinHammingWeightRecodingBench(ECP_ShortW_Prj[Fp[curve], G1], bits, MulIters)
|
||||
scalarMulUnsafeMinHammingWeightRecodingBench(ECP_ShortW_Jac[Fp[curve], G1], bits, MulIters)
|
||||
scalarMulVartimeMinHammingWeightRecodingBench(ECP_ShortW_Prj[Fp[curve], G1], bits, MulIters)
|
||||
scalarMulVartimeMinHammingWeightRecodingBench(ECP_ShortW_Jac[Fp[curve], G1], bits, MulIters)
|
||||
separator()
|
||||
scalarMulGenericBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 2, MulIters)
|
||||
scalarMulGenericBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 3, MulIters)
|
||||
|
@ -60,16 +60,25 @@ proc main() =
|
|||
scalarMulGenericBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 4, MulIters)
|
||||
scalarMulGenericBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 5, MulIters)
|
||||
separator()
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 2, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 3, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 4, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 5, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 2, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 3, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 4, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 5, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 2, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 3, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 4, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 5, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 2, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 3, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 4, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 5, MulIters)
|
||||
separator()
|
||||
when bits >= 196: # All endomorphisms constants are below this threshold
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 2, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 3, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 4, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Prj[Fp[curve], G1], bits, window = 5, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 2, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 3, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 4, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Jac[Fp[curve], G1], bits, window = 5, MulIters)
|
||||
separator()
|
||||
scalarMulEndo( ECP_ShortW_Prj[Fp[curve], G1], bits, MulIters)
|
||||
scalarMulEndoWindow(ECP_ShortW_Prj[Fp[curve], G1], bits, MulIters)
|
||||
scalarMulEndo( ECP_ShortW_Jac[Fp[curve], G1], bits, MulIters)
|
||||
|
|
|
@ -46,11 +46,11 @@ proc main() =
|
|||
staticFor i, 0, AvailableCurves.len:
|
||||
const curve = AvailableCurves[i]
|
||||
const bits = curve.getCurveOrderBitwidth()
|
||||
scalarMulUnsafeDoubleAddBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, MulIters)
|
||||
scalarMulUnsafeDoubleAddBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, MulIters)
|
||||
scalarMulVartimeDoubleAddBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, MulIters)
|
||||
scalarMulVartimeDoubleAddBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, MulIters)
|
||||
separator()
|
||||
scalarMulUnsafeMinHammingWeightRecodingBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, MulIters)
|
||||
scalarMulUnsafeMinHammingWeightRecodingBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, MulIters)
|
||||
scalarMulVartimeMinHammingWeightRecodingBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, MulIters)
|
||||
scalarMulVartimeMinHammingWeightRecodingBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, MulIters)
|
||||
separator()
|
||||
scalarMulGenericBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 2, MulIters)
|
||||
scalarMulGenericBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 3, MulIters)
|
||||
|
@ -61,16 +61,25 @@ proc main() =
|
|||
scalarMulGenericBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 4, MulIters)
|
||||
scalarMulGenericBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 5, MulIters)
|
||||
separator()
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 2, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 3, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 4, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 5, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 2, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 3, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 4, MulIters)
|
||||
scalarMulUnsafeWNAFBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 5, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 2, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 3, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 4, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 5, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 2, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 3, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 4, MulIters)
|
||||
scalarMulVartimeWNAFBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 5, MulIters)
|
||||
separator()
|
||||
when bits >= 196: # All endomorphisms constants are below this threshold
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 2, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 3, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 4, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Prj[Fp2[curve], G2], bits, window = 5, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 2, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 3, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 4, MulIters)
|
||||
scalarMulVartimeEndoWNAFBench(ECP_ShortW_Jac[Fp2[curve], G2], bits, window = 5, MulIters)
|
||||
separator()
|
||||
scalarMulEndo(ECP_ShortW_Prj[Fp2[curve], G2], bits, MulIters)
|
||||
scalarMulEndo(ECP_ShortW_Jac[Fp2[curve], G2], bits, MulIters)
|
||||
separator()
|
||||
|
|
|
@ -52,9 +52,9 @@ proc report(op, elliptic: string, start, stop: MonoTime, startClk, stopClk: int6
|
|||
let ns = inNanoseconds((stop-start) div iters)
|
||||
let throughput = 1e9 / float64(ns)
|
||||
when SupportsGetTicks:
|
||||
echo &"{op:<68} {elliptic:<32} {throughput:>15.3f} ops/s {ns:>16} ns/op {(stopClk - startClk) div iters:>12} CPU cycles (approx)"
|
||||
echo &"{op:<68} {elliptic:<36} {throughput:>15.3f} ops/s {ns:>16} ns/op {(stopClk - startClk) div iters:>12} CPU cycles (approx)"
|
||||
else:
|
||||
echo &"{op:<68} {elliptic:<32} {throughput:>15.3f} ops/s {ns:>16} ns/op"
|
||||
echo &"{op:<68} {elliptic:<36} {throughput:>15.3f} ops/s {ns:>16} ns/op"
|
||||
|
||||
template bench*(op: string, EC: typedesc, iters: int, body: untyped): untyped =
|
||||
measure(iters, startTime, stopTime, startClk, stopClk, body)
|
||||
|
@ -74,8 +74,12 @@ proc addBench*(EC: typedesc, iters: int) =
|
|||
bench("EC Add vartime " & $EC.G, EC, iters):
|
||||
r.sum_vartime(P, Q)
|
||||
else:
|
||||
bench("EC Add " & $EC.G, EC, iters):
|
||||
r.sum(P, Q)
|
||||
block:
|
||||
bench("EC Add " & $EC.G, EC, iters):
|
||||
r.sum(P, Q)
|
||||
block:
|
||||
bench("EC Add vartime " & $EC.G, EC, iters):
|
||||
r.sum_vartime(P, Q)
|
||||
|
||||
proc mixedAddBench*(EC: typedesc, iters: int) =
|
||||
var r {.noInit.}: EC
|
||||
|
@ -88,8 +92,12 @@ proc mixedAddBench*(EC: typedesc, iters: int) =
|
|||
bench("EC Mixed Addition vartime " & $EC.G, EC, iters):
|
||||
r.madd_vartime(P, Qaff)
|
||||
else:
|
||||
bench("EC Mixed Addition " & $EC.G, EC, iters):
|
||||
r.madd(P, Qaff)
|
||||
block:
|
||||
bench("EC Mixed Addition " & $EC.G, EC, iters):
|
||||
r.madd(P, Qaff)
|
||||
block:
|
||||
bench("EC Mixed Addition vartime " & $EC.G, EC, iters):
|
||||
r.madd_vartime(P, Qaff)
|
||||
|
||||
proc doublingBench*(EC: typedesc, iters: int) =
|
||||
var r {.noInit.}: EC
|
||||
|
@ -175,39 +183,50 @@ proc scalarMulEndoWindow*(EC: typedesc, bits: static int, iters: int) =
|
|||
else:
|
||||
{.error: "Not implemented".}
|
||||
|
||||
proc scalarMulUnsafeDoubleAddBench*(EC: typedesc, bits: static int, iters: int) =
|
||||
proc scalarMulVartimeDoubleAddBench*(EC: typedesc, bits: static int, iters: int) =
|
||||
var r {.noInit.}: EC
|
||||
var P = rng.random_unsafe(EC)
|
||||
P.clearCofactor()
|
||||
|
||||
let exponent = rng.random_unsafe(BigInt[bits])
|
||||
|
||||
bench("EC ScalarMul " & $bits & "-bit " & $EC.G & " (unsafe reference DoubleAdd)", EC, iters):
|
||||
bench("EC ScalarMul " & $bits & "-bit " & $EC.G & " (vartime reference DoubleAdd)", EC, iters):
|
||||
r = P
|
||||
r.scalarMul_doubleAdd_vartime(exponent)
|
||||
|
||||
proc scalarMulUnsafeMinHammingWeightRecodingBench*(EC: typedesc, bits: static int, iters: int) =
|
||||
proc scalarMulVartimeMinHammingWeightRecodingBench*(EC: typedesc, bits: static int, iters: int) =
|
||||
var r {.noInit.}: EC
|
||||
var P = rng.random_unsafe(EC)
|
||||
P.clearCofactor()
|
||||
|
||||
let exponent = rng.random_unsafe(BigInt[bits])
|
||||
|
||||
bench("EC ScalarMul " & $bits & "-bit " & $EC.G & " (unsafe min Hamming Weight recoding)", EC, iters):
|
||||
bench("EC ScalarMul " & $bits & "-bit " & $EC.G & " (vartime min Hamming Weight recoding)", EC, iters):
|
||||
r = P
|
||||
r.scalarMul_minHammingWeight_vartime(exponent)
|
||||
|
||||
proc scalarMulUnsafeWNAFBench*(EC: typedesc, bits, window: static int, iters: int) =
|
||||
proc scalarMulVartimeWNAFBench*(EC: typedesc, bits, window: static int, iters: int) =
|
||||
var r {.noInit.}: EC
|
||||
var P = rng.random_unsafe(EC)
|
||||
P.clearCofactor()
|
||||
|
||||
let exponent = rng.random_unsafe(BigInt[bits])
|
||||
|
||||
bench("EC ScalarMul " & $bits & "-bit " & $EC.G & " (unsafe wNAF-" & $window & ")", EC, iters):
|
||||
bench("EC ScalarMul " & $bits & "-bit " & $EC.G & " (vartime wNAF-" & $window & ")", EC, iters):
|
||||
r = P
|
||||
r.scalarMul_minHammingWeight_windowed_vartime(exponent, window)
|
||||
|
||||
proc scalarMulVartimeEndoWNAFBench*(EC: typedesc, bits, window: static int, iters: int) =
|
||||
var r {.noInit.}: EC
|
||||
var P = rng.random_unsafe(EC)
|
||||
P.clearCofactor()
|
||||
|
||||
let exponent = rng.random_unsafe(BigInt[bits])
|
||||
|
||||
bench("EC ScalarMul " & $bits & "-bit " & $EC.G & " (vartime endomorphism + wNAF-" & $window & ")", EC, iters):
|
||||
r = P
|
||||
r.scalarMulEndo_minHammingWeight_windowed_vartime(exponent, window)
|
||||
|
||||
proc multiAddBench*(EC: typedesc, numPoints: int, useBatching: bool, iters: int) =
|
||||
var points = newSeq[ECP_ShortW_Aff[EC.F, EC.G]](numPoints)
|
||||
|
||||
|
|
|
@ -280,8 +280,7 @@ func secretLookup[T](dst: var T, table: openArray[T], index: SecretWord) =
|
|||
|
||||
func scalarMulEndo*[scalBits; EC](
|
||||
P: var EC,
|
||||
scalar: BigInt[scalBits]
|
||||
) =
|
||||
scalar: BigInt[scalBits]) =
|
||||
## Elliptic Curve Scalar Multiplication
|
||||
##
|
||||
## P <- [k] P
|
||||
|
|
|
@ -76,8 +76,8 @@ func multiScalarMulImpl_reference_vartime[F, G; bits: static int](
|
|||
|
||||
# Example with c = 3, 2³ = 8
|
||||
for k in countdown(numBuckets-2, 0):
|
||||
accumBuckets += buckets[k] # Stores S₈ then S₈+S₇ then S₈+S₇+S₆ then ...
|
||||
miniMSM += accumBuckets # Stores S₈ then [2]S₈+S₇ then [3]S₈+[2]S₇+S₆ then ...
|
||||
accumBuckets.sum_vartime(accumBuckets, buckets[k]) # Stores S₈ then S₈+S₇ then S₈+S₇+S₆ then ...
|
||||
miniMSM.sum_vartime(miniMSM, accumBuckets) # Stores S₈ then [2]S₈+S₇ then [3]S₈+[2]S₇+S₆ then ...
|
||||
|
||||
miniMSMs[w] = miniMSM
|
||||
|
||||
|
@ -86,7 +86,7 @@ func multiScalarMulImpl_reference_vartime[F, G; bits: static int](
|
|||
for w in countdown(numWindows-2, 0):
|
||||
for _ in 0 ..< c:
|
||||
r.double()
|
||||
r += miniMSMs[w]
|
||||
r.sum_vartime(r, miniMSMs[w])
|
||||
|
||||
# Cleanup
|
||||
# -------
|
||||
|
@ -152,8 +152,8 @@ func bucketReduce[EC](r: var EC, buckets: ptr UncheckedArray[EC], numBuckets: st
|
|||
buckets[numBuckets-1].setInf()
|
||||
|
||||
for k in countdown(numBuckets-2, 0):
|
||||
accumBuckets += buckets[k]
|
||||
r += accumBuckets
|
||||
accumBuckets.sum_vartime(accumBuckets, buckets[k])
|
||||
r.sum_vartime(r, accumBuckets)
|
||||
buckets[k].setInf()
|
||||
|
||||
type MiniMsmKind* = enum
|
||||
|
@ -211,7 +211,7 @@ func miniMSM_jacext[F, G; bits: static int](
|
|||
coefs, points, N)
|
||||
|
||||
# 3. Mini-MSM on the slice [bitIndex, bitIndex+window)
|
||||
r += windowSum
|
||||
r.sum_vartime(r, windowSum)
|
||||
when miniMsmKind != kBottomWindow:
|
||||
for _ in 0 ..< c:
|
||||
r.double()
|
||||
|
@ -303,7 +303,7 @@ func miniMSM_affine[NumBuckets, QueueLen, F, G; bits: static int](
|
|||
# 3. Mini-MSM on the slice [bitIndex, bitIndex+window)
|
||||
var windowSum{.noInit.}: typeof(r)
|
||||
windowSum.fromJacobianExtended_vartime(windowSum_jacext)
|
||||
r += windowSum
|
||||
r.sum_vartime(r, windowSum)
|
||||
|
||||
when miniMsmKind != kBottomWindow:
|
||||
for _ in 0 ..< c:
|
||||
|
|
|
@ -200,7 +200,7 @@ proc msmJacExt_vartime_parallel*[bits: static int, EC, F, G](
|
|||
for _ in 0 ..< c:
|
||||
r[].double()
|
||||
discard sync miniMSMsReady[w]
|
||||
r[] += miniMSMsResults[w]
|
||||
r[].sum_vartime(r[], miniMSMsResults[w])
|
||||
elif numWindows >= 2:
|
||||
discard sync miniMSMsReady[numWindows-2]
|
||||
r[] = miniMSMsResults[numWindows-2]
|
||||
|
@ -208,7 +208,7 @@ proc msmJacExt_vartime_parallel*[bits: static int, EC, F, G](
|
|||
for _ in 0 ..< c:
|
||||
r[].double()
|
||||
discard sync miniMSMsReady[w]
|
||||
r[] += miniMSMsResults[w]
|
||||
r[].sum_vartime(r[], miniMSMsResults[w])
|
||||
|
||||
# Cleanup
|
||||
# -------
|
||||
|
@ -389,7 +389,7 @@ proc msmAffine_vartime_parallel*[bits: static int, EC, F, G](
|
|||
for _ in 0 ..< c:
|
||||
r[].double()
|
||||
discard sync miniMSMsReady[w]
|
||||
r[] += miniMSMsResults[w]
|
||||
r[].sum_vartime(r[], miniMSMsResults[w])
|
||||
elif numWindows >= 2:
|
||||
discard sync miniMSMsReady[numWindows-2]
|
||||
r[] = miniMSMsResults[numWindows-2]
|
||||
|
@ -397,7 +397,7 @@ proc msmAffine_vartime_parallel*[bits: static int, EC, F, G](
|
|||
for _ in 0 ..< c:
|
||||
r[].double()
|
||||
discard sync miniMSMsReady[w]
|
||||
r[] += miniMSMsResults[w]
|
||||
r[].sum_vartime(r[], miniMSMsResults[w])
|
||||
|
||||
# Cleanup
|
||||
# -------
|
||||
|
@ -446,7 +446,7 @@ proc msmAffine_vartime_parallel_split[bits: static int, EC, F, G](
|
|||
|
||||
for i in countdown(msmParallelism-2, 0):
|
||||
discard sync splitMSMsReady[i]
|
||||
r[] += splitMSMsResults[i]
|
||||
r[].sum_vartime(r[], splitMSMsResults[i])
|
||||
|
||||
freeHeap(splitMSMsResults)
|
||||
|
||||
|
|
|
@ -8,21 +8,33 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
./ec_endomorphism_accel,
|
||||
../arithmetic,
|
||||
../extension_fields,
|
||||
../ec_shortweierstrass,
|
||||
../io/io_bigints,
|
||||
../../platforms/abstractions
|
||||
../constants/zoo_endomorphisms,
|
||||
../isogenies/frobenius,
|
||||
../../platforms/abstractions,
|
||||
../../math_arbitrary_precision/arithmetic/limbs_views
|
||||
|
||||
{.push raises: [].} # No exceptions allowed in core cryptographic operations
|
||||
{.push checks: off.} # No defects due to array bound checking or signed integer overflow allowed
|
||||
|
||||
# Support files for testing Elliptic Curve arithmetic
|
||||
# Bit operations
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
iterator unpackBE(scalarByte: byte): bool =
|
||||
for i in countdown(7, 0):
|
||||
yield bool((scalarByte shr i) and 1)
|
||||
|
||||
# Variable-time scalar multiplication
|
||||
# ------------------------------------------------------------------------------
|
||||
template `+=`[F; G: static Subgroup](P: var ECP_ShortW[F, G], Q: ECP_ShortW_Aff[F, G]) =
|
||||
P.madd_vartime(P, Q)
|
||||
template `-=`[F; G: static Subgroup](P: var ECP_ShortW[F, G], Q: ECP_ShortW_Aff[F, G]) =
|
||||
P.msub_vartime(P, Q)
|
||||
|
||||
func scalarMul_doubleAdd_vartime*[EC](P: var EC, scalar: BigInt) {.tags:[VarTime].} =
|
||||
## **Variable-time** Elliptic Curve Scalar Multiplication
|
||||
##
|
||||
|
@ -39,11 +51,106 @@ func scalarMul_doubleAdd_vartime*[EC](P: var EC, scalar: BigInt) {.tags:[VarTime
|
|||
Paff.affine(P)
|
||||
|
||||
P.setInf()
|
||||
var isInf = true
|
||||
|
||||
for scalarByte in scalarCanonical:
|
||||
for bit in unpackBE(scalarByte):
|
||||
P.double()
|
||||
if not isInf:
|
||||
P.double()
|
||||
if bit:
|
||||
P += Paff
|
||||
if isInf:
|
||||
P.fromAffine(Paff)
|
||||
isInf = false
|
||||
else:
|
||||
P += Paff
|
||||
|
||||
func scalarMul_addchain_4bit_vartime[EC](P: var EC, scalar: BigInt) {.tags:[VarTime].} =
|
||||
## **Variable-time** Elliptic Curve Scalar Multiplication
|
||||
## This can only handle for small scalars up to 2⁴ = 16 excluded
|
||||
let s = uint scalar.limbs[0]
|
||||
|
||||
case s
|
||||
of 0:
|
||||
P.setInf()
|
||||
of 1:
|
||||
return
|
||||
of 2:
|
||||
P.double()
|
||||
of 3:
|
||||
var t {.noInit.}: EC
|
||||
t.double(P)
|
||||
P.sum_vartime(P, t)
|
||||
of 4:
|
||||
P.double()
|
||||
P.double()
|
||||
of 5:
|
||||
var t {.noInit.}: EC
|
||||
t.double(P)
|
||||
t.double(P)
|
||||
P.sum_vartime(P, t)
|
||||
of 6:
|
||||
var t {.noInit.}: EC
|
||||
t.double(P)
|
||||
P.sum_vartime(P, t)
|
||||
P.double()
|
||||
of 7:
|
||||
var t {.noInit.}: EC
|
||||
t.double(P)
|
||||
t.double()
|
||||
t.double()
|
||||
P.diff_vartime(t, P)
|
||||
of 8:
|
||||
P.double()
|
||||
P.double()
|
||||
P.double()
|
||||
of 9:
|
||||
var t {.noInit.}: EC
|
||||
t.double(P)
|
||||
t.double()
|
||||
t.double()
|
||||
P.sum_vartime(P, t)
|
||||
of 10:
|
||||
var t {.noInit.}: EC
|
||||
t.double(P)
|
||||
t.double()
|
||||
P.sum_vartime(P, t)
|
||||
P.double()
|
||||
of 11:
|
||||
var t1 {.noInit.}, t2 {.noInit.}: EC
|
||||
t1.double(P) # [2]P
|
||||
t2.double(t1)
|
||||
t2.double() # [8]P
|
||||
t1.sum_vartime(t1, t2)
|
||||
P.sum_vartime(P, t1)
|
||||
of 12:
|
||||
var t1 {.noInit.}, t2 {.noInit.}: EC
|
||||
t1.double(P)
|
||||
t1.double() # [4]P
|
||||
t2.double(t1) # [8]P
|
||||
P.sum_vartime(t1, t2)
|
||||
of 13:
|
||||
var t1 {.noInit.}, t2 {.noInit.}: EC
|
||||
t1.double(P)
|
||||
t1.double() # [4]P
|
||||
t2.double(t1) # [8]P
|
||||
t1.sum_vartime(t1, t2)
|
||||
P.sum_vartime(P, t1)
|
||||
of 14:
|
||||
var t {.noInit.}: EC
|
||||
t.double(P)
|
||||
t.double()
|
||||
t.double()
|
||||
t.diff_vartime(t, P) # [7]P
|
||||
P.double(t)
|
||||
of 15:
|
||||
var t {.noInit.}: EC
|
||||
t.double(P)
|
||||
t.double()
|
||||
t.double()
|
||||
t.double()
|
||||
P.diff_vartime(t, P)
|
||||
else:
|
||||
unreachable()
|
||||
|
||||
func scalarMul_minHammingWeight_vartime*[EC](P: var EC, scalar: BigInt) {.tags:[VarTime].} =
|
||||
## **Variable-time** Elliptic Curve Scalar Multiplication
|
||||
|
@ -66,6 +173,36 @@ func scalarMul_minHammingWeight_vartime*[EC](P: var EC, scalar: BigInt) {.tags:[
|
|||
elif bit == -1:
|
||||
P -= Paff
|
||||
|
||||
func initNAF[precompSize, NafMax: static int, EC, ECaff](
|
||||
P: var EC,
|
||||
tab: array[precompSize, ECaff],
|
||||
naf: array[NafMax, int8], nafLen: int,
|
||||
nafIteratorIdx: int): bool {.inline.} =
|
||||
|
||||
let digit = naf[nafLen-1-nafIteratorIdx]
|
||||
if digit > 0:
|
||||
P.fromAffine(tab[digit shr 1])
|
||||
return true
|
||||
elif digit < 0:
|
||||
P.fromAffine(tab[digit shr 1])
|
||||
P.neg()
|
||||
return true
|
||||
else:
|
||||
P.setInf()
|
||||
return false
|
||||
|
||||
func accumNAF[precompSize, NafMax: static int, EC, ECaff](
|
||||
P: var EC,
|
||||
tab: array[precompSize, ECaff],
|
||||
naf: array[NafMax, int8], nafLen: int,
|
||||
nafIteratorIdx: int) {.inline.} =
|
||||
|
||||
let digit = naf[nafLen-1-nafIteratorIdx]
|
||||
if digit > 0:
|
||||
P += tab[digit shr 1]
|
||||
elif digit < 0:
|
||||
P -= tab[-digit shr 1]
|
||||
|
||||
func scalarMul_minHammingWeight_windowed_vartime*[EC](P: var EC, scalar: BigInt, window: static int) {.tags:[VarTime, Alloca].} =
|
||||
## **Variable-time** Elliptic Curve Scalar Multiplication
|
||||
##
|
||||
|
@ -78,45 +215,169 @@ func scalarMul_minHammingWeight_windowed_vartime*[EC](P: var EC, scalar: BigInt,
|
|||
|
||||
# Signed digits divides precomputation table size by 2
|
||||
# Odd-only divides precomputation table size by another 2
|
||||
|
||||
const precompSize = 1 shl (window - 2)
|
||||
|
||||
when window <= 8:
|
||||
type I = int8
|
||||
elif window <= 16:
|
||||
type I = int16
|
||||
elif window <= 32:
|
||||
type I = int32
|
||||
else:
|
||||
type I = int64
|
||||
|
||||
var naf {.noInit.}: array[BigInt.bits+1, I]
|
||||
let nafLen = naf.recode_r2l_signed_window_vartime(scalar, window)
|
||||
|
||||
var P2{.noInit.}: EC
|
||||
P2.double(P)
|
||||
static: doAssert window < 8, "Window is too large and precomputation would use " & $(precompSize * sizeof(EC)) & " stack space."
|
||||
|
||||
var tabEC {.noinit.}: array[precompSize, EC]
|
||||
var P2{.noInit.}: EC
|
||||
tabEC[0] = P
|
||||
P2.double(P)
|
||||
for i in 1 ..< tabEC.len:
|
||||
tabEC[i].sum(tabEC[i-1], P2)
|
||||
tabEC[i].sum_vartime(tabEC[i-1], P2)
|
||||
|
||||
var tab {.noinit.}: array[precompSize, affine(EC)]
|
||||
tab.batchAffine(tabEC)
|
||||
|
||||
# init
|
||||
if naf[nafLen-1] > 0:
|
||||
P.fromAffine(tab[naf[nafLen-1] shr 1])
|
||||
elif naf[nafLen-1] < 0:
|
||||
P.fromAffine(tab[-naf[nafLen-1] shr 1])
|
||||
P.neg()
|
||||
else:
|
||||
P.setInf()
|
||||
var naf {.noInit.}: array[BigInt.bits+1, int8]
|
||||
let nafLen = naf.recode_r2l_signed_window_vartime(scalar, window)
|
||||
|
||||
# steady state
|
||||
for i in 1 ..< nafLen:
|
||||
P.double()
|
||||
let digit = naf[nafLen-1-i]
|
||||
if digit > 0:
|
||||
P += tab[digit shr 1]
|
||||
elif digit < 0:
|
||||
P -= tab[-digit shr 1]
|
||||
var isInit = false
|
||||
for i in 0 ..< nafLen:
|
||||
if isInit:
|
||||
P.double()
|
||||
P.accumNAF(tab, naf, nafLen, i)
|
||||
else:
|
||||
isInit = P.initNAF(tab, naf, nafLen, i)
|
||||
|
||||
func scalarMulEndo_minHammingWeight_windowed_vartime*[scalBits: static int; EC](
|
||||
P: var EC,
|
||||
scalar: BigInt[scalBits],
|
||||
window: static int) {.tags:[VarTime, Alloca].} =
|
||||
## Endomorphism-accelerated windowed vartime scalar multiplication
|
||||
##
|
||||
## P <- [k] P
|
||||
##
|
||||
## This uses windowed-NAF (wNAF)
|
||||
## This MUST NOT be used with secret data.
|
||||
##
|
||||
## This is highly VULNERABLE to timing attacks and power analysis attacks
|
||||
|
||||
# Signed digits divides precomputation table size by 2
|
||||
# Odd-only divides precomputation table size by another 2
|
||||
const precompSize = 1 shl (window - 2)
|
||||
static: doAssert window < 8, "Window is too large and precomputation would use " & $(precompSize * sizeof(EC)) & " stack space."
|
||||
|
||||
when P.F is Fp:
|
||||
const M = 2
|
||||
# 1. Compute endomorphisms
|
||||
var endomorphisms {.noInit.}: array[M-1, EC]
|
||||
when P.G == G1:
|
||||
endomorphisms[0] = P
|
||||
endomorphisms[0].x *= EC.F.C.getCubicRootOfUnity_mod_p()
|
||||
else:
|
||||
endomorphisms[0].frobenius_psi(P, 2)
|
||||
|
||||
elif P.F is Fp2:
|
||||
const M = 4
|
||||
# 1. Compute endomorphisms
|
||||
var endomorphisms {.noInit.}: array[M-1, EC]
|
||||
endomorphisms[0].frobenius_psi(P)
|
||||
endomorphisms[1].frobenius_psi(P, 2)
|
||||
endomorphisms[2].frobenius_psi(P, 3)
|
||||
else:
|
||||
{.error: "Unconfigured".}
|
||||
|
||||
# 2. Decompose scalar into mini-scalars
|
||||
const L = scalBits.ceilDiv_vartime(M) + 1
|
||||
var miniScalars {.noInit.}: array[M, BigInt[L]]
|
||||
var negatePoints {.noInit.}: array[M, SecretBool]
|
||||
miniScalars.decomposeEndo(negatePoints, scalar, EC.F)
|
||||
|
||||
# 3. Handle negative mini-scalars
|
||||
if negatePoints[0].bool:
|
||||
P.neg()
|
||||
for m in 1 ..< M:
|
||||
if negatePoints[m].bool:
|
||||
endomorphisms[m-1].neg()
|
||||
|
||||
# 4. EC precomputed table
|
||||
var tabEC {.noinit.}: array[M, array[precompSize, EC]]
|
||||
for m in 0 ..< M:
|
||||
var P2{.noInit.}: EC
|
||||
if m == 0:
|
||||
tabEC[0][0] = P
|
||||
P2.double(P)
|
||||
else:
|
||||
tabEC[m][0] = endomorphisms[m-1]
|
||||
P2.double(endomorphisms[m-1])
|
||||
for i in 1 ..< tabEC[m].len:
|
||||
tabEC[m][i].sum_vartime(tabEC[m][i-1], P2)
|
||||
|
||||
var tab {.noinit.}: array[M, array[precompSize, affine(EC)]]
|
||||
tab.batchAffine(tabEC)
|
||||
|
||||
# 5. wNAF precomputed tables
|
||||
const NafLen = L+1
|
||||
var tabNaf {.noinit.}: array[M, array[NafLen, int8]]
|
||||
|
||||
for m in 0 ..< M:
|
||||
# tabNaf returns NAF from least-significant to most significant bits
|
||||
let miniScalarLen = tabNaf[m].recode_r2l_signed_window_vartime(miniScalars[m], window)
|
||||
# We compute from most significant to least significant
|
||||
# so we pad with 0
|
||||
for i in miniScalarLen ..< NafLen:
|
||||
tabNaf[m][i] = 0
|
||||
|
||||
# 6. Compute
|
||||
var isInit = false
|
||||
|
||||
for i in 0 ..< NafLen:
|
||||
if isInit:
|
||||
P.double()
|
||||
for m in 0 ..< M:
|
||||
if isInit:
|
||||
P.accumNAF(tab[m], tabNaf[m], NafLen, i)
|
||||
else:
|
||||
isInit = P.initNAF(tab[m], tabNaf[m], NafLen, i)
|
||||
|
||||
func scalarMul_vartime*[scalBits; EC](
|
||||
P: var EC,
|
||||
scalar: BigInt[scalBits]
|
||||
) {.inline.} =
|
||||
## Elliptic Curve Scalar Multiplication
|
||||
##
|
||||
## P <- [k] P
|
||||
##
|
||||
## 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
|
||||
##
|
||||
## This may use endomorphism acceleration.
|
||||
## As endomorphism acceleration requires:
|
||||
## - Cofactor to be cleared
|
||||
## - 0 <= scalar < curve order
|
||||
## Those conditions will be assumed.
|
||||
|
||||
when P.F is Fp:
|
||||
const M = 2
|
||||
elif P.F is Fp2:
|
||||
const M = 4
|
||||
else:
|
||||
{.error: "Unconfigured".}
|
||||
|
||||
const L = scalBits.ceilDiv_vartime(M) + 1
|
||||
|
||||
let usedBits = scalar.limbs.getBits_vartime()
|
||||
|
||||
when scalBits == EC.F.C.getCurveOrderBitwidth() and
|
||||
EC.F.C.hasEndomorphismAcceleration():
|
||||
if usedBits >= L:
|
||||
when EC.F is Fp:
|
||||
P.scalarMulEndo_minHammingWeight_windowed_vartime(scalar, window = 4)
|
||||
elif EC.F is Fp2:
|
||||
P.scalarMulEndo_minHammingWeight_windowed_vartime(scalar, window = 3)
|
||||
else: # Curves defined on Fp^m with m > 2
|
||||
{.error: "Unreachable".}
|
||||
return
|
||||
|
||||
if 64 < usedBits:
|
||||
# With a window of 5, we precompute 2^3 = 8 points
|
||||
P.scalarMul_minHammingWeight_windowed_vartime(scalar, window = 5)
|
||||
elif 16 < usedBits:
|
||||
# With a window of 3, we precompute 2^1 = 2 points
|
||||
P.scalarMul_minHammingWeight_windowed_vartime(scalar, window = 3)
|
||||
elif 4 < usedBits:
|
||||
P.scalarMul_doubleAdd_vartime(scalar)
|
||||
else:
|
||||
P.scalarMul_addchain_4bit_vartime(scalar)
|
|
@ -151,6 +151,16 @@ func batchAffine*[N: static int, F, G](
|
|||
jacs: array[N, ECP_ShortW_Jac[F, G]]) {.inline.} =
|
||||
batchAffine(affs.asUnchecked(), jacs.asUnchecked(), N)
|
||||
|
||||
func batchAffine*[M, N: static int, F, G](
|
||||
affs: var array[M, array[N, ECP_ShortW_Aff[F, G]]],
|
||||
projs: array[M, array[N, ECP_ShortW_Prj[F, G]]]) {.inline.} =
|
||||
batchAffine(affs[0].asUnchecked(), projs[0].asUnchecked(), M*N)
|
||||
|
||||
func batchAffine*[M, N: static int, F, G](
|
||||
affs: var array[M, array[N, ECP_ShortW_Aff[F, G]]],
|
||||
projs: array[M, array[N, ECP_ShortW_Jac[F, G]]]) {.inline.} =
|
||||
batchAffine(affs[0].asUnchecked(), projs[0].asUnchecked(), M*N)
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Elliptic Curve in Short Weierstrass form
|
||||
|
|
|
@ -66,7 +66,7 @@ proc sum_reduce_vartime_parallelChunks[F; G: static Subgroup](
|
|||
if chunkDesc.numChunks < minChunkSizeSerial:
|
||||
r.setInf()
|
||||
for i in 0 ..< chunkDesc.numChunks:
|
||||
r += partialResults[i]
|
||||
r.sum_vartime(r, partialResults[i])
|
||||
else:
|
||||
let partialResultsAffine = allocStackArray(ECP_ShortW_Aff[F, G], chunkDesc.numChunks)
|
||||
partialResultsAffine.batchAffine(partialResults, chunkDesc.numChunks)
|
||||
|
@ -99,7 +99,7 @@ proc sum_reduce_vartime_parallelFor[F; G: static Subgroup](
|
|||
let n = min(maxStride, pointsLen-i)
|
||||
localSum.accumSum_chunk_vartime(p +% i, n)
|
||||
merge(remoteSum: Flowvar[typeof(r)]):
|
||||
localSum += sync(remoteSum)
|
||||
localSum.sum_vartime(localSum, sync(remoteSum))
|
||||
epilogue:
|
||||
return localSum
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ Let's look first at Cohen et al, 1998 formulae
|
|||
| S₁ = Y₁*Z₂*Z₂Z₂ | | | |
|
||||
| S₂ = Y₂*Z₁*Z₁Z₁ | | | |
|
||||
| H = U₂-U₁ # P=-Q, P=Inf, P=Q | | | |
|
||||
| F = S₂-S₁ # Q=Inf | | | |
|
||||
| R = S₂-S₁ # Q=Inf | | | |
|
||||
| | | | |
|
||||
| HH = H² | YY = Y₁² | | |
|
||||
| HHH = H*HH | M = 3*X₁²+a*ZZ² | 3(X₁-Z₁)(X₁+Z₁) | 3*X₁² |
|
||||
|
|
|
@ -184,7 +184,7 @@ template sumImpl[F; G: static Subgroup](
|
|||
## to simple side-channel attacks (SCA)
|
||||
## This is done by using a "complete" or "exception-free" addition law.
|
||||
#
|
||||
# Implementation, see write-up at the bottom.
|
||||
# Implementation, see write-up in the accompanying Markdown file.
|
||||
# We fuse addition and doubling with condition copy by swapping
|
||||
# terms with the following table
|
||||
#
|
||||
|
@ -403,7 +403,7 @@ func madd*[F; G: static Subgroup](
|
|||
## to simple side-channel attacks (SCA)
|
||||
## This is done by using a "complete" or "exception-free" addition law.
|
||||
#
|
||||
# Implementation, see write-up at the bottom.
|
||||
# Implementation, see write-up in the accompanying markdown file.
|
||||
# We fuse addition and doubling with condition copy by swapping
|
||||
# terms with the following table
|
||||
#
|
||||
|
@ -674,3 +674,247 @@ func fromAffine*[F; G](
|
|||
jac.y = aff.y
|
||||
jac.z.setOne()
|
||||
jac.z.csetZero(aff.isInf())
|
||||
|
||||
# Variable-time
|
||||
# -------------
|
||||
|
||||
# In some primitives like FFTs, the extra work done for constant-time
|
||||
# is amplified by O(n log n) which may result in extra tens of minutes
|
||||
# to hours of computations. Those primitives do not need constant-timeness.
|
||||
|
||||
func sum_vartime*[F; G: static Subgroup](
|
||||
r: var ECP_ShortW_Jac[F, G],
|
||||
p, q: ECP_ShortW_Jac[F, G])
|
||||
{.tags:[VarTime], meter.} =
|
||||
## **Variable-time** Jacobian addition
|
||||
##
|
||||
## This MUST NOT be used with secret data.
|
||||
##
|
||||
## This is highly VULNERABLE to timing attacks and power analysis attacks.
|
||||
|
||||
if p.isInf().bool:
|
||||
r = q
|
||||
return
|
||||
if q.isInf().bool:
|
||||
r = p
|
||||
return
|
||||
|
||||
# Accelerate mixed additions
|
||||
let isPz1 = p.z.isOne().bool
|
||||
let isQz1 = q.z.isOne().bool
|
||||
|
||||
# Addition, Cohen et al, 1998
|
||||
# General case: 12M + 4S + 6add + 1*2
|
||||
#
|
||||
# Mixed-addition: 8M + 3S + 6add + 1*2
|
||||
# Affine+Affine->Jacobian: 4M + 2S + 6add + 1*2
|
||||
|
||||
# | Addition, Cohen et al, 1998 |
|
||||
# | 12M + 4S + 6add + 1*2 |
|
||||
# | ----------------------------- |
|
||||
# | Z₁Z₁ = Z₁² |
|
||||
# | Z₂Z₂ = Z₂² |
|
||||
# | |
|
||||
# | U₁ = X₁*Z₂Z₂ |
|
||||
# | U₂ = X₂*Z₁Z₁ |
|
||||
# | S₁ = Y₁*Z₂*Z₂Z₂ |
|
||||
# | S₂ = Y₂*Z₁*Z₁Z₁ |
|
||||
# | H = U₂-U₁ # P=-Q, P=Inf, P=Q |
|
||||
# | R = S₂-S₁ # Q=Inf |
|
||||
# | |
|
||||
# | HH = H² |
|
||||
# | V = U₁*HH |
|
||||
# | HHH = H*HH |
|
||||
# | |
|
||||
# | X₃ = R²-HHH-2*V |
|
||||
# | Y₃ = R*(V-X₃)-S₁*HHH |
|
||||
# | Z₃ = Z₁*Z₂*H |
|
||||
|
||||
var U {.noInit.}, S{.noInit.}, H{.noInit.}, R{.noInit.}: F
|
||||
|
||||
if not isPz1: # case Z₁ != 1
|
||||
R.square(p.z, skipFinalSub = true) # Z₁Z₁ = Z₁²
|
||||
if isQz1: # case Z₂ = 1
|
||||
U = p.x # U₁ = X₁*Z₂Z₂
|
||||
if isPz1: # case Z₁ = Z₂ = 1
|
||||
H = q.x
|
||||
else:
|
||||
H.prod(q.x, R)
|
||||
H -= U # H = U₂-U₁
|
||||
S = p.y # S₁ = Y₁*Z₂*Z₂Z₂
|
||||
else: # case Z₂ != 1
|
||||
S.square(q.z, skipFinalSub = true)
|
||||
U.prod(p.x, S) # U₁ = X₁*Z₂Z₂
|
||||
if isPz1:
|
||||
H = q.x
|
||||
else:
|
||||
H.prod(q.x, R)
|
||||
H -= U # H = U₂-U₁
|
||||
S.prod(S, q.z, skipFinalSub = true)
|
||||
S *= p.y # S₁ = Y₁*Z₂*Z₂Z₂
|
||||
if isPz1:
|
||||
R = q.y
|
||||
else:
|
||||
R.prod(R, p.z, skipFinalSub = true)
|
||||
R *= q.y # S₂ = Y₂*Z₁*Z₁Z₁
|
||||
R -= S # R = S₂-S₁
|
||||
|
||||
if H.isZero().bool: # Same x coordinate
|
||||
if R.isZero().bool: # case P = Q
|
||||
r.double(p)
|
||||
return
|
||||
else: # case P = -Q
|
||||
r.setInf()
|
||||
return
|
||||
|
||||
var HHH{.noInit.}: F
|
||||
template V: untyped = U
|
||||
|
||||
HHH.square(H, skipFinalSub = true)
|
||||
V *= HHH # V = U₁*HH
|
||||
HHH *= H # HHH = H*HH
|
||||
|
||||
# X₃ = R²-HHH-2*V, we use the y coordinate as temporary (should we? cache misses?)
|
||||
r.y.square(R)
|
||||
r.y -= V
|
||||
r.y -= V
|
||||
r.x.diff(r.y, HHH)
|
||||
|
||||
# Y₃ = R*(V-X₃)-S₁*HHH
|
||||
V -= r.x
|
||||
V *= R
|
||||
HHH *= S
|
||||
r.y.diff(V, HHH)
|
||||
|
||||
# Z₃ = Z₁*Z₂*H
|
||||
if isPz1:
|
||||
if isQz1:
|
||||
r.z = H
|
||||
else:
|
||||
r.z.prod(H, q.z)
|
||||
else:
|
||||
if isQz1:
|
||||
r.z.prod(H, p.z)
|
||||
else:
|
||||
r.z.prod(p.z, q.z, skipFinalSub = true)
|
||||
r.z *= H
|
||||
|
||||
func madd_vartime*[F; G: static Subgroup](
|
||||
r: var ECP_ShortW_Jac[F, G],
|
||||
p: ECP_ShortW_Jac[F, G],
|
||||
q: ECP_ShortW_Aff[F, G])
|
||||
{.tags:[VarTime], meter.} =
|
||||
## **Variable-time** Jacobian mixed addition
|
||||
##
|
||||
## This MUST NOT be used with secret data.
|
||||
##
|
||||
## This is highly VULNERABLE to timing attacks and power analysis attacks.
|
||||
|
||||
if p.isInf().bool:
|
||||
r.fromAffine(q)
|
||||
return
|
||||
if q.isInf().bool:
|
||||
r = p
|
||||
return
|
||||
|
||||
# Accelerate mixed additions
|
||||
let isPz1 = p.z.isOne().bool
|
||||
|
||||
# Addition, Cohen et al, 1998
|
||||
#
|
||||
# Mixed-addition: 8M + 3S + 6add + 1*2
|
||||
# Affine+Affine->Jacobian: 4M + 2S + 6add + 1*2
|
||||
|
||||
# | Addition, Cohen et al, 1998 |
|
||||
# | 12M + 4S + 6add + 1*2 |
|
||||
# | ----------------------------- |
|
||||
# | Z₁Z₁ = Z₁² |
|
||||
# | Z₂Z₂ = Z₂² |
|
||||
# | |
|
||||
# | U₁ = X₁*Z₂Z₂ |
|
||||
# | U₂ = X₂*Z₁Z₁ |
|
||||
# | S₁ = Y₁*Z₂*Z₂Z₂ |
|
||||
# | S₂ = Y₂*Z₁*Z₁Z₁ |
|
||||
# | H = U₂-U₁ # P=-Q, P=Inf, P=Q |
|
||||
# | R = S₂-S₁ # Q=Inf |
|
||||
# | |
|
||||
# | HH = H² |
|
||||
# | V = U₁*HH |
|
||||
# | HHH = H*HH |
|
||||
# | |
|
||||
# | X₃ = R²-HHH-2*V |
|
||||
# | Y₃ = R*(V-X₃)-S₁*HHH |
|
||||
# | Z₃ = Z₁*Z₂*H |
|
||||
|
||||
var U {.noInit.}, S{.noInit.}, H{.noInit.}, R{.noInit.}: F
|
||||
|
||||
if not isPz1: # case Z₁ != 1
|
||||
R.square(p.z, skipFinalSub = true) # Z₁Z₁ = Z₁²
|
||||
|
||||
U = p.x # U₁ = X₁*Z₂Z₂
|
||||
if isPz1: # case Z₁ = Z₂ = 1
|
||||
H = q.x
|
||||
else:
|
||||
H.prod(q.x, R)
|
||||
H -= U # H = U₂-U₁
|
||||
S = p.y # S₁ = Y₁*Z₂*Z₂Z₂
|
||||
|
||||
if isPz1:
|
||||
R = q.y
|
||||
else:
|
||||
R.prod(R, p.z, skipFinalSub = true)
|
||||
R *= q.y # S₂ = Y₂*Z₁*Z₁Z₁
|
||||
R -= S # R = S₂-S₁
|
||||
|
||||
if H.isZero().bool: # Same x coordinate
|
||||
if R.isZero().bool: # case P = Q
|
||||
r.double(p)
|
||||
return
|
||||
else: # case P = -Q
|
||||
r.setInf()
|
||||
return
|
||||
|
||||
var HHH{.noInit.}: F
|
||||
template V: untyped = U
|
||||
|
||||
HHH.square(H, skipFinalSub = true)
|
||||
V *= HHH # V = U₁*HH
|
||||
HHH *= H # HHH = H*HH
|
||||
|
||||
# X₃ = R²-HHH-2*V, we use the y coordinate as temporary (should we? cache misses?)
|
||||
r.y.square(R)
|
||||
r.y -= V
|
||||
r.y -= V
|
||||
r.x.diff(r.y, HHH)
|
||||
|
||||
# Y₃ = R*(V-X₃)-S₁*HHH
|
||||
V -= r.x
|
||||
V *= R
|
||||
HHH *= S
|
||||
r.y.diff(V, HHH)
|
||||
|
||||
# Z₃ = Z₁*Z₂*H
|
||||
if isPz1:
|
||||
r.z = H
|
||||
else:
|
||||
r.z.prod(H, p.z)
|
||||
|
||||
func diff_vartime*(r: var ECP_ShortW_Jac, P, Q: ECP_ShortW_Jac) {.inline.} =
|
||||
## r = P - Q
|
||||
##
|
||||
## This MUST NOT be used with secret data.
|
||||
##
|
||||
## This is highly VULNERABLE to timing attacks and power analysis attacks.
|
||||
var nQ {.noInit.}: typeof(Q)
|
||||
nQ.neg(Q)
|
||||
r.sum_vartime(P, nQ)
|
||||
|
||||
func msub_vartime*(r: var ECP_ShortW_Jac, P: ECP_ShortW_Jac, Q: ECP_ShortW_Aff) {.inline.} =
|
||||
## r = P - Q
|
||||
##
|
||||
## This MUST NOT be used with secret data.
|
||||
##
|
||||
## This is highly VULNERABLE to timing attacks and power analysis attacks.
|
||||
var nQ {.noInit.}: typeof(Q)
|
||||
nQ.neg(Q)
|
||||
r.madd_vartime(P, nQ)
|
|
@ -200,11 +200,15 @@ func sum_vartime*[F; G: static Subgroup](
|
|||
r.setInf()
|
||||
return
|
||||
|
||||
var PP{.noInit.}, PPP{.noInit.}, Q{.noInit.}: F
|
||||
var PPP{.noInit.}, Q{.noInit.}: F
|
||||
|
||||
PP.square(P)
|
||||
PPP.prod(PP, P)
|
||||
Q.prod(U, PP)
|
||||
PPP.square(P)
|
||||
|
||||
Q.prod(U, PPP)
|
||||
r.zz.prod(p.zz, q.zz)
|
||||
r.zz *= PPP
|
||||
|
||||
PPP *= P
|
||||
|
||||
r.x.square(R)
|
||||
P.double(Q)
|
||||
|
@ -216,8 +220,6 @@ func sum_vartime*[F; G: static Subgroup](
|
|||
R *= Q
|
||||
r.y.diff(R, r.y)
|
||||
|
||||
r.zz.prod(p.zz, q.zz)
|
||||
r.zz *= PP
|
||||
r.zzz.prod(p.zzz, q.zzz)
|
||||
r.zzz *= PPP
|
||||
|
||||
|
|
|
@ -464,3 +464,220 @@ func fromAffine*[F, G](
|
|||
proj.x.csetZero(inf)
|
||||
proj.y.csetOne(inf)
|
||||
proj.z.csetZero(inf)
|
||||
|
||||
# Variable-time
|
||||
# -------------
|
||||
|
||||
# In some primitives like FFTs, the extra work done for constant-time
|
||||
# is amplified by O(n log n) which may result in extra tens of minutes
|
||||
# to hours of computations. Those primitives do not need constant-timeness.
|
||||
|
||||
func sum_vartime*[F; G: static Subgroup](
|
||||
r: var ECP_ShortW_Prj[F, G],
|
||||
p, q: ECP_ShortW_Prj[F, G])
|
||||
{.tags:[VarTime], meter.} =
|
||||
## **Variable-time** homogeneous projective addition
|
||||
##
|
||||
## This MUST NOT be used with secret data.
|
||||
##
|
||||
## This is highly VULNERABLE to timing attacks and power analysis attacks.
|
||||
|
||||
if p.isInf().bool:
|
||||
r = q
|
||||
return
|
||||
if q.isInf().bool:
|
||||
r = p
|
||||
return
|
||||
|
||||
# Accelerate mixed additions
|
||||
let isPz1 = p.z.isOne().bool
|
||||
let isQz1 = q.z.isOne().bool
|
||||
|
||||
# Addition, Cohen et al, 1998
|
||||
# General case: 12M + 4S + 6add + 1*2
|
||||
# https://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html#addition-add-1998-cmo-2
|
||||
#
|
||||
# Y₁Z₂ = Y₁*Z₂
|
||||
# X₁Z₂ = X₁*Z₂
|
||||
# Z₁Z₂ = Z₁*Z₂
|
||||
# u = Y₂*Z₁-Y₁Z₂
|
||||
# uu = u²
|
||||
# v = X₂*Z₁-X₁Z₂
|
||||
# vv = v²
|
||||
# vvv = v*vv
|
||||
# R = vv*X₁Z₂
|
||||
# A = uu*Z₁Z₂-vvv-2*R
|
||||
# X₃ = v*A
|
||||
# Y₃ = u*(R-A)-vvv*Y₁Z₂
|
||||
# Z₃ = vvv*Z₁Z₂
|
||||
|
||||
var Y1Z2 {.noInit.}, R {.noInit.}: F
|
||||
var U {.noInit.}, V {.noInit.}: F
|
||||
|
||||
if isQz1:
|
||||
R = p.x
|
||||
Y1Z2 = p.y
|
||||
else:
|
||||
R.prod(p.x, q.z) # X₁Z₂
|
||||
Y1Z2.prod(p.y, q.z)
|
||||
if isPz1:
|
||||
U = q.y
|
||||
V = q.x
|
||||
else:
|
||||
U.prod(q.y, p.z)
|
||||
V.prod(q.x, p.z)
|
||||
V -= R
|
||||
|
||||
if V.isZero().bool: # Same x coordinate
|
||||
if bool(U == Y1Z2): # case P = Q
|
||||
r.double(p)
|
||||
return
|
||||
else:
|
||||
r.setInf() # case P = -Q
|
||||
return
|
||||
|
||||
var VVV{.noInit.}: F
|
||||
|
||||
VVV.square(V, skipFinalSub = true)
|
||||
R *= VVV
|
||||
VVV *= V
|
||||
|
||||
r.y.diff(U, Y1Z2) # u = Y₂*Z₁-Y₁Z₂
|
||||
U.square(r.y) # uu = u²
|
||||
|
||||
# A and Z₃ depend on Z₁Z₂
|
||||
template A:untyped = U
|
||||
if isQz1:
|
||||
if isPz1:
|
||||
r.z = VVV
|
||||
else:
|
||||
A.prod(U, p.z)
|
||||
r.z.prod(VVV, p.z)
|
||||
else:
|
||||
if isPz1:
|
||||
A.prod(U, q.z)
|
||||
r.z.prod(VVV, q.z)
|
||||
else:
|
||||
r.z.prod(p.z, q.z, skipFinalSub = true)
|
||||
A.prod(U, r.z)
|
||||
r.z *= VVV
|
||||
|
||||
A -= VVV
|
||||
A -= R
|
||||
A -= R # A = uu*Z₁Z₂-vvv-2*R
|
||||
|
||||
r.x.prod(V, A)
|
||||
|
||||
R -= A
|
||||
Y1Z2 *= VVV
|
||||
r.y *= R
|
||||
r.y -= Y1Z2
|
||||
|
||||
func madd_vartime*[F; G: static Subgroup](
|
||||
r: var ECP_ShortW_Prj[F, G],
|
||||
p: ECP_ShortW_Prj[F, G],
|
||||
q: ECP_ShortW_Aff[F, G])
|
||||
{.tags:[VarTime], meter.} =
|
||||
## **Variable-time** homogeneous projective mixed addition
|
||||
##
|
||||
## This MUST NOT be used with secret data.
|
||||
##
|
||||
## This is highly VULNERABLE to timing attacks and power analysis attacks.
|
||||
|
||||
if p.isInf().bool:
|
||||
r.fromAffine(q)
|
||||
return
|
||||
if q.isInf().bool:
|
||||
r = p
|
||||
return
|
||||
|
||||
# Accelerate mixed additions
|
||||
let isPz1 = p.z.isOne().bool
|
||||
|
||||
# Addition, Cohen et al, 1998
|
||||
# General case: 12M + 4S + 6add + 1*2
|
||||
# https://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html#addition-add-1998-cmo-2
|
||||
#
|
||||
# Y₁Z₂ = Y₁*Z₂
|
||||
# X₁Z₂ = X₁*Z₂
|
||||
# Z₁Z₂ = Z₁*Z₂
|
||||
# u = Y₂*Z₁-Y₁Z₂
|
||||
# uu = u²
|
||||
# v = X₂*Z₁-X₁Z₂
|
||||
# vv = v²
|
||||
# vvv = v*vv
|
||||
# R = vv*X₁Z₂
|
||||
# A = uu*Z₁Z₂-vvv-2*R
|
||||
# X₃ = v*A
|
||||
# Y₃ = u*(R-A)-vvv*Y₁Z₂
|
||||
# Z₃ = vvv*Z₁Z₂
|
||||
|
||||
var Y1Z2 {.noInit.}, R {.noInit.}: F
|
||||
var U {.noInit.}, V {.noInit.}: F
|
||||
|
||||
R = p.x
|
||||
Y1Z2 = p.y
|
||||
|
||||
if isPz1:
|
||||
U = q.y
|
||||
V = q.x
|
||||
else:
|
||||
U.prod(q.y, p.z)
|
||||
V.prod(q.x, p.z)
|
||||
V -= R
|
||||
|
||||
if V.isZero().bool: # Same x coordinate
|
||||
if bool(U == Y1Z2): # case P = Q
|
||||
r.double(p)
|
||||
return
|
||||
else:
|
||||
r.setInf() # case P = -Q
|
||||
return
|
||||
|
||||
var VVV{.noInit.}: F
|
||||
|
||||
VVV.square(V, skipFinalSub = true)
|
||||
R *= VVV
|
||||
VVV *= V
|
||||
|
||||
r.y.diff(U, Y1Z2) # u = Y₂*Z₁-Y₁Z₂
|
||||
U.square(r.y) # uu = u²
|
||||
|
||||
# A and Z₃ depend on Z₁Z₂
|
||||
template A:untyped = U
|
||||
if isPz1:
|
||||
r.z = VVV
|
||||
else:
|
||||
A.prod(U, p.z)
|
||||
r.z.prod(VVV, p.z)
|
||||
|
||||
A -= VVV
|
||||
A -= R
|
||||
A -= R # A = uu*Z₁Z₂-vvv-2*R
|
||||
|
||||
r.x.prod(V, A)
|
||||
|
||||
R -= A
|
||||
Y1Z2 *= VVV
|
||||
r.y *= R
|
||||
r.y -= Y1Z2
|
||||
|
||||
func diff_vartime*(r: var ECP_ShortW_Prj, P, Q: ECP_ShortW_Prj) {.inline.} =
|
||||
## r = P - Q
|
||||
##
|
||||
## This MUST NOT be used with secret data.
|
||||
##
|
||||
## This is highly VULNERABLE to timing attacks and power analysis attacks.
|
||||
var nQ {.noInit.}: typeof(Q)
|
||||
nQ.neg(Q)
|
||||
r.sum_vartime(P, nQ)
|
||||
|
||||
func msub_vartime*(r: var ECP_ShortW_Prj, P: ECP_ShortW_Prj, Q: ECP_ShortW_Aff) {.inline.} =
|
||||
## r = P - Q
|
||||
##
|
||||
## This MUST NOT be used with secret data.
|
||||
##
|
||||
## This is highly VULNERABLE to timing attacks and power analysis attacks.
|
||||
var nQ {.noInit.}: typeof(Q)
|
||||
nQ.neg(Q)
|
||||
r.madd_vartime(P, nQ)
|
|
@ -64,18 +64,18 @@ func simpleFT[EC; bits: static int](
|
|||
# FFT is a recursive algorithm
|
||||
# This is the base-case using a O(n²) algorithm
|
||||
|
||||
# TODO: endomorphism acceleration for windowed-NAF
|
||||
|
||||
let L = output.len
|
||||
var last {.noInit.}, v {.noInit.}: EC
|
||||
|
||||
var v0w0 {.noInit.} = vals[0]
|
||||
v0w0.scalarMul_vartime(rootsOfUnity[0])
|
||||
|
||||
for i in 0 ..< L:
|
||||
last = vals[0]
|
||||
last.scalarMul_minHammingWeight_windowed_vartime(rootsOfUnity[0], window = 5)
|
||||
last = v0w0
|
||||
for j in 1 ..< L:
|
||||
v = vals[j]
|
||||
v.scalarMul_minHammingWeight_windowed_vartime(rootsOfUnity[(i*j) mod L], window = 5)
|
||||
last += v
|
||||
v.scalarMul_vartime(rootsOfUnity[(i*j) mod L])
|
||||
last.sum_vartime(last, v)
|
||||
output[i] = last
|
||||
|
||||
func fft_internal[EC; bits: static int](
|
||||
|
@ -100,11 +100,11 @@ func fft_internal[EC; bits: static int](
|
|||
for i in 0 ..< half:
|
||||
# FFT Butterfly
|
||||
y_times_root = output[i+half]
|
||||
y_times_root .scalarMul_minHammingWeight_windowed_vartime(rootsOfUnity[i], window = 5)
|
||||
output[i+half] .diff(output[i], y_times_root)
|
||||
output[i] += y_times_root
|
||||
y_times_root .scalarMul_vartime(rootsOfUnity[i])
|
||||
output[i+half] .diff_vartime(output[i], y_times_root)
|
||||
output[i] .sum_vartime(output[i], y_times_root)
|
||||
|
||||
func fft*[EC](
|
||||
func fft_vartime*[EC](
|
||||
desc: ECFFT_Descriptor[EC],
|
||||
output: var openarray[EC],
|
||||
vals: openarray[EC]): FFT_Status =
|
||||
|
@ -121,7 +121,7 @@ func fft*[EC](
|
|||
fft_internal(voutput, vals.toStridedView(), rootz)
|
||||
return FFTS_Success
|
||||
|
||||
func ifft*[EC](
|
||||
func ifft_vartime*[EC](
|
||||
desc: ECFFT_Descriptor[EC],
|
||||
output: var openarray[EC],
|
||||
vals: openarray[EC]): FFT_Status =
|
||||
|
@ -144,7 +144,7 @@ func ifft*[EC](
|
|||
invLen.invmod_vartime(invLen, EC.F.C.getCurveOrder())
|
||||
|
||||
for i in 0 ..< output.len:
|
||||
output[i].scalarMul_minHammingWeight_windowed_vartime(invLen, window = 5)
|
||||
output[i].scalarMul_vartime(invLen)
|
||||
|
||||
return FFTS_Success
|
||||
|
||||
|
@ -360,12 +360,12 @@ when isMainModule:
|
|||
data[i].madd(data[i-1], BLS12_381.getGenerator("G1"))
|
||||
|
||||
var coefs = newSeq[EC_G1](data.len)
|
||||
let fftOk = fft(fftDesc, coefs, data)
|
||||
let fftOk = fft_vartime(fftDesc, coefs, data)
|
||||
doAssert fftOk == FFTS_Success
|
||||
# display("coefs", 0, coefs)
|
||||
|
||||
var res = newSeq[EC_G1](data.len)
|
||||
let ifftOk = ifft(fftDesc, res, coefs)
|
||||
let ifftOk = ifft_vartime(fftDesc, res, coefs)
|
||||
doAssert ifftOk == FFTS_Success
|
||||
# display("res", 0, res)
|
||||
|
||||
|
@ -415,7 +415,7 @@ when isMainModule:
|
|||
# Bench
|
||||
let start = getMonotime()
|
||||
for i in 0 ..< NumIters:
|
||||
let status = fftDesc.fft(coefsOut, data)
|
||||
let status = fftDesc.fft_vartime(coefsOut, data)
|
||||
doAssert status == FFTS_Success
|
||||
let stop = getMonotime()
|
||||
|
||||
|
|
|
@ -419,8 +419,8 @@ func update*[Pubkey, Sig: ECP_ShortW_Aff](
|
|||
|
||||
var randFactor{.noInit.}: BigInt[64]
|
||||
randFactor.unmarshal(ctx.secureBlinding.toOpenArray(0, 7), bigEndian)
|
||||
pkG1_jac.scalarMul_minHammingWeight_windowed_vartime(randFactor, window = 3)
|
||||
sigG2_jac.scalarMul_minHammingWeight_windowed_vartime(randFactor, window = 3)
|
||||
pkG1_jac.scalarMul_vartime(randFactor)
|
||||
sigG2_jac.scalarMul_vartime(randFactor)
|
||||
|
||||
if ctx.aggSigOnce == false:
|
||||
ctx.aggSig = sigG2_jac
|
||||
|
@ -455,8 +455,8 @@ func update*[Pubkey, Sig: ECP_ShortW_Aff](
|
|||
|
||||
var randFactor{.noInit.}: BigInt[64]
|
||||
randFactor.unmarshal(ctx.secureBlinding.toOpenArray(0, 7), bigEndian)
|
||||
hmsgG1_jac.scalarMul_minHammingWeight_windowed_vartime(randFactor, window = 3)
|
||||
sigG1_jac.scalarMul_minHammingWeight_windowed_vartime(randFactor, window = 3)
|
||||
hmsgG1_jac.scalarMul_vartime(randFactor)
|
||||
sigG1_jac.scalarMul_vartime(randFactor)
|
||||
|
||||
if ctx.aggSigOnce == false:
|
||||
ctx.aggSig = sigG1_jac
|
||||
|
|
|
@ -11,6 +11,7 @@ import
|
|||
../../constantine/math/config/curves,
|
||||
../../constantine/math/arithmetic,
|
||||
../../constantine/math/ec_shortweierstrass,
|
||||
../../constantine/math/elliptic/ec_scalar_mul_vartime,
|
||||
../../constantine/math/io/[io_fields, io_ec, io_bigints],
|
||||
# Research
|
||||
./strided_views,
|
||||
|
@ -95,28 +96,29 @@ func expandRootOfUnity[F](rootOfUnity: F): auto {.noInit.} =
|
|||
func simpleFT[EC; bits: static int](
|
||||
output: var View[EC],
|
||||
vals: View[EC],
|
||||
rootsOfUnity: View[BigInt[bits]]
|
||||
) =
|
||||
rootsOfUnity: View[BigInt[bits]]) =
|
||||
# FFT is a recursive algorithm
|
||||
# This is the base-case using a O(n²) algorithm
|
||||
|
||||
let L = output.len
|
||||
var last {.noInit.}, v {.noInit.}: EC
|
||||
|
||||
var v0w0 {.noInit.} = vals[0]
|
||||
v0w0.scalarMul_vartime(rootsOfUnity[0])
|
||||
|
||||
for i in 0 ..< L:
|
||||
last = vals[0]
|
||||
last.scalarMul(rootsOfUnity[0])
|
||||
last = v0w0
|
||||
for j in 1 ..< L:
|
||||
v = vals[j]
|
||||
v.scalarMul(rootsOfUnity[(i*j) mod L])
|
||||
last += v
|
||||
|
||||
v.scalarMul_vartime(rootsOfUnity[(i*j) mod L])
|
||||
last.sum_vartime(last, v)
|
||||
output[i] = last
|
||||
|
||||
func fft_internal[EC; bits: static int](
|
||||
output: var View[EC],
|
||||
vals: View[EC],
|
||||
rootsOfUnity: View[BigInt[bits]]
|
||||
) =
|
||||
rootsOfUnity: View[BigInt[bits]]) =
|
||||
if output.len <= 4:
|
||||
simpleFT(output, vals, rootsOfUnity)
|
||||
return
|
||||
|
@ -135,11 +137,11 @@ func fft_internal[EC; bits: static int](
|
|||
for i in 0 ..< half:
|
||||
# FFT Butterfly
|
||||
y_times_root = output[i+half]
|
||||
y_times_root .scalarMul(rootsOfUnity[i])
|
||||
output[i+half] .diff(output[i], y_times_root)
|
||||
output[i] += y_times_root
|
||||
y_times_root .scalarMul_vartime(rootsOfUnity[i])
|
||||
output[i+half] .diff_vartime(output[i], y_times_root)
|
||||
output[i] .sum_vartime(output[i], y_times_root)
|
||||
|
||||
func fft*[EC](
|
||||
func fft_vartime*[EC](
|
||||
desc: FFTDescriptor[EC],
|
||||
output: var openarray[EC],
|
||||
vals: openarray[EC]): FFT_Status =
|
||||
|
@ -156,7 +158,7 @@ func fft*[EC](
|
|||
fft_internal(voutput, vals.toView(), rootz)
|
||||
return FFTS_Success
|
||||
|
||||
func ifft*[EC](
|
||||
func ifft_vartime*[EC](
|
||||
desc: FFTDescriptor[EC],
|
||||
output: var openarray[EC],
|
||||
vals: openarray[EC]): FFT_Status =
|
||||
|
@ -179,8 +181,8 @@ func ifft*[EC](
|
|||
invLen.inv_vartime()
|
||||
let inv = invLen.toBig()
|
||||
|
||||
for i in 0..< output.len:
|
||||
output[i].scalarMul(inv)
|
||||
for i in 0 ..< output.len:
|
||||
output[i].scalarMul_vartime(inv)
|
||||
|
||||
return FFTS_Success
|
||||
|
||||
|
@ -221,12 +223,12 @@ when isMainModule:
|
|||
data[i].madd(data[i-1], Generator1)
|
||||
|
||||
var coefs = newSeq[EC_G1](data.len)
|
||||
let fftOk = fft(fftDesc, coefs, data)
|
||||
let fftOk = fft_vartime(fftDesc, coefs, data)
|
||||
doAssert fftOk == FFTS_Success
|
||||
# display("coefs", 0, coefs)
|
||||
|
||||
var res = newSeq[EC_G1](data.len)
|
||||
let ifftOk = ifft(fftDesc, res, coefs)
|
||||
let ifftOk = ifft_vartime(fftDesc, res, coefs)
|
||||
doAssert ifftOk == FFTS_Success
|
||||
# display("res", 0, res)
|
||||
|
||||
|
@ -262,7 +264,7 @@ when isMainModule:
|
|||
|
||||
warmup()
|
||||
|
||||
for scale in 4 ..< 10:
|
||||
for scale in 4 ..< 16:
|
||||
# Setup
|
||||
|
||||
let desc = FFTDescriptor[EC_G1].init(uint8 scale)
|
||||
|
@ -276,7 +278,7 @@ when isMainModule:
|
|||
# Bench
|
||||
let start = getMonotime()
|
||||
for i in 0 ..< NumIters:
|
||||
let status = desc.fft(coefsOut, data)
|
||||
let status = desc.fft_vartime(coefsOut, data)
|
||||
doAssert status == FFTS_Success
|
||||
let stop = getMonotime()
|
||||
|
||||
|
|
|
@ -172,7 +172,7 @@ proc run_scalar_mul_test_vs_sage*(
|
|||
doAssert: bool(Q == impl)
|
||||
doAssert: bool(Q == refMinWeight)
|
||||
|
||||
staticFor w, 2, 14:
|
||||
staticFor w, 2, 5:
|
||||
var refWNAF = P
|
||||
refWNAF.scalarMul_minHammingWeight_windowed_vartime(vec.vectors[i].scalar, window = w)
|
||||
check: bool(impl == refWNAF)
|
||||
|
@ -186,3 +186,8 @@ proc run_scalar_mul_test_vs_sage*(
|
|||
var endoW = P
|
||||
endoW.scalarMulGLV_m2w2(vec.vectors[i].scalar)
|
||||
doAssert: bool(Q == endoW)
|
||||
|
||||
staticFor w, 2, 5:
|
||||
var endoWNAF = P
|
||||
endoWNAF.scalarMulEndo_minHammingWeight_windowed_vartime(vec.vectors[i].scalar, window = w)
|
||||
check: bool(impl == endoWNAF)
|
|
@ -51,3 +51,39 @@ run_EC_addition_tests(
|
|||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_jacobian_g1_add_double_" & $Vesta
|
||||
)
|
||||
|
||||
run_EC_addition_vartime_tests(
|
||||
ec = ECP_ShortW_Jac[Fp[BN254_Snarks], G1],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_jacobian_g1_add_double_vartime_" & $BN254_Snarks
|
||||
)
|
||||
|
||||
run_EC_addition_vartime_tests(
|
||||
ec = ECP_ShortW_Jac[Fp[BLS12_381], G1],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_jacobian_g1_add_double_vartime_" & $BLS12_381
|
||||
)
|
||||
|
||||
run_EC_addition_vartime_tests(
|
||||
ec = ECP_ShortW_Jac[Fp[BLS12_377], G1],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_jacobian_g1_add_double_vartime_" & $BLS12_377
|
||||
)
|
||||
|
||||
run_EC_addition_vartime_tests(
|
||||
ec = ECP_ShortW_Jac[Fp[BW6_761], G1],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_jacobian_g1_add_double_vartime_" & $BW6_761
|
||||
)
|
||||
|
||||
run_EC_addition_vartime_tests(
|
||||
ec = ECP_ShortW_Jac[Fp[Pallas], G1],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_jacobian_g1_add_double_vartime_" & $Pallas
|
||||
)
|
||||
|
||||
run_EC_addition_vartime_tests(
|
||||
ec = ECP_ShortW_Jac[Fp[Vesta], G1],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_jacobian_g1_add_double_vartime_" & $Vesta
|
||||
)
|
|
@ -14,7 +14,7 @@ import
|
|||
./t_ec_template
|
||||
|
||||
const
|
||||
Iters = 8
|
||||
Iters = 6
|
||||
|
||||
run_EC_addition_tests(
|
||||
ec = ECP_ShortW_Prj[Fp[BN254_Snarks], G1],
|
||||
|
@ -51,3 +51,39 @@ run_EC_addition_tests(
|
|||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_projective_g1_add_double_" & $Vesta
|
||||
)
|
||||
|
||||
run_EC_addition_vartime_tests(
|
||||
ec = ECP_ShortW_Prj[Fp[BN254_Snarks], G1],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_projective_g1_add_double_vartime_" & $BN254_Snarks
|
||||
)
|
||||
|
||||
run_EC_addition_vartime_tests(
|
||||
ec = ECP_ShortW_Prj[Fp[BLS12_381], G1],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_projective_g1_add_double_vartime_" & $BLS12_381
|
||||
)
|
||||
|
||||
run_EC_addition_vartime_tests(
|
||||
ec = ECP_ShortW_Prj[Fp[BLS12_377], G1],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_projective_g1_add_double_vartime_" & $BLS12_377
|
||||
)
|
||||
|
||||
run_EC_addition_vartime_tests(
|
||||
ec = ECP_ShortW_Prj[Fp[BW6_761], G1],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_projective_g1_add_double_vartime_" & $BW6_761
|
||||
)
|
||||
|
||||
run_EC_addition_vartime_tests(
|
||||
ec = ECP_ShortW_Prj[Fp[Pallas], G1],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_projective_g1_add_double_vartime_" & $Pallas
|
||||
)
|
||||
|
||||
run_EC_addition_vartime_tests(
|
||||
ec = ECP_ShortW_Prj[Fp[Vesta], G1],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_shortweierstrass_projective_g1_add_double_vartime_" & $Vesta
|
||||
)
|
||||
|
|
|
@ -92,7 +92,7 @@ proc run_EC_addition_tests*(
|
|||
echo "\n------------------------------------------------------\n"
|
||||
echo moduleName, " xoshiro512** seed: ", seed
|
||||
|
||||
const testSuiteDesc = "Elliptic curve in " & $ec.F.C.getEquationForm() & " form with projective coordinates"
|
||||
const testSuiteDesc = "Elliptic curve in " & $ec.F.C.getEquationForm() & " form"
|
||||
|
||||
suite testSuiteDesc & " - " & $ec & " - [" & $WordBitWidth & "-bit mode]":
|
||||
test "The infinity point is the neutral element w.r.t. to EC " & $ec.G & " addition":
|
||||
|
@ -268,6 +268,193 @@ proc run_EC_addition_tests*(
|
|||
test(ec, randZ = false, gen = Long01Sequence)
|
||||
test(ec, randZ = true, gen = Long01Sequence)
|
||||
|
||||
|
||||
proc run_EC_addition_vartime_tests*(
|
||||
ec: typedesc,
|
||||
Iters: static int,
|
||||
moduleName: string) =
|
||||
var rng: RngState
|
||||
let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32
|
||||
rng.seed(seed)
|
||||
echo "\n------------------------------------------------------\n"
|
||||
echo moduleName, " xoshiro512** seed: ", seed
|
||||
|
||||
const testSuiteDesc = "Elliptic curve in " & $ec.F.C.getEquationForm() & " form"
|
||||
|
||||
suite testSuiteDesc & " - " & $ec & " (vartime) - [" & $WordBitWidth & "-bit mode]":
|
||||
test "The infinity point is the neutral element w.r.t. to EC " & $ec.G & " addition (vartime)":
|
||||
proc test(EC: typedesc, randZ: bool, gen: RandomGen) =
|
||||
var inf {.noInit.}: EC
|
||||
inf.setInf()
|
||||
check: bool inf.isInf()
|
||||
|
||||
for _ in 0 ..< Iters:
|
||||
var r{.noInit.}: EC
|
||||
let P = rng.random_point(EC, randZ, gen)
|
||||
|
||||
r.sum_vartime(P, inf)
|
||||
check: bool(r == P)
|
||||
|
||||
r.sum_vartime(inf, P)
|
||||
check: bool(r == P)
|
||||
|
||||
# Aliasing tests
|
||||
r = P
|
||||
r.sum_vartime(r, inf)
|
||||
check: bool(r == P)
|
||||
|
||||
r.setInf()
|
||||
r.sum_vartime(r, P)
|
||||
check: bool(r == P)
|
||||
|
||||
test(ec, randZ = false, gen = Uniform)
|
||||
test(ec, randZ = true, gen = Uniform)
|
||||
test(ec, randZ = false, gen = HighHammingWeight)
|
||||
test(ec, randZ = true, gen = HighHammingWeight)
|
||||
test(ec, randZ = false, gen = Long01Sequence)
|
||||
test(ec, randZ = true, gen = Long01Sequence)
|
||||
|
||||
test "Infinity point from affine conversion gives proper result (vartime)":
|
||||
proc test(EC: typedesc, randZ: bool, gen: RandomGen) =
|
||||
var affInf {.noInit.}: affine(EC)
|
||||
var inf {.noInit.}: EC
|
||||
affInf.setInf()
|
||||
inf.fromAffine(affInf)
|
||||
check: bool inf.isInf()
|
||||
|
||||
for _ in 0 ..< Iters:
|
||||
var r{.noInit.}: EC
|
||||
let P = rng.random_point(EC, randZ, gen)
|
||||
|
||||
r.sum_vartime(P, inf)
|
||||
check: bool(r == P)
|
||||
|
||||
r.sum_vartime(inf, P)
|
||||
check: bool(r == P)
|
||||
|
||||
# Aliasing tests
|
||||
r = P
|
||||
r.sum_vartime(r, inf)
|
||||
check: bool(r == P)
|
||||
|
||||
r.setInf()
|
||||
r.sum_vartime(r, P)
|
||||
check: bool(r == P)
|
||||
|
||||
test(ec, randZ = false, gen = Uniform)
|
||||
test(ec, randZ = true, gen = Uniform)
|
||||
test(ec, randZ = false, gen = HighHammingWeight)
|
||||
test(ec, randZ = true, gen = HighHammingWeight)
|
||||
test(ec, randZ = false, gen = Long01Sequence)
|
||||
test(ec, randZ = true, gen = Long01Sequence)
|
||||
|
||||
test "Adding opposites gives an infinity point (vartime)":
|
||||
proc test(EC: typedesc, randZ: bool, gen: RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
var r{.noInit.}: EC
|
||||
let P = rng.random_point(EC, randZ, gen)
|
||||
var Q = P
|
||||
Q.neg()
|
||||
|
||||
r.sum_vartime(P, Q)
|
||||
check: bool r.isInf()
|
||||
|
||||
r.sum_vartime(Q, P)
|
||||
check: bool r.isInf()
|
||||
|
||||
test(ec, randZ = false, gen = Uniform)
|
||||
test(ec, randZ = true, gen = Uniform)
|
||||
test(ec, randZ = false, gen = HighHammingWeight)
|
||||
test(ec, randZ = true, gen = HighHammingWeight)
|
||||
test(ec, randZ = false, gen = Long01Sequence)
|
||||
test(ec, randZ = true, gen = Long01Sequence)
|
||||
|
||||
test "EC " & $ec.G & " add is commutative (vartime)":
|
||||
proc test(EC: typedesc, randZ: bool, gen: RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
var r0{.noInit.}, r1{.noInit.}: EC
|
||||
let P = rng.random_point(EC, randZ, gen)
|
||||
let Q = rng.random_point(EC, randZ, gen)
|
||||
|
||||
r0.sum_vartime(P, Q)
|
||||
r1.sum_vartime(Q, P)
|
||||
check: bool(r0 == r1)
|
||||
|
||||
test(ec, randZ = false, gen = Uniform)
|
||||
test(ec, randZ = true, gen = Uniform)
|
||||
test(ec, randZ = false, gen = HighHammingWeight)
|
||||
test(ec, randZ = true, gen = HighHammingWeight)
|
||||
test(ec, randZ = false, gen = Long01Sequence)
|
||||
test(ec, randZ = true, gen = Long01Sequence)
|
||||
|
||||
test "EC " & $ec.G & " add is associative (vartime)":
|
||||
proc test(EC: typedesc, randZ: bool, gen: RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
let a = rng.random_point(EC, randZ, gen)
|
||||
let b = rng.random_point(EC, randZ, gen)
|
||||
let c = rng.random_point(EC, randZ, gen)
|
||||
|
||||
var tmp1{.noInit.}, tmp2{.noInit.}: EC
|
||||
|
||||
# r0 = (a + b) + c
|
||||
tmp1.sum_vartime(a, b)
|
||||
tmp2.sum_vartime(tmp1, c)
|
||||
let r0 = tmp2
|
||||
|
||||
# r1 = a + (b + c)
|
||||
tmp1.sum_vartime(b, c)
|
||||
tmp2.sum_vartime(a, tmp1)
|
||||
let r1 = tmp2
|
||||
|
||||
# r2 = (a + c) + b
|
||||
tmp1.sum_vartime(a, c)
|
||||
tmp2.sum_vartime(tmp1, b)
|
||||
let r2 = tmp2
|
||||
|
||||
# r3 = a + (c + b)
|
||||
tmp1.sum_vartime(c, b)
|
||||
tmp2.sum_vartime(a, tmp1)
|
||||
let r3 = tmp2
|
||||
|
||||
# r4 = (c + a) + b
|
||||
tmp1.sum_vartime(c, a)
|
||||
tmp2.sum_vartime(tmp1, b)
|
||||
let r4 = tmp2
|
||||
|
||||
# ...
|
||||
|
||||
check:
|
||||
bool(r0 == r1)
|
||||
bool(r0 == r2)
|
||||
bool(r0 == r3)
|
||||
bool(r0 == r4)
|
||||
|
||||
test(ec, randZ = false, gen = Uniform)
|
||||
test(ec, randZ = true, gen = Uniform)
|
||||
test(ec, randZ = false, gen = HighHammingWeight)
|
||||
test(ec, randZ = true, gen = HighHammingWeight)
|
||||
test(ec, randZ = false, gen = Long01Sequence)
|
||||
test(ec, randZ = true, gen = Long01Sequence)
|
||||
|
||||
test "EC " & $ec.G & " double and EC " & $ec.G & " add are consistent (vartime)":
|
||||
proc test(EC: typedesc, randZ: bool, gen: RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
let a = rng.random_point(EC, randZ, gen)
|
||||
|
||||
var r0{.noInit.}, r1{.noInit.}: EC
|
||||
|
||||
r0.double(a)
|
||||
r1.sum_vartime(a, a)
|
||||
|
||||
check: bool(r0 == r1)
|
||||
|
||||
test(ec, randZ = false, gen = Uniform)
|
||||
test(ec, randZ = true, gen = Uniform)
|
||||
test(ec, randZ = false, gen = HighHammingWeight)
|
||||
test(ec, randZ = true, gen = HighHammingWeight)
|
||||
test(ec, randZ = false, gen = Long01Sequence)
|
||||
test(ec, randZ = true, gen = Long01Sequence)
|
||||
|
||||
proc run_EC_mul_sanity_tests*(
|
||||
ec: typedesc,
|
||||
ItersMul: static int,
|
||||
|
@ -479,8 +666,6 @@ proc run_EC_mul_vs_ref_impl*(
|
|||
refWNaf(2)
|
||||
refWNaf(3)
|
||||
refWNaf(5)
|
||||
refWNaf(8)
|
||||
refWNaf(13)
|
||||
|
||||
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Uniform)
|
||||
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Uniform)
|
||||
|
@ -509,14 +694,23 @@ proc run_EC_mixed_add_impl*(
|
|||
let a = rng.random_point(EC, randZ, gen)
|
||||
let b = rng.random_point(EC, randZ, gen)
|
||||
var bAff: ECP_ShortW_Aff[EC.F, EC.G]
|
||||
var bz1: EC
|
||||
bAff.affine(b)
|
||||
bz1.fromAffine(bAff) # internals special-case Z=1
|
||||
|
||||
var r_generic, r_mixed: EC
|
||||
var r_generic, r_mixed, r_vartime, r_vartime2, r_vartime3: EC
|
||||
|
||||
r_generic.sum(a, b)
|
||||
r_mixed.madd(a, bAff)
|
||||
r_vartime.sum_vartime(a, bz1)
|
||||
r_vartime2.sum_vartime(a, b)
|
||||
r_vartime3.madd_vartime(a, bAff)
|
||||
|
||||
check: bool(r_generic == r_mixed)
|
||||
check:
|
||||
bool(r_generic == r_mixed)
|
||||
bool(r_generic == r_vartime)
|
||||
bool(r_generic == r_vartime2)
|
||||
bool(r_generic == r_vartime3)
|
||||
|
||||
test(ec, randZ = false, gen = Uniform)
|
||||
test(ec, randZ = true, gen = Uniform)
|
||||
|
@ -530,18 +724,37 @@ proc run_EC_mixed_add_impl*(
|
|||
for _ in 0 ..< Iters:
|
||||
let a = rng.random_point(EC, randZ, gen)
|
||||
var aAff: ECP_ShortW_Aff[EC.F, EC.G]
|
||||
var az1: EC
|
||||
aAff.affine(a)
|
||||
az1.fromAffine(aAff)
|
||||
|
||||
var r_generic, r_mixed: EC
|
||||
var r_generic, r_mixed, r_vartime, r_vartime2, r_vartime3: EC
|
||||
|
||||
r_generic.double(a)
|
||||
r_mixed.madd(a, aAff)
|
||||
check: bool(r_generic == r_mixed)
|
||||
r_vartime.sum_vartime(a, a)
|
||||
r_vartime2.sum_vartime(a, az1)
|
||||
r_vartime3.madd_vartime(a, aAff)
|
||||
check:
|
||||
bool(r_generic == r_mixed)
|
||||
bool(r_generic == r_vartime)
|
||||
bool(r_generic == r_vartime2)
|
||||
bool(r_generic == r_vartime3)
|
||||
|
||||
# Aliasing test
|
||||
r_mixed = a
|
||||
r_mixed += aAff
|
||||
check: bool(r_generic == r_mixed)
|
||||
r_vartime = a
|
||||
r_vartime.sum_vartime(r_vartime, a)
|
||||
r_vartime2 = az1
|
||||
r_vartime2.sum_vartime(r_vartime2, az1)
|
||||
r_vartime3 = a
|
||||
r_vartime3.madd_vartime(r_vartime3, aAff)
|
||||
check:
|
||||
bool(r_generic == r_mixed)
|
||||
bool(r_generic == r_vartime)
|
||||
bool(r_generic == r_vartime2)
|
||||
bool(r_generic == r_vartime3)
|
||||
|
||||
test(ec, randZ = false, gen = Uniform)
|
||||
test(ec, randZ = true, gen = Uniform)
|
||||
|
@ -563,41 +776,41 @@ proc run_EC_mixed_add_impl*(
|
|||
var r{.noInit.}: ECP_ShortW_Aff[EC.F, EC.G]
|
||||
r.affine(r_mixed)
|
||||
|
||||
# Aliasing test
|
||||
a += bAff
|
||||
|
||||
check:
|
||||
bool(r == bAff)
|
||||
bool(a == r_mixed)
|
||||
|
||||
# vartime - internals special-case Z=1
|
||||
var r_vartime, r_vartime2: EC
|
||||
var b: EC
|
||||
b.fromAffine(bAff)
|
||||
|
||||
a.setInf()
|
||||
r_vartime.sum_vartime(a, b)
|
||||
r_vartime2.madd_vartime(a, bAff)
|
||||
|
||||
check:
|
||||
bool(r_vartime == r_mixed)
|
||||
bool(r_vartime2 == r_mixed)
|
||||
|
||||
# Aliasing
|
||||
r_vartime.setInf()
|
||||
r_vartime.sum_vartime(r_vartime, b)
|
||||
r_vartime2.setInf()
|
||||
r_vartime2.sum_vartime(r_vartime2, b)
|
||||
|
||||
check:
|
||||
bool(r_vartime == r_mixed)
|
||||
bool(r_vartime2 == r_mixed)
|
||||
|
||||
test(ec, randZ = false, gen = Uniform)
|
||||
test(ec, randZ = false, gen = HighHammingWeight)
|
||||
test(ec, randZ = false, gen = Long01Sequence)
|
||||
|
||||
test "EC " & $ec.G & " mixed addition - adding infinity RHS":
|
||||
proc test(EC: typedesc, randZ: bool, gen: RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
let a = rng.random_point(EC, randZ, gen)
|
||||
var naAff{.noInit.}: ECP_ShortW_Aff[EC.F, EC.G]
|
||||
naAff.affine(a)
|
||||
naAff.neg()
|
||||
|
||||
var r{.noInit.}: EC
|
||||
r.madd(a, naAff)
|
||||
|
||||
check: r.isInf().bool
|
||||
|
||||
r = a
|
||||
r += naAff
|
||||
check: r.isInf().bool
|
||||
|
||||
test(ec, randZ = false, gen = Uniform)
|
||||
test(ec, randZ = true, gen = Uniform)
|
||||
test(ec, randZ = false, gen = HighHammingWeight)
|
||||
test(ec, randZ = true, gen = HighHammingWeight)
|
||||
test(ec, randZ = false, gen = Long01Sequence)
|
||||
test(ec, randZ = true, gen = Long01Sequence)
|
||||
|
||||
test "EC " & $ec.G & " mixed addition - adding opposites":
|
||||
proc test(EC: typedesc, randZ: bool, gen: RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
let a = rng.random_point(EC, randZ, gen)
|
||||
|
@ -613,6 +826,75 @@ proc run_EC_mixed_add_impl*(
|
|||
r += bAff
|
||||
check: bool(r == a)
|
||||
|
||||
# vartime
|
||||
var r_vartime, r_vartime2: EC
|
||||
var b: EC
|
||||
b.fromAffine(bAff)
|
||||
|
||||
r_vartime.sum_vartime(a, b)
|
||||
r_vartime2.madd_vartime(a, bAff)
|
||||
|
||||
check:
|
||||
bool(r_vartime == r)
|
||||
bool(r_vartime2 == r)
|
||||
|
||||
# Aliasing
|
||||
r_vartime = a
|
||||
r_vartime.sum_vartime(r_vartime, b)
|
||||
r_vartime2 = a
|
||||
r_vartime2.sum_vartime(r_vartime2, b)
|
||||
|
||||
check:
|
||||
bool(r_vartime == r)
|
||||
bool(r_vartime2 == r)
|
||||
|
||||
test(ec, randZ = false, gen = Uniform)
|
||||
test(ec, randZ = true, gen = Uniform)
|
||||
test(ec, randZ = false, gen = HighHammingWeight)
|
||||
test(ec, randZ = true, gen = HighHammingWeight)
|
||||
test(ec, randZ = false, gen = Long01Sequence)
|
||||
test(ec, randZ = true, gen = Long01Sequence)
|
||||
|
||||
test "EC " & $ec.G & " mixed addition - adding opposites":
|
||||
proc test(EC: typedesc, randZ: bool, gen: RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
let a = rng.random_point(EC, randZ, gen)
|
||||
var naAff{.noInit.}: ECP_ShortW_Aff[EC.F, EC.G]
|
||||
naAff.affine(a)
|
||||
naAff.neg()
|
||||
|
||||
var r{.noInit.}: EC
|
||||
r.madd(a, naAff)
|
||||
|
||||
check: r.isInf().bool
|
||||
|
||||
# Aliasing
|
||||
r = a
|
||||
r += naAff
|
||||
check: r.isInf().bool
|
||||
|
||||
# vartime
|
||||
var r_vartime, r_vartime2: EC
|
||||
var na: EC
|
||||
na.fromAffine(naAff)
|
||||
|
||||
r_vartime.sum_vartime(a, na)
|
||||
r_vartime2.madd_vartime(a, naAff)
|
||||
|
||||
check:
|
||||
bool(r_vartime == r)
|
||||
bool(r_vartime2 == r)
|
||||
|
||||
# Aliasing
|
||||
r_vartime = a
|
||||
r_vartime.sum_vartime(r_vartime, na)
|
||||
r_vartime2 = a
|
||||
r_vartime2.madd_vartime(r_vartime2, naAff)
|
||||
|
||||
check:
|
||||
bool(r_vartime == r)
|
||||
bool(r_vartime2 == r)
|
||||
|
||||
test(ec, randZ = false, gen = Uniform)
|
||||
test(ec, randZ = true, gen = Uniform)
|
||||
test(ec, randZ = false, gen = HighHammingWeight)
|
||||
|
|
Loading…
Reference in New Issue