From 78dee73648153f6c5c5358a90658c1877f9264c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mamy=20Andr=C3=A9-Ratsimbazafy?= Date: Mon, 24 Feb 2020 20:39:36 +0100 Subject: [PATCH] Fp: setZero, setOne, double, in-place mul, Fp2: square --- constantine/arithmetic/bigints_checked.nim | 23 +++- constantine/arithmetic/bigints_raw.nim | 2 +- constantine/arithmetic/finite_fields.nim | 41 ++++-- .../tower_field_extensions/fp2_complex.nim | 118 ++++++++++++++++++ 4 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 constantine/tower_field_extensions/fp2_complex.nim diff --git a/constantine/arithmetic/bigints_checked.nim b/constantine/arithmetic/bigints_checked.nim index bb434c8..38dc143 100644 --- a/constantine/arithmetic/bigints_checked.nim +++ b/constantine/arithmetic/bigints_checked.nim @@ -101,6 +101,18 @@ func isZero*(a: BigInt): CTBool[Word] = ## Returns true if a big int is equal to zero 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] = ## Constant-time big integer in-place optional addition ## 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. 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]) = ## 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 ## the natural and montgomery domain. let one = block: - var one: BigInt[mBits] - one.setInternalBitLength() - one.limbs[0] = Word(1) + var one {.noInit.}: BigInt[mBits] + one.setOne() one redc(r.view, a.view, one.view, N.view, Word(negInvModWord)) diff --git a/constantine/arithmetic/bigints_raw.nim b/constantine/arithmetic/bigints_raw.nim index b582b64..3046ad8 100644 --- a/constantine/arithmetic/bigints_raw.nim +++ b/constantine/arithmetic/bigints_raw.nim @@ -223,7 +223,7 @@ func isZero*(a: BigIntViewAny): CTBool[Word] = accum = accum or a[i] result = accum.isZero() -func setZero*(a: BigIntViewMut) = +func setZero(a: BigIntViewMut) = ## Set a BigInt to 0 ## It's bit size is unchanged zeroMem(a[0].unsafeAddr, a.numLimbs() * sizeof(Word)) diff --git a/constantine/arithmetic/finite_fields.nim b/constantine/arithmetic/finite_fields.nim index 39d9d63..3c80228 100644 --- a/constantine/arithmetic/finite_fields.nim +++ b/constantine/arithmetic/finite_fields.nim @@ -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) # 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) = - ## Addition over Fp + ## Addition modulo p var ctl = add(a, b, CtTrue) ctl = ctl or not sub(a, Fp.C.Mod, CtFalse) discard sub(a, Fp.C.Mod, ctl) func `-=`*(a: var Fp, b: Fp) = - ## Substraction over Fp + ## Substraction modulo p let ctl = sub(a, b, CtTrue) 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.} = - ## Multiplication over Fp + ## 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.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.} = - ## Squaring over Fp + ## Squaring modulo p ## ## It is recommended to assign with {.noInit.} ## 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()) func pow*(a: var Fp, exponent: BigInt) = - ## Exponentiation over Fp + ## 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 @@ -152,7 +179,7 @@ func pow*(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 ## ``exponent``: a big integer ## @@ -170,7 +197,7 @@ func powUnsafeExponent*(a: var Fp, exponent: BigInt) = ) func inv*(a: var Fp) = - ## Modular inversion + ## Inversion modulo p ## Warning ⚠️ : ## - This assumes that `Fp` is a prime field const windowSize = 5 # TODO: find best window size for each curves diff --git a/constantine/tower_field_extensions/fp2_complex.nim b/constantine/tower_field_extensions/fp2_complex.nim new file mode 100644 index 0000000..89f7549 --- /dev/null +++ b/constantine/tower_field_extensions/fp2_complex.nim @@ -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]