diff options
Diffstat (limited to 'bcprov/src/main/java/org/bouncycastle/math/ec')
56 files changed, 8919 insertions, 2037 deletions
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java index 69ab797..d1f35c5 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java @@ -13,7 +13,13 @@ public abstract class AbstractECMultiplier implements ECMultiplier } ECPoint positive = multiplyPositive(p, k.abs()); - return sign > 0 ? positive : positive.negate(); + ECPoint result = sign > 0 ? positive : positive.negate(); + + /* + * Although the various multipliers ought not to produce invalid output under normal + * circumstances, a final check here is advised to guard against fault attacks. + */ + return ECAlgorithms.validatePoint(result); } protected abstract ECPoint multiplyPositive(ECPoint p, BigInteger k); diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java index d640b5f..3b334d2 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java @@ -2,8 +2,62 @@ package org.bouncycastle.math.ec; import java.math.BigInteger; +import org.bouncycastle.math.ec.endo.ECEndomorphism; +import org.bouncycastle.math.ec.endo.GLVEndomorphism; +import org.bouncycastle.math.field.FiniteField; +import org.bouncycastle.math.field.PolynomialExtensionField; + public class ECAlgorithms { + public static boolean isF2mCurve(ECCurve c) + { + FiniteField field = c.getField(); + return field.getDimension() > 1 && field.getCharacteristic().equals(ECConstants.TWO) + && field instanceof PolynomialExtensionField; + } + + public static boolean isFpCurve(ECCurve c) + { + return c.getField().getDimension() == 1; + } + + public static ECPoint sumOfMultiplies(ECPoint[] ps, BigInteger[] ks) + { + if (ps == null || ks == null || ps.length != ks.length || ps.length < 1) + { + throw new IllegalArgumentException("point and scalar arrays should be non-null, and of equal, non-zero, length"); + } + + int count = ps.length; + switch (count) + { + case 1: + return ps[0].multiply(ks[0]); + case 2: + return sumOfTwoMultiplies(ps[0], ks[0], ps[1], ks[1]); + default: + break; + } + + ECPoint p = ps[0]; + ECCurve c = p.getCurve(); + + ECPoint[] imported = new ECPoint[count]; + imported[0] = p; + for (int i = 1; i < count; ++i) + { + imported[i] = importPoint(c, ps[i]); + } + + ECEndomorphism endomorphism = c.getEndomorphism(); + if (endomorphism instanceof GLVEndomorphism) + { + return validatePoint(implSumOfMultipliesGLV(imported, ks, (GLVEndomorphism)endomorphism)); + } + + return validatePoint(implSumOfMultiplies(imported, ks)); + } + public static ECPoint sumOfTwoMultiplies(ECPoint P, BigInteger a, ECPoint Q, BigInteger b) { @@ -16,11 +70,18 @@ public class ECAlgorithms ECCurve.F2m f2mCurve = (ECCurve.F2m)cp; if (f2mCurve.isKoblitz()) { - return P.multiply(a).add(Q.multiply(b)); + return validatePoint(P.multiply(a).add(Q.multiply(b))); } } - return implShamirsTrick(P, a, Q, b); + ECEndomorphism endomorphism = cp.getEndomorphism(); + if (endomorphism instanceof GLVEndomorphism) + { + return validatePoint( + implSumOfMultipliesGLV(new ECPoint[]{ P, Q }, new BigInteger[]{ a, b }, (GLVEndomorphism)endomorphism)); + } + + return validatePoint(implShamirsTrickWNaf(P, a, Q, b)); } /* @@ -48,7 +109,7 @@ public class ECAlgorithms ECCurve cp = P.getCurve(); Q = importPoint(cp, Q); - return implShamirsTrick(P, k, Q, l); + return validatePoint(implShamirsTrickJsf(P, k, Q, l)); } public static ECPoint importPoint(ECCurve c, ECPoint p) @@ -61,7 +122,7 @@ public class ECAlgorithms return c.importPoint(p); } - static void implMontgomeryTrick(ECFieldElement[] zs, int off, int len) + public static void montgomeryTrick(ECFieldElement[] zs, int off, int len) { /* * Uses the "Montgomery Trick" to invert many field elements, with only a single actual @@ -92,7 +153,50 @@ public class ECAlgorithms zs[off] = u; } - static ECPoint implShamirsTrick(ECPoint P, BigInteger k, + /** + * Simple shift-and-add multiplication. Serves as reference implementation + * to verify (possibly faster) implementations, and for very small scalars. + * + * @param p + * The point to multiply. + * @param k + * The multiplier. + * @return The result of the point multiplication <code>kP</code>. + */ + public static ECPoint referenceMultiply(ECPoint p, BigInteger k) + { + BigInteger x = k.abs(); + ECPoint q = p.getCurve().getInfinity(); + int t = x.bitLength(); + if (t > 0) + { + if (x.testBit(0)) + { + q = p; + } + for (int i = 1; i < t; i++) + { + p = p.twice(); + if (x.testBit(i)) + { + q = q.add(p); + } + } + } + return k.signum() < 0 ? q.negate() : q; + } + + public static ECPoint validatePoint(ECPoint p) + { + if (!p.isValid()) + { + throw new IllegalArgumentException("Invalid point"); + } + + return p; + } + + static ECPoint implShamirsTrickJsf(ECPoint P, BigInteger k, ECPoint Q, BigInteger l) { ECCurve curve = P.getCurve(); @@ -118,7 +222,9 @@ public class ECAlgorithms while (--i >= 0) { int jsfi = jsf[i]; - int kDigit = (jsfi >> 4), lDigit = ((jsfi << 28) >> 28); + + // NOTE: The shifting ensures the sign is extended correctly + int kDigit = ((jsfi << 24) >> 28), lDigit = ((jsfi << 28) >> 28); int index = 4 + (kDigit * 3) + lDigit; R = R.twicePlus(table[index]); @@ -126,4 +232,238 @@ public class ECAlgorithms return R; } + + static ECPoint implShamirsTrickWNaf(ECPoint P, BigInteger k, + ECPoint Q, BigInteger l) + { + boolean negK = k.signum() < 0, negL = l.signum() < 0; + + k = k.abs(); + l = l.abs(); + + int widthP = Math.max(2, Math.min(16, WNafUtil.getWindowSize(k.bitLength()))); + int widthQ = Math.max(2, Math.min(16, WNafUtil.getWindowSize(l.bitLength()))); + + WNafPreCompInfo infoP = WNafUtil.precompute(P, widthP, true); + WNafPreCompInfo infoQ = WNafUtil.precompute(Q, widthQ, true); + + ECPoint[] preCompP = negK ? infoP.getPreCompNeg() : infoP.getPreComp(); + ECPoint[] preCompQ = negL ? infoQ.getPreCompNeg() : infoQ.getPreComp(); + ECPoint[] preCompNegP = negK ? infoP.getPreComp() : infoP.getPreCompNeg(); + ECPoint[] preCompNegQ = negL ? infoQ.getPreComp() : infoQ.getPreCompNeg(); + + byte[] wnafP = WNafUtil.generateWindowNaf(widthP, k); + byte[] wnafQ = WNafUtil.generateWindowNaf(widthQ, l); + + return implShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ, preCompNegQ, wnafQ); + } + + static ECPoint implShamirsTrickWNaf(ECPoint P, BigInteger k, ECPointMap pointMapQ, BigInteger l) + { + boolean negK = k.signum() < 0, negL = l.signum() < 0; + + k = k.abs(); + l = l.abs(); + + int width = Math.max(2, Math.min(16, WNafUtil.getWindowSize(Math.max(k.bitLength(), l.bitLength())))); + + ECPoint Q = WNafUtil.mapPointWithPrecomp(P, width, true, pointMapQ); + WNafPreCompInfo infoP = WNafUtil.getWNafPreCompInfo(P); + WNafPreCompInfo infoQ = WNafUtil.getWNafPreCompInfo(Q); + + ECPoint[] preCompP = negK ? infoP.getPreCompNeg() : infoP.getPreComp(); + ECPoint[] preCompQ = negL ? infoQ.getPreCompNeg() : infoQ.getPreComp(); + ECPoint[] preCompNegP = negK ? infoP.getPreComp() : infoP.getPreCompNeg(); + ECPoint[] preCompNegQ = negL ? infoQ.getPreComp() : infoQ.getPreCompNeg(); + + byte[] wnafP = WNafUtil.generateWindowNaf(width, k); + byte[] wnafQ = WNafUtil.generateWindowNaf(width, l); + + return implShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ, preCompNegQ, wnafQ); + } + + private static ECPoint implShamirsTrickWNaf(ECPoint[] preCompP, ECPoint[] preCompNegP, byte[] wnafP, + ECPoint[] preCompQ, ECPoint[] preCompNegQ, byte[] wnafQ) + { + int len = Math.max(wnafP.length, wnafQ.length); + + ECCurve curve = preCompP[0].getCurve(); + ECPoint infinity = curve.getInfinity(); + + ECPoint R = infinity; + int zeroes = 0; + + for (int i = len - 1; i >= 0; --i) + { + int wiP = i < wnafP.length ? wnafP[i] : 0; + int wiQ = i < wnafQ.length ? wnafQ[i] : 0; + + if ((wiP | wiQ) == 0) + { + ++zeroes; + continue; + } + + ECPoint r = infinity; + if (wiP != 0) + { + int nP = Math.abs(wiP); + ECPoint[] tableP = wiP < 0 ? preCompNegP : preCompP; + r = r.add(tableP[nP >>> 1]); + } + if (wiQ != 0) + { + int nQ = Math.abs(wiQ); + ECPoint[] tableQ = wiQ < 0 ? preCompNegQ : preCompQ; + r = r.add(tableQ[nQ >>> 1]); + } + + if (zeroes > 0) + { + R = R.timesPow2(zeroes); + zeroes = 0; + } + + R = R.twicePlus(r); + } + + if (zeroes > 0) + { + R = R.timesPow2(zeroes); + } + + return R; + } + + static ECPoint implSumOfMultiplies(ECPoint[] ps, BigInteger[] ks) + { + int count = ps.length; + boolean[] negs = new boolean[count]; + WNafPreCompInfo[] infos = new WNafPreCompInfo[count]; + byte[][] wnafs = new byte[count][]; + + for (int i = 0; i < count; ++i) + { + BigInteger ki = ks[i]; negs[i] = ki.signum() < 0; ki = ki.abs(); + + int width = Math.max(2, Math.min(16, WNafUtil.getWindowSize(ki.bitLength()))); + infos[i] = WNafUtil.precompute(ps[i], width, true); + wnafs[i] = WNafUtil.generateWindowNaf(width, ki); + } + + return implSumOfMultiplies(negs, infos, wnafs); + } + + static ECPoint implSumOfMultipliesGLV(ECPoint[] ps, BigInteger[] ks, GLVEndomorphism glvEndomorphism) + { + BigInteger n = ps[0].getCurve().getOrder(); + + int len = ps.length; + + BigInteger[] abs = new BigInteger[len << 1]; + for (int i = 0, j = 0; i < len; ++i) + { + BigInteger[] ab = glvEndomorphism.decomposeScalar(ks[i].mod(n)); + abs[j++] = ab[0]; + abs[j++] = ab[1]; + } + + ECPointMap pointMap = glvEndomorphism.getPointMap(); + if (glvEndomorphism.hasEfficientPointMap()) + { + return ECAlgorithms.implSumOfMultiplies(ps, pointMap, abs); + } + + ECPoint[] pqs = new ECPoint[len << 1]; + for (int i = 0, j = 0; i < len; ++i) + { + ECPoint p = ps[i], q = pointMap.map(p); + pqs[j++] = p; + pqs[j++] = q; + } + + return ECAlgorithms.implSumOfMultiplies(pqs, abs); + + } + + static ECPoint implSumOfMultiplies(ECPoint[] ps, ECPointMap pointMap, BigInteger[] ks) + { + int halfCount = ps.length, fullCount = halfCount << 1; + + boolean[] negs = new boolean[fullCount]; + WNafPreCompInfo[] infos = new WNafPreCompInfo[fullCount]; + byte[][] wnafs = new byte[fullCount][]; + + for (int i = 0; i < halfCount; ++i) + { + int j0 = i << 1, j1 = j0 + 1; + + BigInteger kj0 = ks[j0]; negs[j0] = kj0.signum() < 0; kj0 = kj0.abs(); + BigInteger kj1 = ks[j1]; negs[j1] = kj1.signum() < 0; kj1 = kj1.abs(); + + int width = Math.max(2, Math.min(16, WNafUtil.getWindowSize(Math.max(kj0.bitLength(), kj1.bitLength())))); + + ECPoint P = ps[i], Q = WNafUtil.mapPointWithPrecomp(P, width, true, pointMap); + infos[j0] = WNafUtil.getWNafPreCompInfo(P); + infos[j1] = WNafUtil.getWNafPreCompInfo(Q); + wnafs[j0] = WNafUtil.generateWindowNaf(width, kj0); + wnafs[j1] = WNafUtil.generateWindowNaf(width, kj1); + } + + return implSumOfMultiplies(negs, infos, wnafs); + } + + private static ECPoint implSumOfMultiplies(boolean[] negs, WNafPreCompInfo[] infos, byte[][] wnafs) + { + int len = 0, count = wnafs.length; + for (int i = 0; i < count; ++i) + { + len = Math.max(len, wnafs[i].length); + } + + ECCurve curve = infos[0].getPreComp()[0].getCurve(); + ECPoint infinity = curve.getInfinity(); + + ECPoint R = infinity; + int zeroes = 0; + + for (int i = len - 1; i >= 0; --i) + { + ECPoint r = infinity; + + for (int j = 0; j < count; ++j) + { + byte[] wnaf = wnafs[j]; + int wi = i < wnaf.length ? wnaf[i] : 0; + if (wi != 0) + { + int n = Math.abs(wi); + WNafPreCompInfo info = infos[j]; + ECPoint[] table = (wi < 0 == negs[j]) ? info.getPreComp() : info.getPreCompNeg(); + r = r.add(table[n >>> 1]); + } + } + + if (r == infinity) + { + ++zeroes; + continue; + } + + if (zeroes > 0) + { + R = R.timesPow2(zeroes); + zeroes = 0; + } + + R = R.twicePlus(r); + } + + if (zeroes > 0) + { + R = R.timesPow2(zeroes); + } + + return R; + } } diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java index 19f0062..4c658f2 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java @@ -1,9 +1,15 @@ package org.bouncycastle.math.ec; import java.math.BigInteger; +import java.util.Hashtable; import java.util.Random; +import org.bouncycastle.math.ec.endo.ECEndomorphism; +import org.bouncycastle.math.ec.endo.GLVEndomorphism; +import org.bouncycastle.math.field.FiniteField; +import org.bouncycastle.math.field.FiniteFields; import org.bouncycastle.util.BigIntegers; +import org.bouncycastle.util.Integers; /** * base class for an elliptic curve @@ -28,11 +34,13 @@ public abstract class ECCurve public class Config { protected int coord; + protected ECEndomorphism endomorphism; protected ECMultiplier multiplier; - Config(int coord, ECMultiplier multiplier) + Config(int coord, ECEndomorphism endomorphism, ECMultiplier multiplier) { this.coord = coord; + this.endomorphism = endomorphism; this.multiplier = multiplier; } @@ -42,6 +50,12 @@ public abstract class ECCurve return this; } + public Config setEndomorphism(ECEndomorphism endomorphism) + { + this.endomorphism = endomorphism; + return this; + } + public Config setMultiplier(ECMultiplier multiplier) { this.multiplier = multiplier; @@ -62,23 +76,57 @@ public abstract class ECCurve } c.coord = coord; + c.endomorphism = endomorphism; c.multiplier = multiplier; return c; } } + protected FiniteField field; protected ECFieldElement a, b; + protected BigInteger order, cofactor; + protected int coord = COORD_AFFINE; + protected ECEndomorphism endomorphism = null; protected ECMultiplier multiplier = null; + protected ECCurve(FiniteField field) + { + this.field = field; + } + public abstract int getFieldSize(); public abstract ECFieldElement fromBigInteger(BigInteger x); public Config configure() { - return new Config(this.coord, this.multiplier); + return new Config(this.coord, this.endomorphism, this.multiplier); + } + + public ECPoint validatePoint(BigInteger x, BigInteger y) + { + ECPoint p = createPoint(x, y); + if (!p.isValid()) + { + throw new IllegalArgumentException("Invalid point coordinates"); + } + return p; + } + + /** + * @deprecated per-point compression property will be removed, use {@link #validatePoint(BigInteger, BigInteger)} + * and refer {@link ECPoint#getEncoded(boolean)} + */ + public ECPoint validatePoint(BigInteger x, BigInteger y, boolean withCompression) + { + ECPoint p = createPoint(x, y, withCompression); + if (!p.isValid()) + { + throw new IllegalArgumentException("Invalid point coordinates"); + } + return p; } public ECPoint createPoint(BigInteger x, BigInteger y) @@ -99,8 +147,15 @@ public abstract class ECCurve protected abstract ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression); + protected abstract ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression); + protected ECMultiplier createDefaultMultiplier() { + if (endomorphism instanceof GLVEndomorphism) + { + return new GLVMultiplier(this, (GLVEndomorphism)endomorphism); + } + return new WNafL2RMultiplier(); } @@ -109,26 +164,40 @@ public abstract class ECCurve return coord == COORD_AFFINE; } - public PreCompInfo getPreCompInfo(ECPoint p) + public PreCompInfo getPreCompInfo(ECPoint point, String name) { - checkPoint(p); - return p.preCompInfo; + checkPoint(point); + synchronized (point) + { + Hashtable table = point.preCompTable; + return table == null ? null : (PreCompInfo)table.get(name); + } } /** - * Sets the <code>PreCompInfo</code> for a point on this curve. Used by + * Adds <code>PreCompInfo</code> for a point on this curve, under a given name. Used by * <code>ECMultiplier</code>s to save the precomputation for this <code>ECPoint</code> for use * by subsequent multiplication. * * @param point * The <code>ECPoint</code> to store precomputations for. + * @param name + * A <code>String</code> used to index precomputations of different types. * @param preCompInfo * The values precomputed by the <code>ECMultiplier</code>. */ - public void setPreCompInfo(ECPoint point, PreCompInfo preCompInfo) + public void setPreCompInfo(ECPoint point, String name, PreCompInfo preCompInfo) { checkPoint(point); - point.preCompInfo = preCompInfo; + synchronized (point) + { + Hashtable table = point.preCompTable; + if (null == table) + { + point.preCompTable = table = new Hashtable(4); + } + table.put(name, preCompInfo); + } } public ECPoint importPoint(ECPoint p) @@ -145,7 +214,7 @@ public abstract class ECCurve // TODO Default behaviour could be improved if the two curves have the same coordinate system by copying any Z coordinates. p = p.normalize(); - return createPoint(p.getXCoord().toBigInteger(), p.getYCoord().toBigInteger(), p.withCompression); + return validatePoint(p.getXCoord().toBigInteger(), p.getYCoord().toBigInteger(), p.withCompression); } /** @@ -188,7 +257,7 @@ public abstract class ECCurve return; } - ECAlgorithms.implMontgomeryTrick(zs, 0, count); + ECAlgorithms.montgomeryTrick(zs, 0, count); for (int j = 0; j < count; ++j) { @@ -199,6 +268,11 @@ public abstract class ECCurve public abstract ECPoint getInfinity(); + public FiniteField getField() + { + return field; + } + public ECFieldElement getA() { return a; @@ -209,6 +283,16 @@ public abstract class ECCurve return b; } + public BigInteger getOrder() + { + return order; + } + + public BigInteger getCofactor() + { + return cofactor; + } + public int getCoordinateSystem() { return coord; @@ -216,10 +300,15 @@ public abstract class ECCurve protected abstract ECPoint decompressPoint(int yTilde, BigInteger X1); + public ECEndomorphism getEndomorphism() + { + return endomorphism; + } + /** * Sets the default <code>ECMultiplier</code>, unless already set. */ - public ECMultiplier getMultiplier() + public synchronized ECMultiplier getMultiplier() { if (this.multiplier == null) { @@ -239,7 +328,8 @@ public abstract class ECCurve ECPoint p = null; int expectedLength = (getFieldSize() + 7) / 8; - switch (encoded[0]) + byte type = encoded[0]; + switch (type) { case 0x00: // infinity { @@ -259,29 +349,56 @@ public abstract class ECCurve throw new IllegalArgumentException("Incorrect length for compressed encoding"); } - int yTilde = encoded[0] & 1; + int yTilde = type & 1; BigInteger X = BigIntegers.fromUnsignedByteArray(encoded, 1, expectedLength); p = decompressPoint(yTilde, X); + if (!p.satisfiesCofactor()) + { + throw new IllegalArgumentException("Invalid point"); + } + break; } case 0x04: // uncompressed + { + if (encoded.length != (2 * expectedLength + 1)) + { + throw new IllegalArgumentException("Incorrect length for uncompressed encoding"); + } + + BigInteger X = BigIntegers.fromUnsignedByteArray(encoded, 1, expectedLength); + BigInteger Y = BigIntegers.fromUnsignedByteArray(encoded, 1 + expectedLength, expectedLength); + + p = validatePoint(X, Y); + break; + } case 0x06: // hybrid case 0x07: // hybrid { if (encoded.length != (2 * expectedLength + 1)) { - throw new IllegalArgumentException("Incorrect length for uncompressed/hybrid encoding"); + throw new IllegalArgumentException("Incorrect length for hybrid encoding"); } BigInteger X = BigIntegers.fromUnsignedByteArray(encoded, 1, expectedLength); BigInteger Y = BigIntegers.fromUnsignedByteArray(encoded, 1 + expectedLength, expectedLength); - p = createPoint(X, Y); + if (Y.testBit(0) != (type == 0x07)) + { + throw new IllegalArgumentException("Inconsistent Y coordinate in hybrid encoding"); + } + + p = validatePoint(X, Y); break; } default: - throw new IllegalArgumentException("Invalid point encoding 0x" + Integer.toString(encoded[0], 16)); + throw new IllegalArgumentException("Invalid point encoding 0x" + Integer.toString(type, 16)); + } + + if (type != 0x00 && p.isInfinity()) + { + throw new IllegalArgumentException("Invalid infinity encoding"); } return p; @@ -312,10 +429,62 @@ public abstract class ECCurve } } + public boolean equals(ECCurve other) + { + return this == other + || (null != other + && getField().equals(other.getField()) + && getA().toBigInteger().equals(other.getA().toBigInteger()) + && getB().toBigInteger().equals(other.getB().toBigInteger())); + } + + public boolean equals(Object obj) + { + return this == obj || (obj instanceof ECCurve && equals((ECCurve)obj)); + } + + public int hashCode() + { + return getField().hashCode() + ^ Integers.rotateLeft(getA().toBigInteger().hashCode(), 8) + ^ Integers.rotateLeft(getB().toBigInteger().hashCode(), 16); + } + + public static abstract class AbstractFp extends ECCurve + { + protected AbstractFp(BigInteger q) + { + super(FiniteFields.getPrimeField(q)); + } + + protected ECPoint decompressPoint(int yTilde, BigInteger X1) + { + ECFieldElement x = this.fromBigInteger(X1); + ECFieldElement rhs = x.square().add(a).multiply(x).add(b); + ECFieldElement y = rhs.sqrt(); + + /* + * If y is not a square, then we haven't got a point on the curve + */ + if (y == null) + { + throw new IllegalArgumentException("Invalid point compression"); + } + + if (y.testBitZero() != (yTilde == 1)) + { + // Use the other root + y = y.negate(); + } + + return this.createRawPoint(x, y, true); + } + } + /** * Elliptic curve over Fp */ - public static class Fp extends ECCurve + public static class Fp extends AbstractFp { private static final int FP_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED; @@ -324,29 +493,47 @@ public abstract class ECCurve public Fp(BigInteger q, BigInteger a, BigInteger b) { + this(q, a, b, null, null); + } + + public Fp(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor) + { + super(q); + this.q = q; this.r = ECFieldElement.Fp.calculateResidue(q); this.infinity = new ECPoint.Fp(this, null, null); this.a = fromBigInteger(a); this.b = fromBigInteger(b); + this.order = order; + this.cofactor = cofactor; this.coord = FP_DEFAULT_COORDS; } protected Fp(BigInteger q, BigInteger r, ECFieldElement a, ECFieldElement b) { + this(q, r, a, b, null, null); + } + + protected Fp(BigInteger q, BigInteger r, ECFieldElement a, ECFieldElement b, BigInteger order, BigInteger cofactor) + { + super(q); + this.q = q; this.r = r; this.infinity = new ECPoint.Fp(this, null, null); this.a = a; this.b = b; + this.order = order; + this.cofactor = cofactor; this.coord = FP_DEFAULT_COORDS; } protected ECCurve cloneCurve() { - return new Fp(q, r, a, b); + return new Fp(q, r, a, b, order, cofactor); } public boolean supportsCoordinateSystem(int coord) @@ -383,6 +570,11 @@ public abstract class ECCurve return new ECPoint.Fp(this, x, y, withCompression); } + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new ECPoint.Fp(this, x, y, zs, withCompression); + } + public ECPoint importPoint(ECPoint p) { if (this != p.getCurve() && this.getCoordinateSystem() == COORD_JACOBIAN && !p.isInfinity()) @@ -405,58 +597,47 @@ public abstract class ECCurve return super.importPoint(p); } - protected ECPoint decompressPoint(int yTilde, BigInteger X1) + public ECPoint getInfinity() { - ECFieldElement x = fromBigInteger(X1); - ECFieldElement alpha = x.multiply(x.square().add(a)).add(b); - ECFieldElement beta = alpha.sqrt(); + return infinity; + } + } - // - // if we can't find a sqrt we haven't got a point on the - // curve - run! - // - if (beta == null) + public static abstract class AbstractF2m extends ECCurve + { + private static FiniteField buildField(int m, int k1, int k2, int k3) + { + if (k1 == 0) { - throw new RuntimeException("Invalid point compression"); + throw new IllegalArgumentException("k1 must be > 0"); } - BigInteger betaValue = beta.toBigInteger(); - if (betaValue.testBit(0) != (yTilde == 1)) + if (k2 == 0) { - // Use the other root - beta = fromBigInteger(q.subtract(betaValue)); - } - - return new ECPoint.Fp(this, x, beta, true); - } + if (k3 != 0) + { + throw new IllegalArgumentException("k3 must be 0 if k2 == 0"); + } - public ECPoint getInfinity() - { - return infinity; - } + return FiniteFields.getBinaryExtensionField(new int[]{ 0, k1, m }); + } - public boolean equals( - Object anObject) - { - if (anObject == this) + if (k2 <= k1) { - return true; + throw new IllegalArgumentException("k2 must be > k1"); } - if (!(anObject instanceof ECCurve.Fp)) + if (k3 <= k2) { - return false; + throw new IllegalArgumentException("k3 must be > k2"); } - ECCurve.Fp other = (ECCurve.Fp) anObject; - - return this.q.equals(other.q) - && a.equals(other.a) && b.equals(other.b); + return FiniteFields.getBinaryExtensionField(new int[]{ 0, k1, k2, k3, m }); } - public int hashCode() + protected AbstractF2m(int m, int k1, int k2, int k3) { - return a.hashCode() ^ b.hashCode() ^ q.hashCode(); + super(buildField(m, k1, k2, k3)); } } @@ -464,9 +645,9 @@ public abstract class ECCurve * Elliptic curves over F2m. The Weierstrass equation is given by * <code>y<sup>2</sup> + xy = x<sup>3</sup> + ax<sup>2</sup> + b</code>. */ - public static class F2m extends ECCurve + public static class F2m extends AbstractF2m { - private static final int F2M_DEFAULT_COORDS = COORD_AFFINE; + private static final int F2M_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; /** * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>. @@ -498,16 +679,6 @@ public abstract class ECCurve * represents the reduction polynomial <code>f(z)</code>.<br> */ private int k3; // can't be final - JDK 1.1 - - /** - * The order of the base point of the curve. - */ - private BigInteger n; // can't be final - JDK 1.1 - - /** - * The cofactor of the curve. - */ - private BigInteger h; // can't be final - JDK 1.1 /** * The point at infinity on this curve. @@ -563,8 +734,8 @@ public abstract class ECCurve * @param b The coefficient <code>b</code> in the Weierstrass equation * for non-supersingular elliptic curves over * <code>F<sub>2<sup>m</sup></sub></code>. - * @param n The order of the main subgroup of the elliptic curve. - * @param h The cofactor of the elliptic curve, i.e. + * @param order The order of the main subgroup of the elliptic curve. + * @param cofactor The cofactor of the elliptic curve, i.e. * <code>#E<sub>a</sub>(F<sub>2<sup>m</sup></sub>) = h * n</code>. */ public F2m( @@ -572,10 +743,10 @@ public abstract class ECCurve int k, BigInteger a, BigInteger b, - BigInteger n, - BigInteger h) + BigInteger order, + BigInteger cofactor) { - this(m, k, 0, 0, a, b, n, h); + this(m, k, 0, 0, a, b, order, cofactor); } /** @@ -628,8 +799,8 @@ public abstract class ECCurve * @param b The coefficient <code>b</code> in the Weierstrass equation * for non-supersingular elliptic curves over * <code>F<sub>2<sup>m</sup></sub></code>. - * @param n The order of the main subgroup of the elliptic curve. - * @param h The cofactor of the elliptic curve, i.e. + * @param order The order of the main subgroup of the elliptic curve. + * @param cofactor The cofactor of the elliptic curve, i.e. * <code>#E<sub>a</sub>(F<sub>2<sup>m</sup></sub>) = h * n</code>. */ public F2m( @@ -639,40 +810,17 @@ public abstract class ECCurve int k3, BigInteger a, BigInteger b, - BigInteger n, - BigInteger h) + BigInteger order, + BigInteger cofactor) { + super(m, k1, k2, k3); + this.m = m; this.k1 = k1; this.k2 = k2; this.k3 = k3; - this.n = n; - this.h = h; - - if (k1 == 0) - { - throw new IllegalArgumentException("k1 must be > 0"); - } - - if (k2 == 0) - { - if (k3 != 0) - { - throw new IllegalArgumentException("k3 must be 0 if k2 == 0"); - } - } - else - { - if (k2 <= k1) - { - throw new IllegalArgumentException("k2 must be > k1"); - } - - if (k3 <= k2) - { - throw new IllegalArgumentException("k3 must be > k2"); - } - } + this.order = order; + this.cofactor = cofactor; this.infinity = new ECPoint.F2m(this, null, null); this.a = fromBigInteger(a); @@ -680,14 +828,16 @@ public abstract class ECCurve this.coord = F2M_DEFAULT_COORDS; } - protected F2m(int m, int k1, int k2, int k3, ECFieldElement a, ECFieldElement b, BigInteger n, BigInteger h) + protected F2m(int m, int k1, int k2, int k3, ECFieldElement a, ECFieldElement b, BigInteger order, BigInteger cofactor) { + super(m, k1, k2, k3); + this.m = m; this.k1 = k1; this.k2 = k2; this.k3 = k3; - this.n = n; - this.h = h; + this.order = order; + this.cofactor = cofactor; this.infinity = new ECPoint.F2m(this, null, null); this.a = a; @@ -697,7 +847,7 @@ public abstract class ECCurve protected ECCurve cloneCurve() { - return new F2m(m, k1, k2, k3, a, b, n, h); + return new F2m(m, k1, k2, k3, a, b, order, cofactor); } public boolean supportsCoordinateSystem(int coord) @@ -742,7 +892,14 @@ public abstract class ECCurve case COORD_LAMBDA_AFFINE: case COORD_LAMBDA_PROJECTIVE: { - if (!X.isZero()) + if (X.isZero()) + { + if (!Y.square().equals(this.getB())) + { + throw new IllegalArgumentException(); + } + } + else { // Y becomes Lambda (X + Y/X) here Y = Y.divide(X).add(X); @@ -763,6 +920,11 @@ public abstract class ECCurve return new ECPoint.F2m(this, x, y, withCompression); } + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new ECPoint.F2m(this, x, y, zs, withCompression); + } + public ECPoint getInfinity() { return infinity; @@ -774,7 +936,7 @@ public abstract class ECCurve */ public boolean isKoblitz() { - return n != null && h != null && a.bitLength() <= 1 && b.bitLength() == 1; + return order != null && cofactor != null && b.isOne() && (a.isZero() || a.isOne()); } /** @@ -817,49 +979,47 @@ public abstract class ECCurve */ protected ECPoint decompressPoint(int yTilde, BigInteger X1) { - ECFieldElement xp = fromBigInteger(X1); - ECFieldElement yp = null; - if (xp.isZero()) + ECFieldElement x = fromBigInteger(X1), y = null; + if (x.isZero()) { - yp = (ECFieldElement.F2m)b; - for (int i = 0; i < m - 1; i++) - { - yp = yp.square(); - } + y = b.sqrt(); } else { - ECFieldElement beta = xp.add(a).add(b.multiply(xp.square().invert())); + ECFieldElement beta = x.square().invert().multiply(b).add(a).add(x); ECFieldElement z = solveQuadraticEquation(beta); - if (z == null) - { - throw new IllegalArgumentException("Invalid point compression"); - } - if (z.testBitZero() != (yTilde == 1)) + if (z != null) { - z = z.addOne(); + if (z.testBitZero() != (yTilde == 1)) + { + z = z.addOne(); + } + + switch (this.getCoordinateSystem()) + { + case COORD_LAMBDA_AFFINE: + case COORD_LAMBDA_PROJECTIVE: + { + y = z.add(x); + break; + } + default: + { + y = z.multiply(x); + break; + } + } } + } - yp = xp.multiply(z); - - switch (this.getCoordinateSystem()) - { - case COORD_LAMBDA_AFFINE: - case COORD_LAMBDA_PROJECTIVE: - { - yp = yp.divide(xp).add(xp); - break; - } - default: - { - break; - } - } + if (y == null) + { + throw new IllegalArgumentException("Invalid point compression"); } - return new ECPoint.F2m(this, xp, yp, true); + return this.createRawPoint(x, y, true); } - + /** * Solves a quadratic equation <code>z<sup>2</sup> + z = beta</code>(X9.62 * D.1.6) The other solution is <code>z + 1</code>. @@ -903,31 +1063,6 @@ public abstract class ECCurve return z; } - - public boolean equals( - Object anObject) - { - if (anObject == this) - { - return true; - } - - if (!(anObject instanceof ECCurve.F2m)) - { - return false; - } - - ECCurve.F2m other = (ECCurve.F2m)anObject; - - return (this.m == other.m) && (this.k1 == other.k1) - && (this.k2 == other.k2) && (this.k3 == other.k3) - && a.equals(other.a) && b.equals(other.b); - } - - public int hashCode() - { - return this.a.hashCode() ^ this.b.hashCode() ^ m ^ k1 ^ k2 ^ k3; - } public int getM() { @@ -959,14 +1094,20 @@ public abstract class ECCurve return k3; } + /** + * @deprecated use {@link #getOrder()} instead + */ public BigInteger getN() { - return n; + return order; } + /** + * @deprecated use {@link #getCofactor()} instead + */ public BigInteger getH() { - return h; + return cofactor; } } } diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java index 87608eb..5943882 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java @@ -3,6 +3,8 @@ package org.bouncycastle.math.ec; import java.math.BigInteger; import java.util.Random; +import org.bouncycastle.math.raw.Mod; +import org.bouncycastle.math.raw.Nat; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; @@ -27,11 +29,36 @@ public abstract class ECFieldElement return toBigInteger().bitLength(); } + public boolean isOne() + { + return bitLength() == 1; + } + public boolean isZero() { return 0 == toBigInteger().signum(); } + public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return multiply(b).subtract(x.multiply(y)); + } + + public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return multiply(b).add(x.multiply(y)); + } + + public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return square().subtract(x.multiply(y)); + } + + public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y) + { + return square().add(x.multiply(y)); + } + public boolean testBitZero() { return toBigInteger().testBit(0); @@ -51,42 +78,10 @@ public abstract class ECFieldElement { BigInteger q, r, x; -// static int[] calculateNaf(BigInteger p) -// { -// int[] naf = WNafUtil.generateCompactNaf(p); -// -// int bit = 0; -// for (int i = 0; i < naf.length; ++i) -// { -// int ni = naf[i]; -// int digit = ni >> 16, zeroes = ni & 0xFFFF; -// -// bit += zeroes; -// naf[i] = digit < 0 ? ~bit : bit; -// ++bit; -// } -// -// int last = naf.length - 1; -// if (last > 0 && last <= 16) -// { -// int top = naf[last], top2 = naf[last - 1]; -// if (top2 < 0) -// { -// top2 = ~top2; -// } -// if (top - top2 >= 64) -// { -// return naf; -// } -// } -// -// return null; -// } - static BigInteger calculateResidue(BigInteger p) { int bitLength = p.bitLength(); - if (bitLength > 128) + if (bitLength >= 96) { BigInteger firstWord = p.shiftRight(bitLength - 64); if (firstWord.longValue() == -1L) @@ -159,13 +154,7 @@ public abstract class ECFieldElement public ECFieldElement subtract(ECFieldElement b) { - BigInteger x2 = b.toBigInteger(); - BigInteger x3 = x.subtract(x2); - if (x3.signum() < 0) - { - x3 = x3.add(q); - } - return new Fp(q, r, x3); + return new Fp(q, r, modSubtract(x, b.toBigInteger())); } public ECFieldElement multiply(ECFieldElement b) @@ -173,27 +162,30 @@ public abstract class ECFieldElement return new Fp(q, r, modMult(x, b.toBigInteger())); } + public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + BigInteger ax = this.x, bx = b.toBigInteger(), xx = x.toBigInteger(), yx = y.toBigInteger(); + BigInteger ab = ax.multiply(bx); + BigInteger xy = xx.multiply(yx); + return new Fp(q, r, modReduce(ab.subtract(xy))); + } + + public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + BigInteger ax = this.x, bx = b.toBigInteger(), xx = x.toBigInteger(), yx = y.toBigInteger(); + BigInteger ab = ax.multiply(bx); + BigInteger xy = xx.multiply(yx); + return new Fp(q, r, modReduce(ab.add(xy))); + } + public ECFieldElement divide(ECFieldElement b) { - return new Fp(q, modMult(x, b.toBigInteger().modInverse(q))); + return new Fp(q, r, modMult(x, modInverse(b.toBigInteger()))); } public ECFieldElement negate() { - BigInteger x2; - if (x.signum() == 0) - { - x2 = x; - } - else if (ONE.equals(r)) - { - x2 = q.xor(x); - } - else - { - x2 = q.subtract(x); - } - return new Fp(q, r, x2); + return x.signum() == 0 ? this : new Fp(q, r, q.subtract(x)); } public ECFieldElement square() @@ -201,10 +193,26 @@ public abstract class ECFieldElement return new Fp(q, r, modMult(x, x)); } + public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y) + { + BigInteger ax = this.x, xx = x.toBigInteger(), yx = y.toBigInteger(); + BigInteger aa = ax.multiply(ax); + BigInteger xy = xx.multiply(yx); + return new Fp(q, r, modReduce(aa.subtract(xy))); + } + + public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y) + { + BigInteger ax = this.x, xx = x.toBigInteger(), yx = y.toBigInteger(); + BigInteger aa = ax.multiply(ax); + BigInteger xy = xx.multiply(yx); + return new Fp(q, r, modReduce(aa.add(xy))); + } + public ECFieldElement invert() { // TODO Modular inversion can be faster for a (Generalized) Mersenne Prime. - return new Fp(q, r, x.modInverse(q)); + return new Fp(q, r, modInverse(x)); } // D.1.4 91 @@ -214,6 +222,11 @@ public abstract class ECFieldElement */ public ECFieldElement sqrt() { + if (this.isZero() || this.isOne()) // earlier JDK compatibility + { + return this; + } + if (!q.testBit(0)) { throw new RuntimeException("not done yet"); @@ -221,29 +234,44 @@ public abstract class ECFieldElement // note: even though this class implements ECConstants don't be tempted to // remove the explicit declaration, some J2ME environments don't cope. - // p mod 4 == 3 - if (q.testBit(1)) + + if (q.testBit(1)) // q == 4m + 3 + { + BigInteger e = q.shiftRight(2).add(ECConstants.ONE); + return checkSqrt(new Fp(q, r, x.modPow(e, q))); + } + + if (q.testBit(2)) // q == 8m + 5 { - // z = g^(u+1) + p, p = 4u + 3 - ECFieldElement z = new Fp(q, r, x.modPow(q.shiftRight(2).add(ECConstants.ONE), q)); + BigInteger t1 = x.modPow(q.shiftRight(3), q); + BigInteger t2 = modMult(t1, x); + BigInteger t3 = modMult(t2, t1); + + if (t3.equals(ECConstants.ONE)) + { + return checkSqrt(new Fp(q, r, t2)); + } + + // TODO This is constant and could be precomputed + BigInteger t4 = ECConstants.TWO.modPow(q.shiftRight(2), q); - return z.square().equals(this) ? z : null; + BigInteger y = modMult(t2, t4); + + return checkSqrt(new Fp(q, r, y)); } - // p mod 4 == 1 - BigInteger qMinusOne = q.subtract(ECConstants.ONE); + // q == 8m + 1 - BigInteger legendreExponent = qMinusOne.shiftRight(1); + BigInteger legendreExponent = q.shiftRight(1); if (!(x.modPow(legendreExponent, q).equals(ECConstants.ONE))) { return null; } - BigInteger u = qMinusOne.shiftRight(2); - BigInteger k = u.shiftLeft(1).add(ECConstants.ONE); + BigInteger X = this.x; + BigInteger fourX = modDouble(modDouble(X)); - BigInteger Q = this.x; - BigInteger fourQ = modDouble(modDouble(Q)); + BigInteger k = legendreExponent.add(ECConstants.ONE), qMinusOne = q.subtract(ECConstants.ONE); BigInteger U, V; Random rand = new Random(); @@ -255,94 +283,39 @@ public abstract class ECFieldElement P = new BigInteger(q.bitLength(), rand); } while (P.compareTo(q) >= 0 - || !(P.multiply(P).subtract(fourQ).modPow(legendreExponent, q).equals(qMinusOne))); + || !modReduce(P.multiply(P).subtract(fourX)).modPow(legendreExponent, q).equals(qMinusOne)); - BigInteger[] result = lucasSequence(P, Q, k); + BigInteger[] result = lucasSequence(P, X, k); U = result[0]; V = result[1]; - if (modMult(V, V).equals(fourQ)) + if (modMult(V, V).equals(fourX)) { - // Integer division by 2, mod q - if (V.testBit(0)) - { - V = V.add(q); - } - - V = V.shiftRight(1); - - //assert V.multiply(V).mod(q).equals(x); - - return new ECFieldElement.Fp(q, r, V); + return new ECFieldElement.Fp(q, r, modHalfAbs(V)); } } while (U.equals(ECConstants.ONE) || U.equals(qMinusOne)); return null; - -// BigInteger qMinusOne = q.subtract(ECConstants.ONE); -// BigInteger legendreExponent = qMinusOne.shiftRight(1); //divide(ECConstants.TWO); -// if (!(x.modPow(legendreExponent, q).equals(ECConstants.ONE))) -// { -// return null; -// } -// -// Random rand = new Random(); -// BigInteger fourX = x.shiftLeft(2); -// -// BigInteger r; -// do -// { -// r = new BigInteger(q.bitLength(), rand); -// } -// while (r.compareTo(q) >= 0 -// || !(r.multiply(r).subtract(fourX).modPow(legendreExponent, q).equals(qMinusOne))); -// -// BigInteger n1 = qMinusOne.shiftRight(2); //.divide(ECConstants.FOUR); -// BigInteger n2 = n1.add(ECConstants.ONE); //q.add(ECConstants.THREE).divide(ECConstants.FOUR); -// -// BigInteger wOne = WOne(r, x, q); -// BigInteger wSum = W(n1, wOne, q).add(W(n2, wOne, q)).mod(q); -// BigInteger twoR = r.shiftLeft(1); //ECConstants.TWO.multiply(r); -// -// BigInteger root = twoR.modPow(q.subtract(ECConstants.TWO), q) -// .multiply(x).mod(q) -// .multiply(wSum).mod(q); -// -// return new Fp(q, root); } -// private static BigInteger W(BigInteger n, BigInteger wOne, BigInteger p) -// { -// if (n.equals(ECConstants.ONE)) -// { -// return wOne; -// } -// boolean isEven = !n.testBit(0); -// n = n.shiftRight(1);//divide(ECConstants.TWO); -// if (isEven) -// { -// BigInteger w = W(n, wOne, p); -// return w.multiply(w).subtract(ECConstants.TWO).mod(p); -// } -// BigInteger w1 = W(n.add(ECConstants.ONE), wOne, p); -// BigInteger w2 = W(n, wOne, p); -// return w1.multiply(w2).subtract(wOne).mod(p); -// } -// -// private BigInteger WOne(BigInteger r, BigInteger x, BigInteger p) -// { -// return r.multiply(r).multiply(x.modPow(q.subtract(ECConstants.TWO), q)).subtract(ECConstants.TWO).mod(p); -// } + private ECFieldElement checkSqrt(ECFieldElement z) + { + return z.square().equals(this) ? z : null; + } private BigInteger[] lucasSequence( BigInteger P, BigInteger Q, BigInteger k) { + // TODO Research and apply "common-multiplicand multiplication here" + int n = k.bitLength(); int s = k.getLowestSetBit(); + // assert k.testBit(s); + BigInteger Uh = ECConstants.ONE; BigInteger Vl = ECConstants.TWO; BigInteger Vh = P; @@ -405,6 +378,35 @@ public abstract class ECFieldElement return _2x; } + protected BigInteger modHalf(BigInteger x) + { + if (x.testBit(0)) + { + x = q.add(x); + } + return x.shiftRight(1); + } + + protected BigInteger modHalfAbs(BigInteger x) + { + if (x.testBit(0)) + { + x = q.subtract(x); + } + return x.shiftRight(1); + } + + protected BigInteger modInverse(BigInteger x) + { + int bits = getFieldSize(); + int len = (bits + 31) >> 5; + int[] p = Nat.fromBigInteger(bits, q); + int[] n = Nat.fromBigInteger(bits, x); + int[] z = Nat.create(len); + Mod.invert(p, n, z); + return Nat.toBigInteger(len, z); + } + protected BigInteger modMult(BigInteger x1, BigInteger x2) { return modReduce(x1.multiply(x2)); @@ -412,44 +414,20 @@ public abstract class ECFieldElement protected BigInteger modReduce(BigInteger x) { -// if (naf != null) -// { -// int last = naf.length - 1; -// int bits = naf[last]; -// while (x.bitLength() > (bits + 1)) -// { -// BigInteger u = x.shiftRight(bits); -// BigInteger v = x.subtract(u.shiftLeft(bits)); -// -// x = v; -// -// for (int i = 0; i < last; ++i) -// { -// int ni = naf[i]; -// if (ni < 0) -// { -// x = x.add(u.shiftLeft(~ni)); -// } -// else -// { -// x = x.subtract(u.shiftLeft(ni)); -// } -// } -// } -// while (x.compareTo(q) >= 0) -// { -// x = x.subtract(q); -// } -// } -// else if (r != null) { + boolean negative = x.signum() < 0; + if (negative) + { + x = x.abs(); + } int qLen = q.bitLength(); + boolean rIsOne = r.equals(ECConstants.ONE); while (x.bitLength() > (qLen + 1)) { BigInteger u = x.shiftRight(qLen); BigInteger v = x.subtract(u.shiftLeft(qLen)); - if (!r.equals(ONE)) + if (!rIsOne) { u = u.multiply(r); } @@ -459,6 +437,10 @@ public abstract class ECFieldElement { x = x.subtract(q); } + if (negative && x.signum() != 0) + { + x = q.subtract(x); + } } else { @@ -467,6 +449,16 @@ public abstract class ECFieldElement return x; } + protected BigInteger modSubtract(BigInteger x1, BigInteger x2) + { + BigInteger x3 = x1.subtract(x2); + if (x3.signum() < 0) + { + x3 = x3.add(q); + } + return x3; + } + public boolean equals(Object other) { if (other == this) @@ -489,467 +481,6 @@ public abstract class ECFieldElement } } -// /** -// * Class representing the Elements of the finite field -// * <code>F<sub>2<sup>m</sup></sub></code> in polynomial basis (PB) -// * representation. Both trinomial (TPB) and pentanomial (PPB) polynomial -// * basis representations are supported. Gaussian normal basis (GNB) -// * representation is not supported. -// */ -// public static class F2m extends ECFieldElement -// { -// BigInteger x; -// -// /** -// * Indicates gaussian normal basis representation (GNB). Number chosen -// * according to X9.62. GNB is not implemented at present. -// */ -// public static final int GNB = 1; -// -// /** -// * Indicates trinomial basis representation (TPB). Number chosen -// * according to X9.62. -// */ -// public static final int TPB = 2; -// -// /** -// * Indicates pentanomial basis representation (PPB). Number chosen -// * according to X9.62. -// */ -// public static final int PPB = 3; -// -// /** -// * TPB or PPB. -// */ -// private int representation; -// -// /** -// * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>. -// */ -// private int m; -// -// /** -// * TPB: The integer <code>k</code> where <code>x<sup>m</sup> + -// * x<sup>k</sup> + 1</code> represents the reduction polynomial -// * <code>f(z)</code>.<br> -// * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> + -// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> -// * represents the reduction polynomial <code>f(z)</code>.<br> -// */ -// private int k1; -// -// /** -// * TPB: Always set to <code>0</code><br> -// * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> + -// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> -// * represents the reduction polynomial <code>f(z)</code>.<br> -// */ -// private int k2; -// -// /** -// * TPB: Always set to <code>0</code><br> -// * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> + -// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> -// * represents the reduction polynomial <code>f(z)</code>.<br> -// */ -// private int k3; -// -// /** -// * Constructor for PPB. -// * @param m The exponent <code>m</code> of -// * <code>F<sub>2<sup>m</sup></sub></code>. -// * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> + -// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> -// * represents the reduction polynomial <code>f(z)</code>. -// * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> + -// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> -// * represents the reduction polynomial <code>f(z)</code>. -// * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> + -// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> -// * represents the reduction polynomial <code>f(z)</code>. -// * @param x The BigInteger representing the value of the field element. -// */ -// public F2m( -// int m, -// int k1, -// int k2, -// int k3, -// BigInteger x) -// { -//// super(x); -// this.x = x; -// -// if ((k2 == 0) && (k3 == 0)) -// { -// this.representation = TPB; -// } -// else -// { -// if (k2 >= k3) -// { -// throw new IllegalArgumentException( -// "k2 must be smaller than k3"); -// } -// if (k2 <= 0) -// { -// throw new IllegalArgumentException( -// "k2 must be larger than 0"); -// } -// this.representation = PPB; -// } -// -// if (x.signum() < 0) -// { -// throw new IllegalArgumentException("x value cannot be negative"); -// } -// -// this.m = m; -// this.k1 = k1; -// this.k2 = k2; -// this.k3 = k3; -// } -// -// /** -// * Constructor for TPB. -// * @param m The exponent <code>m</code> of -// * <code>F<sub>2<sup>m</sup></sub></code>. -// * @param k The integer <code>k</code> where <code>x<sup>m</sup> + -// * x<sup>k</sup> + 1</code> represents the reduction -// * polynomial <code>f(z)</code>. -// * @param x The BigInteger representing the value of the field element. -// */ -// public F2m(int m, int k, BigInteger x) -// { -// // Set k1 to k, and set k2 and k3 to 0 -// this(m, k, 0, 0, x); -// } -// -// public BigInteger toBigInteger() -// { -// return x; -// } -// -// public String getFieldName() -// { -// return "F2m"; -// } -// -// public int getFieldSize() -// { -// return m; -// } -// -// /** -// * Checks, if the ECFieldElements <code>a</code> and <code>b</code> -// * are elements of the same field <code>F<sub>2<sup>m</sup></sub></code> -// * (having the same representation). -// * @param a field element. -// * @param b field element to be compared. -// * @throws IllegalArgumentException if <code>a</code> and <code>b</code> -// * are not elements of the same field -// * <code>F<sub>2<sup>m</sup></sub></code> (having the same -// * representation). -// */ -// public static void checkFieldElements( -// ECFieldElement a, -// ECFieldElement b) -// { -// if ((!(a instanceof F2m)) || (!(b instanceof F2m))) -// { -// throw new IllegalArgumentException("Field elements are not " -// + "both instances of ECFieldElement.F2m"); -// } -// -// if ((a.toBigInteger().signum() < 0) || (b.toBigInteger().signum() < 0)) -// { -// throw new IllegalArgumentException( -// "x value may not be negative"); -// } -// -// ECFieldElement.F2m aF2m = (ECFieldElement.F2m)a; -// ECFieldElement.F2m bF2m = (ECFieldElement.F2m)b; -// -// if ((aF2m.m != bF2m.m) || (aF2m.k1 != bF2m.k1) -// || (aF2m.k2 != bF2m.k2) || (aF2m.k3 != bF2m.k3)) -// { -// throw new IllegalArgumentException("Field elements are not " -// + "elements of the same field F2m"); -// } -// -// if (aF2m.representation != bF2m.representation) -// { -// // Should never occur -// throw new IllegalArgumentException( -// "One of the field " -// + "elements are not elements has incorrect representation"); -// } -// } -// -// /** -// * Computes <code>z * a(z) mod f(z)</code>, where <code>f(z)</code> is -// * the reduction polynomial of <code>this</code>. -// * @param a The polynomial <code>a(z)</code> to be multiplied by -// * <code>z mod f(z)</code>. -// * @return <code>z * a(z) mod f(z)</code> -// */ -// private BigInteger multZModF(final BigInteger a) -// { -// // Left-shift of a(z) -// BigInteger az = a.shiftLeft(1); -// if (az.testBit(this.m)) -// { -// // If the coefficient of z^m in a(z) equals 1, reduction -// // modulo f(z) is performed: Add f(z) to to a(z): -// // Step 1: Unset mth coeffient of a(z) -// az = az.clearBit(this.m); -// -// // Step 2: Add r(z) to a(z), where r(z) is defined as -// // f(z) = z^m + r(z), and k1, k2, k3 are the positions of -// // the non-zero coefficients in r(z) -// az = az.flipBit(0); -// az = az.flipBit(this.k1); -// if (this.representation == PPB) -// { -// az = az.flipBit(this.k2); -// az = az.flipBit(this.k3); -// } -// } -// return az; -// } -// -// public ECFieldElement add(final ECFieldElement b) -// { -// // No check performed here for performance reasons. Instead the -// // elements involved are checked in ECPoint.F2m -// // checkFieldElements(this, b); -// if (b.toBigInteger().signum() == 0) -// { -// return this; -// } -// -// return new F2m(this.m, this.k1, this.k2, this.k3, this.x.xor(b.toBigInteger())); -// } -// -// public ECFieldElement subtract(final ECFieldElement b) -// { -// // Addition and subtraction are the same in F2m -// return add(b); -// } -// -// -// public ECFieldElement multiply(final ECFieldElement b) -// { -// // Left-to-right shift-and-add field multiplication in F2m -// // Input: Binary polynomials a(z) and b(z) of degree at most m-1 -// // Output: c(z) = a(z) * b(z) mod f(z) -// -// // No check performed here for performance reasons. Instead the -// // elements involved are checked in ECPoint.F2m -// // checkFieldElements(this, b); -// final BigInteger az = this.x; -// BigInteger bz = b.toBigInteger(); -// BigInteger cz; -// -// // Compute c(z) = a(z) * b(z) mod f(z) -// if (az.testBit(0)) -// { -// cz = bz; -// } -// else -// { -// cz = ECConstants.ZERO; -// } -// -// for (int i = 1; i < this.m; i++) -// { -// // b(z) := z * b(z) mod f(z) -// bz = multZModF(bz); -// -// if (az.testBit(i)) -// { -// // If the coefficient of x^i in a(z) equals 1, b(z) is added -// // to c(z) -// cz = cz.xor(bz); -// } -// } -// return new ECFieldElement.F2m(m, this.k1, this.k2, this.k3, cz); -// } -// -// -// public ECFieldElement divide(final ECFieldElement b) -// { -// // There may be more efficient implementations -// ECFieldElement bInv = b.invert(); -// return multiply(bInv); -// } -// -// public ECFieldElement negate() -// { -// // -x == x holds for all x in F2m -// return this; -// } -// -// public ECFieldElement square() -// { -// // Naive implementation, can probably be speeded up using modular -// // reduction -// return multiply(this); -// } -// -// public ECFieldElement invert() -// { -// // Inversion in F2m using the extended Euclidean algorithm -// // Input: A nonzero polynomial a(z) of degree at most m-1 -// // Output: a(z)^(-1) mod f(z) -// -// // u(z) := a(z) -// BigInteger uz = this.x; -// if (uz.signum() <= 0) -// { -// throw new ArithmeticException("x is zero or negative, " + -// "inversion is impossible"); -// } -// -// // v(z) := f(z) -// BigInteger vz = ECConstants.ZERO.setBit(m); -// vz = vz.setBit(0); -// vz = vz.setBit(this.k1); -// if (this.representation == PPB) -// { -// vz = vz.setBit(this.k2); -// vz = vz.setBit(this.k3); -// } -// -// // g1(z) := 1, g2(z) := 0 -// BigInteger g1z = ECConstants.ONE; -// BigInteger g2z = ECConstants.ZERO; -// -// // while u != 1 -// while (!(uz.equals(ECConstants.ZERO))) -// { -// // j := deg(u(z)) - deg(v(z)) -// int j = uz.bitLength() - vz.bitLength(); -// -// // If j < 0 then: u(z) <-> v(z), g1(z) <-> g2(z), j := -j -// if (j < 0) -// { -// final BigInteger uzCopy = uz; -// uz = vz; -// vz = uzCopy; -// -// final BigInteger g1zCopy = g1z; -// g1z = g2z; -// g2z = g1zCopy; -// -// j = -j; -// } -// -// // u(z) := u(z) + z^j * v(z) -// // Note, that no reduction modulo f(z) is required, because -// // deg(u(z) + z^j * v(z)) <= max(deg(u(z)), j + deg(v(z))) -// // = max(deg(u(z)), deg(u(z)) - deg(v(z)) + deg(v(z)) -// // = deg(u(z)) -// uz = uz.xor(vz.shiftLeft(j)); -// -// // g1(z) := g1(z) + z^j * g2(z) -// g1z = g1z.xor(g2z.shiftLeft(j)); -//// if (g1z.bitLength() > this.m) { -//// throw new ArithmeticException( -//// "deg(g1z) >= m, g1z = " + g1z.toString(16)); -//// } -// } -// return new ECFieldElement.F2m( -// this.m, this.k1, this.k2, this.k3, g2z); -// } -// -// public ECFieldElement sqrt() -// { -// throw new RuntimeException("Not implemented"); -// } -// -// /** -// * @return the representation of the field -// * <code>F<sub>2<sup>m</sup></sub></code>, either of -// * TPB (trinomial -// * basis representation) or -// * PPB (pentanomial -// * basis representation). -// */ -// public int getRepresentation() -// { -// return this.representation; -// } -// -// /** -// * @return the degree <code>m</code> of the reduction polynomial -// * <code>f(z)</code>. -// */ -// public int getM() -// { -// return this.m; -// } -// -// /** -// * @return TPB: The integer <code>k</code> where <code>x<sup>m</sup> + -// * x<sup>k</sup> + 1</code> represents the reduction polynomial -// * <code>f(z)</code>.<br> -// * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> + -// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> -// * represents the reduction polynomial <code>f(z)</code>.<br> -// */ -// public int getK1() -// { -// return this.k1; -// } -// -// /** -// * @return TPB: Always returns <code>0</code><br> -// * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> + -// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> -// * represents the reduction polynomial <code>f(z)</code>.<br> -// */ -// public int getK2() -// { -// return this.k2; -// } -// -// /** -// * @return TPB: Always set to <code>0</code><br> -// * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> + -// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> -// * represents the reduction polynomial <code>f(z)</code>.<br> -// */ -// public int getK3() -// { -// return this.k3; -// } -// -// public boolean equals(Object anObject) -// { -// if (anObject == this) -// { -// return true; -// } -// -// if (!(anObject instanceof ECFieldElement.F2m)) -// { -// return false; -// } -// -// ECFieldElement.F2m b = (ECFieldElement.F2m)anObject; -// -// return ((this.m == b.m) && (this.k1 == b.k1) && (this.k2 == b.k2) -// && (this.k3 == b.k3) -// && (this.representation == b.representation) -// && (this.x.equals(b.x))); -// } -// -// public int hashCode() -// { -// return x.hashCode() ^ m ^ k1 ^ k2 ^ k3; -// } -// } - /** * Class representing the Elements of the finite field * <code>F<sub>2<sup>m</sup></sub></code> in polynomial basis (PB) @@ -987,32 +518,6 @@ public abstract class ECFieldElement */ private int m; -// /** -// * TPB: The integer <code>k</code> where <code>x<sup>m</sup> + -// * x<sup>k</sup> + 1</code> represents the reduction polynomial -// * <code>f(z)</code>.<br> -// * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> + -// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> -// * represents the reduction polynomial <code>f(z)</code>.<br> -// */ -// private int k1; -// -// /** -// * TPB: Always set to <code>0</code><br> -// * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> + -// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> -// * represents the reduction polynomial <code>f(z)</code>.<br> -// */ -// private int k2; -// -// /** -// * TPB: Always set to <code>0</code><br> -// * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> + -// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> -// * represents the reduction polynomial <code>f(z)</code>.<br> -// */ -// private int k3; - private int[] ks; /** @@ -1097,6 +602,11 @@ public abstract class ECFieldElement return x.degree(); } + public boolean isOne() + { + return x.isOne(); + } + public boolean isZero() { return x.isZero(); @@ -1192,6 +702,29 @@ public abstract class ECFieldElement return new F2m(m, ks, x.modMultiply(((F2m)b).x, m, ks)); } + public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return multiplyPlusProduct(b, x, y); + } + + public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + LongArray ax = this.x, bx = ((F2m)b).x, xx = ((F2m)x).x, yx = ((F2m)y).x; + + LongArray ab = ax.multiply(bx, m, ks); + LongArray xy = xx.multiply(yx, m, ks); + + if (ab == ax || ab == bx) + { + ab = (LongArray)ab.clone(); + } + + ab.addShiftedByWords(xy, 0); + ab.reduce(m, ks); + + return new F2m(m, ks, ab); + } + public ECFieldElement divide(final ECFieldElement b) { // There may be more efficient implementations @@ -1210,6 +743,29 @@ public abstract class ECFieldElement return new F2m(m, ks, x.modSquare(m, ks)); } + public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return squarePlusProduct(x, y); + } + + public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y) + { + LongArray ax = this.x, xx = ((F2m)x).x, yx = ((F2m)y).x; + + LongArray aa = ax.square(m, ks); + LongArray xy = xx.multiply(yx, m, ks); + + if (aa == ax) + { + aa = (LongArray)aa.clone(); + } + + aa.addShiftedByWords(xy, 0); + aa.reduce(m, ks); + + return new F2m(m, ks, aa); + } + public ECFieldElement invert() { return new ECFieldElement.F2m(this.m, this.ks, this.x.modInverse(m, ks)); @@ -1217,7 +773,14 @@ public abstract class ECFieldElement public ECFieldElement sqrt() { - throw new RuntimeException("Not implemented"); + LongArray x1 = this.x; + if (x1.isOne() || x1.isZero()) + { + return this; + } + + LongArray x2 = x1.modSquareN(m - 1, m, ks); + return new ECFieldElement.F2m(m, ks, x2); } /** diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java index 7f740e4..7cd04e4 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java @@ -1,6 +1,7 @@ package org.bouncycastle.math.ec; import java.math.BigInteger; +import java.util.Hashtable; /** * base class for points on elliptic curves. @@ -47,7 +48,8 @@ public abstract class ECPoint protected boolean withCompression; - protected PreCompInfo preCompInfo = null; + // Hashtable is (String -> PreCompInfo) + protected Hashtable preCompTable = null; protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y) { @@ -62,11 +64,26 @@ public abstract class ECPoint this.zs = zs; } + protected boolean satisfiesCofactor() + { + BigInteger h = curve.getCofactor(); + return h == null || h.equals(ECConstants.ONE) || !ECAlgorithms.referenceMultiply(this, h).isInfinity(); + } + + protected abstract boolean satisfiesCurveEquation(); + + public final ECPoint getDetachedPoint() + { + return normalize().detach(); + } + public ECCurve getCurve() { return curve; } + protected abstract ECPoint detach(); + protected int getCurveCoordinateSystem() { // Cope with null curve, most commonly used by implicitlyCa @@ -79,7 +96,7 @@ public abstract class ECPoint * Note: normalization can be expensive, this method is deprecated in favour * of caller-controlled normalization. * - * @deprecated Use getAffineXCoord, or normalize() and getXCoord(), instead + * @deprecated Use getAffineXCoord(), or normalize() and getXCoord(), instead */ public ECFieldElement getX() { @@ -93,7 +110,7 @@ public abstract class ECPoint * Note: normalization can be expensive, this method is deprecated in favour * of caller-controlled normalization. * - * @deprecated Use getAffineYCoord, or normalize() and getYCoord(), instead + * @deprecated Use getAffineYCoord(), or normalize() and getYCoord(), instead */ public ECFieldElement getY() { @@ -129,7 +146,7 @@ public abstract class ECPoint * * 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 + * affine values, or use getAffineXCoord() if you expect the point to already have been * normalized. * * @return the x-coordinate of this point @@ -144,7 +161,7 @@ public abstract class ECPoint * * 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 + * affine values, or use getAffineYCoord() if you expect the point to already have been * normalized. * * @return the y-coordinate of this point @@ -171,16 +188,21 @@ public abstract class ECPoint return copy; } - protected ECFieldElement getRawXCoord() + protected final ECFieldElement getRawXCoord() { return x; } - protected ECFieldElement getRawYCoord() + protected final ECFieldElement getRawYCoord() { return y; } + protected final ECFieldElement[] getRawZCoords() + { + return zs; + } + protected void checkNormalized() { if (!isNormalized()) @@ -196,7 +218,7 @@ public abstract class ECPoint return coord == ECCurve.COORD_AFFINE || coord == ECCurve.COORD_LAMBDA_AFFINE || isInfinity() - || zs[0].bitLength() == 1; + || zs[0].isOne(); } /** @@ -222,7 +244,7 @@ public abstract class ECPoint default: { ECFieldElement Z1 = getZCoord(0); - if (Z1.bitLength() == 1) + if (Z1.isOne()) { return this; } @@ -270,6 +292,46 @@ public abstract class ECPoint return this.withCompression; } + public boolean isValid() + { + if (isInfinity()) + { + return true; + } + + // TODO Sanity-check the field elements + + ECCurve curve = getCurve(); + if (curve != null) + { + if (!satisfiesCurveEquation()) + { + return false; + } + + if (!satisfiesCofactor()) + { + return false; + } + } + + return true; + } + + public ECPoint scaleX(ECFieldElement scale) + { + return isInfinity() + ? this + : getCurve().createRawPoint(getRawXCoord().multiply(scale), getRawYCoord(), getRawZCoords(), this.withCompression); + } + + public ECPoint scaleY(ECFieldElement scale) + { + return isInfinity() + ? this + : getCurve().createRawPoint(getRawXCoord(), getRawYCoord().multiply(scale), getRawZCoords(), this.withCompression); + } + public boolean equals(ECPoint other) { if (null == other) @@ -454,13 +516,84 @@ public abstract class ECPoint return this.getCurve().getMultiplier().multiply(this, k); } + public static abstract class AbstractFp extends ECPoint + { + protected AbstractFp(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + super(curve, x, y); + } + + protected AbstractFp(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs) + { + super(curve, x, y, zs); + } + + protected boolean getCompressionYTilde() + { + return this.getAffineYCoord().testBitZero(); + } + + protected boolean satisfiesCurveEquation() + { + ECFieldElement X = this.x, Y = this.y, A = curve.getA(), B = curve.getB(); + ECFieldElement lhs = Y.square(); + + switch (this.getCurveCoordinateSystem()) + { + case ECCurve.COORD_AFFINE: + break; + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Z = this.zs[0]; + if (!Z.isOne()) + { + ECFieldElement Z2 = Z.square(), Z3 = Z.multiply(Z2); + lhs = lhs.multiply(Z); + A = A.multiply(Z2); + B = B.multiply(Z3); + } + break; + } + case ECCurve.COORD_JACOBIAN: + case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: + case ECCurve.COORD_JACOBIAN_MODIFIED: + { + ECFieldElement Z = this.zs[0]; + if (!Z.isOne()) + { + ECFieldElement Z2 = Z.square(), Z4 = Z2.square(), Z6 = Z2.multiply(Z4); + A = A.multiply(Z4); + B = B.multiply(Z6); + } + break; + } + default: + throw new IllegalStateException("unsupported coordinate system"); + } + + ECFieldElement rhs = X.square().add(A).multiply(X).add(B); + return lhs.equals(rhs); + } + + public ECPoint subtract(ECPoint b) + { + if (b.isInfinity()) + { + return this; + } + + // Add -b + return this.add(b.negate()); + } + } + /** * Elliptic curve points over Fp */ - public static class Fp extends ECPoint + public static class Fp extends AbstractFp { /** - * Create a point which encodes with point compression. + * Create a point which encodes without point compression. * * @param curve the curve to use * @param x affine x co-ordinate @@ -474,7 +607,7 @@ public abstract class ECPoint } /** - * Create a point that encodes with or without point compresion. + * Create a point that encodes with or without point compression. * * @param curve the curve to use * @param x affine x co-ordinate @@ -487,7 +620,7 @@ public abstract class ECPoint { super(curve, x, y); - if ((x != null && y == null) || (x == null && y != null)) + if ((x == null) != (y == null)) { throw new IllegalArgumentException("Exactly one of the field elements is null"); } @@ -502,9 +635,9 @@ public abstract class ECPoint this.withCompression = withCompression; } - protected boolean getCompressionYTilde() + protected ECPoint detach() { - return this.getAffineYCoord().testBitZero(); + return new ECPoint.Fp(null, this.getAffineXCoord(), this.getAffineYCoord()); } public ECFieldElement getZCoord(int index) @@ -569,8 +702,8 @@ public abstract class ECPoint ECFieldElement Z1 = this.zs[0]; ECFieldElement Z2 = b.zs[0]; - boolean Z1IsOne = Z1.bitLength() == 1; - boolean Z2IsOne = Z2.bitLength() == 1; + boolean Z1IsOne = Z1.isOne(); + boolean Z2IsOne = Z2.isOne(); ECFieldElement u1 = Z1IsOne ? Y2 : Y2.multiply(Z1); ECFieldElement u2 = Z2IsOne ? Y1 : Y1.multiply(Z2); @@ -600,7 +733,7 @@ public abstract class ECPoint 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 Y3 = vSquaredV2.subtract(A).multiplyMinusProduct(u, u2, vCubed); ECFieldElement Z3 = vCubed.multiply(w); return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); @@ -612,7 +745,7 @@ public abstract class ECPoint ECFieldElement Z1 = this.zs[0]; ECFieldElement Z2 = b.zs[0]; - boolean Z1IsOne = Z1.bitLength() == 1; + boolean Z1IsOne = Z1.isOne(); ECFieldElement X3, Y3, Z3, Z3Squared = null; @@ -662,7 +795,7 @@ public abstract class ECPoint S2 = Z1Cubed.multiply(Y2); } - boolean Z2IsOne = Z2.bitLength() == 1; + boolean Z2IsOne = Z2.isOne(); ECFieldElement Z2Squared, U1, S1; if (Z2IsOne) { @@ -697,8 +830,8 @@ public abstract class ECPoint ECFieldElement V = HSquared.multiply(U1); X3 = R.square().add(G).subtract(two(V)); - Y3 = V.subtract(X3).multiply(R).subtract(S1.multiply(G)); - + Y3 = V.subtract(X3).multiplyMinusProduct(R, G, S1); + Z3 = H; if (!Z1IsOne) { @@ -735,6 +868,7 @@ public abstract class ECPoint return new ECPoint.Fp(curve, X3, Y3, zs, this.withCompression); } + default: { throw new IllegalStateException("unsupported coordinate system"); @@ -778,14 +912,13 @@ public abstract class ECPoint { ECFieldElement Z1 = this.zs[0]; - boolean Z1IsOne = Z1.bitLength() == 1; - ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square(); + boolean Z1IsOne = Z1.isOne(); // TODO Optimize for small negative a4 and -3 ECFieldElement w = curve.getA(); - if (!Z1IsOne) + if (!w.isZero() && !Z1IsOne) { - w = w.multiply(Z1Squared); + w = w.multiply(Z1.square()); } w = w.add(three(X1.square())); @@ -795,9 +928,11 @@ public abstract class ECPoint 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 _2s = two(s); + ECFieldElement X3 = h.multiply(_2s); + ECFieldElement _2t = two(t); + ECFieldElement Y3 = _4B.subtract(h).multiply(w).subtract(two(_2t.square())); + ECFieldElement _4sSquared = Z1IsOne ? two(_2t) : _2s.square(); ECFieldElement Z3 = two(_4sSquared).multiply(s); return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); @@ -807,8 +942,7 @@ public abstract class ECPoint { ECFieldElement Z1 = this.zs[0]; - boolean Z1IsOne = Z1.bitLength() == 1; - ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square(); + boolean Z1IsOne = Z1.isOne(); ECFieldElement Y1Squared = Y1.square(); ECFieldElement T = Y1Squared.square(); @@ -819,6 +953,7 @@ public abstract class ECPoint ECFieldElement M, S; if (a4Neg.toBigInteger().equals(BigInteger.valueOf(3))) { + ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square(); M = three(X1.add(Z1Squared).multiply(X1.subtract(Z1Squared))); S = four(Y1Squared.multiply(X1)); } @@ -830,8 +965,9 @@ public abstract class ECPoint { M = M.add(a4); } - else + else if (!a4.isZero()) { + ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square(); ECFieldElement Z1Pow4 = Z1Squared.square(); if (a4Neg.bitLength() < a4.bitLength()) { @@ -842,7 +978,8 @@ public abstract class ECPoint M = M.add(Z1Pow4.multiply(a4)); } } - S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T)); +// S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T)); + S = four(X1.multiply(Y1Squared)); } ECFieldElement X3 = M.square().subtract(two(S)); @@ -951,7 +1088,13 @@ public abstract class ECPoint public ECPoint threeTimes() { - if (this.isInfinity() || this.y.isZero()) + if (this.isInfinity()) + { + return this; + } + + ECFieldElement Y1 = this.y; + if (Y1.isZero()) { return this; } @@ -963,7 +1106,7 @@ public abstract class ECPoint { case ECCurve.COORD_AFFINE: { - ECFieldElement X1 = this.x, Y1 = this.y; + ECFieldElement X1 = this.x; ECFieldElement _2Y1 = two(Y1); ECFieldElement X = _2Y1.square(); @@ -997,6 +1140,98 @@ public abstract class ECPoint } } + public ECPoint timesPow2(int e) + { + if (e < 0) + { + throw new IllegalArgumentException("'e' cannot be negative"); + } + if (e == 0 || this.isInfinity()) + { + return this; + } + if (e == 1) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement Y1 = this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + int coord = curve.getCoordinateSystem(); + + ECFieldElement W1 = curve.getA(); + ECFieldElement X1 = this.x; + ECFieldElement Z1 = this.zs.length < 1 ? curve.fromBigInteger(ECConstants.ONE) : this.zs[0]; + + if (!Z1.isOne()) + { + switch (coord) + { + case ECCurve.COORD_HOMOGENEOUS: + ECFieldElement Z1Sq = Z1.square(); + X1 = X1.multiply(Z1); + Y1 = Y1.multiply(Z1Sq); + W1 = calculateJacobianModifiedW(Z1, Z1Sq); + break; + case ECCurve.COORD_JACOBIAN: + W1 = calculateJacobianModifiedW(Z1, null); + break; + case ECCurve.COORD_JACOBIAN_MODIFIED: + W1 = getJacobianModifiedW(); + break; + } + } + + for (int i = 0; i < e; ++i) + { + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + ECFieldElement X1Squared = X1.square(); + ECFieldElement M = three(X1Squared); + ECFieldElement _2Y1 = two(Y1); + ECFieldElement _2Y1Squared = _2Y1.multiply(Y1); + ECFieldElement S = two(X1.multiply(_2Y1Squared)); + ECFieldElement _4T = _2Y1Squared.square(); + ECFieldElement _8T = two(_4T); + + if (!W1.isZero()) + { + M = M.add(W1); + W1 = two(_8T.multiply(W1)); + } + + X1 = M.square().subtract(two(S)); + Y1 = M.multiply(S.subtract(X1)).subtract(_8T); + Z1 = Z1.isOne() ? _2Y1 : _2Y1.multiply(Z1); + } + + switch (coord) + { + case ECCurve.COORD_AFFINE: + ECFieldElement zInv = Z1.invert(), zInv2 = zInv.square(), zInv3 = zInv2.multiply(zInv); + return new Fp(curve, X1.multiply(zInv2), Y1.multiply(zInv3), this.withCompression); + case ECCurve.COORD_HOMOGENEOUS: + X1 = X1.multiply(Z1); + Z1 = Z1.multiply(Z1.square()); + return new Fp(curve, X1, Y1, new ECFieldElement[]{ Z1 }, this.withCompression); + case ECCurve.COORD_JACOBIAN: + return new Fp(curve, X1, Y1, new ECFieldElement[]{ Z1 }, this.withCompression); + case ECCurve.COORD_JACOBIAN_MODIFIED: + return new Fp(curve, X1, Y1, new ECFieldElement[]{ Z1, W1 }, this.withCompression); + default: + throw new IllegalStateException("unsupported coordinate system"); + } + } + protected ECFieldElement two(ECFieldElement x) { return x.add(x); @@ -1027,18 +1262,6 @@ public abstract class ECPoint 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()) @@ -1059,13 +1282,18 @@ public abstract class ECPoint protected ECFieldElement calculateJacobianModifiedW(ECFieldElement Z, ECFieldElement ZSquared) { + ECFieldElement a4 = this.getCurve().getA(); + if (a4.isZero() || Z.isOne()) + { + return a4; + } + if (ZSquared == null) { ZSquared = Z.square(); } ECFieldElement W = ZSquared.square(); - ECFieldElement a4 = this.getCurve().getA(); ECFieldElement a4Neg = a4.negate(); if (a4Neg.bitLength() < a4.bitLength()) { @@ -1095,23 +1323,105 @@ public abstract class ECPoint 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 _2Y1 = two(Y1); + ECFieldElement _2Y1Squared = _2Y1.multiply(Y1); + ECFieldElement S = two(X1.multiply(_2Y1Squared)); ECFieldElement X3 = M.square().subtract(two(S)); - ECFieldElement _8T = eight(T); + ECFieldElement _4T = _2Y1Squared.square(); + ECFieldElement _8T = two(_4T); 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)); + ECFieldElement Z3 = Z1.isOne() ? _2Y1 : _2Y1.multiply(Z1); return new ECPoint.Fp(this.getCurve(), X3, Y3, new ECFieldElement[]{ Z3, W3 }, this.withCompression); } } + public static abstract class AbstractF2m extends ECPoint + { + protected AbstractF2m(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + super(curve, x, y); + } + + protected AbstractF2m(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs) + { + super(curve, x, y, zs); + } + + protected boolean satisfiesCurveEquation() + { + ECCurve curve = this.getCurve(); + ECFieldElement X = this.x, A = curve.getA(), B = curve.getB(); + + int coord = curve.getCoordinateSystem(); + if (coord == ECCurve.COORD_LAMBDA_PROJECTIVE) + { + ECFieldElement Z = this.zs[0]; + boolean ZIsOne = Z.isOne(); + + if (X.isZero()) + { + // NOTE: For x == 0, we expect the affine-y instead of the lambda-y + ECFieldElement Y = this.y; + ECFieldElement lhs = Y.square(), rhs = B; + if (!ZIsOne) + { + rhs = rhs.multiply(Z.square()); + } + return lhs.equals(rhs); + } + + ECFieldElement L = this.y, X2 = X.square(); + ECFieldElement lhs, rhs; + if (ZIsOne) + { + lhs = L.square().add(L).add(A); + rhs = X2.square().add(B); + } + else + { + ECFieldElement Z2 = Z.square(), Z4 = Z2.square(); + lhs = L.add(Z).multiplyPlusProduct(L, A, Z2); + // TODO If sqrt(b) is precomputed this can be simplified to a single square + rhs = X2.squarePlusProduct(B, Z4); + } + lhs = lhs.multiply(X2); + return lhs.equals(rhs); + } + + ECFieldElement Y = this.y; + ECFieldElement lhs = Y.add(X).multiply(Y); + + switch (coord) + { + case ECCurve.COORD_AFFINE: + break; + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Z = this.zs[0]; + if (!Z.isOne()) + { + ECFieldElement Z2 = Z.square(), Z3 = Z.multiply(Z2); + lhs = lhs.multiply(Z); + A = A.multiply(Z); + B = B.multiply(Z3); + } + break; + } + default: + throw new IllegalStateException("unsupported coordinate system"); + } + + ECFieldElement rhs = X.add(A).multiply(X.square()).add(B); + return lhs.equals(rhs); + } + } + /** * Elliptic curve points over F2m */ - public static class F2m extends ECPoint + public static class F2m extends AbstractF2m { /** * @param curve base curve @@ -1137,7 +1447,7 @@ public abstract class ECPoint { super(curve, x, y); - if ((x != null && y == null) || (x == null && y != null)) + if ((x == null) != (y == null)) { throw new IllegalArgumentException("Exactly one of the field elements is null"); } @@ -1168,6 +1478,11 @@ public abstract class ECPoint // checkCurveEquation(); } + protected ECPoint detach() + { + return new ECPoint.F2m(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK + } + public ECFieldElement getYCoord() { int coord = this.getCurveCoordinateSystem(); @@ -1177,19 +1492,19 @@ public abstract class ECPoint case ECCurve.COORD_LAMBDA_AFFINE: case ECCurve.COORD_LAMBDA_PROJECTIVE: { - // TODO The X == 0 stuff needs further thought - if (this.isInfinity() || x.isZero()) + ECFieldElement X = x, L = y; + + if (this.isInfinity() || X.isZero()) { - return y; + return L; } // 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); + ECFieldElement Y = L.add(X).multiply(X); if (ECCurve.COORD_LAMBDA_PROJECTIVE == coord) { ECFieldElement Z = zs[0]; - if (Z.bitLength() != 1) + if (!Z.isOne()) { Y = Y.divide(Z); } @@ -1203,6 +1518,74 @@ public abstract class ECPoint } } + public ECPoint scaleX(ECFieldElement scale) + { + if (this.isInfinity()) + { + return this; + } + + int coord = this.getCurveCoordinateSystem(); + + switch (coord) + { + case ECCurve.COORD_LAMBDA_AFFINE: + { + // Y is actually Lambda (X + Y/X) here + ECFieldElement X = this.getRawXCoord(), L = this.getRawYCoord(); // earlier JDK + + ECFieldElement X2 = X.multiply(scale); + ECFieldElement L2 = L.add(X).divide(scale).add(X2); + + return this.getCurve().createRawPoint(X, L2, this.getRawZCoords(), this.withCompression); // earlier JDK + } + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + // Y is actually Lambda (X + Y/X) here + ECFieldElement X = this.getRawXCoord(), L = this.getRawYCoord(), Z = this.getRawZCoords()[0]; // earlier JDK + + // We scale the Z coordinate also, to avoid an inversion + ECFieldElement X2 = X.multiply(scale.square()); + ECFieldElement L2 = L.add(X).add(X2); + ECFieldElement Z2 = Z.multiply(scale); + + return this.getCurve().createRawPoint(X2, L2, new ECFieldElement[]{ Z2 }, this.withCompression); // earlier JDK + } + default: + { + return super.scaleX(scale); + } + } + } + + public ECPoint scaleY(ECFieldElement scale) + { + if (this.isInfinity()) + { + return this; + } + + int coord = this.getCurveCoordinateSystem(); + + switch (coord) + { + case ECCurve.COORD_LAMBDA_AFFINE: + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + ECFieldElement X = this.getRawXCoord(), L = this.getRawYCoord(); // earlier JDK + + // Y is actually Lambda (X + Y/X) here + ECFieldElement L2 = L.add(X).multiply(scale).add(X); + + return this.getCurve().createRawPoint(X, L2, this.getRawZCoords(), this.withCompression); // earlier JDK + } + default: + { + return super.scaleY(scale); + } + } + } + protected boolean getCompressionYTilde() { ECFieldElement X = this.getRawXCoord(); @@ -1219,7 +1602,7 @@ public abstract class ECPoint case ECCurve.COORD_LAMBDA_PROJECTIVE: { // Y is actually Lambda (X + Y/X) here - return Y.subtract(X).testBitZero(); + return Y.testBitZero() != X.testBitZero(); } default: { @@ -1289,9 +1672,10 @@ public abstract class ECPoint ECFieldElement Y1 = this.y; ECFieldElement Y2 = b.y; - if (X1.equals(X2)) + ECFieldElement dx = X1.add(X2), dy = Y1.add(Y2); + if (dx.isZero()) { - if (Y1.equals(Y2)) + if (dy.isZero()) { return (ECPoint.F2m)twice(); } @@ -1299,10 +1683,9 @@ public abstract class ECPoint return (ECPoint.F2m)curve.getInfinity(); } - ECFieldElement sumX = X1.add(X2); - ECFieldElement L = Y1.add(Y2).divide(sumX); + ECFieldElement L = dy.divide(dx); - ECFieldElement X3 = L.square().add(L).add(sumX).add(curve.getA()); + ECFieldElement X3 = L.square().add(L).add(dx).add(curve.getA()); ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); return new ECPoint.F2m(curve, X3, Y3, this.withCompression); @@ -1312,18 +1695,18 @@ public abstract class ECPoint ECFieldElement Y1 = this.y, Z1 = this.zs[0]; ECFieldElement Y2 = b.y, Z2 = b.zs[0]; - boolean Z2IsOne = Z2.bitLength() == 1; + boolean Z2IsOne = Z2.isOne(); - ECFieldElement U1 = Z1.multiply(Y2); + ECFieldElement U1 = Z1.multiply(Y2); ECFieldElement U2 = Z2IsOne ? Y1 : Y1.multiply(Z2); - ECFieldElement U = U1.subtract(U2); + ECFieldElement U = U1.add(U2); ECFieldElement V1 = Z1.multiply(X2); ECFieldElement V2 = Z2IsOne ? X1 : X1.multiply(Z2); - ECFieldElement V = V1.subtract(V2); + ECFieldElement V = V1.add(V2); - if (V1.equals(V2)) + if (V.isZero()) { - if (U1.equals(U2)) + if (U.isZero()) { return (ECPoint.F2m)twice(); } @@ -1331,14 +1714,16 @@ public abstract class ECPoint return (ECPoint.F2m)curve.getInfinity(); } - ECFieldElement VSq = V.square(); + ECFieldElement VSq = V.square(); + ECFieldElement VCu = VSq.multiply(V); 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 uv = U.add(V); + ECFieldElement A = uv.multiplyPlusProduct(U, VSq, curve.getA()).multiply(W).add(VCu); 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); + ECFieldElement Y3 = U.multiplyPlusProduct(X1, V, Y1).multiplyPlusProduct(VSqZ2, uv, A); + ECFieldElement Z3 = VCu.multiply(W); return new ECPoint.F2m(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); } @@ -1346,13 +1731,18 @@ public abstract class ECPoint { if (X1.isZero()) { + if (X2.isZero()) + { + return (ECPoint.F2m)curve.getInfinity(); + } + 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; + boolean Z1IsOne = Z1.isOne(); ECFieldElement U2 = X2, S2 = L2; if (!Z1IsOne) { @@ -1360,7 +1750,7 @@ public abstract class ECPoint S2 = S2.multiply(Z1); } - boolean Z2IsOne = Z2.bitLength() == 1; + boolean Z2IsOne = Z2.isOne(); ECFieldElement U1 = X1, S1 = L1; if (!Z2IsOne) { @@ -1385,13 +1775,21 @@ public abstract class ECPoint if (X2.isZero()) { // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); - ECFieldElement Y1 = getYCoord(), Y2 = L2; + ECFieldElement Y2 = L2; ECFieldElement L = Y1.add(Y2).divide(X1); X3 = L.square().add(L).add(X1).add(curve.getA()); + if (X3.isZero()) + { + return new ECPoint.F2m(curve, X3, curve.getB().sqrt(), this.withCompression); + } + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); - L3 = X3.isZero() ? Y3 : Y3.divide(X3).add(X3); + L3 = Y3.divide(X3).add(X3); Z3 = curve.fromBigInteger(ECConstants.ONE); } else @@ -1400,14 +1798,20 @@ public abstract class ECPoint ECFieldElement AU1 = A.multiply(U1); ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { + return new ECPoint.F2m(curve, X3, curve.getB().sqrt(), this.withCompression); + } + 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))); + L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1)); Z3 = ABZ2; if (!Z1IsOne) @@ -1514,7 +1918,7 @@ public abstract class ECPoint 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())); + ECFieldElement Y3 = X1.squarePlusProduct(X3, L1.addOne()); return new ECPoint.F2m(curve, X3, Y3, this.withCompression); } @@ -1522,7 +1926,7 @@ public abstract class ECPoint { ECFieldElement Y1 = this.y, Z1 = this.zs[0]; - boolean Z1IsOne = Z1.bitLength() == 1; + boolean Z1IsOne = Z1.isOne(); ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); ECFieldElement Y1Z1 = Z1IsOne ? Y1 : Y1.multiply(Z1); @@ -1530,10 +1934,11 @@ public abstract class ECPoint 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 sv = S.add(V); + ECFieldElement h = sv.multiplyPlusProduct(S, vSquared, curve.getA()); ECFieldElement X3 = V.multiply(h); - ECFieldElement Y3 = h.multiply(S.add(V)).add(X1Sq.square().multiply(V)); + ECFieldElement Y3 = X1Sq.square().multiplyPlusProduct(V, h, sv); ECFieldElement Z3 = V.multiply(vSquared); return new ECPoint.F2m(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); @@ -1542,12 +1947,16 @@ public abstract class ECPoint { ECFieldElement L1 = this.y, Z1 = this.zs[0]; - boolean Z1IsOne = Z1.bitLength() == 1; + boolean Z1IsOne = Z1.isOne(); 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); + if (T.isZero()) + { + return new ECPoint.F2m(curve, T, curve.getB().sqrt(), withCompression); + } ECFieldElement X3 = T.square(); ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); @@ -1557,14 +1966,30 @@ public abstract class ECPoint 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)); + ECFieldElement t2; + if (b.isOne()) + { + t2 = aZ1Sq.add(Z1Sq).square(); + } + else + { + // TODO Can be calculated with one square if we pre-compute sqrt(b) + t2 = aZ1Sq.squarePlusProduct(b, Z1Sq.square()); + } + L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2).add(X3); + if (a.isZero()) + { + L3 = L3.add(Z3); + } + else if (!a.isOne()) + { + L3 = L3.add(a.addOne().multiply(Z3)); + } } else { ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); - L3 = X1Z1.square().add(X3).add(T.multiply(L1Z1)).add(Z3); + L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); } return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); @@ -1604,7 +2029,7 @@ public abstract class ECPoint { // NOTE: twicePlus() only optimized for lambda-affine argument ECFieldElement X2 = b.x, Z2 = b.zs[0]; - if (X2.isZero() || Z2.bitLength() != 1) + if (X2.isZero() || !Z2.isOne()) { return twice().add(b); } @@ -1619,13 +2044,28 @@ public abstract class ECPoint 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 A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); ECFieldElement X2Z1Sq = X2.multiply(Z1Sq); ECFieldElement B = X2Z1Sq.add(T).square(); + if (B.isZero()) + { + if (A.isZero()) + { + return b.twice(); + } + + return curve.getInfinity(); + } + + if (A.isZero()) + { + return new ECPoint.F2m(curve, A, curve.getB().sqrt(), withCompression); + } + 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)); + ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3); return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); } @@ -1636,57 +2076,6 @@ public abstract class ECPoint } } - 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()) diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECPointMap.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECPointMap.java new file mode 100644 index 0000000..439e8da --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECPointMap.java @@ -0,0 +1,6 @@ +package org.bouncycastle.math.ec; + +public interface ECPointMap +{ + ECPoint map(ECPoint p); +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointCombMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointCombMultiplier.java new file mode 100644 index 0000000..9fe00b9 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointCombMultiplier.java @@ -0,0 +1,57 @@ +package org.bouncycastle.math.ec; + +import java.math.BigInteger; + +public class FixedPointCombMultiplier extends AbstractECMultiplier +{ + protected ECPoint multiplyPositive(ECPoint p, BigInteger k) + { + ECCurve c = p.getCurve(); + int size = FixedPointUtil.getCombSize(c); + + if (k.bitLength() > size) + { + /* + * TODO The comb works best when the scalars are less than the (possibly unknown) order. + * Still, if we want to handle larger scalars, we could allow customization of the comb + * size, or alternatively we could deal with the 'extra' bits either by running the comb + * multiple times as necessary, or by using an alternative multiplier as prelude. + */ + throw new IllegalStateException("fixed-point comb doesn't support scalars larger than the curve order"); + } + + int minWidth = getWidthForCombSize(size); + + FixedPointPreCompInfo info = FixedPointUtil.precompute(p, minWidth); + ECPoint[] lookupTable = info.getPreComp(); + int width = info.getWidth(); + + int d = (size + width - 1) / width; + + ECPoint R = c.getInfinity(); + + int top = d * width - 1; + for (int i = 0; i < d; ++i) + { + int index = 0; + + for (int j = top - i; j >= 0; j -= d) + { + index <<= 1; + if (k.testBit(j)) + { + index |= 1; + } + } + + R = R.twicePlus(lookupTable[index]); + } + + return R; + } + + protected int getWidthForCombSize(int combSize) + { + return combSize > 257 ? 6 : 5; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointPreCompInfo.java b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointPreCompInfo.java new file mode 100644 index 0000000..b7569aa --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointPreCompInfo.java @@ -0,0 +1,40 @@ +package org.bouncycastle.math.ec; + +/** + * Class holding precomputation data for fixed-point multiplications. + */ +public class FixedPointPreCompInfo implements PreCompInfo +{ + /** + * Array holding the precomputed <code>ECPoint</code>s used for a fixed + * point multiplication. + */ + protected ECPoint[] preComp = null; + + /** + * The width used for the precomputation. If a larger width precomputation + * is already available this may be larger than was requested, so calling + * code should refer to the actual width. + */ + protected int width = -1; + + public ECPoint[] getPreComp() + { + return preComp; + } + + public void setPreComp(ECPoint[] preComp) + { + this.preComp = preComp; + } + + public int getWidth() + { + return width; + } + + public void setWidth(int width) + { + this.width = width; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointUtil.java b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointUtil.java new file mode 100644 index 0000000..e4fbb8d --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointUtil.java @@ -0,0 +1,71 @@ +package org.bouncycastle.math.ec; + +import java.math.BigInteger; + +public class FixedPointUtil +{ + public static final String PRECOMP_NAME = "bc_fixed_point"; + + public static int getCombSize(ECCurve c) + { + BigInteger order = c.getOrder(); + return order == null ? c.getFieldSize() + 1 : order.bitLength(); + } + + public static FixedPointPreCompInfo getFixedPointPreCompInfo(PreCompInfo preCompInfo) + { + if ((preCompInfo != null) && (preCompInfo instanceof FixedPointPreCompInfo)) + { + return (FixedPointPreCompInfo)preCompInfo; + } + + return new FixedPointPreCompInfo(); + } + + public static FixedPointPreCompInfo precompute(ECPoint p, int minWidth) + { + ECCurve c = p.getCurve(); + + int n = 1 << minWidth; + FixedPointPreCompInfo info = getFixedPointPreCompInfo(c.getPreCompInfo(p, PRECOMP_NAME)); + ECPoint[] lookupTable = info.getPreComp(); + + if (lookupTable == null || lookupTable.length < n) + { + int bits = getCombSize(c); + int d = (bits + minWidth - 1) / minWidth; + + ECPoint[] pow2Table = new ECPoint[minWidth]; + pow2Table[0] = p; + for (int i = 1; i < minWidth; ++i) + { + pow2Table[i] = pow2Table[i - 1].timesPow2(d); + } + + c.normalizeAll(pow2Table); + + lookupTable = new ECPoint[n]; + lookupTable[0] = c.getInfinity(); + + for (int bit = minWidth - 1; bit >= 0; --bit) + { + ECPoint pow2 = pow2Table[bit]; + + int step = 1 << bit; + for (int i = step; i < n; i += (step << 1)) + { + lookupTable[i] = lookupTable[i - step].add(pow2); + } + } + + c.normalizeAll(lookupTable); + + info.setPreComp(lookupTable); + info.setWidth(minWidth); + + c.setPreCompInfo(p, PRECOMP_NAME, info); + } + + return info; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/GLVMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/GLVMultiplier.java new file mode 100644 index 0000000..09b8366 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/GLVMultiplier.java @@ -0,0 +1,42 @@ +package org.bouncycastle.math.ec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.endo.GLVEndomorphism; + +public class GLVMultiplier extends AbstractECMultiplier +{ + protected final ECCurve curve; + protected final GLVEndomorphism glvEndomorphism; + + public GLVMultiplier(ECCurve curve, GLVEndomorphism glvEndomorphism) + { + if (curve == null || curve.getOrder() == null) + { + throw new IllegalArgumentException("Need curve with known group order"); + } + + this.curve = curve; + this.glvEndomorphism = glvEndomorphism; + } + + protected ECPoint multiplyPositive(ECPoint p, BigInteger k) + { + if (!curve.equals(p.getCurve())) + { + throw new IllegalStateException(); + } + + BigInteger n = p.getCurve().getOrder(); + BigInteger[] ab = glvEndomorphism.decomposeScalar(k.mod(n)); + BigInteger a = ab[0], b = ab[1]; + + ECPointMap pointMap = glvEndomorphism.getPointMap(); + if (glvEndomorphism.hasEfficientPointMap()) + { + return ECAlgorithms.implShamirsTrickWNaf(p, a, pointMap, b); + } + + return ECAlgorithms.implShamirsTrickWNaf(p, a, pointMap.map(p), b); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/IntArray.java b/bcprov/src/main/java/org/bouncycastle/math/ec/IntArray.java deleted file mode 100644 index 34395a5..0000000 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/IntArray.java +++ /dev/null @@ -1,860 +0,0 @@ -package org.bouncycastle.math.ec; - -import org.bouncycastle.util.Arrays; - -import java.math.BigInteger; - -class IntArray -{ -// private static int DEINTERLEAVE_MASK = 0x55555555; - - /* - * This expands 8 bit indices into 16 bit contents, by inserting 0s between bits. - * In a binary field, this operation is the same as squaring an 8 bit number. - */ - private static final int[] INTERLEAVE_TABLE = new int[] { 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, - 0x0015, 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, 0x0100, 0x0101, 0x0104, 0x0105, 0x0110, - 0x0111, 0x0114, 0x0115, 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155, 0x0400, 0x0401, 0x0404, - 0x0405, 0x0410, 0x0411, 0x0414, 0x0415, 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455, 0x0500, - 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515, 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, - 0x0555, 0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015, 0x1040, 0x1041, 0x1044, 0x1045, 0x1050, - 0x1051, 0x1054, 0x1055, 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115, 0x1140, 0x1141, 0x1144, - 0x1145, 0x1150, 0x1151, 0x1154, 0x1155, 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415, 0x1440, - 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455, 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, - 0x1515, 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555, 0x4000, 0x4001, 0x4004, 0x4005, 0x4010, - 0x4011, 0x4014, 0x4015, 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055, 0x4100, 0x4101, 0x4104, - 0x4105, 0x4110, 0x4111, 0x4114, 0x4115, 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155, 0x4400, - 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415, 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, - 0x4455, 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515, 0x4540, 0x4541, 0x4544, 0x4545, 0x4550, - 0x4551, 0x4554, 0x4555, 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015, 0x5040, 0x5041, 0x5044, - 0x5045, 0x5050, 0x5051, 0x5054, 0x5055, 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115, 0x5140, - 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155, 0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, - 0x5415, 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455, 0x5500, 0x5501, 0x5504, 0x5505, 0x5510, - 0x5511, 0x5514, 0x5515, 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555 }; - - // For toString(); must have length 32 - private static final String ZEROES = "00000000000000000000000000000000"; - - private final static byte[] bitLengths = - { - 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 - }; - - public static int getWordLength(int bits) - { - return (bits + 31) >>> 5; - } - - // TODO make m fixed for the IntArray, and hence compute T once and for all - - private int[] m_ints; - - public IntArray(int intLen) - { - m_ints = new int[intLen]; - } - - public IntArray(int[] ints) - { - m_ints = ints; - } - - public IntArray(BigInteger bigInt) - { - if (bigInt == null || bigInt.signum() < 0) - { - throw new IllegalArgumentException("invalid F2m field value"); - } - - if (bigInt.signum() == 0) - { - m_ints = new int[] { 0 }; - return; - } - - byte[] barr = bigInt.toByteArray(); - int barrLen = barr.length; - int barrStart = 0; - if (barr[0] == 0) - { - // First byte is 0 to enforce highest (=sign) bit is zero. - // In this case ignore barr[0]. - barrLen--; - barrStart = 1; - } - int intLen = (barrLen + 3) / 4; - m_ints = new int[intLen]; - - int iarrJ = intLen - 1; - int rem = barrLen % 4 + barrStart; - int temp = 0; - int barrI = barrStart; - if (barrStart < rem) - { - for (; barrI < rem; barrI++) - { - temp <<= 8; - int barrBarrI = barr[barrI] & 0xFF; - temp |= barrBarrI; - } - m_ints[iarrJ--] = temp; - } - - for (; iarrJ >= 0; iarrJ--) - { - temp = 0; - for (int i = 0; i < 4; i++) - { - temp <<= 8; - int barrBarrI = barr[barrI++] & 0xFF; - temp |= barrBarrI; - } - m_ints[iarrJ] = temp; - } - } - - public boolean isZero() - { - int[] a = m_ints; - for (int i = 0; i < a.length; ++i) - { - if (a[i] != 0) - { - return false; - } - } - return true; - } - - public int getUsedLength() - { - return getUsedLengthFrom(m_ints.length); - } - - public int getUsedLengthFrom(int from) - { - int[] a = m_ints; - from = Math.min(from, a.length); - - if (from < 1) - { - return 0; - } - - // Check if first element will act as sentinel - if (a[0] != 0) - { - while (a[--from] == 0) - { - } - return from + 1; - } - - do - { - if (a[--from] != 0) - { - return from + 1; - } - } - while (from > 0); - - return 0; - } - - public int degree() - { - int i = m_ints.length, w; - do - { - if (i == 0) - { - return 0; - } - w = m_ints[--i]; - } - while (w == 0); - - return (i << 5) + bitLength(w); - } - - private static int bitLength(int w) - { - int t = w >>> 16; - if (t == 0) - { - t = w >>> 8; - return (t == 0) ? bitLengths[w] : 8 + bitLengths[t]; - } - - int u = t >>> 8; - return (u == 0) ? 16 + bitLengths[t] : 24 + bitLengths[u]; - } - - private int[] resizedInts(int newLen) - { - int[] newInts = new int[newLen]; - System.arraycopy(m_ints, 0, newInts, 0, Math.min(m_ints.length, newLen)); - return newInts; - } - - public BigInteger toBigInteger() - { - int usedLen = getUsedLength(); - if (usedLen == 0) - { - return ECConstants.ZERO; - } - - int highestInt = m_ints[usedLen - 1]; - byte[] temp = new byte[4]; - int barrI = 0; - boolean trailingZeroBytesDone = false; - for (int j = 3; j >= 0; j--) - { - byte thisByte = (byte) (highestInt >>> (8 * j)); - if (trailingZeroBytesDone || (thisByte != 0)) - { - trailingZeroBytesDone = true; - temp[barrI++] = thisByte; - } - } - - int barrLen = 4 * (usedLen - 1) + barrI; - byte[] barr = new byte[barrLen]; - for (int j = 0; j < barrI; j++) - { - barr[j] = temp[j]; - } - // Highest value int is done now - - for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--) - { - for (int j = 3; j >= 0; j--) - { - barr[barrI++] = (byte) (m_ints[iarrJ] >>> (8 * j)); - } - } - return new BigInteger(1, barr); - } - - private static int shiftLeft(int[] x, int count) - { - int prev = 0; - for (int i = 0; i < count; ++i) - { - int next = x[i]; - x[i] = (next << 1) | prev; - prev = next >>> 31; - } - return prev; - } - - public void addOneShifted(int shift) - { - if (shift >= m_ints.length) - { - m_ints = resizedInts(shift + 1); - } - - m_ints[shift] ^= 1; - } - - private void addShiftedByBits(IntArray other, int bits) - { - int words = bits >>> 5; - int shift = bits & 0x1F; - - if (shift == 0) - { - addShiftedByWords(other, words); - return; - } - - int otherUsedLen = other.getUsedLength(); - if (otherUsedLen == 0) - { - return; - } - - int minLen = otherUsedLen + words + 1; - if (minLen > m_ints.length) - { - m_ints = resizedInts(minLen); - } - - int shiftInv = 32 - shift, prev = 0; - for (int i = 0; i < otherUsedLen; ++i) - { - int next = other.m_ints[i]; - m_ints[i + words] ^= (next << shift) | prev; - prev = next >>> shiftInv; - } - m_ints[otherUsedLen + words] ^= prev; - } - - private static int addShiftedByBits(int[] x, int[] y, int count, int shift) - { - int shiftInv = 32 - shift, prev = 0; - for (int i = 0; i < count; ++i) - { - int next = y[i]; - x[i] ^= (next << shift) | prev; - prev = next >>> shiftInv; - } - return prev; - } - - private static int addShiftedByBits(int[] x, int xOff, int[] y, int yOff, int count, int shift) - { - int shiftInv = 32 - shift, prev = 0; - for (int i = 0; i < count; ++i) - { - int next = y[yOff + i]; - x[xOff + i] ^= (next << shift) | prev; - prev = next >>> shiftInv; - } - return prev; - } - - public void addShiftedByWords(IntArray other, int words) - { - int otherUsedLen = other.getUsedLength(); - if (otherUsedLen == 0) - { - return; - } - - int minLen = otherUsedLen + words; - if (minLen > m_ints.length) - { - m_ints = resizedInts(minLen); - } - - for (int i = 0; i < otherUsedLen; i++) - { - m_ints[words + i] ^= other.m_ints[i]; - } - } - - private static void addShiftedByWords(int[] x, int xOff, int[] y, int count) - { - for (int i = 0; i < count; ++i) - { - x[xOff + i] ^= y[i]; - } - } - - private static void add(int[] x, int[] y, int count) - { - for (int i = 0; i < count; ++i) - { - x[i] ^= y[i]; - } - } - - private static void distribute(int[] x, int dst1, int dst2, int src, int count) - { - for (int i = 0; i < count; ++i) - { - int v = x[src + i]; - x[dst1 + i] ^= v; - x[dst2 + i] ^= v; - } - } - - public int getLength() - { - return m_ints.length; - } - - public void flipWord(int bit, int word) - { - int len = m_ints.length; - int n = bit >>> 5; - if (n < len) - { - int shift = bit & 0x1F; - if (shift == 0) - { - m_ints[n] ^= word; - } - else - { - m_ints[n] ^= word << shift; - if (++n < len) - { - m_ints[n] ^= word >>> (32 - shift); - } - } - } - } - - public int getWord(int bit) - { - int len = m_ints.length; - int n = bit >>> 5; - if (n >= len) - { - return 0; - } - int shift = bit & 0x1F; - if (shift == 0) - { - return m_ints[n]; - } - int result = m_ints[n] >>> shift; - if (++n < len) - { - result |= m_ints[n] << (32 - shift); - } - return result; - } - - public boolean testBitZero() - { - return m_ints.length > 0 && (m_ints[0] & 1) != 0; - } - - public boolean testBit(int n) - { - // theInt = n / 32 - int theInt = n >>> 5; - // theBit = n % 32 - int theBit = n & 0x1F; - int tester = 1 << theBit; - return ((m_ints[theInt] & tester) != 0); - } - - public void flipBit(int n) - { - // theInt = n / 32 - int theInt = n >>> 5; - // theBit = n % 32 - int theBit = n & 0x1F; - int flipper = 1 << theBit; - m_ints[theInt] ^= flipper; - } - - public void setBit(int n) - { - // theInt = n / 32 - int theInt = n >>> 5; - // theBit = n % 32 - int theBit = n & 0x1F; - int setter = 1 << theBit; - m_ints[theInt] |= setter; - } - - public void clearBit(int n) - { - // theInt = n / 32 - int theInt = n >>> 5; - // theBit = n % 32 - int theBit = n & 0x1F; - int setter = 1 << theBit; - m_ints[theInt] &= ~setter; - } - - public IntArray multiply(IntArray other, int m) - { - int aLen = getUsedLength(); - if (aLen == 0) - { - return new IntArray(1); - } - - int bLen = other.getUsedLength(); - if (bLen == 0) - { - return new IntArray(1); - } - - IntArray A = this, B = other; - if (aLen > bLen) - { - A = other; B = this; - int tmp = aLen; aLen = bLen; bLen = tmp; - } - - if (aLen == 1) - { - int a = A.m_ints[0]; - int[] b = B.m_ints; - int[] c = new int[aLen + bLen]; - if ((a & 1) != 0) - { - add(c, b, bLen); - } - int k = 1; - while ((a >>>= 1) != 0) - { - if ((a & 1) != 0) - { - addShiftedByBits(c, b, bLen, k); - } - ++k; - } - return new IntArray(c); - } - - // TODO It'd be better to be able to tune the width directly (need support for interleaving arbitrary widths) - int complexity = aLen <= 8 ? 1 : 2; - - int width = 1 << complexity; - int shifts = (32 >>> complexity); - - int bExt = bLen; - if ((B.m_ints[bLen - 1] >>> (33 - shifts)) != 0) - { - ++bExt; - } - - int cLen = bExt + aLen; - - int[] c = new int[cLen << width]; - System.arraycopy(B.m_ints, 0, c, 0, bLen); - interleave(A.m_ints, 0, c, bExt, aLen, complexity); - - int[] ci = new int[1 << width]; - for (int i = 1; i < ci.length; ++i) - { - ci[i] = ci[i - 1] + cLen; - } - - int MASK = (1 << width) - 1; - - int k = 0; - for (;;) - { - for (int aPos = 0; aPos < aLen; ++aPos) - { - int index = (c[bExt + aPos] >>> k) & MASK; - if (index != 0) - { - addShiftedByWords(c, aPos + ci[index], c, bExt); - } - } - - if ((k += width) >= 32) - { - break; - } - - shiftLeft(c, bExt); - } - - int ciPos = ci.length, pow2 = ciPos >>> 1, offset = 32; - while (--ciPos > 1) - { - if (ciPos == pow2) - { - offset -= shifts; - addShiftedByBits(c, ci[1], c, ci[pow2], cLen, offset); - pow2 >>>= 1; - } - else - { - distribute(c, ci[pow2], ci[ciPos - pow2], ci[ciPos], cLen); - } - } - - // TODO reduce in place to avoid extra copying - IntArray p = new IntArray(cLen); - System.arraycopy(c, ci[1], p.m_ints, 0, cLen); - return p; - } - -// private static void deInterleave(int[] x, int xOff, int[] z, int zOff, int count, int rounds) -// { -// for (int i = 0; i < count; ++i) -// { -// z[zOff + i] = deInterleave(x[zOff + i], rounds); -// } -// } -// -// private static int deInterleave(int x, int rounds) -// { -// while (--rounds >= 0) -// { -// x = deInterleave16(x & DEINTERLEAVE_MASK) | (deInterleave16((x >>> 1) & DEINTERLEAVE_MASK) << 16); -// } -// return x; -// } -// -// private static int deInterleave16(int x) -// { -// x = (x | (x >>> 1)) & 0x33333333; -// x = (x | (x >>> 2)) & 0x0F0F0F0F; -// x = (x | (x >>> 4)) & 0x00FF00FF; -// x = (x | (x >>> 8)) & 0x0000FFFF; -// return x; -// } - - public void reduce(int m, int[] ks) - { - int len = getUsedLength(); - int mLen = (m + 31) >>> 5; - if (len < mLen) - { - return; - } - - int _2m = m << 1; - int pos = Math.min(_2m - 2, (len << 5) - 1); - - int kMax = ks[ks.length - 1]; - if (kMax < m - 31) - { - reduceWordWise(pos, m, ks); - } - else - { - reduceBitWise(pos, m, ks); - } - - // Instead of flipping the high bits in the loop, explicitly clear any partial word above m bits - int partial = m & 0x1F; - if (partial != 0) - { - m_ints[mLen - 1] &= (1 << partial) - 1; - } - - if (len > mLen) - { - m_ints = resizedInts(mLen); - } - } - - private void reduceBitWise(int from, int m, int[] ks) - { - for (int i = from; i >= m; --i) - { - if (testBit(i)) - { -// clearBit(i); - int bit = i - m; - flipBit(bit); - int j = ks.length; - while (--j >= 0) - { - flipBit(ks[j] + bit); - } - } - } - } - - private void reduceWordWise(int from, int m, int[] ks) - { - int pos = m + ((from - m) & ~0x1F); - for (int i = pos; i >= m; i -= 32) - { - int word = getWord(i); - if (word != 0) - { -// flipWord(i); - int bit = i - m; - flipWord(bit, word); - int j = ks.length; - while (--j >= 0) - { - flipWord(ks[j] + bit, word); - } - } - } - } - - public IntArray square(int m) - { - int len = getUsedLength(); - if (len == 0) - { - return this; - } - - int _2len = len << 1; - int[] r = new int[_2len]; - - int pos = 0; - while (pos < _2len) - { - int mi = m_ints[pos >>> 1]; - r[pos++] = interleave16(mi & 0xFFFF); - r[pos++] = interleave16(mi >>> 16); - } - - return new IntArray(r); - } - - private static void interleave(int[] x, int xOff, int[] z, int zOff, int count, int rounds) - { - for (int i = 0; i < count; ++i) - { - z[zOff + i] = interleave(x[xOff + i], rounds); - } - } - - private static int interleave(int x, int rounds) - { - while (--rounds >= 0) - { - x = interleave16(x & 0xFFFF) | (interleave16(x >>> 16) << 1); - } - return x; - } - - private static int interleave16(int n) - { - return INTERLEAVE_TABLE[n & 0xFF] | INTERLEAVE_TABLE[n >>> 8] << 16; - } - - public IntArray modInverse(int m, int[] ks) - { - // Inversion in F2m using the extended Euclidean algorithm - // Input: A nonzero polynomial a(z) of degree at most m-1 - // Output: a(z)^(-1) mod f(z) - - int uzDegree = degree(); - if (uzDegree == 1) - { - return this; - } - - // u(z) := a(z) - IntArray uz = (IntArray)clone(); - - int t = getWordLength(m); - - // v(z) := f(z) - IntArray vz = new IntArray(t); - vz.setBit(m); - vz.setBit(0); - vz.setBit(ks[0]); - if (ks.length > 1) - { - vz.setBit(ks[1]); - vz.setBit(ks[2]); - } - - // g1(z) := 1, g2(z) := 0 - IntArray g1z = new IntArray(t); - g1z.setBit(0); - IntArray g2z = new IntArray(t); - - while (uzDegree != 0) - { - // j := deg(u(z)) - deg(v(z)) - int j = uzDegree - vz.degree(); - - // If j < 0 then: u(z) <-> v(z), g1(z) <-> g2(z), j := -j - if (j < 0) - { - final IntArray uzCopy = uz; - uz = vz; - vz = uzCopy; - - final IntArray g1zCopy = g1z; - g1z = g2z; - g2z = g1zCopy; - - j = -j; - } - - // u(z) := u(z) + z^j * v(z) - // Note, that no reduction modulo f(z) is required, because - // deg(u(z) + z^j * v(z)) <= max(deg(u(z)), j + deg(v(z))) - // = max(deg(u(z)), deg(u(z)) - deg(v(z)) + deg(v(z)) - // = deg(u(z)) - // uz = uz.xor(vz.shiftLeft(j)); - uz.addShiftedByBits(vz, j); - uzDegree = uz.degree(); - - // g1(z) := g1(z) + z^j * g2(z) -// g1z = g1z.xor(g2z.shiftLeft(j)); - if (uzDegree != 0) - { - g1z.addShiftedByBits(g2z, j); - } - } - return g2z; - } - - public boolean equals(Object o) - { - if (!(o instanceof IntArray)) - { - return false; - } - IntArray other = (IntArray) o; - int usedLen = getUsedLength(); - if (other.getUsedLength() != usedLen) - { - return false; - } - for (int i = 0; i < usedLen; i++) - { - if (m_ints[i] != other.m_ints[i]) - { - return false; - } - } - return true; - } - - public int hashCode() - { - int usedLen = getUsedLength(); - int hash = 1; - for (int i = 0; i < usedLen; i++) - { - hash *= 31; - hash ^= m_ints[i]; - } - return hash; - } - - public Object clone() - { - return new IntArray(Arrays.clone(m_ints)); - } - - public String toString() - { - int i = getUsedLength(); - if (i == 0) - { - return "0"; - } - - StringBuffer sb = new StringBuffer(Integer.toBinaryString(m_ints[--i])); - while (--i >= 0) - { - String s = Integer.toBinaryString(m_ints[i]); - - // Add leading zeroes, except for highest significant word - int len = s.length(); - if (len < 32) - { - sb.append(ZEROES.substring(len)); - } - - sb.append(s); - } - return sb.toString(); - } -}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java b/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java index 7e8b172..e3069dc 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java @@ -371,6 +371,23 @@ class LongArray } } + public boolean isOne() + { + long[] a = m_ints; + if (a[0] != 1L) + { + return false; + } + for (int i = 1; i < a.length; ++i) + { + if (a[i] != 0L) + { + return false; + } + } + return true; + } + public boolean isZero() { long[] a = m_ints; @@ -822,12 +839,12 @@ class LongArray add(c, cOff, b, 0, bLen); } int k = 1; - while ((a >>>= 1) != 0) + while ((a >>>= 1) != 0L) { if ((a & 1L) != 0L) { long carry = addShiftedUp(c, cOff, b, 0, bLen, k); - if (carry != 0) + if (carry != 0L) { c[cOff + bLen] ^= carry; } @@ -871,8 +888,8 @@ class LongArray if (aLen == 1) { - long a = A.m_ints[0]; - if (a == 1L) + long a0 = A.m_ints[0]; + if (a0 == 1L) { return B; } @@ -880,13 +897,13 @@ class LongArray /* * Fast path for small A, with performance dependent only on the number of set bits */ - long[] c = new long[cLen]; - multiplyWord(a, B.m_ints, bLen, c, 0); + long[] c0 = new long[cLen]; + multiplyWord(a0, B.m_ints, bLen, c0, 0); /* * Reduce the raw answer against the reduction coefficients */ - return reduceResult(c, 0, cLen, m, ks); + return reduceResult(c0, 0, cLen, m, ks); } /* @@ -1003,8 +1020,8 @@ class LongArray if (aLen == 1) { - long a = A.m_ints[0]; - if (a == 1L) + long a0 = A.m_ints[0]; + if (a0 == 1L) { return B; } @@ -1012,13 +1029,13 @@ class LongArray /* * Fast path for small A, with performance dependent only on the number of set bits */ - long[] c = new long[cLen]; - multiplyWord(a, B.m_ints, bLen, c, 0); + long[] c0 = new long[cLen]; + multiplyWord(a0, B.m_ints, bLen, c0, 0); /* * Reduce the raw answer against the reduction coefficients */ - return reduceResult(c, 0, cLen, m, ks); + return reduceResult(c0, 0, cLen, m, ks); } /* @@ -1077,7 +1094,8 @@ class LongArray aVal >>>= 4; int v = (int)aVal & MASK; addBoth(c, cOff, T0, ti[u], T1, ti[v], bMax); - if ((aVal >>>= 4) == 0L) + aVal >>>= 4; + if (aVal == 0L) { break; } @@ -1085,10 +1103,12 @@ class LongArray } } - int cOff = c.length; - while ((cOff -= cLen) != 0) { - addShiftedUp(c, cOff - cLen, c, cOff, cLen, 8); + int cOff = c.length; + while ((cOff -= cLen) != 0) + { + addShiftedUp(c, cOff - cLen, c, cOff, cLen, 8); + } } /* @@ -1132,8 +1152,8 @@ class LongArray if (aLen == 1) { - long a = A.m_ints[0]; - if (a == 1L) + long a0 = A.m_ints[0]; + if (a0 == 1L) { return B; } @@ -1141,13 +1161,13 @@ class LongArray /* * Fast path for small A, with performance dependent only on the number of set bits */ - long[] c = new long[cLen]; - multiplyWord(a, B.m_ints, bLen, c, 0); + long[] c0 = new long[cLen]; + multiplyWord(a0, B.m_ints, bLen, c0, 0); /* * Reduce the raw answer against the reduction coefficients */ - return reduceResult(c, 0, cLen, m, ks); + return reduceResult(c0, 0, cLen, m, ks); } // NOTE: This works, but is slower than width 4 processing @@ -1314,6 +1334,158 @@ class LongArray return reduceResult(c, ci[1], cLen, m, ks); } + public LongArray modReduce(int m, int[] ks) + { + long[] buf = Arrays.clone(m_ints); + int rLen = reduceInPlace(buf, 0, buf.length, m, ks); + return new LongArray(buf, 0, rLen); + } + + public LongArray multiply(LongArray other, int m, int[] ks) + { + /* + * Find out the degree of each argument and handle the zero cases + */ + int aDeg = degree(); + if (aDeg == 0) + { + return this; + } + int bDeg = other.degree(); + if (bDeg == 0) + { + return other; + } + + /* + * Swap if necessary so that A is the smaller argument + */ + LongArray A = this, B = other; + if (aDeg > bDeg) + { + A = other; B = this; + int tmp = aDeg; aDeg = bDeg; bDeg = tmp; + } + + /* + * Establish the word lengths of the arguments and result + */ + int aLen = (aDeg + 63) >>> 6; + int bLen = (bDeg + 63) >>> 6; + int cLen = (aDeg + bDeg + 62) >>> 6; + + if (aLen == 1) + { + long a0 = A.m_ints[0]; + if (a0 == 1L) + { + return B; + } + + /* + * Fast path for small A, with performance dependent only on the number of set bits + */ + long[] c0 = new long[cLen]; + multiplyWord(a0, B.m_ints, bLen, c0, 0); + + /* + * Reduce the raw answer against the reduction coefficients + */ +// return reduceResult(c0, 0, cLen, m, ks); + return new LongArray(c0, 0, cLen); + } + + /* + * Determine if B will get bigger during shifting + */ + int bMax = (bDeg + 7 + 63) >>> 6; + + /* + * Lookup table for the offset of each B in the tables + */ + int[] ti = new int[16]; + + /* + * Precompute table of all 4-bit products of B + */ + long[] T0 = new long[bMax << 4]; + int tOff = bMax; + ti[1] = tOff; + System.arraycopy(B.m_ints, 0, T0, tOff, bLen); + for (int i = 2; i < 16; ++i) + { + ti[i] = (tOff += bMax); + if ((i & 1) == 0) + { + shiftUp(T0, tOff >>> 1, T0, tOff, bMax, 1); + } + else + { + add(T0, bMax, T0, tOff - bMax, T0, tOff, bMax); + } + } + + /* + * Second table with all 4-bit products of B shifted 4 bits + */ + long[] T1 = new long[T0.length]; + shiftUp(T0, 0, T1, 0, T0.length, 4); +// shiftUp(T0, bMax, T1, bMax, tOff, 4); + + long[] a = A.m_ints; + long[] c = new long[cLen << 3]; + + int MASK = 0xF; + + /* + * Lopez-Dahab (Modified) algorithm + */ + + for (int aPos = 0; aPos < aLen; ++aPos) + { + long aVal = a[aPos]; + int cOff = aPos; + for (;;) + { + int u = (int)aVal & MASK; + aVal >>>= 4; + int v = (int)aVal & MASK; + addBoth(c, cOff, T0, ti[u], T1, ti[v], bMax); + aVal >>>= 4; + if (aVal == 0L) + { + break; + } + cOff += cLen; + } + } + + { + int cOff = c.length; + while ((cOff -= cLen) != 0) + { + addShiftedUp(c, cOff - cLen, c, cOff, cLen, 8); + } + } + + /* + * Finally the raw answer is collected, reduce it against the reduction coefficients + */ +// return reduceResult(c, 0, cLen, m, ks); + return new LongArray(c, 0, cLen); + } + + public void reduce(int m, int[] ks) + { + long[] buf = m_ints; + int rLen = reduceInPlace(buf, 0, buf.length, m, ks); + if (rLen < buf.length) + { + m_ints = new long[rLen]; + System.arraycopy(buf, 0, m_ints, 0, rLen); + } + } + private static LongArray reduceResult(long[] buf, int off, int len, int m, int[] ks) { int rLen = reduceInPlace(buf, off, len, m, ks); @@ -1405,13 +1577,13 @@ class LongArray private static void reduceBit(long[] buf, int off, int bit, int m, int[] ks) { flipBit(buf, off, bit); - int base = bit - m; + int n = bit - m; int j = ks.length; while (--j >= 0) { - flipBit(buf, off, ks[j] + base); + flipBit(buf, off, ks[j] + n); } - flipBit(buf, off, base); + flipBit(buf, off, n); } private static void reduceWordWise(long[] buf, int off, int len, int toBit, int m, int[] ks) @@ -1428,12 +1600,14 @@ class LongArray } } - int partial = toBit & 0x3F; - long word = buf[off + toPos] >>> partial; - if (word != 0) { - buf[off + toPos] ^= word << partial; - reduceWord(buf, off, toBit, word, m, ks); + int partial = toBit & 0x3F; + long word = buf[off + toPos] >>> partial; + if (word != 0) + { + buf[off + toPos] ^= word << partial; + reduceWord(buf, off, toBit, word, m, ks); + } } } @@ -1502,37 +1676,59 @@ class LongArray return new LongArray(r, 0, reduceInPlace(r, 0, r.length, m, ks)); } -// private LongArray modSquareN(int n, int m, int[] ks) -// { -// int len = getUsedLength(); -// if (len == 0) -// { -// return this; -// } -// -// int mLen = (m + 63) >>> 6; -// long[] r = new long[mLen << 1]; -// System.arraycopy(m_ints, 0, r, 0, len); -// -// while (--n >= 0) -// { -// squareInPlace(r, len, m, ks); -// len = reduceInPlace(r, 0, r.length, m, ks); -// } -// -// return new LongArray(r, 0, len); -// } -// -// private static void squareInPlace(long[] x, int xLen, int m, int[] ks) -// { -// int pos = xLen << 1; -// while (--xLen >= 0) -// { -// long xVal = x[xLen]; -// x[--pos] = interleave2_32to64((int)(xVal >>> 32)); -// x[--pos] = interleave2_32to64((int)xVal); -// } -// } + public LongArray modSquareN(int n, int m, int[] ks) + { + int len = getUsedLength(); + if (len == 0) + { + return this; + } + + int mLen = (m + 63) >>> 6; + long[] r = new long[mLen << 1]; + System.arraycopy(m_ints, 0, r, 0, len); + + while (--n >= 0) + { + squareInPlace(r, len, m, ks); + len = reduceInPlace(r, 0, r.length, m, ks); + } + + return new LongArray(r, 0, len); + } + + public LongArray square(int m, int[] ks) + { + int len = getUsedLength(); + if (len == 0) + { + return this; + } + + int _2len = len << 1; + long[] r = new long[_2len]; + + int pos = 0; + while (pos < _2len) + { + long mi = m_ints[pos >>> 1]; + r[pos++] = interleave2_32to64((int)mi); + r[pos++] = interleave2_32to64((int)(mi >>> 32)); + } + + return new LongArray(r, 0, r.length); + } + + private static void squareInPlace(long[] x, int xLen, int m, int[] ks) + { + int pos = xLen << 1; + while (--xLen >= 0) + { + long xVal = x[xLen]; + x[--pos] = interleave2_32to64((int)(xVal >>> 32)); + x[--pos] = interleave2_32to64((int)xVal); + } + } private static void interleave(long[] x, int xOff, long[] z, int zOff, int count, int width) { @@ -1856,6 +2052,10 @@ class LongArray * Output: a(z)^(-1) mod f(z) */ int uzDegree = degree(); + if (uzDegree == 0) + { + throw new IllegalStateException(); + } if (uzDegree == 1) { return this; diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ScaleXPointMap.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ScaleXPointMap.java new file mode 100644 index 0000000..099f5fb --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ScaleXPointMap.java @@ -0,0 +1,16 @@ +package org.bouncycastle.math.ec; + +public class ScaleXPointMap implements ECPointMap +{ + protected final ECFieldElement scale; + + public ScaleXPointMap(ECFieldElement scale) + { + this.scale = scale; + } + + public ECPoint map(ECPoint p) + { + return p.scaleX(scale); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/SimpleBigDecimal.java b/bcprov/src/main/java/org/bouncycastle/math/ec/SimpleBigDecimal.java index 96e666d..229fae4 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/SimpleBigDecimal.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/SimpleBigDecimal.java @@ -54,12 +54,6 @@ class SimpleBigDecimal this.scale = scale; } - private SimpleBigDecimal(SimpleBigDecimal limBigDec) - { - bigInt = limBigDec.bigInt; - scale = limBigDec.scale; - } - private void checkScale(SimpleBigDecimal b) { if (scale != b.scale) @@ -78,7 +72,7 @@ class SimpleBigDecimal if (newScale == scale) { - return new SimpleBigDecimal(this); + return this; } return new SimpleBigDecimal(bigInt.shiftLeft(newScale - scale), diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java b/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java index 42d6738..236bbc8 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java @@ -535,45 +535,36 @@ class Tnaf int m = curve.getM(); int a = curve.getA().toBigInteger().intValue(); byte mu = curve.getMu(); - int h = curve.getH().intValue(); + int shifts = getShiftsForCofactor(curve.getCofactor()); int index = m + 3 - a; BigInteger[] ui = getLucas(mu, index, false); - - BigInteger dividend0; - BigInteger dividend1; if (mu == 1) { - dividend0 = ECConstants.ONE.subtract(ui[1]); - dividend1 = ECConstants.ONE.subtract(ui[0]); - } - else if (mu == -1) - { - dividend0 = ECConstants.ONE.add(ui[1]); - dividend1 = ECConstants.ONE.add(ui[0]); - } - else - { - throw new IllegalArgumentException("mu must be 1 or -1"); + ui[0] = ui[0].negate(); + ui[1] = ui[1].negate(); } - BigInteger[] si = new BigInteger[2]; + BigInteger dividend0 = ECConstants.ONE.add(ui[1]).shiftRight(shifts); + BigInteger dividend1 = ECConstants.ONE.add(ui[0]).shiftRight(shifts).negate(); - if (h == 2) - { - si[0] = dividend0.shiftRight(1); - si[1] = dividend1.shiftRight(1).negate(); - } - else if (h == 4) - { - si[0] = dividend0.shiftRight(2); - si[1] = dividend1.shiftRight(2).negate(); - } - else + return new BigInteger[] { dividend0, dividend1 }; + } + + protected static int getShiftsForCofactor(BigInteger h) + { + if (h != null) { - throw new IllegalArgumentException("h (Cofactor) must be 2 or 4"); + if (h.equals(ECConstants.TWO)) + { + return 1; + } + if (h.equals(ECConstants.FOUR)) + { + return 2; + } } - return si; + throw new IllegalArgumentException("h (Cofactor) must be 2 or 4"); } /** diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafL2RMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafL2RMultiplier.java index 59a9313..90b0847 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafL2RMultiplier.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafL2RMultiplier.java @@ -31,7 +31,7 @@ public class WNafL2RMultiplier extends AbstractECMultiplier int i = wnaf.length; /* - * NOTE This code optimizes the first window using the precomputed points to substitute an + * NOTE: We try to optimize the first window using the precomputed points to substitute an * addition for 2 or more doublings. */ if (i > 1) @@ -42,19 +42,14 @@ public class WNafL2RMultiplier extends AbstractECMultiplier int n = Math.abs(digit); ECPoint[] table = digit < 0 ? preCompNeg : preComp; - /* - * NOTE: We use this optimization conservatively, since some coordinate systems have - * significantly cheaper doubling relative to addition. - * - * (n << 2) selects precomputed values in the lower half of the table - * (n << 3) selects precomputed values in the lower quarter of the table - */ - //if ((n << 2) < (1 << width)) - if ((n << 3) < (1 << width)) + // Optimization can only be used for values in the lower half of the table + if ((n << 2) < (1 << width)) { int highest = LongArray.bitLengths[n]; - int lowBits = n ^ (1 << (highest - 1)); + + // TODO Get addition/doubling cost ratio from curve and compare to 'scale' to see if worth substituting? int scale = width - highest; + int lowBits = n ^ (1 << (highest - 1)); int i1 = ((1 << (width - 1)) - 1); int i2 = (lowBits << scale) + 1; diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java index d142ab7..e8f16e6 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java @@ -10,47 +10,47 @@ public class WNafPreCompInfo implements PreCompInfo * Array holding the precomputed <code>ECPoint</code>s used for a Window * NAF multiplication. */ - private ECPoint[] preComp = null; + protected ECPoint[] preComp = null; /** * Array holding the negations of the precomputed <code>ECPoint</code>s used * for a Window NAF multiplication. */ - private ECPoint[] preCompNeg = null; + protected ECPoint[] preCompNeg = null; /** * Holds an <code>ECPoint</code> representing twice(this). Used for the * Window NAF multiplication to create or extend the precomputed values. */ - private ECPoint twiceP = null; + protected ECPoint twice = null; - protected ECPoint[] getPreComp() + public ECPoint[] getPreComp() { return preComp; } - protected ECPoint[] getPreCompNeg() + public void setPreComp(ECPoint[] preComp) { - return preCompNeg; + this.preComp = preComp; } - protected void setPreComp(ECPoint[] preComp) + public ECPoint[] getPreCompNeg() { - this.preComp = preComp; + return preCompNeg; } - protected void setPreCompNeg(ECPoint[] preCompNeg) + public void setPreCompNeg(ECPoint[] preCompNeg) { this.preCompNeg = preCompNeg; } - protected ECPoint getTwiceP() + public ECPoint getTwice() { - return twiceP; + return twice; } - protected void setTwiceP(ECPoint twiceP) + public void setTwice(ECPoint twice) { - this.twiceP = twiceP; + this.twice = twice; } } diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java index 6465d66..7ac3160 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java @@ -4,7 +4,12 @@ import java.math.BigInteger; public abstract class WNafUtil { - private static int[] DEFAULT_WINDOW_SIZE_CUTOFFS = new int[]{ 13, 41, 121, 337, 897, 2305 }; + public static final String PRECOMP_NAME = "bc_wnaf"; + + private static final int[] DEFAULT_WINDOW_SIZE_CUTOFFS = new int[]{ 13, 41, 121, 337, 897, 2305 }; + + private static final byte[] EMPTY_BYTES = new byte[0]; + private static final int[] EMPTY_INTS = new int[0]; public static int[] generateCompactNaf(BigInteger k) { @@ -12,30 +17,35 @@ public abstract class WNafUtil { throw new IllegalArgumentException("'k' must have bitlength < 2^16"); } + if (k.signum() == 0) + { + return EMPTY_INTS; + } BigInteger _3k = k.shiftLeft(1).add(k); - int digits = _3k.bitLength() - 1; - int[] naf = new int[(digits + 1) >> 1]; + int bits = _3k.bitLength(); + int[] naf = new int[bits >> 1]; - int length = 0, zeroes = 0; - for (int i = 1; i <= digits; ++i) - { - boolean _3kBit = _3k.testBit(i); - boolean kBit = k.testBit(i); + BigInteger diff = _3k.xor(k); - if (_3kBit == kBit) + int highBit = bits - 1, length = 0, zeroes = 0; + for (int i = 1; i < highBit; ++i) + { + if (!diff.testBit(i)) { ++zeroes; + continue; } - else - { - int digit = kBit ? -1 : 1; - naf[length++] = (digit << 16) | zeroes; - zeroes = 0; - } + + int digit = k.testBit(i) ? -1 : 1; + naf[length++] = (digit << 16) | zeroes; + zeroes = 1; + ++i; } + naf[length++] = (1 << 16) | zeroes; + if (naf.length > length) { naf = trim(naf, length); @@ -59,6 +69,10 @@ public abstract class WNafUtil { throw new IllegalArgumentException("'k' must have bitlength < 2^16"); } + if (k.signum() == 0) + { + return EMPTY_INTS; + } int[] wnaf = new int[k.bitLength() / width + 1]; @@ -114,9 +128,10 @@ public abstract class WNafUtil BigInteger k0 = g, k1 = h; int j = 0, d0 = 0, d1 = 0; - while (k0.signum() > 0 || k1.signum() > 0 || d0 > 0 || d1 > 0) + int offset = 0; + while ((d0 | d1) != 0 || k0.bitLength() > offset || k1.bitLength() > offset) { - int n0 = (k0.intValue() + d0) & 7, n1 = (k1.intValue() + d1) & 7; + int n0 = ((k0.intValue() >>> offset) + d0) & 7, n1 = ((k1.intValue() >>> offset) + d1) & 7; int u0 = n0 & 1; if (u0 != 0) @@ -140,15 +155,19 @@ public abstract class WNafUtil if ((d0 << 1) == 1 + u0) { - d0 = 1 - d0; + d0 ^= 1; } if ((d1 << 1) == 1 + u1) { - d1 = 1 - d1; + d1 ^= 1; } - k0 = k0.shiftRight(1); - k1 = k1.shiftRight(1); + if (++offset == 30) + { + offset = 0; + k0 = k0.shiftRight(30); + k1 = k1.shiftRight(30); + } jsf[j++] = (byte)((u0 << 4) | (u1 & 0xF)); } @@ -164,19 +183,29 @@ public abstract class WNafUtil public static byte[] generateNaf(BigInteger k) { + if (k.signum() == 0) + { + return EMPTY_BYTES; + } + BigInteger _3k = k.shiftLeft(1).add(k); int digits = _3k.bitLength() - 1; byte[] naf = new byte[digits]; - for (int i = 1; i <= digits; ++i) - { - boolean _3kBit = _3k.testBit(i); - boolean kBit = k.testBit(i); + BigInteger diff = _3k.xor(k); - naf[i - 1] = (byte)(_3kBit == kBit ? 0 : kBit ? -1 : 1); + for (int i = 1; i < digits; ++i) + { + if (diff.testBit(i)) + { + naf[i - 1] = (byte)(k.testBit(i) ? -1 : 1); + ++i; + } } + naf[digits - 1] = 1; + return naf; } @@ -203,6 +232,10 @@ public abstract class WNafUtil { throw new IllegalArgumentException("'width' must be in the range [2, 8]"); } + if (k.signum() == 0) + { + return EMPTY_BYTES; + } byte[] wnaf = new byte[k.bitLength() + 1]; @@ -250,6 +283,24 @@ public abstract class WNafUtil return wnaf; } + public static int getNafWeight(BigInteger k) + { + if (k.signum() == 0) + { + return 0; + } + + BigInteger _3k = k.shiftLeft(1).add(k); + BigInteger diff = _3k.xor(k); + + return diff.bitCount(); + } + + public static WNafPreCompInfo getWNafPreCompInfo(ECPoint p) + { + return getWNafPreCompInfo(p.getCurve().getPreCompInfo(p, PRECOMP_NAME)); + } + public static WNafPreCompInfo getWNafPreCompInfo(PreCompInfo preCompInfo) { if ((preCompInfo != null) && (preCompInfo instanceof WNafPreCompInfo)) @@ -291,10 +342,49 @@ public abstract class WNafUtil return w + 2; } + public static ECPoint mapPointWithPrecomp(ECPoint p, int width, boolean includeNegated, + ECPointMap pointMap) + { + ECCurve c = p.getCurve(); + WNafPreCompInfo wnafPreCompP = precompute(p, width, includeNegated); + + ECPoint q = pointMap.map(p); + WNafPreCompInfo wnafPreCompQ = getWNafPreCompInfo(c.getPreCompInfo(q, PRECOMP_NAME)); + + ECPoint twiceP = wnafPreCompP.getTwice(); + if (twiceP != null) + { + ECPoint twiceQ = pointMap.map(twiceP); + wnafPreCompQ.setTwice(twiceQ); + } + + ECPoint[] preCompP = wnafPreCompP.getPreComp(); + ECPoint[] preCompQ = new ECPoint[preCompP.length]; + for (int i = 0; i < preCompP.length; ++i) + { + preCompQ[i] = pointMap.map(preCompP[i]); + } + wnafPreCompQ.setPreComp(preCompQ); + + if (includeNegated) + { + ECPoint[] preCompNegQ = new ECPoint[preCompQ.length]; + for (int i = 0; i < preCompNegQ.length; ++i) + { + preCompNegQ[i] = preCompQ[i].negate(); + } + wnafPreCompQ.setPreCompNeg(preCompNegQ); + } + + c.setPreCompInfo(q, PRECOMP_NAME, wnafPreCompQ); + + return q; + } + public static WNafPreCompInfo precompute(ECPoint p, int width, boolean includeNegated) { ECCurve c = p.getCurve(); - WNafPreCompInfo wnafPreCompInfo = getWNafPreCompInfo(c.getPreCompInfo(p)); + WNafPreCompInfo wnafPreCompInfo = getWNafPreCompInfo(c.getPreCompInfo(p, PRECOMP_NAME)); ECPoint[] preComp = wnafPreCompInfo.getPreComp(); if (preComp == null) @@ -307,26 +397,28 @@ public abstract class WNafUtil if (preCompLen < reqPreCompLen) { - ECPoint twiceP = wnafPreCompInfo.getTwiceP(); - if (twiceP == null) + preComp = resizeTable(preComp, reqPreCompLen); + if (reqPreCompLen == 2) { - twiceP = preComp[0].twice().normalize(); - wnafPreCompInfo.setTwiceP(twiceP); + preComp[1] = preComp[0].threeTimes(); } - - preComp = resizeTable(preComp, reqPreCompLen); - - /* - * TODO Okeya/Sakurai paper has precomputation trick and "Montgomery's Trick" to speed this up. - * Also, co-Z arithmetic could avoid the subsequent normalization too. - */ - for (int i = preCompLen; i < reqPreCompLen; i++) + else { - /* - * Compute the new ECPoints for the precomputation array. The values 1, 3, 5, ..., - * 2^(width-1)-1 times p are computed - */ - preComp[i] = twiceP.add(preComp[i - 1]); + ECPoint twiceP = wnafPreCompInfo.getTwice(); + if (twiceP == null) + { + twiceP = preComp[0].twice(); + wnafPreCompInfo.setTwice(twiceP); + } + + for (int i = preCompLen; i < reqPreCompLen; i++) + { + /* + * Compute the new ECPoints for the precomputation array. The values 1, 3, 5, ..., + * 2^(width-1)-1 times p are computed + */ + preComp[i] = twiceP.add(preComp[i - 1]); + } } /* @@ -365,7 +457,7 @@ public abstract class WNafUtil wnafPreCompInfo.setPreCompNeg(preCompNeg); } - c.setPreCompInfo(p, wnafPreCompInfo); + c.setPreCompInfo(p, PRECOMP_NAME, wnafPreCompInfo); return wnafPreCompInfo; } diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java index 7bd30ec..93d03b4 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java @@ -8,6 +8,9 @@ import java.math.BigInteger; */ public class WTauNafMultiplier extends AbstractECMultiplier { + // TODO Create WTauNafUtil class and move various functionality into it + static final String PRECOMP_NAME = "bc_wtnaf"; + /** * Multiplies a {@link org.bouncycastle.math.ec.ECPoint.F2m ECPoint.F2m} * by <code>k</code> using the reduced <code>τ</code>-adic NAF (RTNAF) @@ -33,7 +36,7 @@ public class WTauNafMultiplier extends AbstractECMultiplier ZTauElement rho = Tnaf.partModReduction(k, m, a, s, mu, (byte)10); - return multiplyWTnaf(p, rho, curve.getPreCompInfo(p), a, mu); + return multiplyWTnaf(p, rho, curve.getPreCompInfo(p, PRECOMP_NAME), a, mu); } /** @@ -49,21 +52,12 @@ public class WTauNafMultiplier extends AbstractECMultiplier private ECPoint.F2m multiplyWTnaf(ECPoint.F2m p, ZTauElement lambda, PreCompInfo preCompInfo, byte a, byte mu) { - ZTauElement[] alpha; - if (a == 0) - { - alpha = Tnaf.alpha0; - } - else - { - // a == 1 - alpha = Tnaf.alpha1; - } + ZTauElement[] alpha = (a == 0) ? Tnaf.alpha0 : Tnaf.alpha1; BigInteger tw = Tnaf.getTw(mu, Tnaf.WIDTH); byte[]u = Tnaf.tauAdicWNaf(mu, lambda, Tnaf.WIDTH, - BigInteger.valueOf(Tnaf.POW_2_WIDTH), tw, alpha); + BigInteger.valueOf(Tnaf.POW_2_WIDTH), tw, alpha); return multiplyFromWTnaf(p, u, preCompInfo); } @@ -77,8 +71,7 @@ public class WTauNafMultiplier extends AbstractECMultiplier * @param u The the WTNAF of <code>λ</code>.. * @return <code>λ * p</code> */ - private static ECPoint.F2m multiplyFromWTnaf(ECPoint.F2m p, byte[] u, - PreCompInfo preCompInfo) + private static ECPoint.F2m multiplyFromWTnaf(ECPoint.F2m p, byte[] u, PreCompInfo preCompInfo) { ECCurve.F2m curve = (ECCurve.F2m)p.getCurve(); byte a = curve.getA().toBigInteger().byteValue(); @@ -87,7 +80,10 @@ public class WTauNafMultiplier extends AbstractECMultiplier if ((preCompInfo == null) || !(preCompInfo instanceof WTauNafPreCompInfo)) { pu = Tnaf.getPreComp(p, a); - curve.setPreCompInfo(p, new WTauNafPreCompInfo(pu)); + + WTauNafPreCompInfo pre = new WTauNafPreCompInfo(); + pre.setPreComp(pu); + curve.setPreCompInfo(p, PRECOMP_NAME, pre); } else { @@ -99,16 +95,16 @@ public class WTauNafMultiplier extends AbstractECMultiplier for (int i = u.length - 1; i >= 0; i--) { q = Tnaf.tau(q); - if (u[i] != 0) + byte ui = u[i]; + if (ui != 0) { - if (u[i] > 0) + if (ui > 0) { - q = q.addSimple(pu[u[i]]); + q = q.addSimple(pu[ui]); } else { - // u[i] < 0 - q = q.subtractSimple(pu[-u[i]]); + q = q.subtractSimple(pu[-ui]); } } } diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafPreCompInfo.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafPreCompInfo.java index d7c583f..190eecb 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafPreCompInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafPreCompInfo.java @@ -4,7 +4,7 @@ package org.bouncycastle.math.ec; * Class holding precomputation data for the WTNAF (Window * <code>τ</code>-adic Non-Adjacent Form) algorithm. */ -class WTauNafPreCompInfo implements PreCompInfo +public class WTauNafPreCompInfo implements PreCompInfo { /** * Array holding the precomputed <code>ECPoint.F2m</code>s used for the @@ -12,28 +12,15 @@ class WTauNafPreCompInfo implements PreCompInfo * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply() * WTauNafMultiplier.multiply()}</code>. */ - private ECPoint.F2m[] preComp = null; + protected ECPoint.F2m[] preComp = null; - /** - * Constructor for <code>WTauNafPreCompInfo</code> - * @param preComp Array holding the precomputed <code>ECPoint.F2m</code>s - * used for the WTNAF multiplication in <code> - * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply() - * WTauNafMultiplier.multiply()}</code>. - */ - WTauNafPreCompInfo(ECPoint.F2m[] preComp) + public ECPoint.F2m[] getPreComp() { - this.preComp = preComp; + return preComp; } - /** - * @return the array holding the precomputed <code>ECPoint.F2m</code>s - * used for the WTNAF multiplication in <code> - * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply() - * WTauNafMultiplier.multiply()}</code>. - */ - protected ECPoint.F2m[] getPreComp() + public void setPreComp(ECPoint.F2m[] preComp) { - return preComp; + this.preComp = preComp; } } diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Curve.java new file mode 100644 index 0000000..b46cba6 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Curve.java @@ -0,0 +1,79 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecP192K1Curve extends ECCurve.AbstractFp +{ + public static final BigInteger q = new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37")); + + private static final int SecP192K1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP192K1Point infinity; + + public SecP192K1Curve() + { + super(q); + + this.infinity = new SecP192K1Point(this, null, null); + + this.a = fromBigInteger(ECConstants.ZERO); + this.b = fromBigInteger(BigInteger.valueOf(3)); + this.order = new BigInteger(1, Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D")); + this.cofactor = BigInteger.valueOf(1); + + this.coord = SecP192K1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecP192K1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public BigInteger getQ() + { + return q; + } + + public int getFieldSize() + { + return q.bitLength(); + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecP192K1FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecP192K1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecP192K1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Field.java new file mode 100644 index 0000000..1a0bde8 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Field.java @@ -0,0 +1,177 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat192; + +public class SecP192K1Field +{ + // 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1 + static final int[] P = new int[]{ 0xFFFFEE37, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + static final int[] PExt = new int[]{ 0x013C4FD1, 0x00002392, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0xFFFFDC6E, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static final int[] PExtInv = new int[]{ 0xFEC3B02F, 0xFFFFDC6D, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00002391, 0x00000002 }; + private static final int P5 = 0xFFFFFFFF; + private static final int PExt11 = 0xFFFFFFFF; + private static final int PInv33 = 0x11C9; + + public static void add(int[] x, int[] y, int[] z) + { + int c = Nat192.add(x, y, z); + if (c != 0 || (z[5] == P5 && Nat192.gte(z, P))) + { + Nat.add33To(6, PInv33, z); + } + } + + public static void addExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.add(12, xx, yy, zz); + if (c != 0 || (zz[11] == PExt11 && Nat.gte(12, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(12, zz, PExtInv.length); + } + } + } + + public static void addOne(int[] x, int[] z) + { + int c = Nat.inc(6, x, z); + if (c != 0 || (z[5] == P5 && Nat192.gte(z, P))) + { + Nat.add33To(6, PInv33, z); + } + } + + public static int[] fromBigInteger(BigInteger x) + { + int[] z = Nat192.fromBigInteger(x); + if (z[5] == P5 && Nat192.gte(z, P)) + { + Nat192.subFrom(P, z); + } + return z; + } + + public static void half(int[] x, int[] z) + { + if ((x[0] & 1) == 0) + { + Nat.shiftDownBit(6, x, 0, z); + } + else + { + int c = Nat192.add(x, P, z); + Nat.shiftDownBit(6, z, c); + } + } + + public static void multiply(int[] x, int[] y, int[] z) + { + int[] tt = Nat192.createExt(); + Nat192.mul(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(int[] x, int[] y, int[] zz) + { + int c = Nat192.mulAddTo(x, y, zz); + if (c != 0 || (zz[11] == PExt11 && Nat.gte(12, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(12, zz, PExtInv.length); + } + } + } + + public static void negate(int[] x, int[] z) + { + if (Nat192.isZero(x)) + { + Nat192.zero(z); + } + else + { + Nat192.sub(P, x, z); + } + } + + public static void reduce(int[] xx, int[] z) + { + long cc = Nat192.mul33Add(PInv33, xx, 6, xx, 0, z, 0); + int c = Nat192.mul33DWordAdd(PInv33, cc, z, 0); + + // assert c == 0L || c == 1L; + + if (c != 0 || (z[5] == P5 && Nat192.gte(z, P))) + { + Nat.add33To(6, PInv33, z); + } + } + + public static void reduce32(int x, int[] z) + { + if ((x != 0 && Nat192.mul33WordAdd(PInv33, x, z, 0) != 0) + || (z[5] == P5 && Nat192.gte(z, P))) + { + Nat.add33To(6, PInv33, z); + } + } + + public static void square(int[] x, int[] z) + { + int[] tt = Nat192.createExt(); + Nat192.square(x, tt); + reduce(tt, z); + } + + public static void squareN(int[] x, int n, int[] z) + { +// assert n > 0; + + int[] tt = Nat192.createExt(); + Nat192.square(x, tt); + reduce(tt, z); + + while (--n > 0) + { + Nat192.square(z, tt); + reduce(tt, z); + } + } + + public static void subtract(int[] x, int[] y, int[] z) + { + int c = Nat192.sub(x, y, z); + if (c != 0) + { + Nat.sub33From(6, PInv33, z); + } + } + + public static void subtractExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.sub(12, xx, yy, zz); + if (c != 0) + { + if (Nat.subFrom(PExtInv.length, PExtInv, zz) != 0) + { + Nat.decAt(12, zz, PExtInv.length); + } + } + } + + public static void twice(int[] x, int[] z) + { + int c = Nat.shiftUpBit(6, x, 0, z); + if (c != 0 || (z[5] == P5 && Nat192.gte(z, P))) + { + Nat.add33To(6, PInv33, z); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1FieldElement.java new file mode 100644 index 0000000..0032f35 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1FieldElement.java @@ -0,0 +1,213 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Mod; +import org.bouncycastle.math.raw.Nat192; +import org.bouncycastle.util.Arrays; + +public class SecP192K1FieldElement extends ECFieldElement +{ + public static final BigInteger Q = SecP192K1Curve.q; + + protected int[] x; + + public SecP192K1FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0) + { + throw new IllegalArgumentException("x value invalid for SecP192K1FieldElement"); + } + + this.x = SecP192K1Field.fromBigInteger(x); + } + + public SecP192K1FieldElement() + { + this.x = Nat192.create(); + } + + protected SecP192K1FieldElement(int[] x) + { + this.x = x; + } + + public boolean isZero() + { + return Nat192.isZero(x); + } + + public boolean isOne() + { + return Nat192.isOne(x); + } + + public boolean testBitZero() + { + return Nat192.getBit(x, 0) == 1; + } + + public BigInteger toBigInteger() + { + return Nat192.toBigInteger(x); + } + + public String getFieldName() + { + return "SecP192K1Field"; + } + + public int getFieldSize() + { + return Q.bitLength(); + } + + public ECFieldElement add(ECFieldElement b) + { + int[] z = Nat192.create(); + SecP192K1Field.add(x, ((SecP192K1FieldElement)b).x, z); + return new SecP192K1FieldElement(z); + } + + public ECFieldElement addOne() + { + int[] z = Nat192.create(); + SecP192K1Field.addOne(x, z); + return new SecP192K1FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + int[] z = Nat192.create(); + SecP192K1Field.subtract(x, ((SecP192K1FieldElement)b).x, z); + return new SecP192K1FieldElement(z); + } + + public ECFieldElement multiply(ECFieldElement b) + { + int[] z = Nat192.create(); + SecP192K1Field.multiply(x, ((SecP192K1FieldElement)b).x, z); + return new SecP192K1FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { +// return multiply(b.invert()); + int[] z = Nat192.create(); + Mod.invert(SecP192K1Field.P, ((SecP192K1FieldElement)b).x, z); + SecP192K1Field.multiply(z, x, z); + return new SecP192K1FieldElement(z); + } + + public ECFieldElement negate() + { + int[] z = Nat192.create(); + SecP192K1Field.negate(x, z); + return new SecP192K1FieldElement(z); + } + + public ECFieldElement square() + { + int[] z = Nat192.create(); + SecP192K1Field.square(x, z); + return new SecP192K1FieldElement(z); + } + + public ECFieldElement invert() + { +// return new SecP192K1FieldElement(toBigInteger().modInverse(Q)); + int[] z = Nat192.create(); + Mod.invert(SecP192K1Field.P, x, z); + return new SecP192K1FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public ECFieldElement sqrt() + { + /* + * Raise this element to the exponent 2^190 - 2^30 - 2^10 - 2^6 - 2^5 - 2^4 - 2^1 + * + * Breaking up the exponent's binary representation into "repunits", we get: + * { 159 1s } { 1 0s } { 19 1s } { 1 0s } { 3 1s } { 3 0s} { 3 1s } { 1 0s } + * + * Therefore we need an addition chain containing 3, 19, 159 (the lengths of the repunits) + * We use: 1, 2, [3], 6, 8, 16, [19], 35, 70, 140, [159] + */ + + int[] x1 = this.x; + if (Nat192.isZero(x1) || Nat192.isOne(x1)) + { + return this; + } + + int[] x2 = Nat192.create(); + SecP192K1Field.square(x1, x2); + SecP192K1Field.multiply(x2, x1, x2); + int[] x3 = Nat192.create(); + SecP192K1Field.square(x2, x3); + SecP192K1Field.multiply(x3, x1, x3); + int[] x6 = Nat192.create(); + SecP192K1Field.squareN(x3, 3, x6); + SecP192K1Field.multiply(x6, x3, x6); + int[] x8 = x6; + SecP192K1Field.squareN(x6, 2, x8); + SecP192K1Field.multiply(x8, x2, x8); + int[] x16 = x2; + SecP192K1Field.squareN(x8, 8, x16); + SecP192K1Field.multiply(x16, x8, x16); + int[] x19 = x8; + SecP192K1Field.squareN(x16, 3, x19); + SecP192K1Field.multiply(x19, x3, x19); + int[] x35 = Nat192.create(); + SecP192K1Field.squareN(x19, 16, x35); + SecP192K1Field.multiply(x35, x16, x35); + int[] x70 = x16; + SecP192K1Field.squareN(x35, 35, x70); + SecP192K1Field.multiply(x70, x35, x70); + int[] x140 = x35; + SecP192K1Field.squareN(x70, 70, x140); + SecP192K1Field.multiply(x140, x70, x140); + int[] x159 = x70; + SecP192K1Field.squareN(x140, 19, x159); + SecP192K1Field.multiply(x159, x19, x159); + + int[] t1 = x159; + SecP192K1Field.squareN(t1, 20, t1); + SecP192K1Field.multiply(t1, x19, t1); + SecP192K1Field.squareN(t1, 4, t1); + SecP192K1Field.multiply(t1, x3, t1); + SecP192K1Field.squareN(t1, 6, t1); + SecP192K1Field.multiply(t1, x3, t1); + SecP192K1Field.square(t1, t1); + + int[] t2 = x3; + SecP192K1Field.square(t1, t2); + + return Nat192.eq(x1, t2) ? new SecP192K1FieldElement(t1) : null; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecP192K1FieldElement)) + { + return false; + } + + SecP192K1FieldElement o = (SecP192K1FieldElement)other; + return Nat192.eq(x, o.x); + } + + public int hashCode() + { + return Q.hashCode() ^ Arrays.hashCode(x, 0, 6); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Point.java new file mode 100644 index 0000000..eaa9727 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Point.java @@ -0,0 +1,298 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat192; + +public class SecP192K1Point extends ECPoint.AbstractFp +{ + /** + * 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 SecP192K1Point(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 SecP192K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecP192K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, + boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecP192K1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + // 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(); + + SecP192K1FieldElement X1 = (SecP192K1FieldElement)this.x, Y1 = (SecP192K1FieldElement)this.y; + SecP192K1FieldElement X2 = (SecP192K1FieldElement)b.getXCoord(), Y2 = (SecP192K1FieldElement)b.getYCoord(); + + SecP192K1FieldElement Z1 = (SecP192K1FieldElement)this.zs[0]; + SecP192K1FieldElement Z2 = (SecP192K1FieldElement)b.getZCoord(0); + + int c; + int[] tt1 = Nat192.createExt(); + int[] t2 = Nat192.create(); + int[] t3 = Nat192.create(); + int[] t4 = Nat192.create(); + + boolean Z1IsOne = Z1.isOne(); + int[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP192K1Field.square(Z1.x, S2); + + U2 = t2; + SecP192K1Field.multiply(S2, X2.x, U2); + + SecP192K1Field.multiply(S2, Z1.x, S2); + SecP192K1Field.multiply(S2, Y2.x, S2); + } + + boolean Z2IsOne = Z2.isOne(); + int[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP192K1Field.square(Z2.x, S1); + + U1 = tt1; + SecP192K1Field.multiply(S1, X1.x, U1); + + SecP192K1Field.multiply(S1, Z2.x, S1); + SecP192K1Field.multiply(S1, Y1.x, S1); + } + + int[] H = Nat192.create(); + SecP192K1Field.subtract(U1, U2, H); + + int[] R = t2; + SecP192K1Field.subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat192.isZero(H)) + { + if (Nat192.isZero(R)) + { + // 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(); + } + + int[] HSquared = t3; + SecP192K1Field.square(H, HSquared); + + int[] G = Nat192.create(); + SecP192K1Field.multiply(HSquared, H, G); + + int[] V = t3; + SecP192K1Field.multiply(HSquared, U1, V); + + SecP192K1Field.negate(G, G); + Nat192.mul(S1, G, tt1); + + c = Nat192.addBothTo(V, V, G); + SecP192K1Field.reduce32(c, G); + + SecP192K1FieldElement X3 = new SecP192K1FieldElement(t4); + SecP192K1Field.square(R, X3.x); + SecP192K1Field.subtract(X3.x, G, X3.x); + + SecP192K1FieldElement Y3 = new SecP192K1FieldElement(G); + SecP192K1Field.subtract(V, X3.x, Y3.x); + SecP192K1Field.multiplyAddToExt(Y3.x, R, tt1); + SecP192K1Field.reduce(tt1, Y3.x); + + SecP192K1FieldElement Z3 = new SecP192K1FieldElement(H); + if (!Z1IsOne) + { + SecP192K1Field.multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP192K1Field.multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP192K1Point(curve, X3, Y3, zs, this.withCompression); + } + + // B.3 pg 62 + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + SecP192K1FieldElement Y1 = (SecP192K1FieldElement)this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + SecP192K1FieldElement X1 = (SecP192K1FieldElement)this.x, Z1 = (SecP192K1FieldElement)this.zs[0]; + + int c; + + int[] Y1Squared = Nat192.create(); + SecP192K1Field.square(Y1.x, Y1Squared); + + int[] T = Nat192.create(); + SecP192K1Field.square(Y1Squared, T); + + int[] M = Nat192.create(); + SecP192K1Field.square(X1.x, M); + c = Nat192.addBothTo(M, M, M); + SecP192K1Field.reduce32(c, M); + + int[] S = Y1Squared; + SecP192K1Field.multiply(Y1Squared, X1.x, S); + c = Nat.shiftUpBits(6, S, 2, 0); + SecP192K1Field.reduce32(c, S); + + int[] t1 = Nat192.create(); + c = Nat.shiftUpBits(6, T, 3, 0, t1); + SecP192K1Field.reduce32(c, t1); + + SecP192K1FieldElement X3 = new SecP192K1FieldElement(T); + SecP192K1Field.square(M, X3.x); + SecP192K1Field.subtract(X3.x, S, X3.x); + SecP192K1Field.subtract(X3.x, S, X3.x); + + SecP192K1FieldElement Y3 = new SecP192K1FieldElement(S); + SecP192K1Field.subtract(S, X3.x, Y3.x); + SecP192K1Field.multiply(Y3.x, M, Y3.x); + SecP192K1Field.subtract(Y3.x, t1, Y3.x); + + SecP192K1FieldElement Z3 = new SecP192K1FieldElement(M); + SecP192K1Field.twice(Y1.x, Z3.x); + if (!Z1.isOne()) + { + SecP192K1Field.multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP192K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, this.withCompression); + } + + 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; + } + + return twice().add(b); + } + + public ECPoint threeTimes() + { + if (this.isInfinity() || this.y.isZero()) + { + return this; + } + + // NOTE: Be careful about recursions between twicePlus and threeTimes + return twice().add(this); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP192K1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Curve.java new file mode 100644 index 0000000..be67100 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Curve.java @@ -0,0 +1,80 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecP192R1Curve extends ECCurve.AbstractFp +{ + public static final BigInteger q = new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF")); + + private static final int SecP192R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP192R1Point infinity; + + public SecP192R1Curve() + { + super(q); + + this.infinity = new SecP192R1Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC"))); + this.b = fromBigInteger(new BigInteger(1, + Hex.decode("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1"))); + this.order = new BigInteger(1, Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831")); + this.cofactor = BigInteger.valueOf(1); + + this.coord = SecP192R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecP192R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public BigInteger getQ() + { + return q; + } + + public int getFieldSize() + { + return q.bitLength(); + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecP192R1FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecP192R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecP192R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Field.java new file mode 100644 index 0000000..c8f5eed --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Field.java @@ -0,0 +1,286 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat192; + +public class SecP192R1Field +{ + private static final long M = 0xFFFFFFFFL; + + // 2^192 - 2^64 - 1 + static final int[] P = new int[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + static final int[] PExt = new int[]{ 0x00000001, 0x00000000, 0x00000002, 0x00000000, 0x00000001, + 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static final int[] PExtInv = new int[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFE, + 0xFFFFFFFF, 0x00000001, 0x00000000, 0x00000002 }; + private static final int P5 = 0xFFFFFFFF; + private static final int PExt11 = 0xFFFFFFFF; + + public static void add(int[] x, int[] y, int[] z) + { + int c = Nat192.add(x, y, z); + if (c != 0 || (z[5] == P5 && Nat192.gte(z, P))) + { + addPInvTo(z); + } + } + + public static void addExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.add(12, xx, yy, zz); + if (c != 0 || (zz[11] == PExt11 && Nat.gte(12, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(12, zz, PExtInv.length); + } + } + } + + public static void addOne(int[] x, int[] z) + { + int c = Nat.inc(6, x, z); + if (c != 0 || (z[5] == P5 && Nat192.gte(z, P))) + { + addPInvTo(z); + } + } + + public static int[] fromBigInteger(BigInteger x) + { + int[] z = Nat192.fromBigInteger(x); + if (z[5] == P5 && Nat192.gte(z, P)) + { + Nat192.subFrom(P, z); + } + return z; + } + + public static void half(int[] x, int[] z) + { + if ((x[0] & 1) == 0) + { + Nat.shiftDownBit(6, x, 0, z); + } + else + { + int c = Nat192.add(x, P, z); + Nat.shiftDownBit(6, z, c); + } + } + + public static void multiply(int[] x, int[] y, int[] z) + { + int[] tt = Nat192.createExt(); + Nat192.mul(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(int[] x, int[] y, int[] zz) + { + int c = Nat192.mulAddTo(x, y, zz); + if (c != 0 || (zz[11] == PExt11 && Nat.gte(12, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(12, zz, PExtInv.length); + } + } + } + + public static void negate(int[] x, int[] z) + { + if (Nat192.isZero(x)) + { + Nat192.zero(z); + } + else + { + Nat192.sub(P, x, z); + } + } + + public static void reduce(int[] xx, int[] z) + { + long xx06 = xx[6] & M, xx07 = xx[7] & M, xx08 = xx[8] & M; + long xx09 = xx[9] & M, xx10 = xx[10] & M, xx11 = xx[11] & M; + + long t0 = xx06 + xx10; + long t1 = xx07 + xx11; + + long cc = 0; + cc += (xx[0] & M) + t0; + int z0 = (int)cc; + cc >>= 32; + cc += (xx[1] & M) + t1; + z[1] = (int)cc; + cc >>= 32; + + t0 += xx08; + t1 += xx09; + + cc += (xx[2] & M) + t0; + long z2 = cc & M; + cc >>= 32; + cc += (xx[3] & M) + t1; + z[3] = (int)cc; + cc >>= 32; + + t0 -= xx06; + t1 -= xx07; + + cc += (xx[4] & M) + t0; + z[4] = (int)cc; + cc >>= 32; + cc += (xx[5] & M) + t1; + z[5] = (int)cc; + cc >>= 32; + + z2 += cc; + + cc += (z0 & M); + z[0] = (int)cc; + cc >>= 32; + if (cc != 0) + { + cc += (z[1] & M); + z[1] = (int)cc; + z2 += cc >> 32; + } + z[2] = (int)z2; + cc = z2 >> 32; + +// assert cc == 0 || cc == 1; + + if ((cc != 0 && Nat.incAt(6, z, 3) != 0) + || (z[5] == P5 && Nat192.gte(z, P))) + { + addPInvTo(z); + } + } + + public static void reduce32(int x, int[] z) + { + long cc = 0; + + if (x != 0) + { + long xx06 = x & M; + + cc += (z[0] & M) + xx06; + z[0] = (int)cc; + cc >>= 32; + if (cc != 0) + { + cc += (z[1] & M); + z[1] = (int)cc; + cc >>= 32; + } + cc += (z[2] & M) + xx06; + z[2] = (int)cc; + cc >>= 32; + +// assert cc == 0 || cc == 1; + } + + if ((cc != 0 && Nat.incAt(6, z, 3) != 0) + || (z[5] == P5 && Nat192.gte(z, P))) + { + addPInvTo(z); + } + } + + public static void square(int[] x, int[] z) + { + int[] tt = Nat192.createExt(); + Nat192.square(x, tt); + reduce(tt, z); + } + + public static void squareN(int[] x, int n, int[] z) + { +// assert n > 0; + + int[] tt = Nat192.createExt(); + Nat192.square(x, tt); + reduce(tt, z); + + while (--n > 0) + { + Nat192.square(z, tt); + reduce(tt, z); + } + } + + public static void subtract(int[] x, int[] y, int[] z) + { + int c = Nat192.sub(x, y, z); + if (c != 0) + { + subPInvFrom(z); + } + } + + public static void subtractExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.sub(12, xx, yy, zz); + if (c != 0) + { + if (Nat.subFrom(PExtInv.length, PExtInv, zz) != 0) + { + Nat.decAt(12, zz, PExtInv.length); + } + } + } + + public static void twice(int[] x, int[] z) + { + int c = Nat.shiftUpBit(6, x, 0, z); + if (c != 0 || (z[5] == P5 && Nat192.gte(z, P))) + { + addPInvTo(z); + } + } + + private static void addPInvTo(int[] z) + { + long c = (z[0] & M) + 1; + z[0] = (int)c; + c >>= 32; + if (c != 0) + { + c += (z[1] & M); + z[1] = (int)c; + c >>= 32; + } + c += (z[2] & M) + 1; + z[2] = (int)c; + c >>= 32; + if (c != 0) + { + Nat.incAt(6, z, 3); + } + } + + private static void subPInvFrom(int[] z) + { + long c = (z[0] & M) - 1; + z[0] = (int)c; + c >>= 32; + if (c != 0) + { + c += (z[1] & M); + z[1] = (int)c; + c >>= 32; + } + c += (z[2] & M) - 1; + z[2] = (int)c; + c >>= 32; + if (c != 0) + { + Nat.decAt(6, z, 3); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1FieldElement.java new file mode 100644 index 0000000..68c8080 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1FieldElement.java @@ -0,0 +1,190 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Mod; +import org.bouncycastle.math.raw.Nat192; +import org.bouncycastle.util.Arrays; + +public class SecP192R1FieldElement extends ECFieldElement +{ + public static final BigInteger Q = SecP192R1Curve.q; + + protected int[] x; + + public SecP192R1FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0) + { + throw new IllegalArgumentException("x value invalid for SecP192R1FieldElement"); + } + + this.x = SecP192R1Field.fromBigInteger(x); + } + + public SecP192R1FieldElement() + { + this.x = Nat192.create(); + } + + protected SecP192R1FieldElement(int[] x) + { + this.x = x; + } + + public boolean isZero() + { + return Nat192.isZero(x); + } + + public boolean isOne() + { + return Nat192.isOne(x); + } + + public boolean testBitZero() + { + return Nat192.getBit(x, 0) == 1; + } + + public BigInteger toBigInteger() + { + return Nat192.toBigInteger(x); + } + + public String getFieldName() + { + return "SecP192R1Field"; + } + + public int getFieldSize() + { + return Q.bitLength(); + } + + public ECFieldElement add(ECFieldElement b) + { + int[] z = Nat192.create(); + SecP192R1Field.add(x, ((SecP192R1FieldElement)b).x, z); + return new SecP192R1FieldElement(z); + } + + public ECFieldElement addOne() + { + int[] z = Nat192.create(); + SecP192R1Field.addOne(x, z); + return new SecP192R1FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + int[] z = Nat192.create(); + SecP192R1Field.subtract(x, ((SecP192R1FieldElement)b).x, z); + return new SecP192R1FieldElement(z); + } + + public ECFieldElement multiply(ECFieldElement b) + { + int[] z = Nat192.create(); + SecP192R1Field.multiply(x, ((SecP192R1FieldElement)b).x, z); + return new SecP192R1FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { +// return multiply(b.invert()); + int[] z = Nat192.create(); + Mod.invert(SecP192R1Field.P, ((SecP192R1FieldElement)b).x, z); + SecP192R1Field.multiply(z, x, z); + return new SecP192R1FieldElement(z); + } + + public ECFieldElement negate() + { + int[] z = Nat192.create(); + SecP192R1Field.negate(x, z); + return new SecP192R1FieldElement(z); + } + + public ECFieldElement square() + { + int[] z = Nat192.create(); + SecP192R1Field.square(x, z); + return new SecP192R1FieldElement(z); + } + + public ECFieldElement invert() + { +// return new SecP192R1FieldElement(toBigInteger().modInverse(Q)); + int[] z = Nat192.create(); + Mod.invert(SecP192R1Field.P, x, z); + return new SecP192R1FieldElement(z); + } + + // D.1.4 91 + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public ECFieldElement sqrt() + { + // Raise this element to the exponent 2^190 - 2^62 + + int[] x1 = this.x; + if (Nat192.isZero(x1) || Nat192.isOne(x1)) + { + return this; + } + + int[] t1 = Nat192.create(); + int[] t2 = Nat192.create(); + + SecP192R1Field.square(x1, t1); + SecP192R1Field.multiply(t1, x1, t1); + + SecP192R1Field.squareN(t1, 2, t2); + SecP192R1Field.multiply(t2, t1, t2); + + SecP192R1Field.squareN(t2, 4, t1); + SecP192R1Field.multiply(t1, t2, t1); + + SecP192R1Field.squareN(t1, 8, t2); + SecP192R1Field.multiply(t2, t1, t2); + + SecP192R1Field.squareN(t2, 16, t1); + SecP192R1Field.multiply(t1, t2, t1); + + SecP192R1Field.squareN(t1, 32, t2); + SecP192R1Field.multiply(t2, t1, t2); + + SecP192R1Field.squareN(t2, 64, t1); + SecP192R1Field.multiply(t1, t2, t1); + + SecP192R1Field.squareN(t1, 62, t1); + SecP192R1Field.square(t1, t2); + + return Nat192.eq(x1, t2) ? new SecP192R1FieldElement(t1) : null; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecP192R1FieldElement)) + { + return false; + } + + SecP192R1FieldElement o = (SecP192R1FieldElement)other; + return Nat192.eq(x, o.x); + } + + public int hashCode() + { + return Q.hashCode() ^ Arrays.hashCode(x, 0, 6); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Point.java new file mode 100644 index 0000000..3ed72f8 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Point.java @@ -0,0 +1,310 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat192; + +public class SecP192R1Point extends ECPoint.AbstractFp +{ + /** + * 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 SecP192R1Point(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 SecP192R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecP192R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecP192R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + // 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(); + + SecP192R1FieldElement X1 = (SecP192R1FieldElement)this.x, Y1 = (SecP192R1FieldElement)this.y; + SecP192R1FieldElement X2 = (SecP192R1FieldElement)b.getXCoord(), Y2 = (SecP192R1FieldElement)b.getYCoord(); + + SecP192R1FieldElement Z1 = (SecP192R1FieldElement)this.zs[0]; + SecP192R1FieldElement Z2 = (SecP192R1FieldElement)b.getZCoord(0); + + int c; + int[] tt1 = Nat192.createExt(); + int[] t2 = Nat192.create(); + int[] t3 = Nat192.create(); + int[] t4 = Nat192.create(); + + boolean Z1IsOne = Z1.isOne(); + int[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP192R1Field.square(Z1.x, S2); + + U2 = t2; + SecP192R1Field.multiply(S2, X2.x, U2); + + SecP192R1Field.multiply(S2, Z1.x, S2); + SecP192R1Field.multiply(S2, Y2.x, S2); + } + + boolean Z2IsOne = Z2.isOne(); + int[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP192R1Field.square(Z2.x, S1); + + U1 = tt1; + SecP192R1Field.multiply(S1, X1.x, U1); + + SecP192R1Field.multiply(S1, Z2.x, S1); + SecP192R1Field.multiply(S1, Y1.x, S1); + } + + int[] H = Nat192.create(); + SecP192R1Field.subtract(U1, U2, H); + + int[] R = t2; + SecP192R1Field.subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat192.isZero(H)) + { + if (Nat192.isZero(R)) + { + // 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(); + } + + int[] HSquared = t3; + SecP192R1Field.square(H, HSquared); + + int[] G = Nat192.create(); + SecP192R1Field.multiply(HSquared, H, G); + + int[] V = t3; + SecP192R1Field.multiply(HSquared, U1, V); + + SecP192R1Field.negate(G, G); + Nat192.mul(S1, G, tt1); + + c = Nat192.addBothTo(V, V, G); + SecP192R1Field.reduce32(c, G); + + SecP192R1FieldElement X3 = new SecP192R1FieldElement(t4); + SecP192R1Field.square(R, X3.x); + SecP192R1Field.subtract(X3.x, G, X3.x); + + SecP192R1FieldElement Y3 = new SecP192R1FieldElement(G); + SecP192R1Field.subtract(V, X3.x, Y3.x); + SecP192R1Field.multiplyAddToExt(Y3.x, R, tt1); + SecP192R1Field.reduce(tt1, Y3.x); + + SecP192R1FieldElement Z3 = new SecP192R1FieldElement(H); + if (!Z1IsOne) + { + SecP192R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP192R1Field.multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[]{ Z3 }; + + return new SecP192R1Point(curve, X3, Y3, zs, this.withCompression); + } + + // B.3 pg 62 + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + SecP192R1FieldElement Y1 = (SecP192R1FieldElement)this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + SecP192R1FieldElement X1 = (SecP192R1FieldElement)this.x, Z1 = (SecP192R1FieldElement)this.zs[0]; + + int c; + int[] t1 = Nat192.create(); + int[] t2 = Nat192.create(); + + int[] Y1Squared = Nat192.create(); + SecP192R1Field.square(Y1.x, Y1Squared); + + int[] T = Nat192.create(); + SecP192R1Field.square(Y1Squared, T); + + boolean Z1IsOne = Z1.isOne(); + + int[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP192R1Field.square(Z1.x, Z1Squared); + } + + SecP192R1Field.subtract(X1.x, Z1Squared, t1); + + int[] M = t2; + SecP192R1Field.add(X1.x, Z1Squared, M); + SecP192R1Field.multiply(M, t1, M); + c = Nat192.addBothTo(M, M, M); + SecP192R1Field.reduce32(c, M); + + int[] S = Y1Squared; + SecP192R1Field.multiply(Y1Squared, X1.x, S); + c = Nat.shiftUpBits(6, S, 2, 0); + SecP192R1Field.reduce32(c, S); + + c = Nat.shiftUpBits(6, T, 3, 0, t1); + SecP192R1Field.reduce32(c, t1); + + SecP192R1FieldElement X3 = new SecP192R1FieldElement(T); + SecP192R1Field.square(M, X3.x); + SecP192R1Field.subtract(X3.x, S, X3.x); + SecP192R1Field.subtract(X3.x, S, X3.x); + + SecP192R1FieldElement Y3 = new SecP192R1FieldElement(S); + SecP192R1Field.subtract(S, X3.x, Y3.x); + SecP192R1Field.multiply(Y3.x, M, Y3.x); + SecP192R1Field.subtract(Y3.x, t1, Y3.x); + + SecP192R1FieldElement Z3 = new SecP192R1FieldElement(M); + SecP192R1Field.twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP192R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP192R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + 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; + } + + return twice().add(b); + } + + public ECPoint threeTimes() + { + if (this.isInfinity() || this.y.isZero()) + { + return this; + } + + // NOTE: Be careful about recursions between twicePlus and threeTimes + return twice().add(this); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP192R1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Curve.java new file mode 100644 index 0000000..ad733da --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Curve.java @@ -0,0 +1,78 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecP224K1Curve extends ECCurve.AbstractFp +{ + public static final BigInteger q = new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D")); + + private static final int SECP224K1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP224K1Point infinity; + + public SecP224K1Curve() + { + super(q); + + this.infinity = new SecP224K1Point(this, null, null); + + this.a = fromBigInteger(ECConstants.ZERO); + this.b = fromBigInteger(BigInteger.valueOf(5)); + this.order = new BigInteger(1, Hex.decode("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7")); + this.cofactor = BigInteger.valueOf(1); + this.coord = SECP224K1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecP224K1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public BigInteger getQ() + { + return q; + } + + public int getFieldSize() + { + return q.bitLength(); + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecP224K1FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecP224K1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecP224K1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Field.java new file mode 100644 index 0000000..0146fec --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Field.java @@ -0,0 +1,178 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat224; + +public class SecP224K1Field +{ + // 2^224 - 2^32 - 2^12 - 2^11 - 2^9 - 2^7 - 2^4 - 2 - 1 + static final int[] P = new int[]{ 0xFFFFE56D, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF }; + static final int[] PExt = new int[]{ 0x02C23069, 0x00003526, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFCADA, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static final int[] PExtInv = new int[]{ 0xFD3DCF97, 0xFFFFCAD9, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00003525, 0x00000002 }; + private static final int P6 = 0xFFFFFFFF; + private static final int PExt13 = 0xFFFFFFFF; + private static final int PInv33 = 0x1A93; + + public static void add(int[] x, int[] y, int[] z) + { + int c = Nat224.add(x, y, z); + if (c != 0 || (z[6] == P6 && Nat224.gte(z, P))) + { + Nat.add33To(7, PInv33, z); + } + } + + public static void addExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.add(14, xx, yy, zz); + if (c != 0 || (zz[13] == PExt13 && Nat.gte(14, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(14, zz, PExtInv.length); + } + } + } + + public static void addOne(int[] x, int[] z) + { + int c = Nat.inc(7, x, z); + if (c != 0 || (z[6] == P6 && Nat224.gte(z, P))) + { + Nat.add33To(7, PInv33, z); + } + } + + public static int[] fromBigInteger(BigInteger x) + { + int[] z = Nat224.fromBigInteger(x); + if (z[6] == P6 && Nat224.gte(z, P)) + { + Nat.add33To(7, PInv33, z); + } + return z; + } + + public static void half(int[] x, int[] z) + { + if ((x[0] & 1) == 0) + { + Nat.shiftDownBit(7, x, 0, z); + } + else + { + int c = Nat224.add(x, P, z); + Nat.shiftDownBit(7, z, c); + } + } + + public static void multiply(int[] x, int[] y, int[] z) + { + int[] tt = Nat224.createExt(); + Nat224.mul(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(int[] x, int[] y, int[] zz) + { + int c = Nat224.mulAddTo(x, y, zz); + if (c != 0 || (zz[13] == PExt13 && Nat.gte(14, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(14, zz, PExtInv.length); + } + } + } + + public static void negate(int[] x, int[] z) + { + if (Nat224.isZero(x)) + { + Nat224.zero(z); + } + else + { + Nat224.sub(P, x, z); + } + } + + public static void reduce(int[] xx, int[] z) + { + long cc = Nat224.mul33Add(PInv33, xx, 7, xx, 0, z, 0); + int c = Nat224.mul33DWordAdd(PInv33, cc, z, 0); + + // assert c == 0L || c == 1L; + + if (c != 0 || (z[6] == P6 && Nat224.gte(z, P))) + { + Nat.add33To(7, PInv33, z); + } + } + + public static void reduce32(int x, int[] z) + { + if ((x != 0 && Nat224.mul33WordAdd(PInv33, x, z, 0) != 0) + || (z[6] == P6 && Nat224.gte(z, P))) + { + Nat.add33To(7, PInv33, z); + } + } + + public static void square(int[] x, int[] z) + { + int[] tt = Nat224.createExt(); + Nat224.square(x, tt); + reduce(tt, z); + } + + public static void squareN(int[] x, int n, int[] z) + { +// assert n > 0; + + int[] tt = Nat224.createExt(); + Nat224.square(x, tt); + reduce(tt, z); + + while (--n > 0) + { + Nat224.square(z, tt); + reduce(tt, z); + } + } + + public static void subtract(int[] x, int[] y, int[] z) + { + int c = Nat224.sub(x, y, z); + if (c != 0) + { + Nat.sub33From(7, PInv33, z); + } + } + + public static void subtractExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.sub(14, xx, yy, zz); + if (c != 0) + { + if (Nat.subFrom(PExtInv.length, PExtInv, zz) != 0) + { + Nat.decAt(14, zz, PExtInv.length); + } + } + } + + public static void twice(int[] x, int[] z) + { + int c = Nat.shiftUpBit(7, x, 0, z); + if (c != 0 || (z[6] == P6 && Nat224.gte(z, P))) + { + Nat.add33To(7, PInv33, z); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1FieldElement.java new file mode 100644 index 0000000..73f1999 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1FieldElement.java @@ -0,0 +1,243 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Mod; +import org.bouncycastle.math.raw.Nat224; +import org.bouncycastle.util.Arrays; + +public class SecP224K1FieldElement extends ECFieldElement +{ + public static final BigInteger Q = SecP224K1Curve.q; + + // Calculated as ECConstants.TWO.modPow(Q.shiftRight(2), Q) + private static final int[] PRECOMP_POW2 = new int[]{ 0x33bfd202, 0xdcfad133, 0x2287624a, 0xc3811ba8, + 0xa85558fc, 0x1eaef5d7, 0x8edf154c }; + + protected int[] x; + + public SecP224K1FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0) + { + throw new IllegalArgumentException("x value invalid for SecP224K1FieldElement"); + } + + this.x = SecP224K1Field.fromBigInteger(x); + } + + public SecP224K1FieldElement() + { + this.x = Nat224.create(); + } + + protected SecP224K1FieldElement(int[] x) + { + this.x = x; + } + + public boolean isZero() + { + return Nat224.isZero(x); + } + + public boolean isOne() + { + return Nat224.isOne(x); + } + + public boolean testBitZero() + { + return Nat224.getBit(x, 0) == 1; + } + + public BigInteger toBigInteger() + { + return Nat224.toBigInteger(x); + } + + public String getFieldName() + { + return "SecP224K1Field"; + } + + public int getFieldSize() + { + return Q.bitLength(); + } + + public ECFieldElement add(ECFieldElement b) + { + int[] z = Nat224.create(); + SecP224K1Field.add(x, ((SecP224K1FieldElement)b).x, z); + return new SecP224K1FieldElement(z); + } + + public ECFieldElement addOne() + { + int[] z = Nat224.create(); + SecP224K1Field.addOne(x, z); + return new SecP224K1FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + int[] z = Nat224.create(); + SecP224K1Field.subtract(x, ((SecP224K1FieldElement)b).x, z); + return new SecP224K1FieldElement(z); + } + + public ECFieldElement multiply(ECFieldElement b) + { + int[] z = Nat224.create(); + SecP224K1Field.multiply(x, ((SecP224K1FieldElement)b).x, z); + return new SecP224K1FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { +// return multiply(b.invert()); + int[] z = Nat224.create(); + Mod.invert(SecP224K1Field.P, ((SecP224K1FieldElement)b).x, z); + SecP224K1Field.multiply(z, x, z); + return new SecP224K1FieldElement(z); + } + + public ECFieldElement negate() + { + int[] z = Nat224.create(); + SecP224K1Field.negate(x, z); + return new SecP224K1FieldElement(z); + } + + public ECFieldElement square() + { + int[] z = Nat224.create(); + SecP224K1Field.square(x, z); + return new SecP224K1FieldElement(z); + } + + public ECFieldElement invert() + { +// return new SecP224K1FieldElement(toBigInteger().modInverse(Q)); + int[] z = Nat224.create(); + Mod.invert(SecP224K1Field.P, x, z); + return new SecP224K1FieldElement(z); + } + + // D.1.4 91 + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public ECFieldElement sqrt() + { + /* + * Q == 8m + 5, so we use Pocklington's method for this case. + * + * First, raise this element to the exponent 2^221 - 2^29 - 2^9 - 2^8 - 2^6 - 2^4 - 2^1 (i.e. m + 1) + * + * Breaking up the exponent's binary representation into "repunits", we get: + * { 191 1s } { 1 0s } { 19 1s } { 2 0s } { 1 1s } { 1 0s} { 1 1s } { 1 0s} { 3 1s } { 1 0s} + * + * Therefore we need an addition chain containing 1, 3, 19, 191 (the lengths of the repunits) + * We use: [1], 2, [3], 4, 8, 11, [19], 23, 42, 84, 107, [191] + */ + + int[] x1 = this.x; + if (Nat224.isZero(x1) || Nat224.isOne(x1)) + { + return this; + } + + int[] x2 = Nat224.create(); + SecP224K1Field.square(x1, x2); + SecP224K1Field.multiply(x2, x1, x2); + int[] x3 = x2; + SecP224K1Field.square(x2, x3); + SecP224K1Field.multiply(x3, x1, x3); + int[] x4 = Nat224.create(); + SecP224K1Field.square(x3, x4); + SecP224K1Field.multiply(x4, x1, x4); + int[] x8 = Nat224.create(); + SecP224K1Field.squareN(x4, 4, x8); + SecP224K1Field.multiply(x8, x4, x8); + int[] x11 = Nat224.create(); + SecP224K1Field.squareN(x8, 3, x11); + SecP224K1Field.multiply(x11, x3, x11); + int[] x19 = x11; + SecP224K1Field.squareN(x11, 8, x19); + SecP224K1Field.multiply(x19, x8, x19); + int[] x23 = x8; + SecP224K1Field.squareN(x19, 4, x23); + SecP224K1Field.multiply(x23, x4, x23); + int[] x42 = x4; + SecP224K1Field.squareN(x23, 19, x42); + SecP224K1Field.multiply(x42, x19, x42); + int[] x84 = Nat224.create(); + SecP224K1Field.squareN(x42, 42, x84); + SecP224K1Field.multiply(x84, x42, x84); + int[] x107 = x42; + SecP224K1Field.squareN(x84, 23, x107); + SecP224K1Field.multiply(x107, x23, x107); + int[] x191 = x23; + SecP224K1Field.squareN(x107, 84, x191); + SecP224K1Field.multiply(x191, x84, x191); + + int[] t1 = x191; + SecP224K1Field.squareN(t1, 20, t1); + SecP224K1Field.multiply(t1, x19, t1); + SecP224K1Field.squareN(t1, 3, t1); + SecP224K1Field.multiply(t1, x1, t1); + SecP224K1Field.squareN(t1, 2, t1); + SecP224K1Field.multiply(t1, x1, t1); + SecP224K1Field.squareN(t1, 4, t1); + SecP224K1Field.multiply(t1, x3, t1); + SecP224K1Field.square(t1, t1); + + int[] t2 = x84; + SecP224K1Field.square(t1, t2); + + if (Nat224.eq(x1, t2)) + { + return new SecP224K1FieldElement(t1); + } + + /* + * If the first guess is incorrect, we multiply by a precomputed power of 2 to get the second guess, + * which is ((4x)^(m + 1))/2 mod Q + */ + SecP224K1Field.multiply(t1, PRECOMP_POW2, t1); + + SecP224K1Field.square(t1, t2); + + if (Nat224.eq(x1, t2)) + { + return new SecP224K1FieldElement(t1); + } + + return null; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecP224K1FieldElement)) + { + return false; + } + + SecP224K1FieldElement o = (SecP224K1FieldElement)other; + return Nat224.eq(x, o.x); + } + + public int hashCode() + { + return Q.hashCode() ^ Arrays.hashCode(x, 0, 7); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Point.java new file mode 100644 index 0000000..114623d --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Point.java @@ -0,0 +1,298 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat224; + +public class SecP224K1Point extends ECPoint.AbstractFp +{ + /** + * 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 SecP224K1Point(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 SecP224K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecP224K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, + boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecP224K1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + // 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(); + + SecP224K1FieldElement X1 = (SecP224K1FieldElement)this.x, Y1 = (SecP224K1FieldElement)this.y; + SecP224K1FieldElement X2 = (SecP224K1FieldElement)b.getXCoord(), Y2 = (SecP224K1FieldElement)b.getYCoord(); + + SecP224K1FieldElement Z1 = (SecP224K1FieldElement)this.zs[0]; + SecP224K1FieldElement Z2 = (SecP224K1FieldElement)b.getZCoord(0); + + int c; + int[] tt1 = Nat224.createExt(); + int[] t2 = Nat224.create(); + int[] t3 = Nat224.create(); + int[] t4 = Nat224.create(); + + boolean Z1IsOne = Z1.isOne(); + int[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP224K1Field.square(Z1.x, S2); + + U2 = t2; + SecP224K1Field.multiply(S2, X2.x, U2); + + SecP224K1Field.multiply(S2, Z1.x, S2); + SecP224K1Field.multiply(S2, Y2.x, S2); + } + + boolean Z2IsOne = Z2.isOne(); + int[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP224K1Field.square(Z2.x, S1); + + U1 = tt1; + SecP224K1Field.multiply(S1, X1.x, U1); + + SecP224K1Field.multiply(S1, Z2.x, S1); + SecP224K1Field.multiply(S1, Y1.x, S1); + } + + int[] H = Nat224.create(); + SecP224K1Field.subtract(U1, U2, H); + + int[] R = t2; + SecP224K1Field.subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat224.isZero(H)) + { + if (Nat224.isZero(R)) + { + // 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(); + } + + int[] HSquared = t3; + SecP224K1Field.square(H, HSquared); + + int[] G = Nat224.create(); + SecP224K1Field.multiply(HSquared, H, G); + + int[] V = t3; + SecP224K1Field.multiply(HSquared, U1, V); + + SecP224K1Field.negate(G, G); + Nat224.mul(S1, G, tt1); + + c = Nat224.addBothTo(V, V, G); + SecP224K1Field.reduce32(c, G); + + SecP224K1FieldElement X3 = new SecP224K1FieldElement(t4); + SecP224K1Field.square(R, X3.x); + SecP224K1Field.subtract(X3.x, G, X3.x); + + SecP224K1FieldElement Y3 = new SecP224K1FieldElement(G); + SecP224K1Field.subtract(V, X3.x, Y3.x); + SecP224K1Field.multiplyAddToExt(Y3.x, R, tt1); + SecP224K1Field.reduce(tt1, Y3.x); + + SecP224K1FieldElement Z3 = new SecP224K1FieldElement(H); + if (!Z1IsOne) + { + SecP224K1Field.multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP224K1Field.multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP224K1Point(curve, X3, Y3, zs, this.withCompression); + } + + // B.3 pg 62 + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + SecP224K1FieldElement Y1 = (SecP224K1FieldElement)this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + SecP224K1FieldElement X1 = (SecP224K1FieldElement)this.x, Z1 = (SecP224K1FieldElement)this.zs[0]; + + int c; + + int[] Y1Squared = Nat224.create(); + SecP224K1Field.square(Y1.x, Y1Squared); + + int[] T = Nat224.create(); + SecP224K1Field.square(Y1Squared, T); + + int[] M = Nat224.create(); + SecP224K1Field.square(X1.x, M); + c = Nat224.addBothTo(M, M, M); + SecP224K1Field.reduce32(c, M); + + int[] S = Y1Squared; + SecP224K1Field.multiply(Y1Squared, X1.x, S); + c = Nat.shiftUpBits(7, S, 2, 0); + SecP224K1Field.reduce32(c, S); + + int[] t1 = Nat224.create(); + c = Nat.shiftUpBits(7, T, 3, 0, t1); + SecP224K1Field.reduce32(c, t1); + + SecP224K1FieldElement X3 = new SecP224K1FieldElement(T); + SecP224K1Field.square(M, X3.x); + SecP224K1Field.subtract(X3.x, S, X3.x); + SecP224K1Field.subtract(X3.x, S, X3.x); + + SecP224K1FieldElement Y3 = new SecP224K1FieldElement(S); + SecP224K1Field.subtract(S, X3.x, Y3.x); + SecP224K1Field.multiply(Y3.x, M, Y3.x); + SecP224K1Field.subtract(Y3.x, t1, Y3.x); + + SecP224K1FieldElement Z3 = new SecP224K1FieldElement(M); + SecP224K1Field.twice(Y1.x, Z3.x); + if (!Z1.isOne()) + { + SecP224K1Field.multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP224K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, this.withCompression); + } + + 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; + } + + return twice().add(b); + } + + public ECPoint threeTimes() + { + if (this.isInfinity() || this.y.isZero()) + { + return this; + } + + // NOTE: Be careful about recursions between twicePlus and threeTimes + return twice().add(this); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP224K1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Curve.java new file mode 100644 index 0000000..c844329 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Curve.java @@ -0,0 +1,80 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecP224R1Curve extends ECCurve.AbstractFp +{ + public static final BigInteger q = new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001")); + + private static final int SecP224R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP224R1Point infinity; + + public SecP224R1Curve() + { + super(q); + + this.infinity = new SecP224R1Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE"))); + this.b = fromBigInteger(new BigInteger(1, + Hex.decode("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4"))); + this.order = new BigInteger(1, Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D")); + this.cofactor = BigInteger.valueOf(1); + + this.coord = SecP224R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecP224R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public BigInteger getQ() + { + return q; + } + + public int getFieldSize() + { + return q.bitLength(); + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecP224R1FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecP224R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecP224R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Field.java new file mode 100644 index 0000000..02a86f0 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Field.java @@ -0,0 +1,298 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat224; + +public class SecP224R1Field +{ + private static final long M = 0xFFFFFFFFL; + + // 2^224 - 2^96 + 1 + static final int[] P = new int[]{ 0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + static final int[] PExt = new int[]{ 0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static final int[] PExtInv = new int[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001 }; + private static final int P6 = 0xFFFFFFFF; + private static final int PExt13 = 0xFFFFFFFF; + + public static void add(int[] x, int[] y, int[] z) + { + int c = Nat224.add(x, y, z); + if (c != 0 || (z[6] == P6 && Nat224.gte(z, P))) + { + addPInvTo(z); + } + } + + public static void addExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.add(14, xx, yy, zz); + if (c != 0 || (zz[13] == PExt13 && Nat.gte(14, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(14, zz, PExtInv.length); + } + } + } + + public static void addOne(int[] x, int[] z) + { + int c = Nat.inc(7, x, z); + if (c != 0 || (z[6] == P6 && Nat224.gte(z, P))) + { + addPInvTo(z); + } + } + + public static int[] fromBigInteger(BigInteger x) + { + int[] z = Nat224.fromBigInteger(x); + if (z[6] == P6 && Nat224.gte(z, P)) + { + Nat224.subFrom(P, z); + } + return z; + } + + public static void half(int[] x, int[] z) + { + if ((x[0] & 1) == 0) + { + Nat.shiftDownBit(7, x, 0, z); + } + else + { + int c = Nat224.add(x, P, z); + Nat.shiftDownBit(7, z, c); + } + } + + public static void multiply(int[] x, int[] y, int[] z) + { + int[] tt = Nat224.createExt(); + Nat224.mul(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(int[] x, int[] y, int[] zz) + { + int c = Nat224.mulAddTo(x, y, zz); + if (c != 0 || (zz[13] == PExt13 && Nat.gte(14, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(14, zz, PExtInv.length); + } + } + } + + public static void negate(int[] x, int[] z) + { + if (Nat224.isZero(x)) + { + Nat224.zero(z); + } + else + { + Nat224.sub(P, x, z); + } + } + + public static void reduce(int[] xx, int[] z) + { + long xx10 = xx[10] & M, xx11 = xx[11] & M, xx12 = xx[12] & M, xx13 = xx[13] & M; + + final long n = 1; + + long t0 = (xx[7] & M) + xx11 - n; + long t1 = (xx[8] & M) + xx12; + long t2 = (xx[9] & M) + xx13; + + long cc = 0; + cc += (xx[0] & M) - t0; + long z0 = cc & M; + cc >>= 32; + cc += (xx[1] & M) - t1; + z[1] = (int)cc; + cc >>= 32; + cc += (xx[2] & M) - t2; + z[2] = (int)cc; + cc >>= 32; + cc += (xx[3] & M) + t0 - xx10; + long z3 = cc & M; + cc >>= 32; + cc += (xx[4] & M) + t1 - xx11; + z[4] = (int)cc; + cc >>= 32; + cc += (xx[5] & M) + t2 - xx12; + z[5] = (int)cc; + cc >>= 32; + cc += (xx[6] & M) + xx10 - xx13; + z[6] = (int)cc; + cc >>= 32; + cc += n; + +// assert cc >= 0; + + z3 += cc; + + z0 -= cc; + z[0] = (int)z0; + cc = z0 >> 32; + if (cc != 0) + { + cc += (z[1] & M); + z[1] = (int)cc; + cc >>= 32; + cc += (z[2] & M); + z[2] = (int)cc; + z3 += cc >> 32; + } + z[3] = (int)z3; + cc = z3 >> 32; + +// assert cc == 0 || cc == 1; + + if ((cc != 0 && Nat.incAt(7, z, 4) != 0) + || (z[6] == P6 && Nat224.gte(z, P))) + { + addPInvTo(z); + } + } + + public static void reduce32(int x, int[] z) + { + long cc = 0; + + if (x != 0) + { + long xx07 = x & M; + + cc += (z[0] & M) - xx07; + z[0] = (int)cc; + cc >>= 32; + if (cc != 0) + { + cc += (z[1] & M); + z[1] = (int)cc; + cc >>= 32; + cc += (z[2] & M); + z[2] = (int)cc; + cc >>= 32; + } + cc += (z[3] & M) + xx07; + z[3] = (int)cc; + cc >>= 32; + +// assert cc == 0 || cc == 1; + } + + if ((cc != 0 && Nat.incAt(7, z, 4) != 0) + || (z[6] == P6 && Nat224.gte(z, P))) + { + addPInvTo(z); + } + } + + public static void square(int[] x, int[] z) + { + int[] tt = Nat224.createExt(); + Nat224.square(x, tt); + reduce(tt, z); + } + + public static void squareN(int[] x, int n, int[] z) + { +// assert n > 0; + + int[] tt = Nat224.createExt(); + Nat224.square(x, tt); + reduce(tt, z); + + while (--n > 0) + { + Nat224.square(z, tt); + reduce(tt, z); + } + } + + public static void subtract(int[] x, int[] y, int[] z) + { + int c = Nat224.sub(x, y, z); + if (c != 0) + { + subPInvFrom(z); + } + } + + public static void subtractExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.sub(14, xx, yy, zz); + if (c != 0) + { + if (Nat.subFrom(PExtInv.length, PExtInv, zz) != 0) + { + Nat.decAt(14, zz, PExtInv.length); + } + } + } + + public static void twice(int[] x, int[] z) + { + int c = Nat.shiftUpBit(7, x, 0, z); + if (c != 0 || (z[6] == P6 && Nat224.gte(z, P))) + { + addPInvTo(z); + } + } + + private static void addPInvTo(int[] z) + { + long c = (z[0] & M) - 1; + z[0] = (int)c; + c >>= 32; + if (c != 0) + { + c += (z[1] & M); + z[1] = (int)c; + c >>= 32; + c += (z[2] & M); + z[2] = (int)c; + c >>= 32; + } + c += (z[3] & M) + 1; + z[3] = (int)c; + c >>= 32; + if (c != 0) + { + Nat.incAt(7, z, 4); + } + } + + private static void subPInvFrom(int[] z) + { + long c = (z[0] & M) + 1; + z[0] = (int)c; + c >>= 32; + if (c != 0) + { + c += (z[1] & M); + z[1] = (int)c; + c >>= 32; + c += (z[2] & M); + z[2] = (int)c; + c >>= 32; + } + c += (z[3] & M) - 1; + z[3] = (int)c; + c >>= 32; + if (c != 0) + { + Nat.decAt(7, z, 4); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1FieldElement.java new file mode 100644 index 0000000..4a28f3d --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1FieldElement.java @@ -0,0 +1,273 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Mod; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat224; +import org.bouncycastle.util.Arrays; + +public class SecP224R1FieldElement extends ECFieldElement +{ + public static final BigInteger Q = SecP224R1Curve.q; + + protected int[] x; + + public SecP224R1FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0) + { + throw new IllegalArgumentException("x value invalid for SecP224R1FieldElement"); + } + + this.x = SecP224R1Field.fromBigInteger(x); + } + + public SecP224R1FieldElement() + { + this.x = Nat224.create(); + } + + protected SecP224R1FieldElement(int[] x) + { + this.x = x; + } + + public boolean isZero() + { + return Nat224.isZero(x); + } + + public boolean isOne() + { + return Nat224.isOne(x); + } + + public boolean testBitZero() + { + return Nat224.getBit(x, 0) == 1; + } + + public BigInteger toBigInteger() + { + return Nat224.toBigInteger(x); + } + + public String getFieldName() + { + return "SecP224R1Field"; + } + + public int getFieldSize() + { + return Q.bitLength(); + } + + public ECFieldElement add(ECFieldElement b) + { + int[] z = Nat224.create(); + SecP224R1Field.add(x, ((SecP224R1FieldElement)b).x, z); + return new SecP224R1FieldElement(z); + } + + public ECFieldElement addOne() + { + int[] z = Nat224.create(); + SecP224R1Field.addOne(x, z); + return new SecP224R1FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + int[] z = Nat224.create(); + SecP224R1Field.subtract(x, ((SecP224R1FieldElement)b).x, z); + return new SecP224R1FieldElement(z); + } + + public ECFieldElement multiply(ECFieldElement b) + { + int[] z = Nat224.create(); + SecP224R1Field.multiply(x, ((SecP224R1FieldElement)b).x, z); + return new SecP224R1FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { +// return multiply(b.invert()); + int[] z = Nat224.create(); + Mod.invert(SecP224R1Field.P, ((SecP224R1FieldElement)b).x, z); + SecP224R1Field.multiply(z, x, z); + return new SecP224R1FieldElement(z); + } + + public ECFieldElement negate() + { + int[] z = Nat224.create(); + SecP224R1Field.negate(x, z); + return new SecP224R1FieldElement(z); + } + + public ECFieldElement square() + { + int[] z = Nat224.create(); + SecP224R1Field.square(x, z); + return new SecP224R1FieldElement(z); + } + + public ECFieldElement invert() + { +// return new SecP224R1FieldElement(toBigInteger().modInverse(Q)); + int[] z = Nat224.create(); + Mod.invert(SecP224R1Field.P, x, z); + return new SecP224R1FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public ECFieldElement sqrt() + { + int[] c = this.x; + if (Nat224.isZero(c) || Nat224.isOne(c)) + { + return this; + } + + int[] nc = Nat224.create(); + SecP224R1Field.negate(c, nc); + + int[] r = Mod.random(SecP224R1Field.P); + int[] t = Nat224.create(); + + if (!isSquare(c)) + { + return null; + } + + while (!trySqrt(nc, r, t)) + { + SecP224R1Field.addOne(r, r); + } + + SecP224R1Field.square(t, r); + + return Nat224.eq(c, r) ? new SecP224R1FieldElement(t) : null; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecP224R1FieldElement)) + { + return false; + } + + SecP224R1FieldElement o = (SecP224R1FieldElement)other; + return Nat224.eq(x, o.x); + } + + public int hashCode() + { + return Q.hashCode() ^ Arrays.hashCode(x, 0, 7); + } + + private static boolean isSquare(int[] x) + { + int[] t1 = Nat224.create(); + int[] t2 = Nat224.create(); + Nat224.copy(x, t1); + + for (int i = 0; i < 7; ++i) + { + Nat224.copy(t1, t2); + SecP224R1Field.squareN(t1, 1 << i, t1); + SecP224R1Field.multiply(t1, t2, t1); + } + + SecP224R1Field.squareN(t1, 95, t1); + return Nat224.isOne(t1); + } + + private static void RM(int[] nc, int[] d0, int[] e0, int[] d1, int[] e1, int[] f1, int[] t) + { + SecP224R1Field.multiply(e1, e0, t); + SecP224R1Field.multiply(t, nc, t); + SecP224R1Field.multiply(d1, d0, f1); + SecP224R1Field.add(f1, t, f1); + SecP224R1Field.multiply(d1, e0, t); + Nat224.copy(f1, d1); + SecP224R1Field.multiply(e1, d0, e1); + SecP224R1Field.add(e1, t, e1); + SecP224R1Field.square(e1, f1); + SecP224R1Field.multiply(f1, nc, f1); + } + + private static void RP(int[] nc, int[] d1, int[] e1, int[] f1, int[] t) + { + Nat224.copy(nc, f1); + + int[] d0 = Nat224.create(); + int[] e0 = Nat224.create(); + + for (int i = 0; i < 7; ++i) + { + Nat224.copy(d1, d0); + Nat224.copy(e1, e0); + + int j = 1 << i; + while (--j >= 0) + { + RS(d1, e1, f1, t); + } + + RM(nc, d0, e0, d1, e1, f1, t); + } + } + + private static void RS(int[] d, int[] e, int[] f, int[] t) + { + SecP224R1Field.multiply(e, d, e); + SecP224R1Field.twice(e, e); + SecP224R1Field.square(d, t); + SecP224R1Field.add(f, t, d); + SecP224R1Field.multiply(f, t, f); + int c = Nat.shiftUpBits(7, f, 2, 0); + SecP224R1Field.reduce32(c, f); + } + + private static boolean trySqrt(int[] nc, int[] r, int[] t) + { + int[] d1 = Nat224.create(); + Nat224.copy(r, d1); + int[] e1 = Nat224.create(); + e1[0] = 1; + int[] f1 = Nat224.create(); + RP(nc, d1, e1, f1, t); + + int[] d0 = Nat224.create(); + int[] e0 = Nat224.create(); + + for (int k = 1; k < 96; ++k) + { + Nat224.copy(d1, d0); + Nat224.copy(e1, e0); + + RS(d1, e1, f1, t); + + if (Nat224.isZero(d1)) + { + Mod.invert(SecP224R1Field.P, e0, t); + SecP224R1Field.multiply(t, d0, t); + return true; + } + } + + return false; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Point.java new file mode 100644 index 0000000..df10b9b --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Point.java @@ -0,0 +1,308 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat224; + +public class SecP224R1Point extends ECPoint.AbstractFp +{ + /** + * 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 SecP224R1Point(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 SecP224R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecP224R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecP224R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + if (this == b) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + SecP224R1FieldElement X1 = (SecP224R1FieldElement)this.x, Y1 = (SecP224R1FieldElement)this.y; + SecP224R1FieldElement X2 = (SecP224R1FieldElement)b.getXCoord(), Y2 = (SecP224R1FieldElement)b.getYCoord(); + + SecP224R1FieldElement Z1 = (SecP224R1FieldElement)this.zs[0]; + SecP224R1FieldElement Z2 = (SecP224R1FieldElement)b.getZCoord(0); + + int c; + int[] tt1 = Nat224.createExt(); + int[] t2 = Nat224.create(); + int[] t3 = Nat224.create(); + int[] t4 = Nat224.create(); + + boolean Z1IsOne = Z1.isOne(); + int[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP224R1Field.square(Z1.x, S2); + + U2 = t2; + SecP224R1Field.multiply(S2, X2.x, U2); + + SecP224R1Field.multiply(S2, Z1.x, S2); + SecP224R1Field.multiply(S2, Y2.x, S2); + } + + boolean Z2IsOne = Z2.isOne(); + int[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP224R1Field.square(Z2.x, S1); + + U1 = tt1; + SecP224R1Field.multiply(S1, X1.x, U1); + + SecP224R1Field.multiply(S1, Z2.x, S1); + SecP224R1Field.multiply(S1, Y1.x, S1); + } + + int[] H = Nat224.create(); + SecP224R1Field.subtract(U1, U2, H); + + int[] R = t2; + SecP224R1Field.subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat224.isZero(H)) + { + if (Nat224.isZero(R)) + { + // 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(); + } + + int[] HSquared = t3; + SecP224R1Field.square(H, HSquared); + + int[] G = Nat224.create(); + SecP224R1Field.multiply(HSquared, H, G); + + int[] V = t3; + SecP224R1Field.multiply(HSquared, U1, V); + + SecP224R1Field.negate(G, G); + Nat224.mul(S1, G, tt1); + + c = Nat224.addBothTo(V, V, G); + SecP224R1Field.reduce32(c, G); + + SecP224R1FieldElement X3 = new SecP224R1FieldElement(t4); + SecP224R1Field.square(R, X3.x); + SecP224R1Field.subtract(X3.x, G, X3.x); + + SecP224R1FieldElement Y3 = new SecP224R1FieldElement(G); + SecP224R1Field.subtract(V, X3.x, Y3.x); + SecP224R1Field.multiplyAddToExt(Y3.x, R, tt1); + SecP224R1Field.reduce(tt1, Y3.x); + + SecP224R1FieldElement Z3 = new SecP224R1FieldElement(H); + if (!Z1IsOne) + { + SecP224R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP224R1Field.multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[]{ Z3 }; + + return new SecP224R1Point(curve, X3, Y3, zs, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + SecP224R1FieldElement Y1 = (SecP224R1FieldElement)this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + SecP224R1FieldElement X1 = (SecP224R1FieldElement)this.x, Z1 = (SecP224R1FieldElement)this.zs[0]; + + int c; + int[] t1 = Nat224.create(); + int[] t2 = Nat224.create(); + + int[] Y1Squared = Nat224.create(); + SecP224R1Field.square(Y1.x, Y1Squared); + + int[] T = Nat224.create(); + SecP224R1Field.square(Y1Squared, T); + + boolean Z1IsOne = Z1.isOne(); + + int[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP224R1Field.square(Z1.x, Z1Squared); + } + + SecP224R1Field.subtract(X1.x, Z1Squared, t1); + + int[] M = t2; + SecP224R1Field.add(X1.x, Z1Squared, M); + SecP224R1Field.multiply(M, t1, M); + c = Nat224.addBothTo(M, M, M); + SecP224R1Field.reduce32(c, M); + + int[] S = Y1Squared; + SecP224R1Field.multiply(Y1Squared, X1.x, S); + c = Nat.shiftUpBits(7, S, 2, 0); + SecP224R1Field.reduce32(c, S); + + c = Nat.shiftUpBits(7, T, 3, 0, t1); + SecP224R1Field.reduce32(c, t1); + + SecP224R1FieldElement X3 = new SecP224R1FieldElement(T); + SecP224R1Field.square(M, X3.x); + SecP224R1Field.subtract(X3.x, S, X3.x); + SecP224R1Field.subtract(X3.x, S, X3.x); + + SecP224R1FieldElement Y3 = new SecP224R1FieldElement(S); + SecP224R1Field.subtract(S, X3.x, Y3.x); + SecP224R1Field.multiply(Y3.x, M, Y3.x); + SecP224R1Field.subtract(Y3.x, t1, Y3.x); + + SecP224R1FieldElement Z3 = new SecP224R1FieldElement(M); + SecP224R1Field.twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP224R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP224R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + 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; + } + + return twice().add(b); + } + + public ECPoint threeTimes() + { + if (this.isInfinity() || this.y.isZero()) + { + return this; + } + + // NOTE: Be careful about recursions between twicePlus and threeTimes + return twice().add(this); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP224R1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Curve.java new file mode 100644 index 0000000..9b88576 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Curve.java @@ -0,0 +1,78 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecP256K1Curve extends ECCurve.AbstractFp +{ + public static final BigInteger q = new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F")); + + private static final int SECP256K1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP256K1Point infinity; + + public SecP256K1Curve() + { + super(q); + + this.infinity = new SecP256K1Point(this, null, null); + + this.a = fromBigInteger(ECConstants.ZERO); + this.b = fromBigInteger(BigInteger.valueOf(7)); + this.order = new BigInteger(1, Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141")); + this.cofactor = BigInteger.valueOf(1); + this.coord = SECP256K1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecP256K1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public BigInteger getQ() + { + return q; + } + + public int getFieldSize() + { + return q.bitLength(); + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecP256K1FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecP256K1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecP256K1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java new file mode 100644 index 0000000..c7b4def --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java @@ -0,0 +1,179 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat256; + +public class SecP256K1Field +{ + // 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 + static final int[] P = new int[]{ 0xFFFFFC2F, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF }; + static final int[] PExt = new int[]{ 0x000E90A1, 0x000007A2, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xFFFFF85E, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF }; + private static final int[] PExtInv = new int[]{ 0xFFF16F5F, 0xFFFFF85D, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000007A1, 0x00000002 }; + private static final int P7 = 0xFFFFFFFF; + private static final int PExt15 = 0xFFFFFFFF; + private static final int PInv33 = 0x3D1; + + public static void add(int[] x, int[] y, int[] z) + { + int c = Nat256.add(x, y, z); + if (c != 0 || (z[7] == P7 && Nat256.gte(z, P))) + { + Nat.add33To(8, PInv33, z); + } + } + + public static void addExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.add(16, xx, yy, zz); + if (c != 0 || (zz[15] == PExt15 && Nat.gte(16, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(16, zz, PExtInv.length); + } + } + } + + public static void addOne(int[] x, int[] z) + { + int c = Nat.inc(8, x, z); + if (c != 0 || (z[7] == P7 && Nat256.gte(z, P))) + { + Nat.add33To(8, PInv33, z); + } + } + + public static int[] fromBigInteger(BigInteger x) + { + int[] z = Nat256.fromBigInteger(x); + if (z[7] == P7 && Nat256.gte(z, P)) + { + Nat256.subFrom(P, z); + } + return z; + } + + public static void half(int[] x, int[] z) + { + if ((x[0] & 1) == 0) + { + Nat.shiftDownBit(8, x, 0, z); + } + else + { + int c = Nat256.add(x, P, z); + Nat.shiftDownBit(8, z, c); + } + } + + public static void multiply(int[] x, int[] y, int[] z) + { + int[] tt = Nat256.createExt(); + Nat256.mul(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(int[] x, int[] y, int[] zz) + { + int c = Nat256.mulAddTo(x, y, zz); + if (c != 0 || (zz[15] == PExt15 && Nat.gte(16, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(16, zz, PExtInv.length); + } + } + } + + public static void negate(int[] x, int[] z) + { + if (Nat256.isZero(x)) + { + Nat256.zero(z); + } + else + { + Nat256.sub(P, x, z); + } + } + + public static void reduce(int[] xx, int[] z) + { + long cc = Nat256.mul33Add(PInv33, xx, 8, xx, 0, z, 0); + int c = Nat256.mul33DWordAdd(PInv33, cc, z, 0); + + // assert c == 0L || c == 1L; + + if (c != 0 || (z[7] == P7 && Nat256.gte(z, P))) + { + Nat.add33To(8, PInv33, z); + } + } + + public static void reduce32(int x, int[] z) + { + if ((x != 0 && Nat256.mul33WordAdd(PInv33, x, z, 0) != 0) + || (z[7] == P7 && Nat256.gte(z, P))) + { + Nat.add33To(8, PInv33, z); + } + } + + public static void square(int[] x, int[] z) + { + int[] tt = Nat256.createExt(); + Nat256.square(x, tt); + reduce(tt, z); + } + + public static void squareN(int[] x, int n, int[] z) + { +// assert n > 0; + + int[] tt = Nat256.createExt(); + Nat256.square(x, tt); + reduce(tt, z); + + while (--n > 0) + { + Nat256.square(z, tt); + reduce(tt, z); + } + } + + public static void subtract(int[] x, int[] y, int[] z) + { + int c = Nat256.sub(x, y, z); + if (c != 0) + { + Nat.sub33From(8, PInv33, z); + } + } + + public static void subtractExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.sub(16, xx, yy, zz); + if (c != 0) + { + if (Nat.subFrom(PExtInv.length, PExtInv, zz) != 0) + { + Nat.decAt(16, zz, PExtInv.length); + } + } + } + + public static void twice(int[] x, int[] z) + { + int c = Nat.shiftUpBit(8, x, 0, z); + if (c != 0 || (z[7] == P7 && Nat256.gte(z, P))) + { + Nat.add33To(8, PInv33, z); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java new file mode 100644 index 0000000..0f7e295 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java @@ -0,0 +1,215 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Mod; +import org.bouncycastle.math.raw.Nat256; +import org.bouncycastle.util.Arrays; + +public class SecP256K1FieldElement extends ECFieldElement +{ + public static final BigInteger Q = SecP256K1Curve.q; + + protected int[] x; + + public SecP256K1FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0) + { + throw new IllegalArgumentException("x value invalid for SecP256K1FieldElement"); + } + + this.x = SecP256K1Field.fromBigInteger(x); + } + + public SecP256K1FieldElement() + { + this.x = Nat256.create(); + } + + protected SecP256K1FieldElement(int[] x) + { + this.x = x; + } + + public boolean isZero() + { + return Nat256.isZero(x); + } + + public boolean isOne() + { + return Nat256.isOne(x); + } + + public boolean testBitZero() + { + return Nat256.getBit(x, 0) == 1; + } + + public BigInteger toBigInteger() + { + return Nat256.toBigInteger(x); + } + + public String getFieldName() + { + return "SecP256K1Field"; + } + + public int getFieldSize() + { + return Q.bitLength(); + } + + public ECFieldElement add(ECFieldElement b) + { + int[] z = Nat256.create(); + SecP256K1Field.add(x, ((SecP256K1FieldElement)b).x, z); + return new SecP256K1FieldElement(z); + } + + public ECFieldElement addOne() + { + int[] z = Nat256.create(); + SecP256K1Field.addOne(x, z); + return new SecP256K1FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + int[] z = Nat256.create(); + SecP256K1Field.subtract(x, ((SecP256K1FieldElement)b).x, z); + return new SecP256K1FieldElement(z); + } + + public ECFieldElement multiply(ECFieldElement b) + { + int[] z = Nat256.create(); + SecP256K1Field.multiply(x, ((SecP256K1FieldElement)b).x, z); + return new SecP256K1FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { +// return multiply(b.invert()); + int[] z = Nat256.create(); + Mod.invert(SecP256K1Field.P, ((SecP256K1FieldElement)b).x, z); + SecP256K1Field.multiply(z, x, z); + return new SecP256K1FieldElement(z); + } + + public ECFieldElement negate() + { + int[] z = Nat256.create(); + SecP256K1Field.negate(x, z); + return new SecP256K1FieldElement(z); + } + + public ECFieldElement square() + { + int[] z = Nat256.create(); + SecP256K1Field.square(x, z); + return new SecP256K1FieldElement(z); + } + + public ECFieldElement invert() + { +// return new SecP256K1FieldElement(toBigInteger().modInverse(Q)); + int[] z = Nat256.create(); + Mod.invert(SecP256K1Field.P, x, z); + return new SecP256K1FieldElement(z); + } + + // D.1.4 91 + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public ECFieldElement sqrt() + { + /* + * Raise this element to the exponent 2^254 - 2^30 - 2^7 - 2^6 - 2^5 - 2^4 - 2^2 + * + * Breaking up the exponent's binary representation into "repunits", we get: + * { 223 1s } { 1 0s } { 22 1s } { 4 0s } { 2 1s } { 2 0s} + * + * Therefore we need an addition chain containing 2, 22, 223 (the lengths of the repunits) + * We use: 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223] + */ + + int[] x1 = this.x; + if (Nat256.isZero(x1) || Nat256.isOne(x1)) + { + return this; + } + + int[] x2 = Nat256.create(); + SecP256K1Field.square(x1, x2); + SecP256K1Field.multiply(x2, x1, x2); + int[] x3 = Nat256.create(); + SecP256K1Field.square(x2, x3); + SecP256K1Field.multiply(x3, x1, x3); + int[] x6 = Nat256.create(); + SecP256K1Field.squareN(x3, 3, x6); + SecP256K1Field.multiply(x6, x3, x6); + int[] x9 = x6; + SecP256K1Field.squareN(x6, 3, x9); + SecP256K1Field.multiply(x9, x3, x9); + int[] x11 = x9; + SecP256K1Field.squareN(x9, 2, x11); + SecP256K1Field.multiply(x11, x2, x11); + int[] x22 = Nat256.create(); + SecP256K1Field.squareN(x11, 11, x22); + SecP256K1Field.multiply(x22, x11, x22); + int[] x44 = x11; + SecP256K1Field.squareN(x22, 22, x44); + SecP256K1Field.multiply(x44, x22, x44); + int[] x88 = Nat256.create(); + SecP256K1Field.squareN(x44, 44, x88); + SecP256K1Field.multiply(x88, x44, x88); + int[] x176 = Nat256.create(); + SecP256K1Field.squareN(x88, 88, x176); + SecP256K1Field.multiply(x176, x88, x176); + int[] x220 = x88; + SecP256K1Field.squareN(x176, 44, x220); + SecP256K1Field.multiply(x220, x44, x220); + int[] x223 = x44; + SecP256K1Field.squareN(x220, 3, x223); + SecP256K1Field.multiply(x223, x3, x223); + + int[] t1 = x223; + SecP256K1Field.squareN(t1, 23, t1); + SecP256K1Field.multiply(t1, x22, t1); + SecP256K1Field.squareN(t1, 6, t1); + SecP256K1Field.multiply(t1, x2, t1); + SecP256K1Field.squareN(t1, 2, t1); + + int[] t2 = x2; + SecP256K1Field.square(t1, t2); + + return Nat256.eq(x1, t2) ? new SecP256K1FieldElement(t1) : null; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecP256K1FieldElement)) + { + return false; + } + + SecP256K1FieldElement o = (SecP256K1FieldElement)other; + return Nat256.eq(x, o.x); + } + + public int hashCode() + { + return Q.hashCode() ^ Arrays.hashCode(x, 0, 8); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java new file mode 100644 index 0000000..f57b200 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java @@ -0,0 +1,298 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat256; + +public class SecP256K1Point extends ECPoint.AbstractFp +{ + /** + * 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 SecP256K1Point(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 SecP256K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecP256K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, + boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecP256K1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + // 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(); + + SecP256K1FieldElement X1 = (SecP256K1FieldElement)this.x, Y1 = (SecP256K1FieldElement)this.y; + SecP256K1FieldElement X2 = (SecP256K1FieldElement)b.getXCoord(), Y2 = (SecP256K1FieldElement)b.getYCoord(); + + SecP256K1FieldElement Z1 = (SecP256K1FieldElement)this.zs[0]; + SecP256K1FieldElement Z2 = (SecP256K1FieldElement)b.getZCoord(0); + + int c; + int[] tt1 = Nat256.createExt(); + int[] t2 = Nat256.create(); + int[] t3 = Nat256.create(); + int[] t4 = Nat256.create(); + + boolean Z1IsOne = Z1.isOne(); + int[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP256K1Field.square(Z1.x, S2); + + U2 = t2; + SecP256K1Field.multiply(S2, X2.x, U2); + + SecP256K1Field.multiply(S2, Z1.x, S2); + SecP256K1Field.multiply(S2, Y2.x, S2); + } + + boolean Z2IsOne = Z2.isOne(); + int[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP256K1Field.square(Z2.x, S1); + + U1 = tt1; + SecP256K1Field.multiply(S1, X1.x, U1); + + SecP256K1Field.multiply(S1, Z2.x, S1); + SecP256K1Field.multiply(S1, Y1.x, S1); + } + + int[] H = Nat256.create(); + SecP256K1Field.subtract(U1, U2, H); + + int[] R = t2; + SecP256K1Field.subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat256.isZero(H)) + { + if (Nat256.isZero(R)) + { + // 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(); + } + + int[] HSquared = t3; + SecP256K1Field.square(H, HSquared); + + int[] G = Nat256.create(); + SecP256K1Field.multiply(HSquared, H, G); + + int[] V = t3; + SecP256K1Field.multiply(HSquared, U1, V); + + SecP256K1Field.negate(G, G); + Nat256.mul(S1, G, tt1); + + c = Nat256.addBothTo(V, V, G); + SecP256K1Field.reduce32(c, G); + + SecP256K1FieldElement X3 = new SecP256K1FieldElement(t4); + SecP256K1Field.square(R, X3.x); + SecP256K1Field.subtract(X3.x, G, X3.x); + + SecP256K1FieldElement Y3 = new SecP256K1FieldElement(G); + SecP256K1Field.subtract(V, X3.x, Y3.x); + SecP256K1Field.multiplyAddToExt(Y3.x, R, tt1); + SecP256K1Field.reduce(tt1, Y3.x); + + SecP256K1FieldElement Z3 = new SecP256K1FieldElement(H); + if (!Z1IsOne) + { + SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP256K1Field.multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP256K1Point(curve, X3, Y3, zs, this.withCompression); + } + + // B.3 pg 62 + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + SecP256K1FieldElement Y1 = (SecP256K1FieldElement)this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + SecP256K1FieldElement X1 = (SecP256K1FieldElement)this.x, Z1 = (SecP256K1FieldElement)this.zs[0]; + + int c; + + int[] Y1Squared = Nat256.create(); + SecP256K1Field.square(Y1.x, Y1Squared); + + int[] T = Nat256.create(); + SecP256K1Field.square(Y1Squared, T); + + int[] M = Nat256.create(); + SecP256K1Field.square(X1.x, M); + c = Nat256.addBothTo(M, M, M); + SecP256K1Field.reduce32(c, M); + + int[] S = Y1Squared; + SecP256K1Field.multiply(Y1Squared, X1.x, S); + c = Nat.shiftUpBits(8, S, 2, 0); + SecP256K1Field.reduce32(c, S); + + int[] t1 = Nat256.create(); + c = Nat.shiftUpBits(8, T, 3, 0, t1); + SecP256K1Field.reduce32(c, t1); + + SecP256K1FieldElement X3 = new SecP256K1FieldElement(T); + SecP256K1Field.square(M, X3.x); + SecP256K1Field.subtract(X3.x, S, X3.x); + SecP256K1Field.subtract(X3.x, S, X3.x); + + SecP256K1FieldElement Y3 = new SecP256K1FieldElement(S); + SecP256K1Field.subtract(S, X3.x, Y3.x); + SecP256K1Field.multiply(Y3.x, M, Y3.x); + SecP256K1Field.subtract(Y3.x, t1, Y3.x); + + SecP256K1FieldElement Z3 = new SecP256K1FieldElement(M); + SecP256K1Field.twice(Y1.x, Z3.x); + if (!Z1.isOne()) + { + SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP256K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, this.withCompression); + } + + 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; + } + + return twice().add(b); + } + + public ECPoint threeTimes() + { + if (this.isInfinity() || this.y.isZero()) + { + return this; + } + + // NOTE: Be careful about recursions between twicePlus and threeTimes + return twice().add(this); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP256K1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Curve.java new file mode 100644 index 0000000..5ff6a38 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Curve.java @@ -0,0 +1,80 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecP256R1Curve extends ECCurve.AbstractFp +{ + public static final BigInteger q = new BigInteger(1, + Hex.decode("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF")); + + private static final int SecP256R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP256R1Point infinity; + + public SecP256R1Curve() + { + super(q); + + this.infinity = new SecP256R1Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, + Hex.decode("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"))); + this.b = fromBigInteger(new BigInteger(1, + Hex.decode("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"))); + this.order = new BigInteger(1, Hex.decode("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551")); + this.cofactor = BigInteger.valueOf(1); + + this.coord = SecP256R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecP256R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public BigInteger getQ() + { + return q; + } + + public int getFieldSize() + { + return q.bitLength(); + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecP256R1FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecP256R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecP256R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java new file mode 100644 index 0000000..985cb0e --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java @@ -0,0 +1,312 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat256; + +public class SecP256R1Field +{ + private static final long M = 0xFFFFFFFFL; + + // 2^256 - 2^224 + 2^192 + 2^96 - 1 + static final int[] P = new int[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, + 0x00000001, 0xFFFFFFFF }; + static final int[] PExt = new int[]{ 0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFE, 0x00000001, 0xFFFFFFFE, 0x00000001, 0xFFFFFFFE, 0x00000001, 0x00000001, 0xFFFFFFFE, + 0x00000002, 0xFFFFFFFE }; + private static final int P7 = 0xFFFFFFFF; + private static final int PExt15 = 0xFFFFFFFF; + + public static void add(int[] x, int[] y, int[] z) + { + int c = Nat256.add(x, y, z); + if (c != 0 || (z[7] == P7 && Nat256.gte(z, P))) + { + addPInvTo(z); + } + } + + public static void addExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.add(16, xx, yy, zz); + if (c != 0 || ((zz[15] & PExt15) == PExt15 && Nat.gte(16, zz, PExt))) + { + Nat.subFrom(16, PExt, zz); + } + } + + public static void addOne(int[] x, int[] z) + { + int c = Nat.inc(8, x, z); + if (c != 0 || (z[7] == P7 && Nat256.gte(z, P))) + { + addPInvTo(z); + } + } + + public static int[] fromBigInteger(BigInteger x) + { + int[] z = Nat256.fromBigInteger(x); + if (z[7] == P7 && Nat256.gte(z, P)) + { + Nat256.subFrom(P, z); + } + return z; + } + + public static void half(int[] x, int[] z) + { + if ((x[0] & 1) == 0) + { + Nat.shiftDownBit(8, x, 0, z); + } + else + { + int c = Nat256.add(x, P, z); + Nat.shiftDownBit(8, z, c); + } + } + + public static void multiply(int[] x, int[] y, int[] z) + { + int[] tt = Nat256.createExt(); + Nat256.mul(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(int[] x, int[] y, int[] zz) + { + int c = Nat256.mulAddTo(x, y, zz); + if (c != 0 || ((zz[15] & PExt15) == PExt15 && Nat.gte(16, zz, PExt))) + { + Nat.subFrom(16, PExt, zz); + } + } + + public static void negate(int[] x, int[] z) + { + if (Nat256.isZero(x)) + { + Nat256.zero(z); + } + else + { + Nat256.sub(P, x, z); + } + } + + public static void reduce(int[] xx, int[] z) + { + long xx08 = xx[8] & M, xx09 = xx[9] & M, xx10 = xx[10] & M, xx11 = xx[11] & M; + long xx12 = xx[12] & M, xx13 = xx[13] & M, xx14 = xx[14] & M, xx15 = xx[15] & M; + + final long n = 6; + + xx08 -= n; + + long t0 = xx08 + xx09; + long t1 = xx09 + xx10; + long t2 = xx10 + xx11 - xx15; + long t3 = xx11 + xx12; + long t4 = xx12 + xx13; + long t5 = xx13 + xx14; + long t6 = xx14 + xx15; + + long cc = 0; + cc += (xx[0] & M) + t0 - t3 - t5; + z[0] = (int)cc; + cc >>= 32; + cc += (xx[1] & M) + t1 - t4 - t6; + z[1] = (int)cc; + cc >>= 32; + cc += (xx[2] & M) + t2 - t5; + z[2] = (int)cc; + cc >>= 32; + cc += (xx[3] & M) + (t3 << 1) + xx13 - xx15 - t0; + z[3] = (int)cc; + cc >>= 32; + cc += (xx[4] & M) + (t4 << 1) + xx14 - t1; + z[4] = (int)cc; + cc >>= 32; + cc += (xx[5] & M) + (t5 << 1) - t2; + z[5] = (int)cc; + cc >>= 32; + cc += (xx[6] & M) + (t6 << 1) + t5 - t0; + z[6] = (int)cc; + cc >>= 32; + cc += (xx[7] & M) + (xx15 << 1) + xx08 - t2 - t4; + z[7] = (int)cc; + cc >>= 32; + cc += n; + +// assert cc >= 0; + + reduce32((int)cc, z); + } + + public static void reduce32(int x, int[] z) + { + long cc = 0; + + if (x != 0) + { + long xx08 = x & M; + + cc += (z[0] & M) + xx08; + z[0] = (int)cc; + cc >>= 32; + if (cc != 0) + { + cc += (z[1] & M); + z[1] = (int)cc; + cc >>= 32; + cc += (z[2] & M); + z[2] = (int)cc; + cc >>= 32; + } + cc += (z[3] & M) - xx08; + z[3] = (int)cc; + cc >>= 32; + if (cc != 0) + { + cc += (z[4] & M); + z[4] = (int)cc; + cc >>= 32; + cc += (z[5] & M); + z[5] = (int)cc; + cc >>= 32; + } + cc += (z[6] & M) - xx08; + z[6] = (int)cc; + cc >>= 32; + cc += (z[7] & M) + xx08; + z[7] = (int)cc; + cc >>= 32; + +// assert cc == 0 || cc == 1; + } + + if (cc != 0 || (z[7] == P7 && Nat256.gte(z, P))) + { + addPInvTo(z); + } + } + + public static void square(int[] x, int[] z) + { + int[] tt = Nat256.createExt(); + Nat256.square(x, tt); + reduce(tt, z); + } + + public static void squareN(int[] x, int n, int[] z) + { +// assert n > 0; + + int[] tt = Nat256.createExt(); + Nat256.square(x, tt); + reduce(tt, z); + + while (--n > 0) + { + Nat256.square(z, tt); + reduce(tt, z); + } + } + + public static void subtract(int[] x, int[] y, int[] z) + { + int c = Nat256.sub(x, y, z); + if (c != 0) + { + subPInvFrom(z); + } + } + + public static void subtractExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.sub(16, xx, yy, zz); + if (c != 0) + { + Nat.addTo(16, PExt, zz); + } + } + + public static void twice(int[] x, int[] z) + { + int c = Nat.shiftUpBit(8, x, 0, z); + if (c != 0 || (z[7] == P7 && Nat256.gte(z, P))) + { + addPInvTo(z); + } + } + + private static void addPInvTo(int[] z) + { + long c = (z[0] & M) + 1; + z[0] = (int)c; + c >>= 32; + if (c != 0) + { + c += (z[1] & M); + z[1] = (int)c; + c >>= 32; + c += (z[2] & M); + z[2] = (int)c; + c >>= 32; + } + c += (z[3] & M) - 1; + z[3] = (int)c; + c >>= 32; + if (c != 0) + { + c += (z[4] & M); + z[4] = (int)c; + c >>= 32; + c += (z[5] & M); + z[5] = (int)c; + c >>= 32; + } + c += (z[6] & M) - 1; + z[6] = (int)c; + c >>= 32; + c += (z[7] & M) + 1; + z[7] = (int)c; +// c >>= 32; + } + + private static void subPInvFrom(int[] z) + { + long c = (z[0] & M) - 1; + z[0] = (int)c; + c >>= 32; + if (c != 0) + { + c += (z[1] & M); + z[1] = (int)c; + c >>= 32; + c += (z[2] & M); + z[2] = (int)c; + c >>= 32; + } + c += (z[3] & M) + 1; + z[3] = (int)c; + c >>= 32; + if (c != 0) + { + c += (z[4] & M); + z[4] = (int)c; + c >>= 32; + c += (z[5] & M); + z[5] = (int)c; + c >>= 32; + } + c += (z[6] & M) + 1; + z[6] = (int)c; + c >>= 32; + c += (z[7] & M) - 1; + z[7] = (int)c; +// c >>= 32; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java new file mode 100644 index 0000000..be250d1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java @@ -0,0 +1,189 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Mod; +import org.bouncycastle.math.raw.Nat256; +import org.bouncycastle.util.Arrays; + +public class SecP256R1FieldElement extends ECFieldElement +{ + public static final BigInteger Q = SecP256R1Curve.q; + + protected int[] x; + + public SecP256R1FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0) + { + throw new IllegalArgumentException("x value invalid for SecP256R1FieldElement"); + } + + this.x = SecP256R1Field.fromBigInteger(x); + } + + public SecP256R1FieldElement() + { + this.x = Nat256.create(); + } + + protected SecP256R1FieldElement(int[] x) + { + this.x = x; + } + + public boolean isZero() + { + return Nat256.isZero(x); + } + + public boolean isOne() + { + return Nat256.isOne(x); + } + + public boolean testBitZero() + { + return Nat256.getBit(x, 0) == 1; + } + + public BigInteger toBigInteger() + { + return Nat256.toBigInteger(x); + } + + public String getFieldName() + { + return "SecP256R1Field"; + } + + public int getFieldSize() + { + return Q.bitLength(); + } + + public ECFieldElement add(ECFieldElement b) + { + int[] z = Nat256.create(); + SecP256R1Field.add(x, ((SecP256R1FieldElement)b).x, z); + return new SecP256R1FieldElement(z); + } + + public ECFieldElement addOne() + { + int[] z = Nat256.create(); + SecP256R1Field.addOne(x, z); + return new SecP256R1FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + int[] z = Nat256.create(); + SecP256R1Field.subtract(x, ((SecP256R1FieldElement)b).x, z); + return new SecP256R1FieldElement(z); + } + + public ECFieldElement multiply(ECFieldElement b) + { + int[] z = Nat256.create(); + SecP256R1Field.multiply(x, ((SecP256R1FieldElement)b).x, z); + return new SecP256R1FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { +// return multiply(b.invert()); + int[] z = Nat256.create(); + Mod.invert(SecP256R1Field.P, ((SecP256R1FieldElement)b).x, z); + SecP256R1Field.multiply(z, x, z); + return new SecP256R1FieldElement(z); + } + + public ECFieldElement negate() + { + int[] z = Nat256.create(); + SecP256R1Field.negate(x, z); + return new SecP256R1FieldElement(z); + } + + public ECFieldElement square() + { + int[] z = Nat256.create(); + SecP256R1Field.square(x, z); + return new SecP256R1FieldElement(z); + } + + public ECFieldElement invert() + { +// return new SecP256R1FieldElement(toBigInteger().modInverse(Q)); + int[] z = Nat256.create(); + Mod.invert(SecP256R1Field.P, x, z); + return new SecP256R1FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public ECFieldElement sqrt() + { + // Raise this element to the exponent 2^254 - 2^222 + 2^190 + 2^94 + + int[] x1 = this.x; + if (Nat256.isZero(x1) || Nat256.isOne(x1)) + { + return this; + } + + int[] t1 = Nat256.create(); + int[] t2 = Nat256.create(); + + SecP256R1Field.square(x1, t1); + SecP256R1Field.multiply(t1, x1, t1); + + SecP256R1Field.squareN(t1, 2, t2); + SecP256R1Field.multiply(t2, t1, t2); + + SecP256R1Field.squareN(t2, 4, t1); + SecP256R1Field.multiply(t1, t2, t1); + + SecP256R1Field.squareN(t1, 8, t2); + SecP256R1Field.multiply(t2, t1, t2); + + SecP256R1Field.squareN(t2, 16, t1); + SecP256R1Field.multiply(t1, t2, t1); + + SecP256R1Field.squareN(t1, 32, t1); + SecP256R1Field.multiply(t1, x1, t1); + + SecP256R1Field.squareN(t1, 96, t1); + SecP256R1Field.multiply(t1, x1, t1); + + SecP256R1Field.squareN(t1, 94, t1); + SecP256R1Field.square(t1, t2); + + return Nat256.eq(x1, t2) ? new SecP256R1FieldElement(t1) : null; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecP256R1FieldElement)) + { + return false; + } + + SecP256R1FieldElement o = (SecP256R1FieldElement)other; + return Nat256.eq(x, o.x); + } + + public int hashCode() + { + return Q.hashCode() ^ Arrays.hashCode(x, 0, 8); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java new file mode 100644 index 0000000..930fdc5 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java @@ -0,0 +1,308 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat256; + +public class SecP256R1Point extends ECPoint.AbstractFp +{ + /** + * 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 SecP256R1Point(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 SecP256R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecP256R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecP256R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + if (this == b) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + SecP256R1FieldElement X1 = (SecP256R1FieldElement)this.x, Y1 = (SecP256R1FieldElement)this.y; + SecP256R1FieldElement X2 = (SecP256R1FieldElement)b.getXCoord(), Y2 = (SecP256R1FieldElement)b.getYCoord(); + + SecP256R1FieldElement Z1 = (SecP256R1FieldElement)this.zs[0]; + SecP256R1FieldElement Z2 = (SecP256R1FieldElement)b.getZCoord(0); + + int c; + int[] tt1 = Nat256.createExt(); + int[] t2 = Nat256.create(); + int[] t3 = Nat256.create(); + int[] t4 = Nat256.create(); + + boolean Z1IsOne = Z1.isOne(); + int[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP256R1Field.square(Z1.x, S2); + + U2 = t2; + SecP256R1Field.multiply(S2, X2.x, U2); + + SecP256R1Field.multiply(S2, Z1.x, S2); + SecP256R1Field.multiply(S2, Y2.x, S2); + } + + boolean Z2IsOne = Z2.isOne(); + int[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP256R1Field.square(Z2.x, S1); + + U1 = tt1; + SecP256R1Field.multiply(S1, X1.x, U1); + + SecP256R1Field.multiply(S1, Z2.x, S1); + SecP256R1Field.multiply(S1, Y1.x, S1); + } + + int[] H = Nat256.create(); + SecP256R1Field.subtract(U1, U2, H); + + int[] R = t2; + SecP256R1Field.subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat256.isZero(H)) + { + if (Nat256.isZero(R)) + { + // 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(); + } + + int[] HSquared = t3; + SecP256R1Field.square(H, HSquared); + + int[] G = Nat256.create(); + SecP256R1Field.multiply(HSquared, H, G); + + int[] V = t3; + SecP256R1Field.multiply(HSquared, U1, V); + + SecP256R1Field.negate(G, G); + Nat256.mul(S1, G, tt1); + + c = Nat256.addBothTo(V, V, G); + SecP256R1Field.reduce32(c, G); + + SecP256R1FieldElement X3 = new SecP256R1FieldElement(t4); + SecP256R1Field.square(R, X3.x); + SecP256R1Field.subtract(X3.x, G, X3.x); + + SecP256R1FieldElement Y3 = new SecP256R1FieldElement(G); + SecP256R1Field.subtract(V, X3.x, Y3.x); + SecP256R1Field.multiplyAddToExt(Y3.x, R, tt1); + SecP256R1Field.reduce(tt1, Y3.x); + + SecP256R1FieldElement Z3 = new SecP256R1FieldElement(H); + if (!Z1IsOne) + { + SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP256R1Field.multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[]{ Z3 }; + + return new SecP256R1Point(curve, X3, Y3, zs, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + SecP256R1FieldElement Y1 = (SecP256R1FieldElement)this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + SecP256R1FieldElement X1 = (SecP256R1FieldElement)this.x, Z1 = (SecP256R1FieldElement)this.zs[0]; + + int c; + int[] t1 = Nat256.create(); + int[] t2 = Nat256.create(); + + int[] Y1Squared = Nat256.create(); + SecP256R1Field.square(Y1.x, Y1Squared); + + int[] T = Nat256.create(); + SecP256R1Field.square(Y1Squared, T); + + boolean Z1IsOne = Z1.isOne(); + + int[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP256R1Field.square(Z1.x, Z1Squared); + } + + SecP256R1Field.subtract(X1.x, Z1Squared, t1); + + int[] M = t2; + SecP256R1Field.add(X1.x, Z1Squared, M); + SecP256R1Field.multiply(M, t1, M); + c = Nat256.addBothTo(M, M, M); + SecP256R1Field.reduce32(c, M); + + int[] S = Y1Squared; + SecP256R1Field.multiply(Y1Squared, X1.x, S); + c = Nat.shiftUpBits(8, S, 2, 0); + SecP256R1Field.reduce32(c, S); + + c = Nat.shiftUpBits(8, T, 3, 0, t1); + SecP256R1Field.reduce32(c, t1); + + SecP256R1FieldElement X3 = new SecP256R1FieldElement(T); + SecP256R1Field.square(M, X3.x); + SecP256R1Field.subtract(X3.x, S, X3.x); + SecP256R1Field.subtract(X3.x, S, X3.x); + + SecP256R1FieldElement Y3 = new SecP256R1FieldElement(S); + SecP256R1Field.subtract(S, X3.x, Y3.x); + SecP256R1Field.multiply(Y3.x, M, Y3.x); + SecP256R1Field.subtract(Y3.x, t1, Y3.x); + + SecP256R1FieldElement Z3 = new SecP256R1FieldElement(M); + SecP256R1Field.twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP256R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + 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; + } + + return twice().add(b); + } + + public ECPoint threeTimes() + { + if (this.isInfinity() || this.y.isZero()) + { + return this; + } + + // NOTE: Be careful about recursions between twicePlus and threeTimes + return twice().add(this); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP256R1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Curve.java new file mode 100644 index 0000000..27cbcdb --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Curve.java @@ -0,0 +1,80 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecP384R1Curve extends ECCurve.AbstractFp +{ + public static final BigInteger q = new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF")); + + private static final int SecP384R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP384R1Point infinity; + + public SecP384R1Curve() + { + super(q); + + this.infinity = new SecP384R1Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC"))); + this.b = fromBigInteger(new BigInteger(1, + Hex.decode("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF"))); + this.order = new BigInteger(1, Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973")); + this.cofactor = BigInteger.valueOf(1); + + this.coord = SecP384R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecP384R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public BigInteger getQ() + { + return q; + } + + public int getFieldSize() + { + return q.bitLength(); + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecP384R1FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecP384R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecP384R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java new file mode 100644 index 0000000..f321a10 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java @@ -0,0 +1,295 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat384; + +public class SecP384R1Field +{ + private static final long M = 0xFFFFFFFFL; + + // 2^384 - 2^128 - 2^96 + 2^32 - 1 + static final int[] P = new int[]{ 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + static final int[] PExt = new int[]{ 0x00000001, 0xFFFFFFFE, 0x00000000, 0x00000002, 0x00000000, 0xFFFFFFFE, + 0x00000000, 0x00000002, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFE, 0x00000001, 0x00000000, + 0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static final int[] PExtInv = new int[]{ 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0x00000001, + 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001, 0xFFFFFFFE, 0xFFFFFFFF, + 0x00000001, 0x00000002 }; + private static final int P11 = 0xFFFFFFFF; + private static final int PExt23 = 0xFFFFFFFF; + + public static void add(int[] x, int[] y, int[] z) + { + int c = Nat.add(12, x, y, z); + if (c != 0 || (z[11] == P11 && Nat.gte(12, z, P))) + { + addPInvTo(z); + } + } + + public static void addExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.add(24, xx, yy, zz); + if (c != 0 || (zz[23] == PExt23 && Nat.gte(24, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(24, zz, PExtInv.length); + } + } + } + + public static void addOne(int[] x, int[] z) + { + int c = Nat.inc(12, x, z); + if (c != 0 || (z[11] == P11 && Nat.gte(12, z, P))) + { + addPInvTo(z); + } + } + + public static int[] fromBigInteger(BigInteger x) + { + int[] z = Nat.fromBigInteger(384, x); + if (z[11] == P11 && Nat.gte(12, z, P)) + { + Nat.subFrom(12, P, z); + } + return z; + } + + public static void half(int[] x, int[] z) + { + if ((x[0] & 1) == 0) + { + Nat.shiftDownBit(12, x, 0, z); + } + else + { + int c = Nat.add(12, x, P, z); + Nat.shiftDownBit(12, z, c); + } + } + + public static void multiply(int[] x, int[] y, int[] z) + { + int[] tt = Nat.create(24); + Nat384.mul(x, y, tt); + reduce(tt, z); + } + + public static void negate(int[] x, int[] z) + { + if (Nat.isZero(12, x)) + { + Nat.zero(12, z); + } + else + { + Nat.sub(12, P, x, z); + } + } + + public static void reduce(int[] xx, int[] z) + { + long xx16 = xx[16] & M, xx17 = xx[17] & M, xx18 = xx[18] & M, xx19 = xx[19] & M; + long xx20 = xx[20] & M, xx21 = xx[21] & M, xx22 = xx[22] & M, xx23 = xx[23] & M; + + final long n = 1; + + long t0 = (xx[12] & M) + xx20 - n; + long t1 = (xx[13] & M) + xx22; + long t2 = (xx[14] & M) + xx22 + xx23; + long t3 = (xx[15] & M) + xx23; + long t4 = xx17 + xx21; + long t5 = xx21 - xx23; + long t6 = xx22 - xx23; + + long cc = 0; + cc += (xx[0] & M) + t0 + t5; + z[0] = (int)cc; + cc >>= 32; + cc += (xx[1] & M) + xx23 - t0 + t1; + z[1] = (int)cc; + cc >>= 32; + cc += (xx[2] & M) - xx21 - t1 + t2; + z[2] = (int)cc; + cc >>= 32; + cc += (xx[3] & M) + t0 - t2 + t3 + t5; + z[3] = (int)cc; + cc >>= 32; + cc += (xx[4] & M) + xx16 + xx21 + t0 + t1 - t3 + t5; + z[4] = (int)cc; + cc >>= 32; + cc += (xx[5] & M) - xx16 + t1 + t2 + t4; + z[5] = (int)cc; + cc >>= 32; + cc += (xx[6] & M) + xx18 - xx17 + t2 + t3; + z[6] = (int)cc; + cc >>= 32; + cc += (xx[7] & M) + xx16 + xx19 - xx18 + t3; + z[7] = (int)cc; + cc >>= 32; + cc += (xx[8] & M) + xx16 + xx17 + xx20 - xx19; + z[8] = (int)cc; + cc >>= 32; + cc += (xx[9] & M) + xx18 - xx20 + t4; + z[9] = (int)cc; + cc >>= 32; + cc += (xx[10] & M) + xx18 + xx19 - t5 + t6; + z[10] = (int)cc; + cc >>= 32; + cc += (xx[11] & M) + xx19 + xx20 - t6; + z[11] = (int)cc; + cc >>= 32; + cc += n; + +// assert cc >= 0; + + reduce32((int)cc, z); + } + + public static void reduce32(int x, int[] z) + { + long cc = 0; + + if (x != 0) + { + long xx12 = x & M; + + cc += (z[0] & M) + xx12; + z[0] = (int)cc; + cc >>= 32; + cc += (z[1] & M) - xx12; + z[1] = (int)cc; + cc >>= 32; + if (cc != 0) + { + cc += (z[2] & M); + z[2] = (int)cc; + cc >>= 32; + } + cc += (z[3] & M) + xx12; + z[3] = (int)cc; + cc >>= 32; + cc += (z[4] & M) + xx12; + z[4] = (int)cc; + cc >>= 32; + +// assert cc == 0 || cc == 1; + } + + if ((cc != 0 && Nat.incAt(12, z, 5) != 0) + || (z[11] == P11 && Nat.gte(12, z, P))) + { + addPInvTo(z); + } + } + + public static void square(int[] x, int[] z) + { + int[] tt = Nat.create(24); + Nat384.square(x, tt); + reduce(tt, z); + } + + public static void squareN(int[] x, int n, int[] z) + { +// assert n > 0; + + int[] tt = Nat.create(24); + Nat384.square(x, tt); + reduce(tt, z); + + while (--n > 0) + { + Nat384.square(z, tt); + reduce(tt, z); + } + } + + public static void subtract(int[] x, int[] y, int[] z) + { + int c = Nat.sub(12, x, y, z); + if (c != 0) + { + subPInvFrom(z); + } + } + + public static void subtractExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.sub(24, xx, yy, zz); + if (c != 0) + { + if (Nat.subFrom(PExtInv.length, PExtInv, zz) != 0) + { + Nat.decAt(24, zz, PExtInv.length); + } + } + } + + public static void twice(int[] x, int[] z) + { + int c = Nat.shiftUpBit(12, x, 0, z); + if (c != 0 || (z[11] == P11 && Nat.gte(12, z, P))) + { + addPInvTo(z); + } + } + + private static void addPInvTo(int[] z) + { + long c = (z[0] & M) + 1; + z[0] = (int)c; + c >>= 32; + c += (z[1] & M) - 1; + z[1] = (int)c; + c >>= 32; + if (c != 0) + { + c += (z[2] & M); + z[2] = (int)c; + c >>= 32; + } + c += (z[3] & M) + 1; + z[3] = (int)c; + c >>= 32; + c += (z[4] & M) + 1; + z[4] = (int)c; + c >>= 32; + if (c != 0) + { + Nat.incAt(12, z, 5); + } + } + + private static void subPInvFrom(int[] z) + { + long c = (z[0] & M) - 1; + z[0] = (int)c; + c >>= 32; + c += (z[1] & M) + 1; + z[1] = (int)c; + c >>= 32; + if (c != 0) + { + c += (z[2] & M); + z[2] = (int)c; + c >>= 32; + } + c += (z[3] & M) - 1; + z[3] = (int)c; + c >>= 32; + c += (z[4] & M) - 1; + z[4] = (int)c; + c >>= 32; + if (c != 0) + { + Nat.decAt(12, z, 5); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java new file mode 100644 index 0000000..24e585d --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java @@ -0,0 +1,211 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Mod; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.util.Arrays; + +public class SecP384R1FieldElement extends ECFieldElement +{ + public static final BigInteger Q = SecP384R1Curve.q; + + protected int[] x; + + public SecP384R1FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0) + { + throw new IllegalArgumentException("x value invalid for SecP384R1FieldElement"); + } + + this.x = SecP384R1Field.fromBigInteger(x); + } + + public SecP384R1FieldElement() + { + this.x = Nat.create(12); + } + + protected SecP384R1FieldElement(int[] x) + { + this.x = x; + } + + public boolean isZero() + { + return Nat.isZero(12, x); + } + + public boolean isOne() + { + return Nat.isOne(12, x); + } + + public boolean testBitZero() + { + return Nat.getBit(x, 0) == 1; + } + + public BigInteger toBigInteger() + { + return Nat.toBigInteger(12, x); + } + + public String getFieldName() + { + return "SecP384R1Field"; + } + + public int getFieldSize() + { + return Q.bitLength(); + } + + public ECFieldElement add(ECFieldElement b) + { + int[] z = Nat.create(12); + SecP384R1Field.add(x, ((SecP384R1FieldElement)b).x, z); + return new SecP384R1FieldElement(z); + } + + public ECFieldElement addOne() + { + int[] z = Nat.create(12); + SecP384R1Field.addOne(x, z); + return new SecP384R1FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + int[] z = Nat.create(12); + SecP384R1Field.subtract(x, ((SecP384R1FieldElement)b).x, z); + return new SecP384R1FieldElement(z); + } + + public ECFieldElement multiply(ECFieldElement b) + { + int[] z = Nat.create(12); + SecP384R1Field.multiply(x, ((SecP384R1FieldElement)b).x, z); + return new SecP384R1FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { +// return multiply(b.invert()); + int[] z = Nat.create(12); + Mod.invert(SecP384R1Field.P, ((SecP384R1FieldElement)b).x, z); + SecP384R1Field.multiply(z, x, z); + return new SecP384R1FieldElement(z); + } + + public ECFieldElement negate() + { + int[] z = Nat.create(12); + SecP384R1Field.negate(x, z); + return new SecP384R1FieldElement(z); + } + + public ECFieldElement square() + { + int[] z = Nat.create(12); + SecP384R1Field.square(x, z); + return new SecP384R1FieldElement(z); + } + + public ECFieldElement invert() + { +// return new SecP384R1FieldElement(toBigInteger().modInverse(Q)); + int[] z = Nat.create(12); + Mod.invert(SecP384R1Field.P, x, z); + return new SecP384R1FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public ECFieldElement sqrt() + { + // Raise this element to the exponent 2^382 - 2^126 - 2^94 + 2^30 + + int[] x1 = this.x; + if (Nat.isZero(12, x1) || Nat.isOne(12, x1)) + { + return this; + } + + int[] t1 = Nat.create(12); + int[] t2 = Nat.create(12); + int[] t3 = Nat.create(12); + int[] t4 = Nat.create(12); + + SecP384R1Field.square(x1, t1); + SecP384R1Field.multiply(t1, x1, t1); + + SecP384R1Field.squareN(t1, 2, t2); + SecP384R1Field.multiply(t2, t1, t2); + + SecP384R1Field.square(t2, t2); + SecP384R1Field.multiply(t2, x1, t2); + + SecP384R1Field.squareN(t2, 5, t3); + SecP384R1Field.multiply(t3, t2, t3); + + SecP384R1Field.squareN(t3, 5, t4); + SecP384R1Field.multiply(t4, t2, t4); + + SecP384R1Field.squareN(t4, 15, t2); + SecP384R1Field.multiply(t2, t4, t2); + + SecP384R1Field.squareN(t2, 2, t3); + SecP384R1Field.multiply(t1, t3, t1); + + SecP384R1Field.squareN(t3, 28, t3); + SecP384R1Field.multiply(t2, t3, t2); + + SecP384R1Field.squareN(t2, 60, t3); + SecP384R1Field.multiply(t3, t2, t3); + + int[] r = t2; + + SecP384R1Field.squareN(t3, 120, r); + SecP384R1Field.multiply(r, t3, r); + + SecP384R1Field.squareN(r, 15, r); + SecP384R1Field.multiply(r, t4, r); + + SecP384R1Field.squareN(r, 33, r); + SecP384R1Field.multiply(r, t1, r); + + SecP384R1Field.squareN(r, 64, r); + SecP384R1Field.multiply(r, x1, r); + + SecP384R1Field.squareN(r, 30, t1); + SecP384R1Field.square(t1, t2); + + return Nat.eq(12, x1, t2) ? new SecP384R1FieldElement(t1) : null; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecP384R1FieldElement)) + { + return false; + } + + SecP384R1FieldElement o = (SecP384R1FieldElement)other; + return Nat.eq(12, x, o.x); + } + + public int hashCode() + { + return Q.hashCode() ^ Arrays.hashCode(x, 0, 12); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java new file mode 100644 index 0000000..89f6bf4 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java @@ -0,0 +1,309 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat384; + +public class SecP384R1Point extends ECPoint.AbstractFp +{ + /** + * 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 SecP384R1Point(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 SecP384R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecP384R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecP384R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + if (this == b) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + SecP384R1FieldElement X1 = (SecP384R1FieldElement)this.x, Y1 = (SecP384R1FieldElement)this.y; + SecP384R1FieldElement X2 = (SecP384R1FieldElement)b.getXCoord(), Y2 = (SecP384R1FieldElement)b.getYCoord(); + + SecP384R1FieldElement Z1 = (SecP384R1FieldElement)this.zs[0]; + SecP384R1FieldElement Z2 = (SecP384R1FieldElement)b.getZCoord(0); + + int c; + int[] tt1 = Nat.create(24); + int[] tt2 = Nat.create(24); + int[] t3 = Nat.create(12); + int[] t4 = Nat.create(12); + + boolean Z1IsOne = Z1.isOne(); + int[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP384R1Field.square(Z1.x, S2); + + U2 = tt2; + SecP384R1Field.multiply(S2, X2.x, U2); + + SecP384R1Field.multiply(S2, Z1.x, S2); + SecP384R1Field.multiply(S2, Y2.x, S2); + } + + boolean Z2IsOne = Z2.isOne(); + int[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP384R1Field.square(Z2.x, S1); + + U1 = tt1; + SecP384R1Field.multiply(S1, X1.x, U1); + + SecP384R1Field.multiply(S1, Z2.x, S1); + SecP384R1Field.multiply(S1, Y1.x, S1); + } + + int[] H = Nat.create(12); + SecP384R1Field.subtract(U1, U2, H); + + int[] R = Nat.create(12); + SecP384R1Field.subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat.isZero(12, H)) + { + if (Nat.isZero(12, R)) + { + // 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(); + } + + int[] HSquared = t3; + SecP384R1Field.square(H, HSquared); + + int[] G = Nat.create(12); + SecP384R1Field.multiply(HSquared, H, G); + + int[] V = t3; + SecP384R1Field.multiply(HSquared, U1, V); + + SecP384R1Field.negate(G, G); + Nat384.mul(S1, G, tt1); + + c = Nat.addBothTo(12, V, V, G); + SecP384R1Field.reduce32(c, G); + + SecP384R1FieldElement X3 = new SecP384R1FieldElement(t4); + SecP384R1Field.square(R, X3.x); + SecP384R1Field.subtract(X3.x, G, X3.x); + + SecP384R1FieldElement Y3 = new SecP384R1FieldElement(G); + SecP384R1Field.subtract(V, X3.x, Y3.x); + Nat384.mul(Y3.x, R, tt2); + SecP384R1Field.addExt(tt1, tt2, tt1); + SecP384R1Field.reduce(tt1, Y3.x); + + SecP384R1FieldElement Z3 = new SecP384R1FieldElement(H); + if (!Z1IsOne) + { + SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP384R1Field.multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[]{ Z3 }; + + return new SecP384R1Point(curve, X3, Y3, zs, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + SecP384R1FieldElement Y1 = (SecP384R1FieldElement)this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + SecP384R1FieldElement X1 = (SecP384R1FieldElement)this.x, Z1 = (SecP384R1FieldElement)this.zs[0]; + + int c; + int[] t1 = Nat.create(12); + int[] t2 = Nat.create(12); + + int[] Y1Squared = Nat.create(12); + SecP384R1Field.square(Y1.x, Y1Squared); + + int[] T = Nat.create(12); + SecP384R1Field.square(Y1Squared, T); + + boolean Z1IsOne = Z1.isOne(); + + int[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP384R1Field.square(Z1.x, Z1Squared); + } + + SecP384R1Field.subtract(X1.x, Z1Squared, t1); + + int[] M = t2; + SecP384R1Field.add(X1.x, Z1Squared, M); + SecP384R1Field.multiply(M, t1, M); + c = Nat.addBothTo(12, M, M, M); + SecP384R1Field.reduce32(c, M); + + int[] S = Y1Squared; + SecP384R1Field.multiply(Y1Squared, X1.x, S); + c = Nat.shiftUpBits(12, S, 2, 0); + SecP384R1Field.reduce32(c, S); + + c = Nat.shiftUpBits(12, T, 3, 0, t1); + SecP384R1Field.reduce32(c, t1); + + SecP384R1FieldElement X3 = new SecP384R1FieldElement(T); + SecP384R1Field.square(M, X3.x); + SecP384R1Field.subtract(X3.x, S, X3.x); + SecP384R1Field.subtract(X3.x, S, X3.x); + + SecP384R1FieldElement Y3 = new SecP384R1FieldElement(S); + SecP384R1Field.subtract(S, X3.x, Y3.x); + SecP384R1Field.multiply(Y3.x, M, Y3.x); + SecP384R1Field.subtract(Y3.x, t1, Y3.x); + + SecP384R1FieldElement Z3 = new SecP384R1FieldElement(M); + SecP384R1Field.twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP384R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + 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; + } + + return twice().add(b); + } + + public ECPoint threeTimes() + { + if (this.isInfinity() || this.y.isZero()) + { + return this; + } + + // NOTE: Be careful about recursions between twicePlus and threeTimes + return twice().add(this); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP384R1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Curve.java new file mode 100644 index 0000000..16691b1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Curve.java @@ -0,0 +1,80 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecP521R1Curve extends ECCurve.AbstractFp +{ + public static final BigInteger q = new BigInteger(1, + Hex.decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")); + + private static final int SecP521R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP521R1Point infinity; + + public SecP521R1Curve() + { + super(q); + + this.infinity = new SecP521R1Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, + Hex.decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC"))); + this.b = fromBigInteger(new BigInteger(1, + Hex.decode("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00"))); + this.order = new BigInteger(1, Hex.decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409")); + this.cofactor = BigInteger.valueOf(1); + + this.coord = SecP521R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecP521R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public BigInteger getQ() + { + return q; + } + + public int getFieldSize() + { + return q.bitLength(); + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecP521R1FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecP521R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecP521R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java new file mode 100644 index 0000000..00f1066 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java @@ -0,0 +1,156 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat512; + +public class SecP521R1Field +{ + // 2^521 - 1 + static final int[] P = new int[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x1FF }; + private static final int P16 = 0x1FF; + + public static void add(int[] x, int[] y, int[] z) + { + int c = Nat.add(16, x, y, z) + x[16] + y[16]; + if (c > P16 || (c == P16 && Nat.eq(16, z, P))) + { + c += Nat.inc(16, z); + c &= P16; + } + z[16] = c; + } + + public static void addOne(int[] x, int[] z) + { + int c = Nat.inc(16, x, z) + x[16]; + if (c > P16 || (c == P16 && Nat.eq(16, z, P))) + { + c += Nat.inc(16, z); + c &= P16; + } + z[16] = c; + } + + public static int[] fromBigInteger(BigInteger x) + { + int[] z = Nat.fromBigInteger(521, x); + if (Nat.eq(17, z, P)) + { + Nat.zero(17, z); + } + return z; + } + + public static void half(int[] x, int[] z) + { + int x16 = x[16]; + int c = Nat.shiftDownBit(16, x, x16, z); + z[16] = (x16 >>> 1) | (c >>> 23); + } + + public static void multiply(int[] x, int[] y, int[] z) + { + int[] tt = Nat.create(33); + implMultiply(x, y, tt); + reduce(tt, z); + } + + public static void negate(int[] x, int[] z) + { + if (Nat.isZero(17, x)) + { + Nat.zero(17, z); + } + else + { + Nat.sub(17, P, x, z); + } + } + + public static void reduce(int[] xx, int[] z) + { +// assert xx[32] >>> 18 == 0; + + int xx32 = xx[32]; + int c = Nat.shiftDownBits(16, xx, 16, 9, xx32, z, 0) >>> 23; + c += xx32 >>> 9; + c += Nat.addTo(16, xx, z); + if (c > P16 || (c == P16 && Nat.eq(16, z, P))) + { + c += Nat.inc(16, z); + c &= P16; + } + z[16] = c; + } + + public static void reduce23(int[] z) + { + int z16 = z[16]; + int c = Nat.addWordTo(16, z16 >>> 9, z) + (z16 & P16); + if (c > P16 || (c == P16 && Nat.eq(16, z, P))) + { + c += Nat.inc(16, z); + c &= P16; + } + z[16] = c; + } + + public static void square(int[] x, int[] z) + { + int[] tt = Nat.create(33); + implSquare(x, tt); + reduce(tt, z); + } + + public static void squareN(int[] x, int n, int[] z) + { +// assert n > 0; + + int[] tt = Nat.create(33); + implSquare(x, tt); + reduce(tt, z); + + while (--n > 0) + { + implSquare(z, tt); + reduce(tt, z); + } + } + + public static void subtract(int[] x, int[] y, int[] z) + { + int c = Nat.sub(16, x, y, z) + x[16] - y[16]; + if (c < 0) + { + c += Nat.dec(16, z); + c &= P16; + } + z[16] = c; + } + + public static void twice(int[] x, int[] z) + { + int x16 = x[16]; + int c = Nat.shiftUpBit(16, x, x16 << 23, z) | (x16 << 1); + z[16] = c & P16; + } + + protected static void implMultiply(int[] x, int[] y, int[] zz) + { + Nat512.mul(x, y, zz); + + int x16 = x[16], y16 = y[16]; + zz[32] = Nat.mul31BothAdd(16, x16, y, y16, x, zz, 16) + (x16 * y16); + } + + protected static void implSquare(int[] x, int[] zz) + { + Nat512.square(x, zz); + + int x16 = x[16]; + zz[32] = Nat.mulWordAddTo(16, x16 << 1, x, 0, zz, 16) + (x16 * x16); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java new file mode 100644 index 0000000..ce9b639 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java @@ -0,0 +1,169 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Mod; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.util.Arrays; + +public class SecP521R1FieldElement extends ECFieldElement +{ + public static final BigInteger Q = SecP521R1Curve.q; + + protected int[] x; + + public SecP521R1FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0) + { + throw new IllegalArgumentException("x value invalid for SecP521R1FieldElement"); + } + + this.x = SecP521R1Field.fromBigInteger(x); + } + + public SecP521R1FieldElement() + { + this.x = Nat.create(17); + } + + protected SecP521R1FieldElement(int[] x) + { + this.x = x; + } + + public boolean isZero() + { + return Nat.isZero(17, x); + } + + public boolean isOne() + { + return Nat.isOne(17, x); + } + + public boolean testBitZero() + { + return Nat.getBit(x, 0) == 1; + } + + public BigInteger toBigInteger() + { + return Nat.toBigInteger(17, x); + } + + public String getFieldName() + { + return "SecP521R1Field"; + } + + public int getFieldSize() + { + return Q.bitLength(); + } + + public ECFieldElement add(ECFieldElement b) + { + int[] z = Nat.create(17); + SecP521R1Field.add(x, ((SecP521R1FieldElement)b).x, z); + return new SecP521R1FieldElement(z); + } + + public ECFieldElement addOne() + { + int[] z = Nat.create(17); + SecP521R1Field.addOne(x, z); + return new SecP521R1FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + int[] z = Nat.create(17); + SecP521R1Field.subtract(x, ((SecP521R1FieldElement)b).x, z); + return new SecP521R1FieldElement(z); + } + + public ECFieldElement multiply(ECFieldElement b) + { + int[] z = Nat.create(17); + SecP521R1Field.multiply(x, ((SecP521R1FieldElement)b).x, z); + return new SecP521R1FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { +// return multiply(b.invert()); + int[] z = Nat.create(17); + Mod.invert(SecP521R1Field.P, ((SecP521R1FieldElement)b).x, z); + SecP521R1Field.multiply(z, x, z); + return new SecP521R1FieldElement(z); + } + + public ECFieldElement negate() + { + int[] z = Nat.create(17); + SecP521R1Field.negate(x, z); + return new SecP521R1FieldElement(z); + } + + public ECFieldElement square() + { + int[] z = Nat.create(17); + SecP521R1Field.square(x, z); + return new SecP521R1FieldElement(z); + } + + public ECFieldElement invert() + { +// return new SecP521R1FieldElement(toBigInteger().modInverse(Q)); + int[] z = Nat.create(17); + Mod.invert(SecP521R1Field.P, x, z); + return new SecP521R1FieldElement(z); + } + + // D.1.4 91 + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public ECFieldElement sqrt() + { + // Raise this element to the exponent 2^519 + + int[] x1 = this.x; + if (Nat.isZero(17, x1) || Nat.isOne(17, x1)) + { + return this; + } + + int[] t1 = Nat.create(17); + int[] t2 = Nat.create(17); + + SecP521R1Field.squareN(x1, 519, t1); + SecP521R1Field.square(t1, t2); + + return Nat.eq(17, x1, t2) ? new SecP521R1FieldElement(t1) : null; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecP521R1FieldElement)) + { + return false; + } + + SecP521R1FieldElement o = (SecP521R1FieldElement)other; + return Nat.eq(17, x, o.x); + } + + public int hashCode() + { + return Q.hashCode() ^ Arrays.hashCode(x, 0, 17); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java new file mode 100644 index 0000000..d973715 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java @@ -0,0 +1,333 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.raw.Nat; + +public class SecP521R1Point extends ECPoint.AbstractFp +{ + /** + * 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 SecP521R1Point(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 SecP521R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecP521R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecP521R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + if (this == b) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + SecP521R1FieldElement X1 = (SecP521R1FieldElement)this.x, Y1 = (SecP521R1FieldElement)this.y; + SecP521R1FieldElement X2 = (SecP521R1FieldElement)b.getXCoord(), Y2 = (SecP521R1FieldElement)b.getYCoord(); + + SecP521R1FieldElement Z1 = (SecP521R1FieldElement)this.zs[0]; + SecP521R1FieldElement Z2 = (SecP521R1FieldElement)b.getZCoord(0); + + int[] t1 = Nat.create(17); + int[] t2 = Nat.create(17); + int[] t3 = Nat.create(17); + int[] t4 = Nat.create(17); + + boolean Z1IsOne = Z1.isOne(); + int[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP521R1Field.square(Z1.x, S2); + + U2 = t2; + SecP521R1Field.multiply(S2, X2.x, U2); + + SecP521R1Field.multiply(S2, Z1.x, S2); + SecP521R1Field.multiply(S2, Y2.x, S2); + } + + boolean Z2IsOne = Z2.isOne(); + int[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP521R1Field.square(Z2.x, S1); + + U1 = t1; + SecP521R1Field.multiply(S1, X1.x, U1); + + SecP521R1Field.multiply(S1, Z2.x, S1); + SecP521R1Field.multiply(S1, Y1.x, S1); + } + + int[] H = Nat.create(17); + SecP521R1Field.subtract(U1, U2, H); + + int[] R = t2; + SecP521R1Field.subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat.isZero(17, H)) + { + if (Nat.isZero(17, R)) + { + // 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(); + } + + int[] HSquared = t3; + SecP521R1Field.square(H, HSquared); + + int[] G = Nat.create(17); + SecP521R1Field.multiply(HSquared, H, G); + + int[] V = t3; + SecP521R1Field.multiply(HSquared, U1, V); + + SecP521R1Field.multiply(S1, G, t1); + + SecP521R1FieldElement X3 = new SecP521R1FieldElement(t4); + SecP521R1Field.square(R, X3.x); + SecP521R1Field.add(X3.x, G, X3.x); + SecP521R1Field.subtract(X3.x, V, X3.x); + SecP521R1Field.subtract(X3.x, V, X3.x); + + SecP521R1FieldElement Y3 = new SecP521R1FieldElement(G); + SecP521R1Field.subtract(V, X3.x, Y3.x); + SecP521R1Field.multiply(Y3.x, R, t2); + SecP521R1Field.subtract(t2, t1, Y3.x); + + SecP521R1FieldElement Z3 = new SecP521R1FieldElement(H); + if (!Z1IsOne) + { + SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP521R1Field.multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[]{ Z3 }; + + return new SecP521R1Point(curve, X3, Y3, zs, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + SecP521R1FieldElement Y1 = (SecP521R1FieldElement)this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + SecP521R1FieldElement X1 = (SecP521R1FieldElement)this.x, Z1 = (SecP521R1FieldElement)this.zs[0]; + + int[] t1 = Nat.create(17); + int[] t2 = Nat.create(17); + + int[] Y1Squared = Nat.create(17); + SecP521R1Field.square(Y1.x, Y1Squared); + + int[] T = Nat.create(17); + SecP521R1Field.square(Y1Squared, T); + + boolean Z1IsOne = Z1.isOne(); + + int[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP521R1Field.square(Z1.x, Z1Squared); + } + + SecP521R1Field.subtract(X1.x, Z1Squared, t1); + + int[] M = t2; + SecP521R1Field.add(X1.x, Z1Squared, M); + SecP521R1Field.multiply(M, t1, M); + Nat.addBothTo(17, M, M, M); + SecP521R1Field.reduce23(M); + + int[] S = Y1Squared; + SecP521R1Field.multiply(Y1Squared, X1.x, S); + Nat.shiftUpBits(17, S, 2, 0); + SecP521R1Field.reduce23(S); + + Nat.shiftUpBits(17, T, 3, 0, t1); + SecP521R1Field.reduce23(t1); + + SecP521R1FieldElement X3 = new SecP521R1FieldElement(T); + SecP521R1Field.square(M, X3.x); + SecP521R1Field.subtract(X3.x, S, X3.x); + SecP521R1Field.subtract(X3.x, S, X3.x); + + SecP521R1FieldElement Y3 = new SecP521R1FieldElement(S); + SecP521R1Field.subtract(S, X3.x, Y3.x); + SecP521R1Field.multiply(Y3.x, M, Y3.x); + SecP521R1Field.subtract(Y3.x, t1, Y3.x); + + SecP521R1FieldElement Z3 = new SecP521R1FieldElement(M); + SecP521R1Field.twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP521R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + 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; + } + + return twice().add(b); + } + + public ECPoint threeTimes() + { + if (this.isInfinity() || this.y.isZero()) + { + return this; + } + + // 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); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP521R1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/endo/ECEndomorphism.java b/bcprov/src/main/java/org/bouncycastle/math/ec/endo/ECEndomorphism.java new file mode 100644 index 0000000..2be0c01 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/endo/ECEndomorphism.java @@ -0,0 +1,10 @@ +package org.bouncycastle.math.ec.endo; + +import org.bouncycastle.math.ec.ECPointMap; + +public interface ECEndomorphism +{ + ECPointMap getPointMap(); + + boolean hasEfficientPointMap(); +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVEndomorphism.java b/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVEndomorphism.java new file mode 100644 index 0000000..8897bb3 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVEndomorphism.java @@ -0,0 +1,8 @@ +package org.bouncycastle.math.ec.endo; + +import java.math.BigInteger; + +public interface GLVEndomorphism extends ECEndomorphism +{ + BigInteger[] decomposeScalar(BigInteger k); +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVTypeBEndomorphism.java b/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVTypeBEndomorphism.java new file mode 100644 index 0000000..ab710d1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVTypeBEndomorphism.java @@ -0,0 +1,58 @@ +package org.bouncycastle.math.ec.endo; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPointMap; +import org.bouncycastle.math.ec.ScaleXPointMap; + +public class GLVTypeBEndomorphism implements GLVEndomorphism +{ + protected final ECCurve curve; + protected final GLVTypeBParameters parameters; + protected final ECPointMap pointMap; + + public GLVTypeBEndomorphism(ECCurve curve, GLVTypeBParameters parameters) + { + this.curve = curve; + this.parameters = parameters; + this.pointMap = new ScaleXPointMap(curve.fromBigInteger(parameters.getBeta())); + } + + public BigInteger[] decomposeScalar(BigInteger k) + { + int bits = parameters.getBits(); + BigInteger b1 = calculateB(k, parameters.getG1(), bits); + BigInteger b2 = calculateB(k, parameters.getG2(), bits); + + BigInteger[] v1 = parameters.getV1(), v2 = parameters.getV2(); + BigInteger a = k.subtract((b1.multiply(v1[0])).add(b2.multiply(v2[0]))); + BigInteger b = (b1.multiply(v1[1])).add(b2.multiply(v2[1])).negate(); + + return new BigInteger[]{ a, b }; + } + + public ECPointMap getPointMap() + { + return pointMap; + } + + public boolean hasEfficientPointMap() + { + return true; + } + + protected BigInteger calculateB(BigInteger k, BigInteger g, int t) + { + boolean negative = (g.signum() < 0); + BigInteger b = k.multiply(g.abs()); + boolean extra = b.testBit(t - 1); + b = b.shiftRight(t); + if (extra) + { + b = b.add(ECConstants.ONE); + } + return negative ? b.negate() : b; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java b/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java new file mode 100644 index 0000000..f02a882 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java @@ -0,0 +1,59 @@ +package org.bouncycastle.math.ec.endo; + +import java.math.BigInteger; + +public class GLVTypeBParameters +{ + protected final BigInteger beta; + protected final BigInteger lambda; + protected final BigInteger[] v1, v2; + protected final BigInteger g1, g2; + protected final int bits; + + public GLVTypeBParameters(BigInteger beta, BigInteger lambda, BigInteger[] v1, BigInteger[] v2, BigInteger g1, + BigInteger g2, int bits) + { + this.beta = beta; + this.lambda = lambda; + this.v1 = v1; + this.v2 = v2; + this.g1 = g1; + this.g2 = g2; + this.bits = bits; + } + + public BigInteger getBeta() + { + return beta; + } + + public BigInteger getLambda() + { + return lambda; + } + + public BigInteger[] getV1() + { + return v1; + } + + public BigInteger[] getV2() + { + return v2; + } + + public BigInteger getG1() + { + return g1; + } + + public BigInteger getG2() + { + return g2; + } + + public int getBits() + { + return bits; + } +} |