github.com/cloudflare/circl@v1.5.0/dh/sidh/internal/p751/curve_test.go (about) 1 // Code generated by go generate; DO NOT EDIT. 2 // This file was generated by robots. 3 4 package p751 5 6 import ( 7 "bytes" 8 crand "crypto/rand" 9 . "github.com/cloudflare/circl/dh/sidh/internal/common" 10 "io" 11 "math" 12 "math/rand" 13 "testing" 14 "time" 15 ) 16 17 func vartimeEqProjFp2(lhs, rhs *ProjectivePoint) bool { 18 var t0, t1 Fp2 19 mul(&t0, &lhs.X, &rhs.Z) 20 mul(&t1, &lhs.Z, &rhs.X) 21 return vartimeEqFp2(&t0, &t1) 22 } 23 24 func toAffine(point *ProjectivePoint) *Fp2 { 25 var affineX Fp2 26 inv(&affineX, &point.Z) 27 mul(&affineX, &affineX, &point.X) 28 return &affineX 29 } 30 31 func Test_jInvariant(t *testing.T) { 32 curve := ProjectiveCurveParameters{A: curveA, C: curveC} 33 jbufRes := make([]byte, params.SharedSecretSize) 34 jbufExp := make([]byte, params.SharedSecretSize) 35 var jInv Fp2 36 37 Jinvariant(&curve, &jInv) 38 FromMontgomery(&jInv, &jInv) 39 Fp2ToBytes(jbufRes, &jInv, params.Bytelen) 40 41 jInv = expectedJ 42 FromMontgomery(&jInv, &jInv) 43 Fp2ToBytes(jbufExp, &jInv, params.Bytelen) 44 45 if !bytes.Equal(jbufRes[:], jbufExp[:]) { 46 t.Error("Computed incorrect j-invariant: found\n", jbufRes, "\nexpected\n", jbufExp) 47 } 48 } 49 50 func TestProjectivePointVartimeEq(t *testing.T) { 51 var xP ProjectivePoint 52 53 xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 54 xQ := xP 55 56 // Scale xQ, which results in the same projective point 57 mul(&xQ.X, &xQ.X, &curveA) 58 mul(&xQ.Z, &xQ.Z, &curveA) 59 if !vartimeEqProjFp2(&xP, &xQ) { 60 t.Error("Expected the scaled point to be equal to the original") 61 } 62 } 63 64 func TestPointMulVersusSage(t *testing.T) { 65 curve := ProjectiveCurveParameters{A: curveA, C: curveC} 66 cparams := CalcCurveParamsEquiv4(&curve) 67 var xP ProjectivePoint 68 69 // x 2 70 xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 71 Pow2k(&xP, &cparams, 1) 72 afxQ := toAffine(&xP) 73 if !vartimeEqFp2(afxQ, &affineXP2) { 74 t.Error("\nExpected\n", affineXP2, "\nfound\n", afxQ) 75 } 76 77 // x 4 78 xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 79 Pow2k(&xP, &cparams, 2) 80 afxQ = toAffine(&xP) 81 if !vartimeEqFp2(afxQ, &affineXP4) { 82 t.Error("\nExpected\n", affineXP4, "\nfound\n", afxQ) 83 } 84 } 85 86 func TestPointMul9VersusSage(t *testing.T) { 87 curve := ProjectiveCurveParameters{A: curveA, C: curveC} 88 cparams := CalcCurveParamsEquiv3(&curve) 89 var xP ProjectivePoint 90 91 xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 92 Pow3k(&xP, &cparams, 2) 93 afxQ := toAffine(&xP) 94 if !vartimeEqFp2(afxQ, &affineXP9) { 95 t.Error("\nExpected\n", affineXP9, "\nfound\n", afxQ) 96 } 97 } 98 99 func BenchmarkThreePointLadder(b *testing.B) { 100 curve := ProjectiveCurveParameters{A: curveA, C: curveC} 101 for n := 0; n < b.N; n++ { 102 ScalarMul3Pt(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(scalar3Pt)*8), scalar3Pt[:]) 103 } 104 } 105 106 /* ------------------------------------------------------------------------- 107 Generate invalid public key points / ciphertext for test TestKEMInvalidPK 108 -------------------------------------------------------------------------*/ 109 110 // Left-to-right Montgomery ladder, Algorithm 4 in Costello-Smith 111 // Input: ProjectivePoint P (xP, zP) 112 // Output: x([scalar]P), z([scalar]P) 113 func montgomeryLadder(cparams *ProjectiveCurveParameters, P *ProjectivePoint, scalar []uint8, random uint) ProjectivePoint { 114 var R0, R2, R1 ProjectivePoint 115 coefEq := CalcCurveParamsEquiv4(cparams) // for xDbl 116 aPlus2Over4 := CalcAplus2Over4(cparams) // for xDblAdd 117 R0 = *P // RO <- P 118 R1 = *P 119 Pow2k(&R1, &coefEq, 1) // R1 <- [2]P 120 R2 = *P // R2 = R1-R0 = P 121 122 prevBit := uint8(0) 123 for i := int(random); i >= 0; i-- { 124 bit := (scalar[i>>3] >> (i & 7) & 1) 125 swap := prevBit ^ bit 126 prevBit = bit 127 cswap(&R0.X, &R0.Z, &R1.X, &R1.Z, swap) 128 R0, R1 = xDbladd(&R0, &R1, &R2, &aPlus2Over4) 129 } 130 cswap(&R0.X, &R0.Z, &R1.X, &R1.Z, prevBit) 131 return R0 132 } 133 134 // P = P + T 135 // From paper https://eprint.iacr.org/2017/212.pdf 136 // The map tau_T: P->P+T is (X : Z) -> (Z : X) on Montgomery curves 137 func tauT(P *ProjectivePoint) { 138 P.X, P.Z = P.Z, P.X // magic! 139 } 140 141 // Construct Invalid public key tuple (P,Q) such that P and Q are linearly dependent 142 // Simulate section 3.1.1 of paper https://eprint.iacr.org/2022/054.pdf 143 // We only construct point P and Q because in the attacks the third point is P-Q by construction 144 // and the countermeasure does not test it 145 // Without loss of generality, we assume the curve is the starting curve 146 func testInvalidPKNoneLinear(t *testing.T) { 147 148 // Generate random scalar as secret 149 secret := make([]byte, params.B.SecretByteLen) 150 _, err := io.ReadFull(crand.Reader, secret) 151 if err != nil { 152 t.Error("Fail read random bytes") 153 } 154 155 var P, Q ProjectivePoint 156 157 rand.Seed(time.Now().UnixNano()) 158 random_index := rand.Intn(int(params.B.SecretByteLen-1) * 8) 159 160 // Set P as a point of order 3^e3 161 P = ProjectivePoint{X: params.B.AffineP, Z: params.OneFp2} 162 163 // Set Q = [k]P, where k = secret[:random_index] 164 Q = montgomeryLadder(¶ms.InitCurve, &P, secret, uint(random_index)) 165 166 // Make sure Q is of full order 3^e_3, 167 var test_Q ProjectivePoint 168 test_Q = Q 169 170 var e3 uint32 171 e3_float := float64(int(params.B.SecretBitLen)+1) / math.Log2(3) 172 e3 = uint32(e3_float) 173 cparam_q := CalcCurveParamsEquiv3(¶ms.InitCurve) 174 Pow3k(&test_Q, &cparam_q, e3-1) 175 176 var test_QZ Fp2 177 FromMontgomery(&test_QZ, &test_Q.Z) 178 179 // Q are not of full order 3^e_3 180 for isZero(&test_QZ) == 1 { 181 rand.Seed(time.Now().UnixNano()) 182 random_index = rand.Intn(int(params.B.SecretByteLen-1) * 8) 183 Q = montgomeryLadder(¶ms.InitCurve, &P, secret, uint(random_index)) 184 test_Q = Q 185 Pow3k(&test_Q, &cparam_q, e3-1) 186 FromMontgomery(&test_QZ, &test_Q.Z) 187 } 188 189 // invQz = 1/Q.Z 190 var invQz Fp2 191 invQz = Q.Z 192 inv(&invQz, &invQz) 193 194 mul(&P.X, &P.X, &P.Z) 195 mul(&Q.X, &Q.X, &invQz) 196 197 var xP, xQ, xQmP ProjectivePoint 198 xP = ProjectivePoint{X: P.X, Z: params.OneFp2} 199 xQ = ProjectivePoint{X: Q.X, Z: params.OneFp2} 200 xQmP = ProjectivePoint{X: params.OneFp2, Z: params.OneFp2} 201 202 error_verify := PublicKeyValidation(¶ms.InitCurve, &xP, &xQ, &xQmP, params.B.SecretBitLen) 203 if error_verify == nil { 204 t.Errorf("\nExpect linearly dependent ciphertext to fail, index: %v scalar: %v ", random_index, secret) 205 } 206 } 207 208 // Construct Invalid public key tuple (P,Q) such that Q = [k]P + T, where k is random and T is the point of order 2. 209 // Simulate HB and section 3.1.2 of paper https://eprint.iacr.org/2022/054.pdf 210 // We only construct point P and Q because in the attacks the third point is P-Q by construction 211 // and the countermeasure does not test it 212 // Without loss of generality, we assume the curve is the starting curve 213 func testInvalidPKT(t *testing.T) { 214 215 // Generate random scalar as secret 216 secret := make([]byte, params.B.SecretByteLen) 217 _, err := io.ReadFull(crand.Reader, secret) 218 if err != nil { 219 t.Error("Fail read random bytes") 220 } 221 222 var P, Q ProjectivePoint 223 224 rand.Seed(time.Now().UnixNano()) 225 random_index := rand.Intn(int(params.B.SecretByteLen-1) * 8) 226 227 // Set P as a point of order 3^e3 228 P = ProjectivePoint{X: params.B.AffineP, Z: params.OneFp2} 229 230 // Set Q = [k]P, where k = secret[:random_index] 231 Q = montgomeryLadder(¶ms.InitCurve, &P, secret, uint(random_index)) 232 // Q = [k]P + T 233 tauT(&Q) 234 235 var invQz Fp2 236 invQz = Q.Z 237 inv(&invQz, &invQz) 238 239 mul(&P.X, &P.X, &P.Z) 240 mul(&Q.X, &Q.X, &invQz) 241 242 var xP, xQ, xQmP ProjectivePoint 243 xP = ProjectivePoint{X: P.X, Z: params.OneFp2} 244 xQ = ProjectivePoint{X: Q.X, Z: params.OneFp2} 245 xQmP = ProjectivePoint{X: params.OneFp2, Z: params.OneFp2} 246 247 error_verify := PublicKeyValidation(¶ms.InitCurve, &xP, &xQ, &xQmP, params.B.SecretBitLen) 248 if error_verify == nil { 249 t.Errorf("\nExpect ciphertext involve point T to fail, index: %v scalar: %v ", random_index, secret) 250 } 251 } 252 253 // Construct Invalid public key tuple (P,Q) such that P and Q are in E[2^e2] 254 // Simulate section 3.2 of paper https://eprint.iacr.org/2022/054.pdf 255 // We only construct point P and Q because in the attacks the third point is P-Q by construction 256 // and the countermeasure does not test it 257 // Without loss of generality, we assume the curve is the starting curve 258 func testInvalidPKOrder2(t *testing.T) { 259 260 // Generate random scalar as secret 261 secret := make([]byte, params.B.SecretByteLen) 262 _, err := io.ReadFull(crand.Reader, secret) 263 if err != nil { 264 t.Error("Fail read random bytes") 265 } 266 267 var P, Q ProjectivePoint 268 269 P = ProjectivePoint{X: params.A.AffineP, Z: params.OneFp2} 270 Q = ProjectivePoint{X: params.A.AffineQ, Z: params.OneFp2} 271 272 rand.Seed(time.Now().UnixNano()) 273 random_index_p := rand.Intn(int(params.A.SecretByteLen-1) * 8) 274 random_index_q := rand.Intn(int(params.A.SecretByteLen-1) * 8) 275 276 P = montgomeryLadder(¶ms.InitCurve, &P, secret, uint(random_index_p)) 277 Q = montgomeryLadder(¶ms.InitCurve, &Q, secret, uint(random_index_q)) 278 279 var invQz, invPz Fp2 280 invQz = Q.Z 281 invPz = P.Z 282 inv(&invQz, &invQz) 283 inv(&invPz, &invPz) 284 285 mul(&P.X, &P.X, &invPz) 286 mul(&Q.X, &Q.X, &invQz) 287 288 var xP, xQ, xQmP ProjectivePoint 289 xP = ProjectivePoint{X: P.X, Z: params.OneFp2} 290 xQ = ProjectivePoint{X: Q.X, Z: params.OneFp2} 291 xQmP = ProjectivePoint{X: params.OneFp2, Z: params.OneFp2} 292 293 error_verify := PublicKeyValidation(¶ms.InitCurve, &xP, &xQ, &xQmP, params.B.SecretBitLen) 294 if error_verify == nil { 295 t.Errorf("\nExpect ciphertext in torsion E[2^e2] to fail, index_p: %v index_q: %v scalar: %v ", random_index_p, random_index_q, secret) 296 } 297 298 } 299 300 // Construct Invalid public key tuple (P,Q) such that P and Q are in E[3^e3] but not of full order 3^e3 301 // Simulate section 3.1.1 of paper https://eprint.iacr.org/2022/054.pdf 302 // We only construct point P and Q because in the attacks the third point is P-Q by construction 303 // and the countermeasure does not test it 304 // Without loss of generality, we assume the curve is the starting curve 305 func testInvalidPKFullOrder(t *testing.T) { 306 307 var P, Q ProjectivePoint 308 309 P = ProjectivePoint{X: params.B.AffineP, Z: params.OneFp2} 310 Q = ProjectivePoint{X: params.B.AffineQ, Z: params.OneFp2} 311 312 var e3 uint32 313 e3_float := float64(int(params.B.SecretBitLen)+1) / math.Log2(3) 314 e3 = uint32(e3_float) 315 316 rand.Seed(time.Now().UnixNano()) 317 random_index_p := rand.Intn(int(e3)) 318 random_index_q := rand.Intn(int(e3)) 319 320 cparam_q := CalcCurveParamsEquiv3(¶ms.InitCurve) 321 Pow3k(&P, &cparam_q, uint32(random_index_p)) 322 Pow3k(&Q, &cparam_q, uint32(random_index_q)) 323 324 var invQz, invPz Fp2 325 invQz = Q.Z 326 invPz = P.Z 327 inv(&invQz, &invQz) 328 inv(&invPz, &invPz) 329 330 mul(&P.X, &P.X, &invPz) 331 mul(&Q.X, &Q.X, &invQz) 332 333 var xP, xQ, xQmP ProjectivePoint 334 xP = ProjectivePoint{X: P.X, Z: params.OneFp2} 335 xQ = ProjectivePoint{X: Q.X, Z: params.OneFp2} 336 xQmP = ProjectivePoint{X: params.OneFp2, Z: params.OneFp2} 337 338 error_verify := PublicKeyValidation(¶ms.InitCurve, &xP, &xQ, &xQmP, params.B.SecretBitLen) 339 if error_verify == nil { 340 t.Errorf("\nExpect ciphertext not of full order to fail, index_p: %v index_q: %v ", random_index_p, random_index_q) 341 } 342 343 } 344 345 // A trivial test case not covered by paper https://eprint.iacr.org/2022/054.pdf and HB 346 // Countermeasure in https://eprint.iacr.org/2022/054.pdf only cares about P and Q 347 // But if PmQ is point T or O, that can also lead to recovery of the first bit 348 func testInvalidPmQ(t *testing.T) { 349 350 var zero Fp2 351 var xP, xQ, xQmP ProjectivePoint 352 xP = ProjectivePoint{X: params.A.AffineP, Z: params.OneFp2} 353 xQ = ProjectivePoint{X: params.A.AffineQ, Z: params.OneFp2} 354 xQmP = ProjectivePoint{X: zero, Z: params.OneFp2} 355 356 error_verify := PublicKeyValidation(¶ms.InitCurve, &xP, &xQ, &xQmP, params.B.SecretBitLen) 357 if error_verify == nil { 358 t.Errorf("\nExpect PmQ as T to fail\n") 359 } 360 361 } 362 363 // Test valid ciphertext 364 // Where P, Q are linearly independent points of correct order 3^e3 in E[3^e3] 365 func testValidPQ(t *testing.T) { 366 367 var xP, xQ, xQmP ProjectivePoint 368 xP = ProjectivePoint{X: params.B.AffineP, Z: params.OneFp2} 369 xQ = ProjectivePoint{X: params.B.AffineQ, Z: params.OneFp2} 370 xQmP = ProjectivePoint{X: params.OneFp2, Z: params.OneFp2} 371 372 error_verify := PublicKeyValidation(¶ms.InitCurve, &xP, &xQ, &xQmP, params.B.SecretBitLen) 373 if error_verify != nil { 374 t.Errorf("\nExpect correct ciphertext to not fail\n") 375 } 376 377 } 378 379 /* ------------------------------------------------------------------------- 380 Public key / Ciphertext validation against attacks proposed in paper https://eprint.iacr.org/2022/054.pdf and HB 381 -------------------------------------------------------------------------*/ 382 383 func TestInvalidPK(t *testing.T) { 384 385 t.Run("InvalidPmQ", testInvalidPmQ) 386 t.Run("InvalidPKNoneLinear", testInvalidPKNoneLinear) 387 t.Run("InvalidPKT", testInvalidPKT) 388 t.Run("InvalidPKOrder2", testInvalidPKOrder2) 389 t.Run("InvalidPKFullOrder", testInvalidPKFullOrder) 390 t.Run("ValidPQ", testValidPQ) 391 392 }