mirror of
https://github.com/logos-storage/constantine.git
synced 2026-01-02 13:13:07 +00:00
231 lines
7.8 KiB
Nim
231 lines
7.8 KiB
Nim
# Constantine
|
|
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
|
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
|
|
# Licensed and distributed under either of
|
|
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
|
|
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
# ############################################################
|
|
#
|
|
# Fp: Finite Field arithmetic with prime field modulus P
|
|
#
|
|
# ############################################################
|
|
|
|
# Constraints:
|
|
# - We assume that p is known at compile-time
|
|
# - We assume that p is not even:
|
|
# - Operations are done in the Montgomery domain
|
|
# - The Montgomery domain introduce a Montgomery constant that must be coprime
|
|
# with the field modulus.
|
|
# - The constant is chosen a power of 2
|
|
# => to be coprime with a power of 2, p must be odd
|
|
# - We assume that p is a prime
|
|
# - Modular inversion uses the Fermat's little theorem
|
|
# which requires a prime
|
|
|
|
import
|
|
../primitives,
|
|
../config/[common, curves],
|
|
./bigints, ./limbs_montgomery
|
|
|
|
type
|
|
Fp*[C: static Curve] = object
|
|
## All operations on a field are modulo P
|
|
## P being the prime modulus of the Curve C
|
|
## Internally, data is stored in Montgomery n-residue form
|
|
## with the magic constant chosen for convenient division (a power of 2 depending on P bitsize)
|
|
mres*: matchingBigInt(C)
|
|
|
|
debug:
|
|
func `$`*[C: static Curve](a: Fp[C]): string =
|
|
result = "Fp[" & $C
|
|
result.add "]("
|
|
result.add $a.mres
|
|
result.add ')'
|
|
|
|
# No exceptions allowed
|
|
{.push raises: [].}
|
|
{.push inline.}
|
|
|
|
# ############################################################
|
|
#
|
|
# Conversion
|
|
#
|
|
# ############################################################
|
|
|
|
func fromBig*[C: static Curve](T: type Fp[C], src: BigInt): Fp[C] {.noInit.} =
|
|
## Convert a BigInt to its Montgomery form
|
|
result.mres.montyResidue(src, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul())
|
|
|
|
func fromBig*[C: static Curve](dst: var Fp[C], src: BigInt) {.noInit.} =
|
|
## Convert a BigInt to its Montgomery form
|
|
dst.mres.montyResidue(src, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul())
|
|
|
|
func toBig*(src: Fp): auto {.noInit.} =
|
|
## Convert a finite-field element to a BigInt in natural representation
|
|
var r {.noInit.}: typeof(src.mres)
|
|
r.redc(src.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontyMul())
|
|
return r
|
|
|
|
# ############################################################
|
|
#
|
|
# Field arithmetic primitives
|
|
#
|
|
# ############################################################
|
|
#
|
|
# Note: the library currently implements generic routine for odd field modulus.
|
|
# Routines for special field modulus form:
|
|
# - Mersenne Prime (2^k - 1),
|
|
# - Generalized Mersenne Prime (NIST Prime P256: 2^256 - 2^224 + 2^192 + 2^96 - 1)
|
|
# - Pseudo-Mersenne Prime (2^m - k for example Curve25519: 2^255 - 19)
|
|
# - Golden Primes (φ^2 - φ - 1 with φ = 2^k for example Ed448-Goldilocks: 2^448 - 2^224 - 1)
|
|
# exist and can be implemented with compile-time specialization.
|
|
|
|
# Note: for `+=`, double, sum
|
|
# not(a.mres < Fp.C.Mod) is unnecessary if the prime has the form
|
|
# (2^64)^w - 1 (if using uint64 words).
|
|
# In practice I'm not aware of such prime being using in elliptic curves.
|
|
# 2^127 - 1 and 2^521 - 1 are used but 127 and 521 are not multiple of 32/64
|
|
|
|
func `==`*(a, b: Fp): CTBool[Word] =
|
|
## Constant-time equality check
|
|
a.mres == b.mres
|
|
|
|
func setZero*(a: var Fp) =
|
|
## Set ``a`` to zero
|
|
a.mres.setZero()
|
|
|
|
func setOne*(a: var Fp) =
|
|
## Set ``a`` to one
|
|
# Note: we need 1 in Montgomery residue form
|
|
# TODO: Nim codegen is not optimal it uses a temporary
|
|
# Check if the compiler optimizes it away
|
|
a.mres = Fp.C.getMontyOne()
|
|
|
|
func `+=`*(a: var Fp, b: Fp) =
|
|
## In-place addition modulo p
|
|
var overflowed = add(a.mres, b.mres)
|
|
overflowed = overflowed or not(a.mres < Fp.C.Mod)
|
|
discard csub(a.mres, Fp.C.Mod, overflowed)
|
|
|
|
func `-=`*(a: var Fp, b: Fp) =
|
|
## In-place substraction modulo p
|
|
let underflowed = sub(a.mres, b.mres)
|
|
discard cadd(a.mres, Fp.C.Mod, underflowed)
|
|
|
|
func double*(a: var Fp) =
|
|
## Double ``a`` modulo p
|
|
var overflowed = double(a.mres)
|
|
overflowed = overflowed or not(a.mres < Fp.C.Mod)
|
|
discard csub(a.mres, Fp.C.Mod, overflowed)
|
|
|
|
func sum*(r: var Fp, a, b: Fp) =
|
|
## Sum ``a`` and ``b`` into ``r`` module p
|
|
## r is initialized/overwritten
|
|
var overflowed = r.mres.sum(a.mres, b.mres)
|
|
overflowed = overflowed or not(r.mres < Fp.C.Mod)
|
|
discard csub(r.mres, Fp.C.Mod, overflowed)
|
|
|
|
func diff*(r: var Fp, a, b: Fp) =
|
|
## Substract `b` from `a` and store the result into `r`.
|
|
## `r` is initialized/overwritten
|
|
var underflowed = r.mres.diff(a.mres, b.mres)
|
|
discard cadd(r.mres, Fp.C.Mod, underflowed)
|
|
|
|
func double*(r: var Fp, a: Fp) =
|
|
## Double ``a`` into ``r``
|
|
## `r` is initialized/overwritten
|
|
var overflowed = r.mres.double(a.mres)
|
|
overflowed = overflowed or not(r.mres < Fp.C.Mod)
|
|
discard csub(r.mres, Fp.C.Mod, overflowed)
|
|
|
|
func prod*(r: var Fp, a, b: Fp) =
|
|
## Store the product of ``a`` by ``b`` modulo p into ``r``
|
|
## ``r`` is initialized / overwritten
|
|
r.mres.montyMul(a.mres, b.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontyMul())
|
|
|
|
func square*(r: var Fp, a: Fp) =
|
|
## Squaring modulo p
|
|
r.mres.montySquare(a.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare())
|
|
|
|
func neg*(r: var Fp, a: Fp) =
|
|
## Negate modulo p
|
|
discard r.mres.diff(Fp.C.Mod, a.mres)
|
|
|
|
# ############################################################
|
|
#
|
|
# Field arithmetic exponentiation and inversion
|
|
#
|
|
# ############################################################
|
|
#
|
|
# Internally those procedures will allocate extra scratchspace on the stack
|
|
|
|
func pow*(a: var Fp, exponent: BigInt) =
|
|
## Exponentiation modulo p
|
|
## ``a``: a field element to be exponentiated
|
|
## ``exponent``: a big integer
|
|
const windowSize = 5 # TODO: find best window size for each curves
|
|
a.mres.montyPow(
|
|
exponent,
|
|
Fp.C.Mod, Fp.C.getMontyOne(),
|
|
Fp.C.getNegInvModWord(), windowSize,
|
|
Fp.C.canUseNoCarryMontyMul()
|
|
)
|
|
|
|
func powUnsafeExponent*(a: var Fp, exponent: BigInt) =
|
|
## Exponentiation modulo p
|
|
## ``a``: a field element to be exponentiated
|
|
## ``exponent``: a big integer
|
|
##
|
|
## Warning ⚠️ :
|
|
## This is an optimization for public exponent
|
|
## Otherwise bits of the exponent can be retrieved with:
|
|
## - memory access analysis
|
|
## - power analysis
|
|
## - timing analysis
|
|
const windowSize = 5 # TODO: find best window size for each curves
|
|
a.mres.montyPowUnsafeExponent(
|
|
exponent,
|
|
Fp.C.Mod, Fp.C.getMontyOne(),
|
|
Fp.C.getNegInvModWord(), windowSize,
|
|
Fp.C.canUseNoCarryMontyMul()
|
|
)
|
|
|
|
|
|
# ############################################################
|
|
#
|
|
# Field arithmetic ergonomic primitives
|
|
#
|
|
# ############################################################
|
|
#
|
|
# This implements extra primitives for ergonomics.
|
|
# The in-place ones should be preferred as they avoid copies on assignment
|
|
# Two kinds:
|
|
# - Those that return a field element
|
|
# - Those that internally allocate a temporary field element
|
|
|
|
func `+`*(a, b: Fp): Fp {.noInit.} =
|
|
## Addition modulo p
|
|
result.sum(a, b)
|
|
|
|
func `-`*(a, b: Fp): Fp {.noInit.} =
|
|
## Substraction modulo p
|
|
result.diff(a, b)
|
|
|
|
func `*`*(a, b: Fp): Fp {.noInit.} =
|
|
## Multiplication modulo p
|
|
##
|
|
## It is recommended to assign with {.noInit.}
|
|
## as Fp elements are usually large and this
|
|
## routine will zero init internally the result.
|
|
result.prod(a, b)
|
|
|
|
func `*=`*(a: var Fp, b: Fp) =
|
|
## Multiplication modulo p
|
|
a.prod(a, b)
|
|
|
|
func square*(a: var Fp) =
|
|
## Squaring modulo p
|
|
a.mres.montySquare(a.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare())
|