ECPoint.java revision 5db505e1f6a68c8d5dfdb0fed0b8607dea7bed96
1package org.bouncycastle.math.ec; 2 3import java.math.BigInteger; 4 5/** 6 * base class for points on elliptic curves. 7 */ 8public abstract class ECPoint 9{ 10 protected static ECFieldElement[] EMPTY_ZS = new ECFieldElement[0]; 11 12 protected static ECFieldElement[] getInitialZCoords(ECCurve curve) 13 { 14 // Cope with null curve, most commonly used by implicitlyCa 15 int coord = null == curve ? ECCurve.COORD_AFFINE : curve.getCoordinateSystem(); 16 17 switch (coord) 18 { 19 case ECCurve.COORD_AFFINE: 20 case ECCurve.COORD_LAMBDA_AFFINE: 21 return EMPTY_ZS; 22 default: 23 break; 24 } 25 26 ECFieldElement one = curve.fromBigInteger(ECConstants.ONE); 27 28 switch (coord) 29 { 30 case ECCurve.COORD_HOMOGENEOUS: 31 case ECCurve.COORD_JACOBIAN: 32 case ECCurve.COORD_LAMBDA_PROJECTIVE: 33 return new ECFieldElement[]{ one }; 34 case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: 35 return new ECFieldElement[]{ one, one, one }; 36 case ECCurve.COORD_JACOBIAN_MODIFIED: 37 return new ECFieldElement[]{ one, curve.getA() }; 38 default: 39 throw new IllegalArgumentException("unknown coordinate system"); 40 } 41 } 42 43 protected ECCurve curve; 44 protected ECFieldElement x; 45 protected ECFieldElement y; 46 protected ECFieldElement[] zs; 47 48 protected boolean withCompression; 49 50 protected PreCompInfo preCompInfo = null; 51 52 protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y) 53 { 54 this(curve, x, y, getInitialZCoords(curve)); 55 } 56 57 protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs) 58 { 59 this.curve = curve; 60 this.x = x; 61 this.y = y; 62 this.zs = zs; 63 } 64 65 public ECCurve getCurve() 66 { 67 return curve; 68 } 69 70 protected int getCurveCoordinateSystem() 71 { 72 // Cope with null curve, most commonly used by implicitlyCa 73 return null == curve ? ECCurve.COORD_AFFINE : curve.getCoordinateSystem(); 74 } 75 76 /** 77 * Normalizes this point, and then returns the affine x-coordinate. 78 * 79 * Note: normalization can be expensive, this method is deprecated in favour 80 * of caller-controlled normalization. 81 * 82 * @deprecated Use getAffineXCoord, or normalize() and getXCoord(), instead 83 */ 84 public ECFieldElement getX() 85 { 86 return normalize().getXCoord(); 87 } 88 89 90 /** 91 * Normalizes this point, and then returns the affine y-coordinate. 92 * 93 * Note: normalization can be expensive, this method is deprecated in favour 94 * of caller-controlled normalization. 95 * 96 * @deprecated Use getAffineYCoord, or normalize() and getYCoord(), instead 97 */ 98 public ECFieldElement getY() 99 { 100 return normalize().getYCoord(); 101 } 102 103 /** 104 * Returns the affine x-coordinate after checking that this point is normalized. 105 * 106 * @return The affine x-coordinate of this point 107 * @throws IllegalStateException if the point is not normalized 108 */ 109 public ECFieldElement getAffineXCoord() 110 { 111 checkNormalized(); 112 return getXCoord(); 113 } 114 115 /** 116 * Returns the affine y-coordinate after checking that this point is normalized 117 * 118 * @return The affine y-coordinate of this point 119 * @throws IllegalStateException if the point is not normalized 120 */ 121 public ECFieldElement getAffineYCoord() 122 { 123 checkNormalized(); 124 return getYCoord(); 125 } 126 127 /** 128 * Returns the x-coordinate. 129 * 130 * Caution: depending on the curve's coordinate system, this may not be the same value as in an 131 * affine coordinate system; use normalize() to get a point where the coordinates have their 132 * affine values, or use getAffineXCoord if you expect the point to already have been 133 * normalized. 134 * 135 * @return the x-coordinate of this point 136 */ 137 public ECFieldElement getXCoord() 138 { 139 return x; 140 } 141 142 /** 143 * Returns the y-coordinate. 144 * 145 * Caution: depending on the curve's coordinate system, this may not be the same value as in an 146 * affine coordinate system; use normalize() to get a point where the coordinates have their 147 * affine values, or use getAffineYCoord if you expect the point to already have been 148 * normalized. 149 * 150 * @return the y-coordinate of this point 151 */ 152 public ECFieldElement getYCoord() 153 { 154 return y; 155 } 156 157 public ECFieldElement getZCoord(int index) 158 { 159 return (index < 0 || index >= zs.length) ? null : zs[index]; 160 } 161 162 public ECFieldElement[] getZCoords() 163 { 164 int zsLen = zs.length; 165 if (zsLen == 0) 166 { 167 return zs; 168 } 169 ECFieldElement[] copy = new ECFieldElement[zsLen]; 170 System.arraycopy(zs, 0, copy, 0, zsLen); 171 return copy; 172 } 173 174 protected ECFieldElement getRawXCoord() 175 { 176 return x; 177 } 178 179 protected ECFieldElement getRawYCoord() 180 { 181 return y; 182 } 183 184 protected void checkNormalized() 185 { 186 if (!isNormalized()) 187 { 188 throw new IllegalStateException("point not in normal form"); 189 } 190 } 191 192 public boolean isNormalized() 193 { 194 int coord = this.getCurveCoordinateSystem(); 195 196 return coord == ECCurve.COORD_AFFINE 197 || coord == ECCurve.COORD_LAMBDA_AFFINE 198 || isInfinity() 199 || zs[0].bitLength() == 1; 200 } 201 202 /** 203 * Normalization ensures that any projective coordinate is 1, and therefore that the x, y 204 * coordinates reflect those of the equivalent point in an affine coordinate system. 205 * 206 * @return a new ECPoint instance representing the same point, but with normalized coordinates 207 */ 208 public ECPoint normalize() 209 { 210 if (this.isInfinity()) 211 { 212 return this; 213 } 214 215 switch (this.getCurveCoordinateSystem()) 216 { 217 case ECCurve.COORD_AFFINE: 218 case ECCurve.COORD_LAMBDA_AFFINE: 219 { 220 return this; 221 } 222 default: 223 { 224 ECFieldElement Z1 = getZCoord(0); 225 if (Z1.bitLength() == 1) 226 { 227 return this; 228 } 229 230 return normalize(Z1.invert()); 231 } 232 } 233 } 234 235 ECPoint normalize(ECFieldElement zInv) 236 { 237 switch (this.getCurveCoordinateSystem()) 238 { 239 case ECCurve.COORD_HOMOGENEOUS: 240 case ECCurve.COORD_LAMBDA_PROJECTIVE: 241 { 242 return createScaledPoint(zInv, zInv); 243 } 244 case ECCurve.COORD_JACOBIAN: 245 case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: 246 case ECCurve.COORD_JACOBIAN_MODIFIED: 247 { 248 ECFieldElement zInv2 = zInv.square(), zInv3 = zInv2.multiply(zInv); 249 return createScaledPoint(zInv2, zInv3); 250 } 251 default: 252 { 253 throw new IllegalStateException("not a projective coordinate system"); 254 } 255 } 256 } 257 258 protected ECPoint createScaledPoint(ECFieldElement sx, ECFieldElement sy) 259 { 260 return this.getCurve().createRawPoint(getRawXCoord().multiply(sx), getRawYCoord().multiply(sy), this.withCompression); 261 } 262 263 public boolean isInfinity() 264 { 265 return x == null || y == null || (zs.length > 0 && zs[0].isZero()); 266 } 267 268 public boolean isCompressed() 269 { 270 return this.withCompression; 271 } 272 273 public boolean equals(ECPoint other) 274 { 275 if (null == other) 276 { 277 return false; 278 } 279 280 ECCurve c1 = this.getCurve(), c2 = other.getCurve(); 281 boolean n1 = (null == c1), n2 = (null == c2); 282 boolean i1 = isInfinity(), i2 = other.isInfinity(); 283 284 if (i1 || i2) 285 { 286 return (i1 && i2) && (n1 || n2 || c1.equals(c2)); 287 } 288 289 ECPoint p1 = this, p2 = other; 290 if (n1 && n2) 291 { 292 // Points with null curve are in affine form, so already normalized 293 } 294 else if (n1) 295 { 296 p2 = p2.normalize(); 297 } 298 else if (n2) 299 { 300 p1 = p1.normalize(); 301 } 302 else if (!c1.equals(c2)) 303 { 304 return false; 305 } 306 else 307 { 308 // TODO Consider just requiring already normalized, to avoid silent performance degradation 309 310 ECPoint[] points = new ECPoint[]{ this, c1.importPoint(p2) }; 311 312 // TODO This is a little strong, really only requires coZNormalizeAll to get Zs equal 313 c1.normalizeAll(points); 314 315 p1 = points[0]; 316 p2 = points[1]; 317 } 318 319 return p1.getXCoord().equals(p2.getXCoord()) && p1.getYCoord().equals(p2.getYCoord()); 320 } 321 322 public boolean equals(Object other) 323 { 324 if (other == this) 325 { 326 return true; 327 } 328 329 if (!(other instanceof ECPoint)) 330 { 331 return false; 332 } 333 334 return equals((ECPoint)other); 335 } 336 337 public int hashCode() 338 { 339 ECCurve c = this.getCurve(); 340 int hc = (null == c) ? 0 : ~c.hashCode(); 341 342 if (!this.isInfinity()) 343 { 344 // TODO Consider just requiring already normalized, to avoid silent performance degradation 345 346 ECPoint p = normalize(); 347 348 hc ^= p.getXCoord().hashCode() * 17; 349 hc ^= p.getYCoord().hashCode() * 257; 350 } 351 352 return hc; 353 } 354 355 public String toString() 356 { 357 if (this.isInfinity()) 358 { 359 return "INF"; 360 } 361 362 StringBuffer sb = new StringBuffer(); 363 sb.append('('); 364 sb.append(getRawXCoord()); 365 sb.append(','); 366 sb.append(getRawYCoord()); 367 for (int i = 0; i < zs.length; ++i) 368 { 369 sb.append(','); 370 sb.append(zs[i]); 371 } 372 sb.append(')'); 373 return sb.toString(); 374 } 375 376 public byte[] getEncoded() 377 { 378 return getEncoded(this.withCompression); 379 } 380 381 /** 382 * return the field element encoded with point compression. (S 4.3.6) 383 */ 384 public byte[] getEncoded(boolean compressed) 385 { 386 if (this.isInfinity()) 387 { 388 return new byte[1]; 389 } 390 391 ECPoint normed = normalize(); 392 393 byte[] X = normed.getXCoord().getEncoded(); 394 395 if (compressed) 396 { 397 byte[] PO = new byte[X.length + 1]; 398 PO[0] = (byte)(normed.getCompressionYTilde() ? 0x03 : 0x02); 399 System.arraycopy(X, 0, PO, 1, X.length); 400 return PO; 401 } 402 403 byte[] Y = normed.getYCoord().getEncoded(); 404 405 byte[] PO = new byte[X.length + Y.length + 1]; 406 PO[0] = 0x04; 407 System.arraycopy(X, 0, PO, 1, X.length); 408 System.arraycopy(Y, 0, PO, X.length + 1, Y.length); 409 return PO; 410 } 411 412 protected abstract boolean getCompressionYTilde(); 413 414 public abstract ECPoint add(ECPoint b); 415 416 public abstract ECPoint negate(); 417 418 public abstract ECPoint subtract(ECPoint b); 419 420 public ECPoint timesPow2(int e) 421 { 422 if (e < 0) 423 { 424 throw new IllegalArgumentException("'e' cannot be negative"); 425 } 426 427 ECPoint p = this; 428 while (--e >= 0) 429 { 430 p = p.twice(); 431 } 432 return p; 433 } 434 435 public abstract ECPoint twice(); 436 437 public ECPoint twicePlus(ECPoint b) 438 { 439 return twice().add(b); 440 } 441 442 public ECPoint threeTimes() 443 { 444 return twicePlus(this); 445 } 446 447 /** 448 * Multiplies this <code>ECPoint</code> by the given number. 449 * @param k The multiplicator. 450 * @return <code>k * this</code>. 451 */ 452 public ECPoint multiply(BigInteger k) 453 { 454 return this.getCurve().getMultiplier().multiply(this, k); 455 } 456 457 /** 458 * Elliptic curve points over Fp 459 */ 460 public static class Fp extends ECPoint 461 { 462 /** 463 * Create a point which encodes with point compression. 464 * 465 * @param curve the curve to use 466 * @param x affine x co-ordinate 467 * @param y affine y co-ordinate 468 * 469 * @deprecated Use ECCurve.createPoint to construct points 470 */ 471 public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y) 472 { 473 this(curve, x, y, false); 474 } 475 476 /** 477 * Create a point that encodes with or without point compresion. 478 * 479 * @param curve the curve to use 480 * @param x affine x co-ordinate 481 * @param y affine y co-ordinate 482 * @param withCompression if true encode with point compression 483 * 484 * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} 485 */ 486 public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) 487 { 488 super(curve, x, y); 489 490 if ((x != null && y == null) || (x == null && y != null)) 491 { 492 throw new IllegalArgumentException("Exactly one of the field elements is null"); 493 } 494 495 this.withCompression = withCompression; 496 } 497 498 Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) 499 { 500 super(curve, x, y, zs); 501 502 this.withCompression = withCompression; 503 } 504 505 protected boolean getCompressionYTilde() 506 { 507 return this.getAffineYCoord().testBitZero(); 508 } 509 510 public ECFieldElement getZCoord(int index) 511 { 512 if (index == 1 && ECCurve.COORD_JACOBIAN_MODIFIED == this.getCurveCoordinateSystem()) 513 { 514 return getJacobianModifiedW(); 515 } 516 517 return super.getZCoord(index); 518 } 519 520 // B.3 pg 62 521 public ECPoint add(ECPoint b) 522 { 523 if (this.isInfinity()) 524 { 525 return b; 526 } 527 if (b.isInfinity()) 528 { 529 return this; 530 } 531 if (this == b) 532 { 533 return twice(); 534 } 535 536 ECCurve curve = this.getCurve(); 537 int coord = curve.getCoordinateSystem(); 538 539 ECFieldElement X1 = this.x, Y1 = this.y; 540 ECFieldElement X2 = b.x, Y2 = b.y; 541 542 switch (coord) 543 { 544 case ECCurve.COORD_AFFINE: 545 { 546 ECFieldElement dx = X2.subtract(X1), dy = Y2.subtract(Y1); 547 548 if (dx.isZero()) 549 { 550 if (dy.isZero()) 551 { 552 // this == b, i.e. this must be doubled 553 return twice(); 554 } 555 556 // this == -b, i.e. the result is the point at infinity 557 return curve.getInfinity(); 558 } 559 560 ECFieldElement gamma = dy.divide(dx); 561 ECFieldElement X3 = gamma.square().subtract(X1).subtract(X2); 562 ECFieldElement Y3 = gamma.multiply(X1.subtract(X3)).subtract(Y1); 563 564 return new ECPoint.Fp(curve, X3, Y3, this.withCompression); 565 } 566 567 case ECCurve.COORD_HOMOGENEOUS: 568 { 569 ECFieldElement Z1 = this.zs[0]; 570 ECFieldElement Z2 = b.zs[0]; 571 572 boolean Z1IsOne = Z1.bitLength() == 1; 573 boolean Z2IsOne = Z2.bitLength() == 1; 574 575 ECFieldElement u1 = Z1IsOne ? Y2 : Y2.multiply(Z1); 576 ECFieldElement u2 = Z2IsOne ? Y1 : Y1.multiply(Z2); 577 ECFieldElement u = u1.subtract(u2); 578 ECFieldElement v1 = Z1IsOne ? X2 : X2.multiply(Z1); 579 ECFieldElement v2 = Z2IsOne ? X1 : X1.multiply(Z2); 580 ECFieldElement v = v1.subtract(v2); 581 582 // Check if b == this or b == -this 583 if (v.isZero()) 584 { 585 if (u.isZero()) 586 { 587 // this == b, i.e. this must be doubled 588 return this.twice(); 589 } 590 591 // this == -b, i.e. the result is the point at infinity 592 return curve.getInfinity(); 593 } 594 595 // TODO Optimize for when w == 1 596 ECFieldElement w = Z1IsOne ? Z2 : Z2IsOne ? Z1 : Z1.multiply(Z2); 597 ECFieldElement vSquared = v.square(); 598 ECFieldElement vCubed = vSquared.multiply(v); 599 ECFieldElement vSquaredV2 = vSquared.multiply(v2); 600 ECFieldElement A = u.square().multiply(w).subtract(vCubed).subtract(two(vSquaredV2)); 601 602 ECFieldElement X3 = v.multiply(A); 603 ECFieldElement Y3 = vSquaredV2.subtract(A).multiply(u).subtract(vCubed.multiply(u2)); 604 ECFieldElement Z3 = vCubed.multiply(w); 605 606 return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); 607 } 608 609 case ECCurve.COORD_JACOBIAN: 610 case ECCurve.COORD_JACOBIAN_MODIFIED: 611 { 612 ECFieldElement Z1 = this.zs[0]; 613 ECFieldElement Z2 = b.zs[0]; 614 615 boolean Z1IsOne = Z1.bitLength() == 1; 616 617 ECFieldElement X3, Y3, Z3, Z3Squared = null; 618 619 if (!Z1IsOne && Z1.equals(Z2)) 620 { 621 // TODO Make this available as public method coZAdd? 622 623 ECFieldElement dx = X1.subtract(X2), dy = Y1.subtract(Y2); 624 if (dx.isZero()) 625 { 626 if (dy.isZero()) 627 { 628 return twice(); 629 } 630 return curve.getInfinity(); 631 } 632 633 ECFieldElement C = dx.square(); 634 ECFieldElement W1 = X1.multiply(C), W2 = X2.multiply(C); 635 ECFieldElement A1 = W1.subtract(W2).multiply(Y1); 636 637 X3 = dy.square().subtract(W1).subtract(W2); 638 Y3 = W1.subtract(X3).multiply(dy).subtract(A1); 639 Z3 = dx; 640 641 if (Z1IsOne) 642 { 643 Z3Squared = C; 644 } 645 else 646 { 647 Z3 = Z3.multiply(Z1); 648 } 649 } 650 else 651 { 652 ECFieldElement Z1Squared, U2, S2; 653 if (Z1IsOne) 654 { 655 Z1Squared = Z1; U2 = X2; S2 = Y2; 656 } 657 else 658 { 659 Z1Squared = Z1.square(); 660 U2 = Z1Squared.multiply(X2); 661 ECFieldElement Z1Cubed = Z1Squared.multiply(Z1); 662 S2 = Z1Cubed.multiply(Y2); 663 } 664 665 boolean Z2IsOne = Z2.bitLength() == 1; 666 ECFieldElement Z2Squared, U1, S1; 667 if (Z2IsOne) 668 { 669 Z2Squared = Z2; U1 = X1; S1 = Y1; 670 } 671 else 672 { 673 Z2Squared = Z2.square(); 674 U1 = Z2Squared.multiply(X1); 675 ECFieldElement Z2Cubed = Z2Squared.multiply(Z2); 676 S1 = Z2Cubed.multiply(Y1); 677 } 678 679 ECFieldElement H = U1.subtract(U2); 680 ECFieldElement R = S1.subtract(S2); 681 682 // Check if b == this or b == -this 683 if (H.isZero()) 684 { 685 if (R.isZero()) 686 { 687 // this == b, i.e. this must be doubled 688 return this.twice(); 689 } 690 691 // this == -b, i.e. the result is the point at infinity 692 return curve.getInfinity(); 693 } 694 695 ECFieldElement HSquared = H.square(); 696 ECFieldElement G = HSquared.multiply(H); 697 ECFieldElement V = HSquared.multiply(U1); 698 699 X3 = R.square().add(G).subtract(two(V)); 700 Y3 = V.subtract(X3).multiply(R).subtract(S1.multiply(G)); 701 702 Z3 = H; 703 if (!Z1IsOne) 704 { 705 Z3 = Z3.multiply(Z1); 706 } 707 if (!Z2IsOne) 708 { 709 Z3 = Z3.multiply(Z2); 710 } 711 712 // Alternative calculation of Z3 using fast square 713 // X3 = four(X3); 714 // Y3 = eight(Y3); 715 // Z3 = doubleProductFromSquares(Z1, Z2, Z1Squared, Z2Squared).multiply(H); 716 717 if (Z3 == H) 718 { 719 Z3Squared = HSquared; 720 } 721 } 722 723 ECFieldElement[] zs; 724 if (coord == ECCurve.COORD_JACOBIAN_MODIFIED) 725 { 726 // TODO If the result will only be used in a subsequent addition, we don't need W3 727 ECFieldElement W3 = calculateJacobianModifiedW(Z3, Z3Squared); 728 729 zs = new ECFieldElement[]{ Z3, W3 }; 730 } 731 else 732 { 733 zs = new ECFieldElement[]{ Z3 }; 734 } 735 736 return new ECPoint.Fp(curve, X3, Y3, zs, this.withCompression); 737 } 738 default: 739 { 740 throw new IllegalStateException("unsupported coordinate system"); 741 } 742 } 743 } 744 745 // B.3 pg 62 746 public ECPoint twice() 747 { 748 if (this.isInfinity()) 749 { 750 return this; 751 } 752 753 ECCurve curve = this.getCurve(); 754 755 ECFieldElement Y1 = this.y; 756 if (Y1.isZero()) 757 { 758 return curve.getInfinity(); 759 } 760 761 int coord = curve.getCoordinateSystem(); 762 763 ECFieldElement X1 = this.x; 764 765 switch (coord) 766 { 767 case ECCurve.COORD_AFFINE: 768 { 769 ECFieldElement X1Squared = X1.square(); 770 ECFieldElement gamma = three(X1Squared).add(this.getCurve().getA()).divide(two(Y1)); 771 ECFieldElement X3 = gamma.square().subtract(two(X1)); 772 ECFieldElement Y3 = gamma.multiply(X1.subtract(X3)).subtract(Y1); 773 774 return new ECPoint.Fp(curve, X3, Y3, this.withCompression); 775 } 776 777 case ECCurve.COORD_HOMOGENEOUS: 778 { 779 ECFieldElement Z1 = this.zs[0]; 780 781 boolean Z1IsOne = Z1.bitLength() == 1; 782 ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square(); 783 784 // TODO Optimize for small negative a4 and -3 785 ECFieldElement w = curve.getA(); 786 if (!Z1IsOne) 787 { 788 w = w.multiply(Z1Squared); 789 } 790 w = w.add(three(X1.square())); 791 792 ECFieldElement s = Z1IsOne ? Y1 : Y1.multiply(Z1); 793 ECFieldElement t = Z1IsOne ? Y1.square() : s.multiply(Y1); 794 ECFieldElement B = X1.multiply(t); 795 ECFieldElement _4B = four(B); 796 ECFieldElement h = w.square().subtract(two(_4B)); 797 798 ECFieldElement X3 = two(h.multiply(s)); 799 ECFieldElement Y3 = w.multiply(_4B.subtract(h)).subtract(two(two(t).square())); 800 ECFieldElement _4sSquared = Z1IsOne ? four(t) : two(s).square(); 801 ECFieldElement Z3 = two(_4sSquared).multiply(s); 802 803 return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); 804 } 805 806 case ECCurve.COORD_JACOBIAN: 807 { 808 ECFieldElement Z1 = this.zs[0]; 809 810 boolean Z1IsOne = Z1.bitLength() == 1; 811 ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square(); 812 813 ECFieldElement Y1Squared = Y1.square(); 814 ECFieldElement T = Y1Squared.square(); 815 816 ECFieldElement a4 = curve.getA(); 817 ECFieldElement a4Neg = a4.negate(); 818 819 ECFieldElement M, S; 820 if (a4Neg.toBigInteger().equals(BigInteger.valueOf(3))) 821 { 822 M = three(X1.add(Z1Squared).multiply(X1.subtract(Z1Squared))); 823 S = four(Y1Squared.multiply(X1)); 824 } 825 else 826 { 827 ECFieldElement X1Squared = X1.square(); 828 M = three(X1Squared); 829 if (Z1IsOne) 830 { 831 M = M.add(a4); 832 } 833 else 834 { 835 ECFieldElement Z1Pow4 = Z1Squared.square(); 836 if (a4Neg.bitLength() < a4.bitLength()) 837 { 838 M = M.subtract(Z1Pow4.multiply(a4Neg)); 839 } 840 else 841 { 842 M = M.add(Z1Pow4.multiply(a4)); 843 } 844 } 845 S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T)); 846 } 847 848 ECFieldElement X3 = M.square().subtract(two(S)); 849 ECFieldElement Y3 = S.subtract(X3).multiply(M).subtract(eight(T)); 850 851 ECFieldElement Z3 = two(Y1); 852 if (!Z1IsOne) 853 { 854 Z3 = Z3.multiply(Z1); 855 } 856 857 // Alternative calculation of Z3 using fast square 858// ECFieldElement Z3 = doubleProductFromSquares(Y1, Z1, Y1Squared, Z1Squared); 859 860 return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); 861 } 862 863 case ECCurve.COORD_JACOBIAN_MODIFIED: 864 { 865 return twiceJacobianModified(true); 866 } 867 868 default: 869 { 870 throw new IllegalStateException("unsupported coordinate system"); 871 } 872 } 873 } 874 875 public ECPoint twicePlus(ECPoint b) 876 { 877 if (this == b) 878 { 879 return threeTimes(); 880 } 881 if (this.isInfinity()) 882 { 883 return b; 884 } 885 if (b.isInfinity()) 886 { 887 return twice(); 888 } 889 890 ECFieldElement Y1 = this.y; 891 if (Y1.isZero()) 892 { 893 return b; 894 } 895 896 ECCurve curve = this.getCurve(); 897 int coord = curve.getCoordinateSystem(); 898 899 switch (coord) 900 { 901 case ECCurve.COORD_AFFINE: 902 { 903 ECFieldElement X1 = this.x; 904 ECFieldElement X2 = b.x, Y2 = b.y; 905 906 ECFieldElement dx = X2.subtract(X1), dy = Y2.subtract(Y1); 907 908 if (dx.isZero()) 909 { 910 if (dy.isZero()) 911 { 912 // this == b i.e. the result is 3P 913 return threeTimes(); 914 } 915 916 // this == -b, i.e. the result is P 917 return this; 918 } 919 920 /* 921 * Optimized calculation of 2P + Q, as described in "Trading Inversions for 922 * Multiplications in Elliptic Curve Cryptography", by Ciet, Joye, Lauter, Montgomery. 923 */ 924 925 ECFieldElement X = dx.square(), Y = dy.square(); 926 ECFieldElement d = X.multiply(two(X1).add(X2)).subtract(Y); 927 if (d.isZero()) 928 { 929 return curve.getInfinity(); 930 } 931 932 ECFieldElement D = d.multiply(dx); 933 ECFieldElement I = D.invert(); 934 ECFieldElement L1 = d.multiply(I).multiply(dy); 935 ECFieldElement L2 = two(Y1).multiply(X).multiply(dx).multiply(I).subtract(L1); 936 ECFieldElement X4 = (L2.subtract(L1)).multiply(L1.add(L2)).add(X2); 937 ECFieldElement Y4 = (X1.subtract(X4)).multiply(L2).subtract(Y1); 938 939 return new ECPoint.Fp(curve, X4, Y4, this.withCompression); 940 } 941 case ECCurve.COORD_JACOBIAN_MODIFIED: 942 { 943 return twiceJacobianModified(false).add(b); 944 } 945 default: 946 { 947 return twice().add(b); 948 } 949 } 950 } 951 952 public ECPoint threeTimes() 953 { 954 if (this.isInfinity() || this.y.isZero()) 955 { 956 return this; 957 } 958 959 ECCurve curve = this.getCurve(); 960 int coord = curve.getCoordinateSystem(); 961 962 switch (coord) 963 { 964 case ECCurve.COORD_AFFINE: 965 { 966 ECFieldElement X1 = this.x, Y1 = this.y; 967 968 ECFieldElement _2Y1 = two(Y1); 969 ECFieldElement X = _2Y1.square(); 970 ECFieldElement Z = three(X1.square()).add(this.getCurve().getA()); 971 ECFieldElement Y = Z.square(); 972 973 ECFieldElement d = three(X1).multiply(X).subtract(Y); 974 if (d.isZero()) 975 { 976 return this.getCurve().getInfinity(); 977 } 978 979 ECFieldElement D = d.multiply(_2Y1); 980 ECFieldElement I = D.invert(); 981 ECFieldElement L1 = d.multiply(I).multiply(Z); 982 ECFieldElement L2 = X.square().multiply(I).subtract(L1); 983 984 ECFieldElement X4 = (L2.subtract(L1)).multiply(L1.add(L2)).add(X1); 985 ECFieldElement Y4 = (X1.subtract(X4)).multiply(L2).subtract(Y1); 986 return new ECPoint.Fp(curve, X4, Y4, this.withCompression); 987 } 988 case ECCurve.COORD_JACOBIAN_MODIFIED: 989 { 990 return twiceJacobianModified(false).add(this); 991 } 992 default: 993 { 994 // NOTE: Be careful about recursions between twicePlus and threeTimes 995 return twice().add(this); 996 } 997 } 998 } 999 1000 protected ECFieldElement two(ECFieldElement x) 1001 { 1002 return x.add(x); 1003 } 1004 1005 protected ECFieldElement three(ECFieldElement x) 1006 { 1007 return two(x).add(x); 1008 } 1009 1010 protected ECFieldElement four(ECFieldElement x) 1011 { 1012 return two(two(x)); 1013 } 1014 1015 protected ECFieldElement eight(ECFieldElement x) 1016 { 1017 return four(two(x)); 1018 } 1019 1020 protected ECFieldElement doubleProductFromSquares(ECFieldElement a, ECFieldElement b, 1021 ECFieldElement aSquared, ECFieldElement bSquared) 1022 { 1023 /* 1024 * NOTE: If squaring in the field is faster than multiplication, then this is a quicker 1025 * way to calculate 2.A.B, if A^2 and B^2 are already known. 1026 */ 1027 return a.add(b).square().subtract(aSquared).subtract(bSquared); 1028 } 1029 1030 // D.3.2 pg 102 (see Note:) 1031 public ECPoint subtract(ECPoint b) 1032 { 1033 if (b.isInfinity()) 1034 { 1035 return this; 1036 } 1037 1038 // Add -b 1039 return add(b.negate()); 1040 } 1041 1042 public ECPoint negate() 1043 { 1044 if (this.isInfinity()) 1045 { 1046 return this; 1047 } 1048 1049 ECCurve curve = this.getCurve(); 1050 int coord = curve.getCoordinateSystem(); 1051 1052 if (ECCurve.COORD_AFFINE != coord) 1053 { 1054 return new ECPoint.Fp(curve, this.x, this.y.negate(), this.zs, this.withCompression); 1055 } 1056 1057 return new ECPoint.Fp(curve, this.x, this.y.negate(), this.withCompression); 1058 } 1059 1060 protected ECFieldElement calculateJacobianModifiedW(ECFieldElement Z, ECFieldElement ZSquared) 1061 { 1062 if (ZSquared == null) 1063 { 1064 ZSquared = Z.square(); 1065 } 1066 1067 ECFieldElement W = ZSquared.square(); 1068 ECFieldElement a4 = this.getCurve().getA(); 1069 ECFieldElement a4Neg = a4.negate(); 1070 if (a4Neg.bitLength() < a4.bitLength()) 1071 { 1072 W = W.multiply(a4Neg).negate(); 1073 } 1074 else 1075 { 1076 W = W.multiply(a4); 1077 } 1078 return W; 1079 } 1080 1081 protected ECFieldElement getJacobianModifiedW() 1082 { 1083 ECFieldElement W = this.zs[1]; 1084 if (W == null) 1085 { 1086 // NOTE: Rarely, twicePlus will result in the need for a lazy W1 calculation here 1087 this.zs[1] = W = calculateJacobianModifiedW(this.zs[0], null); 1088 } 1089 return W; 1090 } 1091 1092 protected ECPoint.Fp twiceJacobianModified(boolean calculateW) 1093 { 1094 ECFieldElement X1 = this.x, Y1 = this.y, Z1 = this.zs[0], W1 = getJacobianModifiedW(); 1095 1096 ECFieldElement X1Squared = X1.square(); 1097 ECFieldElement M = three(X1Squared).add(W1); 1098 ECFieldElement Y1Squared = Y1.square(); 1099 ECFieldElement T = Y1Squared.square(); 1100 ECFieldElement S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T)); 1101 ECFieldElement X3 = M.square().subtract(two(S)); 1102 ECFieldElement _8T = eight(T); 1103 ECFieldElement Y3 = M.multiply(S.subtract(X3)).subtract(_8T); 1104 ECFieldElement W3 = calculateW ? two(_8T.multiply(W1)) : null; 1105 ECFieldElement Z3 = two(Z1.bitLength() == 1 ? Y1 : Y1.multiply(Z1)); 1106 1107 return new ECPoint.Fp(this.getCurve(), X3, Y3, new ECFieldElement[]{ Z3, W3 }, this.withCompression); 1108 } 1109 } 1110 1111 /** 1112 * Elliptic curve points over F2m 1113 */ 1114 public static class F2m extends ECPoint 1115 { 1116 /** 1117 * @param curve base curve 1118 * @param x x point 1119 * @param y y point 1120 * 1121 * @deprecated Use ECCurve.createPoint to construct points 1122 */ 1123 public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y) 1124 { 1125 this(curve, x, y, false); 1126 } 1127 1128 /** 1129 * @param curve base curve 1130 * @param x x point 1131 * @param y y point 1132 * @param withCompression true if encode with point compression. 1133 * 1134 * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} 1135 */ 1136 public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) 1137 { 1138 super(curve, x, y); 1139 1140 if ((x != null && y == null) || (x == null && y != null)) 1141 { 1142 throw new IllegalArgumentException("Exactly one of the field elements is null"); 1143 } 1144 1145 if (x != null) 1146 { 1147 // Check if x and y are elements of the same field 1148 ECFieldElement.F2m.checkFieldElements(this.x, this.y); 1149 1150 // Check if x and a are elements of the same field 1151 if (curve != null) 1152 { 1153 ECFieldElement.F2m.checkFieldElements(this.x, this.curve.getA()); 1154 } 1155 } 1156 1157 this.withCompression = withCompression; 1158 1159// checkCurveEquation(); 1160 } 1161 1162 F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) 1163 { 1164 super(curve, x, y, zs); 1165 1166 this.withCompression = withCompression; 1167 1168// checkCurveEquation(); 1169 } 1170 1171 public ECFieldElement getYCoord() 1172 { 1173 int coord = this.getCurveCoordinateSystem(); 1174 1175 switch (coord) 1176 { 1177 case ECCurve.COORD_LAMBDA_AFFINE: 1178 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1179 { 1180 // TODO The X == 0 stuff needs further thought 1181 if (this.isInfinity() || x.isZero()) 1182 { 1183 return y; 1184 } 1185 1186 // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly 1187 ECFieldElement X = x, L = y; 1188 ECFieldElement Y = L.subtract(X).multiply(X); 1189 if (ECCurve.COORD_LAMBDA_PROJECTIVE == coord) 1190 { 1191 ECFieldElement Z = zs[0]; 1192 if (Z.bitLength() != 1) 1193 { 1194 Y = Y.divide(Z); 1195 } 1196 } 1197 return Y; 1198 } 1199 default: 1200 { 1201 return y; 1202 } 1203 } 1204 } 1205 1206 protected boolean getCompressionYTilde() 1207 { 1208 ECFieldElement X = this.getRawXCoord(); 1209 if (X.isZero()) 1210 { 1211 return false; 1212 } 1213 1214 ECFieldElement Y = this.getRawYCoord(); 1215 1216 switch (this.getCurveCoordinateSystem()) 1217 { 1218 case ECCurve.COORD_LAMBDA_AFFINE: 1219 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1220 { 1221 // Y is actually Lambda (X + Y/X) here 1222 return Y.subtract(X).testBitZero(); 1223 } 1224 default: 1225 { 1226 return Y.divide(X).testBitZero(); 1227 } 1228 } 1229 } 1230 1231 /** 1232 * Check, if two <code>ECPoint</code>s can be added or subtracted. 1233 * @param a The first <code>ECPoint</code> to check. 1234 * @param b The second <code>ECPoint</code> to check. 1235 * @throws IllegalArgumentException if <code>a</code> and <code>b</code> 1236 * cannot be added. 1237 */ 1238 private static void checkPoints(ECPoint a, ECPoint b) 1239 { 1240 // Check, if points are on the same curve 1241 if (a.curve != b.curve) 1242 { 1243 throw new IllegalArgumentException("Only points on the same " 1244 + "curve can be added or subtracted"); 1245 } 1246 1247// ECFieldElement.F2m.checkFieldElements(a.x, b.x); 1248 } 1249 1250 /* (non-Javadoc) 1251 * @see org.bouncycastle.math.ec.ECPoint#add(org.bouncycastle.math.ec.ECPoint) 1252 */ 1253 public ECPoint add(ECPoint b) 1254 { 1255 checkPoints(this, b); 1256 return addSimple((ECPoint.F2m)b); 1257 } 1258 1259 /** 1260 * Adds another <code>ECPoints.F2m</code> to <code>this</code> without 1261 * checking if both points are on the same curve. Used by multiplication 1262 * algorithms, because there all points are a multiple of the same point 1263 * and hence the checks can be omitted. 1264 * @param b The other <code>ECPoints.F2m</code> to add to 1265 * <code>this</code>. 1266 * @return <code>this + b</code> 1267 */ 1268 public ECPoint.F2m addSimple(ECPoint.F2m b) 1269 { 1270 if (this.isInfinity()) 1271 { 1272 return b; 1273 } 1274 if (b.isInfinity()) 1275 { 1276 return this; 1277 } 1278 1279 ECCurve curve = this.getCurve(); 1280 int coord = curve.getCoordinateSystem(); 1281 1282 ECFieldElement X1 = this.x; 1283 ECFieldElement X2 = b.x; 1284 1285 switch (coord) 1286 { 1287 case ECCurve.COORD_AFFINE: 1288 { 1289 ECFieldElement Y1 = this.y; 1290 ECFieldElement Y2 = b.y; 1291 1292 if (X1.equals(X2)) 1293 { 1294 if (Y1.equals(Y2)) 1295 { 1296 return (ECPoint.F2m)twice(); 1297 } 1298 1299 return (ECPoint.F2m)curve.getInfinity(); 1300 } 1301 1302 ECFieldElement sumX = X1.add(X2); 1303 ECFieldElement L = Y1.add(Y2).divide(sumX); 1304 1305 ECFieldElement X3 = L.square().add(L).add(sumX).add(curve.getA()); 1306 ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); 1307 1308 return new ECPoint.F2m(curve, X3, Y3, this.withCompression); 1309 } 1310 case ECCurve.COORD_HOMOGENEOUS: 1311 { 1312 ECFieldElement Y1 = this.y, Z1 = this.zs[0]; 1313 ECFieldElement Y2 = b.y, Z2 = b.zs[0]; 1314 1315 boolean Z2IsOne = Z2.bitLength() == 1; 1316 1317 ECFieldElement U1 = Z1.multiply(Y2); 1318 ECFieldElement U2 = Z2IsOne ? Y1 : Y1.multiply(Z2); 1319 ECFieldElement U = U1.subtract(U2); 1320 ECFieldElement V1 = Z1.multiply(X2); 1321 ECFieldElement V2 = Z2IsOne ? X1 : X1.multiply(Z2); 1322 ECFieldElement V = V1.subtract(V2); 1323 1324 if (V1.equals(V2)) 1325 { 1326 if (U1.equals(U2)) 1327 { 1328 return (ECPoint.F2m)twice(); 1329 } 1330 1331 return (ECPoint.F2m)curve.getInfinity(); 1332 } 1333 1334 ECFieldElement VSq = V.square(); 1335 ECFieldElement W = Z2IsOne ? Z1 : Z1.multiply(Z2); 1336 ECFieldElement A = U.square().add(U.multiply(V).add(VSq.multiply(curve.getA()))).multiply(W).add(V.multiply(VSq)); 1337 1338 ECFieldElement X3 = V.multiply(A); 1339 ECFieldElement VSqZ2 = Z2IsOne ? VSq : VSq.multiply(Z2); 1340 ECFieldElement Y3 = VSqZ2.multiply(U.multiply(X1).add(Y1.multiply(V))).add(A.multiply(U.add(V))); 1341 ECFieldElement Z3 = VSq.multiply(V).multiply(W); 1342 1343 return new ECPoint.F2m(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); 1344 } 1345 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1346 { 1347 if (X1.isZero()) 1348 { 1349 return b.addSimple(this); 1350 } 1351 1352 ECFieldElement L1 = this.y, Z1 = this.zs[0]; 1353 ECFieldElement L2 = b.y, Z2 = b.zs[0]; 1354 1355 boolean Z1IsOne = Z1.bitLength() == 1; 1356 ECFieldElement U2 = X2, S2 = L2; 1357 if (!Z1IsOne) 1358 { 1359 U2 = U2.multiply(Z1); 1360 S2 = S2.multiply(Z1); 1361 } 1362 1363 boolean Z2IsOne = Z2.bitLength() == 1; 1364 ECFieldElement U1 = X1, S1 = L1; 1365 if (!Z2IsOne) 1366 { 1367 U1 = U1.multiply(Z2); 1368 S1 = S1.multiply(Z2); 1369 } 1370 1371 ECFieldElement A = S1.add(S2); 1372 ECFieldElement B = U1.add(U2); 1373 1374 if (B.isZero()) 1375 { 1376 if (A.isZero()) 1377 { 1378 return (ECPoint.F2m)twice(); 1379 } 1380 1381 return (ECPoint.F2m)curve.getInfinity(); 1382 } 1383 1384 ECFieldElement X3, L3, Z3; 1385 if (X2.isZero()) 1386 { 1387 // TODO This can probably be optimized quite a bit 1388 1389 ECFieldElement Y1 = getYCoord(), Y2 = L2; 1390 ECFieldElement L = Y1.add(Y2).divide(X1); 1391 1392 X3 = L.square().add(L).add(X1).add(curve.getA()); 1393 ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); 1394 L3 = X3.isZero() ? Y3 : Y3.divide(X3).add(X3); 1395 Z3 = curve.fromBigInteger(ECConstants.ONE); 1396 } 1397 else 1398 { 1399 B = B.square(); 1400 1401 ECFieldElement AU1 = A.multiply(U1); 1402 ECFieldElement AU2 = A.multiply(U2); 1403 ECFieldElement ABZ2 = A.multiply(B); 1404 if (!Z2IsOne) 1405 { 1406 ABZ2 = ABZ2.multiply(Z2); 1407 } 1408 1409 X3 = AU1.multiply(AU2); 1410 L3 = AU2.add(B).square().add(ABZ2.multiply(L1.add(Z1))); 1411 1412 Z3 = ABZ2; 1413 if (!Z1IsOne) 1414 { 1415 Z3 = Z3.multiply(Z1); 1416 } 1417 } 1418 1419 return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); 1420 } 1421 default: 1422 { 1423 throw new IllegalStateException("unsupported coordinate system"); 1424 } 1425 } 1426 } 1427 1428 /* (non-Javadoc) 1429 * @see org.bouncycastle.math.ec.ECPoint#subtract(org.bouncycastle.math.ec.ECPoint) 1430 */ 1431 public ECPoint subtract(ECPoint b) 1432 { 1433 checkPoints(this, b); 1434 return subtractSimple((ECPoint.F2m)b); 1435 } 1436 1437 /** 1438 * Subtracts another <code>ECPoints.F2m</code> from <code>this</code> 1439 * without checking if both points are on the same curve. Used by 1440 * multiplication algorithms, because there all points are a multiple 1441 * of the same point and hence the checks can be omitted. 1442 * @param b The other <code>ECPoints.F2m</code> to subtract from 1443 * <code>this</code>. 1444 * @return <code>this - b</code> 1445 */ 1446 public ECPoint.F2m subtractSimple(ECPoint.F2m b) 1447 { 1448 if (b.isInfinity()) 1449 { 1450 return this; 1451 } 1452 1453 // Add -b 1454 return addSimple((ECPoint.F2m)b.negate()); 1455 } 1456 1457 public ECPoint.F2m tau() 1458 { 1459 if (this.isInfinity()) 1460 { 1461 return this; 1462 } 1463 1464 ECCurve curve = this.getCurve(); 1465 int coord = curve.getCoordinateSystem(); 1466 1467 ECFieldElement X1 = this.x; 1468 1469 switch (coord) 1470 { 1471 case ECCurve.COORD_AFFINE: 1472 case ECCurve.COORD_LAMBDA_AFFINE: 1473 { 1474 ECFieldElement Y1 = this.y; 1475 return new ECPoint.F2m(curve, X1.square(), Y1.square(), this.withCompression); 1476 } 1477 case ECCurve.COORD_HOMOGENEOUS: 1478 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1479 { 1480 ECFieldElement Y1 = this.y, Z1 = this.zs[0]; 1481 return new ECPoint.F2m(curve, X1.square(), Y1.square(), new ECFieldElement[]{ Z1.square() }, this.withCompression); 1482 } 1483 default: 1484 { 1485 throw new IllegalStateException("unsupported coordinate system"); 1486 } 1487 } 1488 } 1489 1490 public ECPoint twice() 1491 { 1492 if (this.isInfinity()) 1493 { 1494 return this; 1495 } 1496 1497 ECCurve curve = this.getCurve(); 1498 1499 ECFieldElement X1 = this.x; 1500 if (X1.isZero()) 1501 { 1502 // A point with X == 0 is it's own additive inverse 1503 return curve.getInfinity(); 1504 } 1505 1506 int coord = curve.getCoordinateSystem(); 1507 1508 switch (coord) 1509 { 1510 case ECCurve.COORD_AFFINE: 1511 { 1512 ECFieldElement Y1 = this.y; 1513 1514 ECFieldElement L1 = Y1.divide(X1).add(X1); 1515 1516 ECFieldElement X3 = L1.square().add(L1).add(curve.getA()); 1517 ECFieldElement Y3 = X1.square().add(X3.multiply(L1.addOne())); 1518 1519 return new ECPoint.F2m(curve, X3, Y3, this.withCompression); 1520 } 1521 case ECCurve.COORD_HOMOGENEOUS: 1522 { 1523 ECFieldElement Y1 = this.y, Z1 = this.zs[0]; 1524 1525 boolean Z1IsOne = Z1.bitLength() == 1; 1526 ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); 1527 ECFieldElement Y1Z1 = Z1IsOne ? Y1 : Y1.multiply(Z1); 1528 1529 ECFieldElement X1Sq = X1.square(); 1530 ECFieldElement S = X1Sq.add(Y1Z1); 1531 ECFieldElement V = X1Z1; 1532 ECFieldElement vSquared = V.square(); 1533 ECFieldElement h = S.square().add(S.multiply(V)).add(curve.getA().multiply(vSquared)); 1534 1535 ECFieldElement X3 = V.multiply(h); 1536 ECFieldElement Y3 = h.multiply(S.add(V)).add(X1Sq.square().multiply(V)); 1537 ECFieldElement Z3 = V.multiply(vSquared); 1538 1539 return new ECPoint.F2m(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); 1540 } 1541 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1542 { 1543 ECFieldElement L1 = this.y, Z1 = this.zs[0]; 1544 1545 boolean Z1IsOne = Z1.bitLength() == 1; 1546 ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); 1547 ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); 1548 ECFieldElement a = curve.getA(); 1549 ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq); 1550 ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq); 1551 1552 ECFieldElement X3 = T.square(); 1553 ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); 1554 1555 ECFieldElement b = curve.getB(); 1556 ECFieldElement L3; 1557 if (b.bitLength() < (curve.getFieldSize() >> 1)) 1558 { 1559 ECFieldElement t1 = L1.add(X1).square(); 1560 ECFieldElement t2 = aZ1Sq.square(); 1561 ECFieldElement t3 = curve.getB().multiply(Z1Sq.square()); 1562 L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2.add(t3)).add(X3).add(a.addOne().multiply(Z3)); 1563 } 1564 else 1565 { 1566 ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); 1567 L3 = X1Z1.square().add(X3).add(T.multiply(L1Z1)).add(Z3); 1568 } 1569 1570 return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); 1571 } 1572 default: 1573 { 1574 throw new IllegalStateException("unsupported coordinate system"); 1575 } 1576 } 1577 } 1578 1579 public ECPoint twicePlus(ECPoint b) 1580 { 1581 if (this.isInfinity()) 1582 { 1583 return b; 1584 } 1585 if (b.isInfinity()) 1586 { 1587 return twice(); 1588 } 1589 1590 ECCurve curve = this.getCurve(); 1591 1592 ECFieldElement X1 = this.x; 1593 if (X1.isZero()) 1594 { 1595 // A point with X == 0 is it's own additive inverse 1596 return b; 1597 } 1598 1599 int coord = curve.getCoordinateSystem(); 1600 1601 switch (coord) 1602 { 1603 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1604 { 1605 // NOTE: twicePlus() only optimized for lambda-affine argument 1606 ECFieldElement X2 = b.x, Z2 = b.zs[0]; 1607 if (X2.isZero() || Z2.bitLength() != 1) 1608 { 1609 return twice().add(b); 1610 } 1611 1612 ECFieldElement L1 = this.y, Z1 = this.zs[0]; 1613 ECFieldElement L2 = b.y; 1614 1615 ECFieldElement X1Sq = X1.square(); 1616 ECFieldElement L1Sq = L1.square(); 1617 ECFieldElement Z1Sq = Z1.square(); 1618 ECFieldElement L1Z1 = L1.multiply(Z1); 1619 1620 ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); 1621 ECFieldElement L2plus1 = L2.addOne(); 1622 ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiply(T).add(X1Sq.multiply(Z1Sq)); 1623 ECFieldElement X2Z1Sq = X2.multiply(Z1Sq); 1624 ECFieldElement B = X2Z1Sq.add(T).square(); 1625 1626 ECFieldElement X3 = A.square().multiply(X2Z1Sq); 1627 ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq); 1628 ECFieldElement L3 = A.add(B).square().multiply(T).add(L2plus1.multiply(Z3)); 1629 1630 return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); 1631 } 1632 default: 1633 { 1634 return twice().add(b); 1635 } 1636 } 1637 } 1638 1639 protected void checkCurveEquation() 1640 { 1641 if (this.isInfinity()) 1642 { 1643 return; 1644 } 1645 1646 ECFieldElement Z; 1647 switch (this.getCurveCoordinateSystem()) 1648 { 1649 case ECCurve.COORD_LAMBDA_AFFINE: 1650 Z = curve.fromBigInteger(ECConstants.ONE); 1651 break; 1652 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1653 Z = this.zs[0]; 1654 break; 1655 default: 1656 return; 1657 } 1658 1659 if (Z.isZero()) 1660 { 1661 throw new IllegalStateException(); 1662 } 1663 1664 ECFieldElement X = this.x; 1665 if (X.isZero()) 1666 { 1667 // NOTE: For x == 0, we expect the affine-y instead of the lambda-y 1668 ECFieldElement Y = this.y; 1669 if (!Y.square().equals(curve.getB().multiply(Z))) 1670 { 1671 throw new IllegalStateException(); 1672 } 1673 1674 return; 1675 } 1676 1677 ECFieldElement L = this.y; 1678 ECFieldElement XSq = X.square(); 1679 ECFieldElement ZSq = Z.square(); 1680 1681 ECFieldElement lhs = L.square().add(L.multiply(Z)).add(this.getCurve().getA().multiply(ZSq)).multiply(XSq); 1682 ECFieldElement rhs = ZSq.square().multiply(this.getCurve().getB()).add(XSq.square()); 1683 1684 if (!lhs.equals(rhs)) 1685 { 1686 throw new IllegalStateException("F2m Lambda-Projective invariant broken"); 1687 } 1688 } 1689 1690 public ECPoint negate() 1691 { 1692 if (this.isInfinity()) 1693 { 1694 return this; 1695 } 1696 1697 ECFieldElement X = this.x; 1698 if (X.isZero()) 1699 { 1700 return this; 1701 } 1702 1703 switch (this.getCurveCoordinateSystem()) 1704 { 1705 case ECCurve.COORD_AFFINE: 1706 { 1707 ECFieldElement Y = this.y; 1708 return new ECPoint.F2m(curve, X, Y.add(X), this.withCompression); 1709 } 1710 case ECCurve.COORD_HOMOGENEOUS: 1711 { 1712 ECFieldElement Y = this.y, Z = this.zs[0]; 1713 return new ECPoint.F2m(curve, X, Y.add(X), new ECFieldElement[]{ Z }, this.withCompression); 1714 } 1715 case ECCurve.COORD_LAMBDA_AFFINE: 1716 { 1717 ECFieldElement L = this.y; 1718 return new ECPoint.F2m(curve, X, L.addOne(), this.withCompression); 1719 } 1720 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1721 { 1722 // L is actually Lambda (X + Y/X) here 1723 ECFieldElement L = this.y, Z = this.zs[0]; 1724 return new ECPoint.F2m(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); 1725 } 1726 default: 1727 { 1728 throw new IllegalStateException("unsupported coordinate system"); 1729 } 1730 } 1731 } 1732 } 1733} 1734