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[:]) }