Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/org/spongycastle/math')
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/AbstractECMultiplier.java26
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/DoubleAddMultiplier.java24
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ECAlgorithms.java469
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ECConstants.java12
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ECCurve.java1113
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ECFieldElement.java868
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ECMultiplier.java19
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ECPoint.java2122
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ECPointMap.java6
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/FixedPointCombMultiplier.java57
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/FixedPointPreCompInfo.java40
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/FixedPointUtil.java71
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/GLVMultiplier.java42
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/LongArray.java2195
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/MixedNafR2LMultiplier.java77
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/MontgomeryLadderMultiplier.java25
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/NafL2RMultiplier.java30
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/NafR2LMultiplier.java31
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/PreCompInfo.java10
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ReferenceMultiplier.java11
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ScaleXPointMap.java16
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ScaleYPointMap.java16
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/SimpleBigDecimal.java247
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/Tnaf.java823
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/WNafL2RMultiplier.java96
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/WNafPreCompInfo.java56
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/WNafUtil.java485
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/WTauNafMultiplier.java114
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/WTauNafPreCompInfo.java26
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ZSignedDigitL2RMultiplier.java29
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ZSignedDigitR2LMultiplier.java30
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/ZTauElement.java37
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519.java80
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519Field.java254
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519FieldElement.java234
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519Point.java348
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1Curve.java79
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1Field.java177
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1FieldElement.java213
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1Point.java298
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1Curve.java80
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1Field.java286
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1FieldElement.java190
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1Point.java310
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1Curve.java78
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1Field.java178
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1FieldElement.java243
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1Point.java298
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1Curve.java80
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1Field.java298
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1FieldElement.java273
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1Point.java308
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1Curve.java78
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1Field.java179
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1FieldElement.java215
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1Point.java298
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1Curve.java80
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1Field.java312
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1FieldElement.java189
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1Point.java308
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1Curve.java80
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1Field.java295
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1FieldElement.java211
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1Point.java309
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1Curve.java80
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1Field.java156
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1FieldElement.java169
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1Point.java333
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/endo/ECEndomorphism.java10
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/endo/GLVEndomorphism.java8
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/endo/GLVTypeBEndomorphism.java58
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/endo/GLVTypeBParameters.java59
-rw-r--r--core/src/main/java/org/spongycastle/math/ec/tools/DiscoverEndomorphisms.java373
-rw-r--r--core/src/main/java/org/spongycastle/math/field/ExtensionField.java8
-rw-r--r--core/src/main/java/org/spongycastle/math/field/FiniteField.java10
-rw-r--r--core/src/main/java/org/spongycastle/math/field/FiniteFields.java53
-rw-r--r--core/src/main/java/org/spongycastle/math/field/GF2Polynomial.java42
-rw-r--r--core/src/main/java/org/spongycastle/math/field/GenericPolynomialExtensionField.java62
-rw-r--r--core/src/main/java/org/spongycastle/math/field/Polynomial.java12
-rw-r--r--core/src/main/java/org/spongycastle/math/field/PolynomialExtensionField.java6
-rw-r--r--core/src/main/java/org/spongycastle/math/field/PrimeField.java42
-rw-r--r--core/src/main/java/org/spongycastle/math/raw/Mod.java199
-rw-r--r--core/src/main/java/org/spongycastle/math/raw/Mont256.java152
-rw-r--r--core/src/main/java/org/spongycastle/math/raw/Nat.java1038
-rw-r--r--core/src/main/java/org/spongycastle/math/raw/Nat192.java968
-rw-r--r--core/src/main/java/org/spongycastle/math/raw/Nat224.java1182
-rw-r--r--core/src/main/java/org/spongycastle/math/raw/Nat256.java1306
-rw-r--r--core/src/main/java/org/spongycastle/math/raw/Nat384.java43
-rw-r--r--core/src/main/java/org/spongycastle/math/raw/Nat512.java43
89 files changed, 22494 insertions, 0 deletions
diff --git a/core/src/main/java/org/spongycastle/math/ec/AbstractECMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/AbstractECMultiplier.java
new file mode 100644
index 00000000..e4dee6dd
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/AbstractECMultiplier.java
@@ -0,0 +1,26 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public abstract class AbstractECMultiplier implements ECMultiplier
+{
+ public ECPoint multiply(ECPoint p, BigInteger k)
+ {
+ int sign = k.signum();
+ if (sign == 0 || p.isInfinity())
+ {
+ return p.getCurve().getInfinity();
+ }
+
+ ECPoint positive = multiplyPositive(p, k.abs());
+ 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/core/src/main/java/org/spongycastle/math/ec/DoubleAddMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/DoubleAddMultiplier.java
new file mode 100644
index 00000000..cc98a3a2
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/DoubleAddMultiplier.java
@@ -0,0 +1,24 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class DoubleAddMultiplier extends AbstractECMultiplier
+{
+ /**
+ * Joye's double-add algorithm.
+ */
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ ECPoint[] R = new ECPoint[]{ p.getCurve().getInfinity(), p };
+
+ int n = k.bitLength();
+ for (int i = 0; i < n; ++i)
+ {
+ int b = k.testBit(i) ? 1 : 0;
+ int bp = 1 - b;
+ R[bp] = R[bp].twicePlus(R[b]);
+ }
+
+ return R[0];
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/ECAlgorithms.java b/core/src/main/java/org/spongycastle/math/ec/ECAlgorithms.java
new file mode 100644
index 00000000..7d73cebe
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ECAlgorithms.java
@@ -0,0 +1,469 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.endo.ECEndomorphism;
+import org.spongycastle.math.ec.endo.GLVEndomorphism;
+import org.spongycastle.math.field.FiniteField;
+import org.spongycastle.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)
+ {
+ ECCurve cp = P.getCurve();
+ Q = importPoint(cp, Q);
+
+ // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick
+ if (cp instanceof ECCurve.F2m)
+ {
+ ECCurve.F2m f2mCurve = (ECCurve.F2m)cp;
+ if (f2mCurve.isKoblitz())
+ {
+ return validatePoint(P.multiply(a).add(Q.multiply(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));
+ }
+
+ /*
+ * "Shamir's Trick", originally due to E. G. Straus
+ * (Addition chains of vectors. American Mathematical Monthly,
+ * 71(7):806-808, Aug./Sept. 1964)
+ * <pre>
+ * Input: The points P, Q, scalar k = (km?, ... , k1, k0)
+ * and scalar l = (lm?, ... , l1, l0).
+ * Output: R = k * P + l * Q.
+ * 1: Z <- P + Q
+ * 2: R <- O
+ * 3: for i from m-1 down to 0 do
+ * 4: R <- R + R {point doubling}
+ * 5: if (ki = 1) and (li = 0) then R <- R + P end if
+ * 6: if (ki = 0) and (li = 1) then R <- R + Q end if
+ * 7: if (ki = 1) and (li = 1) then R <- R + Z end if
+ * 8: end for
+ * 9: return R
+ * </pre>
+ */
+ public static ECPoint shamirsTrick(ECPoint P, BigInteger k,
+ ECPoint Q, BigInteger l)
+ {
+ ECCurve cp = P.getCurve();
+ Q = importPoint(cp, Q);
+
+ return validatePoint(implShamirsTrickJsf(P, k, Q, l));
+ }
+
+ public static ECPoint importPoint(ECCurve c, ECPoint p)
+ {
+ ECCurve cp = p.getCurve();
+ if (!c.equals(cp))
+ {
+ throw new IllegalArgumentException("Point must be on the same curve");
+ }
+ return c.importPoint(p);
+ }
+
+ public static void montgomeryTrick(ECFieldElement[] zs, int off, int len)
+ {
+ /*
+ * Uses the "Montgomery Trick" to invert many field elements, with only a single actual
+ * field inversion. See e.g. the paper:
+ * "Fast Multi-scalar Multiplication Methods on Elliptic Curves with Precomputation Strategy Using Montgomery Trick"
+ * by Katsuyuki Okeya, Kouichi Sakurai.
+ */
+
+ ECFieldElement[] c = new ECFieldElement[len];
+ c[0] = zs[off];
+
+ int i = 0;
+ while (++i < len)
+ {
+ c[i] = c[i - 1].multiply(zs[off + i]);
+ }
+
+ ECFieldElement u = c[--i].invert();
+
+ while (i > 0)
+ {
+ int j = off + i--;
+ ECFieldElement tmp = zs[j];
+ zs[j] = c[i].multiply(u);
+ u = u.multiply(tmp);
+ }
+
+ zs[off] = u;
+ }
+
+ /**
+ * 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();
+ ECPoint infinity = curve.getInfinity();
+
+ // TODO conjugate co-Z addition (ZADDC) can return both of these
+ ECPoint PaddQ = P.add(Q);
+ ECPoint PsubQ = P.subtract(Q);
+
+ ECPoint[] points = new ECPoint[]{ Q, PsubQ, P, PaddQ };
+ curve.normalizeAll(points);
+
+ ECPoint[] table = new ECPoint[] {
+ points[3].negate(), points[2].negate(), points[1].negate(),
+ points[0].negate(), infinity, points[0],
+ points[1], points[2], points[3] };
+
+ byte[] jsf = WNafUtil.generateJSF(k, l);
+
+ ECPoint R = infinity;
+
+ int i = jsf.length;
+ while (--i >= 0)
+ {
+ int jsfi = jsf[i];
+
+ // 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]);
+ }
+
+ 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/core/src/main/java/org/spongycastle/math/ec/ECConstants.java b/core/src/main/java/org/spongycastle/math/ec/ECConstants.java
new file mode 100644
index 00000000..c2b2a09a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ECConstants.java
@@ -0,0 +1,12 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public interface ECConstants
+{
+ public static final BigInteger ZERO = BigInteger.valueOf(0);
+ public static final BigInteger ONE = BigInteger.valueOf(1);
+ public static final BigInteger TWO = BigInteger.valueOf(2);
+ public static final BigInteger THREE = BigInteger.valueOf(3);
+ public static final BigInteger FOUR = BigInteger.valueOf(4);
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/ECCurve.java b/core/src/main/java/org/spongycastle/math/ec/ECCurve.java
new file mode 100644
index 00000000..35c35473
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ECCurve.java
@@ -0,0 +1,1113 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+import java.util.Hashtable;
+import java.util.Random;
+
+import org.spongycastle.math.ec.endo.ECEndomorphism;
+import org.spongycastle.math.ec.endo.GLVEndomorphism;
+import org.spongycastle.math.field.FiniteField;
+import org.spongycastle.math.field.FiniteFields;
+import org.spongycastle.util.BigIntegers;
+import org.spongycastle.util.Integers;
+
+/**
+ * base class for an elliptic curve
+ */
+public abstract class ECCurve
+{
+ public static final int COORD_AFFINE = 0;
+ public static final int COORD_HOMOGENEOUS = 1;
+ public static final int COORD_JACOBIAN = 2;
+ public static final int COORD_JACOBIAN_CHUDNOVSKY = 3;
+ public static final int COORD_JACOBIAN_MODIFIED = 4;
+ public static final int COORD_LAMBDA_AFFINE = 5;
+ public static final int COORD_LAMBDA_PROJECTIVE = 6;
+ public static final int COORD_SKEWED = 7;
+
+ public static int[] getAllCoordinateSystems()
+ {
+ return new int[]{ COORD_AFFINE, COORD_HOMOGENEOUS, COORD_JACOBIAN, COORD_JACOBIAN_CHUDNOVSKY,
+ COORD_JACOBIAN_MODIFIED, COORD_LAMBDA_AFFINE, COORD_LAMBDA_PROJECTIVE, COORD_SKEWED };
+ }
+
+ public class Config
+ {
+ protected int coord;
+ protected ECEndomorphism endomorphism;
+ protected ECMultiplier multiplier;
+
+ Config(int coord, ECEndomorphism endomorphism, ECMultiplier multiplier)
+ {
+ this.coord = coord;
+ this.endomorphism = endomorphism;
+ this.multiplier = multiplier;
+ }
+
+ public Config setCoordinateSystem(int coord)
+ {
+ this.coord = coord;
+ return this;
+ }
+
+ public Config setEndomorphism(ECEndomorphism endomorphism)
+ {
+ this.endomorphism = endomorphism;
+ return this;
+ }
+
+ public Config setMultiplier(ECMultiplier multiplier)
+ {
+ this.multiplier = multiplier;
+ return this;
+ }
+
+ public ECCurve create()
+ {
+ if (!supportsCoordinateSystem(coord))
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+
+ ECCurve c = cloneCurve();
+ if (c == ECCurve.this)
+ {
+ throw new IllegalStateException("implementation returned current curve");
+ }
+
+ 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.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)
+ {
+ return createPoint(x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, use {@link #createPoint(BigInteger, BigInteger)}
+ * and refer {@link ECPoint#getEncoded(boolean)}
+ */
+ public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression)
+ {
+ return createRawPoint(fromBigInteger(x), fromBigInteger(y), withCompression);
+ }
+
+ protected abstract ECCurve cloneCurve();
+
+ 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();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ return coord == COORD_AFFINE;
+ }
+
+ public PreCompInfo getPreCompInfo(ECPoint point, String name)
+ {
+ checkPoint(point);
+ synchronized (point)
+ {
+ Hashtable table = point.preCompTable;
+ return table == null ? null : (PreCompInfo)table.get(name);
+ }
+ }
+
+ /**
+ * 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, String name, PreCompInfo preCompInfo)
+ {
+ checkPoint(point);
+ synchronized (point)
+ {
+ Hashtable table = point.preCompTable;
+ if (null == table)
+ {
+ point.preCompTable = table = new Hashtable(4);
+ }
+ table.put(name, preCompInfo);
+ }
+ }
+
+ public ECPoint importPoint(ECPoint p)
+ {
+ if (this == p.getCurve())
+ {
+ return p;
+ }
+ if (p.isInfinity())
+ {
+ return getInfinity();
+ }
+
+ // TODO Default behaviour could be improved if the two curves have the same coordinate system by copying any Z coordinates.
+ p = p.normalize();
+
+ return validatePoint(p.getXCoord().toBigInteger(), p.getYCoord().toBigInteger(), p.withCompression);
+ }
+
+ /**
+ * Normalization ensures that any projective coordinate is 1, and therefore that the x, y
+ * coordinates reflect those of the equivalent point in an affine coordinate system. Where more
+ * than one point is to be normalized, this method will generally be more efficient than
+ * normalizing each point separately.
+ *
+ * @param points
+ * An array of points that will be updated in place with their normalized versions,
+ * where necessary
+ */
+ public void normalizeAll(ECPoint[] points)
+ {
+ checkPoints(points);
+
+ if (this.getCoordinateSystem() == ECCurve.COORD_AFFINE)
+ {
+ return;
+ }
+
+ /*
+ * Figure out which of the points actually need to be normalized
+ */
+ ECFieldElement[] zs = new ECFieldElement[points.length];
+ int[] indices = new int[points.length];
+ int count = 0;
+ for (int i = 0; i < points.length; ++i)
+ {
+ ECPoint p = points[i];
+ if (null != p && !p.isNormalized())
+ {
+ zs[count] = p.getZCoord(0);
+ indices[count++] = i;
+ }
+ }
+
+ if (count == 0)
+ {
+ return;
+ }
+
+ ECAlgorithms.montgomeryTrick(zs, 0, count);
+
+ for (int j = 0; j < count; ++j)
+ {
+ int index = indices[j];
+ points[index] = points[index].normalize(zs[j]);
+ }
+ }
+
+ public abstract ECPoint getInfinity();
+
+ public FiniteField getField()
+ {
+ return field;
+ }
+
+ public ECFieldElement getA()
+ {
+ return a;
+ }
+
+ public ECFieldElement getB()
+ {
+ return b;
+ }
+
+ public BigInteger getOrder()
+ {
+ return order;
+ }
+
+ public BigInteger getCofactor()
+ {
+ return cofactor;
+ }
+
+ public int getCoordinateSystem()
+ {
+ return coord;
+ }
+
+ protected abstract ECPoint decompressPoint(int yTilde, BigInteger X1);
+
+ public ECEndomorphism getEndomorphism()
+ {
+ return endomorphism;
+ }
+
+ /**
+ * Sets the default <code>ECMultiplier</code>, unless already set.
+ */
+ public synchronized ECMultiplier getMultiplier()
+ {
+ if (this.multiplier == null)
+ {
+ this.multiplier = createDefaultMultiplier();
+ }
+ return this.multiplier;
+ }
+
+ /**
+ * Decode a point on this curve from its ASN.1 encoding. The different
+ * encodings are taken account of, including point compression for
+ * <code>F<sub>p</sub></code> (X9.62 s 4.2.1 pg 17).
+ * @return The decoded point.
+ */
+ public ECPoint decodePoint(byte[] encoded)
+ {
+ ECPoint p = null;
+ int expectedLength = (getFieldSize() + 7) / 8;
+
+ byte type = encoded[0];
+ switch (type)
+ {
+ case 0x00: // infinity
+ {
+ if (encoded.length != 1)
+ {
+ throw new IllegalArgumentException("Incorrect length for infinity encoding");
+ }
+
+ p = getInfinity();
+ break;
+ }
+ case 0x02: // compressed
+ case 0x03: // compressed
+ {
+ if (encoded.length != (expectedLength + 1))
+ {
+ throw new IllegalArgumentException("Incorrect length for compressed encoding");
+ }
+
+ 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 hybrid encoding");
+ }
+
+ BigInteger X = BigIntegers.fromUnsignedByteArray(encoded, 1, expectedLength);
+ BigInteger Y = BigIntegers.fromUnsignedByteArray(encoded, 1 + expectedLength, expectedLength);
+
+ 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(type, 16));
+ }
+
+ if (type != 0x00 && p.isInfinity())
+ {
+ throw new IllegalArgumentException("Invalid infinity encoding");
+ }
+
+ return p;
+ }
+
+ protected void checkPoint(ECPoint point)
+ {
+ if (null == point || (this != point.getCurve()))
+ {
+ throw new IllegalArgumentException("'point' must be non-null and on this curve");
+ }
+ }
+
+ protected void checkPoints(ECPoint[] points)
+ {
+ if (points == null)
+ {
+ throw new IllegalArgumentException("'points' cannot be null");
+ }
+
+ for (int i = 0; i < points.length; ++i)
+ {
+ ECPoint point = points[i];
+ if (null != point && this != point.getCurve())
+ {
+ throw new IllegalArgumentException("'points' entries must be null or on this curve");
+ }
+ }
+ }
+
+ 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 AbstractFp
+ {
+ private static final int FP_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED;
+
+ BigInteger q, r;
+ ECPoint.Fp infinity;
+
+ 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, order, cofactor);
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_AFFINE:
+ case COORD_HOMOGENEOUS:
+ case COORD_JACOBIAN:
+ case COORD_JACOBIAN_MODIFIED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public int getFieldSize()
+ {
+ return q.bitLength();
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new ECFieldElement.Fp(this.q, this.r, x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ 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())
+ {
+ switch (p.getCurve().getCoordinateSystem())
+ {
+ case COORD_JACOBIAN:
+ case COORD_JACOBIAN_CHUDNOVSKY:
+ case COORD_JACOBIAN_MODIFIED:
+ return new ECPoint.Fp(this,
+ fromBigInteger(p.x.toBigInteger()),
+ fromBigInteger(p.y.toBigInteger()),
+ new ECFieldElement[]{ fromBigInteger(p.zs[0].toBigInteger()) },
+ p.withCompression);
+ default:
+ break;
+ }
+ }
+
+ return super.importPoint(p);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+ }
+
+ public static abstract class AbstractF2m extends ECCurve
+ {
+ private static FiniteField buildField(int m, int k1, int k2, int k3)
+ {
+ 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");
+ }
+
+ return FiniteFields.getBinaryExtensionField(new int[]{ 0, k1, m });
+ }
+
+ if (k2 <= k1)
+ {
+ throw new IllegalArgumentException("k2 must be > k1");
+ }
+
+ if (k3 <= k2)
+ {
+ throw new IllegalArgumentException("k3 must be > k2");
+ }
+
+ return FiniteFields.getBinaryExtensionField(new int[]{ 0, k1, k2, k3, m });
+ }
+
+ protected AbstractF2m(int m, int k1, int k2, int k3)
+ {
+ super(buildField(m, k1, k2, k3));
+ }
+ }
+
+ /**
+ * 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 AbstractF2m
+ {
+ 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>.
+ */
+ private int m; // can't be final - JDK 1.1
+
+ /**
+ * 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; // can't be final - JDK 1.1
+
+ /**
+ * 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; // can't be final - JDK 1.1
+
+ /**
+ * 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; // can't be final - JDK 1.1
+
+ /**
+ * The point at infinity on this curve.
+ */
+ private ECPoint.F2m infinity; // can't be final - JDK 1.1
+
+ /**
+ * The parameter <code>&mu;</code> of the elliptic curve if this is
+ * a Koblitz curve.
+ */
+ private byte mu = 0;
+
+ /**
+ * The auxiliary values <code>s<sub>0</sub></code> and
+ * <code>s<sub>1</sub></code> used for partial modular reduction for
+ * Koblitz curves.
+ */
+ private BigInteger[] si = null;
+
+ /**
+ * Constructor for Trinomial Polynomial Basis (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 a The coefficient <code>a</code> in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @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>.
+ */
+ public F2m(
+ int m,
+ int k,
+ BigInteger a,
+ BigInteger b)
+ {
+ this(m, k, 0, 0, a, b, null, null);
+ }
+
+ /**
+ * Constructor for Trinomial Polynomial Basis (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 a The coefficient <code>a</code> in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @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 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(
+ int m,
+ int k,
+ BigInteger a,
+ BigInteger b,
+ BigInteger order,
+ BigInteger cofactor)
+ {
+ this(m, k, 0, 0, a, b, order, cofactor);
+ }
+
+ /**
+ * Constructor for Pentanomial Polynomial Basis (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 a The coefficient <code>a</code> in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @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>.
+ */
+ public F2m(
+ int m,
+ int k1,
+ int k2,
+ int k3,
+ BigInteger a,
+ BigInteger b)
+ {
+ this(m, k1, k2, k3, a, b, null, null);
+ }
+
+ /**
+ * Constructor for Pentanomial Polynomial Basis (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 a The coefficient <code>a</code> in the Weierstrass equation
+ * for non-supersingular elliptic curves over
+ * <code>F<sub>2<sup>m</sup></sub></code>.
+ * @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 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(
+ int m,
+ int k1,
+ int k2,
+ int k3,
+ BigInteger a,
+ BigInteger b,
+ BigInteger order,
+ BigInteger cofactor)
+ {
+ super(m, k1, k2, k3);
+
+ this.m = m;
+ this.k1 = k1;
+ this.k2 = k2;
+ this.k3 = k3;
+ this.order = order;
+ this.cofactor = cofactor;
+
+ this.infinity = new ECPoint.F2m(this, null, null);
+ this.a = fromBigInteger(a);
+ this.b = fromBigInteger(b);
+ this.coord = F2M_DEFAULT_COORDS;
+ }
+
+ 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.order = order;
+ this.cofactor = cofactor;
+
+ this.infinity = new ECPoint.F2m(this, null, null);
+ this.a = a;
+ this.b = b;
+ this.coord = F2M_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new F2m(m, k1, k2, k3, a, b, order, cofactor);
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_AFFINE:
+ case COORD_HOMOGENEOUS:
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected ECMultiplier createDefaultMultiplier()
+ {
+ if (isKoblitz())
+ {
+ return new WTauNafMultiplier();
+ }
+
+ return super.createDefaultMultiplier();
+ }
+
+ public int getFieldSize()
+ {
+ return m;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new ECFieldElement.F2m(this.m, this.k1, this.k2, this.k3, x);
+ }
+
+ public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression)
+ {
+ ECFieldElement X = fromBigInteger(x), Y = fromBigInteger(y);
+
+ switch (this.getCoordinateSystem())
+ {
+ case COORD_LAMBDA_AFFINE:
+ case COORD_LAMBDA_PROJECTIVE:
+ {
+ 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);
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return createRawPoint(X, Y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ 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;
+ }
+
+ /**
+ * Returns true if this is a Koblitz curve (ABC curve).
+ * @return true if this is a Koblitz curve (ABC curve), false otherwise
+ */
+ public boolean isKoblitz()
+ {
+ return order != null && cofactor != null && b.isOne() && (a.isZero() || a.isOne());
+ }
+
+ /**
+ * Returns the parameter <code>&mu;</code> of the elliptic curve.
+ * @return <code>&mu;</code> of the elliptic curve.
+ * @throws IllegalArgumentException if the given ECCurve is not a
+ * Koblitz curve.
+ */
+ synchronized byte getMu()
+ {
+ if (mu == 0)
+ {
+ mu = Tnaf.getMu(this);
+ }
+ return mu;
+ }
+
+ /**
+ * @return the auxiliary values <code>s<sub>0</sub></code> and
+ * <code>s<sub>1</sub></code> used for partial modular reduction for
+ * Koblitz curves.
+ */
+ synchronized BigInteger[] getSi()
+ {
+ if (si == null)
+ {
+ si = Tnaf.getSi(this);
+ }
+ return si;
+ }
+
+ /**
+ * Decompresses a compressed point P = (xp, yp) (X9.62 s 4.2.2).
+ *
+ * @param yTilde
+ * ~yp, an indication bit for the decompression of yp.
+ * @param X1
+ * The field element xp.
+ * @return the decompressed point.
+ */
+ protected ECPoint decompressPoint(int yTilde, BigInteger X1)
+ {
+ ECFieldElement x = fromBigInteger(X1), y = null;
+ if (x.isZero())
+ {
+ y = b.sqrt();
+ }
+ else
+ {
+ ECFieldElement beta = x.square().invert().multiply(b).add(a).add(x);
+ ECFieldElement z = solveQuadraticEquation(beta);
+ if (z != null)
+ {
+ 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;
+ }
+ }
+ }
+ }
+
+ if (y == null)
+ {
+ throw new IllegalArgumentException("Invalid point compression");
+ }
+
+ 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>.
+ *
+ * @param beta
+ * The value to solve the quadratic equation for.
+ * @return the solution for <code>z<sup>2</sup> + z = beta</code> or
+ * <code>null</code> if no solution exists.
+ */
+ private ECFieldElement solveQuadraticEquation(ECFieldElement beta)
+ {
+ if (beta.isZero())
+ {
+ return beta;
+ }
+
+ ECFieldElement zeroElement = fromBigInteger(ECConstants.ZERO);
+
+ ECFieldElement z = null;
+ ECFieldElement gamma = null;
+
+ Random rand = new Random();
+ do
+ {
+ ECFieldElement t = fromBigInteger(new BigInteger(m, rand));
+ z = zeroElement;
+ ECFieldElement w = beta;
+ for (int i = 1; i <= m - 1; i++)
+ {
+ ECFieldElement w2 = w.square();
+ z = z.square().add(w2.multiply(t));
+ w = w2.add(beta);
+ }
+ if (!w.isZero())
+ {
+ return null;
+ }
+ gamma = z.square().add(z);
+ }
+ while (gamma.isZero());
+
+ return z;
+ }
+
+ public int getM()
+ {
+ return m;
+ }
+
+ /**
+ * Return true if curve uses a Trinomial basis.
+ *
+ * @return true if curve Trinomial, false otherwise.
+ */
+ public boolean isTrinomial()
+ {
+ return k2 == 0 && k3 == 0;
+ }
+
+ public int getK1()
+ {
+ return k1;
+ }
+
+ public int getK2()
+ {
+ return k2;
+ }
+
+ public int getK3()
+ {
+ return k3;
+ }
+
+ /**
+ * @deprecated use {@link #getOrder()} instead
+ */
+ public BigInteger getN()
+ {
+ return order;
+ }
+
+ /**
+ * @deprecated use {@link #getCofactor()} instead
+ */
+ public BigInteger getH()
+ {
+ return cofactor;
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/ECFieldElement.java b/core/src/main/java/org/spongycastle/math/ec/ECFieldElement.java
new file mode 100644
index 00000000..f96b8e61
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ECFieldElement.java
@@ -0,0 +1,868 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+import java.util.Random;
+
+import org.spongycastle.math.raw.Mod;
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.BigIntegers;
+
+public abstract class ECFieldElement
+ implements ECConstants
+{
+ public abstract BigInteger toBigInteger();
+ public abstract String getFieldName();
+ public abstract int getFieldSize();
+ public abstract ECFieldElement add(ECFieldElement b);
+ public abstract ECFieldElement addOne();
+ public abstract ECFieldElement subtract(ECFieldElement b);
+ public abstract ECFieldElement multiply(ECFieldElement b);
+ public abstract ECFieldElement divide(ECFieldElement b);
+ public abstract ECFieldElement negate();
+ public abstract ECFieldElement square();
+ public abstract ECFieldElement invert();
+ public abstract ECFieldElement sqrt();
+
+ public int bitLength()
+ {
+ 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);
+ }
+
+ public String toString()
+ {
+ return this.toBigInteger().toString(16);
+ }
+
+ public byte[] getEncoded()
+ {
+ return BigIntegers.asUnsignedByteArray((getFieldSize() + 7) / 8, toBigInteger());
+ }
+
+ public static class Fp extends ECFieldElement
+ {
+ BigInteger q, r, x;
+
+ static BigInteger calculateResidue(BigInteger p)
+ {
+ int bitLength = p.bitLength();
+ if (bitLength >= 96)
+ {
+ BigInteger firstWord = p.shiftRight(bitLength - 64);
+ if (firstWord.longValue() == -1L)
+ {
+ return ONE.shiftLeft(bitLength).subtract(p);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @deprecated Use ECCurve.fromBigInteger to construct field elements
+ */
+ public Fp(BigInteger q, BigInteger x)
+ {
+ this(q, calculateResidue(q), x);
+ }
+
+ Fp(BigInteger q, BigInteger r, BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.compareTo(q) >= 0)
+ {
+ throw new IllegalArgumentException("x value invalid in Fp field element");
+ }
+
+ this.q = q;
+ this.r = r;
+ this.x = x;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return x;
+ }
+
+ /**
+ * return the field name for this field.
+ *
+ * @return the string "Fp".
+ */
+ public String getFieldName()
+ {
+ return "Fp";
+ }
+
+ public int getFieldSize()
+ {
+ return q.bitLength();
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ return new Fp(q, r, modAdd(x, b.toBigInteger()));
+ }
+
+ public ECFieldElement addOne()
+ {
+ BigInteger x2 = x.add(ECConstants.ONE);
+ if (x2.compareTo(q) == 0)
+ {
+ x2 = ECConstants.ZERO;
+ }
+ return new Fp(q, r, x2);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ return new Fp(q, r, modSubtract(x, b.toBigInteger()));
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ 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, r, modMult(x, modInverse(b.toBigInteger())));
+ }
+
+ public ECFieldElement negate()
+ {
+ return x.signum() == 0 ? this : new Fp(q, r, q.subtract(x));
+ }
+
+ public ECFieldElement square()
+ {
+ 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, modInverse(x));
+ }
+
+ // 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()
+ {
+ if (this.isZero() || this.isOne()) // earlier JDK compatibility
+ {
+ return this;
+ }
+
+ if (!q.testBit(0))
+ {
+ throw new RuntimeException("not done yet");
+ }
+
+ // note: even though this class implements ECConstants don't be tempted to
+ // remove the explicit declaration, some J2ME environments don't cope.
+
+ 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
+ {
+ 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);
+
+ BigInteger y = modMult(t2, t4);
+
+ return checkSqrt(new Fp(q, r, y));
+ }
+
+ // q == 8m + 1
+
+ BigInteger legendreExponent = q.shiftRight(1);
+ if (!(x.modPow(legendreExponent, q).equals(ECConstants.ONE)))
+ {
+ return null;
+ }
+
+ BigInteger X = this.x;
+ BigInteger fourX = modDouble(modDouble(X));
+
+ BigInteger k = legendreExponent.add(ECConstants.ONE), qMinusOne = q.subtract(ECConstants.ONE);
+
+ BigInteger U, V;
+ Random rand = new Random();
+ do
+ {
+ BigInteger P;
+ do
+ {
+ P = new BigInteger(q.bitLength(), rand);
+ }
+ while (P.compareTo(q) >= 0
+ || !modReduce(P.multiply(P).subtract(fourX)).modPow(legendreExponent, q).equals(qMinusOne));
+
+ BigInteger[] result = lucasSequence(P, X, k);
+ U = result[0];
+ V = result[1];
+
+ if (modMult(V, V).equals(fourX))
+ {
+ return new ECFieldElement.Fp(q, r, modHalfAbs(V));
+ }
+ }
+ while (U.equals(ECConstants.ONE) || U.equals(qMinusOne));
+
+ return null;
+ }
+
+ 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;
+ BigInteger Ql = ECConstants.ONE;
+ BigInteger Qh = ECConstants.ONE;
+
+ for (int j = n - 1; j >= s + 1; --j)
+ {
+ Ql = modMult(Ql, Qh);
+
+ if (k.testBit(j))
+ {
+ Qh = modMult(Ql, Q);
+ Uh = modMult(Uh, Vh);
+ Vl = modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql)));
+ Vh = modReduce(Vh.multiply(Vh).subtract(Qh.shiftLeft(1)));
+ }
+ else
+ {
+ Qh = Ql;
+ Uh = modReduce(Uh.multiply(Vl).subtract(Ql));
+ Vh = modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql)));
+ Vl = modReduce(Vl.multiply(Vl).subtract(Ql.shiftLeft(1)));
+ }
+ }
+
+ Ql = modMult(Ql, Qh);
+ Qh = modMult(Ql, Q);
+ Uh = modReduce(Uh.multiply(Vl).subtract(Ql));
+ Vl = modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql)));
+ Ql = modMult(Ql, Qh);
+
+ for (int j = 1; j <= s; ++j)
+ {
+ Uh = modMult(Uh, Vl);
+ Vl = modReduce(Vl.multiply(Vl).subtract(Ql.shiftLeft(1)));
+ Ql = modMult(Ql, Ql);
+ }
+
+ return new BigInteger[]{ Uh, Vl };
+ }
+
+ protected BigInteger modAdd(BigInteger x1, BigInteger x2)
+ {
+ BigInteger x3 = x1.add(x2);
+ if (x3.compareTo(q) >= 0)
+ {
+ x3 = x3.subtract(q);
+ }
+ return x3;
+ }
+
+ protected BigInteger modDouble(BigInteger x)
+ {
+ BigInteger _2x = x.shiftLeft(1);
+ if (_2x.compareTo(q) >= 0)
+ {
+ _2x = _2x.subtract(q);
+ }
+ 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));
+ }
+
+ protected BigInteger modReduce(BigInteger x)
+ {
+ 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 (!rIsOne)
+ {
+ u = u.multiply(r);
+ }
+ x = u.add(v);
+ }
+ while (x.compareTo(q) >= 0)
+ {
+ x = x.subtract(q);
+ }
+ if (negative && x.signum() != 0)
+ {
+ x = q.subtract(x);
+ }
+ }
+ else
+ {
+ x = x.mod(q);
+ }
+ 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)
+ {
+ return true;
+ }
+
+ if (!(other instanceof ECFieldElement.Fp))
+ {
+ return false;
+ }
+
+ ECFieldElement.Fp o = (ECFieldElement.Fp)other;
+ return q.equals(o.q) && x.equals(o.x);
+ }
+
+ public int hashCode()
+ {
+ return q.hashCode() ^ x.hashCode();
+ }
+ }
+
+ /**
+ * 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
+ {
+ /**
+ * 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;
+
+ private int[] ks;
+
+ /**
+ * The <code>LongArray</code> holding the bits.
+ */
+ private LongArray x;
+
+ /**
+ * 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.
+ * @deprecated Use ECCurve.fromBigInteger to construct field elements
+ */
+ public F2m(
+ int m,
+ int k1,
+ int k2,
+ int k3,
+ BigInteger x)
+ {
+ if ((k2 == 0) && (k3 == 0))
+ {
+ this.representation = TPB;
+ this.ks = new int[]{ k1 };
+ }
+ 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;
+ this.ks = new int[]{ k1, k2, k3 };
+ }
+
+ this.m = m;
+ this.x = new LongArray(x);
+ }
+
+ /**
+ * 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.
+ * @deprecated Use ECCurve.fromBigInteger to construct field elements
+ */
+ 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);
+ }
+
+ private F2m(int m, int[] ks, LongArray x)
+ {
+ this.m = m;
+ this.representation = (ks.length == 1) ? TPB : PPB;
+ this.ks = ks;
+ this.x = x;
+ }
+
+ public int bitLength()
+ {
+ return x.degree();
+ }
+
+ public boolean isOne()
+ {
+ return x.isOne();
+ }
+
+ public boolean isZero()
+ {
+ return x.isZero();
+ }
+
+ public boolean testBitZero()
+ {
+ return x.testBitZero();
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return x.toBigInteger();
+ }
+
+ 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");
+ }
+
+ ECFieldElement.F2m aF2m = (ECFieldElement.F2m)a;
+ ECFieldElement.F2m bF2m = (ECFieldElement.F2m)b;
+
+ if (aF2m.representation != bF2m.representation)
+ {
+ // Should never occur
+ throw new IllegalArgumentException("One of the F2m field elements has incorrect representation");
+ }
+
+ if ((aF2m.m != bF2m.m) || !Arrays.areEqual(aF2m.ks, bF2m.ks))
+ {
+ throw new IllegalArgumentException("Field elements are not elements of the same field F2m");
+ }
+ }
+
+ 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);
+ LongArray iarrClone = (LongArray)this.x.clone();
+ F2m bF2m = (F2m)b;
+ iarrClone.addShiftedByWords(bF2m.x, 0);
+ return new F2m(m, ks, iarrClone);
+ }
+
+ public ECFieldElement addOne()
+ {
+ return new F2m(m, ks, x.addOne());
+ }
+
+ public ECFieldElement subtract(final ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return add(b);
+ }
+
+ public ECFieldElement multiply(final ECFieldElement b)
+ {
+ // Right-to-left comb multiplication in the LongArray
+ // 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);
+ 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
+ ECFieldElement bInv = b.invert();
+ return multiply(bInv);
+ }
+
+ public ECFieldElement negate()
+ {
+ // -x == x holds for all x in F2m
+ return this;
+ }
+
+ public ECFieldElement square()
+ {
+ 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));
+ }
+
+ public ECFieldElement sqrt()
+ {
+ 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);
+ }
+
+ /**
+ * @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.ks[0];
+ }
+
+ /**
+ * @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.ks.length >= 2 ? this.ks[1] : 0;
+ }
+
+ /**
+ * @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.ks.length >= 3 ? this.ks[2] : 0;
+ }
+
+ 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.representation == b.representation)
+ && Arrays.areEqual(this.ks, b.ks)
+ && (this.x.equals(b.x)));
+ }
+
+ public int hashCode()
+ {
+ return x.hashCode() ^ m ^ Arrays.hashCode(ks);
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/ECMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/ECMultiplier.java
new file mode 100644
index 00000000..17205e63
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ECMultiplier.java
@@ -0,0 +1,19 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Interface for classes encapsulating a point multiplication algorithm
+ * for <code>ECPoint</code>s.
+ */
+public interface ECMultiplier
+{
+ /**
+ * Multiplies the <code>ECPoint p</code> by <code>k</code>, i.e.
+ * <code>p</code> is added <code>k</code> times to itself.
+ * @param p The <code>ECPoint</code> to be multiplied.
+ * @param k The factor by which <code>p</code> is multiplied.
+ * @return <code>p</code> multiplied by <code>k</code>.
+ */
+ ECPoint multiply(ECPoint p, BigInteger k);
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/ECPoint.java b/core/src/main/java/org/spongycastle/math/ec/ECPoint.java
new file mode 100644
index 00000000..d5783ada
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ECPoint.java
@@ -0,0 +1,2122 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+import java.util.Hashtable;
+
+/**
+ * base class for points on elliptic curves.
+ */
+public abstract class ECPoint
+{
+ protected static ECFieldElement[] EMPTY_ZS = new ECFieldElement[0];
+
+ protected static ECFieldElement[] getInitialZCoords(ECCurve curve)
+ {
+ // Cope with null curve, most commonly used by implicitlyCa
+ int coord = null == curve ? ECCurve.COORD_AFFINE : curve.getCoordinateSystem();
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ return EMPTY_ZS;
+ default:
+ break;
+ }
+
+ ECFieldElement one = curve.fromBigInteger(ECConstants.ONE);
+
+ switch (coord)
+ {
+ case ECCurve.COORD_HOMOGENEOUS:
+ case ECCurve.COORD_JACOBIAN:
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ return new ECFieldElement[]{ one };
+ case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+ return new ECFieldElement[]{ one, one, one };
+ case ECCurve.COORD_JACOBIAN_MODIFIED:
+ return new ECFieldElement[]{ one, curve.getA() };
+ default:
+ throw new IllegalArgumentException("unknown coordinate system");
+ }
+ }
+
+ protected ECCurve curve;
+ protected ECFieldElement x;
+ protected ECFieldElement y;
+ protected ECFieldElement[] zs;
+
+ protected boolean withCompression;
+
+ // Hashtable is (String -> PreCompInfo)
+ protected Hashtable preCompTable = null;
+
+ protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, getInitialZCoords(curve));
+ }
+
+ protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs)
+ {
+ this.curve = curve;
+ this.x = x;
+ this.y = y;
+ this.zs = zs;
+ }
+
+ 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
+ return null == curve ? ECCurve.COORD_AFFINE : curve.getCoordinateSystem();
+ }
+
+ /**
+ * Normalizes this point, and then returns the affine x-coordinate.
+ *
+ * Note: normalization can be expensive, this method is deprecated in favour
+ * of caller-controlled normalization.
+ *
+ * @deprecated Use getAffineXCoord(), or normalize() and getXCoord(), instead
+ */
+ public ECFieldElement getX()
+ {
+ return normalize().getXCoord();
+ }
+
+
+ /**
+ * Normalizes this point, and then returns the affine y-coordinate.
+ *
+ * Note: normalization can be expensive, this method is deprecated in favour
+ * of caller-controlled normalization.
+ *
+ * @deprecated Use getAffineYCoord(), or normalize() and getYCoord(), instead
+ */
+ public ECFieldElement getY()
+ {
+ return normalize().getYCoord();
+ }
+
+ /**
+ * Returns the affine x-coordinate after checking that this point is normalized.
+ *
+ * @return The affine x-coordinate of this point
+ * @throws IllegalStateException if the point is not normalized
+ */
+ public ECFieldElement getAffineXCoord()
+ {
+ checkNormalized();
+ return getXCoord();
+ }
+
+ /**
+ * Returns the affine y-coordinate after checking that this point is normalized
+ *
+ * @return The affine y-coordinate of this point
+ * @throws IllegalStateException if the point is not normalized
+ */
+ public ECFieldElement getAffineYCoord()
+ {
+ checkNormalized();
+ return getYCoord();
+ }
+
+ /**
+ * Returns the x-coordinate.
+ *
+ * Caution: depending on the curve's coordinate system, this may not be the same value as in an
+ * affine coordinate system; use normalize() to get a point where the coordinates have their
+ * affine values, or use getAffineXCoord() if you expect the point to already have been
+ * normalized.
+ *
+ * @return the x-coordinate of this point
+ */
+ public ECFieldElement getXCoord()
+ {
+ return x;
+ }
+
+ /**
+ * Returns the y-coordinate.
+ *
+ * Caution: depending on the curve's coordinate system, this may not be the same value as in an
+ * affine coordinate system; use normalize() to get a point where the coordinates have their
+ * affine values, or use getAffineYCoord() if you expect the point to already have been
+ * normalized.
+ *
+ * @return the y-coordinate of this point
+ */
+ public ECFieldElement getYCoord()
+ {
+ return y;
+ }
+
+ public ECFieldElement getZCoord(int index)
+ {
+ return (index < 0 || index >= zs.length) ? null : zs[index];
+ }
+
+ public ECFieldElement[] getZCoords()
+ {
+ int zsLen = zs.length;
+ if (zsLen == 0)
+ {
+ return zs;
+ }
+ ECFieldElement[] copy = new ECFieldElement[zsLen];
+ System.arraycopy(zs, 0, copy, 0, zsLen);
+ return copy;
+ }
+
+ protected final ECFieldElement getRawXCoord()
+ {
+ return x;
+ }
+
+ protected final ECFieldElement getRawYCoord()
+ {
+ return y;
+ }
+
+ protected final ECFieldElement[] getRawZCoords()
+ {
+ return zs;
+ }
+
+ protected void checkNormalized()
+ {
+ if (!isNormalized())
+ {
+ throw new IllegalStateException("point not in normal form");
+ }
+ }
+
+ public boolean isNormalized()
+ {
+ int coord = this.getCurveCoordinateSystem();
+
+ return coord == ECCurve.COORD_AFFINE
+ || coord == ECCurve.COORD_LAMBDA_AFFINE
+ || isInfinity()
+ || zs[0].isOne();
+ }
+
+ /**
+ * Normalization ensures that any projective coordinate is 1, and therefore that the x, y
+ * coordinates reflect those of the equivalent point in an affine coordinate system.
+ *
+ * @return a new ECPoint instance representing the same point, but with normalized coordinates
+ */
+ public ECPoint normalize()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ switch (this.getCurveCoordinateSystem())
+ {
+ case ECCurve.COORD_AFFINE:
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ {
+ return this;
+ }
+ default:
+ {
+ ECFieldElement Z1 = getZCoord(0);
+ if (Z1.isOne())
+ {
+ return this;
+ }
+
+ return normalize(Z1.invert());
+ }
+ }
+ }
+
+ ECPoint normalize(ECFieldElement zInv)
+ {
+ switch (this.getCurveCoordinateSystem())
+ {
+ case ECCurve.COORD_HOMOGENEOUS:
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ return createScaledPoint(zInv, zInv);
+ }
+ case ECCurve.COORD_JACOBIAN:
+ case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+ case ECCurve.COORD_JACOBIAN_MODIFIED:
+ {
+ ECFieldElement zInv2 = zInv.square(), zInv3 = zInv2.multiply(zInv);
+ return createScaledPoint(zInv2, zInv3);
+ }
+ default:
+ {
+ throw new IllegalStateException("not a projective coordinate system");
+ }
+ }
+ }
+
+ protected ECPoint createScaledPoint(ECFieldElement sx, ECFieldElement sy)
+ {
+ return this.getCurve().createRawPoint(getRawXCoord().multiply(sx), getRawYCoord().multiply(sy), this.withCompression);
+ }
+
+ public boolean isInfinity()
+ {
+ return x == null || y == null || (zs.length > 0 && zs[0].isZero());
+ }
+
+ public boolean isCompressed()
+ {
+ return this.withCompression;
+ }
+
+ public boolean 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)
+ {
+ return false;
+ }
+
+ ECCurve c1 = this.getCurve(), c2 = other.getCurve();
+ boolean n1 = (null == c1), n2 = (null == c2);
+ boolean i1 = isInfinity(), i2 = other.isInfinity();
+
+ if (i1 || i2)
+ {
+ return (i1 && i2) && (n1 || n2 || c1.equals(c2));
+ }
+
+ ECPoint p1 = this, p2 = other;
+ if (n1 && n2)
+ {
+ // Points with null curve are in affine form, so already normalized
+ }
+ else if (n1)
+ {
+ p2 = p2.normalize();
+ }
+ else if (n2)
+ {
+ p1 = p1.normalize();
+ }
+ else if (!c1.equals(c2))
+ {
+ return false;
+ }
+ else
+ {
+ // TODO Consider just requiring already normalized, to avoid silent performance degradation
+
+ ECPoint[] points = new ECPoint[]{ this, c1.importPoint(p2) };
+
+ // TODO This is a little strong, really only requires coZNormalizeAll to get Zs equal
+ c1.normalizeAll(points);
+
+ p1 = points[0];
+ p2 = points[1];
+ }
+
+ return p1.getXCoord().equals(p2.getXCoord()) && p1.getYCoord().equals(p2.getYCoord());
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof ECPoint))
+ {
+ return false;
+ }
+
+ return equals((ECPoint)other);
+ }
+
+ public int hashCode()
+ {
+ ECCurve c = this.getCurve();
+ int hc = (null == c) ? 0 : ~c.hashCode();
+
+ if (!this.isInfinity())
+ {
+ // TODO Consider just requiring already normalized, to avoid silent performance degradation
+
+ ECPoint p = normalize();
+
+ hc ^= p.getXCoord().hashCode() * 17;
+ hc ^= p.getYCoord().hashCode() * 257;
+ }
+
+ return hc;
+ }
+
+ public String toString()
+ {
+ if (this.isInfinity())
+ {
+ return "INF";
+ }
+
+ StringBuffer sb = new StringBuffer();
+ sb.append('(');
+ sb.append(getRawXCoord());
+ sb.append(',');
+ sb.append(getRawYCoord());
+ for (int i = 0; i < zs.length; ++i)
+ {
+ sb.append(',');
+ sb.append(zs[i]);
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+
+ public byte[] getEncoded()
+ {
+ return getEncoded(this.withCompression);
+ }
+
+ /**
+ * return the field element encoded with point compression. (S 4.3.6)
+ */
+ public byte[] getEncoded(boolean compressed)
+ {
+ if (this.isInfinity())
+ {
+ return new byte[1];
+ }
+
+ ECPoint normed = normalize();
+
+ byte[] X = normed.getXCoord().getEncoded();
+
+ if (compressed)
+ {
+ byte[] PO = new byte[X.length + 1];
+ PO[0] = (byte)(normed.getCompressionYTilde() ? 0x03 : 0x02);
+ System.arraycopy(X, 0, PO, 1, X.length);
+ return PO;
+ }
+
+ byte[] Y = normed.getYCoord().getEncoded();
+
+ byte[] PO = new byte[X.length + Y.length + 1];
+ PO[0] = 0x04;
+ System.arraycopy(X, 0, PO, 1, X.length);
+ System.arraycopy(Y, 0, PO, X.length + 1, Y.length);
+ return PO;
+ }
+
+ protected abstract boolean getCompressionYTilde();
+
+ public abstract ECPoint add(ECPoint b);
+
+ public abstract ECPoint negate();
+
+ public abstract ECPoint subtract(ECPoint b);
+
+ public ECPoint timesPow2(int e)
+ {
+ if (e < 0)
+ {
+ throw new IllegalArgumentException("'e' cannot be negative");
+ }
+
+ ECPoint p = this;
+ while (--e >= 0)
+ {
+ p = p.twice();
+ }
+ return p;
+ }
+
+ public abstract ECPoint twice();
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ return twice().add(b);
+ }
+
+ public ECPoint threeTimes()
+ {
+ return twicePlus(this);
+ }
+
+ /**
+ * Multiplies this <code>ECPoint</code> by the given number.
+ * @param k The multiplicator.
+ * @return <code>k * this</code>.
+ */
+ public ECPoint multiply(BigInteger k)
+ {
+ 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 AbstractFp
+ {
+ /**
+ * Create a point which encodes without point compression.
+ *
+ * @param curve the curve to use
+ * @param x affine x co-ordinate
+ * @param y affine y co-ordinate
+ *
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * Create a point that encodes with or without point compression.
+ *
+ * @param curve the curve to use
+ * @param x affine x co-ordinate
+ * @param y affine y co-ordinate
+ * @param withCompression if true encode with point compression
+ *
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new ECPoint.Fp(null, this.getAffineXCoord(), this.getAffineYCoord());
+ }
+
+ public ECFieldElement getZCoord(int index)
+ {
+ if (index == 1 && ECCurve.COORD_JACOBIAN_MODIFIED == this.getCurveCoordinateSystem())
+ {
+ return getJacobianModifiedW();
+ }
+
+ return super.getZCoord(index);
+ }
+
+ // B.3 pg 62
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+ if (this == b)
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+ int coord = curve.getCoordinateSystem();
+
+ ECFieldElement X1 = this.x, Y1 = this.y;
+ ECFieldElement X2 = b.x, Y2 = b.y;
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement dx = X2.subtract(X1), dy = Y2.subtract(Y1);
+
+ if (dx.isZero())
+ {
+ if (dy.isZero())
+ {
+ // this == b, i.e. this must be doubled
+ return twice();
+ }
+
+ // this == -b, i.e. the result is the point at infinity
+ return curve.getInfinity();
+ }
+
+ ECFieldElement gamma = dy.divide(dx);
+ ECFieldElement X3 = gamma.square().subtract(X1).subtract(X2);
+ ECFieldElement Y3 = gamma.multiply(X1.subtract(X3)).subtract(Y1);
+
+ return new ECPoint.Fp(curve, X3, Y3, this.withCompression);
+ }
+
+ case ECCurve.COORD_HOMOGENEOUS:
+ {
+ ECFieldElement Z1 = this.zs[0];
+ ECFieldElement Z2 = b.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ boolean Z2IsOne = Z2.isOne();
+
+ ECFieldElement u1 = Z1IsOne ? Y2 : Y2.multiply(Z1);
+ ECFieldElement u2 = Z2IsOne ? Y1 : Y1.multiply(Z2);
+ ECFieldElement u = u1.subtract(u2);
+ ECFieldElement v1 = Z1IsOne ? X2 : X2.multiply(Z1);
+ ECFieldElement v2 = Z2IsOne ? X1 : X1.multiply(Z2);
+ ECFieldElement v = v1.subtract(v2);
+
+ // Check if b == this or b == -this
+ if (v.isZero())
+ {
+ if (u.isZero())
+ {
+ // this == b, i.e. this must be doubled
+ return this.twice();
+ }
+
+ // this == -b, i.e. the result is the point at infinity
+ return curve.getInfinity();
+ }
+
+ // TODO Optimize for when w == 1
+ ECFieldElement w = Z1IsOne ? Z2 : Z2IsOne ? Z1 : Z1.multiply(Z2);
+ ECFieldElement vSquared = v.square();
+ ECFieldElement vCubed = vSquared.multiply(v);
+ ECFieldElement vSquaredV2 = vSquared.multiply(v2);
+ ECFieldElement A = u.square().multiply(w).subtract(vCubed).subtract(two(vSquaredV2));
+
+ ECFieldElement X3 = v.multiply(A);
+ ECFieldElement Y3 = vSquaredV2.subtract(A).multiplyMinusProduct(u, u2, vCubed);
+ ECFieldElement Z3 = vCubed.multiply(w);
+
+ return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ case ECCurve.COORD_JACOBIAN:
+ case ECCurve.COORD_JACOBIAN_MODIFIED:
+ {
+ ECFieldElement Z1 = this.zs[0];
+ ECFieldElement Z2 = b.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+
+ ECFieldElement X3, Y3, Z3, Z3Squared = null;
+
+ if (!Z1IsOne && Z1.equals(Z2))
+ {
+ // TODO Make this available as public method coZAdd?
+
+ ECFieldElement dx = X1.subtract(X2), dy = Y1.subtract(Y2);
+ if (dx.isZero())
+ {
+ if (dy.isZero())
+ {
+ return twice();
+ }
+ return curve.getInfinity();
+ }
+
+ ECFieldElement C = dx.square();
+ ECFieldElement W1 = X1.multiply(C), W2 = X2.multiply(C);
+ ECFieldElement A1 = W1.subtract(W2).multiply(Y1);
+
+ X3 = dy.square().subtract(W1).subtract(W2);
+ Y3 = W1.subtract(X3).multiply(dy).subtract(A1);
+ Z3 = dx;
+
+ if (Z1IsOne)
+ {
+ Z3Squared = C;
+ }
+ else
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+ else
+ {
+ ECFieldElement Z1Squared, U2, S2;
+ if (Z1IsOne)
+ {
+ Z1Squared = Z1; U2 = X2; S2 = Y2;
+ }
+ else
+ {
+ Z1Squared = Z1.square();
+ U2 = Z1Squared.multiply(X2);
+ ECFieldElement Z1Cubed = Z1Squared.multiply(Z1);
+ S2 = Z1Cubed.multiply(Y2);
+ }
+
+ boolean Z2IsOne = Z2.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).multiplyMinusProduct(R, G, S1);
+
+ Z3 = H;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ if (!Z2IsOne)
+ {
+ Z3 = Z3.multiply(Z2);
+ }
+
+ // Alternative calculation of Z3 using fast square
+ // X3 = four(X3);
+ // Y3 = eight(Y3);
+ // Z3 = doubleProductFromSquares(Z1, Z2, Z1Squared, Z2Squared).multiply(H);
+
+ if (Z3 == H)
+ {
+ Z3Squared = HSquared;
+ }
+ }
+
+ ECFieldElement[] zs;
+ if (coord == ECCurve.COORD_JACOBIAN_MODIFIED)
+ {
+ // TODO If the result will only be used in a subsequent addition, we don't need W3
+ ECFieldElement W3 = calculateJacobianModifiedW(Z3, Z3Squared);
+
+ zs = new ECFieldElement[]{ Z3, W3 };
+ }
+ else
+ {
+ zs = new ECFieldElement[]{ Z3 };
+ }
+
+ return new ECPoint.Fp(curve, X3, Y3, zs, this.withCompression);
+ }
+
+ default:
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+ }
+ }
+
+ // B.3 pg 62
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement Y1 = this.y;
+ if (Y1.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ int coord = curve.getCoordinateSystem();
+
+ ECFieldElement X1 = this.x;
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement X1Squared = X1.square();
+ ECFieldElement gamma = three(X1Squared).add(this.getCurve().getA()).divide(two(Y1));
+ ECFieldElement X3 = gamma.square().subtract(two(X1));
+ ECFieldElement Y3 = gamma.multiply(X1.subtract(X3)).subtract(Y1);
+
+ return new ECPoint.Fp(curve, X3, Y3, this.withCompression);
+ }
+
+ case ECCurve.COORD_HOMOGENEOUS:
+ {
+ ECFieldElement Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+
+ // TODO Optimize for small negative a4 and -3
+ ECFieldElement w = curve.getA();
+ if (!w.isZero() && !Z1IsOne)
+ {
+ w = w.multiply(Z1.square());
+ }
+ w = w.add(three(X1.square()));
+
+ ECFieldElement s = Z1IsOne ? Y1 : Y1.multiply(Z1);
+ ECFieldElement t = Z1IsOne ? Y1.square() : s.multiply(Y1);
+ ECFieldElement B = X1.multiply(t);
+ ECFieldElement _4B = four(B);
+ ECFieldElement h = w.square().subtract(two(_4B));
+
+ ECFieldElement _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);
+ }
+
+ case ECCurve.COORD_JACOBIAN:
+ {
+ ECFieldElement Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+
+ ECFieldElement Y1Squared = Y1.square();
+ ECFieldElement T = Y1Squared.square();
+
+ ECFieldElement a4 = curve.getA();
+ ECFieldElement a4Neg = a4.negate();
+
+ ECFieldElement M, S;
+ if (a4Neg.toBigInteger().equals(BigInteger.valueOf(3)))
+ {
+ ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square();
+ M = three(X1.add(Z1Squared).multiply(X1.subtract(Z1Squared)));
+ S = four(Y1Squared.multiply(X1));
+ }
+ else
+ {
+ ECFieldElement X1Squared = X1.square();
+ M = three(X1Squared);
+ if (Z1IsOne)
+ {
+ M = M.add(a4);
+ }
+ else if (!a4.isZero())
+ {
+ ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement Z1Pow4 = Z1Squared.square();
+ if (a4Neg.bitLength() < a4.bitLength())
+ {
+ M = M.subtract(Z1Pow4.multiply(a4Neg));
+ }
+ else
+ {
+ M = M.add(Z1Pow4.multiply(a4));
+ }
+ }
+// S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T));
+ S = four(X1.multiply(Y1Squared));
+ }
+
+ ECFieldElement X3 = M.square().subtract(two(S));
+ ECFieldElement Y3 = S.subtract(X3).multiply(M).subtract(eight(T));
+
+ ECFieldElement Z3 = two(Y1);
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+
+ // Alternative calculation of Z3 using fast square
+// ECFieldElement Z3 = doubleProductFromSquares(Y1, Z1, Y1Squared, Z1Squared);
+
+ return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ case ECCurve.COORD_JACOBIAN_MODIFIED:
+ {
+ return twiceJacobianModified(true);
+ }
+
+ default:
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+ }
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this == b)
+ {
+ return threeTimes();
+ }
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECFieldElement Y1 = this.y;
+ if (Y1.isZero())
+ {
+ return b;
+ }
+
+ ECCurve curve = this.getCurve();
+ int coord = curve.getCoordinateSystem();
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.x, Y2 = b.y;
+
+ ECFieldElement dx = X2.subtract(X1), dy = Y2.subtract(Y1);
+
+ if (dx.isZero())
+ {
+ if (dy.isZero())
+ {
+ // this == b i.e. the result is 3P
+ return threeTimes();
+ }
+
+ // this == -b, i.e. the result is P
+ return this;
+ }
+
+ /*
+ * Optimized calculation of 2P + Q, as described in "Trading Inversions for
+ * Multiplications in Elliptic Curve Cryptography", by Ciet, Joye, Lauter, Montgomery.
+ */
+
+ ECFieldElement X = dx.square(), Y = dy.square();
+ ECFieldElement d = X.multiply(two(X1).add(X2)).subtract(Y);
+ if (d.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ ECFieldElement D = d.multiply(dx);
+ ECFieldElement I = D.invert();
+ ECFieldElement L1 = d.multiply(I).multiply(dy);
+ ECFieldElement L2 = two(Y1).multiply(X).multiply(dx).multiply(I).subtract(L1);
+ ECFieldElement X4 = (L2.subtract(L1)).multiply(L1.add(L2)).add(X2);
+ ECFieldElement Y4 = (X1.subtract(X4)).multiply(L2).subtract(Y1);
+
+ return new ECPoint.Fp(curve, X4, Y4, this.withCompression);
+ }
+ case ECCurve.COORD_JACOBIAN_MODIFIED:
+ {
+ return twiceJacobianModified(false).add(b);
+ }
+ default:
+ {
+ return twice().add(b);
+ }
+ }
+ }
+
+ public ECPoint threeTimes()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement Y1 = this.y;
+ if (Y1.isZero())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+ int coord = curve.getCoordinateSystem();
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement X1 = this.x;
+
+ ECFieldElement _2Y1 = two(Y1);
+ ECFieldElement X = _2Y1.square();
+ ECFieldElement Z = three(X1.square()).add(this.getCurve().getA());
+ ECFieldElement Y = Z.square();
+
+ ECFieldElement d = three(X1).multiply(X).subtract(Y);
+ if (d.isZero())
+ {
+ return this.getCurve().getInfinity();
+ }
+
+ ECFieldElement D = d.multiply(_2Y1);
+ ECFieldElement I = D.invert();
+ ECFieldElement L1 = d.multiply(I).multiply(Z);
+ ECFieldElement L2 = X.square().multiply(I).subtract(L1);
+
+ ECFieldElement X4 = (L2.subtract(L1)).multiply(L1.add(L2)).add(X1);
+ ECFieldElement Y4 = (X1.subtract(X4)).multiply(L2).subtract(Y1);
+ return new ECPoint.Fp(curve, X4, Y4, this.withCompression);
+ }
+ case ECCurve.COORD_JACOBIAN_MODIFIED:
+ {
+ return twiceJacobianModified(false).add(this);
+ }
+ default:
+ {
+ // NOTE: Be careful about recursions between twicePlus and threeTimes
+ return twice().add(this);
+ }
+ }
+ }
+
+ 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);
+ }
+
+ 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;
+ }
+
+ ECCurve curve = this.getCurve();
+ int coord = curve.getCoordinateSystem();
+
+ if (ECCurve.COORD_AFFINE != coord)
+ {
+ return new ECPoint.Fp(curve, this.x, this.y.negate(), this.zs, this.withCompression);
+ }
+
+ return new ECPoint.Fp(curve, this.x, this.y.negate(), this.withCompression);
+ }
+
+ protected ECFieldElement calculateJacobianModifiedW(ECFieldElement Z, ECFieldElement ZSquared)
+ {
+ ECFieldElement a4 = this.getCurve().getA();
+ if (a4.isZero() || Z.isOne())
+ {
+ return a4;
+ }
+
+ if (ZSquared == null)
+ {
+ ZSquared = Z.square();
+ }
+
+ ECFieldElement W = ZSquared.square();
+ ECFieldElement a4Neg = a4.negate();
+ if (a4Neg.bitLength() < a4.bitLength())
+ {
+ W = W.multiply(a4Neg).negate();
+ }
+ else
+ {
+ W = W.multiply(a4);
+ }
+ return W;
+ }
+
+ protected ECFieldElement getJacobianModifiedW()
+ {
+ ECFieldElement W = this.zs[1];
+ if (W == null)
+ {
+ // NOTE: Rarely, twicePlus will result in the need for a lazy W1 calculation here
+ this.zs[1] = W = calculateJacobianModifiedW(this.zs[0], null);
+ }
+ return W;
+ }
+
+ protected ECPoint.Fp twiceJacobianModified(boolean calculateW)
+ {
+ ECFieldElement X1 = this.x, Y1 = this.y, Z1 = this.zs[0], W1 = getJacobianModifiedW();
+
+ ECFieldElement X1Squared = X1.square();
+ ECFieldElement M = three(X1Squared).add(W1);
+ ECFieldElement _2Y1 = two(Y1);
+ ECFieldElement _2Y1Squared = _2Y1.multiply(Y1);
+ ECFieldElement S = two(X1.multiply(_2Y1Squared));
+ ECFieldElement X3 = M.square().subtract(two(S));
+ 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 = 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 AbstractF2m
+ {
+ /**
+ * @param curve base curve
+ * @param x x point
+ * @param y y point
+ *
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @param curve base curve
+ * @param x x point
+ * @param y y point
+ * @param withCompression true if encode with point compression.
+ *
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ if (x != null)
+ {
+ // Check if x and y are elements of the same field
+ ECFieldElement.F2m.checkFieldElements(this.x, this.y);
+
+ // Check if x and a are elements of the same field
+ if (curve != null)
+ {
+ ECFieldElement.F2m.checkFieldElements(this.x, this.curve.getA());
+ }
+ }
+
+ this.withCompression = withCompression;
+
+// checkCurveEquation();
+ }
+
+ F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+
+// checkCurveEquation();
+ }
+
+ protected ECPoint detach()
+ {
+ return new ECPoint.F2m(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ int coord = this.getCurveCoordinateSystem();
+
+ switch (coord)
+ {
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+ if (ECCurve.COORD_LAMBDA_PROJECTIVE == coord)
+ {
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+ }
+ return Y;
+ }
+ default:
+ {
+ return y;
+ }
+ }
+ }
+
+ 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();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ switch (this.getCurveCoordinateSystem())
+ {
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+ default:
+ {
+ return Y.divide(X).testBitZero();
+ }
+ }
+ }
+
+ /**
+ * Check, if two <code>ECPoint</code>s can be added or subtracted.
+ * @param a The first <code>ECPoint</code> to check.
+ * @param b The second <code>ECPoint</code> to check.
+ * @throws IllegalArgumentException if <code>a</code> and <code>b</code>
+ * cannot be added.
+ */
+ private static void checkPoints(ECPoint a, ECPoint b)
+ {
+ // Check, if points are on the same curve
+ if (a.curve != b.curve)
+ {
+ throw new IllegalArgumentException("Only points on the same "
+ + "curve can be added or subtracted");
+ }
+
+// ECFieldElement.F2m.checkFieldElements(a.x, b.x);
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.math.ec.ECPoint#add(org.spongycastle.math.ec.ECPoint)
+ */
+ public ECPoint add(ECPoint b)
+ {
+ checkPoints(this, b);
+ return addSimple((ECPoint.F2m)b);
+ }
+
+ /**
+ * Adds another <code>ECPoints.F2m</code> to <code>this</code> without
+ * checking if both points are on the same curve. Used by multiplication
+ * algorithms, because there all points are a multiple of the same point
+ * and hence the checks can be omitted.
+ * @param b The other <code>ECPoints.F2m</code> to add to
+ * <code>this</code>.
+ * @return <code>this + b</code>
+ */
+ public ECPoint.F2m addSimple(ECPoint.F2m b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+ int coord = curve.getCoordinateSystem();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.x;
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement Y1 = this.y;
+ ECFieldElement Y2 = b.y;
+
+ ECFieldElement dx = X1.add(X2), dy = Y1.add(Y2);
+ if (dx.isZero())
+ {
+ if (dy.isZero())
+ {
+ return (ECPoint.F2m)twice();
+ }
+
+ return (ECPoint.F2m)curve.getInfinity();
+ }
+
+ ECFieldElement L = dy.divide(dx);
+
+ 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);
+ }
+ case ECCurve.COORD_HOMOGENEOUS:
+ {
+ ECFieldElement Y1 = this.y, Z1 = this.zs[0];
+ ECFieldElement Y2 = b.y, Z2 = b.zs[0];
+
+ boolean Z2IsOne = Z2.isOne();
+
+ ECFieldElement U1 = Z1.multiply(Y2);
+ ECFieldElement U2 = Z2IsOne ? Y1 : Y1.multiply(Z2);
+ ECFieldElement U = U1.add(U2);
+ ECFieldElement V1 = Z1.multiply(X2);
+ ECFieldElement V2 = Z2IsOne ? X1 : X1.multiply(Z2);
+ ECFieldElement V = V1.add(V2);
+
+ if (V.isZero())
+ {
+ if (U.isZero())
+ {
+ return (ECPoint.F2m)twice();
+ }
+
+ return (ECPoint.F2m)curve.getInfinity();
+ }
+
+ ECFieldElement VSq = V.square();
+ ECFieldElement VCu = VSq.multiply(V);
+ ECFieldElement W = Z2IsOne ? Z1 : Z1.multiply(Z2);
+ 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 = 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);
+ }
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ 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.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return (ECPoint.F2m)twice();
+ }
+
+ return (ECPoint.F2m)curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ 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 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ 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);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+ default:
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.math.ec.ECPoint#subtract(org.spongycastle.math.ec.ECPoint)
+ */
+ public ECPoint subtract(ECPoint b)
+ {
+ checkPoints(this, b);
+ return subtractSimple((ECPoint.F2m)b);
+ }
+
+ /**
+ * Subtracts another <code>ECPoints.F2m</code> from <code>this</code>
+ * without checking if both points are on the same curve. Used by
+ * multiplication algorithms, because there all points are a multiple
+ * of the same point and hence the checks can be omitted.
+ * @param b The other <code>ECPoints.F2m</code> to subtract from
+ * <code>this</code>.
+ * @return <code>this - b</code>
+ */
+ public ECPoint.F2m subtractSimple(ECPoint.F2m b)
+ {
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ // Add -b
+ return addSimple((ECPoint.F2m)b.negate());
+ }
+
+ public ECPoint.F2m tau()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+ int coord = curve.getCoordinateSystem();
+
+ ECFieldElement X1 = this.x;
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ {
+ ECFieldElement Y1 = this.y;
+ return new ECPoint.F2m(curve, X1.square(), Y1.square(), this.withCompression);
+ }
+ case ECCurve.COORD_HOMOGENEOUS:
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ ECFieldElement Y1 = this.y, Z1 = this.zs[0];
+ return new ECPoint.F2m(curve, X1.square(), Y1.square(), new ECFieldElement[]{ Z1.square() }, this.withCompression);
+ }
+ default:
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+ }
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ int coord = curve.getCoordinateSystem();
+
+ switch (coord)
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement Y1 = this.y;
+
+ ECFieldElement L1 = Y1.divide(X1).add(X1);
+
+ ECFieldElement X3 = L1.square().add(L1).add(curve.getA());
+ ECFieldElement Y3 = X1.squarePlusProduct(X3, L1.addOne());
+
+ return new ECPoint.F2m(curve, X3, Y3, this.withCompression);
+ }
+ case ECCurve.COORD_HOMOGENEOUS:
+ {
+ ECFieldElement Y1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement Y1Z1 = Z1IsOne ? Y1 : Y1.multiply(Z1);
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement S = X1Sq.add(Y1Z1);
+ ECFieldElement V = X1Z1;
+ ECFieldElement vSquared = V.square();
+ ECFieldElement sv = S.add(V);
+ ECFieldElement h = sv.multiplyPlusProduct(S, vSquared, curve.getA());
+
+ ECFieldElement X3 = V.multiply(h);
+ 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);
+ }
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ 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);
+
+ ECFieldElement b = curve.getB();
+ ECFieldElement L3;
+ if (b.bitLength() < (curve.getFieldSize() >> 1))
+ {
+ ECFieldElement t1 = L1.add(X1).square();
+ 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.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+ }
+
+ return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+ default:
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+ }
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ int coord = curve.getCoordinateSystem();
+
+ switch (coord)
+ {
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ // NOTE: twicePlus() only optimized for lambda-affine argument
+ ECFieldElement X2 = b.x, Z2 = b.zs[0];
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.y;
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+ ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+ ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).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().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+ default:
+ {
+ return twice().add(b);
+ }
+ }
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ switch (this.getCurveCoordinateSystem())
+ {
+ case ECCurve.COORD_AFFINE:
+ {
+ ECFieldElement Y = this.y;
+ return new ECPoint.F2m(curve, X, Y.add(X), this.withCompression);
+ }
+ case ECCurve.COORD_HOMOGENEOUS:
+ {
+ ECFieldElement Y = this.y, Z = this.zs[0];
+ return new ECPoint.F2m(curve, X, Y.add(X), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+ case ECCurve.COORD_LAMBDA_AFFINE:
+ {
+ ECFieldElement L = this.y;
+ return new ECPoint.F2m(curve, X, L.addOne(), this.withCompression);
+ }
+ case ECCurve.COORD_LAMBDA_PROJECTIVE:
+ {
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new ECPoint.F2m(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+ default:
+ {
+ throw new IllegalStateException("unsupported coordinate system");
+ }
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/ECPointMap.java b/core/src/main/java/org/spongycastle/math/ec/ECPointMap.java
new file mode 100644
index 00000000..69503585
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ECPointMap.java
@@ -0,0 +1,6 @@
+package org.spongycastle.math.ec;
+
+public interface ECPointMap
+{
+ ECPoint map(ECPoint p);
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/FixedPointCombMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/FixedPointCombMultiplier.java
new file mode 100644
index 00000000..42ba2cc8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/FixedPointCombMultiplier.java
@@ -0,0 +1,57 @@
+package org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/FixedPointPreCompInfo.java b/core/src/main/java/org/spongycastle/math/ec/FixedPointPreCompInfo.java
new file mode 100644
index 00000000..0a14f499
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/FixedPointPreCompInfo.java
@@ -0,0 +1,40 @@
+package org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/FixedPointUtil.java b/core/src/main/java/org/spongycastle/math/ec/FixedPointUtil.java
new file mode 100644
index 00000000..aa96ebcb
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/FixedPointUtil.java
@@ -0,0 +1,71 @@
+package org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/GLVMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/GLVMultiplier.java
new file mode 100644
index 00000000..fb2c3ee3
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/GLVMultiplier.java
@@ -0,0 +1,42 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/LongArray.java b/core/src/main/java/org/spongycastle/math/ec/LongArray.java
new file mode 100644
index 00000000..6f718cd5
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/LongArray.java
@@ -0,0 +1,2195 @@
+package org.spongycastle.math.ec;
+
+import org.spongycastle.util.Arrays;
+
+import java.math.BigInteger;
+
+class LongArray
+{
+// private static long DEINTERLEAVE_MASK = 0x5555555555555555L;
+
+ /*
+ * This expands 8 bit indices into 16 bit contents (high bit 14), by inserting 0s between bits.
+ * In a binary field, this operation is the same as squaring an 8 bit number.
+ */
+ private static final int[] INTERLEAVE2_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
+ };
+
+ /*
+ * This expands 7 bit indices into 21 bit contents (high bit 18), by inserting 0s between bits.
+ */
+ private static final int[] INTERLEAVE3_TABLE = new int[]
+ {
+ 0x00000, 0x00001, 0x00008, 0x00009, 0x00040, 0x00041, 0x00048, 0x00049,
+ 0x00200, 0x00201, 0x00208, 0x00209, 0x00240, 0x00241, 0x00248, 0x00249,
+ 0x01000, 0x01001, 0x01008, 0x01009, 0x01040, 0x01041, 0x01048, 0x01049,
+ 0x01200, 0x01201, 0x01208, 0x01209, 0x01240, 0x01241, 0x01248, 0x01249,
+ 0x08000, 0x08001, 0x08008, 0x08009, 0x08040, 0x08041, 0x08048, 0x08049,
+ 0x08200, 0x08201, 0x08208, 0x08209, 0x08240, 0x08241, 0x08248, 0x08249,
+ 0x09000, 0x09001, 0x09008, 0x09009, 0x09040, 0x09041, 0x09048, 0x09049,
+ 0x09200, 0x09201, 0x09208, 0x09209, 0x09240, 0x09241, 0x09248, 0x09249,
+ 0x40000, 0x40001, 0x40008, 0x40009, 0x40040, 0x40041, 0x40048, 0x40049,
+ 0x40200, 0x40201, 0x40208, 0x40209, 0x40240, 0x40241, 0x40248, 0x40249,
+ 0x41000, 0x41001, 0x41008, 0x41009, 0x41040, 0x41041, 0x41048, 0x41049,
+ 0x41200, 0x41201, 0x41208, 0x41209, 0x41240, 0x41241, 0x41248, 0x41249,
+ 0x48000, 0x48001, 0x48008, 0x48009, 0x48040, 0x48041, 0x48048, 0x48049,
+ 0x48200, 0x48201, 0x48208, 0x48209, 0x48240, 0x48241, 0x48248, 0x48249,
+ 0x49000, 0x49001, 0x49008, 0x49009, 0x49040, 0x49041, 0x49048, 0x49049,
+ 0x49200, 0x49201, 0x49208, 0x49209, 0x49240, 0x49241, 0x49248, 0x49249
+ };
+
+ /*
+ * This expands 8 bit indices into 32 bit contents (high bit 28), by inserting 0s between bits.
+ */
+ private static final int[] INTERLEAVE4_TABLE = new int[]
+ {
+ 0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100, 0x00000101, 0x00000110, 0x00000111,
+ 0x00001000, 0x00001001, 0x00001010, 0x00001011, 0x00001100, 0x00001101, 0x00001110, 0x00001111,
+ 0x00010000, 0x00010001, 0x00010010, 0x00010011, 0x00010100, 0x00010101, 0x00010110, 0x00010111,
+ 0x00011000, 0x00011001, 0x00011010, 0x00011011, 0x00011100, 0x00011101, 0x00011110, 0x00011111,
+ 0x00100000, 0x00100001, 0x00100010, 0x00100011, 0x00100100, 0x00100101, 0x00100110, 0x00100111,
+ 0x00101000, 0x00101001, 0x00101010, 0x00101011, 0x00101100, 0x00101101, 0x00101110, 0x00101111,
+ 0x00110000, 0x00110001, 0x00110010, 0x00110011, 0x00110100, 0x00110101, 0x00110110, 0x00110111,
+ 0x00111000, 0x00111001, 0x00111010, 0x00111011, 0x00111100, 0x00111101, 0x00111110, 0x00111111,
+ 0x01000000, 0x01000001, 0x01000010, 0x01000011, 0x01000100, 0x01000101, 0x01000110, 0x01000111,
+ 0x01001000, 0x01001001, 0x01001010, 0x01001011, 0x01001100, 0x01001101, 0x01001110, 0x01001111,
+ 0x01010000, 0x01010001, 0x01010010, 0x01010011, 0x01010100, 0x01010101, 0x01010110, 0x01010111,
+ 0x01011000, 0x01011001, 0x01011010, 0x01011011, 0x01011100, 0x01011101, 0x01011110, 0x01011111,
+ 0x01100000, 0x01100001, 0x01100010, 0x01100011, 0x01100100, 0x01100101, 0x01100110, 0x01100111,
+ 0x01101000, 0x01101001, 0x01101010, 0x01101011, 0x01101100, 0x01101101, 0x01101110, 0x01101111,
+ 0x01110000, 0x01110001, 0x01110010, 0x01110011, 0x01110100, 0x01110101, 0x01110110, 0x01110111,
+ 0x01111000, 0x01111001, 0x01111010, 0x01111011, 0x01111100, 0x01111101, 0x01111110, 0x01111111,
+ 0x10000000, 0x10000001, 0x10000010, 0x10000011, 0x10000100, 0x10000101, 0x10000110, 0x10000111,
+ 0x10001000, 0x10001001, 0x10001010, 0x10001011, 0x10001100, 0x10001101, 0x10001110, 0x10001111,
+ 0x10010000, 0x10010001, 0x10010010, 0x10010011, 0x10010100, 0x10010101, 0x10010110, 0x10010111,
+ 0x10011000, 0x10011001, 0x10011010, 0x10011011, 0x10011100, 0x10011101, 0x10011110, 0x10011111,
+ 0x10100000, 0x10100001, 0x10100010, 0x10100011, 0x10100100, 0x10100101, 0x10100110, 0x10100111,
+ 0x10101000, 0x10101001, 0x10101010, 0x10101011, 0x10101100, 0x10101101, 0x10101110, 0x10101111,
+ 0x10110000, 0x10110001, 0x10110010, 0x10110011, 0x10110100, 0x10110101, 0x10110110, 0x10110111,
+ 0x10111000, 0x10111001, 0x10111010, 0x10111011, 0x10111100, 0x10111101, 0x10111110, 0x10111111,
+ 0x11000000, 0x11000001, 0x11000010, 0x11000011, 0x11000100, 0x11000101, 0x11000110, 0x11000111,
+ 0x11001000, 0x11001001, 0x11001010, 0x11001011, 0x11001100, 0x11001101, 0x11001110, 0x11001111,
+ 0x11010000, 0x11010001, 0x11010010, 0x11010011, 0x11010100, 0x11010101, 0x11010110, 0x11010111,
+ 0x11011000, 0x11011001, 0x11011010, 0x11011011, 0x11011100, 0x11011101, 0x11011110, 0x11011111,
+ 0x11100000, 0x11100001, 0x11100010, 0x11100011, 0x11100100, 0x11100101, 0x11100110, 0x11100111,
+ 0x11101000, 0x11101001, 0x11101010, 0x11101011, 0x11101100, 0x11101101, 0x11101110, 0x11101111,
+ 0x11110000, 0x11110001, 0x11110010, 0x11110011, 0x11110100, 0x11110101, 0x11110110, 0x11110111,
+ 0x11111000, 0x11111001, 0x11111010, 0x11111011, 0x11111100, 0x11111101, 0x11111110, 0x11111111
+ };
+
+ /*
+ * This expands 7 bit indices into 35 bit contents (high bit 30), by inserting 0s between bits.
+ */
+ private static final int[] INTERLEAVE5_TABLE = new int[] {
+ 0x00000000, 0x00000001, 0x00000020, 0x00000021, 0x00000400, 0x00000401, 0x00000420, 0x00000421,
+ 0x00008000, 0x00008001, 0x00008020, 0x00008021, 0x00008400, 0x00008401, 0x00008420, 0x00008421,
+ 0x00100000, 0x00100001, 0x00100020, 0x00100021, 0x00100400, 0x00100401, 0x00100420, 0x00100421,
+ 0x00108000, 0x00108001, 0x00108020, 0x00108021, 0x00108400, 0x00108401, 0x00108420, 0x00108421,
+ 0x02000000, 0x02000001, 0x02000020, 0x02000021, 0x02000400, 0x02000401, 0x02000420, 0x02000421,
+ 0x02008000, 0x02008001, 0x02008020, 0x02008021, 0x02008400, 0x02008401, 0x02008420, 0x02008421,
+ 0x02100000, 0x02100001, 0x02100020, 0x02100021, 0x02100400, 0x02100401, 0x02100420, 0x02100421,
+ 0x02108000, 0x02108001, 0x02108020, 0x02108021, 0x02108400, 0x02108401, 0x02108420, 0x02108421,
+ 0x40000000, 0x40000001, 0x40000020, 0x40000021, 0x40000400, 0x40000401, 0x40000420, 0x40000421,
+ 0x40008000, 0x40008001, 0x40008020, 0x40008021, 0x40008400, 0x40008401, 0x40008420, 0x40008421,
+ 0x40100000, 0x40100001, 0x40100020, 0x40100021, 0x40100400, 0x40100401, 0x40100420, 0x40100421,
+ 0x40108000, 0x40108001, 0x40108020, 0x40108021, 0x40108400, 0x40108401, 0x40108420, 0x40108421,
+ 0x42000000, 0x42000001, 0x42000020, 0x42000021, 0x42000400, 0x42000401, 0x42000420, 0x42000421,
+ 0x42008000, 0x42008001, 0x42008020, 0x42008021, 0x42008400, 0x42008401, 0x42008420, 0x42008421,
+ 0x42100000, 0x42100001, 0x42100020, 0x42100021, 0x42100400, 0x42100401, 0x42100420, 0x42100421,
+ 0x42108000, 0x42108001, 0x42108020, 0x42108021, 0x42108400, 0x42108401, 0x42108420, 0x42108421
+ };
+
+ /*
+ * This expands 9 bit indices into 63 bit (long) contents (high bit 56), by inserting 0s between bits.
+ */
+ private static final long[] INTERLEAVE7_TABLE = new long[]
+ {
+ 0x0000000000000000L, 0x0000000000000001L, 0x0000000000000080L, 0x0000000000000081L,
+ 0x0000000000004000L, 0x0000000000004001L, 0x0000000000004080L, 0x0000000000004081L,
+ 0x0000000000200000L, 0x0000000000200001L, 0x0000000000200080L, 0x0000000000200081L,
+ 0x0000000000204000L, 0x0000000000204001L, 0x0000000000204080L, 0x0000000000204081L,
+ 0x0000000010000000L, 0x0000000010000001L, 0x0000000010000080L, 0x0000000010000081L,
+ 0x0000000010004000L, 0x0000000010004001L, 0x0000000010004080L, 0x0000000010004081L,
+ 0x0000000010200000L, 0x0000000010200001L, 0x0000000010200080L, 0x0000000010200081L,
+ 0x0000000010204000L, 0x0000000010204001L, 0x0000000010204080L, 0x0000000010204081L,
+ 0x0000000800000000L, 0x0000000800000001L, 0x0000000800000080L, 0x0000000800000081L,
+ 0x0000000800004000L, 0x0000000800004001L, 0x0000000800004080L, 0x0000000800004081L,
+ 0x0000000800200000L, 0x0000000800200001L, 0x0000000800200080L, 0x0000000800200081L,
+ 0x0000000800204000L, 0x0000000800204001L, 0x0000000800204080L, 0x0000000800204081L,
+ 0x0000000810000000L, 0x0000000810000001L, 0x0000000810000080L, 0x0000000810000081L,
+ 0x0000000810004000L, 0x0000000810004001L, 0x0000000810004080L, 0x0000000810004081L,
+ 0x0000000810200000L, 0x0000000810200001L, 0x0000000810200080L, 0x0000000810200081L,
+ 0x0000000810204000L, 0x0000000810204001L, 0x0000000810204080L, 0x0000000810204081L,
+ 0x0000040000000000L, 0x0000040000000001L, 0x0000040000000080L, 0x0000040000000081L,
+ 0x0000040000004000L, 0x0000040000004001L, 0x0000040000004080L, 0x0000040000004081L,
+ 0x0000040000200000L, 0x0000040000200001L, 0x0000040000200080L, 0x0000040000200081L,
+ 0x0000040000204000L, 0x0000040000204001L, 0x0000040000204080L, 0x0000040000204081L,
+ 0x0000040010000000L, 0x0000040010000001L, 0x0000040010000080L, 0x0000040010000081L,
+ 0x0000040010004000L, 0x0000040010004001L, 0x0000040010004080L, 0x0000040010004081L,
+ 0x0000040010200000L, 0x0000040010200001L, 0x0000040010200080L, 0x0000040010200081L,
+ 0x0000040010204000L, 0x0000040010204001L, 0x0000040010204080L, 0x0000040010204081L,
+ 0x0000040800000000L, 0x0000040800000001L, 0x0000040800000080L, 0x0000040800000081L,
+ 0x0000040800004000L, 0x0000040800004001L, 0x0000040800004080L, 0x0000040800004081L,
+ 0x0000040800200000L, 0x0000040800200001L, 0x0000040800200080L, 0x0000040800200081L,
+ 0x0000040800204000L, 0x0000040800204001L, 0x0000040800204080L, 0x0000040800204081L,
+ 0x0000040810000000L, 0x0000040810000001L, 0x0000040810000080L, 0x0000040810000081L,
+ 0x0000040810004000L, 0x0000040810004001L, 0x0000040810004080L, 0x0000040810004081L,
+ 0x0000040810200000L, 0x0000040810200001L, 0x0000040810200080L, 0x0000040810200081L,
+ 0x0000040810204000L, 0x0000040810204001L, 0x0000040810204080L, 0x0000040810204081L,
+ 0x0002000000000000L, 0x0002000000000001L, 0x0002000000000080L, 0x0002000000000081L,
+ 0x0002000000004000L, 0x0002000000004001L, 0x0002000000004080L, 0x0002000000004081L,
+ 0x0002000000200000L, 0x0002000000200001L, 0x0002000000200080L, 0x0002000000200081L,
+ 0x0002000000204000L, 0x0002000000204001L, 0x0002000000204080L, 0x0002000000204081L,
+ 0x0002000010000000L, 0x0002000010000001L, 0x0002000010000080L, 0x0002000010000081L,
+ 0x0002000010004000L, 0x0002000010004001L, 0x0002000010004080L, 0x0002000010004081L,
+ 0x0002000010200000L, 0x0002000010200001L, 0x0002000010200080L, 0x0002000010200081L,
+ 0x0002000010204000L, 0x0002000010204001L, 0x0002000010204080L, 0x0002000010204081L,
+ 0x0002000800000000L, 0x0002000800000001L, 0x0002000800000080L, 0x0002000800000081L,
+ 0x0002000800004000L, 0x0002000800004001L, 0x0002000800004080L, 0x0002000800004081L,
+ 0x0002000800200000L, 0x0002000800200001L, 0x0002000800200080L, 0x0002000800200081L,
+ 0x0002000800204000L, 0x0002000800204001L, 0x0002000800204080L, 0x0002000800204081L,
+ 0x0002000810000000L, 0x0002000810000001L, 0x0002000810000080L, 0x0002000810000081L,
+ 0x0002000810004000L, 0x0002000810004001L, 0x0002000810004080L, 0x0002000810004081L,
+ 0x0002000810200000L, 0x0002000810200001L, 0x0002000810200080L, 0x0002000810200081L,
+ 0x0002000810204000L, 0x0002000810204001L, 0x0002000810204080L, 0x0002000810204081L,
+ 0x0002040000000000L, 0x0002040000000001L, 0x0002040000000080L, 0x0002040000000081L,
+ 0x0002040000004000L, 0x0002040000004001L, 0x0002040000004080L, 0x0002040000004081L,
+ 0x0002040000200000L, 0x0002040000200001L, 0x0002040000200080L, 0x0002040000200081L,
+ 0x0002040000204000L, 0x0002040000204001L, 0x0002040000204080L, 0x0002040000204081L,
+ 0x0002040010000000L, 0x0002040010000001L, 0x0002040010000080L, 0x0002040010000081L,
+ 0x0002040010004000L, 0x0002040010004001L, 0x0002040010004080L, 0x0002040010004081L,
+ 0x0002040010200000L, 0x0002040010200001L, 0x0002040010200080L, 0x0002040010200081L,
+ 0x0002040010204000L, 0x0002040010204001L, 0x0002040010204080L, 0x0002040010204081L,
+ 0x0002040800000000L, 0x0002040800000001L, 0x0002040800000080L, 0x0002040800000081L,
+ 0x0002040800004000L, 0x0002040800004001L, 0x0002040800004080L, 0x0002040800004081L,
+ 0x0002040800200000L, 0x0002040800200001L, 0x0002040800200080L, 0x0002040800200081L,
+ 0x0002040800204000L, 0x0002040800204001L, 0x0002040800204080L, 0x0002040800204081L,
+ 0x0002040810000000L, 0x0002040810000001L, 0x0002040810000080L, 0x0002040810000081L,
+ 0x0002040810004000L, 0x0002040810004001L, 0x0002040810004080L, 0x0002040810004081L,
+ 0x0002040810200000L, 0x0002040810200001L, 0x0002040810200080L, 0x0002040810200081L,
+ 0x0002040810204000L, 0x0002040810204001L, 0x0002040810204080L, 0x0002040810204081L,
+ 0x0100000000000000L, 0x0100000000000001L, 0x0100000000000080L, 0x0100000000000081L,
+ 0x0100000000004000L, 0x0100000000004001L, 0x0100000000004080L, 0x0100000000004081L,
+ 0x0100000000200000L, 0x0100000000200001L, 0x0100000000200080L, 0x0100000000200081L,
+ 0x0100000000204000L, 0x0100000000204001L, 0x0100000000204080L, 0x0100000000204081L,
+ 0x0100000010000000L, 0x0100000010000001L, 0x0100000010000080L, 0x0100000010000081L,
+ 0x0100000010004000L, 0x0100000010004001L, 0x0100000010004080L, 0x0100000010004081L,
+ 0x0100000010200000L, 0x0100000010200001L, 0x0100000010200080L, 0x0100000010200081L,
+ 0x0100000010204000L, 0x0100000010204001L, 0x0100000010204080L, 0x0100000010204081L,
+ 0x0100000800000000L, 0x0100000800000001L, 0x0100000800000080L, 0x0100000800000081L,
+ 0x0100000800004000L, 0x0100000800004001L, 0x0100000800004080L, 0x0100000800004081L,
+ 0x0100000800200000L, 0x0100000800200001L, 0x0100000800200080L, 0x0100000800200081L,
+ 0x0100000800204000L, 0x0100000800204001L, 0x0100000800204080L, 0x0100000800204081L,
+ 0x0100000810000000L, 0x0100000810000001L, 0x0100000810000080L, 0x0100000810000081L,
+ 0x0100000810004000L, 0x0100000810004001L, 0x0100000810004080L, 0x0100000810004081L,
+ 0x0100000810200000L, 0x0100000810200001L, 0x0100000810200080L, 0x0100000810200081L,
+ 0x0100000810204000L, 0x0100000810204001L, 0x0100000810204080L, 0x0100000810204081L,
+ 0x0100040000000000L, 0x0100040000000001L, 0x0100040000000080L, 0x0100040000000081L,
+ 0x0100040000004000L, 0x0100040000004001L, 0x0100040000004080L, 0x0100040000004081L,
+ 0x0100040000200000L, 0x0100040000200001L, 0x0100040000200080L, 0x0100040000200081L,
+ 0x0100040000204000L, 0x0100040000204001L, 0x0100040000204080L, 0x0100040000204081L,
+ 0x0100040010000000L, 0x0100040010000001L, 0x0100040010000080L, 0x0100040010000081L,
+ 0x0100040010004000L, 0x0100040010004001L, 0x0100040010004080L, 0x0100040010004081L,
+ 0x0100040010200000L, 0x0100040010200001L, 0x0100040010200080L, 0x0100040010200081L,
+ 0x0100040010204000L, 0x0100040010204001L, 0x0100040010204080L, 0x0100040010204081L,
+ 0x0100040800000000L, 0x0100040800000001L, 0x0100040800000080L, 0x0100040800000081L,
+ 0x0100040800004000L, 0x0100040800004001L, 0x0100040800004080L, 0x0100040800004081L,
+ 0x0100040800200000L, 0x0100040800200001L, 0x0100040800200080L, 0x0100040800200081L,
+ 0x0100040800204000L, 0x0100040800204001L, 0x0100040800204080L, 0x0100040800204081L,
+ 0x0100040810000000L, 0x0100040810000001L, 0x0100040810000080L, 0x0100040810000081L,
+ 0x0100040810004000L, 0x0100040810004001L, 0x0100040810004080L, 0x0100040810004081L,
+ 0x0100040810200000L, 0x0100040810200001L, 0x0100040810200080L, 0x0100040810200081L,
+ 0x0100040810204000L, 0x0100040810204001L, 0x0100040810204080L, 0x0100040810204081L,
+ 0x0102000000000000L, 0x0102000000000001L, 0x0102000000000080L, 0x0102000000000081L,
+ 0x0102000000004000L, 0x0102000000004001L, 0x0102000000004080L, 0x0102000000004081L,
+ 0x0102000000200000L, 0x0102000000200001L, 0x0102000000200080L, 0x0102000000200081L,
+ 0x0102000000204000L, 0x0102000000204001L, 0x0102000000204080L, 0x0102000000204081L,
+ 0x0102000010000000L, 0x0102000010000001L, 0x0102000010000080L, 0x0102000010000081L,
+ 0x0102000010004000L, 0x0102000010004001L, 0x0102000010004080L, 0x0102000010004081L,
+ 0x0102000010200000L, 0x0102000010200001L, 0x0102000010200080L, 0x0102000010200081L,
+ 0x0102000010204000L, 0x0102000010204001L, 0x0102000010204080L, 0x0102000010204081L,
+ 0x0102000800000000L, 0x0102000800000001L, 0x0102000800000080L, 0x0102000800000081L,
+ 0x0102000800004000L, 0x0102000800004001L, 0x0102000800004080L, 0x0102000800004081L,
+ 0x0102000800200000L, 0x0102000800200001L, 0x0102000800200080L, 0x0102000800200081L,
+ 0x0102000800204000L, 0x0102000800204001L, 0x0102000800204080L, 0x0102000800204081L,
+ 0x0102000810000000L, 0x0102000810000001L, 0x0102000810000080L, 0x0102000810000081L,
+ 0x0102000810004000L, 0x0102000810004001L, 0x0102000810004080L, 0x0102000810004081L,
+ 0x0102000810200000L, 0x0102000810200001L, 0x0102000810200080L, 0x0102000810200081L,
+ 0x0102000810204000L, 0x0102000810204001L, 0x0102000810204080L, 0x0102000810204081L,
+ 0x0102040000000000L, 0x0102040000000001L, 0x0102040000000080L, 0x0102040000000081L,
+ 0x0102040000004000L, 0x0102040000004001L, 0x0102040000004080L, 0x0102040000004081L,
+ 0x0102040000200000L, 0x0102040000200001L, 0x0102040000200080L, 0x0102040000200081L,
+ 0x0102040000204000L, 0x0102040000204001L, 0x0102040000204080L, 0x0102040000204081L,
+ 0x0102040010000000L, 0x0102040010000001L, 0x0102040010000080L, 0x0102040010000081L,
+ 0x0102040010004000L, 0x0102040010004001L, 0x0102040010004080L, 0x0102040010004081L,
+ 0x0102040010200000L, 0x0102040010200001L, 0x0102040010200080L, 0x0102040010200081L,
+ 0x0102040010204000L, 0x0102040010204001L, 0x0102040010204080L, 0x0102040010204081L,
+ 0x0102040800000000L, 0x0102040800000001L, 0x0102040800000080L, 0x0102040800000081L,
+ 0x0102040800004000L, 0x0102040800004001L, 0x0102040800004080L, 0x0102040800004081L,
+ 0x0102040800200000L, 0x0102040800200001L, 0x0102040800200080L, 0x0102040800200081L,
+ 0x0102040800204000L, 0x0102040800204001L, 0x0102040800204080L, 0x0102040800204081L,
+ 0x0102040810000000L, 0x0102040810000001L, 0x0102040810000080L, 0x0102040810000081L,
+ 0x0102040810004000L, 0x0102040810004001L, 0x0102040810004080L, 0x0102040810004081L,
+ 0x0102040810200000L, 0x0102040810200001L, 0x0102040810200080L, 0x0102040810200081L,
+ 0x0102040810204000L, 0x0102040810204001L, 0x0102040810204080L, 0x0102040810204081L
+ };
+
+ // For toString(); must have length 64
+ private static final String ZEROES = "0000000000000000000000000000000000000000000000000000000000000000";
+
+ 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
+ };
+
+ // TODO make m fixed for the LongArray, and hence compute T once and for all
+
+ private long[] m_ints;
+
+ public LongArray(int intLen)
+ {
+ m_ints = new long[intLen];
+ }
+
+ public LongArray(long[] ints)
+ {
+ m_ints = ints;
+ }
+
+ public LongArray(long[] ints, int off, int len)
+ {
+ if (off == 0 && len == ints.length)
+ {
+ m_ints = ints;
+ }
+ else
+ {
+ m_ints = new long[len];
+ System.arraycopy(ints, off, m_ints, 0, len);
+ }
+ }
+
+ public LongArray(BigInteger bigInt)
+ {
+ if (bigInt == null || bigInt.signum() < 0)
+ {
+ throw new IllegalArgumentException("invalid F2m field value");
+ }
+
+ if (bigInt.signum() == 0)
+ {
+ m_ints = new long[] { 0L };
+ 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 + 7) / 8;
+ m_ints = new long[intLen];
+
+ int iarrJ = intLen - 1;
+ int rem = barrLen % 8 + barrStart;
+ long 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 < 8; i++)
+ {
+ temp <<= 8;
+ int barrBarrI = barr[barrI++] & 0xFF;
+ temp |= barrBarrI;
+ }
+ m_ints[iarrJ] = temp;
+ }
+ }
+
+ 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;
+ for (int i = 0; i < a.length; ++i)
+ {
+ if (a[i] != 0L)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int getUsedLength()
+ {
+ return getUsedLengthFrom(m_ints.length);
+ }
+
+ public int getUsedLengthFrom(int from)
+ {
+ long[] 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;
+ long w;
+ do
+ {
+ if (i == 0)
+ {
+ return 0;
+ }
+ w = m_ints[--i];
+ }
+ while (w == 0);
+
+ return (i << 6) + bitLength(w);
+ }
+
+ private int degreeFrom(int limit)
+ {
+ int i = (limit + 62) >>> 6;
+ long w;
+ do
+ {
+ if (i == 0)
+ {
+ return 0;
+ }
+ w = m_ints[--i];
+ }
+ while (w == 0);
+
+ return (i << 6) + bitLength(w);
+ }
+
+// private int lowestCoefficient()
+// {
+// for (int i = 0; i < m_ints.length; ++i)
+// {
+// long mi = m_ints[i];
+// if (mi != 0)
+// {
+// int j = 0;
+// while ((mi & 0xFFL) == 0)
+// {
+// j += 8;
+// mi >>>= 8;
+// }
+// while ((mi & 1L) == 0)
+// {
+// ++j;
+// mi >>>= 1;
+// }
+// return (i << 6) + j;
+// }
+// }
+// return -1;
+// }
+
+ private static int bitLength(long w)
+ {
+ int u = (int)(w >>> 32), b;
+ if (u == 0)
+ {
+ u = (int)w;
+ b = 0;
+ }
+ else
+ {
+ b = 32;
+ }
+
+ int t = u >>> 16, k;
+ if (t == 0)
+ {
+ t = u >>> 8;
+ k = (t == 0) ? bitLengths[u] : 8 + bitLengths[t];
+ }
+ else
+ {
+ int v = t >>> 8;
+ k = (v == 0) ? 16 + bitLengths[t] : 24 + bitLengths[v];
+ }
+
+ return b + k;
+ }
+
+ private long[] resizedInts(int newLen)
+ {
+ long[] newInts = new long[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;
+ }
+
+ long highestInt = m_ints[usedLen - 1];
+ byte[] temp = new byte[8];
+ int barrI = 0;
+ boolean trailingZeroBytesDone = false;
+ for (int j = 7; j >= 0; j--)
+ {
+ byte thisByte = (byte)(highestInt >>> (8 * j));
+ if (trailingZeroBytesDone || (thisByte != 0))
+ {
+ trailingZeroBytesDone = true;
+ temp[barrI++] = thisByte;
+ }
+ }
+
+ int barrLen = 8 * (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--)
+ {
+ long mi = m_ints[iarrJ];
+ for (int j = 7; j >= 0; j--)
+ {
+ barr[barrI++] = (byte)(mi >>> (8 * j));
+ }
+ }
+ return new BigInteger(1, barr);
+ }
+
+// private static long shiftUp(long[] x, int xOff, int count)
+// {
+// long prev = 0;
+// for (int i = 0; i < count; ++i)
+// {
+// long next = x[xOff + i];
+// x[xOff + i] = (next << 1) | prev;
+// prev = next >>> 63;
+// }
+// return prev;
+// }
+
+ private static long shiftUp(long[] x, int xOff, int count, int shift)
+ {
+ int shiftInv = 64 - shift;
+ long prev = 0;
+ for (int i = 0; i < count; ++i)
+ {
+ long next = x[xOff + i];
+ x[xOff + i] = (next << shift) | prev;
+ prev = next >>> shiftInv;
+ }
+ return prev;
+ }
+
+ private static long shiftUp(long[] x, int xOff, long[] z, int zOff, int count, int shift)
+ {
+ int shiftInv = 64 - shift;
+ long prev = 0;
+ for (int i = 0; i < count; ++i)
+ {
+ long next = x[xOff + i];
+ z[zOff + i] = (next << shift) | prev;
+ prev = next >>> shiftInv;
+ }
+ return prev;
+ }
+
+ public LongArray addOne()
+ {
+ if (m_ints.length == 0)
+ {
+ return new LongArray(new long[]{ 1L });
+ }
+
+ int resultLen = Math.max(1, getUsedLength());
+ long[] ints = resizedInts(resultLen);
+ ints[0] ^= 1L;
+ return new LongArray(ints);
+ }
+
+// private void addShiftedByBits(LongArray other, int bits)
+// {
+// int words = bits >>> 6;
+// int shift = bits & 0x3F;
+//
+// 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);
+// }
+//
+// long carry = addShiftedByBits(m_ints, words, other.m_ints, 0, otherUsedLen, shift);
+// m_ints[otherUsedLen + words] ^= carry;
+// }
+
+ private void addShiftedByBitsSafe(LongArray other, int otherDegree, int bits)
+ {
+ int otherLen = (otherDegree + 63) >>> 6;
+
+ int words = bits >>> 6;
+ int shift = bits & 0x3F;
+
+ if (shift == 0)
+ {
+ add(m_ints, words, other.m_ints, 0, otherLen);
+ return;
+ }
+
+ long carry = addShiftedUp(m_ints, words, other.m_ints, 0, otherLen, shift);
+ if (carry != 0L)
+ {
+ m_ints[otherLen + words] ^= carry;
+ }
+ }
+
+ private static long addShiftedUp(long[] x, int xOff, long[] y, int yOff, int count, int shift)
+ {
+ int shiftInv = 64 - shift;
+ long prev = 0;
+ for (int i = 0; i < count; ++i)
+ {
+ long next = y[yOff + i];
+ x[xOff + i] ^= (next << shift) | prev;
+ prev = next >>> shiftInv;
+ }
+ return prev;
+ }
+
+ private static long addShiftedDown(long[] x, int xOff, long[] y, int yOff, int count, int shift)
+ {
+ int shiftInv = 64 - shift;
+ long prev = 0;
+ int i = count;
+ while (--i >= 0)
+ {
+ long next = y[yOff + i];
+ x[xOff + i] ^= (next >>> shift) | prev;
+ prev = next << shiftInv;
+ }
+ return prev;
+ }
+
+ public void addShiftedByWords(LongArray other, int words)
+ {
+ int otherUsedLen = other.getUsedLength();
+ if (otherUsedLen == 0)
+ {
+ return;
+ }
+
+ int minLen = otherUsedLen + words;
+ if (minLen > m_ints.length)
+ {
+ m_ints = resizedInts(minLen);
+ }
+
+ add(m_ints, words, other.m_ints, 0, otherUsedLen);
+ }
+
+ private static void add(long[] x, int xOff, long[] y, int yOff, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ x[xOff + i] ^= y[yOff + i];
+ }
+ }
+
+ private static void add(long[] x, int xOff, long[] y, int yOff, long[] z, int zOff, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ z[zOff + i] = x[xOff + i] ^ y[yOff + i];
+ }
+ }
+
+ private static void addBoth(long[] x, int xOff, long[] y1, int y1Off, long[] y2, int y2Off, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ x[xOff + i] ^= y1[y1Off + i] ^ y2[y2Off + i];
+ }
+ }
+
+ private static void distribute(long[] x, int src, int dst1, int dst2, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ long v = x[src + i];
+ x[dst1 + i] ^= v;
+ x[dst2 + i] ^= v;
+ }
+ }
+
+ public int getLength()
+ {
+ return m_ints.length;
+ }
+
+ private static void flipWord(long[] buf, int off, int bit, long word)
+ {
+ int n = off + (bit >>> 6);
+ int shift = bit & 0x3F;
+ if (shift == 0)
+ {
+ buf[n] ^= word;
+ }
+ else
+ {
+ buf[n] ^= word << shift;
+ word >>>= (64 - shift);
+ if (word != 0)
+ {
+ buf[++n] ^= word;
+ }
+ }
+ }
+
+// private static long getWord(long[] buf, int off, int len, int bit)
+// {
+// int n = off + (bit >>> 6);
+// int shift = bit & 0x3F;
+// if (shift == 0)
+// {
+// return buf[n];
+// }
+// long result = buf[n] >>> shift;
+// if (++n < len)
+// {
+// result |= buf[n] << (64 - shift);
+// }
+// return result;
+// }
+
+ public boolean testBitZero()
+ {
+ return m_ints.length > 0 && (m_ints[0] & 1L) != 0;
+ }
+
+ private static boolean testBit(long[] buf, int off, int n)
+ {
+ // theInt = n / 64
+ int theInt = n >>> 6;
+ // theBit = n % 64
+ int theBit = n & 0x3F;
+ long tester = 1L << theBit;
+ return (buf[off + theInt] & tester) != 0;
+ }
+
+ private static void flipBit(long[] buf, int off, int n)
+ {
+ // theInt = n / 64
+ int theInt = n >>> 6;
+ // theBit = n % 64
+ int theBit = n & 0x3F;
+ long flipper = 1L << theBit;
+ buf[off + theInt] ^= flipper;
+ }
+
+// private static void setBit(long[] buf, int off, int n)
+// {
+// // theInt = n / 64
+// int theInt = n >>> 6;
+// // theBit = n % 64
+// int theBit = n & 0x3F;
+// long setter = 1L << theBit;
+// buf[off + theInt] |= setter;
+// }
+//
+// private static void clearBit(long[] buf, int off, int n)
+// {
+// // theInt = n / 64
+// int theInt = n >>> 6;
+// // theBit = n % 64
+// int theBit = n & 0x3F;
+// long setter = 1L << theBit;
+// buf[off + theInt] &= ~setter;
+// }
+
+ private static void multiplyWord(long a, long[] b, int bLen, long[] c, int cOff)
+ {
+ if ((a & 1L) != 0L)
+ {
+ add(c, cOff, b, 0, bLen);
+ }
+ int k = 1;
+ while ((a >>>= 1) != 0L)
+ {
+ if ((a & 1L) != 0L)
+ {
+ long carry = addShiftedUp(c, cOff, b, 0, bLen, k);
+ if (carry != 0L)
+ {
+ c[cOff + bLen] ^= carry;
+ }
+ }
+ ++k;
+ }
+ }
+
+ public LongArray modMultiplyLD(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);
+ }
+
+ /*
+ * 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];
+
+ int MASK = 0xF;
+
+ /*
+ * Lopez-Dahab algorithm
+ */
+
+ for (int k = 56; k >= 0; k -= 8)
+ {
+ for (int j = 1; j < aLen; j += 2)
+ {
+ int aVal = (int)(a[j] >>> k);
+ int u = aVal & MASK;
+ int v = (aVal >>> 4) & MASK;
+ addBoth(c, j - 1, T0, ti[u], T1, ti[v], bMax);
+ }
+ shiftUp(c, 0, cLen, 8);
+ }
+
+ for (int k = 56; k >= 0; k -= 8)
+ {
+ for (int j = 0; j < aLen; j += 2)
+ {
+ int aVal = (int)(a[j] >>> k);
+ int u = aVal & MASK;
+ int v = (aVal >>> 4) & MASK;
+ addBoth(c, j, T0, ti[u], T1, ti[v], bMax);
+ }
+ if (k > 0)
+ {
+ shiftUp(c, 0, cLen, 8);
+ }
+ }
+
+ /*
+ * Finally the raw answer is collected, reduce it against the reduction coefficients
+ */
+ return reduceResult(c, 0, cLen, m, ks);
+ }
+
+ public LongArray modMultiply(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);
+ }
+
+ /*
+ * 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);
+ }
+
+ public LongArray modMultiplyAlt(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);
+ }
+
+ // NOTE: This works, but is slower than width 4 processing
+// if (aLen == 2)
+// {
+// /*
+// * Use common-multiplicand optimization to save ~1/4 of the adds
+// */
+// long a1 = A.m_ints[0], a2 = A.m_ints[1];
+// long aa = a1 & a2; a1 ^= aa; a2 ^= aa;
+//
+// long[] b = B.m_ints;
+// long[] c = new long[cLen];
+// multiplyWord(aa, b, bLen, c, 1);
+// add(c, 0, c, 1, cLen - 1);
+// multiplyWord(a1, b, bLen, c, 0);
+// multiplyWord(a2, b, bLen, c, 1);
+//
+// /*
+// * Reduce the raw answer against the reduction coefficients
+// */
+// return reduceResult(c, 0, cLen, m, ks);
+// }
+
+ /*
+ * Determine the parameters of the interleaved window algorithm: the 'width' in bits to
+ * process together, the number of evaluation 'positions' implied by that width, and the
+ * 'top' position at which the regular window algorithm stops.
+ */
+ int width, positions, top, banks;
+
+ // NOTE: width 4 is the fastest over the entire range of sizes used in current crypto
+// width = 1; positions = 64; top = 64; banks = 4;
+// width = 2; positions = 32; top = 64; banks = 4;
+// width = 3; positions = 21; top = 63; banks = 3;
+ width = 4; positions = 16; top = 64; banks = 8;
+// width = 5; positions = 13; top = 65; banks = 7;
+// width = 7; positions = 9; top = 63; banks = 9;
+// width = 8; positions = 8; top = 64; banks = 8;
+
+ /*
+ * Determine if B will get bigger during shifting
+ */
+ int shifts = top < 64 ? positions : positions - 1;
+ int bMax = (bDeg + shifts + 63) >>> 6;
+
+ int bTotal = bMax * banks, stride = width * banks;
+
+ /*
+ * Create a single temporary buffer, with an offset table to find the positions of things in it
+ */
+ int[] ci = new int[1 << width];
+ int cTotal = aLen;
+ {
+ ci[0] = cTotal;
+ cTotal += bTotal;
+ ci[1] = cTotal;
+ for (int i = 2; i < ci.length; ++i)
+ {
+ cTotal += cLen;
+ ci[i] = cTotal;
+ }
+ cTotal += cLen;
+ }
+ // NOTE: Provide a safe dump for "high zeroes" since we are adding 'bMax' and not 'bLen'
+ ++cTotal;
+
+ long[] c = new long[cTotal];
+
+ // Prepare A in interleaved form, according to the chosen width
+ interleave(A.m_ints, 0, c, 0, aLen, width);
+
+ // Make a working copy of B, since we will be shifting it
+ {
+ int bOff = aLen;
+ System.arraycopy(B.m_ints, 0, c, bOff, bLen);
+ for (int bank = 1; bank < banks; ++bank)
+ {
+ shiftUp(c, aLen, c, bOff += bMax, bMax, bank);
+ }
+ }
+
+ /*
+ * The main loop analyzes the interleaved windows in A, and for each non-zero window
+ * a single word-array XOR is performed to a carefully selected slice of 'c'. The loop is
+ * breadth-first, checking the lowest window in each word, then looping again for the
+ * next higher window position.
+ */
+ int MASK = (1 << width) - 1;
+
+ int k = 0;
+ for (;;)
+ {
+ int aPos = 0;
+ do
+ {
+ long aVal = c[aPos] >>> k;
+ int bank = 0, bOff = aLen;
+ for (;;)
+ {
+ int index = (int)(aVal) & MASK;
+ if (index != 0)
+ {
+ /*
+ * Add to a 'c' buffer based on the bit-pattern of 'index'. Since A is in
+ * interleaved form, the bits represent the current B shifted by 0, 'positions',
+ * 'positions' * 2, ..., 'positions' * ('width' - 1)
+ */
+ add(c, aPos + ci[index], c, bOff, bMax);
+ }
+ if (++bank == banks)
+ {
+ break;
+ }
+ bOff += bMax;
+ aVal >>>= width;
+ }
+ }
+ while (++aPos < aLen);
+
+ if ((k += stride) >= top)
+ {
+ if (k >= 64)
+ {
+ break;
+ }
+
+ /*
+ * Adjustment for window setups with top == 63, the final bit (if any) is processed
+ * as the top-bit of a window
+ */
+ k = 64 - width;
+ MASK &= MASK << (top - k);
+ }
+
+ /*
+ * After each position has been checked for all words of A, B is shifted up 1 place
+ */
+ shiftUp(c, aLen, bTotal, banks);
+ }
+
+ int ciPos = ci.length;
+ while (--ciPos > 1)
+ {
+ if ((ciPos & 1L) == 0L)
+ {
+ /*
+ * For even numbers, shift contents and add to the half-position
+ */
+ addShiftedUp(c, ci[ciPos >>> 1], c, ci[ciPos], cLen, positions);
+ }
+ else
+ {
+ /*
+ * For odd numbers, 'distribute' contents to the result and the next-lowest position
+ */
+ distribute(c, ci[ciPos], ci[ciPos - 1], ci[1], cLen);
+ }
+ }
+
+ /*
+ * Finally the raw answer is collected, reduce it against the reduction coefficients
+ */
+ 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);
+ return new LongArray(buf, off, rLen);
+ }
+
+// private static void deInterleave(long[] x, int xOff, long[] z, int zOff, int count, int rounds)
+// {
+// for (int i = 0; i < count; ++i)
+// {
+// z[zOff + i] = deInterleave(x[zOff + i], rounds);
+// }
+// }
+//
+// private static long deInterleave(long x, int rounds)
+// {
+// while (--rounds >= 0)
+// {
+// x = deInterleave32(x & DEINTERLEAVE_MASK) | (deInterleave32((x >>> 1) & DEINTERLEAVE_MASK) << 32);
+// }
+// return x;
+// }
+//
+// private static long deInterleave32(long x)
+// {
+// x = (x | (x >>> 1)) & 0x3333333333333333L;
+// x = (x | (x >>> 2)) & 0x0F0F0F0F0F0F0F0FL;
+// x = (x | (x >>> 4)) & 0x00FF00FF00FF00FFL;
+// x = (x | (x >>> 8)) & 0x0000FFFF0000FFFFL;
+// x = (x | (x >>> 16)) & 0x00000000FFFFFFFFL;
+// return x;
+// }
+
+ private static int reduceInPlace(long[] buf, int off, int len, int m, int[] ks)
+ {
+ int mLen = (m + 63) >>> 6;
+ if (len < mLen)
+ {
+ return len;
+ }
+
+ int numBits = Math.min(len << 6, (m << 1) - 1); // TODO use actual degree?
+ int excessBits = (len << 6) - numBits;
+ while (excessBits >= 64)
+ {
+ --len;
+ excessBits -= 64;
+ }
+
+ int kLen = ks.length, kMax = ks[kLen - 1], kNext = kLen > 1 ? ks[kLen - 2] : 0;
+ int wordWiseLimit = Math.max(m, kMax + 64);
+ int vectorableWords = (excessBits + Math.min(numBits - wordWiseLimit, m - kNext)) >> 6;
+ if (vectorableWords > 1)
+ {
+ int vectorWiseWords = len - vectorableWords;
+ reduceVectorWise(buf, off, len, vectorWiseWords, m, ks);
+ while (len > vectorWiseWords)
+ {
+ buf[off + --len] = 0L;
+ }
+ numBits = vectorWiseWords << 6;
+ }
+
+ if (numBits > wordWiseLimit)
+ {
+ reduceWordWise(buf, off, len, wordWiseLimit, m, ks);
+ numBits = wordWiseLimit;
+ }
+
+ if (numBits > m)
+ {
+ reduceBitWise(buf, off, numBits, m, ks);
+ }
+
+ return mLen;
+ }
+
+ private static void reduceBitWise(long[] buf, int off, int bitlength, int m, int[] ks)
+ {
+ while (--bitlength >= m)
+ {
+ if (testBit(buf, off, bitlength))
+ {
+ reduceBit(buf, off, bitlength, m, ks);
+ }
+ }
+ }
+
+ private static void reduceBit(long[] buf, int off, int bit, int m, int[] ks)
+ {
+ flipBit(buf, off, bit);
+ int n = bit - m;
+ int j = ks.length;
+ while (--j >= 0)
+ {
+ flipBit(buf, off, ks[j] + n);
+ }
+ flipBit(buf, off, n);
+ }
+
+ private static void reduceWordWise(long[] buf, int off, int len, int toBit, int m, int[] ks)
+ {
+ int toPos = toBit >>> 6;
+
+ while (--len > toPos)
+ {
+ long word = buf[off + len];
+ if (word != 0)
+ {
+ buf[off + len] = 0;
+ reduceWord(buf, off, (len << 6), 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);
+ }
+ }
+ }
+
+ private static void reduceWord(long[] buf, int off, int bit, long word, int m, int[] ks)
+ {
+ int offset = bit - m;
+ int j = ks.length;
+ while (--j >= 0)
+ {
+ flipWord(buf, off, offset + ks[j], word);
+ }
+ flipWord(buf, off, offset, word);
+ }
+
+ private static void reduceVectorWise(long[] buf, int off, int len, int words, int m, int[] ks)
+ {
+ /*
+ * NOTE: It's important we go from highest coefficient to lowest, because for the highest
+ * one (only) we allow the ranges to partially overlap, and therefore any changes must take
+ * effect for the subsequent lower coefficients.
+ */
+ int baseBit = (words << 6) - m;
+ int j = ks.length;
+ while (--j >= 0)
+ {
+ flipVector(buf, off, buf, off + words, len - words, baseBit + ks[j]);
+ }
+ flipVector(buf, off, buf, off + words, len - words, baseBit);
+ }
+
+ private static void flipVector(long[] x, int xOff, long[] y, int yOff, int yLen, int bits)
+ {
+ xOff += bits >>> 6;
+ bits &= 0x3F;
+
+ if (bits == 0)
+ {
+ add(x, xOff, y, yOff, yLen);
+ }
+ else
+ {
+ long carry = addShiftedDown(x, xOff + 1, y, yOff, yLen, 64 - bits);
+ x[xOff] ^= carry;
+ }
+ }
+
+ public LongArray modSquare(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, reduceInPlace(r, 0, r.length, m, ks));
+ }
+
+ 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)
+ {
+ switch (width)
+ {
+ case 3:
+ interleave3(x, xOff, z, zOff, count);
+ break;
+ case 5:
+ interleave5(x, xOff, z, zOff, count);
+ break;
+ case 7:
+ interleave7(x, xOff, z, zOff, count);
+ break;
+ default:
+ interleave2_n(x, xOff, z, zOff, count, bitLengths[width] - 1);
+ break;
+ }
+ }
+
+ private static void interleave3(long[] x, int xOff, long[] z, int zOff, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ z[zOff + i] = interleave3(x[xOff + i]);
+ }
+ }
+
+ private static long interleave3(long x)
+ {
+ long z = x & (1L << 63);
+ return z
+ | interleave3_21to63((int)x & 0x1FFFFF)
+ | interleave3_21to63((int)(x >>> 21) & 0x1FFFFF) << 1
+ | interleave3_21to63((int)(x >>> 42) & 0x1FFFFF) << 2;
+
+// int zPos = 0, wPos = 0, xPos = 0;
+// for (;;)
+// {
+// z |= ((x >>> xPos) & 1L) << zPos;
+// if (++zPos == 63)
+// {
+// String sz2 = Long.toBinaryString(z);
+// return z;
+// }
+// if ((xPos += 21) >= 63)
+// {
+// xPos = ++wPos;
+// }
+// }
+ }
+
+ private static long interleave3_21to63(int x)
+ {
+ int r00 = INTERLEAVE3_TABLE[x & 0x7F];
+ int r21 = INTERLEAVE3_TABLE[(x >>> 7) & 0x7F];
+ int r42 = INTERLEAVE3_TABLE[x >>> 14];
+ return (r42 & 0xFFFFFFFFL) << 42 | (r21 & 0xFFFFFFFFL) << 21 | (r00 & 0xFFFFFFFFL);
+ }
+
+ private static void interleave5(long[] x, int xOff, long[] z, int zOff, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ z[zOff + i] = interleave5(x[xOff + i]);
+ }
+ }
+
+ private static long interleave5(long x)
+ {
+ return interleave3_13to65((int)x & 0x1FFF)
+ | interleave3_13to65((int)(x >>> 13) & 0x1FFF) << 1
+ | interleave3_13to65((int)(x >>> 26) & 0x1FFF) << 2
+ | interleave3_13to65((int)(x >>> 39) & 0x1FFF) << 3
+ | interleave3_13to65((int)(x >>> 52) & 0x1FFF) << 4;
+
+// long z = 0;
+// int zPos = 0, wPos = 0, xPos = 0;
+// for (;;)
+// {
+// z |= ((x >>> xPos) & 1L) << zPos;
+// if (++zPos == 64)
+// {
+// return z;
+// }
+// if ((xPos += 13) >= 64)
+// {
+// xPos = ++wPos;
+// }
+// }
+ }
+
+ private static long interleave3_13to65(int x)
+ {
+ int r00 = INTERLEAVE5_TABLE[x & 0x7F];
+ int r35 = INTERLEAVE5_TABLE[x >>> 7];
+ return (r35 & 0xFFFFFFFFL) << 35 | (r00 & 0xFFFFFFFFL);
+ }
+
+ private static void interleave7(long[] x, int xOff, long[] z, int zOff, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ z[zOff + i] = interleave7(x[xOff + i]);
+ }
+ }
+
+ private static long interleave7(long x)
+ {
+ long z = x & (1L << 63);
+ return z
+ | INTERLEAVE7_TABLE[(int)x & 0x1FF]
+ | INTERLEAVE7_TABLE[(int)(x >>> 9) & 0x1FF] << 1
+ | INTERLEAVE7_TABLE[(int)(x >>> 18) & 0x1FF] << 2
+ | INTERLEAVE7_TABLE[(int)(x >>> 27) & 0x1FF] << 3
+ | INTERLEAVE7_TABLE[(int)(x >>> 36) & 0x1FF] << 4
+ | INTERLEAVE7_TABLE[(int)(x >>> 45) & 0x1FF] << 5
+ | INTERLEAVE7_TABLE[(int)(x >>> 54) & 0x1FF] << 6;
+
+// int zPos = 0, wPos = 0, xPos = 0;
+// for (;;)
+// {
+// z |= ((x >>> xPos) & 1L) << zPos;
+// if (++zPos == 63)
+// {
+// return z;
+// }
+// if ((xPos += 9) >= 63)
+// {
+// xPos = ++wPos;
+// }
+// }
+ }
+
+ private static void interleave2_n(long[] x, int xOff, long[] z, int zOff, int count, int rounds)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ z[zOff + i] = interleave2_n(x[xOff + i], rounds);
+ }
+ }
+
+ private static long interleave2_n(long x, int rounds)
+ {
+ while (rounds > 1)
+ {
+ rounds -= 2;
+ x = interleave4_16to64((int)x & 0xFFFF)
+ | interleave4_16to64((int)(x >>> 16) & 0xFFFF) << 1
+ | interleave4_16to64((int)(x >>> 32) & 0xFFFF) << 2
+ | interleave4_16to64((int)(x >>> 48) & 0xFFFF) << 3;
+ }
+ if (rounds > 0)
+ {
+ x = interleave2_32to64((int)x) | interleave2_32to64((int)(x >>> 32)) << 1;
+ }
+ return x;
+ }
+
+ private static long interleave4_16to64(int x)
+ {
+ int r00 = INTERLEAVE4_TABLE[x & 0xFF];
+ int r32 = INTERLEAVE4_TABLE[x >>> 8];
+ return (r32 & 0xFFFFFFFFL) << 32 | (r00 & 0xFFFFFFFFL);
+ }
+
+ private static long interleave2_32to64(int x)
+ {
+ int r00 = INTERLEAVE2_TABLE[x & 0xFF] | INTERLEAVE2_TABLE[(x >>> 8) & 0xFF] << 16;
+ int r32 = INTERLEAVE2_TABLE[(x >>> 16) & 0xFF] | INTERLEAVE2_TABLE[x >>> 24] << 16;
+ return (r32 & 0xFFFFFFFFL) << 32 | (r00 & 0xFFFFFFFFL);
+ }
+
+// private static LongArray expItohTsujii2(LongArray B, int n, int m, int[] ks)
+// {
+// LongArray t1 = B, t3 = new LongArray(new long[]{ 1L });
+// int scale = 1;
+//
+// int numTerms = n;
+// while (numTerms > 1)
+// {
+// if ((numTerms & 1) != 0)
+// {
+// t3 = t3.modMultiply(t1, m, ks);
+// t1 = t1.modSquareN(scale, m, ks);
+// }
+//
+// LongArray t2 = t1.modSquareN(scale, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+// numTerms >>>= 1; scale <<= 1;
+// }
+//
+// return t3.modMultiply(t1, m, ks);
+// }
+//
+// private static LongArray expItohTsujii23(LongArray B, int n, int m, int[] ks)
+// {
+// LongArray t1 = B, t3 = new LongArray(new long[]{ 1L });
+// int scale = 1;
+//
+// int numTerms = n;
+// while (numTerms > 1)
+// {
+// boolean m03 = numTerms % 3 == 0;
+// boolean m14 = !m03 && (numTerms & 1) != 0;
+//
+// if (m14)
+// {
+// t3 = t3.modMultiply(t1, m, ks);
+// t1 = t1.modSquareN(scale, m, ks);
+// }
+//
+// LongArray t2 = t1.modSquareN(scale, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+//
+// if (m03)
+// {
+// t2 = t2.modSquareN(scale, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+// numTerms /= 3; scale *= 3;
+// }
+// else
+// {
+// numTerms >>>= 1; scale <<= 1;
+// }
+// }
+//
+// return t3.modMultiply(t1, m, ks);
+// }
+//
+// private static LongArray expItohTsujii235(LongArray B, int n, int m, int[] ks)
+// {
+// LongArray t1 = B, t4 = new LongArray(new long[]{ 1L });
+// int scale = 1;
+//
+// int numTerms = n;
+// while (numTerms > 1)
+// {
+// if (numTerms % 5 == 0)
+// {
+//// t1 = expItohTsujii23(t1, 5, m, ks);
+//
+// LongArray t3 = t1;
+// t1 = t1.modSquareN(scale, m, ks);
+//
+// LongArray t2 = t1.modSquareN(scale, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+// t2 = t1.modSquareN(scale << 1, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+//
+// t1 = t1.modMultiply(t3, m, ks);
+//
+// numTerms /= 5; scale *= 5;
+// continue;
+// }
+//
+// boolean m03 = numTerms % 3 == 0;
+// boolean m14 = !m03 && (numTerms & 1) != 0;
+//
+// if (m14)
+// {
+// t4 = t4.modMultiply(t1, m, ks);
+// t1 = t1.modSquareN(scale, m, ks);
+// }
+//
+// LongArray t2 = t1.modSquareN(scale, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+//
+// if (m03)
+// {
+// t2 = t2.modSquareN(scale, m, ks);
+// t1 = t1.modMultiply(t2, m, ks);
+// numTerms /= 3; scale *= 3;
+// }
+// else
+// {
+// numTerms >>>= 1; scale <<= 1;
+// }
+// }
+//
+// return t4.modMultiply(t1, m, ks);
+// }
+
+ public LongArray modInverse(int m, int[] ks)
+ {
+ /*
+ * Fermat's Little Theorem
+ */
+// LongArray A = this;
+// LongArray B = A.modSquare(m, ks);
+// LongArray R0 = B, R1 = B;
+// for (int i = 2; i < m; ++i)
+// {
+// R1 = R1.modSquare(m, ks);
+// R0 = R0.modMultiply(R1, m, ks);
+// }
+//
+// return R0;
+
+ /*
+ * Itoh-Tsujii
+ */
+// LongArray B = modSquare(m, ks);
+// switch (m)
+// {
+// case 409:
+// return expItohTsujii23(B, m - 1, m, ks);
+// case 571:
+// return expItohTsujii235(B, m - 1, m, ks);
+// case 163:
+// case 233:
+// case 283:
+// default:
+// return expItohTsujii2(B, m - 1, m, 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 == 0)
+ {
+ throw new IllegalStateException();
+ }
+ if (uzDegree == 1)
+ {
+ return this;
+ }
+
+ // u(z) := a(z)
+ LongArray uz = (LongArray)clone();
+
+ int t = (m + 63) >>> 6;
+
+ // v(z) := f(z)
+ LongArray vz = new LongArray(t);
+ reduceBit(vz.m_ints, 0, m, m, ks);
+
+ // g1(z) := 1, g2(z) := 0
+ LongArray g1z = new LongArray(t);
+ g1z.m_ints[0] = 1L;
+ LongArray g2z = new LongArray(t);
+
+ int[] uvDeg = new int[]{ uzDegree, m + 1 };
+ LongArray[] uv = new LongArray[]{ uz, vz };
+
+ int[] ggDeg = new int[]{ 1, 0 };
+ LongArray[] gg = new LongArray[]{ g1z, g2z };
+
+ int b = 1;
+ int duv1 = uvDeg[b];
+ int dgg1 = ggDeg[b];
+ int j = duv1 - uvDeg[1 - b];
+
+ for (;;)
+ {
+ if (j < 0)
+ {
+ j = -j;
+ uvDeg[b] = duv1;
+ ggDeg[b] = dgg1;
+ b = 1 - b;
+ duv1 = uvDeg[b];
+ dgg1 = ggDeg[b];
+ }
+
+ uv[b].addShiftedByBitsSafe(uv[1 - b], uvDeg[1 - b], j);
+
+ int duv2 = uv[b].degreeFrom(duv1);
+ if (duv2 == 0)
+ {
+ return gg[1 - b];
+ }
+
+ {
+ int dgg2 = ggDeg[1 - b];
+ gg[b].addShiftedByBitsSafe(gg[1 - b], dgg2, j);
+ dgg2 += j;
+
+ if (dgg2 > dgg1)
+ {
+ dgg1 = dgg2;
+ }
+ else if (dgg2 == dgg1)
+ {
+ dgg1 = gg[b].degreeFrom(dgg1);
+ }
+ }
+
+ j += (duv2 - duv1);
+ duv1 = duv2;
+ }
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof LongArray))
+ {
+ return false;
+ }
+ LongArray other = (LongArray) 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++)
+ {
+ long mi = m_ints[i];
+ hash *= 31;
+ hash ^= (int)mi;
+ hash *= 31;
+ hash ^= (int)(mi >>> 32);
+ }
+ return hash;
+ }
+
+ public Object clone()
+ {
+ return new LongArray(Arrays.clone(m_ints));
+ }
+
+ public String toString()
+ {
+ int i = getUsedLength();
+ if (i == 0)
+ {
+ return "0";
+ }
+
+ StringBuffer sb = new StringBuffer(Long.toBinaryString(m_ints[--i]));
+ while (--i >= 0)
+ {
+ String s = Long.toBinaryString(m_ints[i]);
+
+ // Add leading zeroes, except for highest significant word
+ int len = s.length();
+ if (len < 64)
+ {
+ sb.append(ZEROES.substring(len));
+ }
+
+ sb.append(s);
+ }
+ return sb.toString();
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/math/ec/MixedNafR2LMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/MixedNafR2LMultiplier.java
new file mode 100644
index 00000000..e626103a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/MixedNafR2LMultiplier.java
@@ -0,0 +1,77 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm (right-to-left) using
+ * mixed coordinates.
+ */
+public class MixedNafR2LMultiplier extends AbstractECMultiplier
+{
+ protected int additionCoord, doublingCoord;
+
+ /**
+ * By default, addition will be done in Jacobian coordinates, and doubling will be done in
+ * Modified Jacobian coordinates (independent of the original coordinate system of each point).
+ */
+ public MixedNafR2LMultiplier()
+ {
+ this(ECCurve.COORD_JACOBIAN, ECCurve.COORD_JACOBIAN_MODIFIED);
+ }
+
+ public MixedNafR2LMultiplier(int additionCoord, int doublingCoord)
+ {
+ this.additionCoord = additionCoord;
+ this.doublingCoord = doublingCoord;
+ }
+
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ ECCurve curveOrig = p.getCurve();
+
+ ECCurve curveAdd = configureCurve(curveOrig, additionCoord);
+ ECCurve curveDouble = configureCurve(curveOrig, doublingCoord);
+
+ int[] naf = WNafUtil.generateCompactNaf(k);
+
+ ECPoint Ra = curveAdd.getInfinity();
+ ECPoint Td = curveDouble.importPoint(p);
+
+ int zeroes = 0;
+ for (int i = 0; i < naf.length; ++i)
+ {
+ int ni = naf[i];
+ int digit = ni >> 16;
+ zeroes += ni & 0xFFFF;
+
+ Td = Td.timesPow2(zeroes);
+
+ ECPoint Tj = curveAdd.importPoint(Td);
+ if (digit < 0)
+ {
+ Tj = Tj.negate();
+ }
+
+ Ra = Ra.add(Tj);
+
+ zeroes = 1;
+ }
+
+ return curveOrig.importPoint(Ra);
+ }
+
+ protected ECCurve configureCurve(ECCurve c, int coord)
+ {
+ if (c.getCoordinateSystem() == coord)
+ {
+ return c;
+ }
+
+ if (!c.supportsCoordinateSystem(coord))
+ {
+ throw new IllegalArgumentException("Coordinate system " + coord + " not supported by this curve");
+ }
+
+ return c.configure().setCoordinateSystem(coord).create();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/MontgomeryLadderMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/MontgomeryLadderMultiplier.java
new file mode 100644
index 00000000..bc4f7a00
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/MontgomeryLadderMultiplier.java
@@ -0,0 +1,25 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class MontgomeryLadderMultiplier extends AbstractECMultiplier
+{
+ /**
+ * Montgomery ladder.
+ */
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ ECPoint[] R = new ECPoint[]{ p.getCurve().getInfinity(), p };
+
+ int n = k.bitLength();
+ int i = n;
+ while (--i >= 0)
+ {
+ int b = k.testBit(i) ? 1 : 0;
+ int bp = 1 - b;
+ R[bp] = R[bp].add(R[b]);
+ R[b] = R[b].twice();
+ }
+ return R[0];
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/NafL2RMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/NafL2RMultiplier.java
new file mode 100644
index 00000000..b2aac42c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/NafL2RMultiplier.java
@@ -0,0 +1,30 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm (left-to-right).
+ */
+public class NafL2RMultiplier extends AbstractECMultiplier
+{
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ int[] naf = WNafUtil.generateCompactNaf(k);
+
+ ECPoint addP = p.normalize(), subP = addP.negate();
+
+ ECPoint R = p.getCurve().getInfinity();
+
+ int i = naf.length;
+ while (--i >= 0)
+ {
+ int ni = naf[i];
+ int digit = ni >> 16, zeroes = ni & 0xFFFF;
+
+ R = R.twicePlus(digit < 0 ? subP : addP);
+ R = R.timesPow2(zeroes);
+ }
+
+ return R;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/NafR2LMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/NafR2LMultiplier.java
new file mode 100644
index 00000000..78aecb02
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/NafR2LMultiplier.java
@@ -0,0 +1,31 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm (right-to-left).
+ */
+public class NafR2LMultiplier extends AbstractECMultiplier
+{
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ int[] naf = WNafUtil.generateCompactNaf(k);
+
+ ECPoint R0 = p.getCurve().getInfinity(), R1 = p;
+
+ int zeroes = 0;
+ for (int i = 0; i < naf.length; ++i)
+ {
+ int ni = naf[i];
+ int digit = ni >> 16;
+ zeroes += ni & 0xFFFF;
+
+ R1 = R1.timesPow2(zeroes);
+ R0 = R0.add(digit < 0 ? R1.negate() : R1);
+
+ zeroes = 1;
+ }
+
+ return R0;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/PreCompInfo.java b/core/src/main/java/org/spongycastle/math/ec/PreCompInfo.java
new file mode 100644
index 00000000..72af12c4
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/PreCompInfo.java
@@ -0,0 +1,10 @@
+package org.spongycastle.math.ec;
+
+/**
+ * Interface for classes storing precomputation data for multiplication
+ * algorithms. Used as a Memento (see GOF patterns) by e.g.
+ * <code>WNafL2RMultiplier</code>.
+ */
+public interface PreCompInfo
+{
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/ReferenceMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/ReferenceMultiplier.java
new file mode 100644
index 00000000..b4c53fdd
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ReferenceMultiplier.java
@@ -0,0 +1,11 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class ReferenceMultiplier extends AbstractECMultiplier
+{
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ return ECAlgorithms.referenceMultiply(p, k);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/ScaleXPointMap.java b/core/src/main/java/org/spongycastle/math/ec/ScaleXPointMap.java
new file mode 100644
index 00000000..6630c4f9
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ScaleXPointMap.java
@@ -0,0 +1,16 @@
+package org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/ScaleYPointMap.java b/core/src/main/java/org/spongycastle/math/ec/ScaleYPointMap.java
new file mode 100644
index 00000000..7a215cb4
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ScaleYPointMap.java
@@ -0,0 +1,16 @@
+package org.spongycastle.math.ec;
+
+public class ScaleYPointMap implements ECPointMap
+{
+ protected final ECFieldElement scale;
+
+ public ScaleYPointMap(ECFieldElement scale)
+ {
+ this.scale = scale;
+ }
+
+ public ECPoint map(ECPoint p)
+ {
+ return p.scaleY(scale);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/SimpleBigDecimal.java b/core/src/main/java/org/spongycastle/math/ec/SimpleBigDecimal.java
new file mode 100644
index 00000000..c367830a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/SimpleBigDecimal.java
@@ -0,0 +1,247 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class representing a simple version of a big decimal. A
+ * <code>SimpleBigDecimal</code> is basically a
+ * {@link java.math.BigInteger BigInteger} with a few digits on the right of
+ * the decimal point. The number of (binary) digits on the right of the decimal
+ * point is called the <code>scale</code> of the <code>SimpleBigDecimal</code>.
+ * Unlike in {@link java.math.BigDecimal BigDecimal}, the scale is not adjusted
+ * automatically, but must be set manually. All <code>SimpleBigDecimal</code>s
+ * taking part in the same arithmetic operation must have equal scale. The
+ * result of a multiplication of two <code>SimpleBigDecimal</code>s returns a
+ * <code>SimpleBigDecimal</code> with double scale.
+ */
+class SimpleBigDecimal
+ //extends Number // not in J2ME - add compatibility class?
+{
+ private static final long serialVersionUID = 1L;
+
+ private final BigInteger bigInt;
+ private final int scale;
+
+ /**
+ * Returns a <code>SimpleBigDecimal</code> representing the same numerical
+ * value as <code>value</code>.
+ * @param value The value of the <code>SimpleBigDecimal</code> to be
+ * created.
+ * @param scale The scale of the <code>SimpleBigDecimal</code> to be
+ * created.
+ * @return The such created <code>SimpleBigDecimal</code>.
+ */
+ public static SimpleBigDecimal getInstance(BigInteger value, int scale)
+ {
+ return new SimpleBigDecimal(value.shiftLeft(scale), scale);
+ }
+
+ /**
+ * Constructor for <code>SimpleBigDecimal</code>. The value of the
+ * constructed <code>SimpleBigDecimal</code> equals <code>bigInt /
+ * 2<sup>scale</sup></code>.
+ * @param bigInt The <code>bigInt</code> value parameter.
+ * @param scale The scale of the constructed <code>SimpleBigDecimal</code>.
+ */
+ public SimpleBigDecimal(BigInteger bigInt, int scale)
+ {
+ if (scale < 0)
+ {
+ throw new IllegalArgumentException("scale may not be negative");
+ }
+
+ this.bigInt = bigInt;
+ this.scale = scale;
+ }
+
+ private void checkScale(SimpleBigDecimal b)
+ {
+ if (scale != b.scale)
+ {
+ throw new IllegalArgumentException("Only SimpleBigDecimal of " +
+ "same scale allowed in arithmetic operations");
+ }
+ }
+
+ public SimpleBigDecimal adjustScale(int newScale)
+ {
+ if (newScale < 0)
+ {
+ throw new IllegalArgumentException("scale may not be negative");
+ }
+
+ if (newScale == scale)
+ {
+ return this;
+ }
+
+ return new SimpleBigDecimal(bigInt.shiftLeft(newScale - scale),
+ newScale);
+ }
+
+ public SimpleBigDecimal add(SimpleBigDecimal b)
+ {
+ checkScale(b);
+ return new SimpleBigDecimal(bigInt.add(b.bigInt), scale);
+ }
+
+ public SimpleBigDecimal add(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.add(b.shiftLeft(scale)), scale);
+ }
+
+ public SimpleBigDecimal negate()
+ {
+ return new SimpleBigDecimal(bigInt.negate(), scale);
+ }
+
+ public SimpleBigDecimal subtract(SimpleBigDecimal b)
+ {
+ return add(b.negate());
+ }
+
+ public SimpleBigDecimal subtract(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.subtract(b.shiftLeft(scale)),
+ scale);
+ }
+
+ public SimpleBigDecimal multiply(SimpleBigDecimal b)
+ {
+ checkScale(b);
+ return new SimpleBigDecimal(bigInt.multiply(b.bigInt), scale + scale);
+ }
+
+ public SimpleBigDecimal multiply(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.multiply(b), scale);
+ }
+
+ public SimpleBigDecimal divide(SimpleBigDecimal b)
+ {
+ checkScale(b);
+ BigInteger dividend = bigInt.shiftLeft(scale);
+ return new SimpleBigDecimal(dividend.divide(b.bigInt), scale);
+ }
+
+ public SimpleBigDecimal divide(BigInteger b)
+ {
+ return new SimpleBigDecimal(bigInt.divide(b), scale);
+ }
+
+ public SimpleBigDecimal shiftLeft(int n)
+ {
+ return new SimpleBigDecimal(bigInt.shiftLeft(n), scale);
+ }
+
+ public int compareTo(SimpleBigDecimal val)
+ {
+ checkScale(val);
+ return bigInt.compareTo(val.bigInt);
+ }
+
+ public int compareTo(BigInteger val)
+ {
+ return bigInt.compareTo(val.shiftLeft(scale));
+ }
+
+ public BigInteger floor()
+ {
+ return bigInt.shiftRight(scale);
+ }
+
+ public BigInteger round()
+ {
+ SimpleBigDecimal oneHalf = new SimpleBigDecimal(ECConstants.ONE, 1);
+ return add(oneHalf.adjustScale(scale)).floor();
+ }
+
+ public int intValue()
+ {
+ return floor().intValue();
+ }
+
+ public long longValue()
+ {
+ return floor().longValue();
+ }
+ /* NON-J2ME compliant.
+ public double doubleValue()
+ {
+ return Double.valueOf(toString()).doubleValue();
+ }
+
+ public float floatValue()
+ {
+ return Float.valueOf(toString()).floatValue();
+ }
+ */
+ public int getScale()
+ {
+ return scale;
+ }
+
+ public String toString()
+ {
+ if (scale == 0)
+ {
+ return bigInt.toString();
+ }
+
+ BigInteger floorBigInt = floor();
+
+ BigInteger fract = bigInt.subtract(floorBigInt.shiftLeft(scale));
+ if (bigInt.signum() == -1)
+ {
+ fract = ECConstants.ONE.shiftLeft(scale).subtract(fract);
+ }
+
+ if ((floorBigInt.signum() == -1) && (!(fract.equals(ECConstants.ZERO))))
+ {
+ floorBigInt = floorBigInt.add(ECConstants.ONE);
+ }
+ String leftOfPoint = floorBigInt.toString();
+
+ char[] fractCharArr = new char[scale];
+ String fractStr = fract.toString(2);
+ int fractLen = fractStr.length();
+ int zeroes = scale - fractLen;
+ for (int i = 0; i < zeroes; i++)
+ {
+ fractCharArr[i] = '0';
+ }
+ for (int j = 0; j < fractLen; j++)
+ {
+ fractCharArr[zeroes + j] = fractStr.charAt(j);
+ }
+ String rightOfPoint = new String(fractCharArr);
+
+ StringBuffer sb = new StringBuffer(leftOfPoint);
+ sb.append(".");
+ sb.append(rightOfPoint);
+
+ return sb.toString();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+
+ if (!(o instanceof SimpleBigDecimal))
+ {
+ return false;
+ }
+
+ SimpleBigDecimal other = (SimpleBigDecimal)o;
+ return ((bigInt.equals(other.bigInt)) && (scale == other.scale));
+ }
+
+ public int hashCode()
+ {
+ return bigInt.hashCode() ^ scale;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/Tnaf.java b/core/src/main/java/org/spongycastle/math/ec/Tnaf.java
new file mode 100644
index 00000000..d5aba320
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/Tnaf.java
@@ -0,0 +1,823 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class holding methods for point multiplication based on the window
+ * &tau;-adic nonadjacent form (WTNAF). The algorithms are based on the
+ * paper "Improved Algorithms for Arithmetic on Anomalous Binary Curves"
+ * by Jerome A. Solinas. The paper first appeared in the Proceedings of
+ * Crypto 1997.
+ */
+class Tnaf
+{
+ private static final BigInteger MINUS_ONE = ECConstants.ONE.negate();
+ private static final BigInteger MINUS_TWO = ECConstants.TWO.negate();
+ private static final BigInteger MINUS_THREE = ECConstants.THREE.negate();
+
+ /**
+ * The window width of WTNAF. The standard value of 4 is slightly less
+ * than optimal for running time, but keeps space requirements for
+ * precomputation low. For typical curves, a value of 5 or 6 results in
+ * a better running time. When changing this value, the
+ * <code>&alpha;<sub>u</sub></code>'s must be computed differently, see
+ * e.g. "Guide to Elliptic Curve Cryptography", Darrel Hankerson,
+ * Alfred Menezes, Scott Vanstone, Springer-Verlag New York Inc., 2004,
+ * p. 121-122
+ */
+ public static final byte WIDTH = 4;
+
+ /**
+ * 2<sup>4</sup>
+ */
+ public static final byte POW_2_WIDTH = 16;
+
+ /**
+ * The <code>&alpha;<sub>u</sub></code>'s for <code>a=0</code> as an array
+ * of <code>ZTauElement</code>s.
+ */
+ public static final ZTauElement[] alpha0 = {
+ null,
+ new ZTauElement(ECConstants.ONE, ECConstants.ZERO), null,
+ new ZTauElement(MINUS_THREE, MINUS_ONE), null,
+ new ZTauElement(MINUS_ONE, MINUS_ONE), null,
+ new ZTauElement(ECConstants.ONE, MINUS_ONE), null
+ };
+
+ /**
+ * The <code>&alpha;<sub>u</sub></code>'s for <code>a=0</code> as an array
+ * of TNAFs.
+ */
+ public static final byte[][] alpha0Tnaf = {
+ null, {1}, null, {-1, 0, 1}, null, {1, 0, 1}, null, {-1, 0, 0, 1}
+ };
+
+ /**
+ * The <code>&alpha;<sub>u</sub></code>'s for <code>a=1</code> as an array
+ * of <code>ZTauElement</code>s.
+ */
+ public static final ZTauElement[] alpha1 = {null,
+ new ZTauElement(ECConstants.ONE, ECConstants.ZERO), null,
+ new ZTauElement(MINUS_THREE, ECConstants.ONE), null,
+ new ZTauElement(MINUS_ONE, ECConstants.ONE), null,
+ new ZTauElement(ECConstants.ONE, ECConstants.ONE), null
+ };
+
+ /**
+ * The <code>&alpha;<sub>u</sub></code>'s for <code>a=1</code> as an array
+ * of TNAFs.
+ */
+ public static final byte[][] alpha1Tnaf = {
+ null, {1}, null, {-1, 0, 1}, null, {1, 0, 1}, null, {-1, 0, 0, -1}
+ };
+
+ /**
+ * Computes the norm of an element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code>.
+ * @param mu The parameter <code>&mu;</code> of the elliptic curve.
+ * @param lambda The element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code>.
+ * @return The norm of <code>&lambda;</code>.
+ */
+ public static BigInteger norm(final byte mu, ZTauElement lambda)
+ {
+ BigInteger norm;
+
+ // s1 = u^2
+ BigInteger s1 = lambda.u.multiply(lambda.u);
+
+ // s2 = u * v
+ BigInteger s2 = lambda.u.multiply(lambda.v);
+
+ // s3 = 2 * v^2
+ BigInteger s3 = lambda.v.multiply(lambda.v).shiftLeft(1);
+
+ if (mu == 1)
+ {
+ norm = s1.add(s2).add(s3);
+ }
+ else if (mu == -1)
+ {
+ norm = s1.subtract(s2).add(s3);
+ }
+ else
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ return norm;
+ }
+
+ /**
+ * Computes the norm of an element <code>&lambda;</code> of
+ * <code><b>R</b>[&tau;]</code>, where <code>&lambda; = u + v&tau;</code>
+ * and <code>u</code> and <code>u</code> are real numbers (elements of
+ * <code><b>R</b></code>).
+ * @param mu The parameter <code>&mu;</code> of the elliptic curve.
+ * @param u The real part of the element <code>&lambda;</code> of
+ * <code><b>R</b>[&tau;]</code>.
+ * @param v The <code>&tau;</code>-adic part of the element
+ * <code>&lambda;</code> of <code><b>R</b>[&tau;]</code>.
+ * @return The norm of <code>&lambda;</code>.
+ */
+ public static SimpleBigDecimal norm(final byte mu, SimpleBigDecimal u,
+ SimpleBigDecimal v)
+ {
+ SimpleBigDecimal norm;
+
+ // s1 = u^2
+ SimpleBigDecimal s1 = u.multiply(u);
+
+ // s2 = u * v
+ SimpleBigDecimal s2 = u.multiply(v);
+
+ // s3 = 2 * v^2
+ SimpleBigDecimal s3 = v.multiply(v).shiftLeft(1);
+
+ if (mu == 1)
+ {
+ norm = s1.add(s2).add(s3);
+ }
+ else if (mu == -1)
+ {
+ norm = s1.subtract(s2).add(s3);
+ }
+ else
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ return norm;
+ }
+
+ /**
+ * Rounds an element <code>&lambda;</code> of <code><b>R</b>[&tau;]</code>
+ * to an element of <code><b>Z</b>[&tau;]</code>, such that their difference
+ * has minimal norm. <code>&lambda;</code> is given as
+ * <code>&lambda; = &lambda;<sub>0</sub> + &lambda;<sub>1</sub>&tau;</code>.
+ * @param lambda0 The component <code>&lambda;<sub>0</sub></code>.
+ * @param lambda1 The component <code>&lambda;<sub>1</sub></code>.
+ * @param mu The parameter <code>&mu;</code> of the elliptic curve. Must
+ * equal 1 or -1.
+ * @return The rounded element of <code><b>Z</b>[&tau;]</code>.
+ * @throws IllegalArgumentException if <code>lambda0</code> and
+ * <code>lambda1</code> do not have same scale.
+ */
+ public static ZTauElement round(SimpleBigDecimal lambda0,
+ SimpleBigDecimal lambda1, byte mu)
+ {
+ int scale = lambda0.getScale();
+ if (lambda1.getScale() != scale)
+ {
+ throw new IllegalArgumentException("lambda0 and lambda1 do not " +
+ "have same scale");
+ }
+
+ if (!((mu == 1) || (mu == -1)))
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger f0 = lambda0.round();
+ BigInteger f1 = lambda1.round();
+
+ SimpleBigDecimal eta0 = lambda0.subtract(f0);
+ SimpleBigDecimal eta1 = lambda1.subtract(f1);
+
+ // eta = 2*eta0 + mu*eta1
+ SimpleBigDecimal eta = eta0.add(eta0);
+ if (mu == 1)
+ {
+ eta = eta.add(eta1);
+ }
+ else
+ {
+ // mu == -1
+ eta = eta.subtract(eta1);
+ }
+
+ // check1 = eta0 - 3*mu*eta1
+ // check2 = eta0 + 4*mu*eta1
+ SimpleBigDecimal threeEta1 = eta1.add(eta1).add(eta1);
+ SimpleBigDecimal fourEta1 = threeEta1.add(eta1);
+ SimpleBigDecimal check1;
+ SimpleBigDecimal check2;
+ if (mu == 1)
+ {
+ check1 = eta0.subtract(threeEta1);
+ check2 = eta0.add(fourEta1);
+ }
+ else
+ {
+ // mu == -1
+ check1 = eta0.add(threeEta1);
+ check2 = eta0.subtract(fourEta1);
+ }
+
+ byte h0 = 0;
+ byte h1 = 0;
+
+ // if eta >= 1
+ if (eta.compareTo(ECConstants.ONE) >= 0)
+ {
+ if (check1.compareTo(MINUS_ONE) < 0)
+ {
+ h1 = mu;
+ }
+ else
+ {
+ h0 = 1;
+ }
+ }
+ else
+ {
+ // eta < 1
+ if (check2.compareTo(ECConstants.TWO) >= 0)
+ {
+ h1 = mu;
+ }
+ }
+
+ // if eta < -1
+ if (eta.compareTo(MINUS_ONE) < 0)
+ {
+ if (check1.compareTo(ECConstants.ONE) >= 0)
+ {
+ h1 = (byte)-mu;
+ }
+ else
+ {
+ h0 = -1;
+ }
+ }
+ else
+ {
+ // eta >= -1
+ if (check2.compareTo(MINUS_TWO) < 0)
+ {
+ h1 = (byte)-mu;
+ }
+ }
+
+ BigInteger q0 = f0.add(BigInteger.valueOf(h0));
+ BigInteger q1 = f1.add(BigInteger.valueOf(h1));
+ return new ZTauElement(q0, q1);
+ }
+
+ /**
+ * Approximate division by <code>n</code>. For an integer
+ * <code>k</code>, the value <code>&lambda; = s k / n</code> is
+ * computed to <code>c</code> bits of accuracy.
+ * @param k The parameter <code>k</code>.
+ * @param s The curve parameter <code>s<sub>0</sub></code> or
+ * <code>s<sub>1</sub></code>.
+ * @param vm The Lucas Sequence element <code>V<sub>m</sub></code>.
+ * @param a The parameter <code>a</code> of the elliptic curve.
+ * @param m The bit length of the finite field
+ * <code><b>F</b><sub>m</sub></code>.
+ * @param c The number of bits of accuracy, i.e. the scale of the returned
+ * <code>SimpleBigDecimal</code>.
+ * @return The value <code>&lambda; = s k / n</code> computed to
+ * <code>c</code> bits of accuracy.
+ */
+ public static SimpleBigDecimal approximateDivisionByN(BigInteger k,
+ BigInteger s, BigInteger vm, byte a, int m, int c)
+ {
+ int _k = (m + 5)/2 + c;
+ BigInteger ns = k.shiftRight(m - _k - 2 + a);
+
+ BigInteger gs = s.multiply(ns);
+
+ BigInteger hs = gs.shiftRight(m);
+
+ BigInteger js = vm.multiply(hs);
+
+ BigInteger gsPlusJs = gs.add(js);
+ BigInteger ls = gsPlusJs.shiftRight(_k-c);
+ if (gsPlusJs.testBit(_k-c-1))
+ {
+ // round up
+ ls = ls.add(ECConstants.ONE);
+ }
+
+ return new SimpleBigDecimal(ls, c);
+ }
+
+ /**
+ * Computes the <code>&tau;</code>-adic NAF (non-adjacent form) of an
+ * element <code>&lambda;</code> of <code><b>Z</b>[&tau;]</code>.
+ * @param mu The parameter <code>&mu;</code> of the elliptic curve.
+ * @param lambda The element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code>.
+ * @return The <code>&tau;</code>-adic NAF of <code>&lambda;</code>.
+ */
+ public static byte[] tauAdicNaf(byte mu, ZTauElement lambda)
+ {
+ if (!((mu == 1) || (mu == -1)))
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger norm = norm(mu, lambda);
+
+ // Ceiling of log2 of the norm
+ int log2Norm = norm.bitLength();
+
+ // If length(TNAF) > 30, then length(TNAF) < log2Norm + 3.52
+ int maxLength = log2Norm > 30 ? log2Norm + 4 : 34;
+
+ // The array holding the TNAF
+ byte[] u = new byte[maxLength];
+ int i = 0;
+
+ // The actual length of the TNAF
+ int length = 0;
+
+ BigInteger r0 = lambda.u;
+ BigInteger r1 = lambda.v;
+
+ while(!((r0.equals(ECConstants.ZERO)) && (r1.equals(ECConstants.ZERO))))
+ {
+ // If r0 is odd
+ if (r0.testBit(0))
+ {
+ u[i] = (byte) ECConstants.TWO.subtract((r0.subtract(r1.shiftLeft(1))).mod(ECConstants.FOUR)).intValue();
+
+ // r0 = r0 - u[i]
+ if (u[i] == 1)
+ {
+ r0 = r0.clearBit(0);
+ }
+ else
+ {
+ // u[i] == -1
+ r0 = r0.add(ECConstants.ONE);
+ }
+ length = i;
+ }
+ else
+ {
+ u[i] = 0;
+ }
+
+ BigInteger t = r0;
+ BigInteger s = r0.shiftRight(1);
+ if (mu == 1)
+ {
+ r0 = r1.add(s);
+ }
+ else
+ {
+ // mu == -1
+ r0 = r1.subtract(s);
+ }
+
+ r1 = t.shiftRight(1).negate();
+ i++;
+ }
+
+ length++;
+
+ // Reduce the TNAF array to its actual length
+ byte[] tnaf = new byte[length];
+ System.arraycopy(u, 0, tnaf, 0, length);
+ return tnaf;
+ }
+
+ /**
+ * Applies the operation <code>&tau;()</code> to an
+ * <code>ECPoint.F2m</code>.
+ * @param p The ECPoint.F2m to which <code>&tau;()</code> is applied.
+ * @return <code>&tau;(p)</code>
+ */
+ public static ECPoint.F2m tau(ECPoint.F2m p)
+ {
+ return p.tau();
+ }
+
+ /**
+ * Returns the parameter <code>&mu;</code> of the elliptic curve.
+ * @param curve The elliptic curve from which to obtain <code>&mu;</code>.
+ * The curve must be a Koblitz curve, i.e. <code>a</code> equals
+ * <code>0</code> or <code>1</code> and <code>b</code> equals
+ * <code>1</code>.
+ * @return <code>&mu;</code> of the elliptic curve.
+ * @throws IllegalArgumentException if the given ECCurve is not a Koblitz
+ * curve.
+ */
+ public static byte getMu(ECCurve.F2m curve)
+ {
+ if (!curve.isKoblitz())
+ {
+ throw new IllegalArgumentException("No Koblitz curve (ABC), TNAF multiplication not possible");
+ }
+
+ if (curve.getA().isZero())
+ {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ /**
+ * Calculates the Lucas Sequence elements <code>U<sub>k-1</sub></code> and
+ * <code>U<sub>k</sub></code> or <code>V<sub>k-1</sub></code> and
+ * <code>V<sub>k</sub></code>.
+ * @param mu The parameter <code>&mu;</code> of the elliptic curve.
+ * @param k The index of the second element of the Lucas Sequence to be
+ * returned.
+ * @param doV If set to true, computes <code>V<sub>k-1</sub></code> and
+ * <code>V<sub>k</sub></code>, otherwise <code>U<sub>k-1</sub></code> and
+ * <code>U<sub>k</sub></code>.
+ * @return An array with 2 elements, containing <code>U<sub>k-1</sub></code>
+ * and <code>U<sub>k</sub></code> or <code>V<sub>k-1</sub></code>
+ * and <code>V<sub>k</sub></code>.
+ */
+ public static BigInteger[] getLucas(byte mu, int k, boolean doV)
+ {
+ if (!((mu == 1) || (mu == -1)))
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger u0;
+ BigInteger u1;
+ BigInteger u2;
+
+ if (doV)
+ {
+ u0 = ECConstants.TWO;
+ u1 = BigInteger.valueOf(mu);
+ }
+ else
+ {
+ u0 = ECConstants.ZERO;
+ u1 = ECConstants.ONE;
+ }
+
+ for (int i = 1; i < k; i++)
+ {
+ // u2 = mu*u1 - 2*u0;
+ BigInteger s = null;
+ if (mu == 1)
+ {
+ s = u1;
+ }
+ else
+ {
+ // mu == -1
+ s = u1.negate();
+ }
+
+ u2 = s.subtract(u0.shiftLeft(1));
+ u0 = u1;
+ u1 = u2;
+// System.out.println(i + ": " + u2);
+// System.out.println();
+ }
+
+ BigInteger[] retVal = {u0, u1};
+ return retVal;
+ }
+
+ /**
+ * Computes the auxiliary value <code>t<sub>w</sub></code>. If the width is
+ * 4, then for <code>mu = 1</code>, <code>t<sub>w</sub> = 6</code> and for
+ * <code>mu = -1</code>, <code>t<sub>w</sub> = 10</code>
+ * @param mu The parameter <code>&mu;</code> of the elliptic curve.
+ * @param w The window width of the WTNAF.
+ * @return the auxiliary value <code>t<sub>w</sub></code>
+ */
+ public static BigInteger getTw(byte mu, int w)
+ {
+ if (w == 4)
+ {
+ if (mu == 1)
+ {
+ return BigInteger.valueOf(6);
+ }
+ else
+ {
+ // mu == -1
+ return BigInteger.valueOf(10);
+ }
+ }
+ else
+ {
+ // For w <> 4, the values must be computed
+ BigInteger[] us = getLucas(mu, w, false);
+ BigInteger twoToW = ECConstants.ZERO.setBit(w);
+ BigInteger u1invert = us[1].modInverse(twoToW);
+ BigInteger tw;
+ tw = ECConstants.TWO.multiply(us[0]).multiply(u1invert).mod(twoToW);
+// System.out.println("mu = " + mu);
+// System.out.println("tw = " + tw);
+ return tw;
+ }
+ }
+
+ /**
+ * Computes the auxiliary values <code>s<sub>0</sub></code> and
+ * <code>s<sub>1</sub></code> used for partial modular reduction.
+ * @param curve The elliptic curve for which to compute
+ * <code>s<sub>0</sub></code> and <code>s<sub>1</sub></code>.
+ * @throws IllegalArgumentException if <code>curve</code> is not a
+ * Koblitz curve (Anomalous Binary Curve, ABC).
+ */
+ public static BigInteger[] getSi(ECCurve.F2m curve)
+ {
+ if (!curve.isKoblitz())
+ {
+ throw new IllegalArgumentException("si is defined for Koblitz curves only");
+ }
+
+ int m = curve.getM();
+ int a = curve.getA().toBigInteger().intValue();
+ byte mu = curve.getMu();
+ int shifts = getShiftsForCofactor(curve.getCofactor());
+ int index = m + 3 - a;
+ BigInteger[] ui = getLucas(mu, index, false);
+ if (mu == 1)
+ {
+ ui[0] = ui[0].negate();
+ ui[1] = ui[1].negate();
+ }
+
+ BigInteger dividend0 = ECConstants.ONE.add(ui[1]).shiftRight(shifts);
+ BigInteger dividend1 = ECConstants.ONE.add(ui[0]).shiftRight(shifts).negate();
+
+ return new BigInteger[] { dividend0, dividend1 };
+ }
+
+ protected static int getShiftsForCofactor(BigInteger h)
+ {
+ if (h != null)
+ {
+ if (h.equals(ECConstants.TWO))
+ {
+ return 1;
+ }
+ if (h.equals(ECConstants.FOUR))
+ {
+ return 2;
+ }
+ }
+
+ throw new IllegalArgumentException("h (Cofactor) must be 2 or 4");
+ }
+
+ /**
+ * Partial modular reduction modulo
+ * <code>(&tau;<sup>m</sup> - 1)/(&tau; - 1)</code>.
+ * @param k The integer to be reduced.
+ * @param m The bitlength of the underlying finite field.
+ * @param a The parameter <code>a</code> of the elliptic curve.
+ * @param s The auxiliary values <code>s<sub>0</sub></code> and
+ * <code>s<sub>1</sub></code>.
+ * @param mu The parameter &mu; of the elliptic curve.
+ * @param c The precision (number of bits of accuracy) of the partial
+ * modular reduction.
+ * @return <code>&rho; := k partmod (&tau;<sup>m</sup> - 1)/(&tau; - 1)</code>
+ */
+ public static ZTauElement partModReduction(BigInteger k, int m, byte a,
+ BigInteger[] s, byte mu, byte c)
+ {
+ // d0 = s[0] + mu*s[1]; mu is either 1 or -1
+ BigInteger d0;
+ if (mu == 1)
+ {
+ d0 = s[0].add(s[1]);
+ }
+ else
+ {
+ d0 = s[0].subtract(s[1]);
+ }
+
+ BigInteger[] v = getLucas(mu, m, true);
+ BigInteger vm = v[1];
+
+ SimpleBigDecimal lambda0 = approximateDivisionByN(
+ k, s[0], vm, a, m, c);
+
+ SimpleBigDecimal lambda1 = approximateDivisionByN(
+ k, s[1], vm, a, m, c);
+
+ ZTauElement q = round(lambda0, lambda1, mu);
+
+ // r0 = n - d0*q0 - 2*s1*q1
+ BigInteger r0 = k.subtract(d0.multiply(q.u)).subtract(
+ BigInteger.valueOf(2).multiply(s[1]).multiply(q.v));
+
+ // r1 = s1*q0 - s0*q1
+ BigInteger r1 = s[1].multiply(q.u).subtract(s[0].multiply(q.v));
+
+ return new ZTauElement(r0, r1);
+ }
+
+ /**
+ * Multiplies a {@link org.spongycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by a <code>BigInteger</code> using the reduced <code>&tau;</code>-adic
+ * NAF (RTNAF) method.
+ * @param p The ECPoint.F2m to multiply.
+ * @param k The <code>BigInteger</code> by which to multiply <code>p</code>.
+ * @return <code>k * p</code>
+ */
+ public static ECPoint.F2m multiplyRTnaf(ECPoint.F2m p, BigInteger k)
+ {
+ ECCurve.F2m curve = (ECCurve.F2m) p.getCurve();
+ int m = curve.getM();
+ byte a = (byte) curve.getA().toBigInteger().intValue();
+ byte mu = curve.getMu();
+ BigInteger[] s = curve.getSi();
+ ZTauElement rho = partModReduction(k, m, a, s, mu, (byte)10);
+
+ return multiplyTnaf(p, rho);
+ }
+
+ /**
+ * Multiplies a {@link org.spongycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by an element <code>&lambda;</code> of <code><b>Z</b>[&tau;]</code>
+ * using the <code>&tau;</code>-adic NAF (TNAF) method.
+ * @param p The ECPoint.F2m to multiply.
+ * @param lambda The element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code>.
+ * @return <code>&lambda; * p</code>
+ */
+ public static ECPoint.F2m multiplyTnaf(ECPoint.F2m p, ZTauElement lambda)
+ {
+ ECCurve.F2m curve = (ECCurve.F2m)p.getCurve();
+ byte mu = curve.getMu();
+ byte[] u = tauAdicNaf(mu, lambda);
+
+ ECPoint.F2m q = multiplyFromTnaf(p, u);
+
+ return q;
+ }
+
+ /**
+ * Multiplies a {@link org.spongycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by an element <code>&lambda;</code> of <code><b>Z</b>[&tau;]</code>
+ * using the <code>&tau;</code>-adic NAF (TNAF) method, given the TNAF
+ * of <code>&lambda;</code>.
+ * @param p The ECPoint.F2m to multiply.
+ * @param u The the TNAF of <code>&lambda;</code>..
+ * @return <code>&lambda; * p</code>
+ */
+ public static ECPoint.F2m multiplyFromTnaf(ECPoint.F2m p, byte[] u)
+ {
+ ECCurve.F2m curve = (ECCurve.F2m)p.getCurve();
+ ECPoint.F2m q = (ECPoint.F2m) curve.getInfinity();
+ for (int i = u.length - 1; i >= 0; i--)
+ {
+ q = tau(q);
+ if (u[i] == 1)
+ {
+ q = (ECPoint.F2m)q.addSimple(p);
+ }
+ else if (u[i] == -1)
+ {
+ q = (ECPoint.F2m)q.subtractSimple(p);
+ }
+ }
+ return q;
+ }
+
+ /**
+ * Computes the <code>[&tau;]</code>-adic window NAF of an element
+ * <code>&lambda;</code> of <code><b>Z</b>[&tau;]</code>.
+ * @param mu The parameter &mu; of the elliptic curve.
+ * @param lambda The element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code> of which to compute the
+ * <code>[&tau;]</code>-adic NAF.
+ * @param width The window width of the resulting WNAF.
+ * @param pow2w 2<sup>width</sup>.
+ * @param tw The auxiliary value <code>t<sub>w</sub></code>.
+ * @param alpha The <code>&alpha;<sub>u</sub></code>'s for the window width.
+ * @return The <code>[&tau;]</code>-adic window NAF of
+ * <code>&lambda;</code>.
+ */
+ public static byte[] tauAdicWNaf(byte mu, ZTauElement lambda,
+ byte width, BigInteger pow2w, BigInteger tw, ZTauElement[] alpha)
+ {
+ if (!((mu == 1) || (mu == -1)))
+ {
+ throw new IllegalArgumentException("mu must be 1 or -1");
+ }
+
+ BigInteger norm = norm(mu, lambda);
+
+ // Ceiling of log2 of the norm
+ int log2Norm = norm.bitLength();
+
+ // If length(TNAF) > 30, then length(TNAF) < log2Norm + 3.52
+ int maxLength = log2Norm > 30 ? log2Norm + 4 + width : 34 + width;
+
+ // The array holding the TNAF
+ byte[] u = new byte[maxLength];
+
+ // 2^(width - 1)
+ BigInteger pow2wMin1 = pow2w.shiftRight(1);
+
+ // Split lambda into two BigIntegers to simplify calculations
+ BigInteger r0 = lambda.u;
+ BigInteger r1 = lambda.v;
+ int i = 0;
+
+ // while lambda <> (0, 0)
+ while (!((r0.equals(ECConstants.ZERO))&&(r1.equals(ECConstants.ZERO))))
+ {
+ // if r0 is odd
+ if (r0.testBit(0))
+ {
+ // uUnMod = r0 + r1*tw mod 2^width
+ BigInteger uUnMod
+ = r0.add(r1.multiply(tw)).mod(pow2w);
+
+ byte uLocal;
+ // if uUnMod >= 2^(width - 1)
+ if (uUnMod.compareTo(pow2wMin1) >= 0)
+ {
+ uLocal = (byte) uUnMod.subtract(pow2w).intValue();
+ }
+ else
+ {
+ uLocal = (byte) uUnMod.intValue();
+ }
+ // uLocal is now in [-2^(width-1), 2^(width-1)-1]
+
+ u[i] = uLocal;
+ boolean s = true;
+ if (uLocal < 0)
+ {
+ s = false;
+ uLocal = (byte)-uLocal;
+ }
+ // uLocal is now >= 0
+
+ if (s)
+ {
+ r0 = r0.subtract(alpha[uLocal].u);
+ r1 = r1.subtract(alpha[uLocal].v);
+ }
+ else
+ {
+ r0 = r0.add(alpha[uLocal].u);
+ r1 = r1.add(alpha[uLocal].v);
+ }
+ }
+ else
+ {
+ u[i] = 0;
+ }
+
+ BigInteger t = r0;
+
+ if (mu == 1)
+ {
+ r0 = r1.add(r0.shiftRight(1));
+ }
+ else
+ {
+ // mu == -1
+ r0 = r1.subtract(r0.shiftRight(1));
+ }
+ r1 = t.shiftRight(1).negate();
+ i++;
+ }
+ return u;
+ }
+
+ /**
+ * Does the precomputation for WTNAF multiplication.
+ * @param p The <code>ECPoint</code> for which to do the precomputation.
+ * @param a The parameter <code>a</code> of the elliptic curve.
+ * @return The precomputation array for <code>p</code>.
+ */
+ public static ECPoint.F2m[] getPreComp(ECPoint.F2m p, byte a)
+ {
+ ECPoint.F2m[] pu;
+ pu = new ECPoint.F2m[16];
+ pu[1] = p;
+ byte[][] alphaTnaf;
+ if (a == 0)
+ {
+ alphaTnaf = Tnaf.alpha0Tnaf;
+ }
+ else
+ {
+ // a == 1
+ alphaTnaf = Tnaf.alpha1Tnaf;
+ }
+
+ int precompLen = alphaTnaf.length;
+ for (int i = 3; i < precompLen; i = i + 2)
+ {
+ pu[i] = Tnaf.multiplyFromTnaf(p, alphaTnaf[i]);
+ }
+
+ p.getCurve().normalizeAll(pu);
+
+ return pu;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/WNafL2RMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/WNafL2RMultiplier.java
new file mode 100644
index 00000000..b9af4da9
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/WNafL2RMultiplier.java
@@ -0,0 +1,96 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the WNAF (Window Non-Adjacent Form) multiplication
+ * algorithm.
+ */
+public class WNafL2RMultiplier extends AbstractECMultiplier
+{
+ /**
+ * Multiplies <code>this</code> by an integer <code>k</code> using the
+ * Window NAF method.
+ * @param k The integer by which <code>this</code> is multiplied.
+ * @return A new <code>ECPoint</code> which equals <code>this</code>
+ * multiplied by <code>k</code>.
+ */
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ // Clamp the window width in the range [2, 16]
+ int width = Math.max(2, Math.min(16, getWindowSize(k.bitLength())));
+
+ WNafPreCompInfo wnafPreCompInfo = WNafUtil.precompute(p, width, true);
+ ECPoint[] preComp = wnafPreCompInfo.getPreComp();
+ ECPoint[] preCompNeg = wnafPreCompInfo.getPreCompNeg();
+
+ int[] wnaf = WNafUtil.generateCompactWindowNaf(width, k);
+
+ ECPoint R = p.getCurve().getInfinity();
+
+ int i = wnaf.length;
+
+ /*
+ * NOTE: We try to optimize the first window using the precomputed points to substitute an
+ * addition for 2 or more doublings.
+ */
+ if (i > 1)
+ {
+ int wi = wnaf[--i];
+ int digit = wi >> 16, zeroes = wi & 0xFFFF;
+
+ int n = Math.abs(digit);
+ ECPoint[] table = digit < 0 ? preCompNeg : preComp;
+
+ // Optimization can only be used for values in the lower half of the table
+ if ((n << 2) < (1 << width))
+ {
+ int highest = LongArray.bitLengths[n];
+
+ // 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;
+ R = table[i1 >>> 1].add(table[i2 >>> 1]);
+
+ zeroes -= scale;
+
+// System.out.println("Optimized: 2^" + scale + " * " + n + " = " + i1 + " + " + i2);
+ }
+ else
+ {
+ R = table[n >>> 1];
+ }
+
+ R = R.timesPow2(zeroes);
+ }
+
+ while (i > 0)
+ {
+ int wi = wnaf[--i];
+ int digit = wi >> 16, zeroes = wi & 0xFFFF;
+
+ int n = Math.abs(digit);
+ ECPoint[] table = digit < 0 ? preCompNeg : preComp;
+ ECPoint r = table[n >>> 1];
+
+ R = R.twicePlus(r);
+ R = R.timesPow2(zeroes);
+ }
+
+ return R;
+ }
+
+ /**
+ * Determine window width to use for a scalar multiplication of the given size.
+ *
+ * @param bits the bit-length of the scalar to multiply by
+ * @return the window size to use
+ */
+ protected int getWindowSize(int bits)
+ {
+ return WNafUtil.getWindowSize(bits);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/WNafPreCompInfo.java b/core/src/main/java/org/spongycastle/math/ec/WNafPreCompInfo.java
new file mode 100644
index 00000000..2eaea940
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/WNafPreCompInfo.java
@@ -0,0 +1,56 @@
+package org.spongycastle.math.ec;
+
+/**
+ * Class holding precomputation data for the WNAF (Window Non-Adjacent Form)
+ * algorithm.
+ */
+public class WNafPreCompInfo implements PreCompInfo
+{
+ /**
+ * Array holding the precomputed <code>ECPoint</code>s used for a Window
+ * NAF multiplication.
+ */
+ protected ECPoint[] preComp = null;
+
+ /**
+ * Array holding the negations of the precomputed <code>ECPoint</code>s used
+ * for a Window NAF multiplication.
+ */
+ 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.
+ */
+ protected ECPoint twice = null;
+
+ public ECPoint[] getPreComp()
+ {
+ return preComp;
+ }
+
+ public void setPreComp(ECPoint[] preComp)
+ {
+ this.preComp = preComp;
+ }
+
+ public ECPoint[] getPreCompNeg()
+ {
+ return preCompNeg;
+ }
+
+ public void setPreCompNeg(ECPoint[] preCompNeg)
+ {
+ this.preCompNeg = preCompNeg;
+ }
+
+ public ECPoint getTwice()
+ {
+ return twice;
+ }
+
+ public void setTwice(ECPoint twice)
+ {
+ this.twice = twice;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/WNafUtil.java b/core/src/main/java/org/spongycastle/math/ec/WNafUtil.java
new file mode 100644
index 00000000..4e0c5869
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/WNafUtil.java
@@ -0,0 +1,485 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public abstract class WNafUtil
+{
+ 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)
+ {
+ if ((k.bitLength() >>> 16) != 0)
+ {
+ throw new IllegalArgumentException("'k' must have bitlength < 2^16");
+ }
+ if (k.signum() == 0)
+ {
+ return EMPTY_INTS;
+ }
+
+ BigInteger _3k = k.shiftLeft(1).add(k);
+
+ int bits = _3k.bitLength();
+ int[] naf = new int[bits >> 1];
+
+ BigInteger diff = _3k.xor(k);
+
+ int highBit = bits - 1, length = 0, zeroes = 0;
+ for (int i = 1; i < highBit; ++i)
+ {
+ if (!diff.testBit(i))
+ {
+ ++zeroes;
+ continue;
+ }
+
+ 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);
+ }
+
+ return naf;
+ }
+
+ public static int[] generateCompactWindowNaf(int width, BigInteger k)
+ {
+ if (width == 2)
+ {
+ return generateCompactNaf(k);
+ }
+
+ if (width < 2 || width > 16)
+ {
+ throw new IllegalArgumentException("'width' must be in the range [2, 16]");
+ }
+ if ((k.bitLength() >>> 16) != 0)
+ {
+ throw new IllegalArgumentException("'k' must have bitlength < 2^16");
+ }
+ if (k.signum() == 0)
+ {
+ return EMPTY_INTS;
+ }
+
+ int[] wnaf = new int[k.bitLength() / width + 1];
+
+ // 2^width and a mask and sign bit set accordingly
+ int pow2 = 1 << width;
+ int mask = pow2 - 1;
+ int sign = pow2 >>> 1;
+
+ boolean carry = false;
+ int length = 0, pos = 0;
+
+ while (pos <= k.bitLength())
+ {
+ if (k.testBit(pos) == carry)
+ {
+ ++pos;
+ continue;
+ }
+
+ k = k.shiftRight(pos);
+
+ int digit = k.intValue() & mask;
+ if (carry)
+ {
+ ++digit;
+ }
+
+ carry = (digit & sign) != 0;
+ if (carry)
+ {
+ digit -= pow2;
+ }
+
+ int zeroes = length > 0 ? pos - 1 : pos;
+ wnaf[length++] = (digit << 16) | zeroes;
+ pos = width;
+ }
+
+ // Reduce the WNAF array to its actual length
+ if (wnaf.length > length)
+ {
+ wnaf = trim(wnaf, length);
+ }
+
+ return wnaf;
+ }
+
+ public static byte[] generateJSF(BigInteger g, BigInteger h)
+ {
+ int digits = Math.max(g.bitLength(), h.bitLength()) + 1;
+ byte[] jsf = new byte[digits];
+
+ BigInteger k0 = g, k1 = h;
+ int j = 0, d0 = 0, d1 = 0;
+
+ int offset = 0;
+ while ((d0 | d1) != 0 || k0.bitLength() > offset || k1.bitLength() > offset)
+ {
+ int n0 = ((k0.intValue() >>> offset) + d0) & 7, n1 = ((k1.intValue() >>> offset) + d1) & 7;
+
+ int u0 = n0 & 1;
+ if (u0 != 0)
+ {
+ u0 -= (n0 & 2);
+ if ((n0 + u0) == 4 && (n1 & 3) == 2)
+ {
+ u0 = -u0;
+ }
+ }
+
+ int u1 = n1 & 1;
+ if (u1 != 0)
+ {
+ u1 -= (n1 & 2);
+ if ((n1 + u1) == 4 && (n0 & 3) == 2)
+ {
+ u1 = -u1;
+ }
+ }
+
+ if ((d0 << 1) == 1 + u0)
+ {
+ d0 ^= 1;
+ }
+ if ((d1 << 1) == 1 + u1)
+ {
+ d1 ^= 1;
+ }
+
+ if (++offset == 30)
+ {
+ offset = 0;
+ k0 = k0.shiftRight(30);
+ k1 = k1.shiftRight(30);
+ }
+
+ jsf[j++] = (byte)((u0 << 4) | (u1 & 0xF));
+ }
+
+ // Reduce the JSF array to its actual length
+ if (jsf.length > j)
+ {
+ jsf = trim(jsf, j);
+ }
+
+ return jsf;
+ }
+
+ 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];
+
+ BigInteger diff = _3k.xor(k);
+
+ 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;
+ }
+
+ /**
+ * Computes the Window NAF (non-adjacent Form) of an integer.
+ * @param width The width <code>w</code> of the Window NAF. The width is
+ * defined as the minimal number <code>w</code>, such that for any
+ * <code>w</code> consecutive digits in the resulting representation, at
+ * most one is non-zero.
+ * @param k The integer of which the Window NAF is computed.
+ * @return The Window NAF of the given width, such that the following holds:
+ * <code>k = &sum;<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup>
+ * </code>, where the <code>k<sub>i</sub></code> denote the elements of the
+ * returned <code>byte[]</code>.
+ */
+ public static byte[] generateWindowNaf(int width, BigInteger k)
+ {
+ if (width == 2)
+ {
+ return generateNaf(k);
+ }
+
+ if (width < 2 || width > 8)
+ {
+ 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];
+
+ // 2^width and a mask and sign bit set accordingly
+ int pow2 = 1 << width;
+ int mask = pow2 - 1;
+ int sign = pow2 >>> 1;
+
+ boolean carry = false;
+ int length = 0, pos = 0;
+
+ while (pos <= k.bitLength())
+ {
+ if (k.testBit(pos) == carry)
+ {
+ ++pos;
+ continue;
+ }
+
+ k = k.shiftRight(pos);
+
+ int digit = k.intValue() & mask;
+ if (carry)
+ {
+ ++digit;
+ }
+
+ carry = (digit & sign) != 0;
+ if (carry)
+ {
+ digit -= pow2;
+ }
+
+ length += (length > 0) ? pos - 1 : pos;
+ wnaf[length++] = (byte)digit;
+ pos = width;
+ }
+
+ // Reduce the WNAF array to its actual length
+ if (wnaf.length > length)
+ {
+ wnaf = trim(wnaf, length);
+ }
+
+ 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))
+ {
+ return (WNafPreCompInfo)preCompInfo;
+ }
+
+ return new WNafPreCompInfo();
+ }
+
+ /**
+ * Determine window width to use for a scalar multiplication of the given size.
+ *
+ * @param bits the bit-length of the scalar to multiply by
+ * @return the window size to use
+ */
+ public static int getWindowSize(int bits)
+ {
+ return getWindowSize(bits, DEFAULT_WINDOW_SIZE_CUTOFFS);
+ }
+
+ /**
+ * Determine window width to use for a scalar multiplication of the given size.
+ *
+ * @param bits the bit-length of the scalar to multiply by
+ * @param windowSizeCutoffs a monotonically increasing list of bit sizes at which to increment the window width
+ * @return the window size to use
+ */
+ public static int getWindowSize(int bits, int[] windowSizeCutoffs)
+ {
+ int w = 0;
+ for (; w < windowSizeCutoffs.length; ++w)
+ {
+ if (bits < windowSizeCutoffs[w])
+ {
+ break;
+ }
+ }
+ 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, PRECOMP_NAME));
+
+ ECPoint[] preComp = wnafPreCompInfo.getPreComp();
+ if (preComp == null)
+ {
+ preComp = new ECPoint[]{ p };
+ }
+
+ int preCompLen = preComp.length;
+ int reqPreCompLen = 1 << Math.max(0, width - 2);
+
+ if (preCompLen < reqPreCompLen)
+ {
+ preComp = resizeTable(preComp, reqPreCompLen);
+ if (reqPreCompLen == 2)
+ {
+ preComp[1] = preComp[0].threeTimes();
+ }
+ else
+ {
+ 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]);
+ }
+ }
+
+ /*
+ * Having oft-used operands in affine form makes operations faster.
+ */
+ c.normalizeAll(preComp);
+ }
+
+ wnafPreCompInfo.setPreComp(preComp);
+
+ if (includeNegated)
+ {
+ ECPoint[] preCompNeg = wnafPreCompInfo.getPreCompNeg();
+
+ int pos;
+ if (preCompNeg == null)
+ {
+ pos = 0;
+ preCompNeg = new ECPoint[reqPreCompLen];
+ }
+ else
+ {
+ pos = preCompNeg.length;
+ if (pos < reqPreCompLen)
+ {
+ preCompNeg = resizeTable(preCompNeg, reqPreCompLen);
+ }
+ }
+
+ while (pos < reqPreCompLen)
+ {
+ preCompNeg[pos] = preComp[pos].negate();
+ ++pos;
+ }
+
+ wnafPreCompInfo.setPreCompNeg(preCompNeg);
+ }
+
+ c.setPreCompInfo(p, PRECOMP_NAME, wnafPreCompInfo);
+
+ return wnafPreCompInfo;
+ }
+
+ private static byte[] trim(byte[] a, int length)
+ {
+ byte[] result = new byte[length];
+ System.arraycopy(a, 0, result, 0, result.length);
+ return result;
+ }
+
+ private static int[] trim(int[] a, int length)
+ {
+ int[] result = new int[length];
+ System.arraycopy(a, 0, result, 0, result.length);
+ return result;
+ }
+
+ private static ECPoint[] resizeTable(ECPoint[] a, int length)
+ {
+ ECPoint[] result = new ECPoint[length];
+ System.arraycopy(a, 0, result, 0, a.length);
+ return result;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/WTauNafMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/WTauNafMultiplier.java
new file mode 100644
index 00000000..068c252f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/WTauNafMultiplier.java
@@ -0,0 +1,114 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the WTNAF (Window
+ * <code>&tau;</code>-adic Non-Adjacent Form) algorithm.
+ */
+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.spongycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by <code>k</code> using the reduced <code>&tau;</code>-adic NAF (RTNAF)
+ * method.
+ * @param p The ECPoint.F2m to multiply.
+ * @param k The integer by which to multiply <code>k</code>.
+ * @return <code>p</code> multiplied by <code>k</code>.
+ */
+ protected ECPoint multiplyPositive(ECPoint point, BigInteger k)
+ {
+ if (!(point instanceof ECPoint.F2m))
+ {
+ throw new IllegalArgumentException("Only ECPoint.F2m can be " +
+ "used in WTauNafMultiplier");
+ }
+
+ ECPoint.F2m p = (ECPoint.F2m)point;
+ ECCurve.F2m curve = (ECCurve.F2m)p.getCurve();
+ int m = curve.getM();
+ byte a = curve.getA().toBigInteger().byteValue();
+ byte mu = curve.getMu();
+ BigInteger[] s = curve.getSi();
+
+ ZTauElement rho = Tnaf.partModReduction(k, m, a, s, mu, (byte)10);
+
+ return multiplyWTnaf(p, rho, curve.getPreCompInfo(p, PRECOMP_NAME), a, mu);
+ }
+
+ /**
+ * Multiplies a {@link org.spongycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by an element <code>&lambda;</code> of <code><b>Z</b>[&tau;]</code> using
+ * the <code>&tau;</code>-adic NAF (TNAF) method.
+ * @param p The ECPoint.F2m to multiply.
+ * @param lambda The element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code> of which to compute the
+ * <code>[&tau;]</code>-adic NAF.
+ * @return <code>p</code> multiplied by <code>&lambda;</code>.
+ */
+ private ECPoint.F2m multiplyWTnaf(ECPoint.F2m p, ZTauElement lambda,
+ PreCompInfo preCompInfo, byte a, byte mu)
+ {
+ 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);
+
+ return multiplyFromWTnaf(p, u, preCompInfo);
+ }
+
+ /**
+ * Multiplies a {@link org.spongycastle.math.ec.ECPoint.F2m ECPoint.F2m}
+ * by an element <code>&lambda;</code> of <code><b>Z</b>[&tau;]</code>
+ * using the window <code>&tau;</code>-adic NAF (TNAF) method, given the
+ * WTNAF of <code>&lambda;</code>.
+ * @param p The ECPoint.F2m to multiply.
+ * @param u The the WTNAF of <code>&lambda;</code>..
+ * @return <code>&lambda; * p</code>
+ */
+ 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();
+
+ ECPoint.F2m[] pu;
+ if ((preCompInfo == null) || !(preCompInfo instanceof WTauNafPreCompInfo))
+ {
+ pu = Tnaf.getPreComp(p, a);
+
+ WTauNafPreCompInfo pre = new WTauNafPreCompInfo();
+ pre.setPreComp(pu);
+ curve.setPreCompInfo(p, PRECOMP_NAME, pre);
+ }
+ else
+ {
+ pu = ((WTauNafPreCompInfo)preCompInfo).getPreComp();
+ }
+
+ // q = infinity
+ ECPoint.F2m q = (ECPoint.F2m) p.getCurve().getInfinity();
+ for (int i = u.length - 1; i >= 0; i--)
+ {
+ q = Tnaf.tau(q);
+ byte ui = u[i];
+ if (ui != 0)
+ {
+ if (ui > 0)
+ {
+ q = q.addSimple(pu[ui]);
+ }
+ else
+ {
+ q = q.subtractSimple(pu[-ui]);
+ }
+ }
+ }
+
+ return q;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/WTauNafPreCompInfo.java b/core/src/main/java/org/spongycastle/math/ec/WTauNafPreCompInfo.java
new file mode 100644
index 00000000..ed7d4ceb
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/WTauNafPreCompInfo.java
@@ -0,0 +1,26 @@
+package org.spongycastle.math.ec;
+
+/**
+ * Class holding precomputation data for the WTNAF (Window
+ * <code>&tau;</code>-adic Non-Adjacent Form) algorithm.
+ */
+public class WTauNafPreCompInfo implements PreCompInfo
+{
+ /**
+ * Array holding the precomputed <code>ECPoint.F2m</code>s used for the
+ * WTNAF multiplication in <code>
+ * {@link org.spongycastle.math.ec.multiplier.WTauNafMultiplier.multiply()
+ * WTauNafMultiplier.multiply()}</code>.
+ */
+ protected ECPoint.F2m[] preComp = null;
+
+ public ECPoint.F2m[] getPreComp()
+ {
+ return preComp;
+ }
+
+ public void setPreComp(ECPoint.F2m[] preComp)
+ {
+ this.preComp = preComp;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/ZSignedDigitL2RMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/ZSignedDigitL2RMultiplier.java
new file mode 100644
index 00000000..543bb581
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ZSignedDigitL2RMultiplier.java
@@ -0,0 +1,29 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class ZSignedDigitL2RMultiplier extends AbstractECMultiplier
+{
+ /**
+ * 'Zeroless' Signed Digit Left-to-Right.
+ */
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ ECPoint addP = p.normalize(), subP = addP.negate();
+
+ ECPoint R0 = addP;
+
+ int n = k.bitLength();
+ int s = k.getLowestSetBit();
+
+ int i = n;
+ while (--i > s)
+ {
+ R0 = R0.twicePlus(k.testBit(i) ? addP : subP);
+ }
+
+ R0 = R0.timesPow2(s);
+
+ return R0;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/ZSignedDigitR2LMultiplier.java b/core/src/main/java/org/spongycastle/math/ec/ZSignedDigitR2LMultiplier.java
new file mode 100644
index 00000000..e9147ea7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ZSignedDigitR2LMultiplier.java
@@ -0,0 +1,30 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class ZSignedDigitR2LMultiplier extends AbstractECMultiplier
+{
+ /**
+ * 'Zeroless' Signed Digit Right-to-Left.
+ */
+ protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+ {
+ ECPoint R0 = p.getCurve().getInfinity(), R1 = p;
+
+ int n = k.bitLength();
+ int s = k.getLowestSetBit();
+
+ R1 = R1.timesPow2(s);
+
+ int i = s;
+ while (++i < n)
+ {
+ R0 = R0.add(k.testBit(i) ? R1 : R1.negate());
+ R1 = R1.twice();
+ }
+
+ R0 = R0.add(R1);
+
+ return R0;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/ZTauElement.java b/core/src/main/java/org/spongycastle/math/ec/ZTauElement.java
new file mode 100644
index 00000000..3fd5e21f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/ZTauElement.java
@@ -0,0 +1,37 @@
+package org.spongycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class representing an element of <code><b>Z</b>[&tau;]</code>. Let
+ * <code>&lambda;</code> be an element of <code><b>Z</b>[&tau;]</code>. Then
+ * <code>&lambda;</code> is given as <code>&lambda; = u + v&tau;</code>. The
+ * components <code>u</code> and <code>v</code> may be used directly, there
+ * are no accessor methods.
+ * Immutable class.
+ */
+class ZTauElement
+{
+ /**
+ * The &quot;real&quot; part of <code>&lambda;</code>.
+ */
+ public final BigInteger u;
+
+ /**
+ * The &quot;<code>&tau;</code>-adic&quot; part of <code>&lambda;</code>.
+ */
+ public final BigInteger v;
+
+ /**
+ * Constructor for an element <code>&lambda;</code> of
+ * <code><b>Z</b>[&tau;]</code>.
+ * @param u The &quot;real&quot; part of <code>&lambda;</code>.
+ * @param v The &quot;<code>&tau;</code>-adic&quot; part of
+ * <code>&lambda;</code>.
+ */
+ public ZTauElement(BigInteger u, BigInteger v)
+ {
+ this.u = u;
+ this.v = v;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519.java b/core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519.java
new file mode 100644
index 00000000..c7d77397
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519.java
@@ -0,0 +1,80 @@
+package org.spongycastle.math.ec.custom.djb;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.math.raw.Nat256;
+import org.spongycastle.util.encoders.Hex;
+
+public class Curve25519 extends ECCurve.AbstractFp
+{
+ public static final BigInteger q = Nat256.toBigInteger(Curve25519Field.P);
+
+ private static final int Curve25519_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED;
+
+ protected Curve25519Point infinity;
+
+ public Curve25519()
+ {
+ super(q);
+
+ this.infinity = new Curve25519Point(this, null, null);
+
+ this.a = fromBigInteger(new BigInteger(1,
+ Hex.decode("2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA984914A144")));
+ this.b = fromBigInteger(new BigInteger(1,
+ Hex.decode("7B425ED097B425ED097B425ED097B425ED097B425ED097B4260B5E9C7710C864")));
+ this.order = new BigInteger(1, Hex.decode("1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"));
+ this.cofactor = BigInteger.valueOf(8);
+
+ this.coord = Curve25519_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new Curve25519();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_JACOBIAN_MODIFIED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public int getFieldSize()
+ {
+ return q.bitLength();
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new Curve25519FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new Curve25519Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new Curve25519Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519Field.java b/core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519Field.java
new file mode 100644
index 00000000..2fe484e1
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519Field.java
@@ -0,0 +1,254 @@
+package org.spongycastle.math.ec.custom.djb;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.math.raw.Nat256;
+
+public class Curve25519Field
+{
+ private static final long M = 0xFFFFFFFFL;
+
+ // 2^255 - 2^4 - 2^1 - 1
+ static final int[] P = new int[]{ 0xFFFFFFED, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x7FFFFFFF };
+ private static final int P7 = 0x7FFFFFFF;
+ private static final int[] PExt = new int[]{ 0x00000169, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFED, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x3FFFFFFF };
+ private static final int PInv = 0x13;
+
+ public static void add(int[] x, int[] y, int[] z)
+ {
+ Nat256.add(x, y, z);
+ if (Nat256.gte(z, P))
+ {
+ subPFrom(z);
+ }
+ }
+
+ public static void addExt(int[] xx, int[] yy, int[] zz)
+ {
+ Nat.add(16, xx, yy, zz);
+ if (Nat.gte(16, zz, PExt))
+ {
+ subPExtFrom(zz);
+ }
+ }
+
+ public static void addOne(int[] x, int[] z)
+ {
+ Nat.inc(8, x, z);
+ if (Nat256.gte(z, P))
+ {
+ subPFrom(z);
+ }
+ }
+
+ public static int[] fromBigInteger(BigInteger x)
+ {
+ int[] z = Nat256.fromBigInteger(x);
+ while (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
+ {
+ Nat256.add(x, P, z);
+ Nat.shiftDownBit(8, z, 0);
+ }
+ }
+
+ 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)
+ {
+ Nat256.mulAddTo(x, y, zz);
+ if (Nat.gte(16, zz, PExt))
+ {
+ subPExtFrom(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)
+ {
+// assert xx[15] >>> 30 == 0;
+
+ int xx07 = xx[7];
+ Nat.shiftUpBit(8, xx, 8, xx07, z, 0);
+ int c = Nat256.mulByWordAddTo(PInv, xx, z) << 1;
+ int z7 = z[7];
+ c += (z7 >>> 31) - (xx07 >>> 31);
+ z7 &= P7;
+ z7 += Nat.addWordTo(7, c * PInv, z);
+ z[7] = z7;
+ if (Nat256.gte(z, P))
+ {
+ subPFrom(z);
+ }
+ }
+
+ public static void reduce27(int x, int[] z)
+ {
+// assert x >>> 26 == 0;
+
+ int z7 = z[7];
+ int c = (x << 1 | z7 >>> 31);
+ z7 &= P7;
+ z7 += Nat.addWordTo(7, c * PInv, z);
+ z[7] = z7;
+ if (Nat256.gte(z, P))
+ {
+ subPFrom(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)
+ {
+ addPTo(z);
+ }
+ }
+
+ public static void subtractExt(int[] xx, int[] yy, int[] zz)
+ {
+ int c = Nat.sub(16, xx, yy, zz);
+ if (c != 0)
+ {
+ addPExtTo(zz);
+ }
+ }
+
+ public static void twice(int[] x, int[] z)
+ {
+ Nat.shiftUpBit(8, x, 0, z);
+ if (Nat256.gte(z, P))
+ {
+ subPFrom(z);
+ }
+ }
+
+ private static int addPTo(int[] z)
+ {
+ long c = (z[0] & M) - PInv;
+ z[0] = (int)c;
+ c >>= 32;
+ if (c != 0)
+ {
+ c = Nat.decAt(7, z, 1);
+ }
+ c += (z[7] & M) + ((P7 + 1) & M);
+ z[7] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ private static int addPExtTo(int[] zz)
+ {
+ long c = (zz[0] & M) + (PExt[0] & M);
+ zz[0] = (int)c;
+ c >>= 32;
+ if (c != 0)
+ {
+ c = Nat.incAt(8, zz, 1);
+ }
+ c += (zz[8] & M) - PInv;
+ zz[8] = (int)c;
+ c >>= 32;
+ if (c != 0)
+ {
+ c = Nat.decAt(15, zz, 9);
+ }
+ c += (zz[15] & M) + ((PExt[15] + 1) & M);
+ zz[15] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ private static int subPFrom(int[] z)
+ {
+ long c = (z[0] & M) + PInv;
+ z[0] = (int)c;
+ c >>= 32;
+ if (c != 0)
+ {
+ c = Nat.incAt(7, z, 1);
+ }
+ c += (z[7] & M) - ((P7 + 1) & M);
+ z[7] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ private static int subPExtFrom(int[] zz)
+ {
+ long c = (zz[0] & M) - (PExt[0] & M);
+ zz[0] = (int)c;
+ c >>= 32;
+ if (c != 0)
+ {
+ c = Nat.decAt(8, zz, 1);
+ }
+ c += (zz[8] & M) + PInv;
+ zz[8] = (int)c;
+ c >>= 32;
+ if (c != 0)
+ {
+ c = Nat.incAt(15, zz, 9);
+ }
+ c += (zz[15] & M) - ((PExt[15] + 1) & M);
+ zz[15] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519FieldElement.java b/core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519FieldElement.java
new file mode 100644
index 00000000..a5d5cb7b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519FieldElement.java
@@ -0,0 +1,234 @@
+package org.spongycastle.math.ec.custom.djb;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.raw.Mod;
+import org.spongycastle.math.raw.Nat256;
+import org.spongycastle.util.Arrays;
+
+public class Curve25519FieldElement extends ECFieldElement
+{
+ public static final BigInteger Q = Curve25519.q;
+
+ // Calculated as ECConstants.TWO.modPow(Q.shiftRight(2), Q)
+ private static final int[] PRECOMP_POW2 = new int[]{ 0x4a0ea0b0, 0xc4ee1b27, 0xad2fe478, 0x2f431806,
+ 0x3dfbd7a7, 0x2b4d0099, 0x4fc1df0b, 0x2b832480 };
+
+ protected int[] x;
+
+ public Curve25519FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0)
+ {
+ throw new IllegalArgumentException("x value invalid for Curve25519FieldElement");
+ }
+
+ this.x = Curve25519Field.fromBigInteger(x);
+ }
+
+ public Curve25519FieldElement()
+ {
+ this.x = Nat256.create();
+ }
+
+ protected Curve25519FieldElement(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 "Curve25519Field";
+ }
+
+ public int getFieldSize()
+ {
+ return Q.bitLength();
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ int[] z = Nat256.create();
+ Curve25519Field.add(x, ((Curve25519FieldElement)b).x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ int[] z = Nat256.create();
+ Curve25519Field.addOne(x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ int[] z = Nat256.create();
+ Curve25519Field.subtract(x, ((Curve25519FieldElement)b).x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ int[] z = Nat256.create();
+ Curve25519Field.multiply(x, ((Curve25519FieldElement)b).x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+// return multiply(b.invert());
+ int[] z = Nat256.create();
+ Mod.invert(Curve25519Field.P, ((Curve25519FieldElement)b).x, z);
+ Curve25519Field.multiply(z, x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public ECFieldElement negate()
+ {
+ int[] z = Nat256.create();
+ Curve25519Field.negate(x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public ECFieldElement square()
+ {
+ int[] z = Nat256.create();
+ Curve25519Field.square(x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+// return new Curve25519FieldElement(toBigInteger().modInverse(Q));
+ int[] z = Nat256.create();
+ Mod.invert(Curve25519Field.P, x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ /**
+ * 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^252 - 2^1 (i.e. m + 1)
+ *
+ * Breaking up the exponent's binary representation into "repunits", we get:
+ * { 251 1s } { 1 0s }
+ *
+ * Therefore we need an addition chain containing 251 (the lengths of the repunits)
+ * We use: 1, 2, 3, 4, 7, 11, 15, 30, 60, 120, 131, [251]
+ */
+
+ int[] x1 = this.x;
+ if (Nat256.isZero(x1) || Nat256.isOne(x1))
+ {
+ return this;
+ }
+
+ int[] x2 = Nat256.create();
+ Curve25519Field.square(x1, x2);
+ Curve25519Field.multiply(x2, x1, x2);
+ int[] x3 = x2;
+ Curve25519Field.square(x2, x3);
+ Curve25519Field.multiply(x3, x1, x3);
+ int[] x4 = Nat256.create();
+ Curve25519Field.square(x3, x4);
+ Curve25519Field.multiply(x4, x1, x4);
+ int[] x7 = Nat256.create();
+ Curve25519Field.squareN(x4, 3, x7);
+ Curve25519Field.multiply(x7, x3, x7);
+ int[] x11 = x3;
+ Curve25519Field.squareN(x7, 4, x11);
+ Curve25519Field.multiply(x11, x4, x11);
+ int[] x15 = x7;
+ Curve25519Field.squareN(x11, 4, x15);
+ Curve25519Field.multiply(x15, x4, x15);
+ int[] x30 = x4;
+ Curve25519Field.squareN(x15, 15, x30);
+ Curve25519Field.multiply(x30, x15, x30);
+ int[] x60 = x15;
+ Curve25519Field.squareN(x30, 30, x60);
+ Curve25519Field.multiply(x60, x30, x60);
+ int[] x120 = x30;
+ Curve25519Field.squareN(x60, 60, x120);
+ Curve25519Field.multiply(x120, x60, x120);
+ int[] x131 = x60;
+ Curve25519Field.squareN(x120, 11, x131);
+ Curve25519Field.multiply(x131, x11, x131);
+ int[] x251 = x11;
+ Curve25519Field.squareN(x131, 120, x251);
+ Curve25519Field.multiply(x251, x120, x251);
+
+ int[] t1 = x251;
+ Curve25519Field.square(t1, t1);
+
+ int[] t2 = x120;
+ Curve25519Field.square(t1, t2);
+
+ if (Nat256.eq(x1, t2))
+ {
+ return new Curve25519FieldElement(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
+ */
+ Curve25519Field.multiply(t1, PRECOMP_POW2, t1);
+
+ Curve25519Field.square(t1, t2);
+
+ if (Nat256.eq(x1, t2))
+ {
+ return new Curve25519FieldElement(t1);
+ }
+
+ return null;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof Curve25519FieldElement))
+ {
+ return false;
+ }
+
+ Curve25519FieldElement o = (Curve25519FieldElement)other;
+ return Nat256.eq(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return Q.hashCode() ^ Arrays.hashCode(x, 0, 8);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519Point.java b/core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519Point.java
new file mode 100644
index 00000000..a8b98a93
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/djb/Curve25519Point.java
@@ -0,0 +1,348 @@
+package org.spongycastle.math.ec.custom.djb;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.math.raw.Nat256;
+
+public class Curve25519Point 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 Curve25519Point(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 Curve25519Point(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;
+ }
+
+ Curve25519Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new Curve25519Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getZCoord(int index)
+ {
+ if (index == 1)
+ {
+ return getJacobianModifiedW();
+ }
+
+ return super.getZCoord(index);
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+ if (this == b)
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ Curve25519FieldElement X1 = (Curve25519FieldElement)this.x, Y1 = (Curve25519FieldElement)this.y,
+ Z1 = (Curve25519FieldElement)this.zs[0];
+ Curve25519FieldElement X2 = (Curve25519FieldElement)b.getXCoord(), Y2 = (Curve25519FieldElement)b.getYCoord(),
+ Z2 = (Curve25519FieldElement)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;
+ Curve25519Field.square(Z1.x, S2);
+
+ U2 = t2;
+ Curve25519Field.multiply(S2, X2.x, U2);
+
+ Curve25519Field.multiply(S2, Z1.x, S2);
+ Curve25519Field.multiply(S2, Y2.x, S2);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ int[] U1, S1;
+ if (Z2IsOne)
+ {
+ U1 = X1.x;
+ S1 = Y1.x;
+ }
+ else
+ {
+ S1 = t4;
+ Curve25519Field.square(Z2.x, S1);
+
+ U1 = tt1;
+ Curve25519Field.multiply(S1, X1.x, U1);
+
+ Curve25519Field.multiply(S1, Z2.x, S1);
+ Curve25519Field.multiply(S1, Y1.x, S1);
+ }
+
+ int[] H = Nat256.create();
+ Curve25519Field.subtract(U1, U2, H);
+
+ int[] R = t2;
+ Curve25519Field.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 = Nat256.create();
+ Curve25519Field.square(H, HSquared);
+
+ int[] G = Nat256.create();
+ Curve25519Field.multiply(HSquared, H, G);
+
+ int[] V = t3;
+ Curve25519Field.multiply(HSquared, U1, V);
+
+ Curve25519Field.negate(G, G);
+ Nat256.mul(S1, G, tt1);
+
+ c = Nat256.addBothTo(V, V, G);
+ Curve25519Field.reduce27(c, G);
+
+ Curve25519FieldElement X3 = new Curve25519FieldElement(t4);
+ Curve25519Field.square(R, X3.x);
+ Curve25519Field.subtract(X3.x, G, X3.x);
+
+ Curve25519FieldElement Y3 = new Curve25519FieldElement(G);
+ Curve25519Field.subtract(V, X3.x, Y3.x);
+ Curve25519Field.multiplyAddToExt(Y3.x, R, tt1);
+ Curve25519Field.reduce(tt1, Y3.x);
+
+ Curve25519FieldElement Z3 = new Curve25519FieldElement(H);
+ if (!Z1IsOne)
+ {
+ Curve25519Field.multiply(Z3.x, Z1.x, Z3.x);
+ }
+ if (!Z2IsOne)
+ {
+ Curve25519Field.multiply(Z3.x, Z2.x, Z3.x);
+ }
+
+ int[] Z3Squared = (Z1IsOne && Z2IsOne) ? HSquared : null;
+
+ // TODO If the result will only be used in a subsequent addition, we don't need W3
+ Curve25519FieldElement W3 = calculateJacobianModifiedW((Curve25519FieldElement)Z3, Z3Squared);
+
+ ECFieldElement[] zs = new ECFieldElement[]{ Z3, W3 };
+
+ return new Curve25519Point(curve, X3, Y3, zs, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement Y1 = this.y;
+ if (Y1.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return twiceJacobianModified(true);
+ }
+
+ 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 twiceJacobianModified(false).add(b);
+ }
+
+ public ECPoint threeTimes()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement Y1 = this.y;
+ if (Y1.isZero())
+ {
+ return this;
+ }
+
+ return twiceJacobianModified(false).add(this);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ return new Curve25519Point(this.getCurve(), this.x, this.y.negate(), this.zs, this.withCompression);
+ }
+
+ protected Curve25519FieldElement calculateJacobianModifiedW(Curve25519FieldElement Z, int[] ZSquared)
+ {
+ Curve25519FieldElement a4 = (Curve25519FieldElement)this.getCurve().getA();
+ if (Z.isOne())
+ {
+ return a4;
+ }
+
+ Curve25519FieldElement W = new Curve25519FieldElement();
+ if (ZSquared == null)
+ {
+ ZSquared = W.x;
+ Curve25519Field.square(Z.x, ZSquared);
+ }
+ Curve25519Field.square(ZSquared, W.x);
+ Curve25519Field.multiply(W.x, a4.x, W.x);
+ return W;
+ }
+
+ protected Curve25519FieldElement getJacobianModifiedW()
+ {
+ Curve25519FieldElement W = (Curve25519FieldElement)this.zs[1];
+ if (W == null)
+ {
+ // NOTE: Rarely, twicePlus will result in the need for a lazy W1 calculation here
+ this.zs[1] = W = calculateJacobianModifiedW((Curve25519FieldElement)this.zs[0], null);
+ }
+ return W;
+ }
+
+ protected Curve25519Point twiceJacobianModified(boolean calculateW)
+ {
+ Curve25519FieldElement X1 = (Curve25519FieldElement)this.x, Y1 = (Curve25519FieldElement)this.y,
+ Z1 = (Curve25519FieldElement)this.zs[0], W1 = getJacobianModifiedW();
+
+ int c;
+
+ int[] M = Nat256.create();
+ Curve25519Field.square(X1.x, M);
+ c = Nat256.addBothTo(M, M, M);
+ c += Nat256.addTo(W1.x, M);
+ Curve25519Field.reduce27(c, M);
+
+ int[] _2Y1 = Nat256.create();
+ Curve25519Field.twice(Y1.x, _2Y1);
+
+ int[] _2Y1Squared = Nat256.create();
+ Curve25519Field.multiply(_2Y1, Y1.x, _2Y1Squared);
+
+ int[] S = Nat256.create();
+ Curve25519Field.multiply(_2Y1Squared, X1.x, S);
+ Curve25519Field.twice(S, S);
+
+ int[] _8T = Nat256.create();
+ Curve25519Field.square(_2Y1Squared, _8T);
+ Curve25519Field.twice(_8T, _8T);
+
+ Curve25519FieldElement X3 = new Curve25519FieldElement(_2Y1Squared);
+ Curve25519Field.square(M, X3.x);
+ Curve25519Field.subtract(X3.x, S, X3.x);
+ Curve25519Field.subtract(X3.x, S, X3.x);
+
+ Curve25519FieldElement Y3 = new Curve25519FieldElement(S);
+ Curve25519Field.subtract(S, X3.x, Y3.x);
+ Curve25519Field.multiply(Y3.x, M, Y3.x);
+ Curve25519Field.subtract(Y3.x, _8T, Y3.x);
+
+ Curve25519FieldElement Z3 = new Curve25519FieldElement(_2Y1);
+ if (!Nat256.isOne(Z1.x))
+ {
+ Curve25519Field.multiply(Z3.x, Z1.x, Z3.x);
+ }
+
+ Curve25519FieldElement W3 = null;
+ if (calculateW)
+ {
+ W3 = new Curve25519FieldElement(_8T);
+ Curve25519Field.multiply(W3.x, W1.x, W3.x);
+ Curve25519Field.twice(W3.x, W3.x);
+ }
+
+ return new Curve25519Point(this.getCurve(), X3, Y3, new ECFieldElement[]{ Z3, W3 }, this.withCompression);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1Curve.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1Curve.java
new file mode 100644
index 00000000..3c5d9cb3
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1Curve.java
@@ -0,0 +1,79 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECConstants;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1Field.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1Field.java
new file mode 100644
index 00000000..b7e87f27
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1Field.java
@@ -0,0 +1,177 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1FieldElement.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1FieldElement.java
new file mode 100644
index 00000000..449c560c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1FieldElement.java
@@ -0,0 +1,213 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.raw.Mod;
+import org.spongycastle.math.raw.Nat192;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1Point.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1Point.java
new file mode 100644
index 00000000..d2fc0ad2
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192K1Point.java
@@ -0,0 +1,298 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1Curve.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1Curve.java
new file mode 100644
index 00000000..705069fc
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1Curve.java
@@ -0,0 +1,80 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1Field.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1Field.java
new file mode 100644
index 00000000..4188b8f8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1Field.java
@@ -0,0 +1,286 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1FieldElement.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1FieldElement.java
new file mode 100644
index 00000000..2ab5030c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1FieldElement.java
@@ -0,0 +1,190 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.raw.Mod;
+import org.spongycastle.math.raw.Nat192;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1Point.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1Point.java
new file mode 100644
index 00000000..9d611267
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP192R1Point.java
@@ -0,0 +1,310 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1Curve.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1Curve.java
new file mode 100644
index 00000000..9b26b6f8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1Curve.java
@@ -0,0 +1,78 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECConstants;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1Field.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1Field.java
new file mode 100644
index 00000000..0db90e56
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1Field.java
@@ -0,0 +1,178 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1FieldElement.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1FieldElement.java
new file mode 100644
index 00000000..655b9592
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1FieldElement.java
@@ -0,0 +1,243 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.raw.Mod;
+import org.spongycastle.math.raw.Nat224;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1Point.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1Point.java
new file mode 100644
index 00000000..e33bc5fd
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224K1Point.java
@@ -0,0 +1,298 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1Curve.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1Curve.java
new file mode 100644
index 00000000..e81cca7f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1Curve.java
@@ -0,0 +1,80 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1Field.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1Field.java
new file mode 100644
index 00000000..1722e158
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1Field.java
@@ -0,0 +1,298 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1FieldElement.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1FieldElement.java
new file mode 100644
index 00000000..3a82fcab
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1FieldElement.java
@@ -0,0 +1,273 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.raw.Mod;
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.math.raw.Nat224;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1Point.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1Point.java
new file mode 100644
index 00000000..d44f2f8f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP224R1Point.java
@@ -0,0 +1,308 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1Curve.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1Curve.java
new file mode 100644
index 00000000..85d31cc1
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1Curve.java
@@ -0,0 +1,78 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECConstants;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1Field.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1Field.java
new file mode 100644
index 00000000..3aae2f2f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1Field.java
@@ -0,0 +1,179 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1FieldElement.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1FieldElement.java
new file mode 100644
index 00000000..10d8a265
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1FieldElement.java
@@ -0,0 +1,215 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.raw.Mod;
+import org.spongycastle.math.raw.Nat256;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1Point.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1Point.java
new file mode 100644
index 00000000..fa939431
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256K1Point.java
@@ -0,0 +1,298 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1Curve.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1Curve.java
new file mode 100644
index 00000000..f856bb34
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1Curve.java
@@ -0,0 +1,80 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1Field.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1Field.java
new file mode 100644
index 00000000..78c15d17
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1Field.java
@@ -0,0 +1,312 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1FieldElement.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1FieldElement.java
new file mode 100644
index 00000000..ddbab1a3
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1FieldElement.java
@@ -0,0 +1,189 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.raw.Mod;
+import org.spongycastle.math.raw.Nat256;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1Point.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1Point.java
new file mode 100644
index 00000000..8daaf049
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP256R1Point.java
@@ -0,0 +1,308 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1Curve.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1Curve.java
new file mode 100644
index 00000000..cb5bab24
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1Curve.java
@@ -0,0 +1,80 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1Field.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1Field.java
new file mode 100644
index 00000000..d3b78762
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1Field.java
@@ -0,0 +1,295 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1FieldElement.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1FieldElement.java
new file mode 100644
index 00000000..3203bda9
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1FieldElement.java
@@ -0,0 +1,211 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.raw.Mod;
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1Point.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1Point.java
new file mode 100644
index 00000000..47e5c793
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP384R1Point.java
@@ -0,0 +1,309 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1Curve.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1Curve.java
new file mode 100644
index 00000000..fb53cfbb
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1Curve.java
@@ -0,0 +1,80 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1Field.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1Field.java
new file mode 100644
index 00000000..5328a510
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1Field.java
@@ -0,0 +1,156 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1FieldElement.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1FieldElement.java
new file mode 100644
index 00000000..b0b92b6e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1FieldElement.java
@@ -0,0 +1,169 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.raw.Mod;
+import org.spongycastle.math.raw.Nat;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1Point.java b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1Point.java
new file mode 100644
index 00000000..c38132b5
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/custom/sec/SecP521R1Point.java
@@ -0,0 +1,333 @@
+package org.spongycastle.math.ec.custom.sec;
+
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/endo/ECEndomorphism.java b/core/src/main/java/org/spongycastle/math/ec/endo/ECEndomorphism.java
new file mode 100644
index 00000000..7396bf33
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/endo/ECEndomorphism.java
@@ -0,0 +1,10 @@
+package org.spongycastle.math.ec.endo;
+
+import org.spongycastle.math.ec.ECPointMap;
+
+public interface ECEndomorphism
+{
+ ECPointMap getPointMap();
+
+ boolean hasEfficientPointMap();
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/endo/GLVEndomorphism.java b/core/src/main/java/org/spongycastle/math/ec/endo/GLVEndomorphism.java
new file mode 100644
index 00000000..9c251a1e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/endo/GLVEndomorphism.java
@@ -0,0 +1,8 @@
+package org.spongycastle.math.ec.endo;
+
+import java.math.BigInteger;
+
+public interface GLVEndomorphism extends ECEndomorphism
+{
+ BigInteger[] decomposeScalar(BigInteger k);
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/endo/GLVTypeBEndomorphism.java b/core/src/main/java/org/spongycastle/math/ec/endo/GLVTypeBEndomorphism.java
new file mode 100644
index 00000000..884cbf53
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/endo/GLVTypeBEndomorphism.java
@@ -0,0 +1,58 @@
+package org.spongycastle.math.ec.endo;
+
+import java.math.BigInteger;
+
+import org.spongycastle.math.ec.ECConstants;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECPointMap;
+import org.spongycastle.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/core/src/main/java/org/spongycastle/math/ec/endo/GLVTypeBParameters.java b/core/src/main/java/org/spongycastle/math/ec/endo/GLVTypeBParameters.java
new file mode 100644
index 00000000..92dbfd0e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/endo/GLVTypeBParameters.java
@@ -0,0 +1,59 @@
+package org.spongycastle.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;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/ec/tools/DiscoverEndomorphisms.java b/core/src/main/java/org/spongycastle/math/ec/tools/DiscoverEndomorphisms.java
new file mode 100644
index 00000000..32341d60
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/ec/tools/DiscoverEndomorphisms.java
@@ -0,0 +1,373 @@
+package org.spongycastle.math.ec.tools;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.asn1.x9.ECNamedCurveTable;
+import org.spongycastle.asn1.x9.X9ECParameters;
+import org.spongycastle.math.ec.ECAlgorithms;
+import org.spongycastle.math.ec.ECConstants;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.util.BigIntegers;
+
+public class DiscoverEndomorphisms
+{
+ private static final int radix = 16;
+
+ public static void main(String[] args)
+ {
+ if (args.length < 1)
+ {
+ System.err.println("Expected a list of curve names as arguments");
+ return;
+ }
+
+ for (int i = 0; i < args.length; ++i)
+ {
+ discoverEndomorphism(args[i]);
+ }
+ }
+
+ private static void discoverEndomorphism(String curveName)
+ {
+ X9ECParameters x9 = ECNamedCurveTable.getByName(curveName);
+ if (x9 == null)
+ {
+ System.err.println("Unknown curve: " + curveName);
+ return;
+ }
+
+ ECCurve c = x9.getCurve();
+ if (ECAlgorithms.isFpCurve(c))
+ {
+ BigInteger characteristic = c.getField().getCharacteristic();
+
+ if (c.getA().isZero() && characteristic.mod(ECConstants.THREE).equals(ECConstants.ONE))
+ {
+ System.out.println("Curve '" + curveName + "' has a 'GLV Type B' endomorphism with these parameters: ");
+ printGLVTypeBParameters(x9);
+ }
+ }
+ }
+
+ private static void printGLVTypeBParameters(X9ECParameters x9)
+ {
+ BigInteger n = x9.getN();
+ BigInteger[] v1 = null;
+ BigInteger[] v2 = null;
+
+ // x^2 + x + 1 = 0 mod n
+ BigInteger lambda = solveQuadraticEquation(n, ECConstants.ONE, ECConstants.ONE);
+
+ BigInteger[] rt = extEuclidGLV(n, lambda);
+ v1 = new BigInteger[]{ rt[2], rt[3].negate() };
+ v2 = chooseShortest(new BigInteger[]{ rt[0], rt[1].negate() }, new BigInteger[]{ rt[4], rt[5].negate() });
+
+ /*
+ * If elements of v2 are not bounded by sqrt(n), then if r1/t1 are relatively prime there
+ * _may_ yet be a GLV generator, so search for it. See
+ * "Integer Decomposition for Fast Scalar Multiplication on Elliptic Curves", D. Kim, S. Lim
+ * (SAC 2002)
+ */
+ if (!isVectorBoundedBySqrt(v2, n) && areRelativelyPrime(v1[0], v1[1]))
+ {
+ BigInteger r = v1[0], t = v1[1], s = r.add(t.multiply(lambda)).divide(n);
+
+ BigInteger[] vw = extEuclidBezout(new BigInteger[]{ s.abs(), t.abs() });
+ BigInteger v = vw[0], w = vw[1];
+
+ if (s.signum() < 0)
+ {
+ v = v.negate();
+ }
+ if (t.signum() > 0)
+ {
+ w = w.negate();
+ }
+
+ BigInteger check = s.multiply(v).subtract(t.multiply(w));
+ if (!check.equals(ECConstants.ONE))
+ {
+ throw new IllegalStateException();
+ }
+
+ BigInteger x = w.multiply(n).subtract(v.multiply(lambda));
+
+ BigInteger base1 = v.negate();
+ BigInteger base2 = x.negate();
+
+ /*
+ * We calculate the range(s) conservatively large to avoid messy rounding issues, so
+ * there may be spurious candidate generators, but we won't miss any.
+ */
+ BigInteger sqrtN = isqrt(n.subtract(ECConstants.ONE)).add(ECConstants.ONE);
+
+ BigInteger[] I1 = calculateRange(base1, sqrtN, t);
+ BigInteger[] I2 = calculateRange(base2, sqrtN, r);
+
+ BigInteger[] range = intersect(I1, I2);
+ if (range != null)
+ {
+ for (BigInteger alpha = range[0]; alpha.compareTo(range[1]) <= 0; alpha = alpha.add(ECConstants.ONE))
+ {
+ BigInteger[] candidate = new BigInteger[]{ x.add(alpha.multiply(r)), v.add(alpha.multiply(t)) };
+ if (isShorter(candidate, v2))
+ {
+ v2 = candidate;
+ }
+ }
+ }
+ }
+
+ /*
+ * 'Beta' is a field element of order 3. There are only two such values besides 1; determine which of them
+ * corresponds to our choice for 'Lambda'.
+ */
+ ECFieldElement beta;
+ {
+ ECPoint G = x9.getG().normalize();
+ ECPoint mapG = G.multiply(lambda).normalize();
+ if (!G.getYCoord().equals(mapG.getYCoord()))
+ {
+ throw new IllegalStateException("Derivation of GLV Type B parameters failed unexpectedly");
+ }
+
+ BigInteger q = x9.getCurve().getField().getCharacteristic();
+ BigInteger e = q.divide(ECConstants.THREE);
+
+ SecureRandom random = new SecureRandom();
+ BigInteger b;
+ do
+ {
+ BigInteger r = BigIntegers.createRandomInRange(ECConstants.TWO, q.subtract(ECConstants.TWO), random);
+ b = r.modPow(e, q);
+ }
+ while (b.equals(ECConstants.ONE));
+
+ beta = x9.getCurve().fromBigInteger(ECConstants.TWO.modPow(e, q));
+
+ if (!G.getXCoord().multiply(beta).equals(mapG.getXCoord()))
+ {
+ beta = beta.square();
+ if (!G.getXCoord().multiply(beta).equals(mapG.getXCoord()))
+ {
+ throw new IllegalStateException("Derivation of GLV Type B parameters failed unexpectedly");
+ }
+ }
+ }
+
+ /*
+ * These parameters are used to avoid division when decomposing the scalar in a GLV point multiplication
+ */
+ BigInteger d = (v1[0].multiply(v2[1])).subtract(v1[1].multiply(v2[0]));
+
+ int bits = n.bitLength() + 16 - (n.bitLength() & 7);
+ BigInteger g1 = roundQuotient(v2[1].shiftLeft(bits), d);
+ BigInteger g2 = roundQuotient(v1[1].shiftLeft(bits), d).negate();
+
+ printProperty("Beta", beta.toBigInteger().toString(radix));
+ printProperty("Lambda", lambda.toString(radix));
+ printProperty("v1", "{ " + v1[0].toString(radix) + ", " + v1[1].toString(radix) + " }");
+ printProperty("v2", "{ " + v2[0].toString(radix) + ", " + v2[1].toString(radix) + " }");
+ printProperty("(OPT) g1", g1.toString(radix));
+ printProperty("(OPT) g2", g2.toString(radix));
+ printProperty("(OPT) bits", Integer.toString(bits));
+ }
+
+ private static void printProperty(String name, Object value)
+ {
+ StringBuffer sb = new StringBuffer(" ");
+ sb.append(name);
+ while (sb.length() < 20)
+ {
+ sb.append(' ');
+ }
+ sb.append("= ");
+ sb.append(value.toString());
+ System.out.println(sb.toString());
+ }
+
+ private static boolean areRelativelyPrime(BigInteger a, BigInteger b)
+ {
+ return a.gcd(b).equals(ECConstants.ONE);
+ }
+
+ private static BigInteger[] calculateRange(BigInteger mid, BigInteger off, BigInteger div)
+ {
+ BigInteger i1 = mid.subtract(off).divide(div);
+ BigInteger i2 = mid.add(off).divide(div);
+ return order(i1, i2);
+ }
+
+ private static BigInteger[] extEuclidBezout(BigInteger[] ab)
+ {
+ boolean swap = ab[0].compareTo(ab[1]) < 0;
+ if (swap)
+ {
+ swap(ab);
+ }
+
+ BigInteger r0 = ab[0], r1 = ab[1];
+ BigInteger s0 = ECConstants.ONE, s1 = ECConstants.ZERO;
+ BigInteger t0 = ECConstants.ZERO, t1 = ECConstants.ONE;
+
+ while (r1.compareTo(ECConstants.ONE) > 0)
+ {
+ BigInteger[] qr = r0.divideAndRemainder(r1);
+ BigInteger q = qr[0], r2 = qr[1];
+
+ BigInteger s2 = s0.subtract(q.multiply(s1));
+ BigInteger t2 = t0.subtract(q.multiply(t1));
+
+ r0 = r1;
+ r1 = r2;
+ s0 = s1;
+ s1 = s2;
+ t0 = t1;
+ t1 = t2;
+ }
+
+ if (r1.signum() <= 0)
+ {
+ throw new IllegalStateException();
+ }
+
+ BigInteger[] st = new BigInteger[]{ s1, t1 };
+ if (swap)
+ {
+ swap(st);
+ }
+ return st;
+ }
+
+ private static BigInteger[] extEuclidGLV(BigInteger n, BigInteger lambda)
+ {
+ BigInteger r0 = n, r1 = lambda;
+ // BigInteger s0 = ECConstants.ONE, s1 = ECConstants.ZERO;
+ BigInteger t0 = ECConstants.ZERO, t1 = ECConstants.ONE;
+
+ for (;;)
+ {
+ BigInteger[] qr = r0.divideAndRemainder(r1);
+ BigInteger q = qr[0], r2 = qr[1];
+
+ // BigInteger s2 = s0.subtract(q.multiply(s1));
+ BigInteger t2 = t0.subtract(q.multiply(t1));
+
+ if (isLessThanSqrt(r1, n))
+ {
+ return new BigInteger[]{ r0, t0, r1, t1, r2, t2 };
+ }
+
+ r0 = r1;
+ r1 = r2;
+ // s0 = s1;
+ // s1 = s2;
+ t0 = t1;
+ t1 = t2;
+ }
+ }
+
+ private static BigInteger[] chooseShortest(BigInteger[] u, BigInteger[] v)
+ {
+ return isShorter(u, v) ? u : v;
+ }
+
+ private static BigInteger[] intersect(BigInteger[] ab, BigInteger[] cd)
+ {
+ BigInteger min = ab[0].max(cd[0]);
+ BigInteger max = ab[1].min(cd[1]);
+ if (min.compareTo(max) > 0)
+ {
+ return null;
+ }
+ return new BigInteger[]{ min, max };
+ }
+
+ private static boolean isLessThanSqrt(BigInteger a, BigInteger b)
+ {
+ a = a.abs();
+ b = b.abs();
+ int target = b.bitLength(), maxBits = a.bitLength() * 2, minBits = maxBits - 1;
+ return minBits <= target && (maxBits < target || a.multiply(a).compareTo(b) < 0);
+ }
+
+ private static boolean isShorter(BigInteger[] u, BigInteger[] v)
+ {
+ BigInteger u1 = u[0].abs(), u2 = u[1].abs(), v1 = v[0].abs(), v2 = v[1].abs();
+
+ // TODO Check whether "shorter" just means by rectangle norm:
+ // return u1.max(u2).compareTo(v1.max(v2)) < 0;
+
+ boolean c1 = u1.compareTo(v1) < 0, c2 = u2.compareTo(v2) < 0;
+ if (c1 == c2)
+ {
+ return c1;
+ }
+
+ BigInteger du = u1.multiply(u1).add(u2.multiply(u2));
+ BigInteger dv = v1.multiply(v1).add(v2.multiply(v2));
+
+ return du.compareTo(dv) < 0;
+ }
+
+ private static boolean isVectorBoundedBySqrt(BigInteger[] v, BigInteger n)
+ {
+ BigInteger max = v[0].abs().max(v[1].abs());
+ return isLessThanSqrt(max, n);
+ }
+
+ private static BigInteger[] order(BigInteger a, BigInteger b)
+ {
+ if (a.compareTo(b) <= 0)
+ {
+ return new BigInteger[]{ a, b };
+ }
+ return new BigInteger[]{ b, a };
+ }
+
+ private static BigInteger roundQuotient(BigInteger x, BigInteger y)
+ {
+ boolean negative = (x.signum() != y.signum());
+ x = x.abs();
+ y = y.abs();
+ BigInteger result = x.add(y.shiftRight(1)).divide(y);
+ return negative ? result.negate() : result;
+ }
+
+ private static BigInteger solveQuadraticEquation(BigInteger n, BigInteger r, BigInteger s)
+ {
+ BigInteger det = r.multiply(r).subtract(s.shiftLeft(2)).mod(n);
+
+ BigInteger root = new ECFieldElement.Fp(n, det).sqrt().toBigInteger();
+ if (!root.testBit(0))
+ {
+ root = n.subtract(root);
+ }
+
+ return root.shiftRight(1); // NOTE: implicit -1 of the low-bit
+ }
+
+ private static BigInteger isqrt(BigInteger x)
+ {
+ BigInteger g0 = x.shiftRight(x.bitLength() / 2);
+ for (;;)
+ {
+ BigInteger g1 = g0.add(x.divide(g0)).shiftRight(1);
+ if (g1.equals(g0))
+ {
+ return g1;
+ }
+ g0 = g1;
+ }
+ }
+
+ private static void swap(BigInteger[] ab)
+ {
+ BigInteger tmp = ab[0];
+ ab[0] = ab[1];
+ ab[1] = tmp;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/field/ExtensionField.java b/core/src/main/java/org/spongycastle/math/field/ExtensionField.java
new file mode 100644
index 00000000..d32fda58
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/field/ExtensionField.java
@@ -0,0 +1,8 @@
+package org.spongycastle.math.field;
+
+public interface ExtensionField extends FiniteField
+{
+ FiniteField getSubfield();
+
+ int getDegree();
+}
diff --git a/core/src/main/java/org/spongycastle/math/field/FiniteField.java b/core/src/main/java/org/spongycastle/math/field/FiniteField.java
new file mode 100644
index 00000000..25801c31
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/field/FiniteField.java
@@ -0,0 +1,10 @@
+package org.spongycastle.math.field;
+
+import java.math.BigInteger;
+
+public interface FiniteField
+{
+ BigInteger getCharacteristic();
+
+ int getDimension();
+}
diff --git a/core/src/main/java/org/spongycastle/math/field/FiniteFields.java b/core/src/main/java/org/spongycastle/math/field/FiniteFields.java
new file mode 100644
index 00000000..602d32e6
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/field/FiniteFields.java
@@ -0,0 +1,53 @@
+package org.spongycastle.math.field;
+
+import java.math.BigInteger;
+
+public abstract class FiniteFields
+{
+ static final FiniteField GF_2 = new PrimeField(BigInteger.valueOf(2));
+ static final FiniteField GF_3 = new PrimeField(BigInteger.valueOf(3));
+
+ public static PolynomialExtensionField getBinaryExtensionField(int[] exponents)
+ {
+ if (exponents[0] != 0)
+ {
+ throw new IllegalArgumentException("Irreducible polynomials in GF(2) must have constant term");
+ }
+ for (int i = 1; i < exponents.length; ++i)
+ {
+ if (exponents[i] <= exponents[i - 1])
+ {
+ throw new IllegalArgumentException("Polynomial exponents must be montonically increasing");
+ }
+ }
+
+ return new GenericPolynomialExtensionField(GF_2, new GF2Polynomial(exponents));
+ }
+
+// public static PolynomialExtensionField getTernaryExtensionField(Term[] terms)
+// {
+// return new GenericPolynomialExtensionField(GF_3, new GF3Polynomial(terms));
+// }
+
+ public static FiniteField getPrimeField(BigInteger characteristic)
+ {
+ int bitLength = characteristic.bitLength();
+ if (characteristic.signum() <= 0 || bitLength < 2)
+ {
+ throw new IllegalArgumentException("'characteristic' must be >= 2");
+ }
+
+ if (bitLength < 3)
+ {
+ switch (characteristic.intValue())
+ {
+ case 2:
+ return GF_2;
+ case 3:
+ return GF_3;
+ }
+ }
+
+ return new PrimeField(characteristic);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/field/GF2Polynomial.java b/core/src/main/java/org/spongycastle/math/field/GF2Polynomial.java
new file mode 100644
index 00000000..a8e900c7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/field/GF2Polynomial.java
@@ -0,0 +1,42 @@
+package org.spongycastle.math.field;
+
+import org.spongycastle.util.Arrays;
+
+class GF2Polynomial implements Polynomial
+{
+ protected final int[] exponents;
+
+ GF2Polynomial(int[] exponents)
+ {
+ this.exponents = Arrays.clone(exponents);
+ }
+
+ public int getDegree()
+ {
+ return exponents[exponents.length - 1];
+ }
+
+ public int[] getExponentsPresent()
+ {
+ return Arrays.clone(exponents);
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (!(obj instanceof GF2Polynomial))
+ {
+ return false;
+ }
+ GF2Polynomial other = (GF2Polynomial)obj;
+ return Arrays.areEqual(exponents, other.exponents);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(exponents);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/field/GenericPolynomialExtensionField.java b/core/src/main/java/org/spongycastle/math/field/GenericPolynomialExtensionField.java
new file mode 100644
index 00000000..677cff96
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/field/GenericPolynomialExtensionField.java
@@ -0,0 +1,62 @@
+package org.spongycastle.math.field;
+
+import java.math.BigInteger;
+
+import org.spongycastle.util.Integers;
+
+class GenericPolynomialExtensionField implements PolynomialExtensionField
+{
+ protected final FiniteField subfield;
+ protected final Polynomial minimalPolynomial;
+
+ GenericPolynomialExtensionField(FiniteField subfield, Polynomial polynomial)
+ {
+ this.subfield = subfield;
+ this.minimalPolynomial = polynomial;
+ }
+
+ public BigInteger getCharacteristic()
+ {
+ return subfield.getCharacteristic();
+ }
+
+ public int getDimension()
+ {
+ return subfield.getDimension() * minimalPolynomial.getDegree();
+ }
+
+ public FiniteField getSubfield()
+ {
+ return subfield;
+ }
+
+ public int getDegree()
+ {
+ return minimalPolynomial.getDegree();
+ }
+
+ public Polynomial getMinimalPolynomial()
+ {
+ return minimalPolynomial;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (!(obj instanceof GenericPolynomialExtensionField))
+ {
+ return false;
+ }
+ GenericPolynomialExtensionField other = (GenericPolynomialExtensionField)obj;
+ return subfield.equals(other.subfield) && minimalPolynomial.equals(other.minimalPolynomial);
+ }
+
+ public int hashCode()
+ {
+ return subfield.hashCode()
+ ^ Integers.rotateLeft(minimalPolynomial.hashCode(), 16);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/field/Polynomial.java b/core/src/main/java/org/spongycastle/math/field/Polynomial.java
new file mode 100644
index 00000000..b0724cb5
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/field/Polynomial.java
@@ -0,0 +1,12 @@
+package org.spongycastle.math.field;
+
+public interface Polynomial
+{
+ int getDegree();
+
+// BigInteger[] getCoefficients();
+
+ int[] getExponentsPresent();
+
+// Term[] getNonZeroTerms();
+}
diff --git a/core/src/main/java/org/spongycastle/math/field/PolynomialExtensionField.java b/core/src/main/java/org/spongycastle/math/field/PolynomialExtensionField.java
new file mode 100644
index 00000000..35eb98ea
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/field/PolynomialExtensionField.java
@@ -0,0 +1,6 @@
+package org.spongycastle.math.field;
+
+public interface PolynomialExtensionField extends ExtensionField
+{
+ Polynomial getMinimalPolynomial();
+}
diff --git a/core/src/main/java/org/spongycastle/math/field/PrimeField.java b/core/src/main/java/org/spongycastle/math/field/PrimeField.java
new file mode 100644
index 00000000..38c313a7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/field/PrimeField.java
@@ -0,0 +1,42 @@
+package org.spongycastle.math.field;
+
+import java.math.BigInteger;
+
+class PrimeField implements FiniteField
+{
+ protected final BigInteger characteristic;
+
+ PrimeField(BigInteger characteristic)
+ {
+ this.characteristic = characteristic;
+ }
+
+ public BigInteger getCharacteristic()
+ {
+ return characteristic;
+ }
+
+ public int getDimension()
+ {
+ return 1;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (!(obj instanceof PrimeField))
+ {
+ return false;
+ }
+ PrimeField other = (PrimeField)obj;
+ return characteristic.equals(other.characteristic);
+ }
+
+ public int hashCode()
+ {
+ return characteristic.hashCode();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/raw/Mod.java b/core/src/main/java/org/spongycastle/math/raw/Mod.java
new file mode 100644
index 00000000..6bb8d748
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/raw/Mod.java
@@ -0,0 +1,199 @@
+package org.spongycastle.math.raw;
+
+import java.util.Random;
+
+import org.spongycastle.util.Pack;
+
+public abstract class Mod
+{
+ public static int inverse32(int d)
+ {
+// int x = d + (((d + 1) & 4) << 1); // d.x == 1 mod 2**4
+ int x = d; // d.x == 1 mod 2**3
+ x *= 2 - d * x; // d.x == 1 mod 2**6
+ x *= 2 - d * x; // d.x == 1 mod 2**12
+ x *= 2 - d * x; // d.x == 1 mod 2**24
+ x *= 2 - d * x; // d.x == 1 mod 2**48
+// assert d * x == 1;
+ return x;
+ }
+
+ public static void invert(int[] p, int[] x, int[] z)
+ {
+ int len = p.length;
+ if (Nat.isZero(len, x))
+ {
+ throw new IllegalArgumentException("'x' cannot be 0");
+ }
+ if (Nat.isOne(len, x))
+ {
+ System.arraycopy(x, 0, z, 0, len);
+ return;
+ }
+
+ int[] u = Nat.copy(len, x);
+ int[] a = Nat.create(len);
+ a[0] = 1;
+ int ac = 0;
+
+ if ((u[0] & 1) == 0)
+ {
+ ac = inversionStep(p, u, len, a, ac);
+ }
+ if (Nat.isOne(len, u))
+ {
+ inversionResult(p, ac, a, z);
+ return;
+ }
+
+ int[] v = Nat.copy(len, p);
+ int[] b = Nat.create(len);
+ int bc = 0;
+
+ int uvLen = len;
+
+ for (;;)
+ {
+ while (u[uvLen - 1] == 0 && v[uvLen - 1] == 0)
+ {
+ --uvLen;
+ }
+
+ if (Nat.gte(uvLen, u, v))
+ {
+ Nat.subFrom(uvLen, v, u);
+// assert (u[0] & 1) == 0;
+ ac += Nat.subFrom(len, b, a) - bc;
+ ac = inversionStep(p, u, uvLen, a, ac);
+ if (Nat.isOne(uvLen, u))
+ {
+ inversionResult(p, ac, a, z);
+ return;
+ }
+ }
+ else
+ {
+ Nat.subFrom(uvLen, u, v);
+// assert (v[0] & 1) == 0;
+ bc += Nat.subFrom(len, a, b) - ac;
+ bc = inversionStep(p, v, uvLen, b, bc);
+ if (Nat.isOne(uvLen, v))
+ {
+ inversionResult(p, bc, b, z);
+ return;
+ }
+ }
+ }
+ }
+
+ public static int[] random(int[] p)
+ {
+ int len = p.length;
+ Random rand = new Random();
+ int[] s = Nat.create(len);
+
+ int m = p[len - 1];
+ m |= m >>> 1;
+ m |= m >>> 2;
+ m |= m >>> 4;
+ m |= m >>> 8;
+ m |= m >>> 16;
+
+ do
+ {
+ for (int i = 0; i != len; i++)
+ {
+ s[i] = rand.nextInt();
+ }
+ s[len - 1] &= m;
+ }
+ while (Nat.gte(len, s, p));
+
+ return s;
+ }
+
+ public static void add(int[] p, int[] x, int[] y, int[] z)
+ {
+ int len = p.length;
+ int c = Nat.add(len, x, y, z);
+ if (c != 0)
+ {
+ Nat.subFrom(len, p, z);
+ }
+ }
+
+ public static void subtract(int[] p, int[] x, int[] y, int[] z)
+ {
+ int len = p.length;
+ int c = Nat.sub(len, x, y, z);
+ if (c != 0)
+ {
+ Nat.addTo(len, p, z);
+ }
+ }
+
+ private static void inversionResult(int[] p, int ac, int[] a, int[] z)
+ {
+ if (ac < 0)
+ {
+ Nat.add(p.length, a, p, z);
+ }
+ else
+ {
+ System.arraycopy(a, 0, z, 0, p.length);
+ }
+ }
+
+ private static int inversionStep(int[] p, int[] u, int uLen, int[] x, int xc)
+ {
+ int len = p.length;
+ int count = 0;
+ while (u[0] == 0)
+ {
+ Nat.shiftDownWord(uLen, u, 0);
+ count += 32;
+ }
+
+ {
+ int zeroes = getTrailingZeroes(u[0]);
+ if (zeroes > 0)
+ {
+ Nat.shiftDownBits(uLen, u, zeroes, 0);
+ count += zeroes;
+ }
+ }
+
+ for (int i = 0; i < count; ++i)
+ {
+ if ((x[0] & 1) != 0)
+ {
+ if (xc < 0)
+ {
+ xc += Nat.addTo(len, p, x);
+ }
+ else
+ {
+ xc += Nat.subFrom(len, p, x);
+ }
+ }
+
+// assert xc == 0 || xc == 1;
+ Nat.shiftDownBit(len, x, xc);
+ }
+
+ return xc;
+ }
+
+ private static int getTrailingZeroes(int x)
+ {
+// assert x != 0;
+
+ int count = 0;
+ while ((x & 1) == 0)
+ {
+ x >>>= 1;
+ ++count;
+ }
+ return count;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/raw/Mont256.java b/core/src/main/java/org/spongycastle/math/raw/Mont256.java
new file mode 100644
index 00000000..b90dcc5a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/raw/Mont256.java
@@ -0,0 +1,152 @@
+package org.spongycastle.math.raw;
+
+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/spongycastle/math/raw/Nat.java b/core/src/main/java/org/spongycastle/math/raw/Nat.java
new file mode 100644
index 00000000..14909673
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/raw/Nat.java
@@ -0,0 +1,1038 @@
+package org.spongycastle.math.raw;
+
+import java.math.BigInteger;
+
+import org.spongycastle.util.Pack;
+
+public abstract class Nat
+{
+ private static final long M = 0xFFFFFFFFL;
+
+ public static int add(int len, int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ c += (x[i] & M) + (y[i] & M);
+ z[i] = (int)c;
+ c >>>= 32;
+ }
+ return (int)c;
+ }
+
+ public static int add33At(int len, int x, int[] z, int zPos)
+ {
+ // assert zPos <= (len - 2);
+ long c = (z[zPos + 0] & M) + (x & M);
+ z[zPos + 0] = (int)c;
+ c >>>= 32;
+ c += (z[zPos + 1] & M) + 1L;
+ z[zPos + 1] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, zPos + 2);
+ }
+
+ public static int add33At(int len, int x, int[] z, int zOff, int zPos)
+ {
+ // assert zPos <= (len - 2);
+ long c = (z[zOff + zPos] & M) + (x & M);
+ z[zOff + zPos] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + zPos + 1] & M) + 1L;
+ z[zOff + zPos + 1] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, zOff, zPos + 2);
+ }
+
+ public static int add33To(int len, int x, int[] z)
+ {
+ long c = (z[0] & M) + (x & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (z[1] & M) + 1L;
+ z[1] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, 2);
+ }
+
+ public static int add33To(int len, int x, int[] z, int zOff)
+ {
+ long c = (z[zOff + 0] & M) + (x & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 1] & M) + 1L;
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, zOff, 2);
+ }
+
+ public static int addBothTo(int len, int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ c += (x[i] & M) + (y[i] & M) + (z[i] & M);
+ z[i] = (int)c;
+ c >>>= 32;
+ }
+ return (int)c;
+ }
+
+ public static int addBothTo(int len, int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ c += (x[xOff + i] & M) + (y[yOff + i] & M) + (z[zOff + i] & M);
+ z[zOff + i] = (int)c;
+ c >>>= 32;
+ }
+ return (int)c;
+ }
+
+ public static int addDWordAt(int len, long x, int[] z, int zPos)
+ {
+ // assert zPos <= (len - 2);
+ long c = (z[zPos + 0] & M) + (x & M);
+ z[zPos + 0] = (int)c;
+ c >>>= 32;
+ c += (z[zPos + 1] & M) + (x >>> 32);
+ z[zPos + 1] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, zPos + 2);
+ }
+
+ public static int addDWordAt(int len, long x, int[] z, int zOff, int zPos)
+ {
+ // assert zPos <= (len - 2);
+ long c = (z[zOff + zPos] & M) + (x & M);
+ z[zOff + zPos] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + zPos + 1] & M) + (x >>> 32);
+ z[zOff + zPos + 1] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, zOff, zPos + 2);
+ }
+
+ public static int addDWordTo(int len, long x, int[] z)
+ {
+ long c = (z[0] & M) + (x & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (z[1] & M) + (x >>> 32);
+ z[1] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, 2);
+ }
+
+ public static int addDWordTo(int len, long x, int[] z, int zOff)
+ {
+ long c = (z[zOff + 0] & M) + (x & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 1] & M) + (x >>> 32);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, zOff, 2);
+ }
+
+ public static int addTo(int len, int[] x, int[] z)
+ {
+ long c = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ c += (x[i] & M) + (z[i] & M);
+ z[i] = (int)c;
+ c >>>= 32;
+ }
+ return (int)c;
+ }
+
+ public static int addTo(int len, int[] x, int xOff, int[] z, int zOff)
+ {
+ long c = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ c += (x[xOff + i] & M) + (z[zOff + i] & M);
+ z[zOff + i] = (int)c;
+ c >>>= 32;
+ }
+ return (int)c;
+ }
+
+ public static int addWordAt(int len, int x, int[] z, int zPos)
+ {
+ // assert zPos <= (len - 1);
+ long c = (x & M) + (z[zPos] & M);
+ z[zPos] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, zPos + 1);
+ }
+
+ public static int addWordAt(int len, int x, int[] z, int zOff, int zPos)
+ {
+ // assert zPos <= (len - 1);
+ long c = (x & M) + (z[zOff + zPos] & M);
+ z[zOff + zPos] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, zOff, zPos + 1);
+ }
+
+ public static int addWordTo(int len, int x, int[] z)
+ {
+ long c = (x & M) + (z[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, 1);
+ }
+
+ public static int addWordTo(int len, int x, int[] z, int zOff)
+ {
+ long c = (x & M) + (z[zOff] & M);
+ z[zOff] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, zOff, 1);
+ }
+
+ public static int[] copy(int len, int[] x)
+ {
+ int[] z = new int[len];
+ System.arraycopy(x, 0, z, 0, len);
+ return z;
+ }
+
+ public static void copy(int len, int[] x, int[] z)
+ {
+ System.arraycopy(x, 0, z, 0, len);
+ }
+
+ public static int[] create(int len)
+ {
+ return new int[len];
+ }
+
+ public static int dec(int len, int[] z)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ if (--z[i] != -1)
+ {
+ return 0;
+ }
+ }
+ return -1;
+ }
+
+ public static int dec(int len, int[] x, int[] z)
+ {
+ int i = 0;
+ while (i < len)
+ {
+ int c = x[i] - 1;
+ z[i] = c;
+ ++i;
+ if (c != -1)
+ {
+ while (i < len)
+ {
+ z[i] = x[i];
+ ++i;
+ }
+ return 0;
+ }
+ }
+ return -1;
+ }
+
+ public static int decAt(int len, int[] z, int zPos)
+ {
+ // assert zPos <= len;
+ for (int i = zPos; i < len; ++i)
+ {
+ if (--z[i] != -1)
+ {
+ return 0;
+ }
+ }
+ return -1;
+ }
+
+ public static int decAt(int len, int[] z, int zOff, int zPos)
+ {
+ // assert zPos <= len;
+ for (int i = zPos; i < len; ++i)
+ {
+ if (--z[zOff + i] != -1)
+ {
+ return 0;
+ }
+ }
+ return -1;
+ }
+
+ public static boolean eq(int len, int[] x, int[] y)
+ {
+ for (int i = len - 1; i >= 0; --i)
+ {
+ if (x[i] != y[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static int[] fromBigInteger(int bits, BigInteger x)
+ {
+ if (x.signum() < 0 || x.bitLength() > bits)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ int len = (bits + 31) >> 5;
+ int[] z = create(len);
+ 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;
+ }
+ int w = bit >> 5;
+ if (w < 0 || w >= x.length)
+ {
+ return 0;
+ }
+ int b = bit & 31;
+ return (x[w] >>> b) & 1;
+ }
+
+ public static boolean gte(int len, int[] x, int[] y)
+ {
+ for (int i = len - 1; i >= 0; --i)
+ {
+ int x_i = x[i] ^ Integer.MIN_VALUE;
+ int y_i = y[i] ^ Integer.MIN_VALUE;
+ if (x_i < y_i)
+ return false;
+ if (x_i > y_i)
+ return true;
+ }
+ return true;
+ }
+
+ public static int inc(int len, int[] z)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ if (++z[i] != 0)
+ {
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ public static int inc(int len, int[] x, int[] z)
+ {
+ int i = 0;
+ while (i < len)
+ {
+ int c = x[i] + 1;
+ z[i] = c;
+ ++i;
+ if (c != 0)
+ {
+ while (i < len)
+ {
+ z[i] = x[i];
+ ++i;
+ }
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ public static int incAt(int len, int[] z, int zPos)
+ {
+ // assert zPos <= len;
+ for (int i = zPos; i < len; ++i)
+ {
+ if (++z[i] != 0)
+ {
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ public static int incAt(int len, int[] z, int zOff, int zPos)
+ {
+ // assert zPos <= len;
+ for (int i = zPos; i < len; ++i)
+ {
+ if (++z[zOff + i] != 0)
+ {
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ public static boolean isOne(int len, int[] x)
+ {
+ if (x[0] != 1)
+ {
+ return false;
+ }
+ for (int i = 1; i < len; ++i)
+ {
+ if (x[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isZero(int len, int[] x)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ if (x[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void mul(int len, int[] x, int[] y, int[] zz)
+ {
+ zz[len] = mulWord(len, x[0], y, zz);
+
+ for (int i = 1; i < len; ++i)
+ {
+ zz[i + len] = mulWordAddTo(len, x[i], y, 0, zz, i);
+ }
+ }
+
+ public static void mul(int len, int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff)
+ {
+ zz[zzOff + len] = mulWord(len, x[xOff], y, yOff, zz, zzOff);
+
+ for (int i = 1; i < len; ++i)
+ {
+ zz[zzOff + i + len] = mulWordAddTo(len, x[xOff + i], y, yOff, zz, zzOff + i);
+ }
+ }
+
+ public static int mulAddTo(int len, int[] x, int[] y, int[] zz)
+ {
+ long zc = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ long c = mulWordAddTo(len, x[i], y, 0, zz, i) & M;
+ c += zc + (zz[i + len] & M);
+ zz[i + len] = (int)c;
+ zc = c >>> 32;
+ }
+ return (int)zc;
+ }
+
+ public static int mulAddTo(int len, int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff)
+ {
+ long zc = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ long c = mulWordAddTo(len, x[xOff + i], y, yOff, zz, zzOff) & M;
+ c += zc + (zz[zzOff + len] & M);
+ zz[zzOff + len] = (int)c;
+ zc = c >>> 32;
+ ++zzOff;
+ }
+ return (int)zc;
+ }
+
+ public static int mul31BothAdd(int len, int a, int[] x, int b, int[] y, int[] z, int zOff)
+ {
+ long c = 0, aVal = a & M, bVal = b & M;
+ int i = 0;
+ do
+ {
+ c += aVal * (x[i] & M) + bVal * (y[i] & M) + (z[zOff + i] & M);
+ z[zOff + i] = (int)c;
+ c >>>= 32;
+ }
+ while (++i < len);
+ return (int)c;
+ }
+
+ public static int mulWord(int len, int x, int[] y, int[] z)
+ {
+ long c = 0, xVal = x & M;
+ int i = 0;
+ do
+ {
+ c += xVal * (y[i] & M);
+ z[i] = (int)c;
+ c >>>= 32;
+ }
+ while (++i < len);
+ return (int)c;
+ }
+
+ public static int mulWord(int len, int x, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0, xVal = x & M;
+ int i = 0;
+ do
+ {
+ c += xVal * (y[yOff + i] & M);
+ z[zOff + i] = (int)c;
+ c >>>= 32;
+ }
+ while (++i < len);
+ return (int)c;
+ }
+
+ public static int mulWordAddTo(int len, int x, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0, xVal = x & M;
+ int i = 0;
+ do
+ {
+ c += xVal * (y[yOff + i] & M) + (z[zOff + i] & M);
+ z[zOff + i] = (int)c;
+ c >>>= 32;
+ }
+ while (++i < len);
+ return (int)c;
+ }
+
+ public static int mulWordDwordAddAt(int len, int x, long y, int[] z, int zPos)
+ {
+ // assert zPos <= (len - 3);
+ long c = 0, xVal = x & M;
+ c += xVal * (y & M) + (z[zPos + 0] & M);
+ z[zPos + 0] = (int)c;
+ c >>>= 32;
+ c += xVal * (y >>> 32) + (z[zPos + 1] & M);
+ z[zPos + 1] = (int)c;
+ c >>>= 32;
+ c += (z[zPos + 2] & M);
+ z[zPos + 2] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : incAt(len, z, zPos + 3);
+ }
+
+ public static int shiftDownBit(int len, int[] z, int c)
+ {
+ int i = len;
+ while (--i >= 0)
+ {
+ int next = z[i];
+ z[i] = (next >>> 1) | (c << 31);
+ c = next;
+ }
+ return c << 31;
+ }
+
+ public static int shiftDownBit(int len, int[] z, int zOff, int c)
+ {
+ int i = len;
+ while (--i >= 0)
+ {
+ int next = z[zOff + i];
+ z[zOff + i] = (next >>> 1) | (c << 31);
+ c = next;
+ }
+ return c << 31;
+ }
+
+ public static int shiftDownBit(int len, int[] x, int c, int[] z)
+ {
+ int i = len;
+ while (--i >= 0)
+ {
+ int next = x[i];
+ z[i] = (next >>> 1) | (c << 31);
+ c = next;
+ }
+ return c << 31;
+ }
+
+ public static int shiftDownBit(int len, int[] x, int xOff, int c, int[] z, int zOff)
+ {
+ int i = len;
+ while (--i >= 0)
+ {
+ int next = x[xOff + i];
+ z[zOff + i] = (next >>> 1) | (c << 31);
+ c = next;
+ }
+ return c << 31;
+ }
+
+ public static int shiftDownBits(int len, int[] z, int bits, int c)
+ {
+// assert bits > 0 && bits < 32;
+ int i = len;
+ while (--i >= 0)
+ {
+ int next = z[i];
+ z[i] = (next >>> bits) | (c << -bits);
+ c = next;
+ }
+ return c << -bits;
+ }
+
+ public static int shiftDownBits(int len, int[] z, int zOff, int bits, int c)
+ {
+// assert bits > 0 && bits < 32;
+ int i = len;
+ while (--i >= 0)
+ {
+ int next = z[zOff + i];
+ z[zOff + i] = (next >>> bits) | (c << -bits);
+ c = next;
+ }
+ return c << -bits;
+ }
+
+ public static int shiftDownBits(int len, int[] x, int bits, int c, int[] z)
+ {
+// assert bits > 0 && bits < 32;
+ int i = len;
+ while (--i >= 0)
+ {
+ int next = x[i];
+ z[i] = (next >>> bits) | (c << -bits);
+ c = next;
+ }
+ return c << -bits;
+ }
+
+ public static int shiftDownBits(int len, int[] x, int xOff, int bits, int c, int[] z, int zOff)
+ {
+// assert bits > 0 && bits < 32;
+ int i = len;
+ while (--i >= 0)
+ {
+ int next = x[xOff + i];
+ z[zOff + i] = (next >>> bits) | (c << -bits);
+ c = next;
+ }
+ return c << -bits;
+ }
+
+ public static int shiftDownWord(int len, int[] z, int c)
+ {
+ int i = len;
+ while (--i >= 0)
+ {
+ int next = z[i];
+ z[i] = c;
+ c = next;
+ }
+ return c;
+ }
+
+ public static int shiftUpBit(int len, int[] z, int c)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ int next = z[i];
+ z[i] = (next << 1) | (c >>> 31);
+ c = next;
+ }
+ return c >>> 31;
+ }
+
+ public static int shiftUpBit(int len, int[] z, int zOff, int c)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ int next = z[zOff + i];
+ z[zOff + i] = (next << 1) | (c >>> 31);
+ c = next;
+ }
+ return c >>> 31;
+ }
+
+ public static int shiftUpBit(int len, int[] x, int c, int[] z)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ int next = x[i];
+ z[i] = (next << 1) | (c >>> 31);
+ c = next;
+ }
+ return c >>> 31;
+ }
+
+ public static int shiftUpBit(int len, int[] x, int xOff, int c, int[] z, int zOff)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ int next = x[xOff + i];
+ z[zOff + i] = (next << 1) | (c >>> 31);
+ c = next;
+ }
+ return c >>> 31;
+ }
+
+ public static int shiftUpBits(int len, int[] z, int bits, int c)
+ {
+// assert bits > 0 && bits < 32;
+ for (int i = 0; i < len; ++i)
+ {
+ int next = z[i];
+ z[i] = (next << bits) | (c >>> -bits);
+ c = next;
+ }
+ return c >>> -bits;
+ }
+
+ public static int shiftUpBits(int len, int[] z, int zOff, int bits, int c)
+ {
+// assert bits > 0 && bits < 32;
+ for (int i = 0; i < len; ++i)
+ {
+ int next = z[zOff + i];
+ z[zOff + i] = (next << bits) | (c >>> -bits);
+ c = next;
+ }
+ return c >>> -bits;
+ }
+
+ public static int shiftUpBits(int len, int[] x, int bits, int c, int[] z)
+ {
+// assert bits > 0 && bits < 32;
+ for (int i = 0; i < len; ++i)
+ {
+ int next = x[i];
+ z[i] = (next << bits) | (c >>> -bits);
+ c = next;
+ }
+ return c >>> -bits;
+ }
+
+ public static int shiftUpBits(int len, int[] x, int xOff, int bits, int c, int[] z, int zOff)
+ {
+// assert bits > 0 && bits < 32;
+ for (int i = 0; i < len; ++i)
+ {
+ int next = x[xOff + i];
+ z[zOff + i] = (next << bits) | (c >>> -bits);
+ c = next;
+ }
+ return c >>> -bits;
+ }
+
+ public static void square(int len, int[] x, int[] zz)
+ {
+ int extLen = len << 1;
+ int c = 0;
+ int j = len, k = extLen;
+ do
+ {
+ long xVal = (x[--j] & M);
+ long p = xVal * xVal;
+ zz[--k] = (c << 31) | (int)(p >>> 33);
+ zz[--k] = (int)(p >>> 1);
+ c = (int)p;
+ }
+ while (j > 0);
+
+ for (int i = 1; i < len; ++i)
+ {
+ c = squareWordAdd(x, i, zz);
+ addWordAt(extLen, c, zz, i << 1);
+ }
+
+ shiftUpBit(extLen, zz, x[0] << 31);
+ }
+
+ public static void square(int len, int[] x, int xOff, int[] zz, int zzOff)
+ {
+ int extLen = len << 1;
+ int c = 0;
+ int j = len, k = extLen;
+ do
+ {
+ long xVal = (x[xOff + --j] & M);
+ long p = xVal * xVal;
+ zz[zzOff + --k] = (c << 31) | (int)(p >>> 33);
+ zz[zzOff + --k] = (int)(p >>> 1);
+ c = (int)p;
+ }
+ while (j > 0);
+
+ for (int i = 1; i < len; ++i)
+ {
+ c = squareWordAdd(x, xOff, i, zz, zzOff);
+ addWordAt(extLen, c, zz, zzOff, i << 1);
+ }
+
+ shiftUpBit(extLen, zz, zzOff, x[xOff] << 31);
+ }
+
+ public static int squareWordAdd(int[] x, int xPos, int[] z)
+ {
+ long c = 0, xVal = x[xPos] & M;
+ int i = 0;
+ do
+ {
+ c += xVal * (x[i] & M) + (z[xPos + i] & M);
+ z[xPos + i] = (int)c;
+ c >>>= 32;
+ }
+ while (++i < xPos);
+ return (int)c;
+ }
+
+ public static int squareWordAdd(int[] x, int xOff, int xPos, int[] z, int zOff)
+ {
+ long c = 0, xVal = x[xOff + xPos] & M;
+ int i = 0;
+ do
+ {
+ c += xVal * (x[xOff + i] & M) + (z[xPos + zOff] & M);
+ z[xPos + zOff] = (int)c;
+ c >>>= 32;
+ ++zOff;
+ }
+ while (++i < xPos);
+ return (int)c;
+ }
+
+ public static int sub(int len, int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ c += (x[i] & M) - (y[i] & M);
+ z[i] = (int)c;
+ c >>= 32;
+ }
+ return (int)c;
+ }
+
+ public static int sub(int len, int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ c += (x[xOff + i] & M) - (y[yOff + i] & M);
+ z[zOff + i] = (int)c;
+ c >>= 32;
+ }
+ return (int)c;
+ }
+
+ public static int sub33At(int len, int x, int[] z, int zPos)
+ {
+ // assert zPos <= (len - 2);
+ long c = (z[zPos + 0] & M) - (x & M);
+ z[zPos + 0] = (int)c;
+ c >>= 32;
+ c += (z[zPos + 1] & M) - 1;
+ z[zPos + 1] = (int)c;
+ c >>= 32;
+ return c == 0 ? 0 : decAt(len, z, zPos + 2);
+ }
+
+ public static int sub33At(int len, int x, int[] z, int zOff, int zPos)
+ {
+ // assert zPos <= (len - 2);
+ long c = (z[zOff + zPos] & M) - (x & M);
+ z[zOff + zPos] = (int)c;
+ c >>= 32;
+ c += (z[zOff + zPos + 1] & M) - 1;
+ z[zOff + zPos + 1] = (int)c;
+ c >>= 32;
+ return c == 0 ? 0 : decAt(len, z, zOff, zPos + 2);
+ }
+
+ public static int sub33From(int len, int x, int[] z)
+ {
+ long c = (z[0] & M) - (x & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (z[1] & M) - 1;
+ z[1] = (int)c;
+ c >>= 32;
+ return c == 0 ? 0 : decAt(len, z, 2);
+ }
+
+ public static int sub33From(int len, int x, int[] z, int zOff)
+ {
+ long c = (z[zOff + 0] & M) - (x & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 1] & M) - 1;
+ z[zOff + 1] = (int)c;
+ c >>= 32;
+ return c == 0 ? 0 : decAt(len, z, zOff, 2);
+ }
+
+ public static int subBothFrom(int len, int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ c += (z[i] & M) - (x[i] & M) - (y[i] & M);
+ z[i] = (int)c;
+ c >>= 32;
+ }
+ return (int)c;
+ }
+
+ public static int subBothFrom(int len, int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ c += (z[zOff + i] & M) - (x[xOff + i] & M) - (y[yOff + i] & M);
+ z[zOff + i] = (int)c;
+ c >>= 32;
+ }
+ return (int)c;
+ }
+
+ public static int subDWordAt(int len, long x, int[] z, int zPos)
+ {
+ // assert zPos <= (len - 2);
+ long c = (z[zPos + 0] & M) - (x & M);
+ z[zPos + 0] = (int)c;
+ c >>= 32;
+ c += (z[zPos + 1] & M) - (x >>> 32);
+ z[zPos + 1] = (int)c;
+ c >>= 32;
+ return c == 0 ? 0 : decAt(len, z, zPos + 2);
+ }
+
+ public static int subDWordAt(int len, long x, int[] z, int zOff, int zPos)
+ {
+ // assert zPos <= (len - 2);
+ long c = (z[zOff + zPos] & M) - (x & M);
+ z[zOff + zPos] = (int)c;
+ c >>= 32;
+ c += (z[zOff + zPos + 1] & M) - (x >>> 32);
+ z[zOff + zPos + 1] = (int)c;
+ c >>= 32;
+ return c == 0 ? 0 : decAt(len, z, zOff, zPos + 2);
+ }
+
+ public static int subDWordFrom(int len, long x, int[] z)
+ {
+ long c = (z[0] & M) - (x & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (z[1] & M) - (x >>> 32);
+ z[1] = (int)c;
+ c >>= 32;
+ return c == 0 ? 0 : decAt(len, z, 2);
+ }
+
+ public static int subDWordFrom(int len, long x, int[] z, int zOff)
+ {
+ long c = (z[zOff + 0] & M) - (x & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 1] & M) - (x >>> 32);
+ z[zOff + 1] = (int)c;
+ c >>= 32;
+ return c == 0 ? 0 : decAt(len, z, zOff, 2);
+ }
+
+ public static int subFrom(int len, int[] x, int[] z)
+ {
+ long c = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ c += (z[i] & M) - (x[i] & M);
+ z[i] = (int)c;
+ c >>= 32;
+ }
+ return (int)c;
+ }
+
+ public static int subFrom(int len, int[] x, int xOff, int[] z, int zOff)
+ {
+ long c = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ c += (z[zOff + i] & M) - (x[xOff + i] & M);
+ z[zOff + i] = (int)c;
+ c >>= 32;
+ }
+ return (int)c;
+ }
+
+ public static int subWordAt(int len, int x, int[] z, int zPos)
+ {
+ // assert zPos <= (len - 1);
+ long c = (z[zPos] & M) - (x & M);
+ z[zPos] = (int)c;
+ c >>= 32;
+ return c == 0 ? 0 : decAt(len, z, zPos + 1);
+ }
+
+ public static int subWordAt(int len, int x, int[] z, int zOff, int zPos)
+ {
+ // assert zPos <= (len - 1);
+ long c = (z[zOff + zPos] & M) - (x & M);
+ z[zOff + zPos] = (int)c;
+ c >>= 32;
+ return c == 0 ? 0 : decAt(len, z, zOff, zPos + 1);
+ }
+
+ public static int subWordFrom(int len, int x, int[] z)
+ {
+ long c = (z[0] & M) - (x & M);
+ z[0] = (int)c;
+ c >>= 32;
+ return c == 0 ? 0 : decAt(len, z, 1);
+ }
+
+ public static int subWordFrom(int len, int x, int[] z, int zOff)
+ {
+ long c = (z[zOff + 0] & M) - (x & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ return c == 0 ? 0 : decAt(len, z, zOff, 1);
+ }
+
+ public static BigInteger toBigInteger(int len, int[] x)
+ {
+ byte[] bs = new byte[len << 2];
+ for (int i = 0; i < len; ++i)
+ {
+ int x_i = x[i];
+ if (x_i != 0)
+ {
+ Pack.intToBigEndian(x_i, bs, (len - 1 - i) << 2);
+ }
+ }
+ return new BigInteger(1, bs);
+ }
+
+ public static void zero(int len, int[] z)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ z[i] = 0;
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/raw/Nat192.java b/core/src/main/java/org/spongycastle/math/raw/Nat192.java
new file mode 100644
index 00000000..0b71cfe8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/raw/Nat192.java
@@ -0,0 +1,968 @@
+package org.spongycastle.math.raw;
+
+import java.math.BigInteger;
+
+import org.spongycastle.util.Pack;
+
+public abstract class Nat192
+{
+ private static final long M = 0xFFFFFFFFL;
+
+ public static int add(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) + (y[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (x[1] & M) + (y[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += (x[2] & M) + (y[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += (x[3] & M) + (y[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += (x[4] & M) + (y[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ c += (x[5] & M) + (y[5] & M);
+ z[5] = (int)c;
+ c >>>= 32;
+ 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;
+ return (int)c;
+ }
+
+ public static int addTo(int[] x, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) + (z[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (x[1] & M) + (z[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += (x[2] & M) + (z[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += (x[3] & M) + (z[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += (x[4] & M) + (z[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ c += (x[5] & M) + (z[5] & M);
+ z[5] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn)
+ {
+ long c = cIn & M;
+ c += (x[xOff + 0] & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 1] & M) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 2] & M) + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 3] & M) + (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 4] & M) + (z[zOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 5] & M) + (z[zOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addToEachOther(int[] u, int uOff, int[] v, int vOff)
+ {
+ long c = 0;
+ c += (u[uOff + 0] & M) + (v[vOff + 0] & M);
+ u[uOff + 0] = (int)c;
+ v[vOff + 0] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 1] & M) + (v[vOff + 1] & M);
+ u[uOff + 1] = (int)c;
+ v[vOff + 1] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 2] & M) + (v[vOff + 2] & M);
+ u[uOff + 2] = (int)c;
+ v[vOff + 2] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 3] & M) + (v[vOff + 3] & M);
+ u[uOff + 3] = (int)c;
+ v[vOff + 3] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 4] & M) + (v[vOff + 4] & M);
+ u[uOff + 4] = (int)c;
+ v[vOff + 4] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 5] & M) + (v[vOff + 5] & M);
+ u[uOff + 5] = (int)c;
+ v[vOff + 5] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static void copy(int[] x, int[] z)
+ {
+ z[0] = x[0];
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ z[4] = x[4];
+ z[5] = x[5];
+ }
+
+ public static int[] create()
+ {
+ return new int[6];
+ }
+
+ public static int[] createExt()
+ {
+ return new int[12];
+ }
+
+ public static boolean diff(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ boolean pos = gte(x, xOff, y, yOff);
+ if (pos)
+ {
+ sub(x, xOff, y, yOff, z, zOff);
+ }
+ else
+ {
+ sub(y, yOff, x, xOff, z, zOff);
+ }
+ return pos;
+ }
+
+ public static boolean eq(int[] x, int[] y)
+ {
+ for (int i = 5; i >= 0; --i)
+ {
+ if (x[i] != y[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static int[] fromBigInteger(BigInteger x)
+ {
+ if (x.signum() < 0 || x.bitLength() > 192)
+ {
+ 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;
+ }
+ int w = bit >> 5;
+ if (w < 0 || w >= 6)
+ {
+ return 0;
+ }
+ int b = bit & 31;
+ return (x[w] >>> b) & 1;
+ }
+
+ public static boolean gte(int[] x, int[] y)
+ {
+ for (int i = 5; i >= 0; --i)
+ {
+ int x_i = x[i] ^ Integer.MIN_VALUE;
+ int y_i = y[i] ^ Integer.MIN_VALUE;
+ if (x_i < y_i)
+ return false;
+ if (x_i > y_i)
+ return true;
+ }
+ return true;
+ }
+
+ public static boolean gte(int[] x, int xOff, int[] y, int yOff)
+ {
+ for (int i = 5; i >= 0; --i)
+ {
+ int x_i = x[xOff + i] ^ Integer.MIN_VALUE;
+ int y_i = y[yOff + i] ^ Integer.MIN_VALUE;
+ if (x_i < y_i)
+ return false;
+ if (x_i > y_i)
+ return true;
+ }
+ return true;
+ }
+
+ public static boolean isOne(int[] x)
+ {
+ if (x[0] != 1)
+ {
+ return false;
+ }
+ for (int i = 1; i < 6; ++i)
+ {
+ if (x[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isZero(int[] x)
+ {
+ for (int i = 0; i < 6; ++i)
+ {
+ if (x[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void mul(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 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;
+ zz[6] = (int)c;
+ }
+
+ for (int i = 1; i < 6; ++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;
+ zz[i + 6] = (int)c;
+ }
+ }
+
+ public static void mul(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff)
+ {
+ long y_0 = y[yOff + 0] & M;
+ long y_1 = y[yOff + 1] & M;
+ long y_2 = y[yOff + 2] & M;
+ long y_3 = y[yOff + 3] & M;
+ long y_4 = y[yOff + 4] & M;
+ long y_5 = y[yOff + 5] & M;
+
+ {
+ long c = 0, x_0 = x[xOff + 0] & M;
+ c += x_0 * y_0;
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_1;
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_2;
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_3;
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_4;
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_5;
+ zz[zzOff + 5] = (int)c;
+ c >>>= 32;
+ zz[zzOff + 6] = (int)c;
+ }
+
+ for (int i = 1; i < 6; ++i)
+ {
+ ++zzOff;
+ long c = 0, x_i = x[xOff + i] & M;
+ c += x_i * y_0 + (zz[zzOff + 0] & M);
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[zzOff + 1] & M);
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[zzOff + 2] & M);
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[zzOff + 3] & M);
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += x_i * y_4 + (zz[zzOff + 4] & M);
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ c += x_i * y_5 + (zz[zzOff + 5] & M);
+ zz[zzOff + 5] = (int)c;
+ c >>>= 32;
+ zz[zzOff + 6] = (int)c;
+ }
+ }
+
+ public static int mulAddTo(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 zc = 0;
+ for (int i = 0; i < 6; ++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 += zc + (zz[i + 6] & M);
+ zz[i + 6] = (int)c;
+ zc = c >>> 32;
+ }
+ return (int)zc;
+ }
+
+ public static int mulAddTo(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff)
+ {
+ long y_0 = y[yOff + 0] & M;
+ long y_1 = y[yOff + 1] & M;
+ long y_2 = y[yOff + 2] & M;
+ long y_3 = y[yOff + 3] & M;
+ long y_4 = y[yOff + 4] & M;
+ long y_5 = y[yOff + 5] & M;
+
+ long zc = 0;
+ for (int i = 0; i < 6; ++i)
+ {
+ long c = 0, x_i = x[xOff + i] & M;
+ c += x_i * y_0 + (zz[zzOff + 0] & M);
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[zzOff + 1] & M);
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[zzOff + 2] & M);
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[zzOff + 3] & M);
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += x_i * y_4 + (zz[zzOff + 4] & M);
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ c += x_i * y_5 + (zz[zzOff + 5] & M);
+ zz[zzOff + 5] = (int)c;
+ c >>>= 32;
+ c += zc + (zz[zzOff + 6] & M);
+ zz[zzOff + 6] = (int)c;
+ zc = c >>> 32;
+ ++zzOff;
+ }
+ return (int)zc;
+ }
+
+ public static long mul33Add(int w, int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ // assert w >>> 31 == 0;
+
+ long c = 0, wVal = w & M;
+ long x0 = x[xOff + 0] & M;
+ c += wVal * x0 + (y[yOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ long x1 = x[xOff + 1] & M;
+ c += wVal * x1 + x0 + (y[yOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ long x2 = x[xOff + 2] & M;
+ c += wVal * x2 + x1 + (y[yOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ long x3 = x[xOff + 3] & M;
+ c += wVal * x3 + x2 + (y[yOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ long x4 = x[xOff + 4] & M;
+ c += wVal * x4 + x3 + (y[yOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ long x5 = x[xOff + 5] & M;
+ c += wVal * x5 + x4 + (y[yOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>>= 32;
+ c += x5;
+ return c;
+ }
+
+ public static int mulWordAddExt(int x, int[] yy, int yyOff, int[] zz, int zzOff)
+ {
+ // assert yyOff <= 6;
+ // assert zzOff <= 6;
+ long c = 0, xVal = x & M;
+ 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;
+ return (int)c;
+ }
+
+ public static int mul33DWordAdd(int x, long y, int[] z, int zOff)
+ {
+ // assert x >>> 31 == 0;
+ // assert zOff <= 2;
+
+ long c = 0, xVal = x & M;
+ long y00 = y & M;
+ c += xVal * y00 + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ long y01 = y >>> 32;
+ c += xVal * y01 + y00 + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += y01 + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(6, z, zOff, 4);
+ }
+
+ public static int mul33WordAdd(int x, int y, int[] z, int zOff)
+ {
+ // assert x >>> 31 == 0;
+ // assert zOff <= 3;
+
+ long c = 0, xVal = x & M, yVal = y & M;
+ c += yVal * xVal + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += yVal + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(6, z, zOff, 3);
+ }
+
+ public static int mulWordDwordAdd(int x, long y, int[] z, int zOff)
+ {
+ // assert zOff <= 3;
+ long c = 0, xVal = x & M;
+ c += xVal * (y & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += xVal * (y >>> 32) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(6, z, zOff, 3);
+ }
+
+ public static int mulWord(int x, int[] y, int[] z, int zOff)
+ {
+ long c = 0, xVal = x & M;
+ int i = 0;
+ do
+ {
+ c += xVal * (y[i] & M);
+ z[zOff + i] = (int)c;
+ c >>>= 32;
+ }
+ while (++i < 6);
+ return (int)c;
+ }
+
+ public static void square(int[] x, int[] zz)
+ {
+ long x_0 = x[0] & M;
+ long zz_1;
+
+ int c = 0, w;
+ {
+ int i = 5, j = 12;
+ do
+ {
+ long xVal = (x[i--] & M);
+ long p = xVal * xVal;
+ zz[--j] = (c << 31) | (int)(p >>> 33);
+ zz[--j] = (int)(p >>> 1);
+ c = (int)p;
+ }
+ while (i > 0);
+
+ {
+ long p = x_0 * x_0;
+ zz_1 = ((c << 31) & M) | (p >>> 33);
+ zz[0] = (int)p;
+ c = (int)(p >>> 32) & 1;
+ }
+ }
+
+ long x_1 = x[1] & M;
+ long zz_2 = zz[2] & M;
+
+ {
+ zz_1 += x_1 * x_0;
+ w = (int)zz_1;
+ zz[1] = (w << 1) | c;
+ c = w >>> 31;
+ zz_2 += zz_1 >>> 32;
+ }
+
+ long x_2 = x[2] & M;
+ long zz_3 = zz[3] & M;
+ long zz_4 = zz[4] & M;
+ {
+ zz_2 += x_2 * x_0;
+ w = (int)zz_2;
+ zz[2] = (w << 1) | c;
+ c = w >>> 31;
+ zz_3 += (zz_2 >>> 32) + x_2 * x_1;
+ zz_4 += zz_3 >>> 32;
+ zz_3 &= M;
+ }
+
+ long x_3 = x[3] & M;
+ long zz_5 = zz[5] & M;
+ long zz_6 = zz[6] & M;
+ {
+ zz_3 += x_3 * x_0;
+ w = (int)zz_3;
+ zz[3] = (w << 1) | c;
+ c = w >>> 31;
+ zz_4 += (zz_3 >>> 32) + x_3 * x_1;
+ zz_5 += (zz_4 >>> 32) + x_3 * x_2;
+ zz_4 &= M;
+ zz_6 += zz_5 >>> 32;
+ zz_5 &= M;
+ }
+
+ long x_4 = x[4] & M;
+ long zz_7 = zz[7] & M;
+ long zz_8 = zz[8] & M;
+ {
+ zz_4 += x_4 * x_0;
+ w = (int)zz_4;
+ zz[4] = (w << 1) | c;
+ c = w >>> 31;
+ zz_5 += (zz_4 >>> 32) + x_4 * x_1;
+ zz_6 += (zz_5 >>> 32) + x_4 * x_2;
+ zz_5 &= M;
+ zz_7 += (zz_6 >>> 32) + x_4 * x_3;
+ zz_6 &= M;
+ zz_8 += zz_7 >>> 32;
+ zz_7 &= M;
+ }
+
+ long x_5 = x[5] & M;
+ long zz_9 = zz[9] & M;
+ long zz_10 = zz[10] & M;
+ {
+ zz_5 += x_5 * x_0;
+ w = (int)zz_5;
+ zz[5] = (w << 1) | c;
+ c = w >>> 31;
+ zz_6 += (zz_5 >>> 32) + x_5 * x_1;
+ zz_7 += (zz_6 >>> 32) + x_5 * x_2;
+ zz_8 += (zz_7 >>> 32) + x_5 * x_3;
+ zz_9 += (zz_8 >>> 32) + x_5 * x_4;
+ zz_10 += zz_9 >>> 32;
+ }
+
+ w = (int)zz_6;
+ zz[6] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_7;
+ zz[7] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_8;
+ zz[8] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_9;
+ zz[9] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_10;
+ zz[10] = (w << 1) | c;
+ c = w >>> 31;
+ w = zz[11] + (int)(zz_10 >> 32);
+ zz[11] = (w << 1) | c;
+ }
+
+ public static void square(int[] x, int xOff, int[] zz, int zzOff)
+ {
+ long x_0 = x[xOff + 0] & M;
+ long zz_1;
+
+ int c = 0, w;
+ {
+ int i = 5, j = 12;
+ do
+ {
+ long xVal = (x[xOff + i--] & M);
+ long p = xVal * xVal;
+ zz[zzOff + --j] = (c << 31) | (int)(p >>> 33);
+ zz[zzOff + --j] = (int)(p >>> 1);
+ c = (int)p;
+ }
+ while (i > 0);
+
+ {
+ long p = x_0 * x_0;
+ zz_1 = ((c << 31) & M) | (p >>> 33);
+ zz[zzOff + 0] = (int)p;
+ c = (int)(p >>> 32) & 1;
+ }
+ }
+
+ long x_1 = x[xOff + 1] & M;
+ long zz_2 = zz[zzOff + 2] & M;
+
+ {
+ zz_1 += x_1 * x_0;
+ w = (int)zz_1;
+ zz[zzOff + 1] = (w << 1) | c;
+ c = w >>> 31;
+ zz_2 += zz_1 >>> 32;
+ }
+
+ long x_2 = x[xOff + 2] & M;
+ long zz_3 = zz[zzOff + 3] & M;
+ long zz_4 = zz[zzOff + 4] & M;
+ {
+ zz_2 += x_2 * x_0;
+ w = (int)zz_2;
+ zz[zzOff + 2] = (w << 1) | c;
+ c = w >>> 31;
+ zz_3 += (zz_2 >>> 32) + x_2 * x_1;
+ zz_4 += zz_3 >>> 32;
+ zz_3 &= M;
+ }
+
+ long x_3 = x[xOff + 3] & M;
+ long zz_5 = zz[zzOff + 5] & M;
+ long zz_6 = zz[zzOff + 6] & M;
+ {
+ zz_3 += x_3 * x_0;
+ w = (int)zz_3;
+ zz[zzOff + 3] = (w << 1) | c;
+ c = w >>> 31;
+ zz_4 += (zz_3 >>> 32) + x_3 * x_1;
+ zz_5 += (zz_4 >>> 32) + x_3 * x_2;
+ zz_4 &= M;
+ zz_6 += zz_5 >>> 32;
+ zz_5 &= M;
+ }
+
+ long x_4 = x[xOff + 4] & M;
+ long zz_7 = zz[zzOff + 7] & M;
+ long zz_8 = zz[zzOff + 8] & M;
+ {
+ zz_4 += x_4 * x_0;
+ w = (int)zz_4;
+ zz[zzOff + 4] = (w << 1) | c;
+ c = w >>> 31;
+ zz_5 += (zz_4 >>> 32) + x_4 * x_1;
+ zz_6 += (zz_5 >>> 32) + x_4 * x_2;
+ zz_5 &= M;
+ zz_7 += (zz_6 >>> 32) + x_4 * x_3;
+ zz_6 &= M;
+ zz_8 += zz_7 >>> 32;
+ zz_7 &= M;
+ }
+
+ long x_5 = x[xOff + 5] & M;
+ long zz_9 = zz[zzOff + 9] & M;
+ long zz_10 = zz[zzOff + 10] & M;
+ {
+ zz_5 += x_5 * x_0;
+ w = (int)zz_5;
+ zz[zzOff + 5] = (w << 1) | c;
+ c = w >>> 31;
+ zz_6 += (zz_5 >>> 32) + x_5 * x_1;
+ zz_7 += (zz_6 >>> 32) + x_5 * x_2;
+ zz_8 += (zz_7 >>> 32) + x_5 * x_3;
+ zz_9 += (zz_8 >>> 32) + x_5 * x_4;
+ zz_10 += zz_9 >>> 32;
+ }
+
+ w = (int)zz_6;
+ zz[zzOff + 6] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_7;
+ zz[zzOff + 7] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_8;
+ zz[zzOff + 8] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_9;
+ zz[zzOff + 9] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_10;
+ zz[zzOff + 10] = (w << 1) | c;
+ c = w >>> 31;
+ w = zz[zzOff + 11] + (int)(zz_10 >> 32);
+ zz[zzOff + 11] = (w << 1) | c;
+ }
+
+ public static int sub(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) - (y[0] & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (x[1] & M) - (y[1] & M);
+ z[1] = (int)c;
+ c >>= 32;
+ c += (x[2] & M) - (y[2] & M);
+ z[2] = (int)c;
+ c >>= 32;
+ c += (x[3] & M) - (y[3] & M);
+ z[3] = (int)c;
+ c >>= 32;
+ c += (x[4] & M) - (y[4] & M);
+ z[4] = (int)c;
+ c >>= 32;
+ c += (x[5] & M) - (y[5] & M);
+ z[5] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int sub(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (x[xOff + 0] & M) - (y[yOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 1] & M) - (y[yOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 2] & M) - (y[yOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 3] & M) - (y[yOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 4] & M) - (y[yOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 5] & M) - (y[yOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>= 32;
+ 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;
+ return (int)c;
+ }
+
+ public static int subFrom(int[] x, int[] z)
+ {
+ long c = 0;
+ c += (z[0] & M) - (x[0] & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (z[1] & M) - (x[1] & M);
+ z[1] = (int)c;
+ c >>= 32;
+ c += (z[2] & M) - (x[2] & M);
+ z[2] = (int)c;
+ c >>= 32;
+ c += (z[3] & M) - (x[3] & M);
+ z[3] = (int)c;
+ c >>= 32;
+ c += (z[4] & M) - (x[4] & M);
+ z[4] = (int)c;
+ c >>= 32;
+ c += (z[5] & M) - (x[5] & M);
+ z[5] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int subFrom(int[] x, int xOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (z[zOff + 0] & M) - (x[xOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 1] & M) - (x[xOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 2] & M) - (x[xOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 3] & M) - (x[xOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 4] & M) - (x[xOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 5] & M) - (x[xOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static BigInteger toBigInteger(int[] x)
+ {
+ byte[] bs = new byte[24];
+ for (int i = 0; i < 6; ++i)
+ {
+ int x_i = x[i];
+ if (x_i != 0)
+ {
+ Pack.intToBigEndian(x_i, bs, (5 - i) << 2);
+ }
+ }
+ return new BigInteger(1, bs);
+ }
+
+ public static void zero(int[] z)
+ {
+ z[0] = 0;
+ z[1] = 0;
+ z[2] = 0;
+ z[3] = 0;
+ z[4] = 0;
+ z[5] = 0;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/raw/Nat224.java b/core/src/main/java/org/spongycastle/math/raw/Nat224.java
new file mode 100644
index 00000000..8f4e47b8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/raw/Nat224.java
@@ -0,0 +1,1182 @@
+package org.spongycastle.math.raw;
+
+import java.math.BigInteger;
+
+import org.spongycastle.util.Pack;
+
+public abstract class Nat224
+{
+ private static final long M = 0xFFFFFFFFL;
+
+ public static int add(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) + (y[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (x[1] & M) + (y[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += (x[2] & M) + (y[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += (x[3] & M) + (y[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += (x[4] & M) + (y[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ c += (x[5] & M) + (y[5] & M);
+ z[5] = (int)c;
+ c >>>= 32;
+ c += (x[6] & M) + (y[6] & M);
+ z[6] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int add(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (x[xOff + 0] & M) + (y[yOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 1] & M) + (y[yOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 2] & M) + (y[yOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 3] & M) + (y[yOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 4] & M) + (y[yOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 5] & M) + (y[yOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 6] & M) + (y[yOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>>= 32;
+ 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;
+ return (int)c;
+ }
+
+ public static int addBothTo(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (x[xOff + 0] & M) + (y[yOff + 0] & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 1] & M) + (y[yOff + 1] & M) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 2] & M) + (y[yOff + 2] & M) + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 3] & M) + (y[yOff + 3] & M) + (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 4] & M) + (y[yOff + 4] & M) + (z[zOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 5] & M) + (y[yOff + 5] & M) + (z[zOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 6] & M) + (y[yOff + 6] & M) + (z[zOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addTo(int[] x, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) + (z[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (x[1] & M) + (z[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += (x[2] & M) + (z[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += (x[3] & M) + (z[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += (x[4] & M) + (z[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ c += (x[5] & M) + (z[5] & M);
+ z[5] = (int)c;
+ c >>>= 32;
+ c += (x[6] & M) + (z[6] & M);
+ z[6] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn)
+ {
+ long c = cIn & M;
+ c += (x[xOff + 0] & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 1] & M) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 2] & M) + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 3] & M) + (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 4] & M) + (z[zOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 5] & M) + (z[zOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 6] & M) + (z[zOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addToEachOther(int[] u, int uOff, int[] v, int vOff)
+ {
+ long c = 0;
+ c += (u[uOff + 0] & M) + (v[vOff + 0] & M);
+ u[uOff + 0] = (int)c;
+ v[vOff + 0] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 1] & M) + (v[vOff + 1] & M);
+ u[uOff + 1] = (int)c;
+ v[vOff + 1] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 2] & M) + (v[vOff + 2] & M);
+ u[uOff + 2] = (int)c;
+ v[vOff + 2] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 3] & M) + (v[vOff + 3] & M);
+ u[uOff + 3] = (int)c;
+ v[vOff + 3] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 4] & M) + (v[vOff + 4] & M);
+ u[uOff + 4] = (int)c;
+ v[vOff + 4] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 5] & M) + (v[vOff + 5] & M);
+ u[uOff + 5] = (int)c;
+ v[vOff + 5] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 6] & M) + (v[vOff + 6] & M);
+ u[uOff + 6] = (int)c;
+ v[vOff + 6] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static void copy(int[] x, int[] z)
+ {
+ z[0] = x[0];
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ z[4] = x[4];
+ z[5] = x[5];
+ z[6] = x[6];
+ }
+
+ public static int[] create()
+ {
+ return new int[7];
+ }
+
+ public static int[] createExt()
+ {
+ return new int[14];
+ }
+
+ public static boolean diff(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ boolean pos = gte(x, xOff, y, yOff);
+ if (pos)
+ {
+ sub(x, xOff, y, yOff, z, zOff);
+ }
+ else
+ {
+ sub(y, yOff, x, xOff, z, zOff);
+ }
+ return pos;
+ }
+
+ public static boolean eq(int[] x, int[] y)
+ {
+ for (int i = 6; i >= 0; --i)
+ {
+ if (x[i] != y[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static int[] fromBigInteger(BigInteger x)
+ {
+ if (x.signum() < 0 || x.bitLength() > 224)
+ {
+ 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;
+ }
+ int w = bit >> 5;
+ if (w < 0 || w >= 7)
+ {
+ return 0;
+ }
+ int b = bit & 31;
+ return (x[w] >>> b) & 1;
+ }
+
+ public static boolean gte(int[] x, int[] y)
+ {
+ for (int i = 6; i >= 0; --i)
+ {
+ int x_i = x[i] ^ Integer.MIN_VALUE;
+ int y_i = y[i] ^ Integer.MIN_VALUE;
+ if (x_i < y_i)
+ return false;
+ if (x_i > y_i)
+ return true;
+ }
+ return true;
+ }
+
+ public static boolean gte(int[] x, int xOff, int[] y, int yOff)
+ {
+ for (int i = 6; i >= 0; --i)
+ {
+ int x_i = x[xOff + i] ^ Integer.MIN_VALUE;
+ int y_i = y[yOff + i] ^ Integer.MIN_VALUE;
+ if (x_i < y_i)
+ return false;
+ if (x_i > y_i)
+ return true;
+ }
+ return true;
+ }
+
+ public static boolean isOne(int[] x)
+ {
+ if (x[0] != 1)
+ {
+ return false;
+ }
+ for (int i = 1; i < 7; ++i)
+ {
+ if (x[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isZero(int[] x)
+ {
+ for (int i = 0; i < 7; ++i)
+ {
+ if (x[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void mul(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 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;
+ zz[7] = (int)c;
+ }
+
+ for (int i = 1; i < 7; ++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;
+ zz[i + 7] = (int)c;
+ }
+ }
+
+ public static void mul(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff)
+ {
+ long y_0 = y[yOff + 0] & M;
+ long y_1 = y[yOff + 1] & M;
+ long y_2 = y[yOff + 2] & M;
+ long y_3 = y[yOff + 3] & M;
+ long y_4 = y[yOff + 4] & M;
+ long y_5 = y[yOff + 5] & M;
+ long y_6 = y[yOff + 6] & M;
+
+ {
+ long c = 0, x_0 = x[xOff + 0] & M;
+ c += x_0 * y_0;
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_1;
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_2;
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_3;
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_4;
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_5;
+ zz[zzOff + 5] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_6;
+ zz[zzOff + 6] = (int)c;
+ c >>>= 32;
+ zz[zzOff + 7] = (int)c;
+ }
+
+ for (int i = 1; i < 7; ++i)
+ {
+ ++zzOff;
+ long c = 0, x_i = x[xOff + i] & M;
+ c += x_i * y_0 + (zz[zzOff + 0] & M);
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[zzOff + 1] & M);
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[zzOff + 2] & M);
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[zzOff + 3] & M);
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += x_i * y_4 + (zz[zzOff + 4] & M);
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ c += x_i * y_5 + (zz[zzOff + 5] & M);
+ zz[zzOff + 5] = (int)c;
+ c >>>= 32;
+ c += x_i * y_6 + (zz[zzOff + 6] & M);
+ zz[zzOff + 6] = (int)c;
+ c >>>= 32;
+ zz[zzOff + 7] = (int)c;
+ }
+ }
+
+ public static int mulAddTo(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 zc = 0;
+ for (int i = 0; i < 7; ++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 += zc + (zz[i + 7] & M);
+ zz[i + 7] = (int)c;
+ zc = c >>> 32;
+ }
+ return (int)zc;
+ }
+
+ public static int mulAddTo(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff)
+ {
+ long y_0 = y[yOff + 0] & M;
+ long y_1 = y[yOff + 1] & M;
+ long y_2 = y[yOff + 2] & M;
+ long y_3 = y[yOff + 3] & M;
+ long y_4 = y[yOff + 4] & M;
+ long y_5 = y[yOff + 5] & M;
+ long y_6 = y[yOff + 6] & M;
+
+ long zc = 0;
+ for (int i = 0; i < 7; ++i)
+ {
+ long c = 0, x_i = x[xOff + i] & M;
+ c += x_i * y_0 + (zz[zzOff + 0] & M);
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[zzOff + 1] & M);
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[zzOff + 2] & M);
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[zzOff + 3] & M);
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += x_i * y_4 + (zz[zzOff + 4] & M);
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ c += x_i * y_5 + (zz[zzOff + 5] & M);
+ zz[zzOff + 5] = (int)c;
+ c >>>= 32;
+ c += x_i * y_6 + (zz[zzOff + 6] & M);
+ zz[zzOff + 6] = (int)c;
+ c >>>= 32;
+ c += zc + (zz[zzOff + 7] & M);
+ zz[zzOff + 7] = (int)c;
+ zc = c >>> 32;
+ ++zzOff;
+ }
+ return (int)zc;
+ }
+
+ public static long mul33Add(int w, int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ // assert w >>> 31 == 0;
+
+ long c = 0, wVal = w & M;
+ long x0 = x[xOff + 0] & M;
+ c += wVal * x0 + (y[yOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ long x1 = x[xOff + 1] & M;
+ c += wVal * x1 + x0 + (y[yOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ long x2 = x[xOff + 2] & M;
+ c += wVal * x2 + x1 + (y[yOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ long x3 = x[xOff + 3] & M;
+ c += wVal * x3 + x2 + (y[yOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ long x4 = x[xOff + 4] & M;
+ c += wVal * x4 + x3 + (y[yOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ long x5 = x[xOff + 5] & M;
+ c += wVal * x5 + x4 + (y[yOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>>= 32;
+ long x6 = x[xOff + 6] & M;
+ c += wVal * x6 + x5 + (y[yOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>>= 32;
+ c += x6;
+ return c;
+ }
+
+ public static int mulByWord(int x, int[] z)
+ {
+ long c = 0, xVal = x & M;
+ c += xVal * (z[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[5] & M);
+ z[5] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[6] & M);
+ z[6] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int mulByWordAddTo(int x, int[] y, int[] z)
+ {
+ long c = 0, xVal = x & M;
+ c += xVal * (z[0] & M) + (y[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[1] & M) + (y[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[2] & M) + (y[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[3] & M) + (y[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[4] & M) + (y[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[5] & M) + (y[5] & M);
+ z[5] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[6] & M) + (y[6] & M);
+ z[6] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int mulWordAddTo(int x, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0, xVal = x & M;
+ c += xVal * (y[yOff + 0] & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 1] & M) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 2] & M) + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 3] & M) + (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 4] & M) + (z[zOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 5] & M) + (z[zOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 6] & M) + (z[zOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int mul33DWordAdd(int x, long y, int[] z, int zOff)
+ {
+ // assert x >>> 31 == 0;
+ // assert zOff <= 3;
+
+ long c = 0, xVal = x & M;
+ long y00 = y & M;
+ c += xVal * y00 + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ long y01 = y >>> 32;
+ c += xVal * y01 + y00 + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += y01 + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(7, z, zOff, 4);
+ }
+
+ public static int mul33WordAdd(int x, int y, int[] z, int zOff)
+ {
+ // assert x >>> 31 == 0;
+ // assert zOff <= 4;
+
+ long c = 0, xVal = x & M, yVal = y & M;
+ c += yVal * xVal + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += yVal + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(7, z, zOff, 3);
+ }
+
+ public static int mulWordDwordAdd(int x, long y, int[] z, int zOff)
+ {
+ // assert zOff <= 4;
+ long c = 0, xVal = x & M;
+ c += xVal * (y & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += xVal * (y >>> 32) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(7, z, zOff, 3);
+ }
+
+ public static int mulWord(int x, int[] y, int[] z, int zOff)
+ {
+ long c = 0, xVal = x & M;
+ int i = 0;
+ do
+ {
+ c += xVal * (y[i] & M);
+ z[zOff + i] = (int)c;
+ c >>>= 32;
+ }
+ while (++i < 7);
+ return (int)c;
+ }
+
+ public static void square(int[] x, int[] zz)
+ {
+ long x_0 = x[0] & M;
+ long zz_1;
+
+ int c = 0, w;
+ {
+ int i = 6, j = 14;
+ do
+ {
+ long xVal = (x[i--] & M);
+ long p = xVal * xVal;
+ zz[--j] = (c << 31) | (int)(p >>> 33);
+ zz[--j] = (int)(p >>> 1);
+ c = (int)p;
+ }
+ while (i > 0);
+
+ {
+ long p = x_0 * x_0;
+ zz_1 = ((c << 31) & M) | (p >>> 33);
+ zz[0] = (int)p;
+ c = (int)(p >>> 32) & 1;
+ }
+ }
+
+ long x_1 = x[1] & M;
+ long zz_2 = zz[2] & M;
+
+ {
+ zz_1 += x_1 * x_0;
+ w = (int)zz_1;
+ zz[1] = (w << 1) | c;
+ c = w >>> 31;
+ zz_2 += zz_1 >>> 32;
+ }
+
+ long x_2 = x[2] & M;
+ long zz_3 = zz[3] & M;
+ long zz_4 = zz[4] & M;
+ {
+ zz_2 += x_2 * x_0;
+ w = (int)zz_2;
+ zz[2] = (w << 1) | c;
+ c = w >>> 31;
+ zz_3 += (zz_2 >>> 32) + x_2 * x_1;
+ zz_4 += zz_3 >>> 32;
+ zz_3 &= M;
+ }
+
+ long x_3 = x[3] & M;
+ long zz_5 = zz[5] & M;
+ long zz_6 = zz[6] & M;
+ {
+ zz_3 += x_3 * x_0;
+ w = (int)zz_3;
+ zz[3] = (w << 1) | c;
+ c = w >>> 31;
+ zz_4 += (zz_3 >>> 32) + x_3 * x_1;
+ zz_5 += (zz_4 >>> 32) + x_3 * x_2;
+ zz_4 &= M;
+ zz_6 += zz_5 >>> 32;
+ zz_5 &= M;
+ }
+
+ long x_4 = x[4] & M;
+ long zz_7 = zz[7] & M;
+ long zz_8 = zz[8] & M;
+ {
+ zz_4 += x_4 * x_0;
+ w = (int)zz_4;
+ zz[4] = (w << 1) | c;
+ c = w >>> 31;
+ zz_5 += (zz_4 >>> 32) + x_4 * x_1;
+ zz_6 += (zz_5 >>> 32) + x_4 * x_2;
+ zz_5 &= M;
+ zz_7 += (zz_6 >>> 32) + x_4 * x_3;
+ zz_6 &= M;
+ zz_8 += zz_7 >>> 32;
+ zz_7 &= M;
+ }
+
+ long x_5 = x[5] & M;
+ long zz_9 = zz[9] & M;
+ long zz_10 = zz[10] & M;
+ {
+ zz_5 += x_5 * x_0;
+ w = (int)zz_5;
+ zz[5] = (w << 1) | c;
+ c = w >>> 31;
+ zz_6 += (zz_5 >>> 32) + x_5 * x_1;
+ zz_7 += (zz_6 >>> 32) + x_5 * x_2;
+ zz_6 &= M;
+ zz_8 += (zz_7 >>> 32) + x_5 * x_3;
+ zz_7 &= M;
+ zz_9 += (zz_8 >>> 32) + x_5 * x_4;
+ zz_8 &= M;
+ zz_10 += zz_9 >>> 32;
+ zz_9 &= M;
+ }
+
+ long x_6 = x[6] & M;
+ long zz_11 = zz[11] & M;
+ long zz_12 = zz[12] & M;
+ {
+ zz_6 += x_6 * x_0;
+ w = (int)zz_6;
+ zz[6] = (w << 1) | c;
+ c = w >>> 31;
+ zz_7 += (zz_6 >>> 32) + x_6 * x_1;
+ zz_8 += (zz_7 >>> 32) + x_6 * x_2;
+ zz_9 += (zz_8 >>> 32) + x_6 * x_3;
+ zz_10 += (zz_9 >>> 32) + x_6 * x_4;
+ zz_11 += (zz_10 >>> 32) + x_6 * x_5;
+ zz_12 += zz_11 >>> 32;
+ }
+
+ w = (int)zz_7;
+ zz[7] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_8;
+ zz[8] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_9;
+ zz[9] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_10;
+ zz[10] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_11;
+ zz[11] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_12;
+ zz[12] = (w << 1) | c;
+ c = w >>> 31;
+ w = zz[13] + (int)(zz_12 >> 32);
+ zz[13] = (w << 1) | c;
+ }
+
+ public static void square(int[] x, int xOff, int[] zz, int zzOff)
+ {
+ long x_0 = x[xOff + 0] & M;
+ long zz_1;
+
+ int c = 0, w;
+ {
+ int i = 6, j = 14;
+ do
+ {
+ long xVal = (x[xOff + i--] & M);
+ long p = xVal * xVal;
+ zz[zzOff + --j] = (c << 31) | (int)(p >>> 33);
+ zz[zzOff + --j] = (int)(p >>> 1);
+ c = (int)p;
+ }
+ while (i > 0);
+
+ {
+ long p = x_0 * x_0;
+ zz_1 = ((c << 31) & M) | (p >>> 33);
+ zz[zzOff + 0] = (int)p;
+ c = (int)(p >>> 32) & 1;
+ }
+ }
+
+ long x_1 = x[xOff + 1] & M;
+ long zz_2 = zz[zzOff + 2] & M;
+
+ {
+ zz_1 += x_1 * x_0;
+ w = (int)zz_1;
+ zz[zzOff + 1] = (w << 1) | c;
+ c = w >>> 31;
+ zz_2 += zz_1 >>> 32;
+ }
+
+ long x_2 = x[xOff + 2] & M;
+ long zz_3 = zz[zzOff + 3] & M;
+ long zz_4 = zz[zzOff + 4] & M;
+ {
+ zz_2 += x_2 * x_0;
+ w = (int)zz_2;
+ zz[zzOff + 2] = (w << 1) | c;
+ c = w >>> 31;
+ zz_3 += (zz_2 >>> 32) + x_2 * x_1;
+ zz_4 += zz_3 >>> 32;
+ zz_3 &= M;
+ }
+
+ long x_3 = x[xOff + 3] & M;
+ long zz_5 = zz[zzOff + 5] & M;
+ long zz_6 = zz[zzOff + 6] & M;
+ {
+ zz_3 += x_3 * x_0;
+ w = (int)zz_3;
+ zz[zzOff + 3] = (w << 1) | c;
+ c = w >>> 31;
+ zz_4 += (zz_3 >>> 32) + x_3 * x_1;
+ zz_5 += (zz_4 >>> 32) + x_3 * x_2;
+ zz_4 &= M;
+ zz_6 += zz_5 >>> 32;
+ zz_5 &= M;
+ }
+
+ long x_4 = x[xOff + 4] & M;
+ long zz_7 = zz[zzOff + 7] & M;
+ long zz_8 = zz[zzOff + 8] & M;
+ {
+ zz_4 += x_4 * x_0;
+ w = (int)zz_4;
+ zz[zzOff + 4] = (w << 1) | c;
+ c = w >>> 31;
+ zz_5 += (zz_4 >>> 32) + x_4 * x_1;
+ zz_6 += (zz_5 >>> 32) + x_4 * x_2;
+ zz_5 &= M;
+ zz_7 += (zz_6 >>> 32) + x_4 * x_3;
+ zz_6 &= M;
+ zz_8 += zz_7 >>> 32;
+ zz_7 &= M;
+ }
+
+ long x_5 = x[xOff + 5] & M;
+ long zz_9 = zz[zzOff + 9] & M;
+ long zz_10 = zz[zzOff + 10] & M;
+ {
+ zz_5 += x_5 * x_0;
+ w = (int)zz_5;
+ zz[zzOff + 5] = (w << 1) | c;
+ c = w >>> 31;
+ zz_6 += (zz_5 >>> 32) + x_5 * x_1;
+ zz_7 += (zz_6 >>> 32) + x_5 * x_2;
+ zz_6 &= M;
+ zz_8 += (zz_7 >>> 32) + x_5 * x_3;
+ zz_7 &= M;
+ zz_9 += (zz_8 >>> 32) + x_5 * x_4;
+ zz_8 &= M;
+ zz_10 += zz_9 >>> 32;
+ zz_9 &= M;
+ }
+
+ long x_6 = x[xOff + 6] & M;
+ long zz_11 = zz[zzOff + 11] & M;
+ long zz_12 = zz[zzOff + 12] & M;
+ {
+ zz_6 += x_6 * x_0;
+ w = (int)zz_6;
+ zz[zzOff + 6] = (w << 1) | c;
+ c = w >>> 31;
+ zz_7 += (zz_6 >>> 32) + x_6 * x_1;
+ zz_8 += (zz_7 >>> 32) + x_6 * x_2;
+ zz_9 += (zz_8 >>> 32) + x_6 * x_3;
+ zz_10 += (zz_9 >>> 32) + x_6 * x_4;
+ zz_11 += (zz_10 >>> 32) + x_6 * x_5;
+ zz_12 += zz_11 >>> 32;
+ }
+
+ w = (int)zz_7;
+ zz[zzOff + 7] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_8;
+ zz[zzOff + 8] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_9;
+ zz[zzOff + 9] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_10;
+ zz[zzOff + 10] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_11;
+ zz[zzOff + 11] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_12;
+ zz[zzOff + 12] = (w << 1) | c;
+ c = w >>> 31;
+ w = zz[zzOff + 13] + (int)(zz_12 >> 32);
+ zz[zzOff + 13] = (w << 1) | c;
+ }
+
+ public static int sub(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) - (y[0] & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (x[1] & M) - (y[1] & M);
+ z[1] = (int)c;
+ c >>= 32;
+ c += (x[2] & M) - (y[2] & M);
+ z[2] = (int)c;
+ c >>= 32;
+ c += (x[3] & M) - (y[3] & M);
+ z[3] = (int)c;
+ c >>= 32;
+ c += (x[4] & M) - (y[4] & M);
+ z[4] = (int)c;
+ c >>= 32;
+ c += (x[5] & M) - (y[5] & M);
+ z[5] = (int)c;
+ c >>= 32;
+ c += (x[6] & M) - (y[6] & M);
+ z[6] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int sub(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (x[xOff + 0] & M) - (y[yOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 1] & M) - (y[yOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 2] & M) - (y[yOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 3] & M) - (y[yOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 4] & M) - (y[yOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 5] & M) - (y[yOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 6] & M) - (y[yOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>= 32;
+ 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;
+ return (int)c;
+ }
+
+ public static int subFrom(int[] x, int[] z)
+ {
+ long c = 0;
+ c += (z[0] & M) - (x[0] & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (z[1] & M) - (x[1] & M);
+ z[1] = (int)c;
+ c >>= 32;
+ c += (z[2] & M) - (x[2] & M);
+ z[2] = (int)c;
+ c >>= 32;
+ c += (z[3] & M) - (x[3] & M);
+ z[3] = (int)c;
+ c >>= 32;
+ c += (z[4] & M) - (x[4] & M);
+ z[4] = (int)c;
+ c >>= 32;
+ c += (z[5] & M) - (x[5] & M);
+ z[5] = (int)c;
+ c >>= 32;
+ c += (z[6] & M) - (x[6] & M);
+ z[6] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int subFrom(int[] x, int xOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (z[zOff + 0] & M) - (x[xOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 1] & M) - (x[xOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 2] & M) - (x[xOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 3] & M) - (x[xOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 4] & M) - (x[xOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 5] & M) - (x[xOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 6] & M) - (x[xOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static BigInteger toBigInteger(int[] x)
+ {
+ byte[] bs = new byte[28];
+ for (int i = 0; i < 7; ++i)
+ {
+ int x_i = x[i];
+ if (x_i != 0)
+ {
+ Pack.intToBigEndian(x_i, bs, (6 - i) << 2);
+ }
+ }
+ return new BigInteger(1, bs);
+ }
+
+ public static void zero(int[] z)
+ {
+ z[0] = 0;
+ z[1] = 0;
+ z[2] = 0;
+ z[3] = 0;
+ z[4] = 0;
+ z[5] = 0;
+ z[6] = 0;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/raw/Nat256.java b/core/src/main/java/org/spongycastle/math/raw/Nat256.java
new file mode 100644
index 00000000..3cbf1ae7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/raw/Nat256.java
@@ -0,0 +1,1306 @@
+package org.spongycastle.math.raw;
+
+import java.math.BigInteger;
+
+import org.spongycastle.util.Pack;
+
+public abstract class Nat256
+{
+ private static final long M = 0xFFFFFFFFL;
+
+ public static int add(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) + (y[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (x[1] & M) + (y[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += (x[2] & M) + (y[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += (x[3] & M) + (y[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += (x[4] & M) + (y[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ c += (x[5] & M) + (y[5] & M);
+ z[5] = (int)c;
+ c >>>= 32;
+ c += (x[6] & M) + (y[6] & M);
+ z[6] = (int)c;
+ c >>>= 32;
+ c += (x[7] & M) + (y[7] & M);
+ z[7] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int add(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (x[xOff + 0] & M) + (y[yOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 1] & M) + (y[yOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 2] & M) + (y[yOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 3] & M) + (y[yOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 4] & M) + (y[yOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 5] & M) + (y[yOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 6] & M) + (y[yOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 7] & M) + (y[yOff + 7] & M);
+ z[zOff + 7] = (int)c;
+ c >>>= 32;
+ 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 addBothTo(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (x[xOff + 0] & M) + (y[yOff + 0] & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 1] & M) + (y[yOff + 1] & M) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 2] & M) + (y[yOff + 2] & M) + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 3] & M) + (y[yOff + 3] & M) + (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 4] & M) + (y[yOff + 4] & M) + (z[zOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 5] & M) + (y[yOff + 5] & M) + (z[zOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 6] & M) + (y[yOff + 6] & M) + (z[zOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 7] & M) + (y[yOff + 7] & M) + (z[zOff + 7] & M);
+ z[zOff + 7] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addTo(int[] x, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) + (z[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (x[1] & M) + (z[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += (x[2] & M) + (z[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += (x[3] & M) + (z[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += (x[4] & M) + (z[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ c += (x[5] & M) + (z[5] & M);
+ z[5] = (int)c;
+ c >>>= 32;
+ c += (x[6] & M) + (z[6] & M);
+ z[6] = (int)c;
+ c >>>= 32;
+ c += (x[7] & M) + (z[7] & M);
+ z[7] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn)
+ {
+ long c = cIn & M;
+ c += (x[xOff + 0] & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 1] & M) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 2] & M) + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 3] & M) + (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 4] & M) + (z[zOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 5] & M) + (z[zOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 6] & M) + (z[zOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 7] & M) + (z[zOff + 7] & M);
+ z[zOff + 7] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addToEachOther(int[] u, int uOff, int[] v, int vOff)
+ {
+ long c = 0;
+ c += (u[uOff + 0] & M) + (v[vOff + 0] & M);
+ u[uOff + 0] = (int)c;
+ v[vOff + 0] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 1] & M) + (v[vOff + 1] & M);
+ u[uOff + 1] = (int)c;
+ v[vOff + 1] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 2] & M) + (v[vOff + 2] & M);
+ u[uOff + 2] = (int)c;
+ v[vOff + 2] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 3] & M) + (v[vOff + 3] & M);
+ u[uOff + 3] = (int)c;
+ v[vOff + 3] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 4] & M) + (v[vOff + 4] & M);
+ u[uOff + 4] = (int)c;
+ v[vOff + 4] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 5] & M) + (v[vOff + 5] & M);
+ u[uOff + 5] = (int)c;
+ v[vOff + 5] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 6] & M) + (v[vOff + 6] & M);
+ u[uOff + 6] = (int)c;
+ v[vOff + 6] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 7] & M) + (v[vOff + 7] & M);
+ u[uOff + 7] = (int)c;
+ v[vOff + 7] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static void copy(int[] x, int[] z)
+ {
+ z[0] = x[0];
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ z[4] = x[4];
+ z[5] = x[5];
+ z[6] = x[6];
+ z[7] = x[7];
+ }
+
+ public static int[] create()
+ {
+ return new int[8];
+ }
+
+ public static int[] createExt()
+ {
+ return new int[16];
+ }
+
+ public static boolean diff(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ boolean pos = gte(x, xOff, y, yOff);
+ if (pos)
+ {
+ sub(x, xOff, y, yOff, z, zOff);
+ }
+ else
+ {
+ sub(y, yOff, x, xOff, z, zOff);
+ }
+ return pos;
+ }
+
+ public static boolean eq(int[] x, int[] y)
+ {
+ for (int i = 7; i >= 0; --i)
+ {
+ if (x[i] != y[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ 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)
+ {
+ int x_i = x[i] ^ Integer.MIN_VALUE;
+ int y_i = y[i] ^ Integer.MIN_VALUE;
+ if (x_i < y_i)
+ return false;
+ if (x_i > y_i)
+ return true;
+ }
+ return true;
+ }
+
+ public static boolean gte(int[] x, int xOff, int[] y, int yOff)
+ {
+ for (int i = 7; i >= 0; --i)
+ {
+ int x_i = x[xOff + i] ^ Integer.MIN_VALUE;
+ int y_i = y[yOff + i] ^ Integer.MIN_VALUE;
+ if (x_i < y_i)
+ return false;
+ if (x_i > y_i)
+ return true;
+ }
+ return true;
+ }
+
+ public static boolean isOne(int[] x)
+ {
+ if (x[0] != 1)
+ {
+ return false;
+ }
+ for (int i = 1; i < 8; ++i)
+ {
+ if (x[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isZero(int[] x)
+ {
+ for (int i = 0; i < 8; ++i)
+ {
+ if (x[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void mul(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 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)
+ {
+ 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 mul(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff)
+ {
+ long y_0 = y[yOff + 0] & M;
+ long y_1 = y[yOff + 1] & M;
+ long y_2 = y[yOff + 2] & M;
+ long y_3 = y[yOff + 3] & M;
+ long y_4 = y[yOff + 4] & M;
+ long y_5 = y[yOff + 5] & M;
+ long y_6 = y[yOff + 6] & M;
+ long y_7 = y[yOff + 7] & M;
+
+ {
+ long c = 0, x_0 = x[xOff + 0] & M;
+ c += x_0 * y_0;
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_1;
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_2;
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_3;
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_4;
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_5;
+ zz[zzOff + 5] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_6;
+ zz[zzOff + 6] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_7;
+ zz[zzOff + 7] = (int)c;
+ c >>>= 32;
+ zz[zzOff + 8] = (int)c;
+ }
+
+ for (int i = 1; i < 8; ++i)
+ {
+ ++zzOff;
+ long c = 0, x_i = x[xOff + i] & M;
+ c += x_i * y_0 + (zz[zzOff + 0] & M);
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[zzOff + 1] & M);
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[zzOff + 2] & M);
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[zzOff + 3] & M);
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += x_i * y_4 + (zz[zzOff + 4] & M);
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ c += x_i * y_5 + (zz[zzOff + 5] & M);
+ zz[zzOff + 5] = (int)c;
+ c >>>= 32;
+ c += x_i * y_6 + (zz[zzOff + 6] & M);
+ zz[zzOff + 6] = (int)c;
+ c >>>= 32;
+ c += x_i * y_7 + (zz[zzOff + 7] & M);
+ zz[zzOff + 7] = (int)c;
+ c >>>= 32;
+ zz[zzOff + 8] = (int)c;
+ }
+ }
+
+ public static int mulAddTo(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 zc = 0;
+ for (int i = 0; i < 8; ++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 += zc + (zz[i + 8] & M);
+ zz[i + 8] = (int)c;
+ zc = c >>> 32;
+ }
+ return (int)zc;
+ }
+
+ public static int mulAddTo(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff)
+ {
+ long y_0 = y[yOff + 0] & M;
+ long y_1 = y[yOff + 1] & M;
+ long y_2 = y[yOff + 2] & M;
+ long y_3 = y[yOff + 3] & M;
+ long y_4 = y[yOff + 4] & M;
+ long y_5 = y[yOff + 5] & M;
+ long y_6 = y[yOff + 6] & M;
+ long y_7 = y[yOff + 7] & M;
+
+ long zc = 0;
+ for (int i = 0; i < 8; ++i)
+ {
+ long c = 0, x_i = x[xOff + i] & M;
+ c += x_i * y_0 + (zz[zzOff + 0] & M);
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[zzOff + 1] & M);
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[zzOff + 2] & M);
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[zzOff + 3] & M);
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += x_i * y_4 + (zz[zzOff + 4] & M);
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ c += x_i * y_5 + (zz[zzOff + 5] & M);
+ zz[zzOff + 5] = (int)c;
+ c >>>= 32;
+ c += x_i * y_6 + (zz[zzOff + 6] & M);
+ zz[zzOff + 6] = (int)c;
+ c >>>= 32;
+ c += x_i * y_7 + (zz[zzOff + 7] & M);
+ zz[zzOff + 7] = (int)c;
+ c >>>= 32;
+ c += zc + (zz[zzOff + 8] & M);
+ zz[zzOff + 8] = (int)c;
+ zc = c >>> 32;
+ ++zzOff;
+ }
+ return (int)zc;
+ }
+
+ public static long mul33Add(int w, int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ // assert w >>> 31 == 0;
+
+ long c = 0, wVal = w & M;
+ long x0 = x[xOff + 0] & M;
+ c += wVal * x0 + (y[yOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ long x1 = x[xOff + 1] & M;
+ c += wVal * x1 + x0 + (y[yOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ long x2 = x[xOff + 2] & M;
+ c += wVal * x2 + x1 + (y[yOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ long x3 = x[xOff + 3] & M;
+ c += wVal * x3 + x2 + (y[yOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ long x4 = x[xOff + 4] & M;
+ c += wVal * x4 + x3 + (y[yOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ long x5 = x[xOff + 5] & M;
+ c += wVal * x5 + x4 + (y[yOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>>= 32;
+ long x6 = x[xOff + 6] & M;
+ c += wVal * x6 + x5 + (y[yOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>>= 32;
+ long x7 = x[xOff + 7] & M;
+ c += wVal * x7 + x6 + (y[yOff + 7] & M);
+ z[zOff + 7] = (int)c;
+ c >>>= 32;
+ c += x7;
+ return c;
+ }
+
+ public static int mulByWord(int x, int[] z)
+ {
+ long c = 0, xVal = x & M;
+ c += xVal * (z[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[5] & M);
+ z[5] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[6] & M);
+ z[6] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[7] & M);
+ z[7] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int mulByWordAddTo(int x, int[] y, int[] z)
+ {
+ long c = 0, xVal = x & M;
+ c += xVal * (z[0] & M) + (y[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[1] & M) + (y[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[2] & M) + (y[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[3] & M) + (y[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[4] & M) + (y[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[5] & M) + (y[5] & M);
+ z[5] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[6] & M) + (y[6] & M);
+ z[6] = (int)c;
+ c >>>= 32;
+ c += xVal * (z[7] & M) + (y[7] & M);
+ z[7] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int mulWordAddTo(int x, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0, xVal = x & M;
+ c += xVal * (y[yOff + 0] & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 1] & M) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 2] & M) + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 3] & M) + (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 4] & M) + (z[zOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 5] & M) + (z[zOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 6] & M) + (z[zOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>>= 32;
+ c += xVal * (y[yOff + 7] & M) + (z[zOff + 7] & M);
+ z[zOff + 7] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int mul33DWordAdd(int x, long y, int[] z, int zOff)
+ {
+ // assert x >>> 31 == 0;
+ // assert zOff <= 4;
+
+ long c = 0, xVal = x & M;
+ long y00 = y & M;
+ c += xVal * y00 + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ long y01 = y >>> 32;
+ c += xVal * y01 + y00 + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += y01 + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(8, z, zOff, 4);
+ }
+
+ public static int mul33WordAdd(int x, int y, int[] z, int zOff)
+ {
+ // assert x >>> 31 == 0;
+ // assert zOff <= 5;
+
+ long c = 0, xVal = x & M, yVal = y & M;
+ c += yVal * xVal + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += yVal + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(8, z, zOff, 3);
+ }
+
+ public static int mulWordDwordAdd(int x, long y, int[] z, int zOff)
+ {
+ // assert zOff <= 5;
+ long c = 0, xVal = x & M;
+ c += xVal * (y & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += xVal * (y >>> 32) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(8, z, zOff, 3);
+ }
+
+ public static int mulWord(int x, int[] y, int[] z, int zOff)
+ {
+ long c = 0, xVal = x & M;
+ int i = 0;
+ do
+ {
+ c += xVal * (y[i] & M);
+ z[zOff + i] = (int)c;
+ c >>>= 32;
+ }
+ while (++i < 8);
+ return (int)c;
+ }
+
+ public static void square(int[] x, int[] zz)
+ {
+ long x_0 = x[0] & M;
+ long zz_1;
+
+ int c = 0, w;
+ {
+ int i = 7, j = 16;
+ do
+ {
+ long xVal = (x[i--] & M);
+ long p = xVal * xVal;
+ zz[--j] = (c << 31) | (int)(p >>> 33);
+ zz[--j] = (int)(p >>> 1);
+ c = (int)p;
+ }
+ while (i > 0);
+
+ {
+ long p = x_0 * x_0;
+ zz_1 = ((c << 31) & M) | (p >>> 33);
+ zz[0] = (int)p;
+ c = (int)(p >>> 32) & 1;
+ }
+ }
+
+ long x_1 = x[1] & M;
+ long zz_2 = zz[2] & M;
+
+ {
+ zz_1 += x_1 * x_0;
+ w = (int)zz_1;
+ zz[1] = (w << 1) | c;
+ c = w >>> 31;
+ zz_2 += zz_1 >>> 32;
+ }
+
+ long x_2 = x[2] & M;
+ long zz_3 = zz[3] & M;
+ long zz_4 = zz[4] & M;
+ {
+ zz_2 += x_2 * x_0;
+ w = (int)zz_2;
+ zz[2] = (w << 1) | c;
+ c = w >>> 31;
+ zz_3 += (zz_2 >>> 32) + x_2 * x_1;
+ zz_4 += zz_3 >>> 32;
+ zz_3 &= M;
+ }
+
+ long x_3 = x[3] & M;
+ long zz_5 = zz[5] & M;
+ long zz_6 = zz[6] & M;
+ {
+ zz_3 += x_3 * x_0;
+ w = (int)zz_3;
+ zz[3] = (w << 1) | c;
+ c = w >>> 31;
+ zz_4 += (zz_3 >>> 32) + x_3 * x_1;
+ zz_5 += (zz_4 >>> 32) + x_3 * x_2;
+ zz_4 &= M;
+ zz_6 += zz_5 >>> 32;
+ zz_5 &= M;
+ }
+
+ long x_4 = x[4] & M;
+ long zz_7 = zz[7] & M;
+ long zz_8 = zz[8] & M;
+ {
+ zz_4 += x_4 * x_0;
+ w = (int)zz_4;
+ zz[4] = (w << 1) | c;
+ c = w >>> 31;
+ zz_5 += (zz_4 >>> 32) + x_4 * x_1;
+ zz_6 += (zz_5 >>> 32) + x_4 * x_2;
+ zz_5 &= M;
+ zz_7 += (zz_6 >>> 32) + x_4 * x_3;
+ zz_6 &= M;
+ zz_8 += zz_7 >>> 32;
+ zz_7 &= M;
+ }
+
+ long x_5 = x[5] & M;
+ long zz_9 = zz[9] & M;
+ long zz_10 = zz[10] & M;
+ {
+ zz_5 += x_5 * x_0;
+ w = (int)zz_5;
+ zz[5] = (w << 1) | c;
+ c = w >>> 31;
+ zz_6 += (zz_5 >>> 32) + x_5 * x_1;
+ zz_7 += (zz_6 >>> 32) + x_5 * x_2;
+ zz_6 &= M;
+ zz_8 += (zz_7 >>> 32) + x_5 * x_3;
+ zz_7 &= M;
+ zz_9 += (zz_8 >>> 32) + x_5 * x_4;
+ zz_8 &= M;
+ zz_10 += zz_9 >>> 32;
+ zz_9 &= M;
+ }
+
+ long x_6 = x[6] & M;
+ long zz_11 = zz[11] & M;
+ long zz_12 = zz[12] & M;
+ {
+ zz_6 += x_6 * x_0;
+ w = (int)zz_6;
+ zz[6] = (w << 1) | c;
+ c = w >>> 31;
+ zz_7 += (zz_6 >>> 32) + x_6 * x_1;
+ zz_8 += (zz_7 >>> 32) + x_6 * x_2;
+ zz_7 &= M;
+ zz_9 += (zz_8 >>> 32) + x_6 * x_3;
+ zz_8 &= M;
+ zz_10 += (zz_9 >>> 32) + x_6 * x_4;
+ zz_9 &= M;
+ zz_11 += (zz_10 >>> 32) + x_6 * x_5;
+ zz_10 &= M;
+ zz_12 += zz_11 >>> 32;
+ zz_11 &= M;
+ }
+
+ long x_7 = x[7] & M;
+ long zz_13 = zz[13] & M;
+ long zz_14 = zz[14] & M;
+ {
+ zz_7 += x_7 * x_0;
+ w = (int)zz_7;
+ zz[7] = (w << 1) | c;
+ c = w >>> 31;
+ zz_8 += (zz_7 >>> 32) + x_7 * x_1;
+ zz_9 += (zz_8 >>> 32) + x_7 * x_2;
+ zz_10 += (zz_9 >>> 32) + x_7 * x_3;
+ zz_11 += (zz_10 >>> 32) + x_7 * x_4;
+ zz_12 += (zz_11 >>> 32) + x_7 * x_5;
+ zz_13 += (zz_12 >>> 32) + x_7 * x_6;
+ zz_14 += zz_13 >>> 32;
+ }
+
+ w = (int)zz_8;
+ zz[8] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_9;
+ zz[9] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_10;
+ zz[10] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_11;
+ zz[11] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_12;
+ zz[12] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_13;
+ zz[13] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_14;
+ zz[14] = (w << 1) | c;
+ c = w >>> 31;
+ w = zz[15] + (int)(zz_14 >> 32);
+ zz[15] = (w << 1) | c;
+ }
+
+ public static void square(int[] x, int xOff, int[] zz, int zzOff)
+ {
+ long x_0 = x[xOff + 0] & M;
+ long zz_1;
+
+ int c = 0, w;
+ {
+ int i = 7, j = 16;
+ do
+ {
+ long xVal = (x[xOff + i--] & M);
+ long p = xVal * xVal;
+ zz[zzOff + --j] = (c << 31) | (int)(p >>> 33);
+ zz[zzOff + --j] = (int)(p >>> 1);
+ c = (int)p;
+ }
+ while (i > 0);
+
+ {
+ long p = x_0 * x_0;
+ zz_1 = ((c << 31) & M) | (p >>> 33);
+ zz[zzOff + 0] = (int)p;
+ c = (int)(p >>> 32) & 1;
+ }
+ }
+
+ long x_1 = x[xOff + 1] & M;
+ long zz_2 = zz[zzOff + 2] & M;
+
+ {
+ zz_1 += x_1 * x_0;
+ w = (int)zz_1;
+ zz[zzOff + 1] = (w << 1) | c;
+ c = w >>> 31;
+ zz_2 += zz_1 >>> 32;
+ }
+
+ long x_2 = x[xOff + 2] & M;
+ long zz_3 = zz[zzOff + 3] & M;
+ long zz_4 = zz[zzOff + 4] & M;
+ {
+ zz_2 += x_2 * x_0;
+ w = (int)zz_2;
+ zz[zzOff + 2] = (w << 1) | c;
+ c = w >>> 31;
+ zz_3 += (zz_2 >>> 32) + x_2 * x_1;
+ zz_4 += zz_3 >>> 32;
+ zz_3 &= M;
+ }
+
+ long x_3 = x[xOff + 3] & M;
+ long zz_5 = zz[zzOff + 5] & M;
+ long zz_6 = zz[zzOff + 6] & M;
+ {
+ zz_3 += x_3 * x_0;
+ w = (int)zz_3;
+ zz[zzOff + 3] = (w << 1) | c;
+ c = w >>> 31;
+ zz_4 += (zz_3 >>> 32) + x_3 * x_1;
+ zz_5 += (zz_4 >>> 32) + x_3 * x_2;
+ zz_4 &= M;
+ zz_6 += zz_5 >>> 32;
+ zz_5 &= M;
+ }
+
+ long x_4 = x[xOff + 4] & M;
+ long zz_7 = zz[zzOff + 7] & M;
+ long zz_8 = zz[zzOff + 8] & M;
+ {
+ zz_4 += x_4 * x_0;
+ w = (int)zz_4;
+ zz[zzOff + 4] = (w << 1) | c;
+ c = w >>> 31;
+ zz_5 += (zz_4 >>> 32) + x_4 * x_1;
+ zz_6 += (zz_5 >>> 32) + x_4 * x_2;
+ zz_5 &= M;
+ zz_7 += (zz_6 >>> 32) + x_4 * x_3;
+ zz_6 &= M;
+ zz_8 += zz_7 >>> 32;
+ zz_7 &= M;
+ }
+
+ long x_5 = x[xOff + 5] & M;
+ long zz_9 = zz[zzOff + 9] & M;
+ long zz_10 = zz[zzOff + 10] & M;
+ {
+ zz_5 += x_5 * x_0;
+ w = (int)zz_5;
+ zz[zzOff + 5] = (w << 1) | c;
+ c = w >>> 31;
+ zz_6 += (zz_5 >>> 32) + x_5 * x_1;
+ zz_7 += (zz_6 >>> 32) + x_5 * x_2;
+ zz_6 &= M;
+ zz_8 += (zz_7 >>> 32) + x_5 * x_3;
+ zz_7 &= M;
+ zz_9 += (zz_8 >>> 32) + x_5 * x_4;
+ zz_8 &= M;
+ zz_10 += zz_9 >>> 32;
+ zz_9 &= M;
+ }
+
+ long x_6 = x[xOff + 6] & M;
+ long zz_11 = zz[zzOff + 11] & M;
+ long zz_12 = zz[zzOff + 12] & M;
+ {
+ zz_6 += x_6 * x_0;
+ w = (int)zz_6;
+ zz[zzOff + 6] = (w << 1) | c;
+ c = w >>> 31;
+ zz_7 += (zz_6 >>> 32) + x_6 * x_1;
+ zz_8 += (zz_7 >>> 32) + x_6 * x_2;
+ zz_7 &= M;
+ zz_9 += (zz_8 >>> 32) + x_6 * x_3;
+ zz_8 &= M;
+ zz_10 += (zz_9 >>> 32) + x_6 * x_4;
+ zz_9 &= M;
+ zz_11 += (zz_10 >>> 32) + x_6 * x_5;
+ zz_10 &= M;
+ zz_12 += zz_11 >>> 32;
+ zz_11 &= M;
+ }
+
+ long x_7 = x[xOff + 7] & M;
+ long zz_13 = zz[zzOff + 13] & M;
+ long zz_14 = zz[zzOff + 14] & M;
+ {
+ zz_7 += x_7 * x_0;
+ w = (int)zz_7;
+ zz[zzOff + 7] = (w << 1) | c;
+ c = w >>> 31;
+ zz_8 += (zz_7 >>> 32) + x_7 * x_1;
+ zz_9 += (zz_8 >>> 32) + x_7 * x_2;
+ zz_10 += (zz_9 >>> 32) + x_7 * x_3;
+ zz_11 += (zz_10 >>> 32) + x_7 * x_4;
+ zz_12 += (zz_11 >>> 32) + x_7 * x_5;
+ zz_13 += (zz_12 >>> 32) + x_7 * x_6;
+ zz_14 += zz_13 >>> 32;
+ }
+
+ w = (int)zz_8;
+ zz[zzOff + 8] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_9;
+ zz[zzOff + 9] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_10;
+ zz[zzOff + 10] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_11;
+ zz[zzOff + 11] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_12;
+ zz[zzOff + 12] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_13;
+ zz[zzOff + 13] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_14;
+ zz[zzOff + 14] = (w << 1) | c;
+ c = w >>> 31;
+ w = zz[zzOff + 15] + (int)(zz_14 >> 32);
+ zz[zzOff + 15] = (w << 1) | c;
+ }
+
+ public static int sub(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) - (y[0] & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (x[1] & M) - (y[1] & M);
+ z[1] = (int)c;
+ c >>= 32;
+ c += (x[2] & M) - (y[2] & M);
+ z[2] = (int)c;
+ c >>= 32;
+ c += (x[3] & M) - (y[3] & M);
+ z[3] = (int)c;
+ c >>= 32;
+ c += (x[4] & M) - (y[4] & M);
+ z[4] = (int)c;
+ c >>= 32;
+ c += (x[5] & M) - (y[5] & M);
+ z[5] = (int)c;
+ c >>= 32;
+ c += (x[6] & M) - (y[6] & M);
+ z[6] = (int)c;
+ c >>= 32;
+ c += (x[7] & M) - (y[7] & M);
+ z[7] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int sub(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (x[xOff + 0] & M) - (y[yOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 1] & M) - (y[yOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 2] & M) - (y[yOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 3] & M) - (y[yOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 4] & M) - (y[yOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 5] & M) - (y[yOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 6] & M) - (y[yOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 7] & M) - (y[yOff + 7] & M);
+ z[zOff + 7] = (int)c;
+ c >>= 32;
+ 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 subFrom(int[] x, int[] z)
+ {
+ long c = 0;
+ c += (z[0] & M) - (x[0] & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (z[1] & M) - (x[1] & M);
+ z[1] = (int)c;
+ c >>= 32;
+ c += (z[2] & M) - (x[2] & M);
+ z[2] = (int)c;
+ c >>= 32;
+ c += (z[3] & M) - (x[3] & M);
+ z[3] = (int)c;
+ c >>= 32;
+ c += (z[4] & M) - (x[4] & M);
+ z[4] = (int)c;
+ c >>= 32;
+ c += (z[5] & M) - (x[5] & M);
+ z[5] = (int)c;
+ c >>= 32;
+ c += (z[6] & M) - (x[6] & M);
+ z[6] = (int)c;
+ c >>= 32;
+ c += (z[7] & M) - (x[7] & M);
+ z[7] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int subFrom(int[] x, int xOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (z[zOff + 0] & M) - (x[xOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 1] & M) - (x[xOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 2] & M) - (x[xOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 3] & M) - (x[xOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 4] & M) - (x[xOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 5] & M) - (x[xOff + 5] & M);
+ z[zOff + 5] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 6] & M) - (x[xOff + 6] & M);
+ z[zOff + 6] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 7] & M) - (x[xOff + 7] & M);
+ z[zOff + 7] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ 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;
+ z[1] = 0;
+ z[2] = 0;
+ z[3] = 0;
+ z[4] = 0;
+ z[5] = 0;
+ z[6] = 0;
+ z[7] = 0;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/raw/Nat384.java b/core/src/main/java/org/spongycastle/math/raw/Nat384.java
new file mode 100644
index 00000000..d5b4b23c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/raw/Nat384.java
@@ -0,0 +1,43 @@
+package org.spongycastle.math.raw;
+
+
+public abstract class Nat384
+{
+ public static void mul(int[] x, int[] y, int[] zz)
+ {
+ Nat192.mul(x, y, zz);
+ Nat192.mul(x, 6, y, 6, zz, 12);
+
+ int c18 = Nat192.addToEachOther(zz, 6, zz, 12);
+ int c12 = c18 + Nat192.addTo(zz, 0, zz, 6, 0);
+ c18 += Nat192.addTo(zz, 18, zz, 12, c12);
+
+ int[] dx = Nat192.create(), dy = Nat192.create();
+ boolean neg = Nat192.diff(x, 6, x, 0, dx, 0) != Nat192.diff(y, 6, y, 0, dy, 0);
+
+ int[] tt = Nat192.createExt();
+ Nat192.mul(dx, dy, tt);
+
+ c18 += neg ? Nat.addTo(12, tt, 0, zz, 6) : Nat.subFrom(12, tt, 0, zz, 6);
+ Nat.addWordAt(24, c18, zz, 18);
+ }
+
+ public static void square(int[] x, int[] zz)
+ {
+ Nat192.square(x, zz);
+ Nat192.square(x, 6, zz, 12);
+
+ int c18 = Nat192.addToEachOther(zz, 6, zz, 12);
+ int c12 = c18 + Nat192.addTo(zz, 0, zz, 6, 0);
+ c18 += Nat192.addTo(zz, 18, zz, 12, c12);
+
+ int[] dx = Nat192.create();
+ Nat192.diff(x, 6, x, 0, dx, 0);
+
+ int[] tt = Nat192.createExt();
+ Nat192.square(dx, tt);
+
+ c18 += Nat.subFrom(12, tt, 0, zz, 6);
+ Nat.addWordAt(24, c18, zz, 18);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/math/raw/Nat512.java b/core/src/main/java/org/spongycastle/math/raw/Nat512.java
new file mode 100644
index 00000000..1b32342b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/math/raw/Nat512.java
@@ -0,0 +1,43 @@
+package org.spongycastle.math.raw;
+
+
+public abstract class Nat512
+{
+ public static void mul(int[] x, int[] y, int[] zz)
+ {
+ Nat256.mul(x, y, zz);
+ Nat256.mul(x, 8, y, 8, zz, 16);
+
+ int c24 = Nat256.addToEachOther(zz, 8, zz, 16);
+ int c16 = c24 + Nat256.addTo(zz, 0, zz, 8, 0);
+ c24 += Nat256.addTo(zz, 24, zz, 16, c16);
+
+ int[] dx = Nat256.create(), dy = Nat256.create();
+ boolean neg = Nat256.diff(x, 8, x, 0, dx, 0) != Nat256.diff(y, 8, y, 0, dy, 0);
+
+ int[] tt = Nat256.createExt();
+ Nat256.mul(dx, dy, tt);
+
+ c24 += neg ? Nat.addTo(16, tt, 0, zz, 8) : Nat.subFrom(16, tt, 0, zz, 8);
+ Nat.addWordAt(32, c24, zz, 24);
+ }
+
+ public static void square(int[] x, int[] zz)
+ {
+ Nat256.square(x, zz);
+ Nat256.square(x, 8, zz, 16);
+
+ int c24 = Nat256.addToEachOther(zz, 8, zz, 16);
+ int c16 = c24 + Nat256.addTo(zz, 0, zz, 8, 0);
+ c24 += Nat256.addTo(zz, 24, zz, 16, c16);
+
+ int[] dx = Nat256.create();
+ Nat256.diff(x, 8, x, 0, dx, 0);
+
+ int[] tt = Nat256.createExt();
+ Nat256.square(dx, tt);
+
+ c24 += Nat.subFrom(16, tt, 0, zz, 8);
+ Nat.addWordAt(32, c24, zz, 24);
+ }
+}