• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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