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