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