diff options
author | David Hook <dgh@cryptoworkshop.com> | 2014-01-03 13:11:55 +0400 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2014-01-03 13:11:55 +0400 |
commit | a65ee18c49c5636b706ab56a0085da3b79a91e97 (patch) | |
tree | 8bb8361fc2ea82fa23870766a47081c1602c54b3 | |
parent | a337d00780b0110bddcedc8bbfd305d13ff3d3f1 (diff) | |
parent | 9f93f7bf294d6dcd4711fa9ab3bd7b5743049e99 (diff) |
Merge remote-tracking branch 'origin/master'
14 files changed, 1334 insertions, 174 deletions
diff --git a/core/src/main/java/org/bouncycastle/math/ec/ECCurve.java b/core/src/main/java/org/bouncycastle/math/ec/ECCurve.java index f44a3756..0bc0dce4 100644 --- a/core/src/main/java/org/bouncycastle/math/ec/ECCurve.java +++ b/core/src/main/java/org/bouncycastle/math/ec/ECCurve.java @@ -789,7 +789,7 @@ public abstract class ECCurve */ public boolean isKoblitz() { - return order != null && cofactor != null && a.bitLength() <= 1 && b.bitLength() == 1; + return order != null && cofactor != null && a.bitLength() <= 1 && b.isOne(); } /** diff --git a/core/src/main/java/org/bouncycastle/math/ec/ECPoint.java b/core/src/main/java/org/bouncycastle/math/ec/ECPoint.java index c6ec37da..9cd51b6e 100644 --- a/core/src/main/java/org/bouncycastle/math/ec/ECPoint.java +++ b/core/src/main/java/org/bouncycastle/math/ec/ECPoint.java @@ -196,7 +196,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 +222,7 @@ public abstract class ECPoint default: { ECFieldElement Z1 = getZCoord(0); - if (Z1.bitLength() == 1) + if (Z1.isOne()) { return this; } @@ -569,8 +569,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); @@ -612,7 +612,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 +662,7 @@ public abstract class ECPoint S2 = Z1Cubed.multiply(Y2); } - boolean Z2IsOne = Z2.bitLength() == 1; + boolean Z2IsOne = Z2.isOne(); ECFieldElement Z2Squared, U1, S1; if (Z2IsOne) { @@ -778,7 +778,7 @@ public abstract class ECPoint { ECFieldElement Z1 = this.zs[0]; - boolean Z1IsOne = Z1.bitLength() == 1; + boolean Z1IsOne = Z1.isOne(); // TODO Optimize for small negative a4 and -3 ECFieldElement w = curve.getA(); @@ -806,7 +806,7 @@ public abstract class ECPoint { ECFieldElement Z1 = this.zs[0]; - boolean Z1IsOne = Z1.bitLength() == 1; + boolean Z1IsOne = Z1.isOne(); ECFieldElement Y1Squared = Y1.square(); ECFieldElement T = Y1Squared.square(); @@ -1107,7 +1107,7 @@ public abstract class ECPoint ECFieldElement _8T = eight(T); ECFieldElement Y3 = M.multiply(S.subtract(X3)).subtract(_8T); ECFieldElement W3 = calculateW ? two(_8T.multiply(W1)) : null; - ECFieldElement Z3 = two(Z1.bitLength() == 1 ? Y1 : Y1.multiply(Z1)); + ECFieldElement Z3 = two(Z1.isOne() ? Y1 : Y1.multiply(Z1)); return new ECPoint.Fp(this.getCurve(), X3, Y3, new ECFieldElement[]{ Z3, W3 }, this.withCompression); } @@ -1194,7 +1194,7 @@ public abstract class ECPoint if (ECCurve.COORD_LAMBDA_PROJECTIVE == coord) { ECFieldElement Z = zs[0]; - if (Z.bitLength() != 1) + if (!Z.isOne()) { Y = Y.divide(Z); } @@ -1317,7 +1317,7 @@ 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 U2 = Z2IsOne ? Y1 : Y1.multiply(Z2); @@ -1357,7 +1357,7 @@ public abstract class ECPoint 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) { @@ -1365,7 +1365,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) { @@ -1527,7 +1527,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); @@ -1547,7 +1547,7 @@ 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(); @@ -1609,7 +1609,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); } diff --git a/core/src/main/java/org/bouncycastle/math/ec/custom/CustomNamedCurves.java b/core/src/main/java/org/bouncycastle/math/ec/custom/CustomNamedCurves.java new file mode 100644 index 00000000..4a788128 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/math/ec/custom/CustomNamedCurves.java @@ -0,0 +1,120 @@ +package org.bouncycastle.math.ec.custom; + +import java.util.Enumeration; +import java.util.Hashtable; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.sec.SECObjectIdentifiers; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.asn1.x9.X9ECParametersHolder; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.custom.sec.SecP256K1Curve; +import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve; +import org.bouncycastle.util.Strings; +import org.bouncycastle.util.encoders.Hex; + +public class CustomNamedCurves +{ + private static ECCurve configureCurve(ECCurve curve) + { + return curve; + } + + /* + * secp256k1 + */ + static X9ECParametersHolder secp256k1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + byte[] S = null; + ECCurve curve = configureCurve(new SecP256K1Curve()); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" + + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8")); + return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S); + } + }; + + /* + * secp256r1 + */ + static X9ECParametersHolder secp256r1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + byte[] S = Hex.decode("C49D360886E704936A6678E1139D26B7819F7E90"); + ECCurve curve = configureCurve(new SecP256R1Curve()); + ECPoint G = curve.decodePoint(Hex.decode("04" + + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5")); + return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S); + } + }; + + static final Hashtable objIds = new Hashtable(); + static final Hashtable curves = new Hashtable(); + static final Hashtable names = new Hashtable(); + + static void defineCurve(String name, ASN1ObjectIdentifier oid, X9ECParametersHolder holder) + { + objIds.put(name, oid); + names.put(oid, name); + curves.put(oid, holder); + } + + static + { + defineCurve("secp256k1", SECObjectIdentifiers.secp256k1, secp256k1); + defineCurve("secp256r1", SECObjectIdentifiers.secp256r1, secp256r1); + + objIds.put(Strings.toLowerCase("P-256"), SECObjectIdentifiers.secp256r1); + } + + public static X9ECParameters getByName(String name) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name)); + return oid == null ? null : getByOID(oid); + } + + /** + * return the X9ECParameters object for the named curve represented by the passed in object + * identifier. Null if the curve isn't present. + * + * @param oid + * an object identifier representing a named curve, if present. + */ + public static X9ECParameters getByOID(ASN1ObjectIdentifier oid) + { + X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid); + return holder == null ? null : holder.getParameters(); + } + + /** + * return the object identifier signified by the passed in name. Null if there is no object + * identifier associated with name. + * + * @return the object identifier associated with name, if present. + */ + public static ASN1ObjectIdentifier getOID(String name) + { + return (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name)); + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static String getName(ASN1ObjectIdentifier oid) + { + return (String)names.get(oid); + } + + /** + * returns an enumeration containing the name strings for curves contained in this structure. + */ + public static Enumeration getNames() + { + return objIds.keys(); + } +} diff --git a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/Mont256.java b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/Mont256.java new file mode 100644 index 00000000..b0a415e7 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/Mont256.java @@ -0,0 +1,152 @@ +package org.bouncycastle.math.ec.custom.sec; + +public abstract class Mont256 +{ + private static final long M = 0xFFFFFFFFL; + + public static int inverse32(int x) + { + assert (x & 1) == 1; + int z = x; // x.z == 1 mod 2**3 + z *= 2 - x * z; // x.z == 1 mod 2**6 + z *= 2 - x * z; // x.z == 1 mod 2**12 + z *= 2 - x * z; // x.z == 1 mod 2**24 + z *= 2 - x * z; // x.z == 1 mod 2**48 + assert x * z == 1; + return z; + } + + public static void multAdd(int[] x, int[] y, int[] z, int[] m, int mInv32) + { + int z_8 = 0; + long y_0 = y[0] & M; + + for (int i = 0; i < 8; ++i) + { + long z_0 = z[0] & M; + long x_i = x[i] & M; + + long prod1 = x_i * y_0; + long carry = (prod1 & M) + z_0; + + long t = ((int)carry * mInv32) & M; + + long prod2 = t * (m[0] & M); + carry += (prod2 & M); + assert (int)carry == 0; + carry = (carry >>> 32) + (prod1 >>> 32) + (prod2 >>> 32); + + for (int j = 1; j < 8; ++j) + { + prod1 = x_i * (y[j] & M); + prod2 = t * (m[j] & M); + + carry += (prod1 & M) + (prod2 & M) + (z[j] & M); + z[j - 1] = (int)carry; + carry = (carry >>> 32) + (prod1 >>> 32) + (prod2 >>> 32); + } + + carry += (z_8 & M); + z[7] = (int)carry; + z_8 = (int)(carry >>> 32); + } + + if (z_8 != 0 || Nat256.gte(z, m)) + { + Nat256.sub(z, m, z); + } + } + + public static void multAddXF(int[] x, int[] y, int[] z, int[] m) + { + assert m[0] == M; + + int z_8 = 0; + long y_0 = y[0] & M; + + for (int i = 0; i < 8; ++i) + { + long x_i = x[i] & M; + + long carry = x_i * y_0 + (z[0] & M); + long t = carry & M; + carry = (carry >>> 32) + t; + + for (int j = 1; j < 8; ++j) + { + long prod1 = x_i * (y[j] & M); + long prod2 = t * (m[j] & M); + + carry += (prod1 & M) + (prod2 & M) + (z[j] & M); + z[j - 1] = (int)carry; + carry = (carry >>> 32) + (prod1 >>> 32) + (prod2 >>> 32); + } + + carry += (z_8 & M); + z[7] = (int)carry; + z_8 = (int)(carry >>> 32); + } + + if (z_8 != 0 || Nat256.gte(z, m)) + { + Nat256.sub(z, m, z); + } + } + + public static void reduce(int[] z, int[] m, int mInv32) + { + for (int i = 0; i < 8; ++i) + { + int z_0 = z[0]; + + long t = (z_0 * mInv32) & M; + + long carry = t * (m[0] & M) + (z_0 & M); + assert (int)carry == 0; + carry >>>= 32; + + for (int j = 1; j < 8; ++j) + { + carry += t * (m[j] & M) + (z[j] & M); + z[j - 1] = (int)carry; + carry >>>= 32; + } + + z[7] = (int)carry; + assert carry >>> 32 == 0; + } + + if (Nat256.gte(z, m)) + { + Nat256.sub(z, m, z); + } + } + + public static void reduceXF(int[] z, int[] m) + { + assert m[0] == M; + + for (int i = 0; i < 8; ++i) + { + int z_0 = z[0]; + + long t = z_0 & M; + long carry = t; + + for (int j = 1; j < 8; ++j) + { + carry += t * (m[j] & M) + (z[j] & M); + z[j - 1] = (int)carry; + carry >>>= 32; + } + + z[7] = (int)carry; + assert carry >>> 32 == 0; + } + + if (Nat256.gte(z, m)) + { + Nat256.sub(z, m, z); + } + } +} diff --git a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/Nat256.java b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/Nat256.java index 91ae8406..9bd1d0dd 100644 --- a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/Nat256.java +++ b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/Nat256.java @@ -1,5 +1,9 @@ package org.bouncycastle.math.ec.custom.sec; +import java.math.BigInteger; + +import org.bouncycastle.crypto.util.Pack; + public abstract class Nat256 { private static final long M = 0xFFFFFFFFL; @@ -34,6 +38,36 @@ public abstract class Nat256 return (int)c; } + public static int addBothTo(int[] x, int[] y, int[] z) + { + long c = 0; + c += (x[0] & M) + (y[0] & M) + (z[0] & M); + z[0] = (int)c; + c >>>= 32; + c += (x[1] & M) + (y[1] & M) + (z[1] & M); + z[1] = (int)c; + c >>>= 32; + c += (x[2] & M) + (y[2] & M) + (z[2] & M); + z[2] = (int)c; + c >>>= 32; + c += (x[3] & M) + (y[3] & M) + (z[3] & M); + z[3] = (int)c; + c >>>= 32; + c += (x[4] & M) + (y[4] & M) + (z[4] & M); + z[4] = (int)c; + c >>>= 32; + c += (x[5] & M) + (y[5] & M) + (z[5] & M); + z[5] = (int)c; + c >>>= 32; + c += (x[6] & M) + (y[6] & M) + (z[6] & M); + z[6] = (int)c; + c >>>= 32; + c += (x[7] & M) + (y[7] & M) + (z[7] & M); + z[7] = (int)c; + c >>>= 32; + return (int)c; + } + public static int addDWord(long x, int[] z, int zOff) { assert zOff < 6; @@ -87,6 +121,16 @@ public abstract class Nat256 return c == 0 ? 0 : incExt(zz, zzOff + 1); } + public static int[] create() + { + return new int[8]; + } + + public static int[] createExt() + { + return new int[16]; + } + public static int dec(int[] z, int zOff) { assert zOff < 8; @@ -98,10 +142,42 @@ public abstract class Nat256 return 0; } } - while(++i < 8); + while (++i < 8); return -1; } + public static int[] fromBigInteger(BigInteger x) + { + if (x.signum() < 0 || x.bitLength() > 256) + { + throw new IllegalArgumentException(); + } + + int[] z = create(); + int i = 0; + while (x.signum() != 0) + { + z[i++] = x.intValue(); + x = x.shiftRight(32); + } + return z; + } + + public static int getBit(int[] x, int bit) + { + if (bit == 0) + { + return x[0] & 1; + } + if ((bit & 255) != bit) + { + return 0; + } + int w = bit >>> 5; + int b = bit & 31; + return (x[w] >>> b) & 1; + } + public static boolean gte(int[] x, int[] y) { for (int i = 7; i >= 0; --i) @@ -176,19 +252,122 @@ public abstract class Nat256 public static void mul(int[] x, int[] y, int[] zz) { - zz[8] = mulWordExt(x[0], y, zz, 0); + long y_0 = y[0] & M; + long y_1 = y[1] & M; + long y_2 = y[2] & M; + long y_3 = y[3] & M; + long y_4 = y[4] & M; + long y_5 = y[5] & M; + long y_6 = y[6] & M; + long y_7 = y[7] & M; + + { + long c = 0, x_0 = x[0] & M; + c += x_0 * y_0; + zz[0] = (int)c; + c >>>= 32; + c += x_0 * y_1; + zz[1] = (int)c; + c >>>= 32; + c += x_0 * y_2; + zz[2] = (int)c; + c >>>= 32; + c += x_0 * y_3; + zz[3] = (int)c; + c >>>= 32; + c += x_0 * y_4; + zz[4] = (int)c; + c >>>= 32; + c += x_0 * y_5; + zz[5] = (int)c; + c >>>= 32; + c += x_0 * y_6; + zz[6] = (int)c; + c >>>= 32; + c += x_0 * y_7; + zz[7] = (int)c; + c >>>= 32; + zz[8] = (int)c; + } + for (int i = 1; i < 8; ++i) { - zz[i + 8] = mulWordAddExt(x[i], y, 0, zz, i); + long c = 0, x_i = x[i] & M; + c += x_i * y_0 + (zz[i + 0] & M); + zz[i + 0] = (int)c; + c >>>= 32; + c += x_i * y_1 + (zz[i + 1] & M); + zz[i + 1] = (int)c; + c >>>= 32; + c += x_i * y_2 + (zz[i + 2] & M); + zz[i + 2] = (int)c; + c >>>= 32; + c += x_i * y_3 + (zz[i + 3] & M); + zz[i + 3] = (int)c; + c >>>= 32; + c += x_i * y_4 + (zz[i + 4] & M); + zz[i + 4] = (int)c; + c >>>= 32; + c += x_i * y_5 + (zz[i + 5] & M); + zz[i + 5] = (int)c; + c >>>= 32; + c += x_i * y_6 + (zz[i + 6] & M); + zz[i + 6] = (int)c; + c >>>= 32; + c += x_i * y_7 + (zz[i + 7] & M); + zz[i + 7] = (int)c; + c >>>= 32; + zz[i + 8] = (int)c; } } - public static void mulAdd(int[] x, int[] y, int[] zz) + public static int mulAdd(int[] x, int[] y, int[] zz) { + long y_0 = y[0] & M; + long y_1 = y[1] & M; + long y_2 = y[2] & M; + long y_3 = y[3] & M; + long y_4 = y[4] & M; + long y_5 = y[5] & M; + long y_6 = y[6] & M; + long y_7 = y[7] & M; + + long t = 0; + for (int i = 0; i < 8; ++i) { - zz[i + 8] += mulWordAddExt(x[i], y, 0, zz, i); + long c = 0, x_i = x[i] & M; + c += x_i * y_0 + (zz[i + 0] & M); + zz[i + 0] = (int)c; + c >>>= 32; + c += x_i * y_1 + (zz[i + 1] & M); + zz[i + 1] = (int)c; + c >>>= 32; + c += x_i * y_2 + (zz[i + 2] & M); + zz[i + 2] = (int)c; + c >>>= 32; + c += x_i * y_3 + (zz[i + 3] & M); + zz[i + 3] = (int)c; + c >>>= 32; + c += x_i * y_4 + (zz[i + 4] & M); + zz[i + 4] = (int)c; + c >>>= 32; + c += x_i * y_5 + (zz[i + 5] & M); + zz[i + 5] = (int)c; + c >>>= 32; + c += x_i * y_6 + (zz[i + 6] & M); + zz[i + 6] = (int)c; + c >>>= 32; + c += x_i * y_7 + (zz[i + 7] & M); + zz[i + 7] = (int)c; + c >>>= 32; + c += t + (zz[i + 8] & M); + zz[i + 8] = (int)c; + c >>>= 32; + t = c; } + + return (int)t; } public static int mulWordAddExt(int x, int[] yy, int yyOff, int[] zz, int zzOff) @@ -204,30 +383,6 @@ public abstract class Nat256 c >>>= 32; } while (++i < 8); -// c += xVal * (yy[yyOff + 0] & M) + (zz[zzOff + 0] & M); -// zz[zzOff + 0] = (int)c; -// c >>>= 32; -// c += xVal * (yy[yyOff + 1] & M) + (zz[zzOff + 1] & M); -// zz[zzOff + 1] = (int)c; -// c >>>= 32; -// c += xVal * (yy[yyOff + 2] & M) + (zz[zzOff + 2] & M); -// zz[zzOff + 2] = (int)c; -// c >>>= 32; -// c += xVal * (yy[yyOff + 3] & M) + (zz[zzOff + 3] & M); -// zz[zzOff + 3] = (int)c; -// c >>>= 32; -// c += xVal * (yy[yyOff + 4] & M) + (zz[zzOff + 4] & M); -// zz[zzOff + 4] = (int)c; -// c >>>= 32; -// c += xVal * (yy[yyOff + 5] & M) + (zz[zzOff + 5] & M); -// zz[zzOff + 5] = (int)c; -// c >>>= 32; -// c += xVal * (yy[yyOff + 6] & M) + (zz[zzOff + 6] & M); -// zz[zzOff + 6] = (int)c; -// c >>>= 32; -// c += xVal * (yy[yyOff + 7] & M) + (zz[zzOff + 7] & M); -// zz[zzOff + 7] = (int)c; -// c >>>= 32; return (int)c; } @@ -274,30 +429,6 @@ public abstract class Nat256 c >>>= 32; } while (++i < 8); -// c += xVal * (y[0] & M); -// zz[zzOff + 0] = (int)c; -// c >>>= 32; -// c += xVal * (y[1] & M); -// zz[zzOff + 1] = (int)c; -// c >>>= 32; -// c += xVal * (y[2] & M); -// zz[zzOff + 2] = (int)c; -// c >>>= 32; -// c += xVal * (y[3] & M); -// zz[zzOff + 3] = (int)c; -// c >>>= 32; -// c += xVal * (y[4] & M); -// zz[zzOff + 4] = (int)c; -// c >>>= 32; -// c += xVal * (y[5] & M); -// zz[zzOff + 5] = (int)c; -// c >>>= 32; -// c += xVal * (y[6] & M); -// zz[zzOff + 6] = (int)c; -// c >>>= 32; -// c += xVal * (y[7] & M); -// zz[zzOff + 7] = (int)c; -// c >>>= 32; return (int)c; } @@ -367,6 +498,36 @@ public abstract class Nat256 return (int)c; } + public static int subBothFrom(int[] x, int[] y, int[] z) + { + long c = 0; + c += (z[0] & M) - (x[0] & M) - (y[0] & M); + z[0] = (int)c; + c >>= 32; + c += (z[1] & M) - (x[1] & M) - (y[1] & M); + z[1] = (int)c; + c >>= 32; + c += (z[2] & M) - (x[2] & M) - (y[2] & M); + z[2] = (int)c; + c >>= 32; + c += (z[3] & M) - (x[3] & M) - (y[3] & M); + z[3] = (int)c; + c >>= 32; + c += (z[4] & M) - (x[4] & M) - (y[4] & M); + z[4] = (int)c; + c >>= 32; + c += (z[5] & M) - (x[5] & M) - (y[5] & M); + z[5] = (int)c; + c >>= 32; + c += (z[6] & M) - (x[6] & M) - (y[6] & M); + z[6] = (int)c; + c >>= 32; + c += (z[7] & M) - (x[7] & M) - (y[7] & M); + z[7] = (int)c; + c >>= 32; + return (int)c; + } + public static int subDWord(long x, int[] z) { x = -x; @@ -379,6 +540,20 @@ public abstract class Nat256 return x == 0 ? 0 : dec(z, 2); } + public static BigInteger toBigInteger(int[] x) + { + byte[] bs = new byte[32]; + for (int i = 0; i < 8; ++i) + { + int x_i = x[i]; + if (x_i != 0) + { + Pack.intToBigEndian(x_i, bs, (7 - i) << 2); + } + } + return new BigInteger(1, bs); + } + public static void zero(int[] z) { z[0] = 0; diff --git a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java index c58c91d2..bc86385a 100644 --- a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java +++ b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java @@ -2,8 +2,6 @@ package org.bouncycastle.math.ec.custom.sec; import java.math.BigInteger; -import org.bouncycastle.crypto.util.Pack; - public class SecP256K1Field { private static final long M = 0xFFFFFFFFL; @@ -11,22 +9,13 @@ public class SecP256K1Field // 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 private static final int[] P = new int[] { 0xFFFFFC2F, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static final int P7 = 0xFFFFFFFF; private static final long PInv = 0x00000001000003D1L; - public static int[] create() - { - return new int[8]; - } - - public static int[] createDouble() - { - return new int[16]; - } - public static void add(int[] x, int[] y, int[] z) { int c = Nat256.add(x, y, z); - if (c != 0 || (z[7] == -1 && Nat256.gte(z, P))) + if (c != 0 || (z[7] == P7 && Nat256.gte(z, P))) { Nat256.addDWord(PInv, z, 0); } @@ -36,7 +25,7 @@ public class SecP256K1Field { System.arraycopy(x, 0, z, 0, 8); int c = Nat256.inc(z, 0); - if (c != 0 || (z[7] == -1 && Nat256.gte(z, P))) + if (c != 0 || (z[7] == P7 && Nat256.gte(z, P))) { Nat256.addDWord(PInv, z, 0); } @@ -44,38 +33,17 @@ public class SecP256K1Field public static int[] fromBigInteger(BigInteger x) { - if (x.signum() < 0 || x.bitLength() > 256) - { - throw new IllegalArgumentException(); - } - - int[] z = create(); - int i = 0; - while (x.bitLength() > 0) - { - z[i++] = x.intValue(); - x = x.shiftRight(32); - } - if (z[7] == -1 && Nat256.gte(z, P)) + int[] z = Nat256.fromBigInteger(x); + if (z[7] == P7 && Nat256.gte(z, P)) { Nat256.addDWord(PInv, z, 0); } return z; } - public static boolean isOne(int[] x) - { - return Nat256.isOne(x); - } - - public static boolean isZero(int[] x) - { - return Nat256.isZero(x); - } - public static void multiply(int[] x, int[] y, int[] z) { - int[] tt = createDouble(); + int[] tt = Nat256.createExt(); Nat256.mul(x, y, tt); reduce(tt, z); } @@ -104,9 +72,9 @@ public class SecP256K1Field assert c == 0L || c == 1L; - if (c != 0 || (tt[7] == -1 && Nat256.gte(tt, P))) + if (c != 0 || (tt[7] == P7 && Nat256.gte(tt, P))) { - Nat256.addDWord(PInv, z, 0); + Nat256.addDWord(PInv, tt, 0); } System.arraycopy(tt, 0, z, 0, 8); @@ -114,7 +82,7 @@ public class SecP256K1Field public static void square(int[] x, int[] z) { - int[] tt = createDouble(); + int[] tt = Nat256.createExt(); // NOTE: The simpler 'mul' performs better than 'square' // Nat256.square(x, tt); Nat256.mul(x, x, tt); @@ -129,25 +97,4 @@ public class SecP256K1Field Nat256.subDWord(PInv, z); } } - - public static boolean testBit(int[] x, int bit) - { - if (bit < 0 || bit > 255) - { - return false; - } - int w = bit >>> 5; - int b = bit & 31; - return (x[w] & (1 << b)) != 0; - } - - public static BigInteger toBigInteger(int[] x) - { - byte[] bs = new byte[32]; - for (int i = 0; i < 8; ++i) - { - Pack.intToBigEndian(x[i], bs, (7 - i) << 2); - } - return new BigInteger(1, bs); - } } diff --git a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java index 92a4f3e5..37122ccc 100644 --- a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java +++ b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java @@ -29,27 +29,27 @@ public class SecP256K1FieldElement extends ECFieldElement public boolean isZero() { - return SecP256K1Field.isZero(x); + return Nat256.isZero(x); } public boolean isOne() { - return SecP256K1Field.isOne(x); + return Nat256.isOne(x); } public boolean testBitZero() { - return SecP256K1Field.testBit(x, 0); + return Nat256.getBit(x, 0) == 1; } public BigInteger toBigInteger() { - return SecP256K1Field.toBigInteger(x); + return Nat256.toBigInteger(x); } public String getFieldName() { - return "FEp256k1"; + return "SecP256K1Field"; } public int getFieldSize() @@ -64,28 +64,28 @@ public class SecP256K1FieldElement extends ECFieldElement public ECFieldElement add(ECFieldElement b) { - int[] z = SecP256K1Field.create(); + int[] z = Nat256.create(); SecP256K1Field.add(x, ((SecP256K1FieldElement)b).x, z); return new SecP256K1FieldElement(z); } public ECFieldElement addOne() { - int[] z = SecP256K1Field.create(); + int[] z = Nat256.create(); SecP256K1Field.addOne(x, z); return new SecP256K1FieldElement(z); } public ECFieldElement subtract(ECFieldElement b) { - int[] z = SecP256K1Field.create(); + int[] z = Nat256.create(); SecP256K1Field.subtract(x, ((SecP256K1FieldElement)b).x, z); return new SecP256K1FieldElement(z); } public ECFieldElement multiply(ECFieldElement b) { - int[] z = SecP256K1Field.create(); + int[] z = Nat256.create(); SecP256K1Field.multiply(x, ((SecP256K1FieldElement)b).x, z); return new SecP256K1FieldElement(z); } @@ -93,21 +93,21 @@ public class SecP256K1FieldElement extends ECFieldElement public ECFieldElement divide(ECFieldElement b) { int[] y = SecP256K1Field.fromBigInteger(b.invert().toBigInteger()); - int[] z = SecP256K1Field.create(); + int[] z = Nat256.create(); SecP256K1Field.multiply(x, y, z); return new SecP256K1FieldElement(z); } public ECFieldElement negate() { - int[] z = SecP256K1Field.create(); + int[] z = Nat256.create(); SecP256K1Field.negate(x, z); return new SecP256K1FieldElement(z); } public ECFieldElement square() { - int[] z = SecP256K1Field.create(); + int[] z = Nat256.create(); SecP256K1Field.square(x, z); return new SecP256K1FieldElement(z); } diff --git a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java index 847a824d..8a3f55c7 100644 --- a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java +++ b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java @@ -50,7 +50,7 @@ public class SecP256K1Point extends ECPoint this.withCompression = withCompression; } - public SecP256K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, + SecP256K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) { super(curve, x, y, zs); @@ -206,7 +206,7 @@ public class SecP256K1Point extends ECPoint ECFieldElement X1Squared = X1.square(); ECFieldElement M = three(X1Squared); - ECFieldElement S = four(X1.multiply(Y1Squared)); + ECFieldElement S = four(Y1Squared.multiply(X1)); ECFieldElement X3 = M.square().subtract(two(S)); ECFieldElement Y3 = S.subtract(X3).multiply(M).subtract(eight(T)); diff --git a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Curve.java b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Curve.java new file mode 100644 index 00000000..04faa974 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Curve.java @@ -0,0 +1,121 @@ +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 SecP256R1Curve extends ECCurve +{ + public static final BigInteger q = new BigInteger(1, + Hex.decode("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF")); + public static final BigInteger r = ECConstants.ONE.shiftLeft(256).subtract(q); + + private static final int SecP256R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP256R1Point infinity; + + public SecP256R1Curve() + { + 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 decompressPoint(int yTilde, BigInteger X1) + { + ECFieldElement x = fromBigInteger(X1); + ECFieldElement alpha = x.square().add(a).multiply(x).add(b); + ECFieldElement beta = alpha.sqrt(); + + // + // if we can't find a sqrt we haven't got a point on the + // curve - run! + // + if (beta == null) + { + throw new RuntimeException("Invalid point compression"); + } + + if (beta.testBitZero() != (yTilde == 1)) + { + // Use the other root + beta = beta.negate(); + } + + return new SecP256R1Point(this, x, beta, true); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean equals(Object anObject) + { + if (anObject == this) + { + return true; + } + + if (!(anObject instanceof SecP256R1Curve)) + { + return false; + } + + SecP256R1Curve other = (SecP256R1Curve)anObject; + + return this.q.equals(other.q) && a.equals(other.a) && b.equals(other.b); + } + + public int hashCode() + { + return a.hashCode() ^ b.hashCode() ^ q.hashCode(); + } +} diff --git a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java new file mode 100644 index 00000000..5647e8f5 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java @@ -0,0 +1,123 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +public class SecP256R1Field +{ + // 2^256 - 2^224 + 2^192 + 2^96 - 1 + private static final int[] P = new int[] { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, + 0x00000001, 0xFFFFFFFF }; + private static final int P7 = 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))) + { + Nat256.sub(z, P, z); + } + } + + public static void addOne(int[] x, int[] z) + { + System.arraycopy(x, 0, z, 0, 8); + int c = Nat256.inc(z, 0); + if (c != 0 || (z[7] == P7 && Nat256.gte(z, P))) + { + Nat256.sub(z, P, z); + } + } + + public static int[] fromBigInteger(BigInteger x) + { + int[] z = Nat256.fromBigInteger(x); + if (z[7] == P7 && Nat256.gte(z, P)) + { + Nat256.sub(z, P, z); + } + return z; + } + + 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 negate(int[] x, int[] z) + { + if (Nat256.isZero(x)) + { + Nat256.zero(z); + } + else + { + Nat256.sub(P, x, z); + } + } + + private static void reduce(int[] tt, int[] z) + { + int c = 0; + + System.arraycopy(tt, 0, z, 0, 8); //s1 + + int[] s2 = new int[] { 0, 0, 0, tt[11], tt[12], tt[13], tt[14], tt[15] }; + c += Nat256.addBothTo(s2, s2, z); + int[] s3 = new int[] { 0, 0, 0, tt[12], tt[13], tt[14], tt[15], 0 }; + c += Nat256.addBothTo(s3, s3, z); + int[] s4 = new int[] { tt[8], tt[9], tt[10], 0, 0, 0, tt[14], tt[15] }; + int[] s5 = new int[] { tt[9], tt[10], tt[11], tt[13], tt[14], tt[15], tt[13], tt[8] }; + c += Nat256.addBothTo(s4, s5, z); + + int[] s6 = new int[] { tt[11], tt[12], tt[13], 0, 0, 0, tt[8], tt[10] }; + int[] s7 = new int[] { tt[12], tt[13], tt[14], tt[15], 0, 0, tt[9], tt[11] }; + c += Nat256.subBothFrom(s6, s7, z); + int[] s8 = new int[] { tt[13], tt[14], tt[15], tt[8], tt[9], tt[10], 0, tt[12] }; + int[] s9 = new int[] { tt[14], tt[15], 0, tt[9], tt[10], tt[11], 0, tt[13] }; + c += Nat256.subBothFrom(s8, s9, z); + + if (c > 0) + { + do + { + c += Nat256.sub(z, P, z); + } + while (c != 0); + } + else if (c < 0) + { + do + { + c += Nat256.add(z, P, z); + } + while (c != 0); + } + + assert c == 0; + + if (z[7] == P7 && Nat256.gte(z, P)) + { + Nat256.sub(z, P, z); + } + } + + public static void square(int[] x, int[] z) + { + int[] tt = Nat256.createExt(); + // NOTE: The simpler 'mul' performs better than 'square' + // Nat256.square(x, tt); + Nat256.mul(x, x, tt); + reduce(tt, z); + } + + public static void subtract(int[] x, int[] y, int[] z) + { + int c = Nat256.sub(x, y, z); + if (c != 0) + { + Nat256.add(z, P, z); + } + } +} diff --git a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java new file mode 100644 index 00000000..5c04f788 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java @@ -0,0 +1,151 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.util.Arrays; + +public class SecP256R1FieldElement extends ECFieldElement +{ + public static final BigInteger Q = SecP256R1Curve.q; + public static final BigInteger Qr = SecP256R1Curve.r; + + protected int[] x; + + public SecP256R1FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0) + { + throw new IllegalArgumentException("x value invalid in FEp256k1 field element"); + } + + this.x = SecP256R1Field.fromBigInteger(x); + } + + 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 BigInteger getQ() + { + return Q; + } + + 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) + { + int[] y = SecP256R1Field.fromBigInteger(b.invert().toBigInteger()); + int[] z = Nat256.create(); + SecP256R1Field.multiply(x, y, 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)); + } + + // 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() + { + ECFieldElement root = new ECFieldElement.Fp(Q, toBigInteger()).sqrt(); + return root == null ? null : new SecP256R1FieldElement(root.toBigInteger()); + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecP256R1FieldElement)) + { + return false; + } + + SecP256R1FieldElement o = (SecP256R1FieldElement)other; + return Arrays.areEqual(x, o.x); + } + + public int hashCode() + { + return Q.hashCode() ^ Arrays.hashCode(x); + } +} diff --git a/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java new file mode 100644 index 00000000..a9e5ab86 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java @@ -0,0 +1,321 @@ +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; + +public class SecP256R1Point extends ECPoint +{ + /** + * Create a point which encodes with point compression. + * + * @param curve + * the curve to use + * @param x + * affine x co-ordinate + * @param y + * affine y co-ordinate + * + * @deprecated Use ECCurve.createPoint to construct points + */ + public 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) || (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 boolean getCompressionYTilde() + { + return this.getAffineYCoord().testBitZero(); + } + + // B.3 pg 62 + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + if (this == b) + { + return twice(); + } + + ECFieldElement X1 = this.x, Y1 = this.y; + ECFieldElement X2 = b.getXCoord(), Y2 = b.getYCoord(); + + ECFieldElement Z1 = this.zs[0]; + ECFieldElement Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + + ECFieldElement X3, Y3, Z3; + + // if (!Z1IsOne && Z1.equals(Z2)) + // { + // // TODO Make this available as public method coZAdd? + // + // ECFieldElement dx = X1.subtract(X2), dy = Y1.subtract(Y2); + // if (dx.isZero()) + // { + // if (dy.isZero()) + // { + // return twice(); + // } + // return curve.getInfinity(); + // } + // + // ECFieldElement C = dx.square(); + // ECFieldElement W1 = X1.multiply(C), W2 = X2.multiply(C); + // ECFieldElement A1 = W1.subtract(W2).multiply(Y1); + // + // X3 = dy.square().subtract(W1).subtract(W2); + // Y3 = W1.subtract(X3).multiply(dy).subtract(A1); + // Z3 = dx; + // + // if (Z1IsOne) + // { + // Z3Squared = C; + // } + // else + // { + // Z3 = Z3.multiply(Z1); + // } + // } + // else + { + ECFieldElement Z1Squared, U2, S2; + if (Z1IsOne) + { + Z1Squared = Z1; + U2 = X2; + S2 = Y2; + } + else + { + Z1Squared = Z1.square(); + U2 = Z1Squared.multiply(X2); + ECFieldElement Z1Cubed = Z1Squared.multiply(Z1); + S2 = Z1Cubed.multiply(Y2); + } + + boolean Z2IsOne = Z2.isOne(); + ECFieldElement Z2Squared, U1, S1; + if (Z2IsOne) + { + Z2Squared = Z2; + U1 = X1; + S1 = Y1; + } + else + { + Z2Squared = Z2.square(); + U1 = Z2Squared.multiply(X1); + ECFieldElement Z2Cubed = Z2Squared.multiply(Z2); + S1 = Z2Cubed.multiply(Y1); + } + + ECFieldElement H = U1.subtract(U2); + ECFieldElement R = S1.subtract(S2); + + // Check if b == this or b == -this + if (H.isZero()) + { + if (R.isZero()) + { + // this == b, i.e. this must be doubled + return this.twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.getInfinity(); + } + + ECFieldElement HSquared = H.square(); + ECFieldElement G = HSquared.multiply(H); + ECFieldElement V = HSquared.multiply(U1); + + X3 = R.square().add(G).subtract(two(V)); + Y3 = V.subtract(X3).multiply(R).subtract(S1.multiply(G)); + + Z3 = H; + if (!Z1IsOne) + { + Z3 = Z3.multiply(Z1); + } + if (!Z2IsOne) + { + Z3 = Z3.multiply(Z2); + } + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP256R1Point(curve, X3, Y3, zs, this.withCompression); + } + + // B.3 pg 62 + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement Y1 = this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + ECFieldElement X1 = this.x, Z1 = this.zs[0]; + + ECFieldElement Y1Squared = Y1.square(); + ECFieldElement T = Y1Squared.square(); + + boolean Z1IsOne = Z1.isOne(); + + ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement M = three(X1.add(Z1Squared).multiply(X1.subtract(Z1Squared))); + ECFieldElement S = four(Y1Squared.multiply(X1)); + + ECFieldElement X3 = M.square().subtract(two(S)); + ECFieldElement Y3 = S.subtract(X3).multiply(M).subtract(eight(T)); + + ECFieldElement Z3 = two(Y1); + if (!Z1IsOne) + { + Z3 = Z3.multiply(Z1); + } + + 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); + } + + protected ECFieldElement two(ECFieldElement x) + { + return x.add(x); + } + + protected ECFieldElement three(ECFieldElement x) + { + return two(x).add(x); + } + + protected ECFieldElement four(ECFieldElement x) + { + return two(two(x)); + } + + protected ECFieldElement eight(ECFieldElement x) + { + return four(two(x)); + } + + protected ECFieldElement doubleProductFromSquares(ECFieldElement a, ECFieldElement b, ECFieldElement aSquared, + ECFieldElement bSquared) + { + /* + * NOTE: If squaring in the field is faster than multiplication, then this is a quicker way + * to calculate 2.A.B, if A^2 and B^2 are already known. + */ + return a.add(b).square().subtract(aSquared).subtract(bSquared); + } + + // D.3.2 pg 102 (see Note:) + public ECPoint subtract(ECPoint b) + { + if (b.isInfinity()) + { + return this; + } + + // Add -b + return add(b.negate()); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP256R1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/core/src/test/java/org/bouncycastle/math/ec/test/ECPointPerformanceTest.java b/core/src/test/java/org/bouncycastle/math/ec/test/ECPointPerformanceTest.java index 7e8a7f29..95acfb60 100644 --- a/core/src/test/java/org/bouncycastle/math/ec/test/ECPointPerformanceTest.java +++ b/core/src/test/java/org/bouncycastle/math/ec/test/ECPointPerformanceTest.java @@ -9,10 +9,11 @@ import org.bouncycastle.asn1.x9.ECNamedCurveTable; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.custom.CustomNamedCurves; /** - * Compares the performance of the the window NAF point multiplication against - * conventional point multiplication. + * Compares the performance of the the window NAF point multiplication against conventional point + * multiplication. */ public class ECPointPerformanceTest extends TestCase { @@ -22,17 +23,31 @@ public class ECPointPerformanceTest extends TestCase private static String[] COORD_NAMES = new String[]{ "AFFINE", "HOMOGENEOUS", "JACOBIAN", "JACOBIAN-CHUDNOVSKY", "JACOBIAN-MODIFIED", "LAMBDA-AFFINE", "LAMBDA-PROJECTIVE", "SKEWED" }; - private void randMult(final String curveName) throws Exception + private void randMult(String curveName) throws Exception { X9ECParameters spec = ECNamedCurveTable.getByName(curveName); + if (spec != null) + { + randMult(curveName, spec); + } + + spec = CustomNamedCurves.getByName(curveName); + if (spec != null) + { + randMult(curveName + " (custom)", spec); + } + } + + private void randMult(String label, X9ECParameters spec) throws Exception + { ECCurve C = spec.getCurve(); - ECPoint G = (ECPoint) spec.getG(); + ECPoint G = (ECPoint)spec.getG(); BigInteger n = spec.getN(); SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN"); random.setSeed(System.currentTimeMillis()); - System.out.println(curveName); + System.out.println(label); int[] coords = ECCurve.getAllCoordinateSystems(); for (int i = 0; i < coords.length; ++i) @@ -100,18 +115,17 @@ public class ECPointPerformanceTest extends TestCase } long endTime = System.currentTimeMillis(); - return (double) (endTime - startTime) / NUM_ROUNDS; + return (double)(endTime - startTime) / NUM_ROUNDS; } public void testMultiply() throws Exception { -// Enumeration e = SECNamedCurves.getNames(); -// while (e.hasMoreElements()) -// { -// String name = (String)e.nextElement(); -// randMult(name); -// } - + // Enumeration e = SECNamedCurves.getNames(); + // while (e.hasMoreElements()) + // { + // String name = (String)e.nextElement(); + // randMult(name); + // } randMult("sect113r1"); randMult("sect113r2"); @@ -147,7 +161,7 @@ public class ECPointPerformanceTest extends TestCase randMult("secp256r1"); randMult("secp384r1"); randMult("secp521r1"); - + randMult("brainpoolp160r1"); randMult("brainpoolp160t1"); randMult("brainpoolp192r1"); @@ -162,6 +176,30 @@ public class ECPointPerformanceTest extends TestCase randMult("brainpoolp384t1"); randMult("brainpoolp512r1"); randMult("brainpoolp512t1"); + + randMult("prime192v1"); + randMult("prime192v2"); + randMult("prime192v3"); + randMult("prime239v1"); + randMult("prime239v2"); + randMult("prime239v3"); + randMult("prime256v1"); + randMult("c2pnb163v1"); + randMult("c2pnb163v2"); + randMult("c2pnb163v3"); + randMult("c2pnb176w1"); + randMult("c2tnb191v1"); + randMult("c2tnb191v2"); + randMult("c2tnb191v3"); + randMult("c2pnb208w1"); + randMult("c2tnb239v1"); + randMult("c2tnb239v2"); + randMult("c2tnb239v3"); + randMult("c2pnb272w1"); + randMult("c2pnb304w1"); + randMult("c2tnb359v1"); + randMult("c2pnb368w1"); + randMult("c2tnb431r1"); } // public static void main(String argv[]) throws Exception diff --git a/core/src/test/java/org/bouncycastle/math/ec/test/ECPointTest.java b/core/src/test/java/org/bouncycastle/math/ec/test/ECPointTest.java index 51244ad3..525bd1c3 100644 --- a/core/src/test/java/org/bouncycastle/math/ec/test/ECPointTest.java +++ b/core/src/test/java/org/bouncycastle/math/ec/test/ECPointTest.java @@ -12,6 +12,7 @@ import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.custom.CustomNamedCurves; /** * Test class for {@link org.bouncycastle.math.ec.ECPoint ECPoint}. All @@ -413,6 +414,24 @@ public class ECPointTest extends TestCase assertPointsEqual("Error decoding compressed point", p, decComp); } + private void implAddSubtractMultiplyTwiceEncodingTest(X9ECParameters x9ECParameters) + { + BigInteger n = x9ECParameters.getN(); + + // The generator is multiplied by random b to get random q + BigInteger b = new BigInteger(n.bitLength(), secRand); + ECPoint g = x9ECParameters.getG(); + ECPoint q = g.multiply(b).normalize(); + + // Get point at infinity on the curve + ECPoint infinity = x9ECParameters.getCurve().getInfinity(); + + implTestAddSubtract(q, infinity); + implTestMultiply(q, n.bitLength()); + implTestMultiply(infinity, n.bitLength()); + implTestEncoding(q); + } + /** * Calls <code>implTestAddSubtract()</code>, * <code>implTestMultiply</code> and <code>implTestEncoding</code> for @@ -424,22 +443,15 @@ public class ECPointTest extends TestCase while (curveEnum.hasMoreElements()) { String name = (String) curveEnum.nextElement(); - X9ECParameters x9ECParameters = SECNamedCurves.getByName(name); - - BigInteger n = x9ECParameters.getN(); - - // The generator is multiplied by random b to get random q - BigInteger b = new BigInteger(n.bitLength(), secRand); - ECPoint g = x9ECParameters.getG(); - ECPoint q = g.multiply(b).normalize(); - // Get point at infinity on the curve - ECPoint infinity = x9ECParameters.getCurve().getInfinity(); + X9ECParameters x9ECParameters = SECNamedCurves.getByName(name); + implAddSubtractMultiplyTwiceEncodingTest(x9ECParameters); - implTestAddSubtract(q, infinity); - implTestMultiply(q, n.bitLength()); - implTestMultiply(infinity, n.bitLength()); - implTestEncoding(q); + x9ECParameters = CustomNamedCurves.getByName(name); + if (x9ECParameters != null) + { + implAddSubtractMultiplyTwiceEncodingTest(x9ECParameters); + } } } |