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 EMPTY_ZS; 185 } 186 ECFieldElement[] copy = new ECFieldElement[zsLen]; 187 System.arraycopy(zs, 0, copy, 0, zsLen); 188 return copy; 189 } 190 getRawXCoord()191 public final ECFieldElement getRawXCoord() 192 { 193 return x; 194 } 195 getRawYCoord()196 public 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_AFFINE: 1179 break; 1180 case ECCurve.COORD_HOMOGENEOUS: 1181 ECFieldElement Z1Sq = Z1.square(); 1182 X1 = X1.multiply(Z1); 1183 Y1 = Y1.multiply(Z1Sq); 1184 W1 = calculateJacobianModifiedW(Z1, Z1Sq); 1185 break; 1186 case ECCurve.COORD_JACOBIAN: 1187 W1 = calculateJacobianModifiedW(Z1, null); 1188 break; 1189 case ECCurve.COORD_JACOBIAN_MODIFIED: 1190 W1 = getJacobianModifiedW(); 1191 break; 1192 default: 1193 throw new IllegalStateException("unsupported coordinate system"); 1194 } 1195 } 1196 1197 for (int i = 0; i < e; ++i) 1198 { 1199 if (Y1.isZero()) 1200 { 1201 return curve.getInfinity(); 1202 } 1203 1204 ECFieldElement X1Squared = X1.square(); 1205 ECFieldElement M = three(X1Squared); 1206 ECFieldElement _2Y1 = two(Y1); 1207 ECFieldElement _2Y1Squared = _2Y1.multiply(Y1); 1208 ECFieldElement S = two(X1.multiply(_2Y1Squared)); 1209 ECFieldElement _4T = _2Y1Squared.square(); 1210 ECFieldElement _8T = two(_4T); 1211 1212 if (!W1.isZero()) 1213 { 1214 M = M.add(W1); 1215 W1 = two(_8T.multiply(W1)); 1216 } 1217 1218 X1 = M.square().subtract(two(S)); 1219 Y1 = M.multiply(S.subtract(X1)).subtract(_8T); 1220 Z1 = Z1.isOne() ? _2Y1 : _2Y1.multiply(Z1); 1221 } 1222 1223 switch (coord) 1224 { 1225 case ECCurve.COORD_AFFINE: 1226 ECFieldElement zInv = Z1.invert(), zInv2 = zInv.square(), zInv3 = zInv2.multiply(zInv); 1227 return new Fp(curve, X1.multiply(zInv2), Y1.multiply(zInv3), this.withCompression); 1228 case ECCurve.COORD_HOMOGENEOUS: 1229 X1 = X1.multiply(Z1); 1230 Z1 = Z1.multiply(Z1.square()); 1231 return new Fp(curve, X1, Y1, new ECFieldElement[]{ Z1 }, this.withCompression); 1232 case ECCurve.COORD_JACOBIAN: 1233 return new Fp(curve, X1, Y1, new ECFieldElement[]{ Z1 }, this.withCompression); 1234 case ECCurve.COORD_JACOBIAN_MODIFIED: 1235 return new Fp(curve, X1, Y1, new ECFieldElement[]{ Z1, W1 }, this.withCompression); 1236 default: 1237 throw new IllegalStateException("unsupported coordinate system"); 1238 } 1239 } 1240 1241 protected ECFieldElement two(ECFieldElement x) 1242 { 1243 return x.add(x); 1244 } 1245 1246 protected ECFieldElement three(ECFieldElement x) 1247 { 1248 return two(x).add(x); 1249 } 1250 1251 protected ECFieldElement four(ECFieldElement x) 1252 { 1253 return two(two(x)); 1254 } 1255 1256 protected ECFieldElement eight(ECFieldElement x) 1257 { 1258 return four(two(x)); 1259 } 1260 1261 protected ECFieldElement doubleProductFromSquares(ECFieldElement a, ECFieldElement b, 1262 ECFieldElement aSquared, ECFieldElement bSquared) 1263 { 1264 /* 1265 * NOTE: If squaring in the field is faster than multiplication, then this is a quicker 1266 * way to calculate 2.A.B, if A^2 and B^2 are already known. 1267 */ 1268 return a.add(b).square().subtract(aSquared).subtract(bSquared); 1269 } 1270 1271 public ECPoint negate() 1272 { 1273 if (this.isInfinity()) 1274 { 1275 return this; 1276 } 1277 1278 ECCurve curve = this.getCurve(); 1279 int coord = curve.getCoordinateSystem(); 1280 1281 if (ECCurve.COORD_AFFINE != coord) 1282 { 1283 return new ECPoint.Fp(curve, this.x, this.y.negate(), this.zs, this.withCompression); 1284 } 1285 1286 return new ECPoint.Fp(curve, this.x, this.y.negate(), this.withCompression); 1287 } 1288 1289 protected ECFieldElement calculateJacobianModifiedW(ECFieldElement Z, ECFieldElement ZSquared) 1290 { 1291 ECFieldElement a4 = this.getCurve().getA(); 1292 if (a4.isZero() || Z.isOne()) 1293 { 1294 return a4; 1295 } 1296 1297 if (ZSquared == null) 1298 { 1299 ZSquared = Z.square(); 1300 } 1301 1302 ECFieldElement W = ZSquared.square(); 1303 ECFieldElement a4Neg = a4.negate(); 1304 if (a4Neg.bitLength() < a4.bitLength()) 1305 { 1306 W = W.multiply(a4Neg).negate(); 1307 } 1308 else 1309 { 1310 W = W.multiply(a4); 1311 } 1312 return W; 1313 } 1314 1315 protected ECFieldElement getJacobianModifiedW() 1316 { 1317 ECFieldElement W = this.zs[1]; 1318 if (W == null) 1319 { 1320 // NOTE: Rarely, twicePlus will result in the need for a lazy W1 calculation here 1321 this.zs[1] = W = calculateJacobianModifiedW(this.zs[0], null); 1322 } 1323 return W; 1324 } 1325 1326 protected ECPoint.Fp twiceJacobianModified(boolean calculateW) 1327 { 1328 ECFieldElement X1 = this.x, Y1 = this.y, Z1 = this.zs[0], W1 = getJacobianModifiedW(); 1329 1330 ECFieldElement X1Squared = X1.square(); 1331 ECFieldElement M = three(X1Squared).add(W1); 1332 ECFieldElement _2Y1 = two(Y1); 1333 ECFieldElement _2Y1Squared = _2Y1.multiply(Y1); 1334 ECFieldElement S = two(X1.multiply(_2Y1Squared)); 1335 ECFieldElement X3 = M.square().subtract(two(S)); 1336 ECFieldElement _4T = _2Y1Squared.square(); 1337 ECFieldElement _8T = two(_4T); 1338 ECFieldElement Y3 = M.multiply(S.subtract(X3)).subtract(_8T); 1339 ECFieldElement W3 = calculateW ? two(_8T.multiply(W1)) : null; 1340 ECFieldElement Z3 = Z1.isOne() ? _2Y1 : _2Y1.multiply(Z1); 1341 1342 return new ECPoint.Fp(this.getCurve(), X3, Y3, new ECFieldElement[]{ Z3, W3 }, this.withCompression); 1343 } 1344 } 1345 1346 public static abstract class AbstractF2m extends ECPoint 1347 { 1348 protected AbstractF2m(ECCurve curve, ECFieldElement x, ECFieldElement y) 1349 { 1350 super(curve, x, y); 1351 } 1352 1353 protected AbstractF2m(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs) 1354 { 1355 super(curve, x, y, zs); 1356 } 1357 1358 protected boolean satisfiesCurveEquation() 1359 { 1360 ECCurve curve = this.getCurve(); 1361 ECFieldElement X = this.x, A = curve.getA(), B = curve.getB(); 1362 1363 int coord = curve.getCoordinateSystem(); 1364 if (coord == ECCurve.COORD_LAMBDA_PROJECTIVE) 1365 { 1366 ECFieldElement Z = this.zs[0]; 1367 boolean ZIsOne = Z.isOne(); 1368 1369 if (X.isZero()) 1370 { 1371 // NOTE: For x == 0, we expect the affine-y instead of the lambda-y 1372 ECFieldElement Y = this.y; 1373 ECFieldElement lhs = Y.square(), rhs = B; 1374 if (!ZIsOne) 1375 { 1376 rhs = rhs.multiply(Z.square()); 1377 } 1378 return lhs.equals(rhs); 1379 } 1380 1381 ECFieldElement L = this.y, X2 = X.square(); 1382 ECFieldElement lhs, rhs; 1383 if (ZIsOne) 1384 { 1385 lhs = L.square().add(L).add(A); 1386 rhs = X2.square().add(B); 1387 } 1388 else 1389 { 1390 ECFieldElement Z2 = Z.square(), Z4 = Z2.square(); 1391 lhs = L.add(Z).multiplyPlusProduct(L, A, Z2); 1392 // TODO If sqrt(b) is precomputed this can be simplified to a single square 1393 rhs = X2.squarePlusProduct(B, Z4); 1394 } 1395 lhs = lhs.multiply(X2); 1396 return lhs.equals(rhs); 1397 } 1398 1399 ECFieldElement Y = this.y; 1400 ECFieldElement lhs = Y.add(X).multiply(Y); 1401 1402 switch (coord) 1403 { 1404 case ECCurve.COORD_AFFINE: 1405 break; 1406 case ECCurve.COORD_HOMOGENEOUS: 1407 { 1408 ECFieldElement Z = this.zs[0]; 1409 if (!Z.isOne()) 1410 { 1411 ECFieldElement Z2 = Z.square(), Z3 = Z.multiply(Z2); 1412 lhs = lhs.multiply(Z); 1413 A = A.multiply(Z); 1414 B = B.multiply(Z3); 1415 } 1416 break; 1417 } 1418 default: 1419 throw new IllegalStateException("unsupported coordinate system"); 1420 } 1421 1422 ECFieldElement rhs = X.add(A).multiply(X.square()).add(B); 1423 return lhs.equals(rhs); 1424 } 1425 1426 public ECPoint scaleX(ECFieldElement scale) 1427 { 1428 if (this.isInfinity()) 1429 { 1430 return this; 1431 } 1432 1433 int coord = this.getCurveCoordinateSystem(); 1434 1435 switch (coord) 1436 { 1437 case ECCurve.COORD_LAMBDA_AFFINE: 1438 { 1439 // Y is actually Lambda (X + Y/X) here 1440 ECFieldElement X = this.getRawXCoord(), L = this.getRawYCoord(); // earlier JDK 1441 1442 ECFieldElement X2 = X.multiply(scale); 1443 ECFieldElement L2 = L.add(X).divide(scale).add(X2); 1444 1445 return this.getCurve().createRawPoint(X, L2, this.getRawZCoords(), this.withCompression); // earlier JDK 1446 } 1447 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1448 { 1449 // Y is actually Lambda (X + Y/X) here 1450 ECFieldElement X = this.getRawXCoord(), L = this.getRawYCoord(), Z = this.getRawZCoords()[0]; // earlier JDK 1451 1452 // We scale the Z coordinate also, to avoid an inversion 1453 ECFieldElement X2 = X.multiply(scale.square()); 1454 ECFieldElement L2 = L.add(X).add(X2); 1455 ECFieldElement Z2 = Z.multiply(scale); 1456 1457 return this.getCurve().createRawPoint(X2, L2, new ECFieldElement[]{ Z2 }, this.withCompression); // earlier JDK 1458 } 1459 default: 1460 { 1461 return super.scaleX(scale); 1462 } 1463 } 1464 } 1465 1466 public ECPoint scaleY(ECFieldElement scale) 1467 { 1468 if (this.isInfinity()) 1469 { 1470 return this; 1471 } 1472 1473 int coord = this.getCurveCoordinateSystem(); 1474 1475 switch (coord) 1476 { 1477 case ECCurve.COORD_LAMBDA_AFFINE: 1478 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1479 { 1480 ECFieldElement X = this.getRawXCoord(), L = this.getRawYCoord(); // earlier JDK 1481 1482 // Y is actually Lambda (X + Y/X) here 1483 ECFieldElement L2 = L.add(X).multiply(scale).add(X); 1484 1485 return this.getCurve().createRawPoint(X, L2, this.getRawZCoords(), this.withCompression); // earlier JDK 1486 } 1487 default: 1488 { 1489 return super.scaleY(scale); 1490 } 1491 } 1492 } 1493 1494 public ECPoint subtract(ECPoint b) 1495 { 1496 if (b.isInfinity()) 1497 { 1498 return this; 1499 } 1500 1501 // Add -b 1502 return this.add(b.negate()); 1503 } 1504 1505 public ECPoint.AbstractF2m tau() 1506 { 1507 if (this.isInfinity()) 1508 { 1509 return this; 1510 } 1511 1512 ECCurve curve = this.getCurve(); 1513 int coord = curve.getCoordinateSystem(); 1514 1515 ECFieldElement X1 = this.x; 1516 1517 switch (coord) 1518 { 1519 case ECCurve.COORD_AFFINE: 1520 case ECCurve.COORD_LAMBDA_AFFINE: 1521 { 1522 ECFieldElement Y1 = this.y; 1523 return (ECPoint.AbstractF2m)curve.createRawPoint(X1.square(), Y1.square(), this.withCompression); 1524 } 1525 case ECCurve.COORD_HOMOGENEOUS: 1526 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1527 { 1528 ECFieldElement Y1 = this.y, Z1 = this.zs[0]; 1529 return (ECPoint.AbstractF2m)curve.createRawPoint(X1.square(), Y1.square(), 1530 new ECFieldElement[]{ Z1.square() }, this.withCompression); 1531 } 1532 default: 1533 { 1534 throw new IllegalStateException("unsupported coordinate system"); 1535 } 1536 } 1537 } 1538 1539 public ECPoint.AbstractF2m tauPow(int pow) 1540 { 1541 if (this.isInfinity()) 1542 { 1543 return this; 1544 } 1545 1546 ECCurve curve = this.getCurve(); 1547 int coord = curve.getCoordinateSystem(); 1548 1549 ECFieldElement X1 = this.x; 1550 1551 switch (coord) 1552 { 1553 case ECCurve.COORD_AFFINE: 1554 case ECCurve.COORD_LAMBDA_AFFINE: 1555 { 1556 ECFieldElement Y1 = this.y; 1557 return (ECPoint.AbstractF2m)curve.createRawPoint(X1.squarePow(pow), Y1.squarePow(pow), this.withCompression); 1558 } 1559 case ECCurve.COORD_HOMOGENEOUS: 1560 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1561 { 1562 ECFieldElement Y1 = this.y, Z1 = this.zs[0]; 1563 return (ECPoint.AbstractF2m)curve.createRawPoint(X1.squarePow(pow), Y1.squarePow(pow), 1564 new ECFieldElement[]{ Z1.squarePow(pow) }, this.withCompression); 1565 } 1566 default: 1567 { 1568 throw new IllegalStateException("unsupported coordinate system"); 1569 } 1570 } 1571 } 1572 } 1573 1574 /** 1575 * Elliptic curve points over F2m 1576 */ 1577 public static class F2m extends AbstractF2m 1578 { 1579 /** 1580 * @param curve base curve 1581 * @param x x point 1582 * @param y y point 1583 * 1584 * @deprecated Use ECCurve.createPoint to construct points 1585 */ 1586 public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y) 1587 { 1588 this(curve, x, y, false); 1589 } 1590 1591 /** 1592 * @param curve base curve 1593 * @param x x point 1594 * @param y y point 1595 * @param withCompression true if encode with point compression. 1596 * 1597 * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} 1598 */ 1599 public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) 1600 { 1601 super(curve, x, y); 1602 1603 if ((x == null) != (y == null)) 1604 { 1605 throw new IllegalArgumentException("Exactly one of the field elements is null"); 1606 } 1607 1608 if (x != null) 1609 { 1610 // Check if x and y are elements of the same field 1611 ECFieldElement.F2m.checkFieldElements(this.x, this.y); 1612 1613 // Check if x and a are elements of the same field 1614 if (curve != null) 1615 { 1616 ECFieldElement.F2m.checkFieldElements(this.x, this.curve.getA()); 1617 } 1618 } 1619 1620 this.withCompression = withCompression; 1621 1622 // checkCurveEquation(); 1623 } 1624 1625 F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) 1626 { 1627 super(curve, x, y, zs); 1628 1629 this.withCompression = withCompression; 1630 1631 // checkCurveEquation(); 1632 } 1633 1634 protected ECPoint detach() 1635 { 1636 return new ECPoint.F2m(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK 1637 } 1638 1639 public ECFieldElement getYCoord() 1640 { 1641 int coord = this.getCurveCoordinateSystem(); 1642 1643 switch (coord) 1644 { 1645 case ECCurve.COORD_LAMBDA_AFFINE: 1646 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1647 { 1648 ECFieldElement X = x, L = y; 1649 1650 if (this.isInfinity() || X.isZero()) 1651 { 1652 return L; 1653 } 1654 1655 // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly 1656 ECFieldElement Y = L.add(X).multiply(X); 1657 if (ECCurve.COORD_LAMBDA_PROJECTIVE == coord) 1658 { 1659 ECFieldElement Z = zs[0]; 1660 if (!Z.isOne()) 1661 { 1662 Y = Y.divide(Z); 1663 } 1664 } 1665 return Y; 1666 } 1667 default: 1668 { 1669 return y; 1670 } 1671 } 1672 } 1673 1674 protected boolean getCompressionYTilde() 1675 { 1676 ECFieldElement X = this.getRawXCoord(); 1677 if (X.isZero()) 1678 { 1679 return false; 1680 } 1681 1682 ECFieldElement Y = this.getRawYCoord(); 1683 1684 switch (this.getCurveCoordinateSystem()) 1685 { 1686 case ECCurve.COORD_LAMBDA_AFFINE: 1687 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1688 { 1689 // Y is actually Lambda (X + Y/X) here 1690 return Y.testBitZero() != X.testBitZero(); 1691 } 1692 default: 1693 { 1694 return Y.divide(X).testBitZero(); 1695 } 1696 } 1697 } 1698 1699 public ECPoint add(ECPoint b) 1700 { 1701 if (this.isInfinity()) 1702 { 1703 return b; 1704 } 1705 if (b.isInfinity()) 1706 { 1707 return this; 1708 } 1709 1710 ECCurve curve = this.getCurve(); 1711 int coord = curve.getCoordinateSystem(); 1712 1713 ECFieldElement X1 = this.x; 1714 ECFieldElement X2 = b.x; 1715 1716 switch (coord) 1717 { 1718 case ECCurve.COORD_AFFINE: 1719 { 1720 ECFieldElement Y1 = this.y; 1721 ECFieldElement Y2 = b.y; 1722 1723 ECFieldElement dx = X1.add(X2), dy = Y1.add(Y2); 1724 if (dx.isZero()) 1725 { 1726 if (dy.isZero()) 1727 { 1728 return twice(); 1729 } 1730 1731 return curve.getInfinity(); 1732 } 1733 1734 ECFieldElement L = dy.divide(dx); 1735 1736 ECFieldElement X3 = L.square().add(L).add(dx).add(curve.getA()); 1737 ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); 1738 1739 return new ECPoint.F2m(curve, X3, Y3, this.withCompression); 1740 } 1741 case ECCurve.COORD_HOMOGENEOUS: 1742 { 1743 ECFieldElement Y1 = this.y, Z1 = this.zs[0]; 1744 ECFieldElement Y2 = b.y, Z2 = b.zs[0]; 1745 1746 boolean Z2IsOne = Z2.isOne(); 1747 1748 ECFieldElement U1 = Z1.multiply(Y2); 1749 ECFieldElement U2 = Z2IsOne ? Y1 : Y1.multiply(Z2); 1750 ECFieldElement U = U1.add(U2); 1751 ECFieldElement V1 = Z1.multiply(X2); 1752 ECFieldElement V2 = Z2IsOne ? X1 : X1.multiply(Z2); 1753 ECFieldElement V = V1.add(V2); 1754 1755 if (V.isZero()) 1756 { 1757 if (U.isZero()) 1758 { 1759 return twice(); 1760 } 1761 1762 return curve.getInfinity(); 1763 } 1764 1765 ECFieldElement VSq = V.square(); 1766 ECFieldElement VCu = VSq.multiply(V); 1767 ECFieldElement W = Z2IsOne ? Z1 : Z1.multiply(Z2); 1768 ECFieldElement uv = U.add(V); 1769 ECFieldElement A = uv.multiplyPlusProduct(U, VSq, curve.getA()).multiply(W).add(VCu); 1770 1771 ECFieldElement X3 = V.multiply(A); 1772 ECFieldElement VSqZ2 = Z2IsOne ? VSq : VSq.multiply(Z2); 1773 ECFieldElement Y3 = U.multiplyPlusProduct(X1, V, Y1).multiplyPlusProduct(VSqZ2, uv, A); 1774 ECFieldElement Z3 = VCu.multiply(W); 1775 1776 return new ECPoint.F2m(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); 1777 } 1778 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1779 { 1780 if (X1.isZero()) 1781 { 1782 if (X2.isZero()) 1783 { 1784 return curve.getInfinity(); 1785 } 1786 1787 return b.add(this); 1788 } 1789 1790 ECFieldElement L1 = this.y, Z1 = this.zs[0]; 1791 ECFieldElement L2 = b.y, Z2 = b.zs[0]; 1792 1793 boolean Z1IsOne = Z1.isOne(); 1794 ECFieldElement U2 = X2, S2 = L2; 1795 if (!Z1IsOne) 1796 { 1797 U2 = U2.multiply(Z1); 1798 S2 = S2.multiply(Z1); 1799 } 1800 1801 boolean Z2IsOne = Z2.isOne(); 1802 ECFieldElement U1 = X1, S1 = L1; 1803 if (!Z2IsOne) 1804 { 1805 U1 = U1.multiply(Z2); 1806 S1 = S1.multiply(Z2); 1807 } 1808 1809 ECFieldElement A = S1.add(S2); 1810 ECFieldElement B = U1.add(U2); 1811 1812 if (B.isZero()) 1813 { 1814 if (A.isZero()) 1815 { 1816 return twice(); 1817 } 1818 1819 return curve.getInfinity(); 1820 } 1821 1822 ECFieldElement X3, L3, Z3; 1823 if (X2.isZero()) 1824 { 1825 // TODO This can probably be optimized quite a bit 1826 ECPoint p = this.normalize(); 1827 X1 = p.getXCoord(); 1828 ECFieldElement Y1 = p.getYCoord(); 1829 1830 ECFieldElement Y2 = L2; 1831 ECFieldElement L = Y1.add(Y2).divide(X1); 1832 1833 X3 = L.square().add(L).add(X1).add(curve.getA()); 1834 if (X3.isZero()) 1835 { 1836 return new ECPoint.F2m(curve, X3, curve.getB().sqrt(), this.withCompression); 1837 } 1838 1839 ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); 1840 L3 = Y3.divide(X3).add(X3); 1841 Z3 = curve.fromBigInteger(ECConstants.ONE); 1842 } 1843 else 1844 { 1845 B = B.square(); 1846 1847 ECFieldElement AU1 = A.multiply(U1); 1848 ECFieldElement AU2 = A.multiply(U2); 1849 1850 X3 = AU1.multiply(AU2); 1851 if (X3.isZero()) 1852 { 1853 return new ECPoint.F2m(curve, X3, curve.getB().sqrt(), this.withCompression); 1854 } 1855 1856 ECFieldElement ABZ2 = A.multiply(B); 1857 if (!Z2IsOne) 1858 { 1859 ABZ2 = ABZ2.multiply(Z2); 1860 } 1861 1862 L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1)); 1863 1864 Z3 = ABZ2; 1865 if (!Z1IsOne) 1866 { 1867 Z3 = Z3.multiply(Z1); 1868 } 1869 } 1870 1871 return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); 1872 } 1873 default: 1874 { 1875 throw new IllegalStateException("unsupported coordinate system"); 1876 } 1877 } 1878 } 1879 1880 public ECPoint twice() 1881 { 1882 if (this.isInfinity()) 1883 { 1884 return this; 1885 } 1886 1887 ECCurve curve = this.getCurve(); 1888 1889 ECFieldElement X1 = this.x; 1890 if (X1.isZero()) 1891 { 1892 // A point with X == 0 is it's own additive inverse 1893 return curve.getInfinity(); 1894 } 1895 1896 int coord = curve.getCoordinateSystem(); 1897 1898 switch (coord) 1899 { 1900 case ECCurve.COORD_AFFINE: 1901 { 1902 ECFieldElement Y1 = this.y; 1903 1904 ECFieldElement L1 = Y1.divide(X1).add(X1); 1905 1906 ECFieldElement X3 = L1.square().add(L1).add(curve.getA()); 1907 ECFieldElement Y3 = X1.squarePlusProduct(X3, L1.addOne()); 1908 1909 return new ECPoint.F2m(curve, X3, Y3, this.withCompression); 1910 } 1911 case ECCurve.COORD_HOMOGENEOUS: 1912 { 1913 ECFieldElement Y1 = this.y, Z1 = this.zs[0]; 1914 1915 boolean Z1IsOne = Z1.isOne(); 1916 ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); 1917 ECFieldElement Y1Z1 = Z1IsOne ? Y1 : Y1.multiply(Z1); 1918 1919 ECFieldElement X1Sq = X1.square(); 1920 ECFieldElement S = X1Sq.add(Y1Z1); 1921 ECFieldElement V = X1Z1; 1922 ECFieldElement vSquared = V.square(); 1923 ECFieldElement sv = S.add(V); 1924 ECFieldElement h = sv.multiplyPlusProduct(S, vSquared, curve.getA()); 1925 1926 ECFieldElement X3 = V.multiply(h); 1927 ECFieldElement Y3 = X1Sq.square().multiplyPlusProduct(V, h, sv); 1928 ECFieldElement Z3 = V.multiply(vSquared); 1929 1930 return new ECPoint.F2m(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); 1931 } 1932 case ECCurve.COORD_LAMBDA_PROJECTIVE: 1933 { 1934 ECFieldElement L1 = this.y, Z1 = this.zs[0]; 1935 1936 boolean Z1IsOne = Z1.isOne(); 1937 ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); 1938 ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); 1939 ECFieldElement a = curve.getA(); 1940 ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq); 1941 ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq); 1942 if (T.isZero()) 1943 { 1944 return new ECPoint.F2m(curve, T, curve.getB().sqrt(), withCompression); 1945 } 1946 1947 ECFieldElement X3 = T.square(); 1948 ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); 1949 1950 ECFieldElement b = curve.getB(); 1951 ECFieldElement L3; 1952 if (b.bitLength() < (curve.getFieldSize() >> 1)) 1953 { 1954 ECFieldElement t1 = L1.add(X1).square(); 1955 ECFieldElement t2; 1956 if (b.isOne()) 1957 { 1958 t2 = aZ1Sq.add(Z1Sq).square(); 1959 } 1960 else 1961 { 1962 // TODO Can be calculated with one square if we pre-compute sqrt(b) 1963 t2 = aZ1Sq.squarePlusProduct(b, Z1Sq.square()); 1964 } 1965 L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2).add(X3); 1966 if (a.isZero()) 1967 { 1968 L3 = L3.add(Z3); 1969 } 1970 else if (!a.isOne()) 1971 { 1972 L3 = L3.add(a.addOne().multiply(Z3)); 1973 } 1974 } 1975 else 1976 { 1977 ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); 1978 L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); 1979 } 1980 1981 return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); 1982 } 1983 default: 1984 { 1985 throw new IllegalStateException("unsupported coordinate system"); 1986 } 1987 } 1988 } 1989 1990 public ECPoint twicePlus(ECPoint b) 1991 { 1992 if (this.isInfinity()) 1993 { 1994 return b; 1995 } 1996 if (b.isInfinity()) 1997 { 1998 return twice(); 1999 } 2000 2001 ECCurve curve = this.getCurve(); 2002 2003 ECFieldElement X1 = this.x; 2004 if (X1.isZero()) 2005 { 2006 // A point with X == 0 is it's own additive inverse 2007 return b; 2008 } 2009 2010 int coord = curve.getCoordinateSystem(); 2011 2012 switch (coord) 2013 { 2014 case ECCurve.COORD_LAMBDA_PROJECTIVE: 2015 { 2016 // NOTE: twicePlus() only optimized for lambda-affine argument 2017 ECFieldElement X2 = b.x, Z2 = b.zs[0]; 2018 if (X2.isZero() || !Z2.isOne()) 2019 { 2020 return twice().add(b); 2021 } 2022 2023 ECFieldElement L1 = this.y, Z1 = this.zs[0]; 2024 ECFieldElement L2 = b.y; 2025 2026 ECFieldElement X1Sq = X1.square(); 2027 ECFieldElement L1Sq = L1.square(); 2028 ECFieldElement Z1Sq = Z1.square(); 2029 ECFieldElement L1Z1 = L1.multiply(Z1); 2030 2031 ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); 2032 ECFieldElement L2plus1 = L2.addOne(); 2033 ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); 2034 ECFieldElement X2Z1Sq = X2.multiply(Z1Sq); 2035 ECFieldElement B = X2Z1Sq.add(T).square(); 2036 2037 if (B.isZero()) 2038 { 2039 if (A.isZero()) 2040 { 2041 return b.twice(); 2042 } 2043 2044 return curve.getInfinity(); 2045 } 2046 2047 if (A.isZero()) 2048 { 2049 return new ECPoint.F2m(curve, A, curve.getB().sqrt(), withCompression); 2050 } 2051 2052 ECFieldElement X3 = A.square().multiply(X2Z1Sq); 2053 ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq); 2054 ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3); 2055 2056 return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); 2057 } 2058 default: 2059 { 2060 return twice().add(b); 2061 } 2062 } 2063 } 2064 2065 public ECPoint negate() 2066 { 2067 if (this.isInfinity()) 2068 { 2069 return this; 2070 } 2071 2072 ECFieldElement X = this.x; 2073 if (X.isZero()) 2074 { 2075 return this; 2076 } 2077 2078 switch (this.getCurveCoordinateSystem()) 2079 { 2080 case ECCurve.COORD_AFFINE: 2081 { 2082 ECFieldElement Y = this.y; 2083 return new ECPoint.F2m(curve, X, Y.add(X), this.withCompression); 2084 } 2085 case ECCurve.COORD_HOMOGENEOUS: 2086 { 2087 ECFieldElement Y = this.y, Z = this.zs[0]; 2088 return new ECPoint.F2m(curve, X, Y.add(X), new ECFieldElement[]{ Z }, this.withCompression); 2089 } 2090 case ECCurve.COORD_LAMBDA_AFFINE: 2091 { 2092 ECFieldElement L = this.y; 2093 return new ECPoint.F2m(curve, X, L.addOne(), this.withCompression); 2094 } 2095 case ECCurve.COORD_LAMBDA_PROJECTIVE: 2096 { 2097 // L is actually Lambda (X + Y/X) here 2098 ECFieldElement L = this.y, Z = this.zs[0]; 2099 return new ECPoint.F2m(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); 2100 } 2101 default: 2102 { 2103 throw new IllegalStateException("unsupported coordinate system"); 2104 } 2105 } 2106 } 2107 } 2108 } 2109