Fp: setZero, setOne, double, in-place mul, Fp2: square

This commit is contained in:
Mamy André-Ratsimbazafy 2020-02-24 20:39:36 +01:00
parent 6b05c69652
commit 78dee73648
No known key found for this signature in database
GPG Key ID: 7B88AD1FE79492E1
4 changed files with 173 additions and 11 deletions

View File

@ -101,6 +101,18 @@ func isZero*(a: BigInt): CTBool[Word] =
## Returns true if a big int is equal to zero ## Returns true if a big int is equal to zero
a.view.isZero a.view.isZero
func setZero*(a: var BigInt) =
## Set a BigInt to 0
a.setInternalBitLength()
zeroMem(a.limbs[0].unsafeAddr, a.limbs.len * sizeof(Word))
func setOne*(a: var BigInt) =
## Set a BigInt to 1
a.setInternalBitLength()
a.limbs[0] = Word(1)
when a.limbs.len > 1:
zeroMem(a.limbs[1].unsafeAddr, (a.limbs.len-1) * sizeof(Word))
func add*[bits](a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]): CTBool[Word] = func add*[bits](a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]): CTBool[Word] =
## Constant-time big integer in-place optional addition ## Constant-time big integer in-place optional addition
## The addition is only performed if ctl is "true" ## The addition is only performed if ctl is "true"
@ -113,6 +125,12 @@ func sub*[bits](a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]): CTBool
## The result carry is always computed. ## The result carry is always computed.
sub(a.view, b.view, ctl) sub(a.view, b.view, ctl)
func double*[bits](a: var BigInt[bits], ctl: CTBool[Word]): CTBool[Word] =
## Constant-time big integer in-place optional doubling
## The doubling is only performed if ctl is "true"
## The result carry is always computed.
add(a.view, a.view, ctl)
func reduce*[aBits, mBits](r: var BigInt[mBits], a: BigInt[aBits], M: BigInt[mBits]) = func reduce*[aBits, mBits](r: var BigInt[mBits], a: BigInt[aBits], M: BigInt[mBits]) =
## Reduce `a` modulo `M` and store the result in `r` ## Reduce `a` modulo `M` and store the result in `r`
## ##
@ -145,9 +163,8 @@ func redc*[mBits](r: var BigInt[mBits], a, N: BigInt[mBits], negInvModWord: stat
## Caller must take care of properly switching between ## Caller must take care of properly switching between
## the natural and montgomery domain. ## the natural and montgomery domain.
let one = block: let one = block:
var one: BigInt[mBits] var one {.noInit.}: BigInt[mBits]
one.setInternalBitLength() one.setOne()
one.limbs[0] = Word(1)
one one
redc(r.view, a.view, one.view, N.view, Word(negInvModWord)) redc(r.view, a.view, one.view, N.view, Word(negInvModWord))

View File

@ -223,7 +223,7 @@ func isZero*(a: BigIntViewAny): CTBool[Word] =
accum = accum or a[i] accum = accum or a[i]
result = accum.isZero() result = accum.isZero()
func setZero*(a: BigIntViewMut) = func setZero(a: BigIntViewMut) =
## Set a BigInt to 0 ## Set a BigInt to 0
## It's bit size is unchanged ## It's bit size is unchanged
zeroMem(a[0].unsafeAddr, a.numLimbs() * sizeof(Word)) zeroMem(a[0].unsafeAddr, a.numLimbs() * sizeof(Word))

View File

@ -113,27 +113,54 @@ template sub(a: var Fp, b: Fp, ctl: CTBool[Word]): CTBool[Word] =
# - Golden Primes (φ^2 - φ - 1 with φ = 2^k for example Ed448-Goldilocks: 2^448 - 2^224 - 1) # - 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. # exist and can be implemented with compile-time specialization.
func setZero*(a: var Fp) =
## Set ``a`` to zero
a.setZero()
func setOne*(a: var Fp) =
## Set ``a`` to one
# Note: we need 1 in Montgomery residue form
a = Fp.C.getMontyOne()
func `+=`*(a: var Fp, b: Fp) = func `+=`*(a: var Fp, b: Fp) =
## Addition over Fp ## Addition modulo p
var ctl = add(a, b, CtTrue) var ctl = add(a, b, CtTrue)
ctl = ctl or not sub(a, Fp.C.Mod, CtFalse) ctl = ctl or not sub(a, Fp.C.Mod, CtFalse)
discard sub(a, Fp.C.Mod, ctl) discard sub(a, Fp.C.Mod, ctl)
func `-=`*(a: var Fp, b: Fp) = func `-=`*(a: var Fp, b: Fp) =
## Substraction over Fp ## Substraction modulo p
let ctl = sub(a, b, CtTrue) let ctl = sub(a, b, CtTrue)
discard add(a, Fp.C.Mod, ctl) discard add(a, Fp.C.Mod, ctl)
func double*(a: var Fp) =
## Double ``a`` modulo p
var ctl = double(a, CtTrue)
ctl = ctl or not sub(a, Fp.C.Mod, CtFalse)
discard sub(a, Fp.C.Mod, ctl)
func `*`*(a, b: Fp): Fp {.noInit.} = func `*`*(a, b: Fp): Fp {.noInit.} =
## Multiplication over Fp ## 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 Fp elements are usually large and this
## routine will zero init internally the result. ## routine will zero init internally the result.
result.mres.montyMul(a.mres, b.mres, Fp.C.Mod.mres, Fp.C.getNegInvModWord()) result.mres.montyMul(a.mres, b.mres, Fp.C.Mod.mres, Fp.C.getNegInvModWord())
func `*=`*(a: var Fp, b: Fp) =
## Multiplication modulo p
##
## Implementation note:
## - This requires a temporary field element
##
## Cost
## Stack: 1 * ModulusBitSize
var tmp{.noInit.}: Fp
tmp.mres.montyMul(a.mres, b.mres, Fp.C.Mod.mres, Fp.C.getNegInvModWord())
a = tmp
func square*(a: Fp): Fp {.noInit.} = func square*(a: Fp): Fp {.noInit.} =
## Squaring over Fp ## Squaring 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 Fp elements are usually large and this
@ -141,7 +168,7 @@ func square*(a: Fp): Fp {.noInit.} =
result.mres.montySquare(a.mres, Fp.C.Mod.mres, Fp.C.getNegInvModWord()) result.mres.montySquare(a.mres, Fp.C.Mod.mres, Fp.C.getNegInvModWord())
func pow*(a: var Fp, exponent: BigInt) = func pow*(a: var Fp, exponent: BigInt) =
## Exponentiation over Fp ## 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
@ -152,7 +179,7 @@ func pow*(a: var Fp, exponent: BigInt) =
) )
func powUnsafeExponent*(a: var Fp, exponent: BigInt) = func powUnsafeExponent*(a: var Fp, exponent: BigInt) =
## Exponentiation over Fp ## 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
## ##
@ -170,7 +197,7 @@ func powUnsafeExponent*(a: var Fp, exponent: BigInt) =
) )
func inv*(a: var Fp) = func inv*(a: var Fp) =
## Modular inversion ## Inversion modulo p
## Warning ⚠️ : ## Warning ⚠️ :
## - This assumes that `Fp` is a prime field ## - This assumes that `Fp` is a prime field
const windowSize = 5 # TODO: find best window size for each curves const windowSize = 5 # TODO: find best window size for each curves

View File

@ -0,0 +1,118 @@
# 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.
# ############################################################
#
# Quadratic Extension field over base field 𝔽p
# 𝔽p2 = 𝔽p[𝑖]
#
# ############################################################
# This implements a quadratic extension field over
# the base field 𝔽p:
# 𝔽p2 = 𝔽p[x]
# with element A of coordinates (a0, a1) represented
# by a0 + a1 x
#
# The irreducible polynomial chosen is
# x² - µ with µ = -1
# i.e. 𝔽p2 = 𝔽p[𝑖], 𝑖 being the imaginary unit
#
# Consequently, for this file Fp2 to be valid
# -1 MUST not be a square in 𝔽p
#
# µ is also chosen to simplify multiplication and squaring
# => A(a0, a1) * B(b0, b1)
# => (a0 + a1 x) * (b0 + b1 x)
# => a0 b0 + (a0 b1 + a1 b0) x + a1 b1 x²
# We need x² to be as cheap as possible
#
# References
# [1] Constructing Tower Extensions for the implementation of Pairing-Based Cryptography\
# Naomi Benger and Michael Scott, 2009\
# https://eprint.iacr.org/2009/556
#
# [2] Choosing and generating parameters for low level pairing implementation on BN curves\
# Sylvain Duquesne and Nadia El Mrabet and Safia Haloui and Franck Rondepierre, 2015\
# https://eprint.iacr.org/2015/1212
import
../arithmetic/finite_fields,
../config/curves
type
Fp2[C: static Curve] = object
## Element of the extension field
## 𝔽p2 = 𝔽p[𝑖] of a prime p
##
## with coordinates (c0, c1) such as
## c0 + c1 𝑖
##
## This requires 𝑖² = -1 to not
## be a square (mod p)
c0, c1: Fp[Curve]
func setZero*(a: var Fp2) =
## Set ``a`` to zero in 𝔽p2
## Coordinates 0 + 0𝑖
a.c0.setZero()
a.c1.setZero()
func setOne*(a: var Fp2) =
## Set ``a`` to one in 𝔽p2
## Coordinates 1 + 0𝑖
a.c0.setOne()
a.c1.setZero()
func `+=`*(a: var Fp2, b: Fp2) =
## Addition over 𝔽p2
a.c0 += b.c0
a.c1 += b.c1
func `-=`*(a: var Fp2, b: Fp2) =
## Substraction over 𝔽p2
a.c0 -= b.c0
a.c1 -= b.c1
func square*(a: Fp2): Fp2 {.noInit.} =
## Return a^2 in 𝔽p2
# (c0, c1)² => (c0 + c1𝑖
# => c0² + 2 c0 c1𝑖 + (c1𝑖
# => c0²-c1² + 2 c0 c1𝑖
# => (c0²-c1², 2 c0 c1)
# or
# => ((c0-c1)(c0+c1), 2 c0 c1)
# => ((c0-c1)(c0-c1 + 2 c1), 2 c0 c1)
#
# Costs (naive implementation)
# - 1 Multiplication 𝔽p
# - 2 Squarings 𝔽p
# - 1 Doubling 𝔽p
# - 1 Substraction 𝔽p
# Stack: 4 * ModulusBitSize (4x 𝔽p element)
#
# Or (with 1 less Mul/Squaring at the cost of 1 addition and extra 2 𝔽p stack space)
#
# - 2 Multiplications 𝔽p
# - 1 Addition 𝔽p
# - 1 Doubling 𝔽p
# - 1 Substraction 𝔽p
# Stack: 6 * ModulusBitSize (4x 𝔽p element + 1 named temporaries + 1 multiplication temporary)
# as multiplications require a (shared) internal temporary
var c0mc1 = a.c0
c0mc1 -= a.c1 # c0mc1 = c0 - c1 [1 Sub]
result.c0 = c0mc1 # result.c0 = c0 - c1
result.c1 = a.c1
result.c1.double() # result.c1 = 2 c1 [1 Dbl, 1 Sub]
result.c0 += result.c1 # result.c0 = c0 - c1 + 2 c1 [1 Add, 1 Dbl, 1 Sub]
result.c0 *= c0mc1 # result.c0 = (c0 + c1)(c0 - c1) = c0² - c1² [1 Mul, 1 Add, 1 Dbl, 1 Sub]
result.c1 *= a.c0 # result.c1 = 2 c1 c0 [2 Mul, 1 Add, 1 Dbl, 1 Sub]