mirror of
https://github.com/logos-storage/constantine.git
synced 2026-05-20 16:39:30 +00:00
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) =
|
proc invPowFermatBench*(T: typedesc, iters: int) =
|
||||||
let x = rng.random_unsafe(T)
|
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):
|
bench("Inversion via exponentiation p-2 (Little Fermat)", T, iters):
|
||||||
var r = x
|
var r = x
|
||||||
r.powUnsafeExponent(exponent)
|
r.powUnsafeExponent(exponent)
|
||||||
|
|||||||
@ -148,7 +148,12 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
|
|||||||
("tests/t_pairing_bls12_381_optate.nim", false),
|
("tests/t_pairing_bls12_381_optate.nim", false),
|
||||||
|
|
||||||
# Hashing vs OpenSSL
|
# 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
|
# For temporary (hopefully) investigation that can only be reproduced in CI
|
||||||
@ -264,7 +269,7 @@ proc buildAllBenches() =
|
|||||||
buildBench("bench_pairing_bn254_snarks")
|
buildBench("bench_pairing_bn254_snarks")
|
||||||
buildBench("bench_sha256")
|
buildBench("bench_sha256")
|
||||||
echo "All benchmarks compile successfully."
|
echo "All benchmarks compile successfully."
|
||||||
|
|
||||||
# Tasks
|
# Tasks
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -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:
|
# Constraints:
|
||||||
# - We assume that p is known at compile-time
|
# - We assume that p and r are known at compile-time
|
||||||
# - We assume that p is not even:
|
# - We assume that p and r are not even:
|
||||||
# - Operations are done in the Montgomery domain
|
# - Operations are done in the Montgomery domain
|
||||||
# - The Montgomery domain introduce a Montgomery constant that must be coprime
|
# - The Montgomery domain introduce a Montgomery constant that must be coprime
|
||||||
# with the field modulus.
|
# with the field modulus.
|
||||||
# - The constant is chosen a power of 2
|
# - The constant is chosen a power of 2
|
||||||
# => to be coprime with a power of 2, p must be odd
|
# => to be coprime with a power of 2, p and r must be odd
|
||||||
# - We assume that p is a prime
|
# - We assume that p and r are a prime
|
||||||
# - Modular inversion uses the Fermat's little theorem
|
# - Modular inversion may use the Fermat's little theorem
|
||||||
# which requires a prime
|
# which requires a prime
|
||||||
|
|
||||||
import
|
import
|
||||||
../primitives,
|
../primitives,
|
||||||
../config/[common, type_fp, curves],
|
../config/[common, type_ff, curves],
|
||||||
./bigints, ./limbs_montgomery
|
./bigints, ./limbs_montgomery
|
||||||
|
|
||||||
when UseASM_X86_64:
|
when UseASM_X86_64:
|
||||||
@ -37,7 +39,7 @@ when nimvm:
|
|||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
export Fp
|
export Fp, Fr, FF
|
||||||
|
|
||||||
# No exceptions allowed
|
# No exceptions allowed
|
||||||
{.push raises: [].}
|
{.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
|
## Convert a BigInt to its Montgomery form
|
||||||
when nimvm:
|
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:
|
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
|
## Convert a BigInt to its Montgomery form
|
||||||
result.fromBig(src)
|
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
|
## Convert a finite-field element to a BigInt in natural representation
|
||||||
var r {.noInit.}: typeof(src.mres)
|
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
|
return r
|
||||||
|
|
||||||
# Copy
|
# 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
|
## Constant-time conditional copy
|
||||||
## If ctl is true: b is copied into a
|
## If ctl is true: b is copied into a
|
||||||
## if ctl is false: b is not copied and a is unmodified
|
## 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
|
## Time and memory accesses are the same whether a copy occurs or not
|
||||||
ccopy(a.mres, b.mres, ctl)
|
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
|
## Swap ``a`` and ``b`` if ``ctl`` is true
|
||||||
##
|
##
|
||||||
## Constant-time:
|
## Constant-time:
|
||||||
@ -98,144 +100,144 @@ func cswap*(a, b: var Fp, ctl: CTBool) {.inline.} =
|
|||||||
# exist and can be implemented with compile-time specialization.
|
# exist and can be implemented with compile-time specialization.
|
||||||
|
|
||||||
# Note: for `+=`, double, sum
|
# 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).
|
# (2^64)^w - 1 (if using uint64 words).
|
||||||
# In practice I'm not aware of such prime being using in elliptic curves.
|
# 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
|
# 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
|
## Constant-time equality check
|
||||||
a.mres == b.mres
|
a.mres == b.mres
|
||||||
|
|
||||||
func isZero*(a: Fp): SecretBool {.inline.} =
|
func isZero*(a: FF): SecretBool {.inline.} =
|
||||||
## Constant-time check if zero
|
## Constant-time check if zero
|
||||||
a.mres.isZero()
|
a.mres.isZero()
|
||||||
|
|
||||||
func isOne*(a: Fp): SecretBool {.inline.} =
|
func isOne*(a: FF): SecretBool {.inline.} =
|
||||||
## Constant-time check if one
|
## 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)
|
## 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
|
## Set ``a`` to zero
|
||||||
a.mres.setZero()
|
a.mres.setZero()
|
||||||
|
|
||||||
func setOne*(a: var Fp) {.inline.} =
|
func setOne*(a: var FF) {.inline.} =
|
||||||
## Set ``a`` to one
|
## Set ``a`` to one
|
||||||
# Note: we need 1 in Montgomery residue form
|
# Note: we need 1 in Montgomery residue form
|
||||||
# TODO: Nim codegen is not optimal it uses a temporary
|
# TODO: Nim codegen is not optimal it uses a temporary
|
||||||
# Check if the compiler optimizes it away
|
# 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
|
## In-place addition modulo p
|
||||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
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:
|
else:
|
||||||
var overflowed = add(a.mres, b.mres)
|
var overflowed = add(a.mres, b.mres)
|
||||||
overflowed = overflowed or not(a.mres < Fp.C.Mod)
|
overflowed = overflowed or not(a.mres < FF.fieldMod())
|
||||||
discard csub(a.mres, Fp.C.Mod, overflowed)
|
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
|
## In-place substraction modulo p
|
||||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
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:
|
else:
|
||||||
let underflowed = sub(a.mres, b.mres)
|
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
|
## Double ``a`` modulo p
|
||||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
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:
|
else:
|
||||||
var overflowed = double(a.mres)
|
var overflowed = double(a.mres)
|
||||||
overflowed = overflowed or not(a.mres < Fp.C.Mod)
|
overflowed = overflowed or not(a.mres < FF.fieldMod())
|
||||||
discard csub(a.mres, Fp.C.Mod, overflowed)
|
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
|
## Sum ``a`` and ``b`` into ``r`` modulo p
|
||||||
## r is initialized/overwritten
|
## r is initialized/overwritten
|
||||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
||||||
r = a
|
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:
|
else:
|
||||||
var overflowed = r.mres.sum(a.mres, b.mres)
|
var overflowed = r.mres.sum(a.mres, b.mres)
|
||||||
overflowed = overflowed or not(r.mres < Fp.C.Mod)
|
overflowed = overflowed or not(r.mres < FF.fieldMod())
|
||||||
discard csub(r.mres, Fp.C.Mod, overflowed)
|
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
|
## Sum ``a`` and ``b`` into ``r`` without reduction
|
||||||
discard r.mres.sum(a.mres, b.mres)
|
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`.
|
## Substract `b` from `a` and store the result into `r`.
|
||||||
## `r` is initialized/overwritten
|
## `r` is initialized/overwritten
|
||||||
## Requires r != b
|
## Requires r != b
|
||||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
||||||
r = a
|
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:
|
else:
|
||||||
var underflowed = r.mres.diff(a.mres, b.mres)
|
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`.
|
## Substract `b` from `a` and store the result into `r`.
|
||||||
## `r` is initialized/overwritten
|
## `r` is initialized/overwritten
|
||||||
## Handles r == b
|
## Handles r == b
|
||||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
||||||
var t = a
|
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
|
r = t
|
||||||
else:
|
else:
|
||||||
var underflowed = r.mres.diff(a.mres, b.mres)
|
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`
|
## Substract `b` from `a` and store the result into `r`
|
||||||
## without reduction
|
## without reduction
|
||||||
discard r.mres.diff(a.mres, b.mres)
|
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``
|
## Double ``a`` into ``r``
|
||||||
## `r` is initialized/overwritten
|
## `r` is initialized/overwritten
|
||||||
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling
|
||||||
r = a
|
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:
|
else:
|
||||||
var overflowed = r.mres.double(a.mres)
|
var overflowed = r.mres.double(a.mres)
|
||||||
overflowed = overflowed or not(r.mres < Fp.C.Mod)
|
overflowed = overflowed or not(r.mres < FF.fieldMod())
|
||||||
discard csub(r.mres, Fp.C.Mod, overflowed)
|
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``
|
## Store the product of ``a`` by ``b`` modulo p into ``r``
|
||||||
## ``r`` is initialized / overwritten
|
## ``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
|
## 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
|
## Negate modulo p
|
||||||
when UseASM_X86_64 and defined(gcc):
|
when UseASM_X86_64 and defined(gcc):
|
||||||
# Clang and every compiler besides GCC
|
# Clang and every compiler besides GCC
|
||||||
# can cleanly optimized this
|
# can cleanly optimized this
|
||||||
# especially on Fp2
|
# especially on FF2
|
||||||
negmod_asm(r.mres.limbs, a.mres.limbs, Fp.C.Mod.limbs)
|
negmod_asm(r.mres.limbs, a.mres.limbs, FF.fieldMod().limbs)
|
||||||
else:
|
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
|
## Negate modulo p
|
||||||
a.neg(a)
|
a.neg(a)
|
||||||
|
|
||||||
func div2*(a: var Fp) {.inline.} =
|
func div2*(a: var FF) {.inline.} =
|
||||||
## Modular division by 2
|
## 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
|
## Constant-time in-place conditional negation
|
||||||
## The negation is only performed if ctl is "true"
|
## The negation is only performed if ctl is "true"
|
||||||
r.neg(a)
|
r.neg(a)
|
||||||
r.ccopy(a, not ctl)
|
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
|
## Constant-time in-place conditional negation
|
||||||
## The negation is only performed if ctl is "true"
|
## The negation is only performed if ctl is "true"
|
||||||
var t = a
|
var t = a
|
||||||
a.cneg(t, ctl)
|
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
|
## Constant-time in-place conditional addition
|
||||||
## The addition is only performed if ctl is "true"
|
## The addition is only performed if ctl is "true"
|
||||||
var t = a
|
var t = a
|
||||||
t += b
|
t += b
|
||||||
a.ccopy(t, ctl)
|
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
|
## Constant-time in-place conditional substraction
|
||||||
## The substraction is only performed if ctl is "true"
|
## The substraction is only performed if ctl is "true"
|
||||||
var t = a
|
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
|
# 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
|
## Exponentiation modulo p
|
||||||
## ``a``: a field element to be exponentiated
|
## ``a``: a field element to be exponentiated
|
||||||
## ``exponent``: a big integer
|
## ``exponent``: a big integer
|
||||||
const windowSize = 5 # TODO: find best window size for each curves
|
const windowSize = 5 # TODO: find best window size for each curves
|
||||||
a.mres.montyPow(
|
a.mres.montyPow(
|
||||||
exponent,
|
exponent,
|
||||||
Fp.C.Mod, Fp.C.getMontyOne(),
|
FF.fieldMod(), FF.getMontyOne(),
|
||||||
Fp.C.getNegInvModWord(), windowSize,
|
FF.getNegInvModWord(), windowSize,
|
||||||
Fp.C.canUseNoCarryMontyMul(),
|
FF.canUseNoCarryMontyMul(),
|
||||||
Fp.C.canUseNoCarryMontySquare()
|
FF.canUseNoCarryMontySquare()
|
||||||
)
|
)
|
||||||
|
|
||||||
func pow*(a: var Fp, exponent: openarray[byte]) {.inline.} =
|
func pow*(a: var FF, exponent: openarray[byte]) {.inline.} =
|
||||||
## Exponentiation modulo p
|
## Exponentiation modulo p
|
||||||
## ``a``: a field element to be exponentiated
|
## ``a``: a field element to be exponentiated
|
||||||
## ``exponent``: a big integer in canonical big endian representation
|
## ``exponent``: a big integer in canonical big endian representation
|
||||||
const windowSize = 5 # TODO: find best window size for each curves
|
const windowSize = 5 # TODO: find best window size for each curves
|
||||||
a.mres.montyPow(
|
a.mres.montyPow(
|
||||||
exponent,
|
exponent,
|
||||||
Fp.C.Mod, Fp.C.getMontyOne(),
|
FF.fieldMod(), FF.getMontyOne(),
|
||||||
Fp.C.getNegInvModWord(), windowSize,
|
FF.getNegInvModWord(), windowSize,
|
||||||
Fp.C.canUseNoCarryMontyMul(),
|
FF.canUseNoCarryMontyMul(),
|
||||||
Fp.C.canUseNoCarryMontySquare()
|
FF.canUseNoCarryMontySquare()
|
||||||
)
|
)
|
||||||
|
|
||||||
func powUnsafeExponent*(a: var Fp, exponent: BigInt) {.inline.} =
|
func powUnsafeExponent*(a: var FF, exponent: BigInt) {.inline.} =
|
||||||
## Exponentiation modulo p
|
## Exponentiation modulo p
|
||||||
## ``a``: a field element to be exponentiated
|
## ``a``: a field element to be exponentiated
|
||||||
## ``exponent``: a big integer
|
## ``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
|
const windowSize = 5 # TODO: find best window size for each curves
|
||||||
a.mres.montyPowUnsafeExponent(
|
a.mres.montyPowUnsafeExponent(
|
||||||
exponent,
|
exponent,
|
||||||
Fp.C.Mod, Fp.C.getMontyOne(),
|
FF.fieldMod(), FF.getMontyOne(),
|
||||||
Fp.C.getNegInvModWord(), windowSize,
|
FF.getNegInvModWord(), windowSize,
|
||||||
Fp.C.canUseNoCarryMontyMul(),
|
FF.canUseNoCarryMontyMul(),
|
||||||
Fp.C.canUseNoCarryMontySquare()
|
FF.canUseNoCarryMontySquare()
|
||||||
)
|
)
|
||||||
|
|
||||||
func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) {.inline.} =
|
func powUnsafeExponent*(a: var FF, exponent: openarray[byte]) {.inline.} =
|
||||||
## Exponentiation modulo p
|
## Exponentiation modulo p
|
||||||
## ``a``: a field element to be exponentiated
|
## ``a``: a field element to be exponentiated
|
||||||
## ``exponent``: a big integer a big integer in canonical big endian representation
|
## ``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
|
const windowSize = 5 # TODO: find best window size for each curves
|
||||||
a.mres.montyPowUnsafeExponent(
|
a.mres.montyPowUnsafeExponent(
|
||||||
exponent,
|
exponent,
|
||||||
Fp.C.Mod, Fp.C.getMontyOne(),
|
FF.fieldMod(), FF.getMontyOne(),
|
||||||
Fp.C.getNegInvModWord(), windowSize,
|
FF.getNegInvModWord(), windowSize,
|
||||||
Fp.C.canUseNoCarryMontyMul(),
|
FF.canUseNoCarryMontyMul(),
|
||||||
Fp.C.canUseNoCarryMontySquare()
|
FF.canUseNoCarryMontySquare()
|
||||||
)
|
)
|
||||||
|
|
||||||
# ############################################################
|
# ############################################################
|
||||||
@ -355,36 +357,36 @@ func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) {.inline.} =
|
|||||||
# - Those that return a field element
|
# - Those that return a field element
|
||||||
# - Those that internally allocate a temporary 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
|
## Addition modulo p
|
||||||
result.sum(a, b)
|
result.sum(a, b)
|
||||||
|
|
||||||
func `-`*(a, b: Fp): Fp {.noInit, inline.} =
|
func `-`*(a, b: FF): FF {.noInit, inline.} =
|
||||||
## Substraction modulo p
|
## Substraction modulo p
|
||||||
result.diff(a, b)
|
result.diff(a, b)
|
||||||
|
|
||||||
func `*`*(a, b: Fp): Fp {.noInit, inline.} =
|
func `*`*(a, b: FF): FF {.noInit, inline.} =
|
||||||
## Multiplication modulo p
|
## Multiplication modulo p
|
||||||
##
|
##
|
||||||
## It is recommended to assign with {.noInit.}
|
## 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.
|
## routine will zero init internally the result.
|
||||||
result.prod(a, b)
|
result.prod(a, b)
|
||||||
|
|
||||||
func `*=`*(a: var Fp, b: Fp) {.inline.} =
|
func `*=`*(a: var FF, b: FF) {.inline.} =
|
||||||
## Multiplication modulo p
|
## Multiplication modulo p
|
||||||
a.prod(a, b)
|
a.prod(a, b)
|
||||||
|
|
||||||
func square*(a: var Fp) {.inline.} =
|
func square*(a: var FF) {.inline.} =
|
||||||
## Squaring modulo p
|
## 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
|
## Repeated squarings
|
||||||
for _ in 0 ..< num:
|
for _ in 0 ..< num:
|
||||||
r.square()
|
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
|
## Multiplication by a small integer known at compile-time
|
||||||
# Implementation:
|
# Implementation:
|
||||||
# We don't want to go convert the integer to the Montgomery domain (O(n²))
|
# 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:
|
else:
|
||||||
{.error: "Multiplication by this small int not implemented".}
|
{.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
|
## Multiplication by a small integer known at compile-time
|
||||||
result = a
|
result = a
|
||||||
result *= b
|
result *= b
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../config/[common, curves, type_fp],
|
../config/[common, curves, type_ff],
|
||||||
../primitives,
|
../primitives,
|
||||||
./bigints,
|
./bigints,
|
||||||
./finite_fields,
|
./finite_fields,
|
||||||
@ -38,8 +38,8 @@ func reduce*(r: var Fp, a: FpDbl) {.inline.} =
|
|||||||
r.mres.limbs,
|
r.mres.limbs,
|
||||||
a.limbs2x,
|
a.limbs2x,
|
||||||
Fp.C.Mod.limbs,
|
Fp.C.Mod.limbs,
|
||||||
Fp.C.getNegInvModWord(),
|
Fp.getNegInvModWord(),
|
||||||
Fp.C.canUseNoCarryMontyMul()
|
Fp.canUseNoCarryMontyMul()
|
||||||
)
|
)
|
||||||
|
|
||||||
func diffNoInline(r: var FpDbl, a, b: FpDbl): Borrow =
|
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.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../config/[curves, type_fp],
|
../config/[curves, type_ff],
|
||||||
./bigints,
|
./bigints,
|
||||||
../curves/zoo_inversions
|
../curves/zoo_inversions
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ func inv_euclid*(r: var Fp, a: Fp) {.inline.} =
|
|||||||
## Inversion modulo p via
|
## Inversion modulo p via
|
||||||
## Niels Moller constant-time version of
|
## Niels Moller constant-time version of
|
||||||
## Stein's GCD derived from extended binary Euclid algorithm
|
## 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.} =
|
func inv*(r: var Fp, a: Fp) {.inline.} =
|
||||||
## Inversion modulo p
|
## Inversion modulo p
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
../primitives,
|
../primitives,
|
||||||
../config/[common, type_fp, curves],
|
../config/[common, type_ff, curves],
|
||||||
../curves/zoo_square_roots,
|
../curves/zoo_square_roots,
|
||||||
./bigints, ./finite_fields
|
./bigints, ./finite_fields
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ import
|
|||||||
# Legendre symbol / Euler's Criterion / Kronecker's symbol
|
# 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
|
## Returns true if ``a`` is a square (quadratic residue) in 𝔽p
|
||||||
##
|
##
|
||||||
## Assumes that the prime modulus ``p`` is public.
|
## 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
|
# Note that we don't care about leaking the bits of p
|
||||||
# as we assume that
|
# as we assume that
|
||||||
var xi {.noInit.} = a # TODO: is noInit necessary? see https://github.com/mratsim/constantine/issues/21
|
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())
|
result = not(xi.isMinusOne())
|
||||||
# xi can be:
|
# xi can be:
|
||||||
# - 1 if a square
|
# - 1 if a square
|
||||||
@ -46,7 +46,7 @@ func isSquare*[C](a: Fp[C]): SecretBool {.inline.} =
|
|||||||
# Specialized routine for p ≡ 3 (mod 4)
|
# 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``
|
## Compute the square root of ``a``
|
||||||
##
|
##
|
||||||
## This requires ``a`` to be a square
|
## 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,
|
## The square root, if it exist is multivalued,
|
||||||
## i.e. both x² == (-x)²
|
## i.e. both x² == (-x)²
|
||||||
## This procedure returns a deterministic result
|
## This procedure returns a deterministic result
|
||||||
static: doAssert BaseType(C.Mod.limbs[0]) mod 4 == 3
|
static: doAssert BaseType(Fp.C.Mod.limbs[0]) mod 4 == 3
|
||||||
a.powUnsafeExponent(C.getPrimePlus1div4_BE())
|
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
|
## If ``a`` is a square, compute the square root of ``a`` in sqrt
|
||||||
## and the inverse square root of a in invsqrt
|
## 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-1)/2)) * a^-1 ≡ 1/a (mod p)
|
||||||
# a^((p-3)/2)) ≡ 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)
|
# 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 = a
|
||||||
invsqrt.powUnsafeExponent(C.getPrimeMinus3div4_BE())
|
invsqrt.powUnsafeExponent(Fp.getPrimeMinus3div4_BE())
|
||||||
# √a ≡ a * 1/√a ≡ a^((p+1)/4) (mod p)
|
# √a ≡ a * 1/√a ≡ a^((p+1)/4) (mod p)
|
||||||
sqrt.prod(invsqrt, a)
|
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
|
## If ``a`` is a square, compute the square root of ``a`` in sqrt
|
||||||
## and the inverse square root of a in invsqrt
|
## 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)
|
## This assumes that the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||||
sqrt_invsqrt_p3mod4(sqrt, invsqrt, a)
|
sqrt_invsqrt_p3mod4(sqrt, invsqrt, a)
|
||||||
var test {.noInit.}: Fp[C]
|
var test {.noInit.}: Fp
|
||||||
test.square(sqrt)
|
test.square(sqrt)
|
||||||
result = test == a
|
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 ``a`` is a square, compute the square root of ``a``
|
||||||
## if not, ``a`` is unmodified.
|
## 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,
|
## The square root, if it exist is multivalued,
|
||||||
## i.e. both x² == (-x)²
|
## i.e. both x² == (-x)²
|
||||||
## This procedure returns a deterministic result
|
## 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)
|
result = sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt, a)
|
||||||
a.ccopy(sqrt, result)
|
a.ccopy(sqrt, result)
|
||||||
|
|
||||||
# Tonelli Shanks for any prime
|
# Tonelli Shanks for any prime
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
func precompute_tonelli_shanks[C](
|
func precompute_tonelli_shanks(
|
||||||
a_pre_exp: var Fp[C],
|
a_pre_exp: var Fp,
|
||||||
a: Fp[C]) =
|
a: Fp) =
|
||||||
a_pre_exp = a
|
a_pre_exp = a
|
||||||
a_pre_exp.powUnsafeExponent(C.tonelliShanks(exponent))
|
a_pre_exp.powUnsafeExponent(Fp.C.tonelliShanks(exponent))
|
||||||
|
|
||||||
func isSquare_tonelli_shanks[C](
|
func isSquare_tonelli_shanks(
|
||||||
a, a_pre_exp: Fp[C]): SecretBool =
|
a, a_pre_exp: Fp): SecretBool =
|
||||||
## Returns if `a` is a quadratic residue
|
## Returns if `a` is a quadratic residue
|
||||||
## This uses common precomputation for
|
## This uses common precomputation for
|
||||||
## Tonelli-Shanks based square root and inverse square root
|
## Tonelli-Shanks based square root and inverse square root
|
||||||
##
|
##
|
||||||
## a^((p-1-2^e)/(2*2^e))
|
## a^((p-1-2^e)/(2*2^e))
|
||||||
const e = C.tonelliShanks(twoAdicity)
|
const e = Fp.C.tonelliShanks(twoAdicity)
|
||||||
var r {.noInit.}: Fp[C]
|
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.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)
|
r *= a # a^((q-1)/2^e)
|
||||||
for _ in 0 ..< e-1:
|
for _ in 0 ..< e-1:
|
||||||
@ -143,9 +143,9 @@ func isSquare_tonelli_shanks[C](
|
|||||||
r.isMinusOne()
|
r.isMinusOne()
|
||||||
)
|
)
|
||||||
|
|
||||||
func sqrt_invsqrt_tonelli_shanks[C](
|
func sqrt_invsqrt_tonelli_shanks(
|
||||||
sqrt, invsqrt: var Fp[C],
|
sqrt, invsqrt: var Fp,
|
||||||
a, a_pre_exp: Fp[C]) =
|
a, a_pre_exp: Fp) =
|
||||||
## Compute the square_root and inverse_square_root
|
## Compute the square_root and inverse_square_root
|
||||||
## of `a` via constant-time Tonelli-Shanks
|
## 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
|
## ThItat is shared with the simultaneous isSquare routine
|
||||||
template z: untyped = a_pre_exp
|
template z: untyped = a_pre_exp
|
||||||
template r: untyped = invsqrt
|
template r: untyped = invsqrt
|
||||||
var t {.noInit.}: Fp[C]
|
var t {.noInit.}: Fp
|
||||||
const e = C.tonelliShanks(twoAdicity)
|
const e = Fp.C.tonelliShanks(twoAdicity)
|
||||||
|
|
||||||
t.square(z)
|
t.square(z)
|
||||||
t *= a
|
t *= a
|
||||||
r = z
|
r = z
|
||||||
var b = t
|
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 i in countdown(e, 2, 1):
|
||||||
for j in 1 .. i-2:
|
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).
|
# * 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.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import curves_prop_core, curves_prop_derived
|
||||||
# Standard library
|
export curves_prop_core, curves_prop_derived
|
||||||
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()
|
|
||||||
|
|||||||
@ -15,7 +15,12 @@ import
|
|||||||
|
|
||||||
{.experimental: "dynamicBindSym".}
|
{.experimental: "dynamicBindSym".}
|
||||||
|
|
||||||
macro genDerivedConstants*(): untyped =
|
type
|
||||||
|
DerivedConstantMode* = enum
|
||||||
|
kModulus
|
||||||
|
kOrder
|
||||||
|
|
||||||
|
macro genDerivedConstants*(mode: static DerivedConstantMode): untyped =
|
||||||
## Generate constants derived from the main constants
|
## Generate constants derived from the main constants
|
||||||
##
|
##
|
||||||
## For example
|
## For example
|
||||||
@ -38,87 +43,92 @@ macro genDerivedConstants*(): untyped =
|
|||||||
nnkPragma.newTree(ident"used")
|
nnkPragma.newTree(ident"used")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let ff = if mode == kModulus: "_Fp" else: "_Fr"
|
||||||
|
|
||||||
|
|
||||||
for curveSym in low(Curve) .. high(Curve):
|
for curveSym in low(Curve) .. high(Curve):
|
||||||
let curve = $curveSym
|
let curve = $curveSym
|
||||||
|
let M = if mode == kModulus: bindSym(curve & "_Modulus")
|
||||||
|
else: bindSym(curve & "_Order")
|
||||||
|
|
||||||
# const MyCurve_CanUseNoCarryMontyMul = useNoCarryMontyMul(MyCurve_Modulus)
|
# const MyCurve_CanUseNoCarryMontyMul = useNoCarryMontyMul(MyCurve_Modulus)
|
||||||
result.add newConstStmt(
|
result.add newConstStmt(
|
||||||
used(curve & "_CanUseNoCarryMontyMul"), newCall(
|
used(curve & ff & "_CanUseNoCarryMontyMul"), newCall(
|
||||||
bindSym"useNoCarryMontyMul",
|
bindSym"useNoCarryMontyMul",
|
||||||
bindSym(curve & "_Modulus")
|
M
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# const MyCurve_CanUseNoCarryMontySquare = useNoCarryMontySquare(MyCurve_Modulus)
|
# const MyCurve_CanUseNoCarryMontySquare = useNoCarryMontySquare(MyCurve_Modulus)
|
||||||
result.add newConstStmt(
|
result.add newConstStmt(
|
||||||
used(curve & "_CanUseNoCarryMontySquare"), newCall(
|
used(curve & ff & "_CanUseNoCarryMontySquare"), newCall(
|
||||||
bindSym"useNoCarryMontySquare",
|
bindSym"useNoCarryMontySquare",
|
||||||
bindSym(curve & "_Modulus")
|
M
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# const MyCurve_R2modP = r2mod(MyCurve_Modulus)
|
# const MyCurve_R2modP = r2mod(MyCurve_Modulus)
|
||||||
result.add newConstStmt(
|
result.add newConstStmt(
|
||||||
used(curve & "_R2modP"), newCall(
|
used(curve & ff & "_R2modP"), newCall(
|
||||||
bindSym"r2mod",
|
bindSym"r2mod",
|
||||||
bindSym(curve & "_Modulus")
|
M
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# const MyCurve_NegInvModWord = negInvModWord(MyCurve_Modulus)
|
# const MyCurve_NegInvModWord = negInvModWord(MyCurve_Modulus)
|
||||||
result.add newConstStmt(
|
result.add newConstStmt(
|
||||||
used(curve & "_NegInvModWord"), newCall(
|
used(curve & ff & "_NegInvModWord"), newCall(
|
||||||
bindSym"negInvModWord",
|
bindSym"negInvModWord",
|
||||||
bindSym(curve & "_Modulus")
|
M
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# const MyCurve_montyOne = montyOne(MyCurve_Modulus)
|
# const MyCurve_montyOne = montyOne(MyCurve_Modulus)
|
||||||
result.add newConstStmt(
|
result.add newConstStmt(
|
||||||
used(curve & "_MontyOne"), newCall(
|
used(curve & ff & "_MontyOne"), newCall(
|
||||||
bindSym"montyOne",
|
bindSym"montyOne",
|
||||||
bindSym(curve & "_Modulus")
|
M
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# const MyCurve_MontyPrimeMinus1 = montyPrimeMinus1(MyCurve_Modulus)
|
# const MyCurve_MontyPrimeMinus1 = montyPrimeMinus1(MyCurve_Modulus)
|
||||||
result.add newConstStmt(
|
result.add newConstStmt(
|
||||||
used(curve & "_MontyPrimeMinus1"), newCall(
|
used(curve & ff & "_MontyPrimeMinus1"), newCall(
|
||||||
bindSym"montyPrimeMinus1",
|
bindSym"montyPrimeMinus1",
|
||||||
bindSym(curve & "_Modulus")
|
M
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# const MyCurve_InvModExponent = primeMinus2_BE(MyCurve_Modulus)
|
# const MyCurve_InvModExponent = primeMinus2_BE(MyCurve_Modulus)
|
||||||
result.add newConstStmt(
|
result.add newConstStmt(
|
||||||
used(curve & "_InvModExponent"), newCall(
|
used(curve & ff & "_InvModExponent"), newCall(
|
||||||
bindSym"primeMinus2_BE",
|
bindSym"primeMinus2_BE",
|
||||||
bindSym(curve & "_Modulus")
|
M
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# const MyCurve_PrimePlus1div2 = primePlus1div2(MyCurve_Modulus)
|
# const MyCurve_PrimePlus1div2 = primePlus1div2(MyCurve_Modulus)
|
||||||
result.add newConstStmt(
|
result.add newConstStmt(
|
||||||
used(curve & "_PrimePlus1div2"), newCall(
|
used(curve & ff & "_PrimePlus1div2"), newCall(
|
||||||
bindSym"primePlus1div2",
|
bindSym"primePlus1div2",
|
||||||
bindSym(curve & "_Modulus")
|
M
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# const MyCurve_PrimeMinus1div2_BE = primeMinus1div2_BE(MyCurve_Modulus)
|
# const MyCurve_PrimeMinus1div2_BE = primeMinus1div2_BE(MyCurve_Modulus)
|
||||||
result.add newConstStmt(
|
result.add newConstStmt(
|
||||||
used(curve & "_PrimeMinus1div2_BE"), newCall(
|
used(curve & ff & "_PrimeMinus1div2_BE"), newCall(
|
||||||
bindSym"primeMinus1div2_BE",
|
bindSym"primeMinus1div2_BE",
|
||||||
bindSym(curve & "_Modulus")
|
M
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# const MyCurve_PrimeMinus3div4_BE = primeMinus3div4_BE(MyCurve_Modulus)
|
# const MyCurve_PrimeMinus3div4_BE = primeMinus3div4_BE(MyCurve_Modulus)
|
||||||
result.add newConstStmt(
|
result.add newConstStmt(
|
||||||
used(curve & "_PrimeMinus3div4_BE"), newCall(
|
used(curve & ff & "_PrimeMinus3div4_BE"), newCall(
|
||||||
bindSym"primeMinus3div4_BE",
|
bindSym"primeMinus3div4_BE",
|
||||||
bindSym(curve & "_Modulus")
|
M
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# const MyCurve_PrimePlus1div4_BE = primePlus1div4_BE(MyCurve_Modulus)
|
# const MyCurve_PrimePlus1div4_BE = primePlus1div4_BE(MyCurve_Modulus)
|
||||||
result.add newConstStmt(
|
result.add newConstStmt(
|
||||||
used(curve & "_PrimePlus1div4_BE"), newCall(
|
used(curve & ff & "_PrimePlus1div4_BE"), newCall(
|
||||||
bindSym"primePlus1div4_BE",
|
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 =
|
proc genMainConstants(defs: var seq[CurveParams]): NimNode =
|
||||||
## Generate curves and fields main constants
|
## Generate curves and fields main constants
|
||||||
|
|
||||||
|
# MapCurveBitWidth & MapCurveOrderBitWidth
|
||||||
|
# are workaround for https://github.com/nim-lang/Nim/issues/16774
|
||||||
|
|
||||||
var Curves: seq[NimNode]
|
var Curves: seq[NimNode]
|
||||||
var MapCurveBitWidth = nnkBracket.newTree()
|
var MapCurveBitWidth = nnkBracket.newTree()
|
||||||
|
var MapCurveOrderBitWidth = nnkBracket.newTree()
|
||||||
var MapCurveFamily = nnkBracket.newTree()
|
var MapCurveFamily = nnkBracket.newTree()
|
||||||
var curveModStmts = newStmtList()
|
var curveModStmts = newStmtList()
|
||||||
var curveEllipticStmts = newStmtList()
|
var curveEllipticStmts = newStmtList()
|
||||||
@ -290,6 +294,22 @@ proc genMainConstants(defs: var seq[CurveParams]): NimNode =
|
|||||||
curveDef.order
|
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:
|
if curveDef.coef_A.kind != NoCoef and curveDef.coef_B.kind != NoCoef:
|
||||||
curveEllipticStmts.add newConstStmt(
|
curveEllipticStmts.add newConstStmt(
|
||||||
exported($curve & "_coef_A"),
|
exported($curve & "_coef_A"),
|
||||||
@ -347,6 +367,10 @@ proc genMainConstants(defs: var seq[CurveParams]): NimNode =
|
|||||||
result.add newConstStmt(
|
result.add newConstStmt(
|
||||||
exported("CurveFamilies"), MapCurveFamily
|
exported("CurveFamilies"), MapCurveFamily
|
||||||
)
|
)
|
||||||
|
# const CurveOrderBitSize: array[Curve, int] = ...
|
||||||
|
result.add newConstStmt(
|
||||||
|
exported("CurveOrderBitWidth"), MapCurveOrderBitWidth
|
||||||
|
)
|
||||||
|
|
||||||
result.add curveModStmts
|
result.add curveModStmts
|
||||||
result.add curveEllipticStmts
|
result.add curveEllipticStmts
|
||||||
|
|||||||
116
constantine/config/curves_prop_core.nim
Normal file
116
constantine/config/curves_prop_core.nim
Normal file
@ -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")
|
||||||
139
constantine/config/curves_prop_derived.nim
Normal file
139
constantine/config/curves_prop_derived.nim
Normal file
@ -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
|
type
|
||||||
Fp*[C: static Curve] = object
|
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
|
## P being the prime modulus of the Curve C
|
||||||
## Internally, data is stored in Montgomery n-residue form
|
## 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)
|
## with the magic constant chosen for convenient division (a power of 2 depending on P bitsize)
|
||||||
mres*: matchingBigInt(C)
|
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:
|
debug:
|
||||||
import ./type_bigint
|
import ./type_bigint
|
||||||
|
|
||||||
@ -26,3 +35,9 @@ debug:
|
|||||||
result.add "]("
|
result.add "]("
|
||||||
result.add $a.mres
|
result.add $a.mres
|
||||||
result.add ')'
|
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.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../config/[curves, type_bigint, type_fp],
|
../config/[curves, type_bigint, type_ff],
|
||||||
../io/[io_bigints, io_fields]
|
../io/[io_bigints, io_fields]
|
||||||
|
|
||||||
# BLS12_377 G1
|
# BLS12_377 G1
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../config/[curves, type_bigint, type_fp],
|
../config/[curves, type_bigint, type_ff],
|
||||||
../io/[io_bigints, io_fields]
|
../io/[io_bigints, io_fields]
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../config/[curves, type_bigint, type_fp],
|
../config/[curves, type_bigint, type_ff],
|
||||||
../io/[io_bigints, io_fields]
|
../io/[io_bigints, io_fields]
|
||||||
|
|
||||||
# BLS12_381 G1
|
# BLS12_381 G1
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../config/[curves, type_bigint, type_fp],
|
../config/[curves, type_bigint, type_ff],
|
||||||
../io/[io_bigints, io_fields]
|
../io/[io_bigints, io_fields]
|
||||||
|
|
||||||
# BN254_Nogami G1
|
# BN254_Nogami G1
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../config/[curves, type_bigint, type_fp],
|
../config/[curves, type_bigint, type_ff],
|
||||||
../io/[io_bigints, io_fields]
|
../io/[io_bigints, io_fields]
|
||||||
|
|
||||||
# BN254_Snarks G1
|
# BN254_Snarks G1
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../config/[curves, type_fp],
|
../config/[curves, type_ff],
|
||||||
../towers,
|
../towers,
|
||||||
../io/[io_fields, io_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.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../config/[curves, type_bigint, type_fp],
|
../config/[curves, type_bigint, type_ff],
|
||||||
../io/[io_bigints, io_fields]
|
../io/[io_bigints, io_fields]
|
||||||
|
|
||||||
# BW6_761 G1
|
# BW6_761 G1
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
std/macros,
|
std/macros,
|
||||||
../config/[curves, type_fp],
|
../config/[curves, type_ff],
|
||||||
../towers,
|
../towers,
|
||||||
./bls12_377_glv,
|
./bls12_377_glv,
|
||||||
./bls12_381_glv,
|
./bls12_381_glv,
|
||||||
|
|||||||
@ -22,18 +22,18 @@ import
|
|||||||
#
|
#
|
||||||
# ############################################################
|
# ############################################################
|
||||||
|
|
||||||
func fromUint*(dst: var Fp,
|
func fromUint*(dst: var FF,
|
||||||
src: SomeUnsignedInt) =
|
src: SomeUnsignedInt) =
|
||||||
## Parse a regular unsigned integer
|
## Parse a regular unsigned integer
|
||||||
## and store it into a Fp
|
## and store it into a Fp or Fr
|
||||||
let raw {.noinit.} = (type dst.mres).fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian)
|
let raw {.noinit.} = (typeof dst.mres).fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian)
|
||||||
dst.fromBig(raw)
|
dst.fromBig(raw)
|
||||||
|
|
||||||
func fromInt*(dst: var Fp,
|
func fromInt*(dst: var FF,
|
||||||
src: SomeInteger) =
|
src: SomeInteger) =
|
||||||
## Parse a regular signed integer
|
## Parse a regular signed integer
|
||||||
## and store it into a Fp
|
## and store it into a Fp or Fr
|
||||||
## A negative integer will be instantiated as a negated number (mod p)
|
## A negative integer will be instantiated as a negated number (mod p) or (mod r)
|
||||||
when src is SomeUnsignedInt:
|
when src is SomeUnsignedInt:
|
||||||
dst.fromUint(src)
|
dst.fromUint(src)
|
||||||
else:
|
else:
|
||||||
@ -45,7 +45,7 @@ func fromInt*(dst: var Fp,
|
|||||||
dst.fromBig(raw)
|
dst.fromBig(raw)
|
||||||
|
|
||||||
func exportRawUint*(dst: var openarray[byte],
|
func exportRawUint*(dst: var openarray[byte],
|
||||||
src: Fp,
|
src: FF,
|
||||||
dstEndianness: static Endianness) =
|
dstEndianness: static Endianness) =
|
||||||
## Serialize a finite field element to its canonical big-endian or little-endian
|
## Serialize a finite field element to its canonical big-endian or little-endian
|
||||||
## representation
|
## representation
|
||||||
@ -58,7 +58,7 @@ func exportRawUint*(dst: var openarray[byte],
|
|||||||
## I.e least significant bit is aligned to buffer boundary
|
## I.e least significant bit is aligned to buffer boundary
|
||||||
exportRawUint(dst, src.toBig(), dstEndianness)
|
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.
|
## Stringify a finite field element to hex.
|
||||||
## Note. Leading zeros are not removed.
|
## Note. Leading zeros are not removed.
|
||||||
## Result is prefixed with 0x
|
## Result is prefixed with 0x
|
||||||
@ -69,7 +69,7 @@ func appendHex*(dst: var string, f: Fp, order: static Endianness = bigEndian) =
|
|||||||
## - no leaks
|
## - no leaks
|
||||||
dst.appendHex(f.toBig(), order)
|
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.
|
## Stringify a finite field element to hex.
|
||||||
## Note. Leading zeros are not removed.
|
## Note. Leading zeros are not removed.
|
||||||
## Result is prefixed with 0x
|
## Result is prefixed with 0x
|
||||||
@ -80,11 +80,11 @@ func toHex*(f: Fp, order: static Endianness = bigEndian): string =
|
|||||||
## - no leaks
|
## - no leaks
|
||||||
result.appendHex(f, order)
|
result.appendHex(f, order)
|
||||||
|
|
||||||
func fromHex*(dst: var Fp, hexString: string) {.raises: [ValueError].}=
|
func fromHex*(dst: var FF, hexString: string) {.raises: [ValueError].}=
|
||||||
## Convert a hex string to a element of Fp
|
## Convert a hex string to a element of Fp or Fr
|
||||||
let raw {.noinit.} = fromHex(dst.mres.typeof, hexString)
|
let raw {.noinit.} = fromHex(dst.mres.typeof, hexString)
|
||||||
dst.fromBig(raw)
|
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
|
## Convert a hex string to a element of Fp
|
||||||
result.fromHex(hexString)
|
result.fromHex(hexString)
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../config/[curves, type_fp],
|
../config/[curves, type_ff],
|
||||||
../towers,
|
../towers,
|
||||||
../elliptic/[
|
../elliptic/[
|
||||||
ec_shortweierstrass_affine,
|
ec_shortweierstrass_affine,
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../config/[curves, type_fp],
|
../config/[curves, type_ff],
|
||||||
../towers,
|
../towers,
|
||||||
../elliptic/[
|
../elliptic/[
|
||||||
ec_shortweierstrass_affine,
|
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
|
# Deactivated for now Clang 360 cycles on i9-9980XE @4.1 GHz
|
||||||
var a0b0 {.noInit.}, a1b1 {.noInit.}: doubleWidth(typeof(r.c0))
|
var a0b0 {.noInit.}, a1b1 {.noInit.}: doubleWidth(typeof(r.c0))
|
||||||
var d {.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
|
a0b0.mulNoReduce(a.c0, b.c0) # 44 cycles - cumul 44
|
||||||
a1b1.mulNoReduce(a.c1, b.c1) # 44 cycles - cumul 88
|
a1b1.mulNoReduce(a.c1, b.c1) # 44 cycles - cumul 88
|
||||||
|
|||||||
@ -9,12 +9,13 @@
|
|||||||
import
|
import
|
||||||
../constantine/arithmetic/bigints,
|
../constantine/arithmetic/bigints,
|
||||||
../constantine/primitives,
|
../constantine/primitives,
|
||||||
../constantine/config/[common, curves],
|
../constantine/config/[common, curves, type_ff],
|
||||||
../constantine/elliptic/[
|
../constantine/elliptic/[
|
||||||
ec_shortweierstrass_affine,
|
ec_shortweierstrass_affine,
|
||||||
ec_shortweierstrass_projective,
|
ec_shortweierstrass_projective,
|
||||||
ec_shortweierstrass_jacobian],
|
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:
|
for i in 0 ..< a.limbs.len:
|
||||||
a.limbs[i] = SecretWord(rng.next())
|
a.limbs[i] = SecretWord(rng.next())
|
||||||
|
|
||||||
func random_unsafe[T](rng: var RngState, a: var T, C: static Curve) =
|
func random_unsafe(rng: var RngState, a: var FF) =
|
||||||
## Recursively initialize a BigInt (part of a field) or Field element
|
## Initialize a Field element
|
||||||
## Unsafe: for testing and benchmarking purposes only
|
## Unsafe: for testing and benchmarking purposes only
|
||||||
when T is BigInt:
|
var reduced, unreduced{.noInit.}: typeof(a.mres)
|
||||||
var reduced, unreduced{.noInit.}: T
|
rng.random_unsafe(unreduced)
|
||||||
rng.random_unsafe(unreduced)
|
|
||||||
|
|
||||||
# Note: a simple modulo will be biaised but it's simple and "fast"
|
# Note: a simple modulo will be biaised but it's simple and "fast"
|
||||||
reduced.reduce(unreduced, C.Mod)
|
reduced.reduce(unreduced, FF.fieldMod())
|
||||||
a.montyResidue(reduced, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul())
|
a.mres.montyResidue(reduced, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul())
|
||||||
|
|
||||||
else:
|
func random_unsafe(rng: var RngState, a: var ExtensionField) =
|
||||||
for field in fields(a):
|
## Recursively initialize an extension Field element
|
||||||
rng.random_unsafe(field, C)
|
## Unsafe: for testing and benchmarking purposes only
|
||||||
|
for field in fields(a):
|
||||||
|
rng.random_unsafe(field)
|
||||||
|
|
||||||
func random_word_highHammingWeight(rng: var RngState): BaseType =
|
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
|
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:
|
for i in 0 ..< a.limbs.len:
|
||||||
a.limbs[i] = SecretWord rng.random_word_highHammingWeight()
|
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
|
## Recursively initialize a BigInt (part of a field) or Field element
|
||||||
## Unsafe: for testing and benchmarking purposes only
|
## Unsafe: for testing and benchmarking purposes only
|
||||||
## The result will have a high Hamming Weight
|
## The result will have a high Hamming Weight
|
||||||
## to have a higher probability of triggering carries
|
## to have a higher probability of triggering carries
|
||||||
when T is BigInt:
|
var reduced, unreduced{.noInit.}: typeof(a.mres)
|
||||||
var reduced, unreduced{.noInit.}: T
|
rng.random_highHammingWeight(unreduced)
|
||||||
rng.random_highHammingWeight(unreduced)
|
|
||||||
|
|
||||||
# Note: a simple modulo will be biaised but it's simple and "fast"
|
# Note: a simple modulo will be biaised but it's simple and "fast"
|
||||||
reduced.reduce(unreduced, C.Mod)
|
reduced.reduce(unreduced, FF.fieldMod())
|
||||||
a.montyResidue(reduced, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul())
|
a.mres.montyResidue(reduced, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul())
|
||||||
|
|
||||||
else:
|
func random_highHammingWeight(rng: var RngState, a: var ExtensionField) =
|
||||||
for field in fields(a):
|
## Recursively initialize an extension Field element
|
||||||
rng.random_highHammingWeight(field, C)
|
## Unsafe: for testing and benchmarking purposes only
|
||||||
|
for field in fields(a):
|
||||||
|
rng.random_highHammingWeight(field)
|
||||||
|
|
||||||
func random_long01Seq(rng: var RngState, a: var openArray[byte]) =
|
func random_long01Seq(rng: var RngState, a: var openArray[byte]) =
|
||||||
## Initialize a bytearray
|
## Initialize a bytearray
|
||||||
@ -210,21 +213,22 @@ func random_long01Seq(rng: var RngState, a: var BigInt) =
|
|||||||
else:
|
else:
|
||||||
a.fromRawUint(buf, littleEndian)
|
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
|
## Recursively initialize a BigInt (part of a field) or Field element
|
||||||
## It is skewed towards producing strings of 1111... and 0000
|
## It is skewed towards producing strings of 1111... and 0000
|
||||||
## to trigger edge cases
|
## to trigger edge cases
|
||||||
when T is BigInt:
|
var reduced, unreduced{.noInit.}: typeof(a.mres)
|
||||||
var reduced, unreduced{.noInit.}: T
|
rng.random_long01Seq(unreduced)
|
||||||
rng.random_long01Seq(unreduced)
|
|
||||||
|
|
||||||
# Note: a simple modulo will be biaised but it's simple and "fast"
|
# Note: a simple modulo will be biaised but it's simple and "fast"
|
||||||
reduced.reduce(unreduced, C.Mod)
|
reduced.reduce(unreduced, FF.fieldMod())
|
||||||
a.montyResidue(reduced, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul())
|
a.mres.montyResidue(reduced, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul())
|
||||||
|
|
||||||
else:
|
func random_long01Seq(rng: var RngState, a: var ExtensionField) =
|
||||||
for field in fields(a):
|
## Recursively initialize an extension Field element
|
||||||
rng.random_highHammingWeight(field, C)
|
## Unsafe: for testing and benchmarking purposes only
|
||||||
|
for field in fields(a):
|
||||||
|
rng.random_long01Seq(field)
|
||||||
|
|
||||||
# Elliptic curves
|
# 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):
|
while not bool(success):
|
||||||
# Euler's criterion: there are (p-1)/2 squares in a field with modulus `p`
|
# 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
|
# 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)
|
success = trySetFromCoordX(a, fieldElem)
|
||||||
|
|
||||||
func random_unsafe_with_randZ(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Jac)) =
|
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
|
## Initialize a random curve point with Z coordinate being random
|
||||||
## Unsafe: for testing and benchmarking purposes only
|
## Unsafe: for testing and benchmarking purposes only
|
||||||
var Z{.noInit.}: a.F
|
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 fieldElem {.noInit.}: a.F
|
||||||
var success = CtFalse
|
var success = CtFalse
|
||||||
|
|
||||||
while not bool(success):
|
while not bool(success):
|
||||||
rng.random_unsafe(fieldElem, a.F.C)
|
rng.random_unsafe(fieldElem)
|
||||||
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
||||||
|
|
||||||
func random_highHammingWeight(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Aff or ECP_ShortW_Jac)) =
|
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):
|
while not bool(success):
|
||||||
# Euler's criterion: there are (p-1)/2 squares in a field with modulus `p`
|
# 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
|
# 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)
|
success = trySetFromCoordX(a, fieldElem)
|
||||||
|
|
||||||
func random_highHammingWeight_with_randZ(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Jac)) =
|
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
|
## This will be generated with a biaised RNG with high Hamming Weight
|
||||||
## to trigger carry bugs
|
## to trigger carry bugs
|
||||||
var Z{.noInit.}: a.F
|
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 fieldElem {.noInit.}: a.F
|
||||||
var success = CtFalse
|
var success = CtFalse
|
||||||
|
|
||||||
while not bool(success):
|
while not bool(success):
|
||||||
rng.random_highHammingWeight(fieldElem, a.F.C)
|
rng.random_highHammingWeight(fieldElem)
|
||||||
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
||||||
|
|
||||||
func random_long01Seq(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Aff or ECP_ShortW_Jac)) =
|
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):
|
while not bool(success):
|
||||||
# Euler's criterion: there are (p-1)/2 squares in a field with modulus `p`
|
# 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
|
# 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)
|
success = trySetFromCoordX(a, fieldElem)
|
||||||
|
|
||||||
func random_long01Seq_with_randZ(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Jac)) =
|
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
|
## that produces long bitstrings of 0 and 1
|
||||||
## to trigger edge cases
|
## to trigger edge cases
|
||||||
var Z{.noInit.}: a.F
|
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 fieldElem {.noInit.}: a.F
|
||||||
var success = CtFalse
|
var success = CtFalse
|
||||||
|
|
||||||
while not bool(success):
|
while not bool(success):
|
||||||
rng.random_long01Seq(fieldElem, a.F.C)
|
rng.random_long01Seq(fieldElem)
|
||||||
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
||||||
|
|
||||||
# Generic over any Constantine type
|
# Generic over any Constantine type
|
||||||
@ -323,7 +327,7 @@ func random_unsafe*(rng: var RngState, T: typedesc): T =
|
|||||||
elif T is BigInt:
|
elif T is BigInt:
|
||||||
rng.random_unsafe(result)
|
rng.random_unsafe(result)
|
||||||
else: # Fields
|
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 =
|
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
|
## 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:
|
elif T is BigInt:
|
||||||
rng.random_highHammingWeight(result)
|
rng.random_highHammingWeight(result)
|
||||||
else: # Fields
|
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 =
|
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
|
## 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:
|
elif T is BigInt:
|
||||||
rng.random_long01Seq(result)
|
rng.random_long01Seq(result)
|
||||||
else: # Fields
|
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 =
|
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
|
## Create a random curve element with a random Z coordinate
|
||||||
|
|||||||
@ -266,7 +266,7 @@ if __name__ == "__main__":
|
|||||||
f.write('\n\n')
|
f.write('\n\n')
|
||||||
f.write(inspect.cleandoc(f"""
|
f.write(inspect.cleandoc(f"""
|
||||||
import
|
import
|
||||||
../config/[curves, type_bigint, type_fp],
|
../config/[curves, type_bigint, type_ff],
|
||||||
../io/[io_bigints, io_fields]
|
../io/[io_bigints, io_fields]
|
||||||
|
|
||||||
# {curve} G1
|
# {curve} G1
|
||||||
|
|||||||
@ -285,7 +285,7 @@ if __name__ == "__main__":
|
|||||||
else:
|
else:
|
||||||
f.write(inspect.cleandoc("""
|
f.write(inspect.cleandoc("""
|
||||||
import
|
import
|
||||||
../config/[curves, type_fp],
|
../config/[curves, type_ff],
|
||||||
../towers,
|
../towers,
|
||||||
../io/[io_fields, io_towers]
|
../io/[io_fields, io_towers]
|
||||||
"""))
|
"""))
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/towers,
|
../constantine/towers,
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/towers,
|
../constantine/towers,
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/towers,
|
../constantine/towers,
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/towers,
|
../constantine/towers,
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import
|
|||||||
# 3rd party
|
# 3rd party
|
||||||
serialization, json_serialization,
|
serialization, json_serialization,
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[common, curves, type_bigint, type_fp],
|
../constantine/config/[common, curves, type_bigint, type_ff],
|
||||||
../constantine/towers,
|
../constantine/towers,
|
||||||
../constantine/io/[io_bigints, io_ec],
|
../constantine/io/[io_bigints, io_ec],
|
||||||
../constantine/elliptic/[
|
../constantine/elliptic/[
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
../constantine/elliptic/ec_shortweierstrass_jacobian,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./t_ec_template
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
# Internals
|
# Internals
|
||||||
../constantine/config/[type_fp, curves],
|
../constantine/config/[type_ff, curves],
|
||||||
../constantine/elliptic/ec_shortweierstrass_projective,
|
../constantine/elliptic/ec_shortweierstrass_projective,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
./t_ec_template
|
./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"
|
static: doAssert defined(testingCurves), "This modules requires the -d:testingCurves compile option"
|
||||||
|
|
||||||
proc sanity(C: static Curve) =
|
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
|
block: # 0² mod
|
||||||
var n: Fp[C]
|
var n: Fp[C]
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ mainSanity()
|
|||||||
|
|
||||||
proc mainSelectCases() =
|
proc mainSelectCases() =
|
||||||
suite "Modular Squaring: selected tricky cases" & " [" & $WordBitwidth & "-bit mode]":
|
suite "Modular Squaring: selected tricky cases" & " [" & $WordBitwidth & "-bit mode]":
|
||||||
test "P-256 [FastSquaring = " & $P256.canUseNoCarryMontySquare & "]":
|
test "P-256 [FastSquaring = " & $Fp[P256].canUseNoCarryMontySquare & "]":
|
||||||
block:
|
block:
|
||||||
# Triggered an issue in the (t[N+1], t[N]) = t[N] + (A1, A0)
|
# 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.
|
# 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)
|
doAssert bool(r_mul == r_sqr)
|
||||||
|
|
||||||
suite "Random Modular Squaring is consistent with Modular Multiplication" & " [" & $WordBitwidth & "-bit mode]":
|
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:
|
for _ in 0 ..< Iters:
|
||||||
randomCurve(P224)
|
randomCurve(P224)
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
@ -144,7 +144,7 @@ suite "Random Modular Squaring is consistent with Modular Multiplication" & " ["
|
|||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
random_long01Seq(P224)
|
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:
|
for _ in 0 ..< Iters:
|
||||||
randomCurve(P256)
|
randomCurve(P256)
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
@ -152,7 +152,7 @@ suite "Random Modular Squaring is consistent with Modular Multiplication" & " ["
|
|||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
random_long01Seq(P256)
|
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:
|
for _ in 0 ..< Iters:
|
||||||
randomCurve(BLS12_381)
|
randomCurve(BLS12_381)
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
|
|||||||
129
tests/t_fr.nim
Normal file
129
tests/t_fr.nim
Normal file
@ -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…
x
Reference in New Issue
Block a user