Cosmetic changes: dumpHex with 0x prefix, montgomery magic part of curve param
This commit is contained in:
parent
18625cc5ac
commit
8da9e20ebb
|
@ -32,6 +32,9 @@ import
|
||||||
# - const CurveBitSize: array[Curve, int]
|
# - const CurveBitSize: array[Curve, int]
|
||||||
# - proc Mod(curve: static Curve): auto
|
# - proc Mod(curve: static Curve): auto
|
||||||
# which returns the field modulus of the curve
|
# 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:
|
declareCurves:
|
||||||
# Barreto-Naehrig curve, Prime 254 bit, 128-bit security, https://eprint.iacr.org/2013/879.pdf
|
# 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
|
# Usage: Zero-Knowledge Proofs / zkSNARKs in ZCash and Ethereum 1
|
||||||
|
|
|
@ -87,6 +87,7 @@ func shiftAdd*(a: var Fp, c: Word) =
|
||||||
let hi = a[0] shr 1 # 64 - 63 = 1
|
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
|
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
|
unsafeDiv2n1n(q, a[0], hi, lo, Fp.C.Mod.limbs[0]) # (hi, lo) mod P
|
||||||
|
return
|
||||||
|
|
||||||
else:
|
else:
|
||||||
## Multiple limbs
|
## Multiple limbs
|
||||||
|
@ -161,3 +162,4 @@ func shiftAdd*(a: var Fp, c: Word) =
|
||||||
|
|
||||||
add(a, Fp.C.Mod, neg)
|
add(a, Fp.C.Mod, neg)
|
||||||
sub(a, Fp.C.Mod, tooBig)
|
sub(a, Fp.C.Mod, tooBig)
|
||||||
|
return
|
||||||
|
|
|
@ -249,15 +249,16 @@ func toHex(bytes: openarray[byte], order: static[Endianness]): string =
|
||||||
## Convert a byte-array to its hex representation
|
## Convert a byte-array to its hex representation
|
||||||
## Output is in lowercase and not prefixed.
|
## Output is in lowercase and not prefixed.
|
||||||
const hexChars = "0123456789abcdef"
|
const hexChars = "0123456789abcdef"
|
||||||
|
result = newString(2 + 2 * bytes.len)
|
||||||
result = newString(2 * bytes.len)
|
result[0] = '0'
|
||||||
|
result[1] = 'x'
|
||||||
for i in 0 ..< bytes.len:
|
for i in 0 ..< bytes.len:
|
||||||
when order == system.cpuEndian:
|
when order == system.cpuEndian:
|
||||||
result[2*i] = hexChars[int bytes[i] shr 4 and 0xF]
|
result[2 + 2*i] = hexChars[int bytes[i] shr 4 and 0xF]
|
||||||
result[2*i+1] = hexChars[int bytes[i] and 0xF]
|
result[2 + 2*i+1] = hexChars[int bytes[i] and 0xF]
|
||||||
else:
|
else:
|
||||||
result[2*i] = hexChars[int bytes[bytes.high - i] shr 4 and 0xF]
|
result[2 + 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+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 =
|
func dumpHex*(big: BigInt, order: static Endianness = bigEndian): string =
|
||||||
## Stringify an int to hex.
|
## Stringify an int to hex.
|
||||||
## Note. Leading zeros are not removed.
|
## Note. Leading zeros are not removed.
|
||||||
|
## Result is prefixed with 0x
|
||||||
##
|
##
|
||||||
## This is a raw memory dump. Output will be padded with 0
|
## 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.
|
## if the big int does not use the full memory allocated for it.
|
||||||
|
|
|
@ -15,61 +15,6 @@
|
||||||
import
|
import
|
||||||
./word_types, ./bigints, ./field_fp, ./curves_config
|
./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] =
|
func toMonty*[C: static Curve](a: Fp[C]): Montgomery[C] =
|
||||||
## Convert a big integer over Fp to it's montgomery representation
|
## Convert a big integer over Fp to it's montgomery representation
|
||||||
## over Fp.
|
## 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
|
## of words needed to represent p in base 2^LimbSize
|
||||||
|
|
||||||
result = a
|
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)
|
shiftAdd(result, 0)
|
||||||
|
|
|
@ -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)
|
|
@ -10,7 +10,7 @@ import
|
||||||
# Standard library
|
# Standard library
|
||||||
macros,
|
macros,
|
||||||
# Internal
|
# Internal
|
||||||
../io, ../bigints
|
../io, ../bigints, ../montgomery_magic
|
||||||
|
|
||||||
# Macro to parse declarative curves configuration.
|
# Macro to parse declarative curves configuration.
|
||||||
|
|
||||||
|
@ -112,6 +112,7 @@ macro declareCurves*(curves: untyped): untyped =
|
||||||
|
|
||||||
result = newStmtList()
|
result = newStmtList()
|
||||||
|
|
||||||
|
# type Curve = enum
|
||||||
result.add newEnum(
|
result.add newEnum(
|
||||||
name = ident"Curve",
|
name = ident"Curve",
|
||||||
fields = Curves,
|
fields = Curves,
|
||||||
|
@ -119,6 +120,7 @@ macro declareCurves*(curves: untyped): untyped =
|
||||||
pure = false
|
pure = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# const CurveBitSize*: array[Curve, int] = ...
|
||||||
let cbs = ident("CurveBitSize")
|
let cbs = ident("CurveBitSize")
|
||||||
result.add quote do:
|
result.add quote do:
|
||||||
const `cbs`*: array[Curve, int] = `CurveBitSize`
|
const `cbs`*: array[Curve, int] = `CurveBitSize`
|
||||||
|
@ -134,6 +136,8 @@ macro declareCurves*(curves: untyped): untyped =
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# proc Mod(curve: static Curve): auto
|
||||||
result.add newProc(
|
result.add newProc(
|
||||||
name = nnkPostfix.newTree(ident"*", ident"Mod"),
|
name = nnkPostfix.newTree(ident"*", ident"Mod"),
|
||||||
params = [
|
params = [
|
||||||
|
@ -148,4 +152,22 @@ macro declareCurves*(curves: untyped): untyped =
|
||||||
pragmas = nnkPragma.newTree(ident"compileTime")
|
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
|
# echo result.toStrLit
|
||||||
|
|
|
@ -64,21 +64,21 @@ suite "IO":
|
||||||
|
|
||||||
test "Round trip on elliptic curve constants":
|
test "Round trip on elliptic curve constants":
|
||||||
block: # Secp256k1 - https://en.bitcoin.it/wiki/Secp256k1
|
block: # Secp256k1 - https://en.bitcoin.it/wiki/Secp256k1
|
||||||
const p = "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"
|
const p = "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"
|
||||||
let x = fromHex(BigInt[256], p)
|
let x = fromHex(BigInt[256], p)
|
||||||
let hex = x.dumpHex(bigEndian)
|
let hex = x.dumpHex(bigEndian)
|
||||||
|
|
||||||
check: p == hex
|
check: p == hex
|
||||||
|
|
||||||
block: # alt-BN128 - https://github.com/ethereum/py_ecc/blob/master/py_ecc/fields/field_properties.py
|
block: # BN254 - https://github.com/ethereum/py_ecc/blob/master/py_ecc/fields/field_properties.py
|
||||||
const p = "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"
|
const p = "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"
|
||||||
let x = fromHex(BigInt[254], p)
|
let x = fromHex(BigInt[254], p)
|
||||||
let hex = x.dumpHex(bigEndian)
|
let hex = x.dumpHex(bigEndian)
|
||||||
|
|
||||||
check: p == hex
|
check: p == hex
|
||||||
|
|
||||||
block: # BLS12-381 - https://github.com/ethereum/py_ecc/blob/master/py_ecc/fields/field_properties.py
|
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 x = fromHex(BigInt[381], p)
|
||||||
let hex = x.dumpHex(bigEndian)
|
let hex = x.dumpHex(bigEndian)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue