diff options
Diffstat (limited to 'core/src/main/java/org/spongycastle/math')
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>μ</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>μ</code> of the elliptic curve. + * @return <code>μ</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 + * τ-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>α<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>α<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>α<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>α<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>α<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>λ</code> of + * <code><b>Z</b>[τ]</code>. + * @param mu The parameter <code>μ</code> of the elliptic curve. + * @param lambda The element <code>λ</code> of + * <code><b>Z</b>[τ]</code>. + * @return The norm of <code>λ</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>λ</code> of + * <code><b>R</b>[τ]</code>, where <code>λ = u + vτ</code> + * and <code>u</code> and <code>u</code> are real numbers (elements of + * <code><b>R</b></code>). + * @param mu The parameter <code>μ</code> of the elliptic curve. + * @param u The real part of the element <code>λ</code> of + * <code><b>R</b>[τ]</code>. + * @param v The <code>τ</code>-adic part of the element + * <code>λ</code> of <code><b>R</b>[τ]</code>. + * @return The norm of <code>λ</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>λ</code> of <code><b>R</b>[τ]</code> + * to an element of <code><b>Z</b>[τ]</code>, such that their difference + * has minimal norm. <code>λ</code> is given as + * <code>λ = λ<sub>0</sub> + λ<sub>1</sub>τ</code>. + * @param lambda0 The component <code>λ<sub>0</sub></code>. + * @param lambda1 The component <code>λ<sub>1</sub></code>. + * @param mu The parameter <code>μ</code> of the elliptic curve. Must + * equal 1 or -1. + * @return The rounded element of <code><b>Z</b>[τ]</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>λ = 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>λ = 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>τ</code>-adic NAF (non-adjacent form) of an + * element <code>λ</code> of <code><b>Z</b>[τ]</code>. + * @param mu The parameter <code>μ</code> of the elliptic curve. + * @param lambda The element <code>λ</code> of + * <code><b>Z</b>[τ]</code>. + * @return The <code>τ</code>-adic NAF of <code>λ</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>τ()</code> to an + * <code>ECPoint.F2m</code>. + * @param p The ECPoint.F2m to which <code>τ()</code> is applied. + * @return <code>τ(p)</code> + */ + public static ECPoint.F2m tau(ECPoint.F2m p) + { + return p.tau(); + } + + /** + * Returns the parameter <code>μ</code> of the elliptic curve. + * @param curve The elliptic curve from which to obtain <code>μ</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>μ</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>μ</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>μ</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>(τ<sup>m</sup> - 1)/(τ - 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 μ of the elliptic curve. + * @param c The precision (number of bits of accuracy) of the partial + * modular reduction. + * @return <code>ρ := k partmod (τ<sup>m</sup> - 1)/(τ - 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>τ</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>λ</code> of <code><b>Z</b>[τ]</code> + * using the <code>τ</code>-adic NAF (TNAF) method. + * @param p The ECPoint.F2m to multiply. + * @param lambda The element <code>λ</code> of + * <code><b>Z</b>[τ]</code>. + * @return <code>λ * 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>λ</code> of <code><b>Z</b>[τ]</code> + * using the <code>τ</code>-adic NAF (TNAF) method, given the TNAF + * of <code>λ</code>. + * @param p The ECPoint.F2m to multiply. + * @param u The the TNAF of <code>λ</code>.. + * @return <code>λ * 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>[τ]</code>-adic window NAF of an element + * <code>λ</code> of <code><b>Z</b>[τ]</code>. + * @param mu The parameter μ of the elliptic curve. + * @param lambda The element <code>λ</code> of + * <code><b>Z</b>[τ]</code> of which to compute the + * <code>[τ]</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>α<sub>u</sub></code>'s for the window width. + * @return The <code>[τ]</code>-adic window NAF of + * <code>λ</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 = ∑<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>τ</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>τ</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>λ</code> of <code><b>Z</b>[τ]</code> using + * the <code>τ</code>-adic NAF (TNAF) method. + * @param p The ECPoint.F2m to multiply. + * @param lambda The element <code>λ</code> of + * <code><b>Z</b>[τ]</code> of which to compute the + * <code>[τ]</code>-adic NAF. + * @return <code>p</code> multiplied by <code>λ</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>λ</code> of <code><b>Z</b>[τ]</code> + * using the window <code>τ</code>-adic NAF (TNAF) method, given the + * WTNAF of <code>λ</code>. + * @param p The ECPoint.F2m to multiply. + * @param u The the WTNAF of <code>λ</code>.. + * @return <code>λ * 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>τ</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>[τ]</code>. Let + * <code>λ</code> be an element of <code><b>Z</b>[τ]</code>. Then + * <code>λ</code> is given as <code>λ = u + vτ</code>. The + * components <code>u</code> and <code>v</code> may be used directly, there + * are no accessor methods. + * Immutable class. + */ +class ZTauElement +{ + /** + * The "real" part of <code>λ</code>. + */ + public final BigInteger u; + + /** + * The "<code>τ</code>-adic" part of <code>λ</code>. + */ + public final BigInteger v; + + /** + * Constructor for an element <code>λ</code> of + * <code><b>Z</b>[τ]</code>. + * @param u The "real" part of <code>λ</code>. + * @param v The "<code>τ</code>-adic" part of + * <code>λ</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); + } +} |