constantine/tests/math_elliptic_curves/t_ec_sage_template.nim

189 lines
5.9 KiB
Nim

# Constantine
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
# Standard library
std/[unittest, times, os, strutils, macros],
# 3rd party
pkg/jsony,
# Internals
../../constantine/platforms/abstractions,
../../constantine/math/[arithmetic, extension_fields],
../../constantine/math/io/[io_bigints, io_ec],
../../constantine/math/elliptic/[
ec_shortweierstrass_affine,
ec_shortweierstrass_projective,
ec_shortweierstrass_jacobian,
ec_scalar_mul,
ec_endomorphism_accel],
../../constantine/math/constants/zoo_endomorphisms,
# Test utilities
../../constantine/math/elliptic/ec_scalar_mul_vartime
export unittest, abstractions, arithmetic # Generic sandwich
# Serialization
# --------------------------------------------------------------------------
type
TestVector*[EC: ECP_ShortW_Aff, bits: static int] = object
id: int
P: EC
scalarBits: int
scalar: BigInt[bits]
Q: EC
EC_G1_hex = object
x: string
y: string
Fp2_hex = object
c0: string
c1: string
EC_G2_hex = object
x: Fp2_hex
y: Fp2_hex
ScalarMulTestG1[EC: ECP_ShortW_Aff, bits: static int] = object
curve: string
group: string
modulus: string
order: string
cofactor: string
form: string
a: string
b: string
# vectors ------------------
vectors: seq[TestVector[EC, bits]]
ScalarMulTestG2[EC: ECP_ShortW_Aff, bits: static int] = object
curve: string
group: string
modulus: string
order: string
cofactor: string
form: string
a: string
b: string
# G2 -----------------------
twist_degree: int
twist: string
non_residue_fp: int
G2_field: string
when EC.F is Fp:
non_residue_twist: int
else:
non_residue_twist: array[2, int]
# vectors ------------------
vectors: seq[TestVector[EC, bits]]
const
TestVectorsDir* =
currentSourcePath.rsplit(DirSep, 1)[0] / "vectors"
proc parseHook*(src: string, pos: var int, value: var BigInt) =
var str: string
parseHook(src, pos, str)
value.fromHex(str)
proc parseHook*(src: string, pos: var int, value: var ECP_ShortW_Aff) =
# Note when nim-serialization was used:
# When ECP_ShortW_Aff[Fp[Foo], G1]
# and ECP_ShortW_Aff[Fp[Foo], G2]
# are generated in the same file (i.e. twists and base curve are both on Fp)
# this creates bad codegen, in the C code, the `value`parameter gets the wrong type
# TODO: upstream
when ECP_ShortW_Aff.F is Fp:
var P: EC_G1_hex
parseHook(src, pos, P)
let ok = value.fromHex(P.x, P.y)
doAssert ok, "\nDeserialization error on G1 for\n" &
" P.x: " & P.x & "\n" &
" P.y: " & P.x & "\n"
elif ECP_ShortW_Aff.F is Fp2:
var P: EC_G2_hex
parseHook(src, pos, P)
let ok = value.fromHex(P.x.c0, P.x.c1, P.y.c0, P.y.c1)
doAssert ok, "\nDeserialization error on G2 for\n" &
" P.x0: " & P.x.c0 & "\n" &
" P.x1: " & P.x.c1 & "\n" &
" P.y0: " & P.y.c0 & "\n" &
" P.y1: " & P.y.c1 & "\n"
else:
{.error: "Not Implemented".}
proc loadVectors(TestType: typedesc): TestType =
const group = when TestType.EC.G == G1: "G1"
else: "G2"
const filename = "tv_" & $TestType.EC.F.C & "_scalar_mul_" & group & "_" & $TestType.bits & "bit.json"
echo "Loading: ", filename
let content = readFile(TestVectorsDir/filename)
result = content.fromJson(TestType)
# Testing
# ------------------------------------------------------------------------
proc run_scalar_mul_test_vs_sage*(
EC: typedesc, bits: static int,
moduleName: string
) =
echo "\n------------------------------------------------------\n"
echo moduleName & '\n'
when EC.G == G1:
const G1_or_G2 = "G1"
let vec = loadVectors(ScalarMulTestG1[ECP_ShortW_Aff[EC.F, EC.G], bits])
else:
const G1_or_G2 = "G2"
let vec = loadVectors(ScalarMulTestG2[ECP_ShortW_Aff[EC.F, EC.G], bits])
const coord = when EC is ECP_ShortW_Prj: " Projective coordinates "
elif EC is ECP_ShortW_Jac: " Jacobian coordinates "
const testSuiteDesc = "Scalar Multiplication " & $EC.F.C & " " & G1_or_G2 & " vs SageMath - " & $bits & "-bit scalar"
suite testSuiteDesc & " [" & $WordBitWidth & "-bit words]":
for i in 0 ..< vec.vectors.len:
test "test " & $vec.vectors[i].id & " - " & $EC & " - " & $bits & "-bit scalar":
var
P{.noInit.}: EC
Q {.noInit.}: EC
impl {.noInit.}: EC
reference {.noInit.}: EC
refMinWeight {.noInit.}: EC
P.fromAffine(vec.vectors[i].P)
Q.fromAffine(vec.vectors[i].Q)
impl = P
reference = P
refMinWeight = P
impl.scalarMulGeneric(vec.vectors[i].scalar)
reference.scalarMul_doubleAdd_vartime(vec.vectors[i].scalar)
refMinWeight.scalarMul_minHammingWeight_vartime(vec.vectors[i].scalar)
doAssert: bool(Q == reference)
doAssert: bool(Q == impl)
doAssert: bool(Q == refMinWeight)
staticFor w, 2, 14:
var refWNAF = P
refWNAF.scalarMul_minHammingWeight_windowed_vartime(vec.vectors[i].scalar, window = w)
check: bool(impl == refWNAF)
when bits >= EndomorphismThreshold: # All endomorphisms constants are below this threshold
var endo = P
endo.scalarMulEndo(vec.vectors[i].scalar)
doAssert: bool(Q == endo)
when EC.F is Fp: # Test windowed endomorphism acceleration
var endoW = P
endoW.scalarMulGLV_m2w2(vec.vectors[i].scalar)
doAssert: bool(Q == endoW)