mirror of
https://github.com/logos-storage/constantine.git
synced 2026-01-07 07:33:08 +00:00
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
|
bitsize: 381
|
||||||
modulus: "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
|
modulus: "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
|
||||||
# Equation: y^2 = x^3 + 4
|
# Equation: y^2 = x^3 + 4
|
||||||
|
curve P256: # secp256r1 / NIST P-256
|
||||||
|
bitsize: 256
|
||||||
|
modulus: "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
|
||||||
else:
|
else:
|
||||||
# Fake curve for testing field arithmetic
|
# Fake curve for testing field arithmetic
|
||||||
declareCurves:
|
declareCurves:
|
||||||
@ -64,6 +67,9 @@ else:
|
|||||||
curve Mersenne127:
|
curve Mersenne127:
|
||||||
bitsize: 127
|
bitsize: 127
|
||||||
modulus: "0x7fffffffffffffffffffffffffffffff" # 2^127 - 1
|
modulus: "0x7fffffffffffffffffffffffffffffff" # 2^127 - 1
|
||||||
|
curve P256: # secp256r1 / NIST P-256
|
||||||
|
bitsize: 256
|
||||||
|
modulus: "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
|
||||||
|
|
||||||
# ############################################################
|
# ############################################################
|
||||||
#
|
#
|
||||||
|
|||||||
@ -11,6 +11,10 @@ import
|
|||||||
../config/curves,
|
../config/curves,
|
||||||
../math/[bigints_checked, finite_fields]
|
../math/[bigints_checked, finite_fields]
|
||||||
|
|
||||||
|
# No exceptions allowed
|
||||||
|
{.push raises: [].}
|
||||||
|
{.push inline.}
|
||||||
|
|
||||||
# ############################################################
|
# ############################################################
|
||||||
#
|
#
|
||||||
# Parsing from canonical inputs to internal representation
|
# Parsing from canonical inputs to internal representation
|
||||||
@ -22,7 +26,7 @@ func fromUint*(dst: var Fq,
|
|||||||
## Parse a regular unsigned integer
|
## Parse a regular unsigned integer
|
||||||
## and store it into a BigInt of size `bits`
|
## and store it into a BigInt of size `bits`
|
||||||
let raw = (type dst.mres).fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian)
|
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],
|
func serializeRawUint*(dst: var openarray[byte],
|
||||||
src: Fq,
|
src: Fq,
|
||||||
|
|||||||
@ -65,6 +65,8 @@ template view*(a: var BigInt): BigIntViewMut =
|
|||||||
BigIntViewMut(cast[BigIntView](a.addr))
|
BigIntViewMut(cast[BigIntView](a.addr))
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
|
import strutils
|
||||||
|
|
||||||
func `==`*(a, b: BigInt): CTBool[Word] =
|
func `==`*(a, b: BigInt): CTBool[Word] =
|
||||||
## Returns true if 2 big ints are equal
|
## Returns true if 2 big ints are equal
|
||||||
var accum: Word
|
var accum: Word
|
||||||
@ -78,10 +80,10 @@ debug:
|
|||||||
result.add "](bitLength: "
|
result.add "](bitLength: "
|
||||||
result.add $a.bitLength
|
result.add $a.bitLength
|
||||||
result.add ", limbs: ["
|
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:
|
for i in 1 ..< a.limbs.len:
|
||||||
result.add ", "
|
result.add ", "
|
||||||
result.add $BaseType(a.limbs[i])
|
result.add $BaseType(a.limbs[i]) & " (0x" & toHex(BaseType(a.limbs[i])) & ')'
|
||||||
result.add "])"
|
result.add "])"
|
||||||
|
|
||||||
# No exceptions allowed
|
# 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.
|
# pass a pointer+length to a fixed session of the BSS.
|
||||||
reduce(r.view, a.view, M.view)
|
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
|
## Convert a BigInt from its natural representation
|
||||||
## to the Montgomery n-residue form
|
## 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.
|
## Nesting Montgomery form is possible by applying this function twice.
|
||||||
montyResidue(mres.view, a.view, N.view, r2modN.view, Word(negInvModWord))
|
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
|
## Convert a BigInt from its Montgomery n-residue form
|
||||||
## to the natural representation
|
## 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
|
## Caller must take care of properly switching between
|
||||||
## the natural and montgomery domain.
|
## 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) =
|
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
|
## Compute r <- a*b (mod M) in the Montgomery domain
|
||||||
|
|||||||
@ -448,7 +448,7 @@ func montyMul*(
|
|||||||
# Then substract M
|
# Then substract M
|
||||||
discard r.sub(M, r_hi.isNonZero() or not r.sub(M, CtFalse))
|
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)
|
## Transform a bigint ``a`` from it's Montgomery N-residue representation (mod N)
|
||||||
## to the regular natural representation (mod N)
|
## to the regular natural representation (mod N)
|
||||||
##
|
##
|
||||||
@ -471,25 +471,12 @@ func redc*(a: BigIntViewMut, N: BigIntViewConst, negInvModWord: Word) =
|
|||||||
checkOddModulus(N)
|
checkOddModulus(N)
|
||||||
checkMatchingBitlengths(a, N)
|
checkMatchingBitlengths(a, N)
|
||||||
|
|
||||||
let nLen = N.numLimbs()
|
# TODO: This is a Montgomery multiplication by 1 and can be specialized
|
||||||
for i in 0 ..< nLen:
|
montyMul(r, a, one, N, negInvModWord)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
func montyResidue*(
|
func montyResidue*(
|
||||||
r: BigIntViewMut, a: BigIntViewAny,
|
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)
|
## Transform a bigint ``a`` from it's natural representation (mod N)
|
||||||
## to a the Montgomery n-residue representation
|
## to a the Montgomery n-residue representation
|
||||||
##
|
##
|
||||||
@ -512,27 +499,3 @@ func montyResidue*(
|
|||||||
checkMatchingBitlengths(a, N)
|
checkMatchingBitlengths(a, N)
|
||||||
|
|
||||||
montyMul(r, a, r2ModN, N, negInvModWord)
|
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.} =
|
func fromBig*[C: static Curve](T: type Fq[C], src: BigInt): Fq[C] {.noInit.} =
|
||||||
## Convert a BigInt to its Montgomery form
|
## 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.} =
|
func toBig*(src: Fq): auto {.noInit.} =
|
||||||
## Convert a finite-field element to a BigInt in natral representation
|
## Convert a finite-field element to a BigInt in natral representation
|
||||||
result = src.mres
|
var r {.noInit.}: typeof(src.mres)
|
||||||
result.unsafeRedC(Fq.C.Mod.mres, Fq.C.getNegInvModWord())
|
r.redc(src.mres, Fq.C.Mod.mres, Fq.C.getNegInvModWord())
|
||||||
|
return r
|
||||||
|
|
||||||
# ############################################################
|
# ############################################################
|
||||||
#
|
#
|
||||||
|
|||||||
@ -161,7 +161,9 @@ func r2mod*(M: BigInt): BigInt =
|
|||||||
const
|
const
|
||||||
w = M.limbs.len
|
w = M.limbs.len
|
||||||
msb = M.bits-1 - WordBitSize * (w - 1)
|
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
|
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)
|
result.doubleMod(M)
|
||||||
|
|||||||
@ -164,4 +164,141 @@ proc main() =
|
|||||||
# Check equality when converting back to natural domain
|
# Check equality when converting back to natural domain
|
||||||
9'u64 == cast[uint64](r_bytes)
|
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()
|
main()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user