mirror of
https://github.com/henrydcase/nobs.git
synced 2024-11-22 23:28:57 +00:00
336 lines
16 KiB
Go
336 lines
16 KiB
Go
package p503
|
|
|
|
// Tools used for testing and debugging
|
|
|
|
import (
|
|
. "github.com/henrydcase/nobs/dh/sidh/internal/isogeny"
|
|
"math/big"
|
|
"math/rand"
|
|
"reflect"
|
|
"testing/quick"
|
|
)
|
|
|
|
/* -------------------------------------------------------------------------
|
|
Underlying field configuration
|
|
-------------------------------------------------------------------------*/
|
|
var (
|
|
kFieldOps = FieldOperations()
|
|
kParams = &SidhParams{
|
|
Op: kFieldOps,
|
|
OneFp2: P503_OneFp2,
|
|
HalfFp2: P503_HalfFp2,
|
|
Bytelen: P503_Bytelen,
|
|
}
|
|
kCurveOps = &CurveOperations{Params: kParams}
|
|
)
|
|
|
|
/* -------------------------------------------------------------------------
|
|
Configure testing/quick
|
|
-------------------------------------------------------------------------*/
|
|
var (
|
|
quickCheckScaleFactor = uint8(3)
|
|
quickCheckConfig = &quick.Config{MaxCount: (1 << (12 + quickCheckScaleFactor))}
|
|
)
|
|
|
|
/* -------------------------------------------------------------------------
|
|
Structure used by tests
|
|
-------------------------------------------------------------------------*/
|
|
type GeneratedTestParams struct {
|
|
Point ProjectivePoint
|
|
Cparam ProjectiveCurveParameters
|
|
ExtElem Fp2Element
|
|
}
|
|
|
|
// A = 8752234765512331234913716743014562460822083005386252003333602919474238975785850965349950219277942402920758585086620525443539725921333735154674119646075*i + 6339624979889725406021454983012408976766782818694212228554611573314701271183857175866122275755278397694585249002282183018114967373119429936587424396917
|
|
var curve_A = Fp2Element{
|
|
A: FpElement{0xd9816986a543095f, 0xa78cb1d7217bec21, 0x9595dc97b74ea70, 0x9120a1da6b42797d, 0x59ef9d903f74e47c, 0x4c58a4cdc45b6d0b, 0x816d5213aaf7ee6d, 0x3892fee6bb7343},
|
|
B: FpElement{0x28c5288acbedf11b, 0x2143a438c86f6c68, 0x7cb5c4ae9c4c8e34, 0xb478aea445eed48b, 0x24d5c175776db478, 0x234582f8676c0ebe, 0x56234267b625fb08, 0x2c6e58d84b1192}}
|
|
|
|
// C = 10458464853790890798085664692909194316288127038910691163573355876336993883402795907795767791362493831987298578966325154262747805705783782806176495638177*i + 7770984753616185271325854825309278833018655051139367603077592443785629339985729818288672809062782315510526648882226172896710704020683893684611137718845
|
|
var curve_C = Fp2Element{
|
|
A: FpElement{0xe05948236f2f913b, 0xc45da9ad1219a255, 0x7a568972a32fc1d0, 0x30f00bdd7071c3b1, 0x3b761b8dac2c98bc, 0x760f21b2179737b6, 0x13217e6656a13476, 0x2606b798e685aa},
|
|
B: FpElement{0x1c0171f78820052e, 0x440b7f7087e57140, 0xe0510c07b31b0e96, 0xd0cf489b2ac4aea9, 0x4fb328f1c1fdf783, 0xb3b4912342951cb7, 0x70a4b64e81961c42, 0x33eed63cf07181}}
|
|
|
|
// x(P) = 9720237205826983370867050298878715935679372786589878620121159082290288918688002583435964840822877971257659901481591644347943354235932355923042390796255*i + 634577413124118560098123299804750904956499531431297942628887930019161512075536652691244843248133437326050395005054997679717801535474938466995392156605
|
|
var affine_xP = Fp2Element{
|
|
A: FpElement{0xb606d954d407faf2, 0x58a1ef6cd213a203, 0x9823b55033e62f7b, 0x59cafc060d5e25a1, 0x529685f1753526fc, 0xc2eac3d219989c7d, 0xc5e30c75dfd343a0, 0x378285adc968a0},
|
|
B: FpElement{0x6670f36db977b9da, 0xa07e2fdda5e1a7f0, 0xf367a7a722aed87d, 0x6c269e06d595cd10, 0x8379aa6092d87700, 0x57276ce3557ee7ae, 0xac8107bfbcd28993, 0x3d6f98869617a7}}
|
|
|
|
// x(Q) = 613162677562606602867371958793876971530136728660199185642812914351735322828980793298955764087721218995329689800176835576008483782305021671417330230966*i + 12939479853552958669415184882894789433224467828463058020095712989798268661016843892597050485554155971441665589419365957826417334087966365414056706471602
|
|
var affine_xQ = Fp2Element{
|
|
A: FpElement{0xd3d14533cb0db45c, 0xdaf10b9f5fb037cf, 0x9562c31985823562, 0xb79b75e2bf5635a5, 0x83d38fb1669c2d9, 0x5e67117a35a57cbd, 0x4b94ed6c3cbf54a4, 0x3f47706b62778d},
|
|
B: FpElement{0x1c766c0e7ed612d6, 0x2f3b42979e8efd86, 0xd82bac0006415ee7, 0x20cfe3bec0970042, 0x8f6628807e862bf9, 0xac4f82d13fddd9c5, 0x70b756e4bac1fa85, 0x350c02508e50dc}}
|
|
|
|
var affine_xPmQ = Fp2Element{
|
|
A: FpElement{0x7295ac0e8a5531c8, 0xc02afbef6cdf51a8, 0x84d0c1bb8d80624f, 0x26abbf06c61e814b, 0xada277883fbdae07, 0x63b6739db68df3a7, 0x3554670381bcfcc6, 0x33419be255bbc6},
|
|
B: FpElement{0xff9df35102da997a, 0xf9b920b2d8bd6210, 0x43d1514920e73bfb, 0x624c6fd6ef16da74, 0xeb535968ed0d1286, 0x9243e6ce29a636e6, 0x1849ed96cb7940e0, 0x1e4b495933b675}}
|
|
|
|
var affine_xP2 = Fp2Element{
|
|
A: FpElement{0x4e1133c2b3855902, 0x875a775c67597fbb, 0xd17eb74254141abb, 0x1d5a464a4f3391f5, 0x24405c332811d007, 0x7e47e3eb489a7372, 0x65b130dfd9efe605, 0xfa69fac179803},
|
|
B: FpElement{0x329f5322e1be51ee, 0x9004dca8132ebd6f, 0x7cd87e447ca8a7b6, 0x10a6ec02c38ce69e, 0x8cef2ed7d112ac46, 0x5f385a9fc4b57cd7, 0x68a366354fe7a32e, 0x2223c1455486ac}}
|
|
|
|
var affine_xP3 = Fp2Element{
|
|
A: FpElement{0x74a2894cccbe287d, 0xa50e3ec842e13fce, 0xd42ea4d3f1b7ad0b, 0xa4943d50d306f99e, 0xf83e9c0955b08c33, 0xffd8e313402b9380, 0x967b315db0b2e6e, 0x3a0945883df3b4},
|
|
B: FpElement{0xa9f610420a81c5ba, 0xbeb84b3ded2b4e75, 0x9fd6cea494470a70, 0x2fb0075e71900b0e, 0x63a0beb6abf3ca3d, 0xeb3e171798959f2e, 0x2209801eb702d40e, 0x36f8c4603e0fdb}}
|
|
|
|
var affine_xP4 = Fp2Element{
|
|
A: FpElement{0x4eb695d34b46be8f, 0xfb5e76c58585f2d2, 0xa41f8aafa6dbb531, 0x4db82f5db5cfd144, 0x14dab0e3200cbba0, 0x430381706a279f81, 0xdf6707a57161f81, 0x44740f17197c3},
|
|
B: FpElement{0xa2473705cdb6d4e9, 0xfa3cd67b9c15502c, 0xf0928166d0c5cee1, 0x6150aba0c874faaa, 0x6c0b18d6d92f9034, 0xcff71d340fc1e72e, 0x19a47027af917587, 0x25ed4bad443b8f}}
|
|
|
|
var affine_xP9 = Fp2Element{
|
|
A: FpElement{0x112da30e288217e0, 0x5b336d527320a5f7, 0xbbf4d9403b68e3c6, 0x55eccb31c40b359c, 0x8907129ab69b3203, 0x69cc8c750125a915, 0xa41a38e6f530c0e1, 0xbe68e23af1b8d},
|
|
B: FpElement{0x472c603765964213, 0xe4e64995b0769754, 0x4515583c74a6dd24, 0xff7c57f5818363a2, 0xbeaeb24662a92177, 0x8a54fa61fbf24c68, 0xa85542049eb45e12, 0x2b54caf655e285}}
|
|
|
|
// m = 3904534670189250445536401319770569077681088033069864532895
|
|
var mScalarBytes = [...]uint8{0x9f, 0x3b, 0xe7, 0xf9, 0xf4, 0x7c, 0xe6, 0xce, 0x79, 0x3e, 0x3d, 0x9f, 0x9f, 0x3b, 0xe7, 0xf9, 0xf4, 0x7c, 0xe6, 0xce, 0x79, 0x3e, 0x3d, 0x9f}
|
|
|
|
var affine_xaP = Fp2Element{
|
|
A: FpElement{0x100a82c2be58e28b, 0x70ee7b57f40d9103, 0xb9f21d6327411cbb, 0x976b2a65166591cb, 0x35435bd4614ca404, 0x15f862a9c6f51469, 0x14d9ccfe634f374a, 0x31234988b0766c},
|
|
B: FpElement{0x323a3a13113b35f8, 0xc949dad174586c8f, 0x1c5ed3b1263143c, 0x13ba9870c9b5bab8, 0x79fb89a31cfe7f19, 0xa8af6d4b5d947ed2, 0xcff00f80d7b67a56, 0xfdfcb136bff75}}
|
|
|
|
// Inputs for testing 3-point-ladder
|
|
var threePointLadderInputs = []ProjectivePoint{
|
|
// x(P)
|
|
ProjectivePoint{
|
|
X: Fp2Element{
|
|
A: FpElement{0x43941FA9244C059E, 0xD1F337D076941189, 0x6B6A8B3A8763C96A, 0x6DF569708D6C9482, 0x487EE5707A52F4AA, 0xDE396F6E2559689E, 0xE5EE3895A8991469, 0x2B0946695790A8},
|
|
B: FpElement{0xAB552C0FDAED092E, 0x7DF895E43E7DCB1C, 0x35C700E761920C4B, 0xCC5807DD70DC117A, 0x0884039A5A8DB18A, 0xD04620B3D0738052, 0xA200835605138F10, 0x3FF2E59B2FDC6A}},
|
|
Z: P503_OneFp2,
|
|
},
|
|
// x(Q)
|
|
ProjectivePoint{
|
|
X: Fp2Element{
|
|
A: FpElement{0x77015826982BA1FD, 0x44024489673471E4, 0x1CAA2A5F4D5DA63B, 0xA183C07E50738C01, 0x8B97782D4E1A0DE6, 0x9B819522FBC38280, 0x0BDA46A937FB7B8A, 0x3B3614305914DF},
|
|
B: FpElement{0xBF0366E97B3168D9, 0xAA522AC3879CEF0F, 0x0AF5EC975BD035C8, 0x1F26FEE7BBAC165C, 0xA0EE6A637724A6AB, 0xFB52101E36BA3A38, 0xD29CF5E376E17376, 0x1374A50DF57071}},
|
|
Z: P503_OneFp2,
|
|
},
|
|
// x(P-Q)
|
|
ProjectivePoint{
|
|
X: Fp2Element{
|
|
A: FpElement{0xD99279BBD41EA559, 0x35CF18E72F578214, 0x90473B1DC77F73E8, 0xBFFEA930B25D7F66, 0xFD558EA177B900B2, 0x7CFAD273A782A23E, 0x6B1F610822E0F611, 0x26D2D2EF9619B5},
|
|
B: FpElement{0x534F83651CBCC75D, 0x591FB4757AED5D08, 0x0B04353D40BED542, 0x829A94703AAC9139, 0x0F9C2E6D7663EB5B, 0x5D2D0F90C283F746, 0x34C872AA12A7676E, 0x0ECDB605FBFA16}},
|
|
Z: P503_OneFp2,
|
|
},
|
|
}
|
|
var curve = ProjectiveCurveParameters{A: curve_A, C: curve_C}
|
|
|
|
// prime p503
|
|
var p503BigIntPrime, _ = new(big.Int).SetString("13175843156907117380839252916199345042492186767578363998445663477035843932020761233518914911546024351608607150390087656982982306331019593961154237431807", 10)
|
|
|
|
/* -------------------------------------------------------------------------
|
|
Values used by benchmarking tools
|
|
-------------------------------------------------------------------------*/
|
|
|
|
// Package-level storage for this field element is intended to deter
|
|
// compiler optimizations.
|
|
var (
|
|
benchmarkFpElement FpElement
|
|
benchmarkFpElementX2 FpElementX2
|
|
bench_x = FpElement{17026702066521327207, 5108203422050077993, 10225396685796065916, 11153620995215874678, 6531160855165088358, 15302925148404145445, 1248821577836769963, 9789766903037985294, 7493111552032041328, 10838999828319306046, 18103257655515297935, 27403304611634}
|
|
bench_y = FpElement{4227467157325093378, 10699492810770426363, 13500940151395637365, 12966403950118934952, 16517692605450415877, 13647111148905630666, 14223628886152717087, 7167843152346903316, 15855377759596736571, 4300673881383687338, 6635288001920617779, 30486099554235}
|
|
bench_z = FpElementX2{1595347748594595712, 10854920567160033970, 16877102267020034574, 12435724995376660096, 3757940912203224231, 8251999420280413600, 3648859773438820227, 17622716832674727914, 11029567000887241528, 11216190007549447055, 17606662790980286987, 4720707159513626555, 12887743598335030915, 14954645239176589309, 14178817688915225254, 1191346797768989683, 12629157932334713723, 6348851952904485603, 16444232588597434895, 7809979927681678066, 14642637672942531613, 3092657597757640067, 10160361564485285723, 240071237}
|
|
)
|
|
|
|
/* -------------------------------------------------------------------------
|
|
Helpers
|
|
-------------------------------------------------------------------------*/
|
|
|
|
// Given xP = x(P), xQ = x(Q), and xPmQ = x(P-Q), compute xR = x(P+Q).
|
|
//
|
|
// Returns xR to allow chaining. Safe to overlap xP, xQ, xR.
|
|
func AddProjFp2(xP, xQ, xPmQ *ProjectivePoint) ProjectivePoint {
|
|
// Algorithm 1 of Costello-Smith.
|
|
var v0, v1, v2, v3, v4 Fp2Element
|
|
var xR ProjectivePoint
|
|
kFieldOps.Add(&v0, &xP.X, &xP.Z) // X_P + Z_P
|
|
kFieldOps.Sub(&v1, &xQ.X, &xQ.Z)
|
|
kFieldOps.Mul(&v1, &v1, &v0) // (X_Q - Z_Q)(X_P + Z_P)
|
|
kFieldOps.Sub(&v0, &xP.X, &xP.Z) // X_P - Z_P
|
|
kFieldOps.Add(&v2, &xQ.X, &xQ.Z)
|
|
kFieldOps.Mul(&v2, &v2, &v0) // (X_Q + Z_Q)(X_P - Z_P)
|
|
kFieldOps.Add(&v3, &v1, &v2)
|
|
kFieldOps.Square(&v3, &v3) // 4(X_Q X_P - Z_Q Z_P)^2
|
|
kFieldOps.Sub(&v4, &v1, &v2)
|
|
kFieldOps.Square(&v4, &v4) // 4(X_Q Z_P - Z_Q X_P)^2
|
|
kFieldOps.Mul(&v0, &xPmQ.Z, &v3) // 4X_{P-Q}(X_Q X_P - Z_Q Z_P)^2
|
|
kFieldOps.Mul(&xR.Z, &xPmQ.X, &v4) // 4Z_{P-Q}(X_Q Z_P - Z_Q X_P)^2
|
|
xR.X = v0
|
|
return xR
|
|
}
|
|
|
|
// Given xP = x(P) and cached curve parameters Aplus2C = A + 2*C, C4 = 4*C,
|
|
// compute xQ = x([2]P).
|
|
//
|
|
// Returns xQ to allow chaining. Safe to overlap xP, xQ.
|
|
func DoubleProjFp2(xP *ProjectivePoint, Aplus2C, C4 *Fp2Element) ProjectivePoint {
|
|
// Algorithm 2 of Costello-Smith, amended to work with projective curve coefficients.
|
|
var v1, v2, v3, xz4 Fp2Element
|
|
var xQ ProjectivePoint
|
|
kFieldOps.Add(&v1, &xP.X, &xP.Z) // (X+Z)^2
|
|
kFieldOps.Square(&v1, &v1)
|
|
kFieldOps.Sub(&v2, &xP.X, &xP.Z) // (X-Z)^2
|
|
kFieldOps.Square(&v2, &v2)
|
|
kFieldOps.Sub(&xz4, &v1, &v2) // 4XZ = (X+Z)^2 - (X-Z)^2
|
|
kFieldOps.Mul(&v2, &v2, C4) // 4C(X-Z)^2
|
|
kFieldOps.Mul(&xQ.X, &v1, &v2) // 4C(X+Z)^2(X-Z)^2
|
|
kFieldOps.Mul(&v3, &xz4, Aplus2C) // 4XZ(A + 2C)
|
|
kFieldOps.Add(&v3, &v3, &v2) // 4XZ(A + 2C) + 4C(X-Z)^2
|
|
kFieldOps.Mul(&xQ.Z, &v3, &xz4) // (4XZ(A + 2C) + 4C(X-Z)^2)4XZ
|
|
// Now (xQ.x : xQ.z)
|
|
// = (4C(X+Z)^2(X-Z)^2 : (4XZ(A + 2C) + 4C(X-Z)^2)4XZ )
|
|
// = ((X+Z)^2(X-Z)^2 : (4XZ((A + 2C)/4C) + (X-Z)^2)4XZ )
|
|
// = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ )
|
|
return xQ
|
|
}
|
|
|
|
// Given x(P) and a scalar m in little-endian bytes, compute x([m]P) using the
|
|
// Montgomery ladder. This is described in Algorithm 8 of Costello-Smith.
|
|
//
|
|
// This function's execution time is dependent only on the byte-length of the
|
|
// input scalar. All scalars of the same input length execute in uniform time.
|
|
// The scalar can be padded with zero bytes to ensure a uniform length.
|
|
//
|
|
// Safe to overlap the source with the destination.
|
|
func ScalarMult(curve *ProjectiveCurveParameters, xP *ProjectivePoint, scalar []uint8) ProjectivePoint {
|
|
var x0, x1, tmp ProjectivePoint
|
|
var Aplus2C, C4 Fp2Element
|
|
|
|
kFieldOps.Add(&Aplus2C, &curve.C, &curve.C) // = 2*C
|
|
kFieldOps.Add(&C4, &Aplus2C, &Aplus2C) // = 4*C
|
|
kFieldOps.Add(&Aplus2C, &Aplus2C, &curve.A) // = 2*C + A
|
|
|
|
x0.X = P503_OneFp2
|
|
x1 = *xP
|
|
|
|
// Iterate over the bits of the scalar, top to bottom
|
|
prevBit := uint8(0)
|
|
for i := len(scalar) - 1; i >= 0; i-- {
|
|
scalarByte := scalar[i]
|
|
for j := 7; j >= 0; j-- {
|
|
bit := (scalarByte >> uint(j)) & 0x1
|
|
kCurveOps.Params.Op.CondSwap(&x0.X, &x0.Z, &x1.X, &x1.Z, (bit ^ prevBit))
|
|
//sProjectivePointConditionalSwap(&x0, &x1, (bit ^ prevBit))
|
|
tmp = DoubleProjFp2(&x0, &Aplus2C, &C4)
|
|
x1 = AddProjFp2(&x0, &x1, xP)
|
|
x0 = tmp
|
|
prevBit = bit
|
|
}
|
|
}
|
|
// now prevBit is the lowest bit of the scalar
|
|
kCurveOps.Params.Op.CondSwap(&x0.X, &x0.Z, &x1.X, &x1.Z, prevBit)
|
|
return x0
|
|
}
|
|
|
|
// Returns true if lhs = rhs. Takes variable time.
|
|
func VartimeEqFp2(lhs, rhs *Fp2Element) bool {
|
|
a := *lhs
|
|
b := *rhs
|
|
|
|
fp503StrongReduce(&a.A)
|
|
fp503StrongReduce(&a.B)
|
|
fp503StrongReduce(&b.A)
|
|
fp503StrongReduce(&b.B)
|
|
|
|
eq := true
|
|
for i := 0; i < len(a.A) && eq; i++ {
|
|
eq = eq && (a.A[i] == b.A[i])
|
|
eq = eq && (a.B[i] == b.B[i])
|
|
}
|
|
return eq
|
|
}
|
|
|
|
// Returns true if lhs = rhs. Takes variable time.
|
|
func VartimeEqProjFp2(lhs, rhs *ProjectivePoint) bool {
|
|
var t0, t1 Fp2Element
|
|
kFieldOps.Mul(&t0, &lhs.X, &rhs.Z)
|
|
kFieldOps.Mul(&t1, &lhs.Z, &rhs.X)
|
|
return VartimeEqFp2(&t0, &t1)
|
|
}
|
|
|
|
func (GeneratedTestParams) generateFp2p503(rand *rand.Rand) Fp2Element {
|
|
// Generation strategy: low limbs taken from [0,2^64); high limb
|
|
// taken from smaller range
|
|
//
|
|
// Size hint is ignored since all elements are fixed size.
|
|
//
|
|
// Field elements taken in range [0,2p). Emulate this by capping
|
|
// the high limb by the top digit of 2*p-1:
|
|
//
|
|
// sage: (2*p-1).digits(2^64)[-1]
|
|
// 36255204122967100
|
|
//
|
|
// This still allows generating values >= 2p, but hopefully that
|
|
// excess is OK (and if it's not, we'll find out, because it's for
|
|
// testing...)
|
|
//
|
|
highLimb := rand.Uint64() % 36255204122967100
|
|
fpElementGen := func() FpElement {
|
|
return FpElement{
|
|
rand.Uint64(),
|
|
rand.Uint64(),
|
|
rand.Uint64(),
|
|
rand.Uint64(),
|
|
rand.Uint64(),
|
|
rand.Uint64(),
|
|
rand.Uint64(),
|
|
highLimb,
|
|
}
|
|
}
|
|
return Fp2Element{A: fpElementGen(), B: fpElementGen()}
|
|
}
|
|
|
|
func (c GeneratedTestParams) Generate(rand *rand.Rand, size int) reflect.Value {
|
|
return reflect.ValueOf(
|
|
GeneratedTestParams{
|
|
ProjectivePoint{
|
|
X: c.generateFp2p503(rand),
|
|
Z: c.generateFp2p503(rand),
|
|
},
|
|
ProjectiveCurveParameters{
|
|
A: c.generateFp2p503(rand),
|
|
C: c.generateFp2p503(rand),
|
|
},
|
|
c.generateFp2p503(rand),
|
|
})
|
|
}
|
|
|
|
func (x primeFieldElement) Generate(rand *rand.Rand, size int) reflect.Value {
|
|
return reflect.ValueOf(primeFieldElement{A: new(GeneratedTestParams).generateFp2p503(rand).A})
|
|
}
|
|
|
|
// Convert an FpElement to a big.Int for testing. Because this is only
|
|
// for testing, no big.Int to FpElement conversion is provided.
|
|
func radix64ToBigInt(x []uint64) *big.Int {
|
|
radix := new(big.Int)
|
|
// 2^64
|
|
radix.UnmarshalText(([]byte)("18446744073709551616"))
|
|
|
|
base := new(big.Int).SetUint64(1)
|
|
val := new(big.Int).SetUint64(0)
|
|
tmp := new(big.Int)
|
|
|
|
for _, xi := range x {
|
|
tmp.SetUint64(xi)
|
|
tmp.Mul(tmp, base)
|
|
val.Add(val, tmp)
|
|
base.Mul(base, radix)
|
|
}
|
|
|
|
return val
|
|
}
|
|
|
|
func toBigInt(x *FpElement) *big.Int {
|
|
// Convert from Montgomery form
|
|
return toBigIntFromMontgomeryForm(x)
|
|
}
|
|
|
|
func toBigIntFromMontgomeryForm(x *FpElement) *big.Int {
|
|
// Convert from Montgomery form
|
|
a := FpElement{}
|
|
aR := FpElementX2{}
|
|
copy(aR[:], x[:]) // = a*R
|
|
fp503MontgomeryReduce(&a, &aR) // = a mod p in [0,2p)
|
|
fp503StrongReduce(&a) // = a mod p in [0,p)
|
|
return radix64ToBigInt(a[:])
|
|
}
|