constantine/tests/math/t_ec_sage_template.nim

227 lines
6.7 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/config/curves,
../../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],
# Test utilities
./support/ec_reference_scalar_mult
export unittest, abstractions, arithmetic # Generic sandwich
# Serialization
# --------------------------------------------------------------------------
macro matchingScalar*(EC: type ECP_ShortW_Aff): untyped =
## Workaround the annoying type system
## 1. Higher-kinded type
## 2. Computation in type section needs template or macro indirection
## 3. Converting NimNode to typedesc
## https://github.com/nim-lang/Nim/issues/6785
# BigInt[EC.F.C.getCurveOrderBitwidth()]
let ec = EC.getTypeImpl()
# echo ec.treerepr
# BracketExpr
# Sym "typeDesc"
# BracketExpr
# Sym "ECP_ShortW_Aff"
# BracketExpr
# Sym "Fp"
# IntLit 12
# IntLit 0
doAssert ec[0].eqIdent"typedesc"
doAssert ec[1][0].eqIdent"ECP_ShortW_Aff"
ec[1][1].expectkind(nnkBracketExpr)
doAssert ($ec[1][1][0]).startsWith"Fp"
let curve = Curve(ec[1][1][1].intVal)
let bitwidth = getAST(getCurveOrderBitwidth(curve))
result = nnkBracketExpr.newTree(
bindSym"BigInt",
bitwidth
)
macro matchingNonResidueType*(EC: type ECP_ShortW_Aff): untyped =
## Workaround the annoying type system
## 1. Higher-kinded type
## 2. Computation in type section needs template or macro indirection
## 3. Converting NimNode to typedesc
## https://github.com/nim-lang/Nim/issues/6785
let ec = EC.getTypeImpl()
doAssert ec[0].eqIdent"typedesc"
doAssert ec[1][0].eqIdent"ECP_ShortW_Aff"
ec[1][1].expectkind(nnkBracketExpr)
doAssert ($ec[1][1][0]).startsWith"Fp"
# int or array[2, int]
if ec[1][1][0].eqIdent"Fp":
result = bindSym"int"
elif ec[1][1][0].eqIdent"Fp2":
result = nnkBracketExpr.newTree(
bindSym"array",
newLit 2,
bindSym"int"
)
type
TestVector*[EC: ECP_ShortW_Aff] = object
id: int
P: EC
scalar: matchingScalar(EC)
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] = object
curve: string
group: string
modulus: string
order: string
cofactor: string
form: string
a: string
b: string
# vectors ------------------
vectors: seq[TestVector[EC]]
ScalarMulTestG2[EC: ECP_ShortW_Aff] = 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
non_residue_twist: matchingNonResidueType(EC) # int or array[2, int]
# vectors ------------------
vectors: seq[TestVector[EC]]
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 & ".json"
let content = readFile(TestVectorsDir/filename)
result = content.fromJson(TestType)
# Testing
# ------------------------------------------------------------------------
proc run_scalar_mul_test_vs_sage*(
EC: typedesc,
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]])
else:
const G1_or_G2 = "G2"
let vec = loadVectors(ScalarMulTestG2[ECP_ShortW_Aff[EC.F, EC.G]])
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"
suite testSuiteDesc & " [" & $WordBitWidth & "-bit mode]":
for i in 0 ..< vec.vectors.len:
test "test " & $vec.vectors[i].id & " - " & $EC:
var
P{.noInit.}: EC
Q {.noInit.}: EC
impl {.noInit.}: EC
reference {.noInit.}: EC
endo {.noInit.}: EC
P.fromAffine(vec.vectors[i].P)
Q.fromAffine(vec.vectors[i].Q)
impl = P
reference = P
endo = P
impl.scalarMulGeneric(vec.vectors[i].scalar)
reference.unsafe_ECmul_double_add(vec.vectors[i].scalar)
endo.scalarMulEndo(vec.vectors[i].scalar)
doAssert: bool(Q == reference)
doAssert: bool(Q == impl)
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)