Skip to content

Commit

Permalink
precompute cube root of unity - Add VM precomputation of Fp - workaro…
Browse files Browse the repository at this point in the history
…und upstream bug nim-lang/Nim#14585
  • Loading branch information
mratsim committed Jun 7, 2020
1 parent bf076c4 commit 110f677
Show file tree
Hide file tree
Showing 19 changed files with 315 additions and 85 deletions.
12 changes: 12 additions & 0 deletions constantine.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ task test, "Run all tests":

test "", "tests/test_finite_fields_vs_gmp.nim"

# Precompute
test "", "tests/test_precomputed"

# Towers of extension fields
test "", "tests/test_fp2.nim"
test "", "tests/test_fp6.nim"
Expand Down Expand Up @@ -90,6 +93,9 @@ task test, "Run all tests":

test "-d:Constantine32", "tests/test_finite_fields_vs_gmp.nim"

# Precompute
test "-d:Constantine32", "tests/test_precomputed"

# Towers of extension fields
test "-d:Constantine32", "tests/test_fp2.nim"
test "-d:Constantine32", "tests/test_fp6.nim"
Expand Down Expand Up @@ -128,6 +134,9 @@ task test_no_gmp, "Run tests that don't require GMP":
test "", "tests/test_finite_fields_sqrt.nim"
test "", "tests/test_finite_fields_powinv.nim"

# Precompute
test "", "tests/test_precomputed"

# Towers of extension fields
test "", "tests/test_fp2.nim"
test "", "tests/test_fp6.nim"
Expand All @@ -154,6 +163,9 @@ task test_no_gmp, "Run tests that don't require GMP":
test "-d:Constantine32", "tests/test_finite_fields_sqrt.nim"
test "-d:Constantine32", "tests/test_finite_fields_powinv.nim"

# Precompute
test "-d:Constantine32", "tests/test_precomputed"

# Towers of extension fields
test "-d:Constantine32", "tests/test_fp2.nim"
test "-d:Constantine32", "tests/test_fp6.nim"
Expand Down
1 change: 0 additions & 1 deletion constantine/arithmetic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ import
arithmetic/[finite_fields, finite_fields_inversion]

export
limbs, limbs_modular, limbs_montgomery,
bigints,
finite_fields, finite_fields_inversion
43 changes: 5 additions & 38 deletions constantine/arithmetic/bigints.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.

import
../config/common,
../config/[common, type_bigint],
../primitives,
./limbs, ./limbs_montgomery, ./limbs_modular

export BigInt

# ############################################################
#
# BigInts
Expand Down Expand Up @@ -55,41 +57,6 @@ import
# having redundant representations that forces costly reductions before multiplications.
# https://github.com/mratsim/constantine/issues/15

func wordsRequired(bits: int): int {.compileTime.} =
## Compute the number of limbs required
# from the **announced** bit length
(bits + WordBitWidth - 1) div WordBitWidth

type
BigInt*[bits: static int] = object
## Fixed-precision big integer
##
## - "bits" is the announced bit-length of the BigInt
## This is public data, usually equal to the curve prime bitlength.
##
## - "limbs" is an internal field that holds the internal representation
## of the big integer. Least-significant limb first. Within limbs words are native-endian.
##
## This internal representation can be changed
## without notice and should not be used by external applications or libraries.
limbs*: array[bits.wordsRequired, SecretWord]

# For unknown reason, `bits` doesn't semcheck if
# `limbs: Limbs[bits.wordsRequired]`
# with
# `Limbs[N: static int] = distinct array[N, SecretWord]`
# so we don't set Limbs as a distinct type

debug:
import strutils

func `$`*(a: BigInt): string =
result = "BigInt["
result.add $BigInt.bits
result.add "](limbs: "
result.add a.limbs.toString()
result.add ")"

# No exceptions allowed
{.push raises: [].}
{.push inline.}
Expand Down Expand Up @@ -248,11 +215,11 @@ func bit*[bits: static int](a: BigInt[bits], index: int): Ct[uint8] =
const BitMask = SecretWord 1

let slot = a.limbs[index shr SlotShift] # LimbEndianness is littleEndian
result = ct[uint8](slot shr (index and SelectMask) and BitMask)
result = ct(slot shr (index and SelectMask) and BitMask, uint8)

func bit0*(a: BigInt): Ct[uint8] =
## Access the least significant bit
ct[uint8](a.limbs[0] and SecretWord(1))
ct(a.limbs[0] and SecretWord(1), uint8)

# ############################################################
#
Expand Down
17 changes: 2 additions & 15 deletions constantine/arithmetic/finite_fields.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,10 @@

import
../primitives,
../config/[common, curves],
../config/[common, type_fp, curves],
./bigints, ./limbs_montgomery

type
Fp*[C: static Curve] = object
## All operations on a field are modulo P
## P being the prime modulus of the Curve C
## Internally, data is stored in Montgomery n-residue form
## with the magic constant chosen for convenient division (a power of 2 depending on P bitsize)
mres*: matchingBigInt(C)

debug:
func `$`*[C: static Curve](a: Fp[C]): string =
result = "Fp[" & $C
result.add "]("
result.add $a.mres
result.add ')'
export Fp

# No exceptions allowed
{.push raises: [].}
Expand Down
15 changes: 0 additions & 15 deletions constantine/arithmetic/limbs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,6 @@ type Limbs*[N: static int] = array[N, SecretWord]
##
## but for unknown reason, it prevents semchecking `bits`

debug:
import strutils

func toString*(a: Limbs): string =
result = "["
result.add " 0x" & toHex(BaseType(a[0]))
for i in 1 ..< a.len:
result.add ", 0x" & toHex(BaseType(a[i]))
result.add "])"

func toHex*(a: Limbs): string =
result = "0x"
for i in countdown(a.len-1, 0):
result.add toHex(BaseType(a[i]))

# No exceptions allowed
{.push raises: [].}

Expand Down
14 changes: 12 additions & 2 deletions constantine/config/curves.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import
# Standard library
macros,
# Internal
./curves_declaration, ./curves_derived, ./curves_parser,
../arithmetic/bigints
./type_bigint, ./common,
./curves_declaration, ./curves_derived, ./curves_parser

export CurveFamily, Curve, SexticTwist

Expand Down Expand Up @@ -170,6 +170,12 @@ macro getBN_param_6u_minus_1_BE*(C: static Curve): untyped =
## of a BN curve in canonical big-endian representation
result = bindSym($C & "_BN_6u_minus_1_BE")

# Endomorphism
# -------------------------------------------------------
macro getCubicRootOfUnity*(C: static Curve): untyped =
## Get a non-trivial cubic root of unity
result = bindSym($C & "_cubicRootOfUnity")

# ############################################################
#
# Debug info printed at compile-time
Expand All @@ -187,12 +193,16 @@ macro debugConsts(): untyped {.used.} =
let modulus = bindSym(curveName & "_Modulus")
let r2modp = bindSym(curveName & "_R2modP")
let negInvModWord = bindSym(curveName & "_NegInvModWord")
let cubeRootOfUnity = ident(curveName & "_cubicRootOfUnity")

result.add quote do:
echo "Curve ", `curveName`,':'
echo " Field Modulus: ", `modulus`
echo " Montgomery R² (mod P): ", `r2modp`
echo " Montgomery -1/P[0] (mod 2^", WordBitWidth, "): ", `negInvModWord`
when declared(`cubeRootOfUnity`):
echo " Cube root of unity: ", `cubeRootOfUnity`

result.add quote do:
echo "----------------------------------------------------------------------------"

Expand Down
2 changes: 2 additions & 0 deletions constantine/config/curves_declaration.nim
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ declareCurves:
family: BarretoNaehrig
bn_u_bitwidth: 63
bn_u: "0x44E992B44A6909F1" # u: 4965661367192848881
cubicRootOfUnity: "0x59e26bcea0d48bacd4f263f1acdb5c4f5763473177fffffe"

# G1 Equation: Y^2 = X^3 + 3
# G2 Equation: Y^2 = X^3 + 3/(9+𝑖)
Expand Down Expand Up @@ -141,6 +142,7 @@ declareCurves:
modulus: "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
family: BarretoLynnScott
# u: -(2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16)
cubicRootOfUnity: "0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe"

# G1 Equation: y² = x³ + 4
# G2 Equation: y² = x³ + 4 (1+i)
Expand Down
26 changes: 24 additions & 2 deletions constantine/config/curves_derived.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import
# Standard library
macros,
# Internal
../arithmetic/precomputed,
./curves_declaration
./precompute,
./curves_declaration,
./type_fp,
../io/io_bigints

{.experimental: "dynamicBindSym".}

Expand Down Expand Up @@ -124,6 +126,24 @@ macro genDerivedConstants*(): untyped =
)
)

# const MyCurve_cubicRootOfUnity
block:
let cubicHex = ident(curve & "_cubicRootOfUnityHex")
let cubic = used(curve & "_cubicRootOfUnity")
let M = bindSym(curve & "_Modulus")
let r2modM = ident(curve & "_R2modP")
let m0ninv = ident(curve & "_NegInvModWord")
result.add quote do:
when declared(`cubichex`):
const `cubic` = block:
var cubic: Fp[Curve(`curveSym`)]
montyResidue_precompute(
cubic.mres,
fromHex(cubic.mres.typeof, `cubicHex`),
`M`, `r2modM`, `m0ninv`
)
cubic

if CurveFamilies[curveSym] == BarretoNaehrig:
# when declared(MyCurve_BN_param_u):
# const MyCurve_BN_u_BE = toCanonicalIntRepr(MyCurve_BN_param_u)
Expand All @@ -148,3 +168,5 @@ macro genDerivedConstants*(): untyped =
bnStmts
)
)

# echo result.toStrLit()
16 changes: 15 additions & 1 deletion constantine/config/curves_parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import
# Standard library
std/[macros, strutils],
# Internal
../io/io_bigints, ../arithmetic/[bigints, precomputed]
../io/io_bigints,
./type_bigint, ./precompute

# Parsing is done in 2 steps:
# 1. All declared parameters are collected in a {.compileTime.} seq[CurveParams]
Expand Down Expand Up @@ -98,6 +99,9 @@ type
nonresidue_quad_fp: NimNode # nnkIntLit
nonresidue_cube_fp2: NimNode # nnkPar(nnkIntLit, nnkIntLit)

# Endomorphisms
cubicRootOfUnity: NimNode # nnkStrLit

# Curve parameters
eq_form: CurveEquationForm
coef_A: CurveCoef
Expand Down Expand Up @@ -181,6 +185,8 @@ proc parseCurveDecls(defs: var seq[CurveParams], curves: NimNode) =
params.bn_u_bitwidth = sectionVal
elif sectionId.eqIdent"bn_u":
params.bn_u = sectionVal
elif sectionId.eqident"cubicRootOfUnity":
params.cubicRootOfUnity = sectionVal
elif sectionId.eqIdent"eq_form":
params.eq_form = parseEnum[CurveEquationForm]($sectionVal)
elif sectionId.eqIdent"coef_a":
Expand Down Expand Up @@ -271,6 +277,14 @@ proc genMainConstants(defs: var seq[CurveParams]): NimNode =
MapCurveFamily.add nnkExprColonExpr.newTree(
curve, newLit(family)
)
# Endomorphisms
# -----------------------------------------------
if not curveDef.cubicRootOfUnity.isNil:
curveExtraStmts.add newConstStmt(
exported($curve & "_cubicRootOfUnityHex"),
curveDef.cubicRootOfUnity
)

# Curve equation
# -----------------------------------------------
curveEllipticStmts.add newConstStmt(
Expand Down
Loading

0 comments on commit 110f677

Please sign in to comment.