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