package org.bouncycastle.math.ec; import java.math.BigInteger; /** * base class for points on elliptic curves. */ public abstract class ECPoint { protected static ECFieldElement[] EMPTY_ZS = new ECFieldElement[0]; protected static ECFieldElement[] getInitialZCoords(ECCurve curve) { // Cope with null curve, most commonly used by implicitlyCa int coord = null == curve ? ECCurve.COORD_AFFINE : curve.getCoordinateSystem(); switch (coord) { case ECCurve.COORD_AFFINE: case ECCurve.COORD_LAMBDA_AFFINE: return EMPTY_ZS; default: break; } ECFieldElement one = curve.fromBigInteger(ECConstants.ONE); switch (coord) { case ECCurve.COORD_HOMOGENEOUS: case ECCurve.COORD_JACOBIAN: case ECCurve.COORD_LAMBDA_PROJECTIVE: return new ECFieldElement[]{ one }; case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: return new ECFieldElement[]{ one, one, one }; case ECCurve.COORD_JACOBIAN_MODIFIED: return new ECFieldElement[]{ one, curve.getA() }; default: throw new IllegalArgumentException("unknown coordinate system"); } } protected ECCurve curve; protected ECFieldElement x; protected ECFieldElement y; protected ECFieldElement[] zs; protected boolean withCompression; protected PreCompInfo preCompInfo = null; protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y) { this(curve, x, y, getInitialZCoords(curve)); } protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs) { this.curve = curve; this.x = x; this.y = y; this.zs = zs; } public ECCurve getCurve() { return curve; } protected int getCurveCoordinateSystem() { // Cope with null curve, most commonly used by implicitlyCa return null == curve ? ECCurve.COORD_AFFINE : curve.getCoordinateSystem(); } /** * Normalizes this point, and then returns the affine x-coordinate. * * Note: normalization can be expensive, this method is deprecated in favour * of caller-controlled normalization. * * @deprecated Use getAffineXCoord, or normalize() and getXCoord(), instead */ public ECFieldElement getX() { return normalize().getXCoord(); } /** * Normalizes this point, and then returns the affine y-coordinate. * * Note: normalization can be expensive, this method is deprecated in favour * of caller-controlled normalization. * * @deprecated Use getAffineYCoord, or normalize() and getYCoord(), instead */ public ECFieldElement getY() { return normalize().getYCoord(); } /** * Returns the affine x-coordinate after checking that this point is normalized. * * @return The affine x-coordinate of this point * @throws IllegalStateException if the point is not normalized */ public ECFieldElement getAffineXCoord() { checkNormalized(); return getXCoord(); } /** * Returns the affine y-coordinate after checking that this point is normalized * * @return The affine y-coordinate of this point * @throws IllegalStateException if the point is not normalized */ public ECFieldElement getAffineYCoord() { checkNormalized(); return getYCoord(); } /** * Returns the x-coordinate. * * Caution: depending on the curve's coordinate system, this may not be the same value as in an * affine coordinate system; use normalize() to get a point where the coordinates have their * affine values, or use getAffineXCoord if you expect the point to already have been * normalized. * * @return the x-coordinate of this point */ public ECFieldElement getXCoord() { return x; } /** * Returns the y-coordinate. * * Caution: depending on the curve's coordinate system, this may not be the same value as in an * affine coordinate system; use normalize() to get a point where the coordinates have their * affine values, or use getAffineYCoord if you expect the point to already have been * normalized. * * @return the y-coordinate of this point */ public ECFieldElement getYCoord() { return y; } public ECFieldElement getZCoord(int index) { return (index < 0 || index >= zs.length) ? null : zs[index]; } public ECFieldElement[] getZCoords() { int zsLen = zs.length; if (zsLen == 0) { return zs; } ECFieldElement[] copy = new ECFieldElement[zsLen]; System.arraycopy(zs, 0, copy, 0, zsLen); return copy; } protected ECFieldElement getRawXCoord() { return x; } protected ECFieldElement getRawYCoord() { return y; } protected void checkNormalized() { if (!isNormalized()) { throw new IllegalStateException("point not in normal form"); } } public boolean isNormalized() { int coord = this.getCurveCoordinateSystem(); return coord == ECCurve.COORD_AFFINE || coord == ECCurve.COORD_LAMBDA_AFFINE || isInfinity() || zs[0].bitLength() == 1; } /** * Normalization ensures that any projective coordinate is 1, and therefore that the x, y * coordinates reflect those of the equivalent point in an affine coordinate system. * * @return a new ECPoint instance representing the same point, but with normalized coordinates */ public ECPoint normalize() { if (this.isInfinity()) { return this; } switch (this.getCurveCoordinateSystem()) { case ECCurve.COORD_AFFINE: case ECCurve.COORD_LAMBDA_AFFINE: { return this; } default: { ECFieldElement Z1 = getZCoord(0); if (Z1.bitLength() == 1) { return this; } return normalize(Z1.invert()); } } } ECPoint normalize(ECFieldElement zInv) { switch (this.getCurveCoordinateSystem()) { case ECCurve.COORD_HOMOGENEOUS: case ECCurve.COORD_LAMBDA_PROJECTIVE: { return createScaledPoint(zInv, zInv); } case ECCurve.COORD_JACOBIAN: case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: case ECCurve.COORD_JACOBIAN_MODIFIED: { ECFieldElement zInv2 = zInv.square(), zInv3 = zInv2.multiply(zInv); return createScaledPoint(zInv2, zInv3); } default: { throw new IllegalStateException("not a projective coordinate system"); } } } protected ECPoint createScaledPoint(ECFieldElement sx, ECFieldElement sy) { return this.getCurve().createRawPoint(getRawXCoord().multiply(sx), getRawYCoord().multiply(sy), this.withCompression); } public boolean isInfinity() { return x == null || y == null || (zs.length > 0 && zs[0].isZero()); } public boolean isCompressed() { return this.withCompression; } public boolean equals(ECPoint other) { if (null == other) { return false; } ECCurve c1 = this.getCurve(), c2 = other.getCurve(); boolean n1 = (null == c1), n2 = (null == c2); boolean i1 = isInfinity(), i2 = other.isInfinity(); if (i1 || i2) { return (i1 && i2) && (n1 || n2 || c1.equals(c2)); } ECPoint p1 = this, p2 = other; if (n1 && n2) { // Points with null curve are in affine form, so already normalized } else if (n1) { p2 = p2.normalize(); } else if (n2) { p1 = p1.normalize(); } else if (!c1.equals(c2)) { return false; } else { // TODO Consider just requiring already normalized, to avoid silent performance degradation ECPoint[] points = new ECPoint[]{ this, c1.importPoint(p2) }; // TODO This is a little strong, really only requires coZNormalizeAll to get Zs equal c1.normalizeAll(points); p1 = points[0]; p2 = points[1]; } return p1.getXCoord().equals(p2.getXCoord()) && p1.getYCoord().equals(p2.getYCoord()); } public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof ECPoint)) { return false; } return equals((ECPoint)other); } public int hashCode() { ECCurve c = this.getCurve(); int hc = (null == c) ? 0 : ~c.hashCode(); if (!this.isInfinity()) { // TODO Consider just requiring already normalized, to avoid silent performance degradation ECPoint p = normalize(); hc ^= p.getXCoord().hashCode() * 17; hc ^= p.getYCoord().hashCode() * 257; } return hc; } public String toString() { if (this.isInfinity()) { return "INF"; } StringBuffer sb = new StringBuffer(); sb.append('('); sb.append(getRawXCoord()); sb.append(','); sb.append(getRawYCoord()); for (int i = 0; i < zs.length; ++i) { sb.append(','); sb.append(zs[i]); } sb.append(')'); return sb.toString(); } public byte[] getEncoded() { return getEncoded(this.withCompression); } /** * return the field element encoded with point compression. (S 4.3.6) */ public byte[] getEncoded(boolean compressed) { if (this.isInfinity()) { return new byte[1]; } ECPoint normed = normalize(); byte[] X = normed.getXCoord().getEncoded(); if (compressed) { byte[] PO = new byte[X.length + 1]; PO[0] = (byte)(normed.getCompressionYTilde() ? 0x03 : 0x02); System.arraycopy(X, 0, PO, 1, X.length); return PO; } byte[] Y = normed.getYCoord().getEncoded(); byte[] PO = new byte[X.length + Y.length + 1]; PO[0] = 0x04; System.arraycopy(X, 0, PO, 1, X.length); System.arraycopy(Y, 0, PO, X.length + 1, Y.length); return PO; } protected abstract boolean getCompressionYTilde(); public abstract ECPoint add(ECPoint b); public abstract ECPoint negate(); public abstract ECPoint subtract(ECPoint b); public ECPoint timesPow2(int e) { if (e < 0) { throw new IllegalArgumentException("'e' cannot be negative"); } ECPoint p = this; while (--e >= 0) { p = p.twice(); } return p; } public abstract ECPoint twice(); public ECPoint twicePlus(ECPoint b) { return twice().add(b); } public ECPoint threeTimes() { return twicePlus(this); } /** * Multiplies this ECPoint by the given number. * @param k The multiplicator. * @return k * this. */ public ECPoint multiply(BigInteger k) { return this.getCurve().getMultiplier().multiply(this, k); } /** * Elliptic curve points over Fp */ public static class Fp extends ECPoint { /** * Create a point which encodes with point compression. * * @param curve the curve to use * @param x affine x co-ordinate * @param y affine y co-ordinate * * @deprecated Use ECCurve.createPoint to construct points */ public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y) { this(curve, x, y, false); } /** * Create a point that encodes with or without point compresion. * * @param curve the curve to use * @param x affine x co-ordinate * @param y affine y co-ordinate * @param withCompression if true encode with point compression * * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} */ public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) { super(curve, x, y); if ((x != null && y == null) || (x == null && y != null)) { throw new IllegalArgumentException("Exactly one of the field elements is null"); } this.withCompression = withCompression; } Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) { super(curve, x, y, zs); this.withCompression = withCompression; } protected boolean getCompressionYTilde() { return this.getAffineYCoord().testBitZero(); } public ECFieldElement getZCoord(int index) { if (index == 1 && ECCurve.COORD_JACOBIAN_MODIFIED == this.getCurveCoordinateSystem()) { return getJacobianModifiedW(); } return super.getZCoord(index); } // B.3 pg 62 public ECPoint add(ECPoint b) { if (this.isInfinity()) { return b; } if (b.isInfinity()) { return this; } if (this == b) { return twice(); } ECCurve curve = this.getCurve(); int coord = curve.getCoordinateSystem(); ECFieldElement X1 = this.x, Y1 = this.y; ECFieldElement X2 = b.x, Y2 = b.y; switch (coord) { case ECCurve.COORD_AFFINE: { ECFieldElement dx = X2.subtract(X1), dy = Y2.subtract(Y1); if (dx.isZero()) { if (dy.isZero()) { // this == b, i.e. this must be doubled return twice(); } // this == -b, i.e. the result is the point at infinity return curve.getInfinity(); } ECFieldElement gamma = dy.divide(dx); ECFieldElement X3 = gamma.square().subtract(X1).subtract(X2); ECFieldElement Y3 = gamma.multiply(X1.subtract(X3)).subtract(Y1); return new ECPoint.Fp(curve, X3, Y3, this.withCompression); } case ECCurve.COORD_HOMOGENEOUS: { ECFieldElement Z1 = this.zs[0]; ECFieldElement Z2 = b.zs[0]; boolean Z1IsOne = Z1.bitLength() == 1; boolean Z2IsOne = Z2.bitLength() == 1; ECFieldElement u1 = Z1IsOne ? Y2 : Y2.multiply(Z1); ECFieldElement u2 = Z2IsOne ? Y1 : Y1.multiply(Z2); ECFieldElement u = u1.subtract(u2); ECFieldElement v1 = Z1IsOne ? X2 : X2.multiply(Z1); ECFieldElement v2 = Z2IsOne ? X1 : X1.multiply(Z2); ECFieldElement v = v1.subtract(v2); // Check if b == this or b == -this if (v.isZero()) { if (u.isZero()) { // this == b, i.e. this must be doubled return this.twice(); } // this == -b, i.e. the result is the point at infinity return curve.getInfinity(); } // TODO Optimize for when w == 1 ECFieldElement w = Z1IsOne ? Z2 : Z2IsOne ? Z1 : Z1.multiply(Z2); ECFieldElement vSquared = v.square(); ECFieldElement vCubed = vSquared.multiply(v); ECFieldElement vSquaredV2 = vSquared.multiply(v2); ECFieldElement A = u.square().multiply(w).subtract(vCubed).subtract(two(vSquaredV2)); ECFieldElement X3 = v.multiply(A); ECFieldElement Y3 = vSquaredV2.subtract(A).multiply(u).subtract(vCubed.multiply(u2)); ECFieldElement Z3 = vCubed.multiply(w); return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); } case ECCurve.COORD_JACOBIAN: case ECCurve.COORD_JACOBIAN_MODIFIED: { ECFieldElement Z1 = this.zs[0]; ECFieldElement Z2 = b.zs[0]; boolean Z1IsOne = Z1.bitLength() == 1; ECFieldElement X3, Y3, Z3, Z3Squared = null; if (!Z1IsOne && Z1.equals(Z2)) { // TODO Make this available as public method coZAdd? ECFieldElement dx = X1.subtract(X2), dy = Y1.subtract(Y2); if (dx.isZero()) { if (dy.isZero()) { return twice(); } return curve.getInfinity(); } ECFieldElement C = dx.square(); ECFieldElement W1 = X1.multiply(C), W2 = X2.multiply(C); ECFieldElement A1 = W1.subtract(W2).multiply(Y1); X3 = dy.square().subtract(W1).subtract(W2); Y3 = W1.subtract(X3).multiply(dy).subtract(A1); Z3 = dx; if (Z1IsOne) { Z3Squared = C; } else { Z3 = Z3.multiply(Z1); } } else { ECFieldElement Z1Squared, U2, S2; if (Z1IsOne) { Z1Squared = Z1; U2 = X2; S2 = Y2; } else { Z1Squared = Z1.square(); U2 = Z1Squared.multiply(X2); ECFieldElement Z1Cubed = Z1Squared.multiply(Z1); S2 = Z1Cubed.multiply(Y2); } boolean Z2IsOne = Z2.bitLength() == 1; ECFieldElement Z2Squared, U1, S1; if (Z2IsOne) { Z2Squared = Z2; U1 = X1; S1 = Y1; } else { Z2Squared = Z2.square(); U1 = Z2Squared.multiply(X1); ECFieldElement Z2Cubed = Z2Squared.multiply(Z2); S1 = Z2Cubed.multiply(Y1); } ECFieldElement H = U1.subtract(U2); ECFieldElement R = S1.subtract(S2); // Check if b == this or b == -this if (H.isZero()) { if (R.isZero()) { // this == b, i.e. this must be doubled return this.twice(); } // this == -b, i.e. the result is the point at infinity return curve.getInfinity(); } ECFieldElement HSquared = H.square(); ECFieldElement G = HSquared.multiply(H); ECFieldElement V = HSquared.multiply(U1); X3 = R.square().add(G).subtract(two(V)); Y3 = V.subtract(X3).multiply(R).subtract(S1.multiply(G)); Z3 = H; if (!Z1IsOne) { Z3 = Z3.multiply(Z1); } if (!Z2IsOne) { Z3 = Z3.multiply(Z2); } // Alternative calculation of Z3 using fast square // X3 = four(X3); // Y3 = eight(Y3); // Z3 = doubleProductFromSquares(Z1, Z2, Z1Squared, Z2Squared).multiply(H); if (Z3 == H) { Z3Squared = HSquared; } } ECFieldElement[] zs; if (coord == ECCurve.COORD_JACOBIAN_MODIFIED) { // TODO If the result will only be used in a subsequent addition, we don't need W3 ECFieldElement W3 = calculateJacobianModifiedW(Z3, Z3Squared); zs = new ECFieldElement[]{ Z3, W3 }; } else { zs = new ECFieldElement[]{ Z3 }; } return new ECPoint.Fp(curve, X3, Y3, zs, this.withCompression); } default: { throw new IllegalStateException("unsupported coordinate system"); } } } // B.3 pg 62 public ECPoint twice() { if (this.isInfinity()) { return this; } ECCurve curve = this.getCurve(); ECFieldElement Y1 = this.y; if (Y1.isZero()) { return curve.getInfinity(); } int coord = curve.getCoordinateSystem(); ECFieldElement X1 = this.x; switch (coord) { case ECCurve.COORD_AFFINE: { ECFieldElement X1Squared = X1.square(); ECFieldElement gamma = three(X1Squared).add(this.getCurve().getA()).divide(two(Y1)); ECFieldElement X3 = gamma.square().subtract(two(X1)); ECFieldElement Y3 = gamma.multiply(X1.subtract(X3)).subtract(Y1); return new ECPoint.Fp(curve, X3, Y3, this.withCompression); } case ECCurve.COORD_HOMOGENEOUS: { ECFieldElement Z1 = this.zs[0]; boolean Z1IsOne = Z1.bitLength() == 1; ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square(); // TODO Optimize for small negative a4 and -3 ECFieldElement w = curve.getA(); if (!Z1IsOne) { w = w.multiply(Z1Squared); } w = w.add(three(X1.square())); ECFieldElement s = Z1IsOne ? Y1 : Y1.multiply(Z1); ECFieldElement t = Z1IsOne ? Y1.square() : s.multiply(Y1); ECFieldElement B = X1.multiply(t); ECFieldElement _4B = four(B); ECFieldElement h = w.square().subtract(two(_4B)); ECFieldElement X3 = two(h.multiply(s)); ECFieldElement Y3 = w.multiply(_4B.subtract(h)).subtract(two(two(t).square())); ECFieldElement _4sSquared = Z1IsOne ? four(t) : two(s).square(); ECFieldElement Z3 = two(_4sSquared).multiply(s); return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); } case ECCurve.COORD_JACOBIAN: { ECFieldElement Z1 = this.zs[0]; boolean Z1IsOne = Z1.bitLength() == 1; ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square(); ECFieldElement Y1Squared = Y1.square(); ECFieldElement T = Y1Squared.square(); ECFieldElement a4 = curve.getA(); ECFieldElement a4Neg = a4.negate(); ECFieldElement M, S; if (a4Neg.toBigInteger().equals(BigInteger.valueOf(3))) { M = three(X1.add(Z1Squared).multiply(X1.subtract(Z1Squared))); S = four(Y1Squared.multiply(X1)); } else { ECFieldElement X1Squared = X1.square(); M = three(X1Squared); if (Z1IsOne) { M = M.add(a4); } else { ECFieldElement Z1Pow4 = Z1Squared.square(); if (a4Neg.bitLength() < a4.bitLength()) { M = M.subtract(Z1Pow4.multiply(a4Neg)); } else { M = M.add(Z1Pow4.multiply(a4)); } } S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T)); } ECFieldElement X3 = M.square().subtract(two(S)); ECFieldElement Y3 = S.subtract(X3).multiply(M).subtract(eight(T)); ECFieldElement Z3 = two(Y1); if (!Z1IsOne) { Z3 = Z3.multiply(Z1); } // Alternative calculation of Z3 using fast square // ECFieldElement Z3 = doubleProductFromSquares(Y1, Z1, Y1Squared, Z1Squared); return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); } case ECCurve.COORD_JACOBIAN_MODIFIED: { return twiceJacobianModified(true); } default: { throw new IllegalStateException("unsupported coordinate system"); } } } public ECPoint twicePlus(ECPoint b) { if (this == b) { return threeTimes(); } if (this.isInfinity()) { return b; } if (b.isInfinity()) { return twice(); } ECFieldElement Y1 = this.y; if (Y1.isZero()) { return b; } ECCurve curve = this.getCurve(); int coord = curve.getCoordinateSystem(); switch (coord) { case ECCurve.COORD_AFFINE: { ECFieldElement X1 = this.x; ECFieldElement X2 = b.x, Y2 = b.y; ECFieldElement dx = X2.subtract(X1), dy = Y2.subtract(Y1); if (dx.isZero()) { if (dy.isZero()) { // this == b i.e. the result is 3P return threeTimes(); } // this == -b, i.e. the result is P return this; } /* * Optimized calculation of 2P + Q, as described in "Trading Inversions for * Multiplications in Elliptic Curve Cryptography", by Ciet, Joye, Lauter, Montgomery. */ ECFieldElement X = dx.square(), Y = dy.square(); ECFieldElement d = X.multiply(two(X1).add(X2)).subtract(Y); if (d.isZero()) { return curve.getInfinity(); } ECFieldElement D = d.multiply(dx); ECFieldElement I = D.invert(); ECFieldElement L1 = d.multiply(I).multiply(dy); ECFieldElement L2 = two(Y1).multiply(X).multiply(dx).multiply(I).subtract(L1); ECFieldElement X4 = (L2.subtract(L1)).multiply(L1.add(L2)).add(X2); ECFieldElement Y4 = (X1.subtract(X4)).multiply(L2).subtract(Y1); return new ECPoint.Fp(curve, X4, Y4, this.withCompression); } case ECCurve.COORD_JACOBIAN_MODIFIED: { return twiceJacobianModified(false).add(b); } default: { return twice().add(b); } } } public ECPoint threeTimes() { if (this.isInfinity() || this.y.isZero()) { return this; } ECCurve curve = this.getCurve(); int coord = curve.getCoordinateSystem(); switch (coord) { case ECCurve.COORD_AFFINE: { ECFieldElement X1 = this.x, Y1 = this.y; ECFieldElement _2Y1 = two(Y1); ECFieldElement X = _2Y1.square(); ECFieldElement Z = three(X1.square()).add(this.getCurve().getA()); ECFieldElement Y = Z.square(); ECFieldElement d = three(X1).multiply(X).subtract(Y); if (d.isZero()) { return this.getCurve().getInfinity(); } ECFieldElement D = d.multiply(_2Y1); ECFieldElement I = D.invert(); ECFieldElement L1 = d.multiply(I).multiply(Z); ECFieldElement L2 = X.square().multiply(I).subtract(L1); ECFieldElement X4 = (L2.subtract(L1)).multiply(L1.add(L2)).add(X1); ECFieldElement Y4 = (X1.subtract(X4)).multiply(L2).subtract(Y1); return new ECPoint.Fp(curve, X4, Y4, this.withCompression); } case ECCurve.COORD_JACOBIAN_MODIFIED: { return twiceJacobianModified(false).add(this); } default: { // NOTE: Be careful about recursions between twicePlus and threeTimes return twice().add(this); } } } protected ECFieldElement two(ECFieldElement x) { return x.add(x); } protected ECFieldElement three(ECFieldElement x) { return two(x).add(x); } protected ECFieldElement four(ECFieldElement x) { return two(two(x)); } protected ECFieldElement eight(ECFieldElement x) { return four(two(x)); } protected ECFieldElement doubleProductFromSquares(ECFieldElement a, ECFieldElement b, ECFieldElement aSquared, ECFieldElement bSquared) { /* * NOTE: If squaring in the field is faster than multiplication, then this is a quicker * way to calculate 2.A.B, if A^2 and B^2 are already known. */ return a.add(b).square().subtract(aSquared).subtract(bSquared); } // D.3.2 pg 102 (see Note:) public ECPoint subtract(ECPoint b) { if (b.isInfinity()) { return this; } // Add -b return add(b.negate()); } public ECPoint negate() { if (this.isInfinity()) { return this; } ECCurve curve = this.getCurve(); int coord = curve.getCoordinateSystem(); if (ECCurve.COORD_AFFINE != coord) { return new ECPoint.Fp(curve, this.x, this.y.negate(), this.zs, this.withCompression); } return new ECPoint.Fp(curve, this.x, this.y.negate(), this.withCompression); } protected ECFieldElement calculateJacobianModifiedW(ECFieldElement Z, ECFieldElement ZSquared) { if (ZSquared == null) { ZSquared = Z.square(); } ECFieldElement W = ZSquared.square(); ECFieldElement a4 = this.getCurve().getA(); ECFieldElement a4Neg = a4.negate(); if (a4Neg.bitLength() < a4.bitLength()) { W = W.multiply(a4Neg).negate(); } else { W = W.multiply(a4); } return W; } protected ECFieldElement getJacobianModifiedW() { ECFieldElement W = this.zs[1]; if (W == null) { // NOTE: Rarely, twicePlus will result in the need for a lazy W1 calculation here this.zs[1] = W = calculateJacobianModifiedW(this.zs[0], null); } return W; } protected ECPoint.Fp twiceJacobianModified(boolean calculateW) { ECFieldElement X1 = this.x, Y1 = this.y, Z1 = this.zs[0], W1 = getJacobianModifiedW(); ECFieldElement X1Squared = X1.square(); ECFieldElement M = three(X1Squared).add(W1); ECFieldElement Y1Squared = Y1.square(); ECFieldElement T = Y1Squared.square(); ECFieldElement S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T)); ECFieldElement X3 = M.square().subtract(two(S)); ECFieldElement _8T = eight(T); ECFieldElement Y3 = M.multiply(S.subtract(X3)).subtract(_8T); ECFieldElement W3 = calculateW ? two(_8T.multiply(W1)) : null; ECFieldElement Z3 = two(Z1.bitLength() == 1 ? Y1 : Y1.multiply(Z1)); return new ECPoint.Fp(this.getCurve(), X3, Y3, new ECFieldElement[]{ Z3, W3 }, this.withCompression); } } /** * Elliptic curve points over F2m */ public static class F2m extends ECPoint { /** * @param curve base curve * @param x x point * @param y y point * * @deprecated Use ECCurve.createPoint to construct points */ public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y) { this(curve, x, y, false); } /** * @param curve base curve * @param x x point * @param y y point * @param withCompression true if encode with point compression. * * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} */ public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) { super(curve, x, y); if ((x != null && y == null) || (x == null && y != null)) { throw new IllegalArgumentException("Exactly one of the field elements is null"); } if (x != null) { // Check if x and y are elements of the same field ECFieldElement.F2m.checkFieldElements(this.x, this.y); // Check if x and a are elements of the same field if (curve != null) { ECFieldElement.F2m.checkFieldElements(this.x, this.curve.getA()); } } this.withCompression = withCompression; // checkCurveEquation(); } F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) { super(curve, x, y, zs); this.withCompression = withCompression; // checkCurveEquation(); } public ECFieldElement getYCoord() { int coord = this.getCurveCoordinateSystem(); switch (coord) { case ECCurve.COORD_LAMBDA_AFFINE: case ECCurve.COORD_LAMBDA_PROJECTIVE: { // TODO The X == 0 stuff needs further thought if (this.isInfinity() || x.isZero()) { return y; } // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly ECFieldElement X = x, L = y; ECFieldElement Y = L.subtract(X).multiply(X); if (ECCurve.COORD_LAMBDA_PROJECTIVE == coord) { ECFieldElement Z = zs[0]; if (Z.bitLength() != 1) { Y = Y.divide(Z); } } return Y; } default: { return y; } } } protected boolean getCompressionYTilde() { ECFieldElement X = this.getRawXCoord(); if (X.isZero()) { return false; } ECFieldElement Y = this.getRawYCoord(); switch (this.getCurveCoordinateSystem()) { case ECCurve.COORD_LAMBDA_AFFINE: case ECCurve.COORD_LAMBDA_PROJECTIVE: { // Y is actually Lambda (X + Y/X) here return Y.subtract(X).testBitZero(); } default: { return Y.divide(X).testBitZero(); } } } /** * Check, if two ECPoints can be added or subtracted. * @param a The first ECPoint to check. * @param b The second ECPoint to check. * @throws IllegalArgumentException if a and b * cannot be added. */ private static void checkPoints(ECPoint a, ECPoint b) { // Check, if points are on the same curve if (a.curve != b.curve) { throw new IllegalArgumentException("Only points on the same " + "curve can be added or subtracted"); } // ECFieldElement.F2m.checkFieldElements(a.x, b.x); } /* (non-Javadoc) * @see org.bouncycastle.math.ec.ECPoint#add(org.bouncycastle.math.ec.ECPoint) */ public ECPoint add(ECPoint b) { checkPoints(this, b); return addSimple((ECPoint.F2m)b); } /** * Adds another ECPoints.F2m to this without * checking if both points are on the same curve. Used by multiplication * algorithms, because there all points are a multiple of the same point * and hence the checks can be omitted. * @param b The other ECPoints.F2m to add to * this. * @return this + b */ public ECPoint.F2m addSimple(ECPoint.F2m b) { if (this.isInfinity()) { return b; } if (b.isInfinity()) { return this; } ECCurve curve = this.getCurve(); int coord = curve.getCoordinateSystem(); ECFieldElement X1 = this.x; ECFieldElement X2 = b.x; switch (coord) { case ECCurve.COORD_AFFINE: { ECFieldElement Y1 = this.y; ECFieldElement Y2 = b.y; if (X1.equals(X2)) { if (Y1.equals(Y2)) { return (ECPoint.F2m)twice(); } return (ECPoint.F2m)curve.getInfinity(); } ECFieldElement sumX = X1.add(X2); ECFieldElement L = Y1.add(Y2).divide(sumX); ECFieldElement X3 = L.square().add(L).add(sumX).add(curve.getA()); ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); return new ECPoint.F2m(curve, X3, Y3, this.withCompression); } case ECCurve.COORD_HOMOGENEOUS: { ECFieldElement Y1 = this.y, Z1 = this.zs[0]; ECFieldElement Y2 = b.y, Z2 = b.zs[0]; boolean Z2IsOne = Z2.bitLength() == 1; ECFieldElement U1 = Z1.multiply(Y2); ECFieldElement U2 = Z2IsOne ? Y1 : Y1.multiply(Z2); ECFieldElement U = U1.subtract(U2); ECFieldElement V1 = Z1.multiply(X2); ECFieldElement V2 = Z2IsOne ? X1 : X1.multiply(Z2); ECFieldElement V = V1.subtract(V2); if (V1.equals(V2)) { if (U1.equals(U2)) { return (ECPoint.F2m)twice(); } return (ECPoint.F2m)curve.getInfinity(); } ECFieldElement VSq = V.square(); ECFieldElement W = Z2IsOne ? Z1 : Z1.multiply(Z2); ECFieldElement A = U.square().add(U.multiply(V).add(VSq.multiply(curve.getA()))).multiply(W).add(V.multiply(VSq)); ECFieldElement X3 = V.multiply(A); ECFieldElement VSqZ2 = Z2IsOne ? VSq : VSq.multiply(Z2); ECFieldElement Y3 = VSqZ2.multiply(U.multiply(X1).add(Y1.multiply(V))).add(A.multiply(U.add(V))); ECFieldElement Z3 = VSq.multiply(V).multiply(W); return new ECPoint.F2m(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); } case ECCurve.COORD_LAMBDA_PROJECTIVE: { if (X1.isZero()) { return b.addSimple(this); } ECFieldElement L1 = this.y, Z1 = this.zs[0]; ECFieldElement L2 = b.y, Z2 = b.zs[0]; boolean Z1IsOne = Z1.bitLength() == 1; ECFieldElement U2 = X2, S2 = L2; if (!Z1IsOne) { U2 = U2.multiply(Z1); S2 = S2.multiply(Z1); } boolean Z2IsOne = Z2.bitLength() == 1; ECFieldElement U1 = X1, S1 = L1; if (!Z2IsOne) { U1 = U1.multiply(Z2); S1 = S1.multiply(Z2); } ECFieldElement A = S1.add(S2); ECFieldElement B = U1.add(U2); if (B.isZero()) { if (A.isZero()) { return (ECPoint.F2m)twice(); } return (ECPoint.F2m)curve.getInfinity(); } ECFieldElement X3, L3, Z3; if (X2.isZero()) { // TODO This can probably be optimized quite a bit ECFieldElement Y1 = getYCoord(), Y2 = L2; ECFieldElement L = Y1.add(Y2).divide(X1); X3 = L.square().add(L).add(X1).add(curve.getA()); ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); L3 = X3.isZero() ? Y3 : Y3.divide(X3).add(X3); Z3 = curve.fromBigInteger(ECConstants.ONE); } else { B = B.square(); ECFieldElement AU1 = A.multiply(U1); ECFieldElement AU2 = A.multiply(U2); ECFieldElement ABZ2 = A.multiply(B); if (!Z2IsOne) { ABZ2 = ABZ2.multiply(Z2); } X3 = AU1.multiply(AU2); L3 = AU2.add(B).square().add(ABZ2.multiply(L1.add(Z1))); Z3 = ABZ2; if (!Z1IsOne) { Z3 = Z3.multiply(Z1); } } return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); } default: { throw new IllegalStateException("unsupported coordinate system"); } } } /* (non-Javadoc) * @see org.bouncycastle.math.ec.ECPoint#subtract(org.bouncycastle.math.ec.ECPoint) */ public ECPoint subtract(ECPoint b) { checkPoints(this, b); return subtractSimple((ECPoint.F2m)b); } /** * Subtracts another ECPoints.F2m from this * without checking if both points are on the same curve. Used by * multiplication algorithms, because there all points are a multiple * of the same point and hence the checks can be omitted. * @param b The other ECPoints.F2m to subtract from * this. * @return this - b */ public ECPoint.F2m subtractSimple(ECPoint.F2m b) { if (b.isInfinity()) { return this; } // Add -b return addSimple((ECPoint.F2m)b.negate()); } public ECPoint.F2m tau() { if (this.isInfinity()) { return this; } ECCurve curve = this.getCurve(); int coord = curve.getCoordinateSystem(); ECFieldElement X1 = this.x; switch (coord) { case ECCurve.COORD_AFFINE: case ECCurve.COORD_LAMBDA_AFFINE: { ECFieldElement Y1 = this.y; return new ECPoint.F2m(curve, X1.square(), Y1.square(), this.withCompression); } case ECCurve.COORD_HOMOGENEOUS: case ECCurve.COORD_LAMBDA_PROJECTIVE: { ECFieldElement Y1 = this.y, Z1 = this.zs[0]; return new ECPoint.F2m(curve, X1.square(), Y1.square(), new ECFieldElement[]{ Z1.square() }, this.withCompression); } default: { throw new IllegalStateException("unsupported coordinate system"); } } } public ECPoint twice() { if (this.isInfinity()) { return this; } ECCurve curve = this.getCurve(); ECFieldElement X1 = this.x; if (X1.isZero()) { // A point with X == 0 is it's own additive inverse return curve.getInfinity(); } int coord = curve.getCoordinateSystem(); switch (coord) { case ECCurve.COORD_AFFINE: { ECFieldElement Y1 = this.y; ECFieldElement L1 = Y1.divide(X1).add(X1); ECFieldElement X3 = L1.square().add(L1).add(curve.getA()); ECFieldElement Y3 = X1.square().add(X3.multiply(L1.addOne())); return new ECPoint.F2m(curve, X3, Y3, this.withCompression); } case ECCurve.COORD_HOMOGENEOUS: { ECFieldElement Y1 = this.y, Z1 = this.zs[0]; boolean Z1IsOne = Z1.bitLength() == 1; ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); ECFieldElement Y1Z1 = Z1IsOne ? Y1 : Y1.multiply(Z1); ECFieldElement X1Sq = X1.square(); ECFieldElement S = X1Sq.add(Y1Z1); ECFieldElement V = X1Z1; ECFieldElement vSquared = V.square(); ECFieldElement h = S.square().add(S.multiply(V)).add(curve.getA().multiply(vSquared)); ECFieldElement X3 = V.multiply(h); ECFieldElement Y3 = h.multiply(S.add(V)).add(X1Sq.square().multiply(V)); ECFieldElement Z3 = V.multiply(vSquared); return new ECPoint.F2m(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); } case ECCurve.COORD_LAMBDA_PROJECTIVE: { ECFieldElement L1 = this.y, Z1 = this.zs[0]; boolean Z1IsOne = Z1.bitLength() == 1; ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); ECFieldElement a = curve.getA(); ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq); ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq); ECFieldElement X3 = T.square(); ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); ECFieldElement b = curve.getB(); ECFieldElement L3; if (b.bitLength() < (curve.getFieldSize() >> 1)) { ECFieldElement t1 = L1.add(X1).square(); ECFieldElement t2 = aZ1Sq.square(); ECFieldElement t3 = curve.getB().multiply(Z1Sq.square()); L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2.add(t3)).add(X3).add(a.addOne().multiply(Z3)); } else { ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); L3 = X1Z1.square().add(X3).add(T.multiply(L1Z1)).add(Z3); } return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); } default: { throw new IllegalStateException("unsupported coordinate system"); } } } public ECPoint twicePlus(ECPoint b) { if (this.isInfinity()) { return b; } if (b.isInfinity()) { return twice(); } ECCurve curve = this.getCurve(); ECFieldElement X1 = this.x; if (X1.isZero()) { // A point with X == 0 is it's own additive inverse return b; } int coord = curve.getCoordinateSystem(); switch (coord) { case ECCurve.COORD_LAMBDA_PROJECTIVE: { // NOTE: twicePlus() only optimized for lambda-affine argument ECFieldElement X2 = b.x, Z2 = b.zs[0]; if (X2.isZero() || Z2.bitLength() != 1) { return twice().add(b); } ECFieldElement L1 = this.y, Z1 = this.zs[0]; ECFieldElement L2 = b.y; ECFieldElement X1Sq = X1.square(); ECFieldElement L1Sq = L1.square(); ECFieldElement Z1Sq = Z1.square(); ECFieldElement L1Z1 = L1.multiply(Z1); ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); ECFieldElement L2plus1 = L2.addOne(); ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiply(T).add(X1Sq.multiply(Z1Sq)); ECFieldElement X2Z1Sq = X2.multiply(Z1Sq); ECFieldElement B = X2Z1Sq.add(T).square(); ECFieldElement X3 = A.square().multiply(X2Z1Sq); ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq); ECFieldElement L3 = A.add(B).square().multiply(T).add(L2plus1.multiply(Z3)); return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); } default: { return twice().add(b); } } } protected void checkCurveEquation() { if (this.isInfinity()) { return; } ECFieldElement Z; switch (this.getCurveCoordinateSystem()) { case ECCurve.COORD_LAMBDA_AFFINE: Z = curve.fromBigInteger(ECConstants.ONE); break; case ECCurve.COORD_LAMBDA_PROJECTIVE: Z = this.zs[0]; break; default: return; } if (Z.isZero()) { throw new IllegalStateException(); } ECFieldElement X = this.x; if (X.isZero()) { // NOTE: For x == 0, we expect the affine-y instead of the lambda-y ECFieldElement Y = this.y; if (!Y.square().equals(curve.getB().multiply(Z))) { throw new IllegalStateException(); } return; } ECFieldElement L = this.y; ECFieldElement XSq = X.square(); ECFieldElement ZSq = Z.square(); ECFieldElement lhs = L.square().add(L.multiply(Z)).add(this.getCurve().getA().multiply(ZSq)).multiply(XSq); ECFieldElement rhs = ZSq.square().multiply(this.getCurve().getB()).add(XSq.square()); if (!lhs.equals(rhs)) { throw new IllegalStateException("F2m Lambda-Projective invariant broken"); } } public ECPoint negate() { if (this.isInfinity()) { return this; } ECFieldElement X = this.x; if (X.isZero()) { return this; } switch (this.getCurveCoordinateSystem()) { case ECCurve.COORD_AFFINE: { ECFieldElement Y = this.y; return new ECPoint.F2m(curve, X, Y.add(X), this.withCompression); } case ECCurve.COORD_HOMOGENEOUS: { ECFieldElement Y = this.y, Z = this.zs[0]; return new ECPoint.F2m(curve, X, Y.add(X), new ECFieldElement[]{ Z }, this.withCompression); } case ECCurve.COORD_LAMBDA_AFFINE: { ECFieldElement L = this.y; return new ECPoint.F2m(curve, X, L.addOne(), this.withCompression); } case ECCurve.COORD_LAMBDA_PROJECTIVE: { // L is actually Lambda (X + Y/X) here ECFieldElement L = this.y, Z = this.zs[0]; return new ECPoint.F2m(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); } default: { throw new IllegalStateException("unsupported coordinate system"); } } } } }