mirror of
https://github.com/logos-storage/constantine.git
synced 2026-04-05 02:23:11 +00:00
Add Montgomery Modular Multiplication
This commit is contained in:
parent
301cf20195
commit
eb94c3d1bc
@ -131,3 +131,10 @@ func unsafeRedc*[mBits](nres: var BigInt[mBits], N: BigInt[mBits], montyMagic: s
|
|||||||
## 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(nres.view, N.view, Word(montyMagic))
|
redc(nres.view, N.view, Word(montyMagic))
|
||||||
|
|
||||||
|
func montyMul*[mBits](r: var BigInt[mBits], a, b, M: BigInt[mBits], montyMagic: static BaseType) =
|
||||||
|
## Compute r <- a*b (mod M) in the Montgomery domain
|
||||||
|
##
|
||||||
|
## This resets r to zero before processing. Use {.noInit.}
|
||||||
|
## to avoid duplicating with Nim zero-init policy
|
||||||
|
montyMul(r.view, a.view, b.view, M.view, Word(montyMagic))
|
||||||
|
|||||||
@ -179,7 +179,7 @@ template checkValidModulus(m: BigIntViewConst) =
|
|||||||
## This is only checked
|
## This is only checked
|
||||||
## with "-d:debugConstantine" and when assertions are on.
|
## with "-d:debugConstantine" and when assertions are on.
|
||||||
debug:
|
debug:
|
||||||
assert not m[^1].isZero.bool, "Internal Error: the modulus must use all declared bits"
|
assert not isZero(m[^1]).bool, "Internal Error: the modulus must use all declared bits"
|
||||||
|
|
||||||
template checkOddModulus(m: BigIntViewConst) =
|
template checkOddModulus(m: BigIntViewConst) =
|
||||||
## CHeck that the modulus is odd
|
## CHeck that the modulus is odd
|
||||||
@ -212,6 +212,11 @@ func isZero*(a: BigIntViewAny): CTBool[Word] =
|
|||||||
accum = accum or a[i]
|
accum = accum or a[i]
|
||||||
result = accum.isZero()
|
result = accum.isZero()
|
||||||
|
|
||||||
|
func setZero*(a: BigIntViewMut) =
|
||||||
|
## Set a BigInt to 0
|
||||||
|
## It's bit size is unchanged
|
||||||
|
zeroMem(a[0].unsafeAddr, a.numLimbs() * sizeof(Word))
|
||||||
|
|
||||||
# The arithmetic primitives all accept a control input that indicates
|
# The arithmetic primitives all accept a control input that indicates
|
||||||
# if it is a placebo operation. It stills performs the
|
# if it is a placebo operation. It stills performs the
|
||||||
# same memory accesses to be side-channel attack resistant.
|
# same memory accesses to be side-channel attack resistant.
|
||||||
@ -382,6 +387,12 @@ func reduce*(r: BigIntViewMut, a: BigIntViewAny, M: BigIntViewConst) =
|
|||||||
for i in countdown(aOffset, 0):
|
for i in countdown(aOffset, 0):
|
||||||
r.shlAddMod(a[i], M)
|
r.shlAddMod(a[i], M)
|
||||||
|
|
||||||
|
# ############################################################
|
||||||
|
#
|
||||||
|
# Montgomery Arithmetic
|
||||||
|
#
|
||||||
|
# ############################################################
|
||||||
|
|
||||||
func montgomeryResidue*(a: BigIntViewMut, N: BigIntViewConst) =
|
func montgomeryResidue*(a: BigIntViewMut, N: BigIntViewConst) =
|
||||||
## 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
|
||||||
@ -401,6 +412,9 @@ func montgomeryResidue*(a: BigIntViewMut, N: BigIntViewConst) =
|
|||||||
for i in countdown(nLen, 1):
|
for i in countdown(nLen, 1):
|
||||||
a.shlAddMod(Zero, N)
|
a.shlAddMod(Zero, N)
|
||||||
|
|
||||||
|
template wordMul(a, b: Word): Word =
|
||||||
|
(a * b) and MaxWord
|
||||||
|
|
||||||
func redc*(a: BigIntViewMut, N: BigIntViewConst, montyMagic: Word) =
|
func redc*(a: BigIntViewMut, N: BigIntViewConst, montyMagic: Word) =
|
||||||
## 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)
|
||||||
@ -424,9 +438,10 @@ func redc*(a: BigIntViewMut, N: BigIntViewConst, montyMagic: Word) =
|
|||||||
|
|
||||||
let nLen = N.numLimbs()
|
let nLen = N.numLimbs()
|
||||||
for i in 0 ..< nLen:
|
for i in 0 ..< nLen:
|
||||||
let z0 = Word(BaseType(a[0]) * BaseType(montyMagic)) and MaxWord
|
|
||||||
|
|
||||||
|
let z0 = wordMul(a[0], montyMagic)
|
||||||
var carry = DoubleWord(0)
|
var carry = DoubleWord(0)
|
||||||
|
|
||||||
for j in 0 ..< nLen:
|
for j in 0 ..< nLen:
|
||||||
let z = DoubleWord(a[i]) + unsafeExtPrecMul(z0, N[i]) + carry
|
let z = DoubleWord(a[i]) + unsafeExtPrecMul(z0, N[i]) + carry
|
||||||
carry = z shr WordBitSize
|
carry = z shr WordBitSize
|
||||||
@ -434,3 +449,44 @@ func redc*(a: BigIntViewMut, N: BigIntViewConst, montyMagic: Word) =
|
|||||||
a[j] = Word(z) and MaxWord
|
a[j] = Word(z) and MaxWord
|
||||||
|
|
||||||
a[^1] = Word(carry)
|
a[^1] = Word(carry)
|
||||||
|
|
||||||
|
func montyMul*(
|
||||||
|
r: BigIntViewMut, a, b: distinct BigIntViewAny,
|
||||||
|
M: BigIntViewConst, montyMagic: Word) =
|
||||||
|
## Compute r <- a*b (mod M) in the Montgomery domain
|
||||||
|
## `montyMagic` = -1/M (mod Word). Our words are 2^31 or 2^63
|
||||||
|
##
|
||||||
|
## This resets r to zero before processing. Use {.noInit.}
|
||||||
|
## to avoid duplicating with Nim zero-init policy
|
||||||
|
# i.e. c'R <- a'R b'R * R^-1 (mod M) in the natural domain
|
||||||
|
# as in the Montgomery domain all numbers are scaled by R
|
||||||
|
|
||||||
|
checkValidModulus(M)
|
||||||
|
checkOddModulus(M)
|
||||||
|
checkMatchingBitlengths(r, M)
|
||||||
|
checkMatchingBitlengths(a, M)
|
||||||
|
checkMatchingBitlengths(b, M)
|
||||||
|
|
||||||
|
let nLen = M.numLimbs()
|
||||||
|
setZero(r)
|
||||||
|
|
||||||
|
var r_hi = Zero # represents the high word that is used in intermediate computation before reduction mod M
|
||||||
|
for i in 0 ..< nLen:
|
||||||
|
|
||||||
|
let zi = (r[0] + wordMul(a[i], b[0])).wordMul(montyMagic)
|
||||||
|
var carry = Zero
|
||||||
|
|
||||||
|
for j in 0 ..< nLen:
|
||||||
|
let z = DoubleWord(r[j]) + unsafeExtPrecMul(a[i], b[j]) +
|
||||||
|
unsafeExtPrecMul(zi, M[j]) + DoubleWord(carry)
|
||||||
|
carry = Word(z shr WordBitSize)
|
||||||
|
if j != 0:
|
||||||
|
r[j-1] = Word(z) and MaxWord
|
||||||
|
|
||||||
|
r_hi += carry
|
||||||
|
r[^1] = r_hi and MaxWord
|
||||||
|
r_hi = r_hi shr WordBitSize
|
||||||
|
|
||||||
|
# If the extra word is not zero or if r-M does not borrow (i.e. r > M)
|
||||||
|
# Then substract M
|
||||||
|
discard r.sub(M, r_hi.isNonZero() or not r.sub(M, CtFalse))
|
||||||
|
|||||||
@ -41,6 +41,7 @@ debug:
|
|||||||
|
|
||||||
# No exceptions allowed
|
# No exceptions allowed
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
{.push inline.}
|
||||||
|
|
||||||
# ############################################################
|
# ############################################################
|
||||||
#
|
#
|
||||||
@ -98,3 +99,12 @@ func `-=`*(a: var Fq, b: Fq) =
|
|||||||
## Substraction over Fq
|
## Substraction over Fq
|
||||||
let ctl = sub(a, b, CtTrue)
|
let ctl = sub(a, b, CtTrue)
|
||||||
discard add(a, Fq.C.Mod, ctl)
|
discard add(a, Fq.C.Mod, ctl)
|
||||||
|
|
||||||
|
func `*`*(a, b: Fq): Fq {.noInit.} =
|
||||||
|
## Multiplication over Fq
|
||||||
|
##
|
||||||
|
## It is recommended to assign with {.noInit.}
|
||||||
|
## as Fq elements are usually large and this
|
||||||
|
## routine will zero init internally the result.
|
||||||
|
result.nres.setInternalBitLength()
|
||||||
|
result.nres.montyMul(a.nres, b.nres, Fq.C.Mod.nres, montyMagic(Fq.C))
|
||||||
|
|||||||
@ -44,7 +44,15 @@ proc main() =
|
|||||||
z.fromUint(0'u32)
|
z.fromUint(0'u32)
|
||||||
|
|
||||||
x += y
|
x += y
|
||||||
check: bool(z == x)
|
|
||||||
|
var x_bytes: array[8, byte]
|
||||||
|
x_bytes.serializeRawUint(x, cpuEndian)
|
||||||
|
|
||||||
|
check:
|
||||||
|
# Check equality in the Montgomery domain
|
||||||
|
bool(z == x)
|
||||||
|
# Check equality when converting back to natural domain
|
||||||
|
0'u64 == cast[uint64](x_bytes)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
var x, y, z: Fq[Fake101]
|
var x, y, z: Fq[Fake101]
|
||||||
@ -119,4 +127,41 @@ proc main() =
|
|||||||
# Check equality when converting back to natural domain
|
# Check equality when converting back to natural domain
|
||||||
100'u64 == cast[uint64](x_bytes)
|
100'u64 == cast[uint64](x_bytes)
|
||||||
|
|
||||||
|
test "Multiplication mod 101":
|
||||||
|
block:
|
||||||
|
var x, y, z: Fq[Fake101]
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
check:
|
||||||
|
# Check equality in the Montgomery domain
|
||||||
|
bool(z == r)
|
||||||
|
# Check equality when converting back to natural domain
|
||||||
|
100'u64 == cast[uint64](r_bytes)
|
||||||
|
|
||||||
|
block:
|
||||||
|
var x, y, z: Fq[Fake101]
|
||||||
|
|
||||||
|
x.fromUint(10'u32)
|
||||||
|
y.fromUint(11'u32)
|
||||||
|
z.fromUint(9'u32)
|
||||||
|
|
||||||
|
let r = x * y
|
||||||
|
|
||||||
|
var r_bytes: array[8, byte]
|
||||||
|
r_bytes.serializeRawUint(r, cpuEndian)
|
||||||
|
|
||||||
|
check:
|
||||||
|
# Check equality in the Montgomery domain
|
||||||
|
bool(z == r)
|
||||||
|
# Check equality when converting back to natural domain
|
||||||
|
9'u64 == cast[uint64](r_bytes)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user