From 18625cc5ac2ff41a91b069579cb89b83842e61c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mamy=20Andr=C3=A9-Ratsimbazafy?= Date: Sat, 8 Feb 2020 15:33:05 +0100 Subject: [PATCH] Allow compilation of FInite Field and Montgomery domain, workaround showstopper bug: https://github.com/nim-lang/Nim/issues/11142 and #5 --- constantine/curves_config.nim | 5 ++ constantine/field_fp.nim | 91 ++++++++------------ constantine/montgomery.nim | 6 +- constantine/private/curves_config_parser.nim | 5 +- 4 files changed, 49 insertions(+), 58 deletions(-) diff --git a/constantine/curves_config.nim b/constantine/curves_config.nim index a2db25a..bff1929 100644 --- a/constantine/curves_config.nim +++ b/constantine/curves_config.nim @@ -27,6 +27,11 @@ import # to name curves according to the length of the prime bit length. # i.e. the BN254 was previously named BN128. +# Generates: +# - type Curve = enum +# - const CurveBitSize: array[Curve, int] +# - proc Mod(curve: static Curve): auto +# which returns the field modulus of the curve declareCurves: # Barreto-Naehrig curve, Prime 254 bit, 128-bit security, https://eprint.iacr.org/2013/879.pdf # Usage: Zero-Knowledge Proofs / zkSNARKs in ZCash and Ethereum 1 diff --git a/constantine/field_fp.nim b/constantine/field_fp.nim index d41ae41..a527001 100644 --- a/constantine/field_fp.nim +++ b/constantine/field_fp.nim @@ -14,21 +14,22 @@ # We assume that p is prime known at compile-time -import - ./word_types, ./bigints - +import ./word_types, ./bigints, ./curves_config from ./private/word_types_internal import unsafe_div2n1n -type - Fp*[P: static BigInt] = object - ## P is a prime number - ## All operations on a field are modulo P - value: type(P) +static: echo CurveBitSize - Montgomery*[M: static BigInt] = object - ## All operations in the Montgomery domain - ## are modulo M. M **must** be odd - value: type(M) +type + Fp*[C: static Curve] = object + ## P is the prime modulus of the Curve C + ## All operations on a field are modulo P + value: BigInt[CurveBitSize[C]] + + Montgomery*[C: static Curve] = object + ## P is the prime modulus of the Curve C + ## All operations in the Montgomery domain are modulo P + ## P **must** be odd + value: BigInt[CurveBitSize[C]] # ############################################################ # @@ -67,43 +68,43 @@ func `+`*(a, b: Fp): Fp = result = a var ctl = add(result, b, CtTrue) - ctl = ctl or not sub(result, Fp.P, CtFalse) - sub(result, Fp.P, ctl) + ctl = ctl or not sub(result, Fp.C.Mod, CtFalse) + sub(result, Fp.C.Mod, ctl) -template shiftAddImpl(a: var Fp, c: Word) = +func shiftAdd*(a: var Fp, c: Word) = ## Shift-accumulate - ## Shift input a by a word and add c. + ## Shift input `a` by a word and add `c`. ## ## With a word W = 2^WordBitSize and a field Fp ## Does a <- a * W + c (mod p) const len = a.value.limbs.len - when Fp.P.bits <= WordBitSize: + when Fp.C.Mod.bits <= WordBitSize: # If the prime fits in a single limb var q: Word # (hi, lo) = a * 2^63 + c - let hi = a[0] shr 1 # 64 - 63 = 1 - let lo = (a[0] shl WordBitSize) or c # Assumes most-significant bit in c is not set - unsafeDiv2n1n(q, a[0], hi, lo, Fp.P.limbs[0]) # (hi, lo) mod P + let hi = a[0] shr 1 # 64 - 63 = 1 + let lo = (a[0] shl WordBitSize) or c # Assumes most-significant bit in c is not set + unsafeDiv2n1n(q, a[0], hi, lo, Fp.C.Mod.limbs[0]) # (hi, lo) mod P else: ## Multiple limbs let hi = a[^1] # Save the high word to detect carries - const R = Fp.P.bits and WordBitSize # R = bits mod 64 + const R = Fp.C.Mod.bits and WordBitSize # R = bits mod 64 when R == 0: # If the number of bits is a multiple of 64 let a1 = a[^2] # let a0 = a[^1] # moveMem(a[1], a[0], (len-1) * Word.sizeof) # we can just shift words a[0] = c # and replace the first one by c - const p0 = Fp.P[^1] + const p0 = Fp.C.Mod[^1] else: # Need to deal with partial word shifts at the edge. let a1 = ((a[^2] shl (WordBitSize-R)) or (a[^3] shr R)) and MaxWord let a0 = ((a[^1] shl (WordBitSize-R)) or (a[^2] shr R)) and MaxWord moveMem(a[1], a[0], (len-1) * Word.sizeof) a[0] = c - const p0 = ((Fp.P[^1] shl (WordBitSize-R)) or (Fp.P[^2] shr R)) and MaxWord + const p0 = ((Fp.C.Mod[^1] shl (WordBitSize-R)) or (Fp.C.Mod[^2] shr R)) and MaxWord # p0 has its high bit set. (a0, a1)/p0 fits in a limb. # Get a quotient q, at most we will be 2 iterations off @@ -113,9 +114,9 @@ template shiftAddImpl(a: var Fp, c: Word) = a_hi = a0 shr 1 # 64 - 63 = 1 a_lo = (a0 shl WordBitSize) or a1 var q, r: Word - q = unsafeDiv2n1n(q, r, a_hi, a_lo, p0) # Estimate quotient + q = unsafeDiv2n1n(q, r, a_hi, a_lo, p0) # Estimate quotient q = mux( # If n_hi == divisor - a0 == b0, MaxWord, # Quotient == MaxWord (0b0111...1111) + a0 == b0, MaxWord, # Quotient == MaxWord (0b0111...1111) mux( q == 0, 0, # elif q == 0, true quotient = 0 q - 1 # else instead of being of by 0, 1 or 2 @@ -124,30 +125,30 @@ template shiftAddImpl(a: var Fp, c: Word) = # Now substract a*2^63 - q*p var carry = Word(0) - var over_p = Word(1) # Track if quotient than the modulus + var over_p = Word(1) # Track if quotient than the modulus - for i in static(0 ..< Fp.P.limbs.len): + for i in static(0 ..< Fp.C.Mod.limbs.len): var qp_lo: Word block: # q*p qp_hi: Word - unsafeExtendedPrecMul(qp_hi, qp_lo, q, Fp.P[i]) # q * p + unsafeExtendedPrecMul(qp_hi, qp_lo, q, Fp.C.Mod[i]) # q * p assert qp_lo.isMsbSet.not assert carry.isMsbSet.not - qp_lo += carry # Add carry from previous limb + qp_lo += carry # Add carry from previous limb let qp_carry = qp_lo.isMsbSet - carry = mux(qp_carry, qp_hi + Word(1), qp_hi) # New carry + carry = mux(qp_carry, qp_hi + Word(1), qp_hi) # New carry - qp_lo = qp_lo and MaxWord # Normalize to u63 + qp_lo = qp_lo and MaxWord # Normalize to u63 block: # a*2^63 - q*p a[i] -= qp_lo - carry += Word(a[i].isMsbSet) # Adjust if borrow - a[i] = a[i] and MaxWord # Normalize to u63 + carry += Word(a[i].isMsbSet) # Adjust if borrow + a[i] = a[i] and MaxWord # Normalize to u63 over_p = mux( - a[i] == Fp.P[i], over_p, - a[i] > Fp.P[i] + a[i] == Fp.C.Mod[i], over_p, + a[i] > Fp.C.Mod[i] ) # Fix quotient, the true quotient is either q-1, q or q+1 @@ -158,21 +159,5 @@ template shiftAddImpl(a: var Fp, c: Word) = let neg = carry < hi let tooBig = not neg and (over_p or (carry < hi)) - add(a, Fp.P, neg) - sub(a, Fp.P, tooBig) - -func shiftAdd*(a: var Fp, c: Word) = - ## Shift-accumulate modulo P - ## Shift input a by a word and add c modulo P - ## - ## With a word W = 2^WordBitSize and a field Fp - ## Does a <- a * W + c (mod p) - shiftAddImpl(a, c) - -func shiftAdd*(a: var Fp, c: static Word) = - ## Scale-accumulate modulo P - ## Shift input a by a word and add c modulo P - ## - ## With a word W = 2^WordBitSize and a field Fp - ## Does a <- a * W + c (mod p) - shiftAddImpl(a, c) + add(a, Fp.C.Mod, neg) + sub(a, Fp.C.Mod, tooBig) diff --git a/constantine/montgomery.nim b/constantine/montgomery.nim index 2c2cc26..8f74eec 100644 --- a/constantine/montgomery.nim +++ b/constantine/montgomery.nim @@ -13,7 +13,7 @@ # ############################################################ import - ./word_types, ./bigints, ./field_fp + ./word_types, ./bigints, ./field_fp, ./curves_config from bitops import fastLog2 # This will only be used at compile-time @@ -70,12 +70,12 @@ func montyMagic*(M: static BigInt): static Word = for _ in static(0 ..< k): result *= 2 + M * result # x' = x(2 + ax) (`+` to avoid negating at the end) -func toMonty*[P: static BigInt](a: Fp[P]): Montgomery[P] = +func toMonty*[C: static Curve](a: Fp[C]): Montgomery[C] = ## Convert a big integer over Fp to it's montgomery representation ## over Fp. ## i.e. Does "a * (2^LimbSize)^W (mod p), where W is the number ## of words needed to represent p in base 2^LimbSize result = a - for i in static(countdown(P.limbs.high, 0)): + for i in static(countdown(C.Mod.limbs.high, 0)): shiftAdd(result, 0) diff --git a/constantine/private/curves_config_parser.nim b/constantine/private/curves_config_parser.nim index 83b1a95..b6ad380 100644 --- a/constantine/private/curves_config_parser.nim +++ b/constantine/private/curves_config_parser.nim @@ -119,8 +119,9 @@ macro declareCurves*(curves: untyped): untyped = pure = false ) + let cbs = ident("CurveBitSize") result.add quote do: - const CurveBitSize: array[Curve, int] = `CurveBitSize` + const `cbs`*: array[Curve, int] = `CurveBitSize` result.add curveModStmts @@ -134,7 +135,7 @@ macro declareCurves*(curves: untyped): untyped = ) ) result.add newProc( - name = nnkPostfix.newTree(ident"*", ident"fieldModulus"), + name = nnkPostfix.newTree(ident"*", ident"Mod"), params = [ ident"auto", newIdentDefs(