Fr: Finite Field parametrized by the curve order (#115)
* Introduce Fr type: finite field over curve order. Need workaround for https://github.com/nim-lang/Nim/issues/16774 * Split curve properties into core and derived * Attach field properties to an instantiated field instead of the curve enum * Workaround https://github.com/nim-lang/Nim/issues/14021, yet another "working with types in macros" is difficult https://github.com/nim-lang/RFCs/issues/44 * Implement finite field over prime order of a curve subgroup * skip OpenSSL tests on windows
This commit is contained in:
parent
ac6300555a
commit
638cb71e16
|
@ -105,7 +105,7 @@ proc invEuclidBench*(T: typedesc, iters: int) =
|
|||
|
||||
proc invPowFermatBench*(T: typedesc, iters: int) =
|
||||
let x = rng.random_unsafe(T)
|
||||
const exponent = T.C.getInvModExponent()
|
||||
const exponent = T.getInvModExponent()
|
||||
bench("Inversion via exponentiation p-2 (Little Fermat)", T, iters):
|
||||
var r = x
|
||||
r.powUnsafeExponent(exponent)
|
||||
|
|
|
@ -148,7 +148,12 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
|
|||
("tests/t_pairing_bls12_381_optate.nim", false),
|
||||
|
||||
# Hashing vs OpenSSL
|
||||
("tests/t_hash_sha256_vs_openssl.nim", true),
|
||||
# ----------------------------------------------------------
|
||||
("tests/t_hash_sha256_vs_openssl.nim", true), # skip OpenSSL tests on Windows
|
||||
|
||||
# Prime order fields
|
||||
# ----------------------------------------------------------
|
||||
("tests/t_fr.nim", false),
|
||||
]
|
||||
|
||||
# For temporary (hopefully) investigation that can only be reproduced in CI
|
||||
|
|
|
@ -8,25 +8,27 @@
|
|||
|
||||
# ############################################################
|
||||
#
|
||||
# Fp: Finite Field arithmetic with prime field modulus P
|
||||
# FF: Finite Field arithmetic
|
||||
# Fp: with prime field modulus P
|
||||
# Fr: with prime curve subgroup order r
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
# Constraints:
|
||||
# - We assume that p is known at compile-time
|
||||
# - We assume that p is not even:
|
||||
# - We assume that p and r are known at compile-time
|
||||
# - We assume that p and r are not even:
|
||||
# - Operations are done in the Montgomery domain
|
||||
# - The Montgomery domain introduce a Montgomery constant that must be coprime
|
||||
# with the field modulus.
|
||||
# - The constant is chosen a power of 2
|
||||
# => to be coprime with a power of 2, p must be odd
|
||||
# - We assume that p is a prime
|
||||
# - Modular inversion uses the Fermat's little theorem
|
||||
# => to be coprime with a power of 2, p and r must be odd
|
||||
# - We assume that p and r are a prime
|
||||
# - Modular inversion may use the Fermat's little theorem
|
||||
# which requires a prime
|
||||
|
||||
import
|
||||
../primitives,
|
||||
../config/[common, type_fp, curves],
|
||||
../config/[common, type_ff, curves],
|
||||
./bigints, ./limbs_montgomery
|
||||
|
||||
when UseASM_X86_64:
|
||||
|
@ -37,7 +39,7 @@ when nimvm:
|
|||
else:
|
||||
discard
|
||||
|
||||
export Fp
|
||||
export Fp, Fr, FF
|
||||
|
||||
# No exceptions allowed
|
||||
{.push raises: [].}
|
||||
|
@ -48,34 +50,34 @@ export Fp
|
|||
#
|
||||
# ############################################################
|
||||
|
||||
func fromBig*[C: static Curve](dst: var Fp[C], src: BigInt) {.inline.}=
|
||||
func fromBig*(dst: var FF, src: BigInt) {.inline.}=
|
||||
## Convert a BigInt to its Montgomery form
|
||||
when nimvm:
|
||||
dst.mres.montyResidue_precompute(src, C.Mod, C.getR2modP(), C.getNegInvModWord())
|
||||
dst.mres.montyResidue_precompute(src, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord())
|
||||
else:
|
||||
dst.mres.montyResidue(src, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul())
|
||||
dst.mres.montyResidue(src, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul())
|
||||
|
||||
func fromBig*[C: static Curve](T: type Fp[C], src: BigInt): Fp[C] {.noInit, inline.} =
|
||||
func fromBig*[C: static Curve](T: type FF[C], src: BigInt): FF[C] {.noInit, inline.} =
|
||||
## Convert a BigInt to its Montgomery form
|
||||
result.fromBig(src)
|
||||
|
||||
func toBig*(src: Fp): auto {.noInit, inline.} =
|
||||
func toBig*(src: FF): auto {.noInit, inline.} =
|
||||
## Convert a finite-field element to a BigInt in natural representation
|
||||
var r {.noInit.}: typeof(src.mres)
|
||||
r.redc(src.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontyMul())
|
||||
r.redc(src.mres, FF.fieldMod(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul())
|
||||
return r
|
||||
|
||||
# Copy
|
||||
# ------------------------------------------------------------
|
||||
|
||||
func ccopy*(a: var Fp, b: Fp, ctl: SecretBool) {.inline.} =
|
||||
func ccopy*(a: var FF, b: FF, ctl: SecretBool) {.inline.} =
|
||||
## Constant-time conditional copy
|
||||
## If ctl is true: b is copied into a
|
||||
## if ctl is false: b is not copied and a is unmodified
|
||||
## Time and memory accesses are the same whether a copy occurs or not
|
||||
ccopy(a.mres, b.mres, ctl)
|
||||
|
||||
func cswap*(a, b: var Fp, ctl: CTBool) {.inline.} =
|
||||
func cswap*(a, b: var FF, ctl: CTBool) {.inline.} =
|
||||
## Swap ``a`` and ``b`` if ``ctl`` is true
|
||||
##
|
||||
## Constant-time:
|
||||
|
@ -98,144 +100,144 @@ func cswap*(a, b: var Fp, ctl: CTBool) {.inline.} =
|
|||
# exist and can be implemented with compile-time specialization.
|
||||
|
||||
# Note: for `+=`, double, sum
|
||||
# not(a.mres < Fp.C.Mod) is unnecessary if the prime has the form
|
||||
# not(a.mres < FF.fieldMod()) is unnecessary if the prime has the form
|
||||
# (2^64)^w - 1 (if using uint64 words).
|
||||
# In practice I'm not aware of such prime being using in elliptic curves.
|
||||
# 2^127 - 1 and 2^521 - 1 are used but 127 and 521 are not multiple of 32/64
|
||||
|
||||
func `==`*(a, b: Fp): SecretBool {.inline.} =
|
||||
func `==`*(a, b: FF): SecretBool {.inline.} =
|
||||
## Constant-time equality check
|
||||
a.mres == b.mres
|
||||
|
||||
func isZero*(a: Fp): SecretBool {.inline.} =
|
||||
func isZero*(a: FF): SecretBool {.inline.} =
|
||||
## Constant-time check if zero
|
||||
a.mres.isZero()
|
||||
|
||||
func isOne*(a: Fp): SecretBool {.inline.} =
|
||||
func isOne*(a: FF): SecretBool {.inline.} =
|
||||
## Constant-time check if one
|
||||
a.mres == Fp.C.getMontyOne()
|
||||
a.mres == FF.getMontyOne()
|
||||
|
||||
func isMinusOne*(a: Fp): SecretBool {.inline.} =
|
||||
func isMinusOne*(a: FF): SecretBool {.inline.} =
|
||||
## Constant-time check if -1 (mod p)
|
||||
a.mres == Fp.C.getMontyPrimeMinus1()
|
||||
a.mres == FF.getMontyPrimeMinus1()
|
||||
|
||||
func setZero*(a: var Fp) {.inline.} =
|
||||
func setZero*(a: var FF) {.inline.} =
|
||||
## Set ``a`` to zero
|
||||
a.mres.setZero()
|
||||
|
||||
func setOne*(a: var Fp) {.inline.} =
|
||||
func setOne*(a: var FF) {.inline.} =
|
||||
## Set ``a`` to one
|
||||
# Note: we need 1 in Montgomery residue form
|
||||
# TODO: Nim codegen is not optimal it uses a temporary
|
||||
# Check if the compiler optimizes it away
|
||||
a.mres = Fp.C.getMontyOne()
|
||||
a.mres = FF.getMontyOne()
|
||||
|
||||
func `+=`*(a: var Fp, b: Fp) {.inline.} =
|
||||
func `+=`*(a: var FF, b: FF) {.inline.} =
|
||||
## In-place addition modulo p
|
||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
||||
addmod_asm(a.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs)
|
||||
addmod_asm(a.mres.limbs, b.mres.limbs, FF.fieldMod().limbs)
|
||||
else:
|
||||
var overflowed = add(a.mres, b.mres)
|
||||
overflowed = overflowed or not(a.mres < Fp.C.Mod)
|
||||
discard csub(a.mres, Fp.C.Mod, overflowed)
|
||||
overflowed = overflowed or not(a.mres < FF.fieldMod())
|
||||
discard csub(a.mres, FF.fieldMod(), overflowed)
|
||||
|
||||
func `-=`*(a: var Fp, b: Fp) {.inline.} =
|
||||
func `-=`*(a: var FF, b: FF) {.inline.} =
|
||||
## In-place substraction modulo p
|
||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
||||
submod_asm(a.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs)
|
||||
submod_asm(a.mres.limbs, b.mres.limbs, FF.fieldMod().limbs)
|
||||
else:
|
||||
let underflowed = sub(a.mres, b.mres)
|
||||
discard cadd(a.mres, Fp.C.Mod, underflowed)
|
||||
discard cadd(a.mres, FF.fieldMod(), underflowed)
|
||||
|
||||
func double*(a: var Fp) {.inline.} =
|
||||
func double*(a: var FF) {.inline.} =
|
||||
## Double ``a`` modulo p
|
||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
||||
addmod_asm(a.mres.limbs, a.mres.limbs, Fp.C.Mod.limbs)
|
||||
addmod_asm(a.mres.limbs, a.mres.limbs, FF.fieldMod().limbs)
|
||||
else:
|
||||
var overflowed = double(a.mres)
|
||||
overflowed = overflowed or not(a.mres < Fp.C.Mod)
|
||||
discard csub(a.mres, Fp.C.Mod, overflowed)
|
||||
overflowed = overflowed or not(a.mres < FF.fieldMod())
|
||||
discard csub(a.mres, FF.fieldMod(), overflowed)
|
||||
|
||||
func sum*(r: var Fp, a, b: Fp) {.inline.} =
|
||||
func sum*(r: var FF, a, b: FF) {.inline.} =
|
||||
## Sum ``a`` and ``b`` into ``r`` modulo p
|
||||
## r is initialized/overwritten
|
||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
||||
r = a
|
||||
addmod_asm(r.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs)
|
||||
addmod_asm(r.mres.limbs, b.mres.limbs, FF.fieldMod().limbs)
|
||||
else:
|
||||
var overflowed = r.mres.sum(a.mres, b.mres)
|
||||
overflowed = overflowed or not(r.mres < Fp.C.Mod)
|
||||
discard csub(r.mres, Fp.C.Mod, overflowed)
|
||||
overflowed = overflowed or not(r.mres < FF.fieldMod())
|
||||
discard csub(r.mres, FF.fieldMod(), overflowed)
|
||||
|
||||
func sumNoReduce*(r: var Fp, a, b: Fp) {.inline.} =
|
||||
func sumNoReduce*(r: var FF, a, b: FF) {.inline.} =
|
||||
## Sum ``a`` and ``b`` into ``r`` without reduction
|
||||
discard r.mres.sum(a.mres, b.mres)
|
||||
|
||||
func diff*(r: var Fp, a, b: Fp) {.inline.} =
|
||||
func diff*(r: var FF, a, b: FF) {.inline.} =
|
||||
## Substract `b` from `a` and store the result into `r`.
|
||||
## `r` is initialized/overwritten
|
||||
## Requires r != b
|
||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
||||
r = a
|
||||
submod_asm(r.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs)
|
||||
submod_asm(r.mres.limbs, b.mres.limbs, FF.fieldMod().limbs)
|
||||
else:
|
||||
var underflowed = r.mres.diff(a.mres, b.mres)
|
||||
discard cadd(r.mres, Fp.C.Mod, underflowed)
|
||||
discard cadd(r.mres, FF.fieldMod(), underflowed)
|
||||
|
||||
func diffAlias*(r: var Fp, a, b: Fp) {.inline.} =
|
||||
func diffAlias*(r: var FF, a, b: FF) {.inline.} =
|
||||
## Substract `b` from `a` and store the result into `r`.
|
||||
## `r` is initialized/overwritten
|
||||
## Handles r == b
|
||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
||||
var t = a
|
||||
submod_asm(t.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs)
|
||||
submod_asm(t.mres.limbs, b.mres.limbs, FF.fieldMod().limbs)
|
||||
r = t
|
||||
else:
|
||||
var underflowed = r.mres.diff(a.mres, b.mres)
|
||||
discard cadd(r.mres, Fp.C.Mod, underflowed)
|
||||
discard cadd(r.mres, FF.fieldMod(), underflowed)
|
||||
|
||||
func diffNoReduce*(r: var Fp, a, b: Fp) {.inline.} =
|
||||
func diffNoReduce*(r: var FF, a, b: FF) {.inline.} =
|
||||
## Substract `b` from `a` and store the result into `r`
|
||||
## without reduction
|
||||
discard r.mres.diff(a.mres, b.mres)
|
||||
|
||||
func double*(r: var Fp, a: Fp) {.inline.} =
|
||||
func double*(r: var FF, a: FF) {.inline.} =
|
||||
## Double ``a`` into ``r``
|
||||
## `r` is initialized/overwritten
|
||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
||||
r = a
|
||||
addmod_asm(r.mres.limbs, a.mres.limbs, Fp.C.Mod.limbs)
|
||||
addmod_asm(r.mres.limbs, a.mres.limbs, FF.fieldMod().limbs)
|
||||
else:
|
||||
var overflowed = r.mres.double(a.mres)
|
||||
overflowed = overflowed or not(r.mres < Fp.C.Mod)
|
||||
discard csub(r.mres, Fp.C.Mod, overflowed)
|
||||
overflowed = overflowed or not(r.mres < FF.fieldMod())
|
||||
discard csub(r.mres, FF.fieldMod(), overflowed)
|
||||
|
||||
func prod*(r: var Fp, a, b: Fp) {.inline.} =
|
||||
func prod*(r: var FF, a, b: FF) {.inline.} =
|
||||
## Store the product of ``a`` by ``b`` modulo p into ``r``
|
||||
## ``r`` is initialized / overwritten
|
||||
r.mres.montyMul(a.mres, b.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontyMul())
|
||||
r.mres.montyMul(a.mres, b.mres, FF.fieldMod(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul())
|
||||
|
||||
func square*(r: var Fp, a: Fp) {.inline.} =
|
||||
func square*(r: var FF, a: FF) {.inline.} =
|
||||
## Squaring modulo p
|
||||
r.mres.montySquare(a.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare())
|
||||
r.mres.montySquare(a.mres, FF.fieldMod(), FF.getNegInvModWord(), FF.canUseNoCarryMontySquare())
|
||||
|
||||
func neg*(r: var Fp, a: Fp) {.inline.} =
|
||||
func neg*(r: var FF, a: FF) {.inline.} =
|
||||
## Negate modulo p
|
||||
when UseASM_X86_64 and defined(gcc):
|
||||
# Clang and every compiler besides GCC
|
||||
# can cleanly optimized this
|
||||
# especially on Fp2
|
||||
negmod_asm(r.mres.limbs, a.mres.limbs, Fp.C.Mod.limbs)
|
||||
# especially on FF2
|
||||
negmod_asm(r.mres.limbs, a.mres.limbs, FF.fieldMod().limbs)
|
||||
else:
|
||||
discard r.mres.diff(Fp.C.Mod, a.mres)
|
||||
discard r.mres.diff(FF.fieldMod(), a.mres)
|
||||
|
||||
func neg*(a: var Fp) {.inline.} =
|
||||
func neg*(a: var FF) {.inline.} =
|
||||
## Negate modulo p
|
||||
a.neg(a)
|
||||
|
||||
func div2*(a: var Fp) {.inline.} =
|
||||
func div2*(a: var FF) {.inline.} =
|
||||
## Modular division by 2
|
||||
a.mres.div2_modular(Fp.C.getPrimePlus1div2())
|
||||
a.mres.div2_modular(FF.getPrimePlus1div2())
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
|
@ -243,26 +245,26 @@ func div2*(a: var Fp) {.inline.} =
|
|||
#
|
||||
# ############################################################
|
||||
|
||||
func cneg*(r: var Fp, a: Fp, ctl: SecretBool) =
|
||||
func cneg*(r: var FF, a: FF, ctl: SecretBool) =
|
||||
## Constant-time in-place conditional negation
|
||||
## The negation is only performed if ctl is "true"
|
||||
r.neg(a)
|
||||
r.ccopy(a, not ctl)
|
||||
|
||||
func cneg*(a: var Fp, ctl: SecretBool) =
|
||||
func cneg*(a: var FF, ctl: SecretBool) =
|
||||
## Constant-time in-place conditional negation
|
||||
## The negation is only performed if ctl is "true"
|
||||
var t = a
|
||||
a.cneg(t, ctl)
|
||||
|
||||
func cadd*(a: var Fp, b: Fp, ctl: SecretBool) =
|
||||
func cadd*(a: var FF, b: FF, ctl: SecretBool) =
|
||||
## Constant-time in-place conditional addition
|
||||
## The addition is only performed if ctl is "true"
|
||||
var t = a
|
||||
t += b
|
||||
a.ccopy(t, ctl)
|
||||
|
||||
func csub*(a: var Fp, b: Fp, ctl: SecretBool) =
|
||||
func csub*(a: var FF, b: FF, ctl: SecretBool) =
|
||||
## Constant-time in-place conditional substraction
|
||||
## The substraction is only performed if ctl is "true"
|
||||
var t = a
|
||||
|
@ -277,33 +279,33 @@ func csub*(a: var Fp, b: Fp, ctl: SecretBool) =
|
|||
#
|
||||
# Internally those procedures will allocate extra scratchspace on the stack
|
||||
|
||||
func pow*(a: var Fp, exponent: BigInt) {.inline.} =
|
||||
func pow*(a: var FF, exponent: BigInt) {.inline.} =
|
||||
## Exponentiation modulo p
|
||||
## ``a``: a field element to be exponentiated
|
||||
## ``exponent``: a big integer
|
||||
const windowSize = 5 # TODO: find best window size for each curves
|
||||
a.mres.montyPow(
|
||||
exponent,
|
||||
Fp.C.Mod, Fp.C.getMontyOne(),
|
||||
Fp.C.getNegInvModWord(), windowSize,
|
||||
Fp.C.canUseNoCarryMontyMul(),
|
||||
Fp.C.canUseNoCarryMontySquare()
|
||||
FF.fieldMod(), FF.getMontyOne(),
|
||||
FF.getNegInvModWord(), windowSize,
|
||||
FF.canUseNoCarryMontyMul(),
|
||||
FF.canUseNoCarryMontySquare()
|
||||
)
|
||||
|
||||
func pow*(a: var Fp, exponent: openarray[byte]) {.inline.} =
|
||||
func pow*(a: var FF, exponent: openarray[byte]) {.inline.} =
|
||||
## Exponentiation modulo p
|
||||
## ``a``: a field element to be exponentiated
|
||||
## ``exponent``: a big integer in canonical big endian representation
|
||||
const windowSize = 5 # TODO: find best window size for each curves
|
||||
a.mres.montyPow(
|
||||
exponent,
|
||||
Fp.C.Mod, Fp.C.getMontyOne(),
|
||||
Fp.C.getNegInvModWord(), windowSize,
|
||||
Fp.C.canUseNoCarryMontyMul(),
|
||||
Fp.C.canUseNoCarryMontySquare()
|
||||
FF.fieldMod(), FF.getMontyOne(),
|
||||
FF.getNegInvModWord(), windowSize,
|
||||
FF.canUseNoCarryMontyMul(),
|
||||
FF.canUseNoCarryMontySquare()
|
||||
)
|
||||
|
||||
func powUnsafeExponent*(a: var Fp, exponent: BigInt) {.inline.} =
|
||||
func powUnsafeExponent*(a: var FF, exponent: BigInt) {.inline.} =
|
||||
## Exponentiation modulo p
|
||||
## ``a``: a field element to be exponentiated
|
||||
## ``exponent``: a big integer
|
||||
|
@ -317,13 +319,13 @@ func powUnsafeExponent*(a: var Fp, exponent: BigInt) {.inline.} =
|
|||
const windowSize = 5 # TODO: find best window size for each curves
|
||||
a.mres.montyPowUnsafeExponent(
|
||||
exponent,
|
||||
Fp.C.Mod, Fp.C.getMontyOne(),
|
||||
Fp.C.getNegInvModWord(), windowSize,
|
||||
Fp.C.canUseNoCarryMontyMul(),
|
||||
Fp.C.canUseNoCarryMontySquare()
|
||||
FF.fieldMod(), FF.getMontyOne(),
|
||||
FF.getNegInvModWord(), windowSize,
|
||||
FF.canUseNoCarryMontyMul(),
|
||||
FF.canUseNoCarryMontySquare()
|
||||
)
|
||||
|
||||
func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) {.inline.} =
|
||||
func powUnsafeExponent*(a: var FF, exponent: openarray[byte]) {.inline.} =
|
||||
## Exponentiation modulo p
|
||||
## ``a``: a field element to be exponentiated
|
||||
## ``exponent``: a big integer a big integer in canonical big endian representation
|
||||
|
@ -337,10 +339,10 @@ func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) {.inline.} =
|
|||
const windowSize = 5 # TODO: find best window size for each curves
|
||||
a.mres.montyPowUnsafeExponent(
|
||||
exponent,
|
||||
Fp.C.Mod, Fp.C.getMontyOne(),
|
||||
Fp.C.getNegInvModWord(), windowSize,
|
||||
Fp.C.canUseNoCarryMontyMul(),
|
||||
Fp.C.canUseNoCarryMontySquare()
|
||||
FF.fieldMod(), FF.getMontyOne(),
|
||||
FF.getNegInvModWord(), windowSize,
|
||||
FF.canUseNoCarryMontyMul(),
|
||||
FF.canUseNoCarryMontySquare()
|
||||
)
|
||||
|
||||
# ############################################################
|
||||
|
@ -355,36 +357,36 @@ func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) {.inline.} =
|
|||
# - Those that return a field element
|
||||
# - Those that internally allocate a temporary field element
|
||||
|
||||
func `+`*(a, b: Fp): Fp {.noInit, inline.} =
|
||||
func `+`*(a, b: FF): FF {.noInit, inline.} =
|
||||
## Addition modulo p
|
||||
result.sum(a, b)
|
||||
|
||||
func `-`*(a, b: Fp): Fp {.noInit, inline.} =
|
||||
func `-`*(a, b: FF): FF {.noInit, inline.} =
|
||||
## Substraction modulo p
|
||||
result.diff(a, b)
|
||||
|
||||
func `*`*(a, b: Fp): Fp {.noInit, inline.} =
|
||||
func `*`*(a, b: FF): FF {.noInit, inline.} =
|
||||
## Multiplication modulo p
|
||||
##
|
||||
## It is recommended to assign with {.noInit.}
|
||||
## as Fp elements are usually large and this
|
||||
## as FF elements are usually large and this
|
||||
## routine will zero init internally the result.
|
||||
result.prod(a, b)
|
||||
|
||||
func `*=`*(a: var Fp, b: Fp) {.inline.} =
|
||||
func `*=`*(a: var FF, b: FF) {.inline.} =
|
||||
## Multiplication modulo p
|
||||
a.prod(a, b)
|
||||
|
||||
func square*(a: var Fp) {.inline.} =
|
||||
func square*(a: var FF) {.inline.} =
|
||||
## Squaring modulo p
|
||||
a.mres.montySquare(a.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare())
|
||||
a.mres.montySquare(a.mres, FF.fieldMod(), FF.getNegInvModWord(), FF.canUseNoCarryMontySquare())
|
||||
|
||||
func square_repeated*(r: var Fp, num: int) {.inline.} =
|
||||
func square_repeated*(r: var FF, num: int) {.inline.} =
|
||||
## Repeated squarings
|
||||
for _ in 0 ..< num:
|
||||
r.square()
|
||||
|
||||
func `*=`*(a: var Fp, b: static int) {.inline.} =
|
||||
func `*=`*(a: var FF, b: static int) {.inline.} =
|
||||
## Multiplication by a small integer known at compile-time
|
||||
# Implementation:
|
||||
# We don't want to go convert the integer to the Montgomery domain (O(n²))
|
||||
|
@ -466,7 +468,7 @@ func `*=`*(a: var Fp, b: static int) {.inline.} =
|
|||
else:
|
||||
{.error: "Multiplication by this small int not implemented".}
|
||||
|
||||
func `*`*(b: static int, a: Fp): Fp {.noinit, inline.} =
|
||||
func `*`*(b: static int, a: FF): FF {.noinit, inline.} =
|
||||
## Multiplication by a small integer known at compile-time
|
||||
result = a
|
||||
result *= b
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
../config/[common, curves, type_fp],
|
||||
../config/[common, curves, type_ff],
|
||||
../primitives,
|
||||
./bigints,
|
||||
./finite_fields,
|
||||
|
@ -38,8 +38,8 @@ func reduce*(r: var Fp, a: FpDbl) {.inline.} =
|
|||
r.mres.limbs,
|
||||
a.limbs2x,
|
||||
Fp.C.Mod.limbs,
|
||||
Fp.C.getNegInvModWord(),
|
||||
Fp.C.canUseNoCarryMontyMul()
|
||||
Fp.getNegInvModWord(),
|
||||
Fp.canUseNoCarryMontyMul()
|
||||
)
|
||||
|
||||
func diffNoInline(r: var FpDbl, a, b: FpDbl): Borrow =
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
../config/[curves, type_fp],
|
||||
../config/[curves, type_ff],
|
||||
./bigints,
|
||||
../curves/zoo_inversions
|
||||
|
||||
|
@ -23,7 +23,7 @@ func inv_euclid*(r: var Fp, a: Fp) {.inline.} =
|
|||
## Inversion modulo p via
|
||||
## Niels Moller constant-time version of
|
||||
## Stein's GCD derived from extended binary Euclid algorithm
|
||||
r.mres.steinsGCD(a.mres, Fp.C.getR2modP(), Fp.C.Mod, Fp.C.getPrimePlus1div2())
|
||||
r.mres.steinsGCD(a.mres, Fp.getR2modP(), Fp.C.Mod, Fp.getPrimePlus1div2())
|
||||
|
||||
func inv*(r: var Fp, a: Fp) {.inline.} =
|
||||
## Inversion modulo p
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
../primitives,
|
||||
../config/[common, type_fp, curves],
|
||||
../config/[common, type_ff, curves],
|
||||
../curves/zoo_square_roots,
|
||||
./bigints, ./finite_fields
|
||||
|
||||
|
@ -21,7 +21,7 @@ import
|
|||
# Legendre symbol / Euler's Criterion / Kronecker's symbol
|
||||
# ------------------------------------------------------------
|
||||
|
||||
func isSquare*[C](a: Fp[C]): SecretBool {.inline.} =
|
||||
func isSquare*(a: Fp): SecretBool {.inline.} =
|
||||
## Returns true if ``a`` is a square (quadratic residue) in 𝔽p
|
||||
##
|
||||
## Assumes that the prime modulus ``p`` is public.
|
||||
|
@ -30,7 +30,7 @@ func isSquare*[C](a: Fp[C]): SecretBool {.inline.} =
|
|||
# Note that we don't care about leaking the bits of p
|
||||
# as we assume that
|
||||
var xi {.noInit.} = a # TODO: is noInit necessary? see https://github.com/mratsim/constantine/issues/21
|
||||
xi.powUnsafeExponent(C.getPrimeMinus1div2_BE())
|
||||
xi.powUnsafeExponent(Fp.getPrimeMinus1div2_BE())
|
||||
result = not(xi.isMinusOne())
|
||||
# xi can be:
|
||||
# - 1 if a square
|
||||
|
@ -46,7 +46,7 @@ func isSquare*[C](a: Fp[C]): SecretBool {.inline.} =
|
|||
# Specialized routine for p ≡ 3 (mod 4)
|
||||
# ------------------------------------------------------------
|
||||
|
||||
func sqrt_p3mod4[C](a: var Fp[C]) {.inline.} =
|
||||
func sqrt_p3mod4(a: var Fp) {.inline.} =
|
||||
## Compute the square root of ``a``
|
||||
##
|
||||
## This requires ``a`` to be a square
|
||||
|
@ -57,10 +57,10 @@ func sqrt_p3mod4[C](a: var Fp[C]) {.inline.} =
|
|||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
static: doAssert BaseType(C.Mod.limbs[0]) mod 4 == 3
|
||||
a.powUnsafeExponent(C.getPrimePlus1div4_BE())
|
||||
static: doAssert BaseType(Fp.C.Mod.limbs[0]) mod 4 == 3
|
||||
a.powUnsafeExponent(Fp.getPrimePlus1div4_BE())
|
||||
|
||||
func sqrt_invsqrt_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) {.inline.} =
|
||||
func sqrt_invsqrt_p3mod4(sqrt, invsqrt: var Fp, a: Fp) {.inline.} =
|
||||
## If ``a`` is a square, compute the square root of ``a`` in sqrt
|
||||
## and the inverse square root of a in invsqrt
|
||||
##
|
||||
|
@ -74,14 +74,14 @@ func sqrt_invsqrt_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) {.inline.} =
|
|||
# a^((p-1)/2)) * a^-1 ≡ 1/a (mod p)
|
||||
# a^((p-3)/2)) ≡ 1/a (mod p)
|
||||
# a^((p-3)/4)) ≡ 1/√a (mod p) # Requires p ≡ 3 (mod 4)
|
||||
static: doAssert BaseType(C.Mod.limbs[0]) mod 4 == 3
|
||||
static: doAssert BaseType(Fp.C.Mod.limbs[0]) mod 4 == 3
|
||||
|
||||
invsqrt = a
|
||||
invsqrt.powUnsafeExponent(C.getPrimeMinus3div4_BE())
|
||||
invsqrt.powUnsafeExponent(Fp.getPrimeMinus3div4_BE())
|
||||
# √a ≡ a * 1/√a ≡ a^((p+1)/4) (mod p)
|
||||
sqrt.prod(invsqrt, a)
|
||||
|
||||
func sqrt_invsqrt_if_square_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool {.inline.} =
|
||||
func sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt: var Fp, a: Fp): SecretBool {.inline.} =
|
||||
## If ``a`` is a square, compute the square root of ``a`` in sqrt
|
||||
## and the inverse square root of a in invsqrt
|
||||
##
|
||||
|
@ -89,11 +89,11 @@ func sqrt_invsqrt_if_square_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): Secre
|
|||
##
|
||||
## This assumes that the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
sqrt_invsqrt_p3mod4(sqrt, invsqrt, a)
|
||||
var test {.noInit.}: Fp[C]
|
||||
var test {.noInit.}: Fp
|
||||
test.square(sqrt)
|
||||
result = test == a
|
||||
|
||||
func sqrt_if_square_p3mod4[C](a: var Fp[C]): SecretBool {.inline.} =
|
||||
func sqrt_if_square_p3mod4(a: var Fp): SecretBool {.inline.} =
|
||||
## If ``a`` is a square, compute the square root of ``a``
|
||||
## if not, ``a`` is unmodified.
|
||||
##
|
||||
|
@ -104,28 +104,28 @@ func sqrt_if_square_p3mod4[C](a: var Fp[C]): SecretBool {.inline.} =
|
|||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
var sqrt {.noInit.}, invsqrt {.noInit.}: Fp[C]
|
||||
var sqrt {.noInit.}, invsqrt {.noInit.}: Fp
|
||||
result = sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt, a)
|
||||
a.ccopy(sqrt, result)
|
||||
|
||||
# Tonelli Shanks for any prime
|
||||
# ------------------------------------------------------------
|
||||
|
||||
func precompute_tonelli_shanks[C](
|
||||
a_pre_exp: var Fp[C],
|
||||
a: Fp[C]) =
|
||||
func precompute_tonelli_shanks(
|
||||
a_pre_exp: var Fp,
|
||||
a: Fp) =
|
||||
a_pre_exp = a
|
||||
a_pre_exp.powUnsafeExponent(C.tonelliShanks(exponent))
|
||||
a_pre_exp.powUnsafeExponent(Fp.C.tonelliShanks(exponent))
|
||||
|
||||
func isSquare_tonelli_shanks[C](
|
||||
a, a_pre_exp: Fp[C]): SecretBool =
|
||||
func isSquare_tonelli_shanks(
|
||||
a, a_pre_exp: Fp): SecretBool =
|
||||
## Returns if `a` is a quadratic residue
|
||||
## This uses common precomputation for
|
||||
## Tonelli-Shanks based square root and inverse square root
|
||||
##
|
||||
## a^((p-1-2^e)/(2*2^e))
|
||||
const e = C.tonelliShanks(twoAdicity)
|
||||
var r {.noInit.}: Fp[C]
|
||||
const e = Fp.C.tonelliShanks(twoAdicity)
|
||||
var r {.noInit.}: Fp
|
||||
r.square(a_pre_exp) # a^(2(q-1-2^e)/(2*2^e)) = a^((q-1)/2^e - 1)
|
||||
r *= a # a^((q-1)/2^e)
|
||||
for _ in 0 ..< e-1:
|
||||
|
@ -143,9 +143,9 @@ func isSquare_tonelli_shanks[C](
|
|||
r.isMinusOne()
|
||||
)
|
||||
|
||||
func sqrt_invsqrt_tonelli_shanks[C](
|
||||
sqrt, invsqrt: var Fp[C],
|
||||
a, a_pre_exp: Fp[C]) =
|
||||
func sqrt_invsqrt_tonelli_shanks(
|
||||
sqrt, invsqrt: var Fp,
|
||||
a, a_pre_exp: Fp) =
|
||||
## Compute the square_root and inverse_square_root
|
||||
## of `a` via constant-time Tonelli-Shanks
|
||||
##
|
||||
|
@ -153,16 +153,16 @@ func sqrt_invsqrt_tonelli_shanks[C](
|
|||
## ThItat is shared with the simultaneous isSquare routine
|
||||
template z: untyped = a_pre_exp
|
||||
template r: untyped = invsqrt
|
||||
var t {.noInit.}: Fp[C]
|
||||
const e = C.tonelliShanks(twoAdicity)
|
||||
var t {.noInit.}: Fp
|
||||
const e = Fp.C.tonelliShanks(twoAdicity)
|
||||
|
||||
t.square(z)
|
||||
t *= a
|
||||
r = z
|
||||
var b = t
|
||||
var root = C.tonelliShanks(root_of_unity)
|
||||
var root = Fp.C.tonelliShanks(root_of_unity)
|
||||
|
||||
var buf {.noInit.}: Fp[C]
|
||||
var buf {.noInit.}: Fp
|
||||
|
||||
for i in countdown(e, 2, 1):
|
||||
for j in 1 .. i-2:
|
||||
|
|
|
@ -6,191 +6,5 @@
|
|||
# * 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/macros,
|
||||
# Internal
|
||||
./type_bigint, ./common,
|
||||
./curves_declaration, ./curves_derived, ./curves_parser
|
||||
|
||||
export CurveFamily, Curve, SexticTwist
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Field properties
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
{.experimental: "dynamicBindSym".}
|
||||
|
||||
macro Mod*(C: static Curve): untyped =
|
||||
## Get the Modulus associated to a curve
|
||||
result = bindSym($C & "_Modulus")
|
||||
|
||||
template getCurveBitwidth*(C: Curve): int =
|
||||
## Returns the number of bits taken by the curve modulus
|
||||
CurveBitWidth[C]
|
||||
|
||||
template matchingBigInt*(C: static Curve): untyped =
|
||||
BigInt[CurveBitWidth[C]]
|
||||
|
||||
template family*(C: Curve): CurveFamily =
|
||||
CurveFamilies[C]
|
||||
|
||||
template matchingLimbs2x*(C: Curve): untyped =
|
||||
const N2 = wordsRequired(getCurveBitwidth(C)) * 2 # TODO upstream, not precomputing N2 breaks semcheck
|
||||
array[N2, SecretWord] # TODO upstream, using Limbs[N2] breaks semcheck
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Curve properties
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
macro getCurveOrder*(C: static Curve): untyped =
|
||||
## Get the curve order `r`
|
||||
## i.e. the number of points on the elliptic curve
|
||||
result = bindSym($C & "_Order")
|
||||
|
||||
macro getCurveOrderBitwidth*(C: static Curve): untyped =
|
||||
## Get the curve order `r`
|
||||
## i.e. the number of points on the elliptic curve
|
||||
result = nnkDotExpr.newTree(
|
||||
getAST(getCurveOrder(C)),
|
||||
ident"bits"
|
||||
)
|
||||
|
||||
macro getEquationForm*(C: static Curve): untyped =
|
||||
## Returns the equation form
|
||||
## (ShortWeierstrass, Montgomery, Twisted Edwards, Weierstrass, ...)
|
||||
result = bindSym($C & "_equation_form")
|
||||
|
||||
macro getCoefA*(C: static Curve): untyped =
|
||||
## Returns the A coefficient of the curve
|
||||
## The return type is polymorphic, it can be an int
|
||||
## or a bigInt depending on the curve
|
||||
result = bindSym($C & "_coef_A")
|
||||
|
||||
macro getCoefB*(C: static Curve): untyped =
|
||||
## Returns the B coefficient of the curve
|
||||
## The return type is polymorphic, it can be an int
|
||||
## or a bigInt depending on the curve
|
||||
result = bindSym($C & "_coef_B")
|
||||
|
||||
macro get_QNR_Fp*(C: static Curve): untyped =
|
||||
## Returns the tower extension quadratic non-residue in 𝔽p
|
||||
## i.e. a number that is not a square in 𝔽p
|
||||
result = bindSym($C & "_nonresidue_quad_fp")
|
||||
|
||||
macro get_CNR_Fp2*(C: static Curve): untyped =
|
||||
## Returns the tower extension cubic non-residue 𝔽p²
|
||||
## i.e. a number that is not a cube in 𝔽p²
|
||||
##
|
||||
## The return value is a tuple (a, b)
|
||||
## that corresponds to the number a + b𝑗
|
||||
## with 𝑗 choosen for 𝑗² - QNR_Fp == 0
|
||||
## i.e. if -1 is chosen as a quadratic non-residue 𝑗 = √-1
|
||||
## if -2 is chosen as a quadratic non-residue 𝑗 = √-2
|
||||
result = bindSym($C & "_nonresidue_cube_fp2")
|
||||
|
||||
macro getEmbeddingDegree*(C: static Curve): untyped =
|
||||
## Returns the prime embedding degree,
|
||||
## i.e. the smallest k such that r|𝑝^𝑘−1
|
||||
## equivalently 𝑝^𝑘 ≡ 1 (mod r)
|
||||
## with r the curve order and p its field modulus
|
||||
result = bindSym($C & "_embedding_degree")
|
||||
|
||||
macro getSexticTwist*(C: static Curve): untyped =
|
||||
## Returns if D-Twist or M-Twist
|
||||
result = bindSym($C & "_sexticTwist")
|
||||
|
||||
macro get_SNR_Fp2*(C: static Curve): untyped =
|
||||
## Returns the sextic non-residue in 𝔽p²
|
||||
## choosen to build the twisted curve E'(𝔽p²)
|
||||
## i.e. a number µ so that x⁶ - µ is irreducible
|
||||
result = bindSym($C & "_sexticNonResidue_fp2")
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Access precomputed derived constants in ROM
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
genDerivedConstants()
|
||||
|
||||
macro canUseNoCarryMontyMul*(C: static Curve): untyped =
|
||||
## Returns true if the Modulus is compatible with a fast
|
||||
## Montgomery multiplication that avoids many carries
|
||||
result = bindSym($C & "_CanUseNoCarryMontyMul")
|
||||
|
||||
macro canUseNoCarryMontySquare*(C: static Curve): untyped =
|
||||
## Returns true if the Modulus is compatible with a fast
|
||||
## Montgomery squaring that avoids many carries
|
||||
result = bindSym($C & "_CanUseNoCarryMontySquare")
|
||||
|
||||
macro getR2modP*(C: static Curve): untyped =
|
||||
## Get the Montgomery "R^2 mod P" constant associated to a curve field modulus
|
||||
result = bindSym($C & "_R2modP")
|
||||
|
||||
macro getNegInvModWord*(C: static Curve): untyped =
|
||||
## Get the Montgomery "-1/P[0] mod 2^Wordbitwidth" constant associated to a curve field modulus
|
||||
result = bindSym($C & "_NegInvModWord")
|
||||
|
||||
macro getMontyOne*(C: static Curve): untyped =
|
||||
## Get one in Montgomery representation (i.e. R mod P)
|
||||
result = bindSym($C & "_MontyOne")
|
||||
|
||||
macro getMontyPrimeMinus1*(C: static Curve): untyped =
|
||||
## Get (P+1) / 2 for an odd prime
|
||||
result = bindSym($C & "_MontyPrimeMinus1")
|
||||
|
||||
macro getInvModExponent*(C: static Curve): untyped =
|
||||
## Get modular inversion exponent (Modulus-2 in canonical representation)
|
||||
result = bindSym($C & "_InvModExponent")
|
||||
|
||||
macro getPrimePlus1div2*(C: static Curve): untyped =
|
||||
## Get (P+1) / 2 for an odd prime
|
||||
## Warning ⚠️: Result in canonical domain (not Montgomery)
|
||||
result = bindSym($C & "_PrimePlus1div2")
|
||||
|
||||
macro getPrimeMinus1div2_BE*(C: static Curve): untyped =
|
||||
## Get (P-1) / 2 in big-endian serialized format
|
||||
result = bindSym($C & "_PrimeMinus1div2_BE")
|
||||
|
||||
macro getPrimeMinus3div4_BE*(C: static Curve): untyped =
|
||||
## Get (P-3) / 2 in big-endian serialized format
|
||||
result = bindSym($C & "_PrimeMinus3div4_BE")
|
||||
|
||||
macro getPrimePlus1div4_BE*(C: static Curve): untyped =
|
||||
## Get (P+1) / 4 for an odd prime in big-endian serialized format
|
||||
result = bindSym($C & "_PrimePlus1div4_BE")
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Debug info printed at compile-time
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
macro debugConsts(): untyped {.used.} =
|
||||
let curves = bindSym("Curve")
|
||||
let E = curves.getImpl[2]
|
||||
|
||||
result = newStmtList()
|
||||
for i in 1 ..< E.len:
|
||||
let curve = E[i]
|
||||
let curveName = $curve
|
||||
let modulus = bindSym(curveName & "_Modulus")
|
||||
let r2modp = bindSym(curveName & "_R2modP")
|
||||
let negInvModWord = bindSym(curveName & "_NegInvModWord")
|
||||
|
||||
result.add quote do:
|
||||
echo "Curve ", `curveName`,':'
|
||||
echo " Field Modulus: ", `modulus`
|
||||
echo " Montgomery R² (mod P): ", `r2modp`
|
||||
echo " Montgomery -1/P[0] (mod 2^", WordBitWidth, "): ", `negInvModWord`
|
||||
|
||||
result.add quote do:
|
||||
echo "----------------------------------------------------------------------------"
|
||||
|
||||
# debug: # displayed with -d:debugConstantine
|
||||
# debugConsts()
|
||||
import curves_prop_core, curves_prop_derived
|
||||
export curves_prop_core, curves_prop_derived
|
||||
|
|
|
@ -15,7 +15,12 @@ import
|
|||
|
||||
{.experimental: "dynamicBindSym".}
|
||||
|
||||
macro genDerivedConstants*(): untyped =
|
||||
type
|
||||
DerivedConstantMode* = enum
|
||||
kModulus
|
||||
kOrder
|
||||
|
||||
macro genDerivedConstants*(mode: static DerivedConstantMode): untyped =
|
||||
## Generate constants derived from the main constants
|
||||
##
|
||||
## For example
|
||||
|
@ -38,87 +43,92 @@ macro genDerivedConstants*(): untyped =
|
|||
nnkPragma.newTree(ident"used")
|
||||
)
|
||||
|
||||
let ff = if mode == kModulus: "_Fp" else: "_Fr"
|
||||
|
||||
|
||||
for curveSym in low(Curve) .. high(Curve):
|
||||
let curve = $curveSym
|
||||
let M = if mode == kModulus: bindSym(curve & "_Modulus")
|
||||
else: bindSym(curve & "_Order")
|
||||
|
||||
# const MyCurve_CanUseNoCarryMontyMul = useNoCarryMontyMul(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_CanUseNoCarryMontyMul"), newCall(
|
||||
used(curve & ff & "_CanUseNoCarryMontyMul"), newCall(
|
||||
bindSym"useNoCarryMontyMul",
|
||||
bindSym(curve & "_Modulus")
|
||||
M
|
||||
)
|
||||
)
|
||||
|
||||
# const MyCurve_CanUseNoCarryMontySquare = useNoCarryMontySquare(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_CanUseNoCarryMontySquare"), newCall(
|
||||
used(curve & ff & "_CanUseNoCarryMontySquare"), newCall(
|
||||
bindSym"useNoCarryMontySquare",
|
||||
bindSym(curve & "_Modulus")
|
||||
M
|
||||
)
|
||||
)
|
||||
|
||||
# const MyCurve_R2modP = r2mod(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_R2modP"), newCall(
|
||||
used(curve & ff & "_R2modP"), newCall(
|
||||
bindSym"r2mod",
|
||||
bindSym(curve & "_Modulus")
|
||||
M
|
||||
)
|
||||
)
|
||||
|
||||
# const MyCurve_NegInvModWord = negInvModWord(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_NegInvModWord"), newCall(
|
||||
used(curve & ff & "_NegInvModWord"), newCall(
|
||||
bindSym"negInvModWord",
|
||||
bindSym(curve & "_Modulus")
|
||||
M
|
||||
)
|
||||
)
|
||||
# const MyCurve_montyOne = montyOne(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_MontyOne"), newCall(
|
||||
used(curve & ff & "_MontyOne"), newCall(
|
||||
bindSym"montyOne",
|
||||
bindSym(curve & "_Modulus")
|
||||
M
|
||||
)
|
||||
)
|
||||
# const MyCurve_MontyPrimeMinus1 = montyPrimeMinus1(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_MontyPrimeMinus1"), newCall(
|
||||
used(curve & ff & "_MontyPrimeMinus1"), newCall(
|
||||
bindSym"montyPrimeMinus1",
|
||||
bindSym(curve & "_Modulus")
|
||||
M
|
||||
)
|
||||
)
|
||||
# const MyCurve_InvModExponent = primeMinus2_BE(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_InvModExponent"), newCall(
|
||||
used(curve & ff & "_InvModExponent"), newCall(
|
||||
bindSym"primeMinus2_BE",
|
||||
bindSym(curve & "_Modulus")
|
||||
M
|
||||
)
|
||||
)
|
||||
# const MyCurve_PrimePlus1div2 = primePlus1div2(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_PrimePlus1div2"), newCall(
|
||||
used(curve & ff & "_PrimePlus1div2"), newCall(
|
||||
bindSym"primePlus1div2",
|
||||
bindSym(curve & "_Modulus")
|
||||
M
|
||||
)
|
||||
)
|
||||
# const MyCurve_PrimeMinus1div2_BE = primeMinus1div2_BE(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_PrimeMinus1div2_BE"), newCall(
|
||||
used(curve & ff & "_PrimeMinus1div2_BE"), newCall(
|
||||
bindSym"primeMinus1div2_BE",
|
||||
bindSym(curve & "_Modulus")
|
||||
M
|
||||
)
|
||||
)
|
||||
# const MyCurve_PrimeMinus3div4_BE = primeMinus3div4_BE(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_PrimeMinus3div4_BE"), newCall(
|
||||
used(curve & ff & "_PrimeMinus3div4_BE"), newCall(
|
||||
bindSym"primeMinus3div4_BE",
|
||||
bindSym(curve & "_Modulus")
|
||||
M
|
||||
)
|
||||
)
|
||||
# const MyCurve_PrimePlus1div4_BE = primePlus1div4_BE(MyCurve_Modulus)
|
||||
result.add newConstStmt(
|
||||
used(curve & "_PrimePlus1div4_BE"), newCall(
|
||||
used(curve & ff & "_PrimePlus1div4_BE"), newCall(
|
||||
bindSym"primePlus1div4_BE",
|
||||
bindSym(curve & "_Modulus")
|
||||
M
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -237,8 +237,12 @@ template getCoef(c: CurveCoef, width: NimNode): untyped {.dirty.}=
|
|||
proc genMainConstants(defs: var seq[CurveParams]): NimNode =
|
||||
## Generate curves and fields main constants
|
||||
|
||||
# MapCurveBitWidth & MapCurveOrderBitWidth
|
||||
# are workaround for https://github.com/nim-lang/Nim/issues/16774
|
||||
|
||||
var Curves: seq[NimNode]
|
||||
var MapCurveBitWidth = nnkBracket.newTree()
|
||||
var MapCurveOrderBitWidth = nnkBracket.newTree()
|
||||
var MapCurveFamily = nnkBracket.newTree()
|
||||
var curveModStmts = newStmtList()
|
||||
var curveEllipticStmts = newStmtList()
|
||||
|
@ -290,6 +294,22 @@ proc genMainConstants(defs: var seq[CurveParams]): NimNode =
|
|||
curveDef.order
|
||||
)
|
||||
)
|
||||
MapCurveOrderBitWidth.add nnkExprColonExpr.newTree(
|
||||
curve, curveDef.orderBitwidth
|
||||
)
|
||||
else: # Dummy
|
||||
curveEllipticStmts.add newConstStmt(
|
||||
exported($curve & "_Order"),
|
||||
newCall(
|
||||
bindSym"fromHex",
|
||||
nnkBracketExpr.newTree(bindSym"BigInt", newLit 1),
|
||||
newLit"0x1"
|
||||
)
|
||||
)
|
||||
MapCurveOrderBitWidth.add nnkExprColonExpr.newTree(
|
||||
curve, newLit 1
|
||||
)
|
||||
|
||||
if curveDef.coef_A.kind != NoCoef and curveDef.coef_B.kind != NoCoef:
|
||||
curveEllipticStmts.add newConstStmt(
|
||||
exported($curve & "_coef_A"),
|
||||
|
@ -347,6 +367,10 @@ proc genMainConstants(defs: var seq[CurveParams]): NimNode =
|
|||
result.add newConstStmt(
|
||||
exported("CurveFamilies"), MapCurveFamily
|
||||
)
|
||||
# const CurveOrderBitSize: array[Curve, int] = ...
|
||||
result.add newConstStmt(
|
||||
exported("CurveOrderBitWidth"), MapCurveOrderBitWidth
|
||||
)
|
||||
|
||||
result.add curveModStmts
|
||||
result.add curveEllipticStmts
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
# 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/macros,
|
||||
# Internal
|
||||
./type_bigint, ./common,
|
||||
./curves_declaration, ./curves_parser
|
||||
|
||||
export CurveFamily, Curve, SexticTwist
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Field properties
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
{.experimental: "dynamicBindSym".}
|
||||
|
||||
macro Mod*(C: static Curve): untyped =
|
||||
## Get the Modulus associated to a curve
|
||||
result = bindSym($C & "_Modulus")
|
||||
|
||||
template getCurveBitwidth*(C: Curve): int =
|
||||
## Returns the number of bits taken by the curve modulus
|
||||
CurveBitWidth[C]
|
||||
|
||||
template matchingBigInt*(C: static Curve): untyped =
|
||||
# Workaround: https://github.com/nim-lang/Nim/issues/16774
|
||||
BigInt[CurveBitWidth[C]]
|
||||
|
||||
template family*(C: Curve): CurveFamily =
|
||||
CurveFamilies[C]
|
||||
|
||||
template matchingLimbs2x*(C: Curve): untyped =
|
||||
const N2 = wordsRequired(getCurveBitwidth(C)) * 2 # TODO upstream, not precomputing N2 breaks semcheck
|
||||
array[N2, SecretWord] # TODO upstream, using Limbs[N2] breaks semcheck
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Curve properties
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
macro getCurveOrder*(C: static Curve): untyped =
|
||||
## Get the curve order `r`
|
||||
## i.e. the number of points on the elliptic curve
|
||||
result = bindSym($C & "_Order")
|
||||
|
||||
macro getCurveOrderBitwidth*(C: static Curve): untyped =
|
||||
## Get the curve order `r`
|
||||
## i.e. the number of points on the elliptic curve
|
||||
result = nnkDotExpr.newTree(
|
||||
getAST(getCurveOrder(C)),
|
||||
ident"bits"
|
||||
)
|
||||
|
||||
template matchingOrderBigInt*(C: static Curve): untyped =
|
||||
# Workaround: https://github.com/nim-lang/Nim/issues/16774
|
||||
BigInt[CurveOrderBitWidth[C]]
|
||||
|
||||
macro getEquationForm*(C: static Curve): untyped =
|
||||
## Returns the equation form
|
||||
## (ShortWeierstrass, Montgomery, Twisted Edwards, Weierstrass, ...)
|
||||
result = bindSym($C & "_equation_form")
|
||||
|
||||
macro getCoefA*(C: static Curve): untyped =
|
||||
## Returns the A coefficient of the curve
|
||||
## The return type is polymorphic, it can be an int
|
||||
## or a bigInt depending on the curve
|
||||
result = bindSym($C & "_coef_A")
|
||||
|
||||
macro getCoefB*(C: static Curve): untyped =
|
||||
## Returns the B coefficient of the curve
|
||||
## The return type is polymorphic, it can be an int
|
||||
## or a bigInt depending on the curve
|
||||
result = bindSym($C & "_coef_B")
|
||||
|
||||
macro get_QNR_Fp*(C: static Curve): untyped =
|
||||
## Returns the tower extension quadratic non-residue in 𝔽p
|
||||
## i.e. a number that is not a square in 𝔽p
|
||||
result = bindSym($C & "_nonresidue_quad_fp")
|
||||
|
||||
macro get_CNR_Fp2*(C: static Curve): untyped =
|
||||
## Returns the tower extension cubic non-residue 𝔽p²
|
||||
## i.e. a number that is not a cube in 𝔽p²
|
||||
##
|
||||
## The return value is a tuple (a, b)
|
||||
## that corresponds to the number a + b𝑗
|
||||
## with 𝑗 choosen for 𝑗² - QNR_Fp == 0
|
||||
## i.e. if -1 is chosen as a quadratic non-residue 𝑗 = √-1
|
||||
## if -2 is chosen as a quadratic non-residue 𝑗 = √-2
|
||||
result = bindSym($C & "_nonresidue_cube_fp2")
|
||||
|
||||
macro getEmbeddingDegree*(C: static Curve): untyped =
|
||||
## Returns the prime embedding degree,
|
||||
## i.e. the smallest k such that r|𝑝^𝑘−1
|
||||
## equivalently 𝑝^𝑘 ≡ 1 (mod r)
|
||||
## with r the curve order and p its field modulus
|
||||
result = bindSym($C & "_embedding_degree")
|
||||
|
||||
macro getSexticTwist*(C: static Curve): untyped =
|
||||
## Returns if D-Twist or M-Twist
|
||||
result = bindSym($C & "_sexticTwist")
|
||||
|
||||
macro get_SNR_Fp2*(C: static Curve): untyped =
|
||||
## Returns the sextic non-residue in 𝔽p²
|
||||
## choosen to build the twisted curve E'(𝔽p²)
|
||||
## i.e. a number µ so that x⁶ - µ is irreducible
|
||||
result = bindSym($C & "_sexticNonResidue_fp2")
|
|
@ -0,0 +1,139 @@
|
|||
# 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/macros,
|
||||
# Internal
|
||||
./type_bigint, ./type_ff, ./common,
|
||||
./curves_declaration, ./curves_derived
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Access precomputed derived constants in ROM
|
||||
#
|
||||
# ############################################################
|
||||
{.experimental: "dynamicBindSym".}
|
||||
|
||||
genDerivedConstants(kModulus)
|
||||
genDerivedConstants(kOrder)
|
||||
|
||||
proc bindConstant(ff: NimNode, property: string): NimNode =
|
||||
# Need to workaround https://github.com/nim-lang/Nim/issues/14021
|
||||
# which prevents checking if a type FF[C] = Fp[C] or Fr[C]
|
||||
# was instantiated with Fp or Fr.
|
||||
# getTypeInst only returns FF and sameType doesn't work.
|
||||
# so quote do + when checks.
|
||||
let T = getTypeInst(ff)
|
||||
T.expectKind(nnkBracketExpr)
|
||||
doAssert T[0].eqIdent("typedesc")
|
||||
|
||||
if T[1].kind == nnkBracketExpr: # typedesc[Fp[BLS12_381]]
|
||||
# doAssert T[1][0].eqIdent"Fp" or T[1][0].eqIdent"Fr", "Found ident: '" & $T[1][0] & "' instead of 'Fp' or 'Fr'"
|
||||
|
||||
T[1][1].expectKind(nnkIntLit) # static enum are ints in the VM
|
||||
|
||||
let curve = $Curve(T[1][1].intVal)
|
||||
let curve_fp = bindSym(curve & "_Fp_" & property)
|
||||
let curve_fr = bindSym(curve & "_Fr_" & property)
|
||||
result = quote do:
|
||||
when `ff` is Fp:
|
||||
`curve_fp`
|
||||
elif `ff` is Fr:
|
||||
`curve_fr`
|
||||
else:
|
||||
{.error: "Unreachable, received type: " & $`ff`.}
|
||||
|
||||
else:
|
||||
echo T.repr()
|
||||
echo getTypeInst(T[1]).treerepr
|
||||
error "getTypeInst didn't return the full instantiation." &
|
||||
" Dealing with types in macros is hard, complain at https://github.com/nim-lang/RFCs/issues/44"
|
||||
|
||||
template fieldMod*(Field: type FF): auto =
|
||||
when Field is Fp:
|
||||
Field.C.Mod
|
||||
else:
|
||||
Field.C.getCurveOrder()
|
||||
|
||||
macro canUseNoCarryMontyMul*(ff: type FF): untyped =
|
||||
## Returns true if the Modulus is compatible with a fast
|
||||
## Montgomery multiplication that avoids many carries
|
||||
result = bindConstant(ff, "CanUseNoCarryMontyMul")
|
||||
|
||||
macro canUseNoCarryMontySquare*(ff: type FF): untyped =
|
||||
## Returns true if the Modulus is compatible with a fast
|
||||
## Montgomery squaring that avoids many carries
|
||||
result = bindConstant(ff, "CanUseNoCarryMontySquare")
|
||||
|
||||
macro getR2modP*(ff: type FF): untyped =
|
||||
## Get the Montgomery "R^2 mod P" constant associated to a curve field modulus
|
||||
result = bindConstant(ff, "R2modP")
|
||||
|
||||
macro getNegInvModWord*(ff: type FF): untyped =
|
||||
## Get the Montgomery "-1/P[0] mod 2^Wordbitwidth" constant associated to a curve field modulus
|
||||
result = bindConstant(ff, "NegInvModWord")
|
||||
|
||||
macro getMontyOne*(ff: type FF): untyped =
|
||||
## Get one in Montgomery representation (i.e. R mod P)
|
||||
result = bindConstant(ff, "MontyOne")
|
||||
|
||||
macro getMontyPrimeMinus1*(ff: type FF): untyped =
|
||||
## Get (P+1) / 2 for an odd prime
|
||||
result = bindConstant(ff, "MontyPrimeMinus1")
|
||||
|
||||
macro getInvModExponent*(ff: type FF): untyped =
|
||||
## Get modular inversion exponent (Modulus-2 in canonical representation)
|
||||
result = bindConstant(ff, "InvModExponent")
|
||||
|
||||
macro getPrimePlus1div2*(ff: type FF): untyped =
|
||||
## Get (P+1) / 2 for an odd prime
|
||||
## Warning ⚠️: Result in canonical domain (not Montgomery)
|
||||
result = bindConstant(ff, "PrimePlus1div2")
|
||||
|
||||
macro getPrimeMinus1div2_BE*(ff: type FF): untyped =
|
||||
## Get (P-1) / 2 in big-endian serialized format
|
||||
result = bindConstant(ff, "PrimeMinus1div2_BE")
|
||||
|
||||
macro getPrimeMinus3div4_BE*(ff: type FF): untyped =
|
||||
## Get (P-3) / 2 in big-endian serialized format
|
||||
result = bindConstant(ff, "PrimeMinus3div4_BE")
|
||||
|
||||
macro getPrimePlus1div4_BE*(ff: type FF): untyped =
|
||||
## Get (P+1) / 4 for an odd prime in big-endian serialized format
|
||||
result = bindConstant(ff, "PrimePlus1div4_BE")
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Debug info printed at compile-time
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
macro debugConsts(): untyped {.used.} =
|
||||
let curves = bindSym("Curve")
|
||||
let E = curves.getImpl[2]
|
||||
|
||||
result = newStmtList()
|
||||
for i in 1 ..< E.len:
|
||||
let curve = E[i]
|
||||
let curveName = $curve
|
||||
let modulus = bindSym(curveName & "_Fp_Modulus")
|
||||
let r2modp = bindSym(curveName & "_Fp_R2modP")
|
||||
let negInvModWord = bindSym(curveName & "_Fp_NegInvModWord")
|
||||
|
||||
result.add quote do:
|
||||
echo "Curve ", `curveName`,':'
|
||||
echo " Field Modulus: ", `modulus`
|
||||
echo " Montgomery R² (mod P): ", `r2modp`
|
||||
echo " Montgomery -1/P[0] (mod 2^", WordBitWidth, "): ", `negInvModWord`
|
||||
|
||||
result.add quote do:
|
||||
echo "----------------------------------------------------------------------------"
|
||||
|
||||
# debug: # displayed with -d:debugConstantine
|
||||
# debugConsts()
|
|
@ -12,12 +12,21 @@ import
|
|||
|
||||
type
|
||||
Fp*[C: static Curve] = object
|
||||
## All operations on a field are modulo P
|
||||
## All operations on a Fp field are modulo P
|
||||
## P being the prime modulus of the Curve C
|
||||
## Internally, data is stored in Montgomery n-residue form
|
||||
## with the magic constant chosen for convenient division (a power of 2 depending on P bitsize)
|
||||
mres*: matchingBigInt(C)
|
||||
|
||||
Fr*[C: static Curve] = object
|
||||
## All operations on a field are modulo `r`
|
||||
## `r` being the prime curve order or subgroup order
|
||||
## Internally, data is stored in Montgomery n-residue form
|
||||
## with the magic constant chosen for convenient division (a power of 2 depending on P bitsize)
|
||||
mres*: matchingOrderBigInt(C)
|
||||
|
||||
FF*[C: static Curve] = Fp[C] or Fr[C]
|
||||
|
||||
debug:
|
||||
import ./type_bigint
|
||||
|
||||
|
@ -26,3 +35,9 @@ debug:
|
|||
result.add "]("
|
||||
result.add $a.mres
|
||||
result.add ')'
|
||||
|
||||
func `$`*[C: static Curve](a: Fr[C]): string =
|
||||
result = "Fr[" & $C
|
||||
result.add "]("
|
||||
result.add $a.mres
|
||||
result.add ')'
|
|
@ -7,7 +7,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
../config/[curves, type_bigint, type_fp],
|
||||
../config/[curves, type_bigint, type_ff],
|
||||
../io/[io_bigints, io_fields]
|
||||
|
||||
# BLS12_377 G1
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
../config/[curves, type_bigint, type_fp],
|
||||
../config/[curves, type_bigint, type_ff],
|
||||
../io/[io_bigints, io_fields]
|
||||
|
||||
const
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
../config/[curves, type_bigint, type_fp],
|
||||
../config/[curves, type_bigint, type_ff],
|
||||
../io/[io_bigints, io_fields]
|
||||
|
||||
# BLS12_381 G1
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
../config/[curves, type_bigint, type_fp],
|
||||
../config/[curves, type_bigint, type_ff],
|
||||
../io/[io_bigints, io_fields]
|
||||
|
||||
# BN254_Nogami G1
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
../config/[curves, type_bigint, type_fp],
|
||||
../config/[curves, type_bigint, type_ff],
|
||||
../io/[io_bigints, io_fields]
|
||||
|
||||
# BN254_Snarks G1
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
../config/[curves, type_fp],
|
||||
../config/[curves, type_ff],
|
||||
../towers,
|
||||
../io/[io_fields, io_towers]
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
../config/[curves, type_bigint, type_fp],
|
||||
../config/[curves, type_bigint, type_ff],
|
||||
../io/[io_bigints, io_fields]
|
||||
|
||||
# BW6_761 G1
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
std/macros,
|
||||
../config/[curves, type_fp],
|
||||
../config/[curves, type_ff],
|
||||
../towers,
|
||||
./bls12_377_glv,
|
||||
./bls12_381_glv,
|
||||
|
|
|
@ -22,18 +22,18 @@ import
|
|||
#
|
||||
# ############################################################
|
||||
|
||||
func fromUint*(dst: var Fp,
|
||||
func fromUint*(dst: var FF,
|
||||
src: SomeUnsignedInt) =
|
||||
## Parse a regular unsigned integer
|
||||
## and store it into a Fp
|
||||
let raw {.noinit.} = (type dst.mres).fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian)
|
||||
## and store it into a Fp or Fr
|
||||
let raw {.noinit.} = (typeof dst.mres).fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian)
|
||||
dst.fromBig(raw)
|
||||
|
||||
func fromInt*(dst: var Fp,
|
||||
func fromInt*(dst: var FF,
|
||||
src: SomeInteger) =
|
||||
## Parse a regular signed integer
|
||||
## and store it into a Fp
|
||||
## A negative integer will be instantiated as a negated number (mod p)
|
||||
## and store it into a Fp or Fr
|
||||
## A negative integer will be instantiated as a negated number (mod p) or (mod r)
|
||||
when src is SomeUnsignedInt:
|
||||
dst.fromUint(src)
|
||||
else:
|
||||
|
@ -45,7 +45,7 @@ func fromInt*(dst: var Fp,
|
|||
dst.fromBig(raw)
|
||||
|
||||
func exportRawUint*(dst: var openarray[byte],
|
||||
src: Fp,
|
||||
src: FF,
|
||||
dstEndianness: static Endianness) =
|
||||
## Serialize a finite field element to its canonical big-endian or little-endian
|
||||
## representation
|
||||
|
@ -58,7 +58,7 @@ func exportRawUint*(dst: var openarray[byte],
|
|||
## I.e least significant bit is aligned to buffer boundary
|
||||
exportRawUint(dst, src.toBig(), dstEndianness)
|
||||
|
||||
func appendHex*(dst: var string, f: Fp, order: static Endianness = bigEndian) =
|
||||
func appendHex*(dst: var string, f: FF, order: static Endianness = bigEndian) =
|
||||
## Stringify a finite field element to hex.
|
||||
## Note. Leading zeros are not removed.
|
||||
## Result is prefixed with 0x
|
||||
|
@ -69,7 +69,7 @@ func appendHex*(dst: var string, f: Fp, order: static Endianness = bigEndian) =
|
|||
## - no leaks
|
||||
dst.appendHex(f.toBig(), order)
|
||||
|
||||
func toHex*(f: Fp, order: static Endianness = bigEndian): string =
|
||||
func toHex*(f: FF, order: static Endianness = bigEndian): string =
|
||||
## Stringify a finite field element to hex.
|
||||
## Note. Leading zeros are not removed.
|
||||
## Result is prefixed with 0x
|
||||
|
@ -80,11 +80,11 @@ func toHex*(f: Fp, order: static Endianness = bigEndian): string =
|
|||
## - no leaks
|
||||
result.appendHex(f, order)
|
||||
|
||||
func fromHex*(dst: var Fp, hexString: string) {.raises: [ValueError].}=
|
||||
## Convert a hex string to a element of Fp
|
||||
func fromHex*(dst: var FF, hexString: string) {.raises: [ValueError].}=
|
||||
## Convert a hex string to a element of Fp or Fr
|
||||
let raw {.noinit.} = fromHex(dst.mres.typeof, hexString)
|
||||
dst.fromBig(raw)
|
||||
|
||||
func fromHex*(T: type Fp, hexString: string): T {.noInit, raises: [ValueError].}=
|
||||
func fromHex*(T: type FF, hexString: string): T {.noInit, raises: [ValueError].}=
|
||||
## Convert a hex string to a element of Fp
|
||||
result.fromHex(hexString)
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
../config/[curves, type_fp],
|
||||
../config/[curves, type_ff],
|
||||
../towers,
|
||||
../elliptic/[
|
||||
ec_shortweierstrass_affine,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
../config/[curves, type_fp],
|
||||
../config/[curves, type_ff],
|
||||
../towers,
|
||||
../elliptic/[
|
||||
ec_shortweierstrass_affine,
|
||||
|
|
|
@ -105,7 +105,7 @@ func prod_complex(r: var QuadraticExt, a, b: QuadraticExt) =
|
|||
# Deactivated for now Clang 360 cycles on i9-9980XE @4.1 GHz
|
||||
var a0b0 {.noInit.}, a1b1 {.noInit.}: doubleWidth(typeof(r.c0))
|
||||
var d {.noInit.}: doubleWidth(typeof(r.c0))
|
||||
const msbSet = r.c0.typeof.C.canUseNoCarryMontyMul()
|
||||
const msbSet = r.c0.typeof.canUseNoCarryMontyMul()
|
||||
|
||||
a0b0.mulNoReduce(a.c0, b.c0) # 44 cycles - cumul 44
|
||||
a1b1.mulNoReduce(a.c1, b.c1) # 44 cycles - cumul 88
|
||||
|
|
|
@ -9,12 +9,13 @@
|
|||
import
|
||||
../constantine/arithmetic/bigints,
|
||||
../constantine/primitives,
|
||||
../constantine/config/[common, curves],
|
||||
../constantine/config/[common, curves, type_ff],
|
||||
../constantine/elliptic/[
|
||||
ec_shortweierstrass_affine,
|
||||
ec_shortweierstrass_projective,
|
||||
ec_shortweierstrass_jacobian],
|
||||
../constantine/io/io_bigints
|
||||
../constantine/io/io_bigints,
|
||||
../constantine/tower_field_extensions/tower_common
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
|
@ -137,20 +138,21 @@ func random_unsafe(rng: var RngState, a: var BigInt) =
|
|||
for i in 0 ..< a.limbs.len:
|
||||
a.limbs[i] = SecretWord(rng.next())
|
||||
|
||||
func random_unsafe[T](rng: var RngState, a: var T, C: static Curve) =
|
||||
## Recursively initialize a BigInt (part of a field) or Field element
|
||||
func random_unsafe(rng: var RngState, a: var FF) =
|
||||
## Initialize a Field element
|
||||
## Unsafe: for testing and benchmarking purposes only
|
||||
when T is BigInt:
|
||||
var reduced, unreduced{.noInit.}: T
|
||||
var reduced, unreduced{.noInit.}: typeof(a.mres)
|
||||
rng.random_unsafe(unreduced)
|
||||
|
||||
# Note: a simple modulo will be biaised but it's simple and "fast"
|
||||
reduced.reduce(unreduced, C.Mod)
|
||||
a.montyResidue(reduced, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul())
|
||||
reduced.reduce(unreduced, FF.fieldMod())
|
||||
a.mres.montyResidue(reduced, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul())
|
||||
|
||||
else:
|
||||
func random_unsafe(rng: var RngState, a: var ExtensionField) =
|
||||
## Recursively initialize an extension Field element
|
||||
## Unsafe: for testing and benchmarking purposes only
|
||||
for field in fields(a):
|
||||
rng.random_unsafe(field, C)
|
||||
rng.random_unsafe(field)
|
||||
|
||||
func random_word_highHammingWeight(rng: var RngState): BaseType =
|
||||
let numZeros = rng.random_unsafe(WordBitWidth div 3) # Average Hamming Weight is 1-0.33/2 = 0.83
|
||||
|
@ -165,22 +167,23 @@ func random_highHammingWeight(rng: var RngState, a: var BigInt) =
|
|||
for i in 0 ..< a.limbs.len:
|
||||
a.limbs[i] = SecretWord rng.random_word_highHammingWeight()
|
||||
|
||||
func random_highHammingWeight[T](rng: var RngState, a: var T, C: static Curve) =
|
||||
func random_highHammingWeight(rng: var RngState, a: var FF) =
|
||||
## Recursively initialize a BigInt (part of a field) or Field element
|
||||
## Unsafe: for testing and benchmarking purposes only
|
||||
## The result will have a high Hamming Weight
|
||||
## to have a higher probability of triggering carries
|
||||
when T is BigInt:
|
||||
var reduced, unreduced{.noInit.}: T
|
||||
var reduced, unreduced{.noInit.}: typeof(a.mres)
|
||||
rng.random_highHammingWeight(unreduced)
|
||||
|
||||
# Note: a simple modulo will be biaised but it's simple and "fast"
|
||||
reduced.reduce(unreduced, C.Mod)
|
||||
a.montyResidue(reduced, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul())
|
||||
reduced.reduce(unreduced, FF.fieldMod())
|
||||
a.mres.montyResidue(reduced, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul())
|
||||
|
||||
else:
|
||||
func random_highHammingWeight(rng: var RngState, a: var ExtensionField) =
|
||||
## Recursively initialize an extension Field element
|
||||
## Unsafe: for testing and benchmarking purposes only
|
||||
for field in fields(a):
|
||||
rng.random_highHammingWeight(field, C)
|
||||
rng.random_highHammingWeight(field)
|
||||
|
||||
func random_long01Seq(rng: var RngState, a: var openArray[byte]) =
|
||||
## Initialize a bytearray
|
||||
|
@ -210,21 +213,22 @@ func random_long01Seq(rng: var RngState, a: var BigInt) =
|
|||
else:
|
||||
a.fromRawUint(buf, littleEndian)
|
||||
|
||||
func random_long01Seq[T](rng: var RngState, a: var T, C: static Curve) =
|
||||
func random_long01Seq(rng: var RngState, a: var FF) =
|
||||
## Recursively initialize a BigInt (part of a field) or Field element
|
||||
## It is skewed towards producing strings of 1111... and 0000
|
||||
## to trigger edge cases
|
||||
when T is BigInt:
|
||||
var reduced, unreduced{.noInit.}: T
|
||||
var reduced, unreduced{.noInit.}: typeof(a.mres)
|
||||
rng.random_long01Seq(unreduced)
|
||||
|
||||
# Note: a simple modulo will be biaised but it's simple and "fast"
|
||||
reduced.reduce(unreduced, C.Mod)
|
||||
a.montyResidue(reduced, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul())
|
||||
reduced.reduce(unreduced, FF.fieldMod())
|
||||
a.mres.montyResidue(reduced, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul())
|
||||
|
||||
else:
|
||||
func random_long01Seq(rng: var RngState, a: var ExtensionField) =
|
||||
## Recursively initialize an extension Field element
|
||||
## Unsafe: for testing and benchmarking purposes only
|
||||
for field in fields(a):
|
||||
rng.random_highHammingWeight(field, C)
|
||||
rng.random_long01Seq(field)
|
||||
|
||||
# Elliptic curves
|
||||
# ------------------------------------------------------------
|
||||
|
@ -238,20 +242,20 @@ func random_unsafe(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Aff
|
|||
while not bool(success):
|
||||
# Euler's criterion: there are (p-1)/2 squares in a field with modulus `p`
|
||||
# so we have a probability of ~0.5 to get a good point
|
||||
rng.random_unsafe(fieldElem, a.F.C)
|
||||
rng.random_unsafe(fieldElem)
|
||||
success = trySetFromCoordX(a, fieldElem)
|
||||
|
||||
func random_unsafe_with_randZ(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Jac)) =
|
||||
## Initialize a random curve point with Z coordinate being random
|
||||
## Unsafe: for testing and benchmarking purposes only
|
||||
var Z{.noInit.}: a.F
|
||||
rng.random_unsafe(Z, a.F.C) # If Z is zero, X will be zero and that will be an infinity point
|
||||
rng.random_unsafe(Z) # If Z is zero, X will be zero and that will be an infinity point
|
||||
|
||||
var fieldElem {.noInit.}: a.F
|
||||
var success = CtFalse
|
||||
|
||||
while not bool(success):
|
||||
rng.random_unsafe(fieldElem, a.F.C)
|
||||
rng.random_unsafe(fieldElem)
|
||||
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
||||
|
||||
func random_highHammingWeight(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Aff or ECP_ShortW_Jac)) =
|
||||
|
@ -264,7 +268,7 @@ func random_highHammingWeight(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_
|
|||
while not bool(success):
|
||||
# Euler's criterion: there are (p-1)/2 squares in a field with modulus `p`
|
||||
# so we have a probability of ~0.5 to get a good point
|
||||
rng.random_highHammingWeight(fieldElem, a.F.C)
|
||||
rng.random_highHammingWeight(fieldElem)
|
||||
success = trySetFromCoordX(a, fieldElem)
|
||||
|
||||
func random_highHammingWeight_with_randZ(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Jac)) =
|
||||
|
@ -272,13 +276,13 @@ func random_highHammingWeight_with_randZ(rng: var RngState, a: var (ECP_ShortW_P
|
|||
## This will be generated with a biaised RNG with high Hamming Weight
|
||||
## to trigger carry bugs
|
||||
var Z{.noInit.}: a.F
|
||||
rng.random_highHammingWeight(Z, a.F.C) # If Z is zero, X will be zero and that will be an infinity point
|
||||
rng.random_highHammingWeight(Z) # If Z is zero, X will be zero and that will be an infinity point
|
||||
|
||||
var fieldElem {.noInit.}: a.F
|
||||
var success = CtFalse
|
||||
|
||||
while not bool(success):
|
||||
rng.random_highHammingWeight(fieldElem, a.F.C)
|
||||
rng.random_highHammingWeight(fieldElem)
|
||||
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
||||
|
||||
func random_long01Seq(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Aff or ECP_ShortW_Jac)) =
|
||||
|
@ -292,7 +296,7 @@ func random_long01Seq(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_A
|
|||
while not bool(success):
|
||||
# Euler's criterion: there are (p-1)/2 squares in a field with modulus `p`
|
||||
# so we have a probability of ~0.5 to get a good point
|
||||
rng.random_long01Seq(fieldElem, a.F.C)
|
||||
rng.random_long01Seq(fieldElem)
|
||||
success = trySetFromCoordX(a, fieldElem)
|
||||
|
||||
func random_long01Seq_with_randZ(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Jac)) =
|
||||
|
@ -301,13 +305,13 @@ func random_long01Seq_with_randZ(rng: var RngState, a: var (ECP_ShortW_Proj or E
|
|||
## that produces long bitstrings of 0 and 1
|
||||
## to trigger edge cases
|
||||
var Z{.noInit.}: a.F
|
||||
rng.random_long01Seq(Z, a.F.C) # If Z is zero, X will be zero and that will be an infinity point
|
||||
rng.random_long01Seq(Z) # If Z is zero, X will be zero and that will be an infinity point
|
||||
|
||||
var fieldElem {.noInit.}: a.F
|
||||
var success = CtFalse
|
||||
|
||||
while not bool(success):
|
||||
rng.random_long01Seq(fieldElem, a.F.C)
|
||||
rng.random_long01Seq(fieldElem)
|
||||
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
||||
|
||||
# Generic over any Constantine type
|
||||
|
@ -323,7 +327,7 @@ func random_unsafe*(rng: var RngState, T: typedesc): T =
|
|||
elif T is BigInt:
|
||||
rng.random_unsafe(result)
|
||||
else: # Fields
|
||||
rng.random_unsafe(result, T.C)
|
||||
rng.random_unsafe(result)
|
||||
|
||||
func random_unsafe_with_randZ*(rng: var RngState, T: typedesc[ECP_ShortW_Proj or ECP_ShortW_Jac]): T =
|
||||
## Create a random curve element with a random Z coordinate
|
||||
|
@ -340,7 +344,7 @@ func random_highHammingWeight*(rng: var RngState, T: typedesc): T =
|
|||
elif T is BigInt:
|
||||
rng.random_highHammingWeight(result)
|
||||
else: # Fields
|
||||
rng.random_highHammingWeight(result, T.C)
|
||||
rng.random_highHammingWeight(result)
|
||||
|
||||
func random_highHammingWeight_with_randZ*(rng: var RngState, T: typedesc[ECP_ShortW_Proj or ECP_ShortW_Jac]): T =
|
||||
## Create a random curve element with a random Z coordinate
|
||||
|
@ -357,7 +361,7 @@ func random_long01Seq*(rng: var RngState, T: typedesc): T =
|
|||
elif T is BigInt:
|
||||
rng.random_long01Seq(result)
|
||||
else: # Fields
|
||||
rng.random_long01Seq(result, T.C)
|
||||
rng.random_long01Seq(result)
|
||||
|
||||
func random_long01Seq_with_randZ*(rng: var RngState, T: typedesc[ECP_ShortW_Proj or ECP_ShortW_Jac]): T =
|
||||
## Create a random curve element with a random Z coordinate
|
||||
|
|
|
@ -266,7 +266,7 @@ if __name__ == "__main__":
|
|||
f.write('\n\n')
|
||||
f.write(inspect.cleandoc(f"""
|
||||
import
|
||||
../config/[curves, type_bigint, type_fp],
|
||||
../config/[curves, type_bigint, type_ff],
|
||||
../io/[io_bigints, io_fields]
|
||||
|
||||
# {curve} G1
|
||||
|
|
|
@ -285,7 +285,7 @@ if __name__ == "__main__":
|
|||
else:
|
||||
f.write(inspect.cleandoc("""
|
||||
import
|
||||
../config/[curves, type_fp],
|
||||
../config/[curves, type_ff],
|
||||
../towers,
|
||||
../io/[io_fields, io_towers]
|
||||
"""))
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/towers,
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/towers,
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/towers,
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/towers,
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
# Test utilities
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
# Test utilities
|
||||
|
|
|
@ -12,7 +12,7 @@ import
|
|||
# 3rd party
|
||||
serialization, json_serialization,
|
||||
# Internals
|
||||
../constantine/config/[common, curves, type_bigint, type_fp],
|
||||
../constantine/config/[common, curves, type_bigint, type_ff],
|
||||
../constantine/towers,
|
||||
../constantine/io/[io_bigints, io_ec],
|
||||
../constantine/elliptic/[
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[type_fp, curves],
|
||||
../constantine/config/[type_ff, curves],
|
||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
|
|
@ -27,7 +27,7 @@ echo "test_finite_fields_mulsquare xoshiro512** seed: ", seed
|
|||
static: doAssert defined(testingCurves), "This modules requires the -d:testingCurves compile option"
|
||||
|
||||
proc sanity(C: static Curve) =
|
||||
test "Squaring 0,1,2 with "& $Curve(C) & " [FastSquaring = " & $C.canUseNoCarryMontySquare & "]":
|
||||
test "Squaring 0,1,2 with "& $Curve(C) & " [FastSquaring = " & $Fp[C].canUseNoCarryMontySquare & "]":
|
||||
block: # 0² mod
|
||||
var n: Fp[C]
|
||||
|
||||
|
@ -89,7 +89,7 @@ mainSanity()
|
|||
|
||||
proc mainSelectCases() =
|
||||
suite "Modular Squaring: selected tricky cases" & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "P-256 [FastSquaring = " & $P256.canUseNoCarryMontySquare & "]":
|
||||
test "P-256 [FastSquaring = " & $Fp[P256].canUseNoCarryMontySquare & "]":
|
||||
block:
|
||||
# Triggered an issue in the (t[N+1], t[N]) = t[N] + (A1, A0)
|
||||
# between the squaring and reduction step, with t[N+1] and A1 being carry bits.
|
||||
|
@ -136,7 +136,7 @@ proc random_long01Seq(C: static Curve) =
|
|||
doAssert bool(r_mul == r_sqr)
|
||||
|
||||
suite "Random Modular Squaring is consistent with Modular Multiplication" & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "Random squaring mod P-224 [FastSquaring = " & $P224.canUseNoCarryMontySquare & "]":
|
||||
test "Random squaring mod P-224 [FastSquaring = " & $Fp[P224].canUseNoCarryMontySquare & "]":
|
||||
for _ in 0 ..< Iters:
|
||||
randomCurve(P224)
|
||||
for _ in 0 ..< Iters:
|
||||
|
@ -144,7 +144,7 @@ suite "Random Modular Squaring is consistent with Modular Multiplication" & " ["
|
|||
for _ in 0 ..< Iters:
|
||||
random_long01Seq(P224)
|
||||
|
||||
test "Random squaring mod P-256 [FastSquaring = " & $P256.canUseNoCarryMontySquare & "]":
|
||||
test "Random squaring mod P-256 [FastSquaring = " & $Fp[P256].canUseNoCarryMontySquare & "]":
|
||||
for _ in 0 ..< Iters:
|
||||
randomCurve(P256)
|
||||
for _ in 0 ..< Iters:
|
||||
|
@ -152,7 +152,7 @@ suite "Random Modular Squaring is consistent with Modular Multiplication" & " ["
|
|||
for _ in 0 ..< Iters:
|
||||
random_long01Seq(P256)
|
||||
|
||||
test "Random squaring mod BLS12_381 [FastSquaring = " & $BLS12_381.canUseNoCarryMontySquare & "]":
|
||||
test "Random squaring mod BLS12_381 [FastSquaring = " & $Fp[BLS12_381].canUseNoCarryMontySquare & "]":
|
||||
for _ in 0 ..< Iters:
|
||||
randomCurve(BLS12_381)
|
||||
for _ in 0 ..< Iters:
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
# 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],
|
||||
# Internal
|
||||
../constantine/arithmetic,
|
||||
../constantine/io/[io_bigints, io_fields],
|
||||
../constantine/config/[curves, common, type_bigint],
|
||||
# Test utilities
|
||||
../helpers/prng_unsafe
|
||||
|
||||
const Iters = 24
|
||||
|
||||
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 "test_fr xoshiro512** seed: ", seed
|
||||
|
||||
proc sanity(C: static Curve) =
|
||||
test "Fr: Squaring 0,1,2 with "& $Fr[C] & " [FastSquaring = " & $Fr[C].canUseNoCarryMontySquare & "]":
|
||||
block: # 0² mod
|
||||
var n: Fr[C]
|
||||
|
||||
n.fromUint(0'u32)
|
||||
let expected = n
|
||||
|
||||
# Out-of-place
|
||||
var r: Fr[C]
|
||||
r.square(n)
|
||||
# In-place
|
||||
n.square()
|
||||
|
||||
check:
|
||||
bool(r == expected)
|
||||
bool(n == expected)
|
||||
|
||||
block: # 1² mod
|
||||
var n: Fr[C]
|
||||
|
||||
n.fromUint(1'u32)
|
||||
let expected = n
|
||||
|
||||
# Out-of-place
|
||||
var r: Fr[C]
|
||||
r.square(n)
|
||||
# In-place
|
||||
n.square()
|
||||
|
||||
check:
|
||||
bool(r == expected)
|
||||
bool(n == expected)
|
||||
|
||||
block: # 2² mod
|
||||
var n, expected: Fr[C]
|
||||
|
||||
n.fromUint(2'u32)
|
||||
expected.fromUint(4'u32)
|
||||
|
||||
# Out-of-place
|
||||
var r: Fr[C]
|
||||
r.square(n)
|
||||
# In-place
|
||||
n.square()
|
||||
|
||||
check:
|
||||
bool(r == expected)
|
||||
bool(n == expected)
|
||||
|
||||
proc mainSanity() =
|
||||
suite "Fr: Modular squaring is consistent with multiplication on special elements" & " [" & $WordBitwidth & "-bit mode]":
|
||||
sanity BN254_Snarks
|
||||
sanity BLS12_381
|
||||
|
||||
mainSanity()
|
||||
|
||||
proc randomCurve(C: static Curve) =
|
||||
let a = rng.random_unsafe(Fr[C])
|
||||
|
||||
var r_mul, r_sqr: Fr[C]
|
||||
|
||||
r_mul.prod(a, a)
|
||||
r_sqr.square(a)
|
||||
|
||||
doAssert bool(r_mul == r_sqr)
|
||||
|
||||
proc randomHighHammingWeight(C: static Curve) =
|
||||
let a = rng.random_highHammingWeight(Fr[C])
|
||||
|
||||
var r_mul, r_sqr: Fr[C]
|
||||
|
||||
r_mul.prod(a, a)
|
||||
r_sqr.square(a)
|
||||
|
||||
doAssert bool(r_mul == r_sqr)
|
||||
|
||||
proc random_long01Seq(C: static Curve) =
|
||||
let a = rng.random_long01Seq(Fr[C])
|
||||
|
||||
var r_mul, r_sqr: Fr[C]
|
||||
|
||||
r_mul.prod(a, a)
|
||||
r_sqr.square(a)
|
||||
|
||||
doAssert bool(r_mul == r_sqr)
|
||||
|
||||
suite "Fr: Random Modular Squaring is consistent with Modular Multiplication" & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "Random squaring mod r_BN254_Snarks [FastSquaring = " & $Fr[BN254_Snarks].canUseNoCarryMontySquare & "]":
|
||||
for _ in 0 ..< Iters:
|
||||
randomCurve(BN254_Snarks)
|
||||
for _ in 0 ..< Iters:
|
||||
randomHighHammingWeight(BN254_Snarks)
|
||||
for _ in 0 ..< Iters:
|
||||
random_long01Seq(BN254_Snarks)
|
||||
|
||||
test "Random squaring mod r_BLS12_381 [FastSquaring = " & $Fr[BLS12_381].canUseNoCarryMontySquare & "]":
|
||||
for _ in 0 ..< Iters:
|
||||
randomCurve(BLS12_381)
|
||||
for _ in 0 ..< Iters:
|
||||
randomHighHammingWeight(BLS12_381)
|
||||
for _ in 0 ..< Iters:
|
||||
random_long01Seq(BLS12_381)
|
Loading…
Reference in New Issue