github.com/cloudflare/circl@v1.5.0/ecc/p384/point_test.go (about) 1 //go:build (!purego && arm64) || (!purego && amd64) 2 // +build !purego,arm64 !purego,amd64 3 4 package p384 5 6 import ( 7 "crypto/elliptic" 8 "crypto/rand" 9 "encoding/binary" 10 "testing" 11 12 "github.com/cloudflare/circl/internal/test" 13 ) 14 15 func randomAffine() *affinePoint { 16 params := elliptic.P384().Params() 17 k, _ := rand.Int(rand.Reader, params.N) 18 return newAffinePoint(params.ScalarBaseMult(k.Bytes())) 19 } 20 21 func randomJacobian() *jacobianPoint { 22 params := elliptic.P384().Params() 23 P := randomAffine().toJacobian() 24 z, _ := rand.Int(rand.Reader, params.P) 25 var l fp384 26 l.SetBigInt(z) 27 fp384Mul(&P.z, &P.z, &l) // z = z * l^1 28 fp384Mul(&P.y, &P.y, &l) 29 fp384Sqr(&l, &l) 30 fp384Mul(&P.x, &P.x, &l) // x = x * l^2 31 fp384Mul(&P.y, &P.y, &l) // y = y * l^3 32 return P 33 } 34 35 func randomProjective() *projectivePoint { 36 return randomJacobian().toProjective() 37 } 38 39 func TestPointDouble(t *testing.T) { 40 t.Run("2∞=∞", func(t *testing.T) { 41 Z := zeroPoint().toJacobian() 42 Z.double() 43 got := Z.isZero() 44 want := true 45 if got != want { 46 test.ReportError(t, got, want) 47 } 48 }) 49 50 t.Run("2P=P+P", func(t *testing.T) { 51 StdCurve := elliptic.P384() 52 for i := 0; i < 128; i++ { 53 P := randomJacobian() 54 55 x1, y1 := P.toAffine().toInt() 56 wantX, wantY := StdCurve.Double(x1, y1) 57 58 P.double() 59 gotX, gotY := P.toAffine().toInt() 60 if gotX.Cmp(wantX) != 0 { 61 test.ReportError(t, gotX, wantX, P) 62 } 63 if gotY.Cmp(wantY) != 0 { 64 test.ReportError(t, gotY, wantY) 65 } 66 } 67 }) 68 } 69 70 func TestPointAdd(t *testing.T) { 71 StdCurve := elliptic.P384() 72 Q, R := &jacobianPoint{}, &jacobianPoint{} 73 Z := zeroPoint().toJacobian() 74 P := randomJacobian() 75 76 t.Run("∞+∞=∞", func(t *testing.T) { 77 R.add(Z, Z) 78 got := R.isZero() 79 want := true 80 if got != want { 81 test.ReportError(t, got, want) 82 } 83 }) 84 85 t.Run("∞+P=P", func(t *testing.T) { 86 R.add(Z, P) 87 gotX, gotY := R.toAffine().toInt() 88 wantX, wantY := P.toAffine().toInt() 89 if gotX.Cmp(wantX) != 0 { 90 test.ReportError(t, gotX, wantX, P) 91 } 92 if gotY.Cmp(wantY) != 0 { 93 test.ReportError(t, gotY, wantY, P) 94 } 95 }) 96 97 t.Run("P+∞=P", func(t *testing.T) { 98 R.add(P, Z) 99 gotX, gotY := R.toAffine().toInt() 100 wantX, wantY := P.toAffine().toInt() 101 if gotX.Cmp(wantX) != 0 { 102 test.ReportError(t, gotX, wantX, P) 103 } 104 if gotY.Cmp(wantY) != 0 { 105 test.ReportError(t, gotY, wantY, P) 106 } 107 }) 108 109 t.Run("P+(-P)=∞", func(t *testing.T) { 110 *Q = *P 111 Q.neg() 112 R.add(P, Q) 113 got := R.isZero() 114 want := true 115 if got != want { 116 test.ReportError(t, got, want, P) 117 } 118 }) 119 120 t.Run("P+P=2P", func(t *testing.T) { 121 // This verifies that add function cannot be used for doublings. 122 for i := 0; i < 128; i++ { 123 P = randomJacobian() 124 125 R.add(P, P) 126 gotX, gotY := R.toAffine().toInt() 127 wantX, wantY := zeroPoint().toInt() 128 129 if gotX.Cmp(wantX) != 0 { 130 test.ReportError(t, gotX, wantX, P) 131 } 132 if gotY.Cmp(wantY) != 0 { 133 test.ReportError(t, gotY, wantY, P) 134 } 135 } 136 }) 137 138 t.Run("P+Q=R", func(t *testing.T) { 139 for i := 0; i < 128; i++ { 140 P = randomJacobian() 141 Q = randomJacobian() 142 143 x1, y1 := P.toAffine().toInt() 144 x2, y2 := Q.toAffine().toInt() 145 wantX, wantY := StdCurve.Add(x1, y1, x2, y2) 146 147 R.add(P, Q) 148 gotX, gotY := R.toAffine().toInt() 149 150 if gotX.Cmp(wantX) != 0 { 151 test.ReportError(t, gotX, wantX, P, Q) 152 } 153 if gotY.Cmp(wantY) != 0 { 154 test.ReportError(t, gotY, wantY, P, Q) 155 } 156 } 157 }) 158 } 159 160 func TestPointCompleteAdd(t *testing.T) { 161 StdCurve := elliptic.P384() 162 Q, R := &projectivePoint{}, &projectivePoint{} 163 Z := zeroPoint().toProjective() 164 P := randomProjective() 165 166 t.Run("∞+∞=∞", func(t *testing.T) { 167 R.completeAdd(Z, Z) 168 got := R.isZero() 169 want := true 170 if got != want { 171 test.ReportError(t, got, want) 172 } 173 }) 174 175 t.Run("∞+P=P", func(t *testing.T) { 176 R.completeAdd(Z, P) 177 gotX, gotY := R.toAffine().toInt() 178 wantX, wantY := P.toAffine().toInt() 179 if gotX.Cmp(wantX) != 0 { 180 test.ReportError(t, gotX, wantX, P) 181 } 182 if gotY.Cmp(wantY) != 0 { 183 test.ReportError(t, gotY, wantY, P) 184 } 185 }) 186 187 t.Run("P+∞=P", func(t *testing.T) { 188 R.completeAdd(P, Z) 189 gotX, gotY := R.toAffine().toInt() 190 wantX, wantY := P.toAffine().toInt() 191 if gotX.Cmp(wantX) != 0 { 192 test.ReportError(t, gotX, wantX, P) 193 } 194 if gotY.Cmp(wantY) != 0 { 195 test.ReportError(t, gotY, wantY, P) 196 } 197 }) 198 199 t.Run("P+(-P)=∞", func(t *testing.T) { 200 *Q = *P 201 Q.cneg(1) 202 R.completeAdd(P, Q) 203 got := R.isZero() 204 want := true 205 if got != want { 206 test.ReportError(t, got, want, P) 207 } 208 }) 209 210 t.Run("P+P=2P", func(t *testing.T) { 211 // This verifies that completeAdd can be used for doublings. 212 for i := 0; i < 128; i++ { 213 P := randomJacobian() 214 PP := P.toProjective() 215 216 R.completeAdd(PP, PP) 217 P.double() 218 219 gotX, gotY := R.toAffine().toInt() 220 wantX, wantY := P.toAffine().toInt() 221 222 if gotX.Cmp(wantX) != 0 { 223 test.ReportError(t, gotX, wantX, P) 224 } 225 if gotY.Cmp(wantY) != 0 { 226 test.ReportError(t, gotY, wantY, P) 227 } 228 } 229 }) 230 231 t.Run("P+Q=R", func(t *testing.T) { 232 for i := 0; i < 128; i++ { 233 P := randomProjective() 234 Q := randomProjective() 235 236 x1, y1 := P.toAffine().toInt() 237 x2, y2 := Q.toAffine().toInt() 238 wantX, wantY := StdCurve.Add(x1, y1, x2, y2) 239 240 R.completeAdd(P, Q) 241 gotX, gotY := R.toAffine().toInt() 242 243 if gotX.Cmp(wantX) != 0 { 244 test.ReportError(t, gotX, wantX, P, Q) 245 } 246 if gotY.Cmp(wantY) != 0 { 247 test.ReportError(t, gotY, wantY, P, Q) 248 } 249 } 250 }) 251 } 252 253 func TestPointMixAdd(t *testing.T) { 254 StdCurve := elliptic.P384() 255 aZ := zeroPoint() 256 jZ := zeroPoint().toJacobian() 257 R := &jacobianPoint{} 258 aQ := &affinePoint{} 259 aP := randomAffine() 260 jP := randomJacobian() 261 262 t.Run("∞+∞=∞", func(t *testing.T) { 263 R.mixadd(jZ, aZ) 264 got := R.isZero() 265 want := true 266 if got != want { 267 test.ReportError(t, got, want) 268 } 269 }) 270 271 t.Run("∞+P=P", func(t *testing.T) { 272 R.mixadd(jZ, aP) 273 gotX, gotY := R.toAffine().toInt() 274 wantX, wantY := aP.toInt() 275 if gotX.Cmp(wantX) != 0 { 276 test.ReportError(t, gotX, wantX, aP) 277 } 278 if gotY.Cmp(wantY) != 0 { 279 test.ReportError(t, gotY, wantY) 280 } 281 }) 282 283 t.Run("P+∞=P", func(t *testing.T) { 284 R.mixadd(jP, aZ) 285 gotX, gotY, gotZ := R.toInt() 286 wantX, wantY, wantZ := jP.toInt() 287 if gotX.Cmp(wantX) != 0 { 288 test.ReportError(t, gotX, wantX, jP) 289 } 290 if gotY.Cmp(wantY) != 0 { 291 test.ReportError(t, gotY, wantY) 292 } 293 if gotZ.Cmp(wantZ) != 0 { 294 test.ReportError(t, gotZ, wantZ) 295 } 296 }) 297 298 t.Run("P+(-P)=∞", func(t *testing.T) { 299 aQ = jP.toAffine() 300 aQ.neg() 301 R.mixadd(jP, aQ) 302 got := R.isZero() 303 want := true 304 if got != want { 305 test.ReportError(t, got, want, jP) 306 } 307 }) 308 309 t.Run("P+P=2P", func(t *testing.T) { 310 for i := 0; i < 128; i++ { 311 aQ := randomAffine() 312 jQ := aQ.toJacobian() 313 314 x, y := aQ.toInt() 315 wantX, wantY := StdCurve.Double(x, y) 316 317 R.mixadd(jQ, aQ) 318 gotX, gotY := R.toAffine().toInt() 319 320 if gotX.Cmp(wantX) != 0 { 321 test.ReportError(t, gotX, wantX, aQ) 322 } 323 if gotY.Cmp(wantY) != 0 { 324 test.ReportError(t, gotY, wantY) 325 } 326 } 327 }) 328 329 t.Run("P+Q=R", func(t *testing.T) { 330 for i := 0; i < 128; i++ { 331 aP = randomAffine() 332 jP = randomJacobian() 333 334 x1, y1 := jP.toAffine().toInt() 335 x2, y2 := aP.toInt() 336 wantX, wantY := StdCurve.Add(x1, y1, x2, y2) 337 338 R.mixadd(jP, aP) 339 gotX, gotY := R.toAffine().toInt() 340 341 if gotX.Cmp(wantX) != 0 { 342 test.ReportError(t, gotX, wantX, jP, aP) 343 } 344 if gotY.Cmp(wantY) != 0 { 345 test.ReportError(t, gotY, wantY) 346 } 347 } 348 }) 349 } 350 351 func TestOddMultiples(t *testing.T) { 352 t.Run("invalidOmega", func(t *testing.T) { 353 for w := uint(0); w < 2; w++ { 354 P := randomAffine() 355 PP := P.oddMultiples(w) 356 got := len(PP) 357 want := 0 358 if got != want { 359 test.ReportError(t, got, want, w) 360 } 361 } 362 }) 363 364 t.Run("validOmega", func(t *testing.T) { 365 StdCurve := elliptic.P384() 366 var jOdd [4]byte 367 for i := 0; i < 32; i++ { 368 P := randomAffine() 369 X, Y := P.toInt() 370 for w := uint(2); w < 10; w++ { 371 PP := P.oddMultiples(w) 372 for j, jP := range PP { 373 binary.BigEndian.PutUint32(jOdd[:], uint32(2*j+1)) 374 wantX, wantY := StdCurve.ScalarMult(X, Y, jOdd[:]) 375 gotX, gotY := jP.toAffine().toInt() 376 if gotX.Cmp(wantX) != 0 { 377 test.ReportError(t, gotX, wantX, w, j) 378 } 379 if gotY.Cmp(wantY) != 0 { 380 test.ReportError(t, gotY, wantY) 381 } 382 } 383 } 384 } 385 }) 386 } 387 388 func BenchmarkPoint(b *testing.B) { 389 P := randomJacobian() 390 Q := randomJacobian() 391 R := randomJacobian() 392 QQ := randomProjective() 393 RR := randomProjective() 394 aR := randomAffine() 395 396 b.Run("addition", func(b *testing.B) { 397 for i := 0; i < b.N; i++ { 398 R.add(P, Q) 399 } 400 }) 401 b.Run("fullAddition", func(b *testing.B) { 402 for i := 0; i < b.N; i++ { 403 RR.completeAdd(RR, QQ) 404 } 405 }) 406 b.Run("mixadd", func(b *testing.B) { 407 for i := 0; i < b.N; i++ { 408 P.mixadd(P, aR) 409 } 410 }) 411 b.Run("double", func(b *testing.B) { 412 for i := 0; i < b.N; i++ { 413 P.double() 414 } 415 }) 416 }