From 8da9e20ebbcfa109a89cf824ef86de4104ec31f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mamy=20Andr=C3=A9-Ratsimbazafy?= Date: Sat, 8 Feb 2020 17:03:30 +0100 Subject: [PATCH] Cosmetic changes: dumpHex with 0x prefix, montgomery magic part of curve param --- constantine/curves_config.nim | 3 + constantine/field_fp.nim | 2 + constantine/io.nim | 14 ++-- constantine/montgomery.nim | 57 +--------------- constantine/montgomery_magic.nim | 71 ++++++++++++++++++++ constantine/private/curves_config_parser.nim | 24 ++++++- tests/test_io.nim | 8 +-- 7 files changed, 112 insertions(+), 67 deletions(-) create mode 100644 constantine/montgomery_magic.nim diff --git a/constantine/curves_config.nim b/constantine/curves_config.nim index bff1929..616924a 100644 --- a/constantine/curves_config.nim +++ b/constantine/curves_config.nim @@ -32,6 +32,9 @@ import # - const CurveBitSize: array[Curve, int] # - proc Mod(curve: static Curve): auto # which returns the field modulus of the curve +# - proc MontyMagic(curve: static Curve): static Word = +# which returns the Montgomery magic constant +# associated with the curve modulus 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 a527001..78bb5cb 100644 --- a/constantine/field_fp.nim +++ b/constantine/field_fp.nim @@ -87,6 +87,7 @@ func shiftAdd*(a: var Fp, c: Word) = 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 + return else: ## Multiple limbs @@ -161,3 +162,4 @@ func shiftAdd*(a: var Fp, c: Word) = add(a, Fp.C.Mod, neg) sub(a, Fp.C.Mod, tooBig) + return diff --git a/constantine/io.nim b/constantine/io.nim index 8e734d9..4778b6b 100644 --- a/constantine/io.nim +++ b/constantine/io.nim @@ -249,15 +249,16 @@ func toHex(bytes: openarray[byte], order: static[Endianness]): string = ## Convert a byte-array to its hex representation ## Output is in lowercase and not prefixed. const hexChars = "0123456789abcdef" - - result = newString(2 * bytes.len) + result = newString(2 + 2 * bytes.len) + result[0] = '0' + result[1] = 'x' for i in 0 ..< bytes.len: when order == system.cpuEndian: - result[2*i] = hexChars[int bytes[i] shr 4 and 0xF] - result[2*i+1] = hexChars[int bytes[i] and 0xF] + result[2 + 2*i] = hexChars[int bytes[i] shr 4 and 0xF] + result[2 + 2*i+1] = hexChars[int bytes[i] and 0xF] else: - result[2*i] = hexChars[int bytes[bytes.high - i] shr 4 and 0xF] - result[2*i+1] = hexChars[int bytes[bytes.high - i] and 0xF] + result[2 + 2*i] = hexChars[int bytes[bytes.high - i] shr 4 and 0xF] + result[2 + 2*i+1] = hexChars[int bytes[bytes.high - i] and 0xF] # ############################################################ @@ -285,6 +286,7 @@ func fromHex*(T: type BigInt, s: string): T = func dumpHex*(big: BigInt, order: static Endianness = bigEndian): string = ## Stringify an int to hex. ## Note. Leading zeros are not removed. + ## Result is prefixed with 0x ## ## This is a raw memory dump. Output will be padded with 0 ## if the big int does not use the full memory allocated for it. diff --git a/constantine/montgomery.nim b/constantine/montgomery.nim index 8f74eec..9715f26 100644 --- a/constantine/montgomery.nim +++ b/constantine/montgomery.nim @@ -15,61 +15,6 @@ import ./word_types, ./bigints, ./field_fp, ./curves_config -from bitops import fastLog2 - # This will only be used at compile-time - # so no constant-time worries (it is constant-time if using the De Bruijn multiplication) - -func montyMagic*(M: static BigInt): static Word = - ## Returns the Montgomery domain magic number for the input modulus: - ## -1/M[0] mod LimbSize - ## M[0] is the least significant limb of M - ## M must be odd and greater than 2. - - # Test vectors: https://www.researchgate.net/publication/4107322_Montgomery_modular_multiplication_architecture_for_public_key_cryptosystems - # on p354 - # Reference C impl: http://www.hackersdelight.org/hdcodetxt/mont64.c.txt - - # ###################################################################### - # Implementation of modular multiplication inverse - # Assuming 2 positive integers a and m the modulo - # - # We are looking for z that solves `az ≡ 1 mod m` - # - # References: - # - Knuth, The Art of Computer Programming, Vol2 p342 - # - Menezes, Handbook of Applied Cryptography (HAC), p610 - # http://cacr.uwaterloo.ca/hac/about/chap14.pdf - - # Starting from the extended GCD formula (Bezout identity), - # `ax + by = gcd(x,y)` with input x,y and outputs a, b, gcd - # We assume a and m are coprimes, i.e. gcd is 1, otherwise no inverse - # `ax + my = 1` <=> `ax + my ≡ 1 mod m` <=> `ax ≡ 1 mod m` - - # For Montgomery magic number, we are in a special case - # where a = M and m = 2^LimbSize. - # For a and m to be coprimes, a must be odd. - - # M being a power of 2 greatly simplifies computation: - # - https://crypto.stackexchange.com/questions/47493/how-to-determine-the-multiplicative-inverse-modulo-64-or-other-power-of-two - # - http://groups.google.com/groups?selm=1994Apr6.093116.27805%40mnemosyne.cs.du.edu - # - https://mumble.net/~campbell/2015/01/21/inverse-mod-power-of-two - # - https://eprint.iacr.org/2017/411 - - # We have the following relation - # ax ≡ 1 (mod 2^k) <=> ax(2 - ax) ≡ 1 (mod 2^(2k)) - # - # To get -1/M0 mod LimbSize - # we can either negate the resulting x of `ax(2 - ax) ≡ 1 (mod 2^(2k))` - # or do ax(2 + ax) ≡ 1 (mod 2^(2k)) - - const - M0 = M.limbs[0] - k = fastLog2(WordBitSize) - - result = M0 # Start from an inverse of M0 modulo 2, M0 is odd and it's own inverse - for _ in static(0 ..< k): - result *= 2 + M * result # x' = x(2 + ax) (`+` to avoid negating at the end) - func toMonty*[C: static Curve](a: Fp[C]): Montgomery[C] = ## Convert a big integer over Fp to it's montgomery representation ## over Fp. @@ -77,5 +22,5 @@ func toMonty*[C: static Curve](a: Fp[C]): Montgomery[C] = ## of words needed to represent p in base 2^LimbSize result = a - for i in static(countdown(C.Mod.limbs.high, 0)): + for i in static(countdown(C.Mod.limbs.high, 1)): shiftAdd(result, 0) diff --git a/constantine/montgomery_magic.nim b/constantine/montgomery_magic.nim new file mode 100644 index 0000000..87da9a8 --- /dev/null +++ b/constantine/montgomery_magic.nim @@ -0,0 +1,71 @@ +# 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. + +# ############################################################ +# +# Compute the Montgomery Magic Number +# +# ############################################################ + +import + ./word_types, ./bigints + +from bitops import fastLog2 + # This will only be used at compile-time + # so no constant-time worries (it is constant-time if using the De Bruijn multiplication) + +func montyMagic*(M: static BigInt): static Word {.inline.} = + ## Returns the Montgomery domain magic number for the input modulus: + ## -1/M[0] mod LimbSize + ## M[0] is the least significant limb of M + ## M must be odd and greater than 2. + + # Test vectors: https://www.researchgate.net/publication/4107322_Montgomery_modular_multiplication_architecture_for_public_key_cryptosystems + # on p354 + # Reference C impl: http://www.hackersdelight.org/hdcodetxt/mont64.c.txt + + # ###################################################################### + # Implementation of modular multiplicative inverse + # Assuming 2 positive integers a and m the modulo + # + # We are looking for z that solves `az ≡ 1 mod m` + # + # References: + # - Knuth, The Art of Computer Programming, Vol2 p342 + # - Menezes, Handbook of Applied Cryptography (HAC), p610 + # http://cacr.uwaterloo.ca/hac/about/chap14.pdf + + # Starting from the extended GCD formula (Bezout identity), + # `ax + by = gcd(x,y)` with input x,y and outputs a, b, gcd + # We assume a and m are coprimes, i.e. gcd is 1, otherwise no inverse + # `ax + my = 1` <=> `ax + my ≡ 1 mod m` <=> `ax ≡ 1 mod m` + + # For Montgomery magic number, we are in a special case + # where a = M and m = 2^LimbSize. + # For a and m to be coprimes, a must be odd. + + # `m` (2^LimbSize) being a power of 2 greatly simplifies computation: + # - https://crypto.stackexchange.com/questions/47493/how-to-determine-the-multiplicative-inverse-modulo-64-or-other-power-of-two + # - http://groups.google.com/groups?selm=1994Apr6.093116.27805%40mnemosyne.cs.du.edu + # - https://mumble.net/~campbell/2015/01/21/inverse-mod-power-of-two + # - https://eprint.iacr.org/2017/411 + + # We have the following relation + # ax ≡ 1 (mod 2^k) <=> ax(2 - ax) ≡ 1 (mod 2^(2k)) + # + # To get -1/M0 mod LimbSize + # we can either negate the resulting x of `ax(2 - ax) ≡ 1 (mod 2^(2k))` + # or do ax(2 + ax) ≡ 1 (mod 2^(2k)) + + const + M0 = M.limbs[0] + k = fastLog2(WordBitSize) + + result = M0 # Start from an inverse of M0 modulo 2, M0 is odd and it's own inverse + for _ in static(0 ..< k): + result *= 2 + M * result # x' = x(2 + ax) (`+` to avoid negating at the end) diff --git a/constantine/private/curves_config_parser.nim b/constantine/private/curves_config_parser.nim index b6ad380..6b793a3 100644 --- a/constantine/private/curves_config_parser.nim +++ b/constantine/private/curves_config_parser.nim @@ -10,7 +10,7 @@ import # Standard library macros, # Internal - ../io, ../bigints + ../io, ../bigints, ../montgomery_magic # Macro to parse declarative curves configuration. @@ -112,6 +112,7 @@ macro declareCurves*(curves: untyped): untyped = result = newStmtList() + # type Curve = enum result.add newEnum( name = ident"Curve", fields = Curves, @@ -119,6 +120,7 @@ macro declareCurves*(curves: untyped): untyped = pure = false ) + # const CurveBitSize*: array[Curve, int] = ... let cbs = ident("CurveBitSize") result.add quote do: const `cbs`*: array[Curve, int] = `CurveBitSize` @@ -134,6 +136,8 @@ macro declareCurves*(curves: untyped): untyped = ) ) ) + + # proc Mod(curve: static Curve): auto result.add newProc( name = nnkPostfix.newTree(ident"*", ident"Mod"), params = [ @@ -148,4 +152,22 @@ macro declareCurves*(curves: untyped): untyped = pragmas = nnkPragma.newTree(ident"compileTime") ) + # proc MontyMagic(curve: static Curve): static Word + result.add newProc( + name = nnkPostfix.newTree(ident"*", ident"MontyMagic"), + params = [ + ident"auto", + newIdentDefs( + name = ident"curve", + kind = nnkStaticTy.newTree(ident"Curve") + ) + ], + body = newCall( + bindSym"montyMagic", + newCall(ident"Mod", ident"curve") + ), + procType = nnkFuncDef, + pragmas = nnkPragma.newTree(ident"compileTime") + ) + # echo result.toStrLit diff --git a/tests/test_io.nim b/tests/test_io.nim index 8412da3..fe9db54 100644 --- a/tests/test_io.nim +++ b/tests/test_io.nim @@ -64,21 +64,21 @@ suite "IO": test "Round trip on elliptic curve constants": block: # Secp256k1 - https://en.bitcoin.it/wiki/Secp256k1 - const p = "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" + const p = "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" let x = fromHex(BigInt[256], p) let hex = x.dumpHex(bigEndian) check: p == hex - block: # alt-BN128 - https://github.com/ethereum/py_ecc/blob/master/py_ecc/fields/field_properties.py - const p = "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47" + block: # BN254 - https://github.com/ethereum/py_ecc/blob/master/py_ecc/fields/field_properties.py + const p = "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47" let x = fromHex(BigInt[254], p) let hex = x.dumpHex(bigEndian) check: p == hex block: # BLS12-381 - https://github.com/ethereum/py_ecc/blob/master/py_ecc/fields/field_properties.py - const p = "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" + const p = "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" let x = fromHex(BigInt[381], p) let hex = x.dumpHex(bigEndian)