Fix bug in redc: use montgomery mul for now. Add NIST P256 curve
This commit is contained in:
parent
7740bfbae4
commit
c3d458e31b
|
@ -52,6 +52,9 @@ when not defined(testingCurves):
|
|||
bitsize: 381
|
||||
modulus: "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
|
||||
# Equation: y^2 = x^3 + 4
|
||||
curve P256: # secp256r1 / NIST P-256
|
||||
bitsize: 256
|
||||
modulus: "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
|
||||
else:
|
||||
# Fake curve for testing field arithmetic
|
||||
declareCurves:
|
||||
|
@ -64,6 +67,9 @@ else:
|
|||
curve Mersenne127:
|
||||
bitsize: 127
|
||||
modulus: "0x7fffffffffffffffffffffffffffffff" # 2^127 - 1
|
||||
curve P256: # secp256r1 / NIST P-256
|
||||
bitsize: 256
|
||||
modulus: "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
|
|
|
@ -11,6 +11,10 @@ import
|
|||
../config/curves,
|
||||
../math/[bigints_checked, finite_fields]
|
||||
|
||||
# No exceptions allowed
|
||||
{.push raises: [].}
|
||||
{.push inline.}
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Parsing from canonical inputs to internal representation
|
||||
|
@ -22,7 +26,7 @@ func fromUint*(dst: var Fq,
|
|||
## Parse a regular unsigned integer
|
||||
## and store it into a BigInt of size `bits`
|
||||
let raw = (type dst.mres).fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian)
|
||||
dst.mres.unsafeMontyResidue(raw, Fq.C.Mod.mres, Fq.C.getR2modP(), Fq.C.getNegInvModWord())
|
||||
dst.fromBig(raw)
|
||||
|
||||
func serializeRawUint*(dst: var openarray[byte],
|
||||
src: Fq,
|
||||
|
|
|
@ -65,6 +65,8 @@ template view*(a: var BigInt): BigIntViewMut =
|
|||
BigIntViewMut(cast[BigIntView](a.addr))
|
||||
|
||||
debug:
|
||||
import strutils
|
||||
|
||||
func `==`*(a, b: BigInt): CTBool[Word] =
|
||||
## Returns true if 2 big ints are equal
|
||||
var accum: Word
|
||||
|
@ -78,10 +80,10 @@ debug:
|
|||
result.add "](bitLength: "
|
||||
result.add $a.bitLength
|
||||
result.add ", limbs: ["
|
||||
result.add $BaseType(a.limbs[0])
|
||||
result.add $BaseType(a.limbs[0]) & " (0x" & toHex(BaseType(a.limbs[0])) & ')'
|
||||
for i in 1 ..< a.limbs.len:
|
||||
result.add ", "
|
||||
result.add $BaseType(a.limbs[i])
|
||||
result.add $BaseType(a.limbs[i]) & " (0x" & toHex(BaseType(a.limbs[i])) & ')'
|
||||
result.add "])"
|
||||
|
||||
# No exceptions allowed
|
||||
|
@ -123,7 +125,7 @@ func reduce*[aBits, mBits](r: var BigInt[mBits], a: BigInt[aBits], M: BigInt[mBi
|
|||
# pass a pointer+length to a fixed session of the BSS.
|
||||
reduce(r.view, a.view, M.view)
|
||||
|
||||
func unsafeMontyResidue*[mBits](mres: var BigInt[mBits], a, N, r2modN: BigInt[mBits], negInvModWord: static BaseType) =
|
||||
func montyResidue*[mBits](mres: var BigInt[mBits], a, N, r2modN: BigInt[mBits], negInvModWord: static BaseType) =
|
||||
## Convert a BigInt from its natural representation
|
||||
## to the Montgomery n-residue form
|
||||
##
|
||||
|
@ -134,7 +136,7 @@ func unsafeMontyResidue*[mBits](mres: var BigInt[mBits], a, N, r2modN: BigInt[mB
|
|||
## Nesting Montgomery form is possible by applying this function twice.
|
||||
montyResidue(mres.view, a.view, N.view, r2modN.view, Word(negInvModWord))
|
||||
|
||||
func unsafeRedc*[mBits](mres: var BigInt[mBits], N: BigInt[mBits], negInvModWord: static BaseType) =
|
||||
func redc*[mBits](r: var BigInt[mBits], a, N: BigInt[mBits], negInvModWord: static BaseType) =
|
||||
## Convert a BigInt from its Montgomery n-residue form
|
||||
## to the natural representation
|
||||
##
|
||||
|
@ -142,7 +144,12 @@ func unsafeRedc*[mBits](mres: var BigInt[mBits], N: BigInt[mBits], negInvModWord
|
|||
##
|
||||
## Caller must take care of properly switching between
|
||||
## the natural and montgomery domain.
|
||||
redc(mres.view, N.view, Word(negInvModWord))
|
||||
let one = block:
|
||||
var one: BigInt[mBits]
|
||||
one.setInternalBitLength()
|
||||
one.limbs[0] = Word(1)
|
||||
one
|
||||
redc(r.view, a.view, one.view, N.view, Word(negInvModWord))
|
||||
|
||||
func montyMul*[mBits](r: var BigInt[mBits], a, b, M: BigInt[mBits], negInvModWord: static BaseType) =
|
||||
## Compute r <- a*b (mod M) in the Montgomery domain
|
||||
|
|
|
@ -448,7 +448,7 @@ func montyMul*(
|
|||
# Then substract M
|
||||
discard r.sub(M, r_hi.isNonZero() or not r.sub(M, CtFalse))
|
||||
|
||||
func redc*(a: BigIntViewMut, N: BigIntViewConst, negInvModWord: Word) =
|
||||
func redc*(r: BigIntViewMut, a: BigIntViewAny, one, N: BigIntViewConst, negInvModWord: Word) {.inline.} =
|
||||
## Transform a bigint ``a`` from it's Montgomery N-residue representation (mod N)
|
||||
## to the regular natural representation (mod N)
|
||||
##
|
||||
|
@ -471,25 +471,12 @@ func redc*(a: BigIntViewMut, N: BigIntViewConst, negInvModWord: Word) =
|
|||
checkOddModulus(N)
|
||||
checkMatchingBitlengths(a, N)
|
||||
|
||||
let nLen = N.numLimbs()
|
||||
for i in 0 ..< nLen:
|
||||
|
||||
let z0 = wordMul(a[0], negInvModWord)
|
||||
var carry = DoubleWord(0)
|
||||
|
||||
for j in 0 ..< nLen:
|
||||
let z = DoubleWord(a[i]) + unsafeExtPrecMul(z0, N[i]) + carry
|
||||
carry = z shr WordBitSize
|
||||
if j != 0:
|
||||
a[j] = Word(z) and MaxWord
|
||||
|
||||
a[^1] = Word(carry)
|
||||
|
||||
# TODO: benchmark Montgomery Multiplication vs Shift-Left (after constant-time division)
|
||||
# TODO: This is a Montgomery multiplication by 1 and can be specialized
|
||||
montyMul(r, a, one, N, negInvModWord)
|
||||
|
||||
func montyResidue*(
|
||||
r: BigIntViewMut, a: BigIntViewAny,
|
||||
N, r2modN: BigIntViewConst, negInvModWord: Word) =
|
||||
N, r2modN: BigIntViewConst, negInvModWord: Word) {.inline.} =
|
||||
## Transform a bigint ``a`` from it's natural representation (mod N)
|
||||
## to a the Montgomery n-residue representation
|
||||
##
|
||||
|
@ -512,27 +499,3 @@ func montyResidue*(
|
|||
checkMatchingBitlengths(a, N)
|
||||
|
||||
montyMul(r, a, r2ModN, N, negInvModWord)
|
||||
|
||||
func montyResidue*(a: BigIntViewMut, N: BigIntViewConst) =
|
||||
## Transform a bigint ``a`` from it's natural representation (mod N)
|
||||
## to a the Montgomery n-residue representation
|
||||
##
|
||||
## Modular shift - based
|
||||
##
|
||||
## with W = N.numLimbs()
|
||||
## and R = (2^WordBitSize)^W
|
||||
##
|
||||
## Does "a * R (mod N)"
|
||||
##
|
||||
## `a`: The source BigInt in the natural representation. `a` in [0, N) range
|
||||
## `N`: The field modulus. N must be odd.
|
||||
##
|
||||
## Important: `a` is overwritten
|
||||
# Reference: https://eprint.iacr.org/2017/1057.pdf
|
||||
checkValidModulus(N)
|
||||
checkOddModulus(N)
|
||||
checkMatchingBitlengths(a, N)
|
||||
|
||||
let nLen = N.numLimbs()
|
||||
for i in countdown(nLen, 1):
|
||||
a.shlAddMod(Zero, N)
|
||||
|
|
|
@ -57,12 +57,17 @@ debug:
|
|||
|
||||
func fromBig*[C: static Curve](T: type Fq[C], src: BigInt): Fq[C] {.noInit.} =
|
||||
## Convert a BigInt to its Montgomery form
|
||||
result.mres.unsafeMontyResidue(src, C.Mod.mres, C.getR2modP(), C.getNegInvModWord())
|
||||
result.mres.montyResidue(src, C.Mod.mres, C.getR2modP(), C.getNegInvModWord())
|
||||
|
||||
func fromBig*[C: static Curve](dst: var Fq[C], src: BigInt) {.noInit.} =
|
||||
## Convert a BigInt to its Montgomery form
|
||||
dst.mres.montyResidue(src, C.Mod.mres, C.getR2modP(), C.getNegInvModWord())
|
||||
|
||||
func toBig*(src: Fq): auto {.noInit.} =
|
||||
## Convert a finite-field element to a BigInt in natral representation
|
||||
result = src.mres
|
||||
result.unsafeRedC(Fq.C.Mod.mres, Fq.C.getNegInvModWord())
|
||||
var r {.noInit.}: typeof(src.mres)
|
||||
r.redc(src.mres, Fq.C.Mod.mres, Fq.C.getNegInvModWord())
|
||||
return r
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
|
|
|
@ -161,7 +161,9 @@ func r2mod*(M: BigInt): BigInt =
|
|||
const
|
||||
w = M.limbs.len
|
||||
msb = M.bits-1 - WordBitSize * (w - 1)
|
||||
start = (w-1)*WordBitSize + msb
|
||||
stop = 2*WordBitSize*w
|
||||
|
||||
result.limbs[^1] = Word(1 shl msb) # C0 = 2^(wn-1), the power of 2 immediatly less than the modulus
|
||||
for _ in (w-1)*WordBitSize + msb ..< w * WordBitSize * 2:
|
||||
for _ in start ..< stop:
|
||||
result.doubleMod(M)
|
||||
|
|
|
@ -164,4 +164,141 @@ proc main() =
|
|||
# Check equality when converting back to natural domain
|
||||
9'u64 == cast[uint64](r_bytes)
|
||||
|
||||
test "Addition mod 2^61 - 1":
|
||||
block:
|
||||
var x, y, z: Fq[Mersenne61]
|
||||
|
||||
x.fromUint(80'u64)
|
||||
y.fromUint(10'u64)
|
||||
z.fromUint(90'u64)
|
||||
|
||||
x += y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.serializeRawUint(x, cpuEndian)
|
||||
let new_x = cast[uint64](x_bytes)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
bool(z == x)
|
||||
# Check equality when converting back to natural domain
|
||||
new_x == 90'u64
|
||||
|
||||
block:
|
||||
var x, y, z: Fq[Mersenne61]
|
||||
|
||||
x.fromUint(1'u64 shl 61 - 2)
|
||||
y.fromUint(1'u32)
|
||||
z.fromUint(0'u32)
|
||||
|
||||
x += y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.serializeRawUint(x, cpuEndian)
|
||||
let new_x = cast[uint64](x_bytes)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
bool(z == x)
|
||||
# Check equality when converting back to natural domain
|
||||
new_x == 0'u64
|
||||
|
||||
block:
|
||||
var x, y, z: Fq[Mersenne61]
|
||||
|
||||
x.fromUint(1'u64 shl 61 - 2)
|
||||
y.fromUint(2'u64)
|
||||
z.fromUint(1'u64)
|
||||
|
||||
x += y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.serializeRawUint(x, cpuEndian)
|
||||
let new_x = cast[uint64](x_bytes)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
bool(z == x)
|
||||
# Check equality when converting back to natural domain
|
||||
new_x == 1'u64
|
||||
|
||||
test "Substraction mod 2^61 - 1":
|
||||
block:
|
||||
var x, y, z: Fq[Mersenne61]
|
||||
|
||||
x.fromUint(80'u64)
|
||||
y.fromUint(10'u64)
|
||||
z.fromUint(70'u64)
|
||||
|
||||
x -= y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.serializeRawUint(x, cpuEndian)
|
||||
let new_x = cast[uint64](x_bytes)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
bool(z == x)
|
||||
# Check equality when converting back to natural domain
|
||||
new_x == 70'u64
|
||||
|
||||
block:
|
||||
var x, y, z: Fq[Mersenne61]
|
||||
|
||||
x.fromUint(0'u64)
|
||||
y.fromUint(1'u64)
|
||||
z.fromUint(1'u64 shl 61 - 2)
|
||||
|
||||
x -= y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.serializeRawUint(x, cpuEndian)
|
||||
let new_x = cast[uint64](x_bytes)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
bool(z == x)
|
||||
# Check equality when converting back to natural domain
|
||||
new_x == 1'u64 shl 61 - 2
|
||||
|
||||
test "Multiplication mod 101":
|
||||
block:
|
||||
var x, y, z: Fq[Mersenne61]
|
||||
|
||||
x.fromUint(10'u32)
|
||||
y.fromUint(10'u32)
|
||||
z.fromUint(100'u32)
|
||||
|
||||
let r = x * y
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
r_bytes.serializeRawUint(r, cpuEndian)
|
||||
let new_r = cast[uint64](r_bytes)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
bool(z == r)
|
||||
# Check equality when converting back to natural domain
|
||||
cast[uint64](r_bytes) == 100'u64
|
||||
|
||||
block:
|
||||
var x, y, z: Fq[Mersenne61]
|
||||
|
||||
x.fromUint(1'u32 shl 31)
|
||||
y.fromUint(1'u32 shl 31)
|
||||
z.fromUint(2'u32)
|
||||
|
||||
let r = x * y
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
r_bytes.serializeRawUint(r, cpuEndian)
|
||||
let new_r = cast[uint64](r_bytes)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
bool(z == r)
|
||||
# Check equality when converting back to natural domain
|
||||
new_r == 2'u64
|
||||
|
||||
|
||||
main()
|
||||
|
|
Loading…
Reference in New Issue