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