All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.math.ec.ECAlgorithms Maven / Gradle / Ivy

Go to download

The Long Term Stable (LTS) Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains the JCA/JCE provider and low-level API for the BC LTS version 2.73.7 for Java 8 and later.

There is a newer version: 2.73.7
Show newest version
package org.bouncycastle.math.ec;

import java.math.BigInteger;

import org.bouncycastle.math.ec.endo.ECEndomorphism;
import org.bouncycastle.math.ec.endo.EndoUtil;
import org.bouncycastle.math.ec.endo.GLVEndomorphism;
import org.bouncycastle.math.field.FiniteField;
import org.bouncycastle.math.field.PolynomialExtensionField;
import org.bouncycastle.math.raw.Nat;

public class ECAlgorithms
{
    public static boolean isF2mCurve(ECCurve c)
    {
        return isF2mField(c.getField());
    }

    public static boolean isF2mField(FiniteField field)
    {
        return field.getDimension() > 1 && field.getCharacteristic().equals(ECConstants.TWO)
            && field instanceof PolynomialExtensionField;
    }

    public static boolean isFpCurve(ECCurve c)
    {
        return isFpField(c.getField());
    }

    public static boolean isFpField(FiniteField field)
    {
        return field.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 implCheckResult(implSumOfMultipliesGLV(imported, ks, (GLVEndomorphism)endomorphism));
        }

        return implCheckResult(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.AbstractF2m)
        {
            ECCurve.AbstractF2m f2mCurve = (ECCurve.AbstractF2m)cp;
            if (f2mCurve.isKoblitz())
            {
                return implCheckResult(P.multiply(a).add(Q.multiply(b)));
            }
        }

        ECEndomorphism endomorphism = cp.getEndomorphism();
        if (endomorphism instanceof GLVEndomorphism)
        {
            return implCheckResult(
                implSumOfMultipliesGLV(new ECPoint[]{ P, Q }, new BigInteger[]{ a, b }, (GLVEndomorphism)endomorphism));
        }

        return implCheckResult(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)
     * 
     * 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
     * 
*/ public static ECPoint shamirsTrick(ECPoint P, BigInteger k, ECPoint Q, BigInteger l) { ECCurve cp = P.getCurve(); Q = importPoint(cp, Q); return implCheckResult(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) { montgomeryTrick(zs, off, len, null); } public static void montgomeryTrick(ECFieldElement[] zs, int off, int len, ECFieldElement scale) { /* * 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]); } --i; if (scale != null) { c[i] = c[i].multiply(scale); } 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. CAUTION: This implementation is NOT * constant-time in any way. It is only intended to be used for diagnostics. * * @param p * The point to multiply. * @param k * The multiplier. * @return The result of the point multiplication kP. */ 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 IllegalStateException("Invalid point"); } return p; } public static ECPoint cleanPoint(ECCurve c, ECPoint p) { ECCurve cp = p.getCurve(); if (!c.equals(cp)) { throw new IllegalArgumentException("Point must be on the same curve"); } return c.decodePoint(p.getEncoded(false)); } static ECPoint implCheckResult(ECPoint p) { if (!p.isValidPartial()) { throw new IllegalStateException("Invalid result"); } 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; BigInteger kAbs = k.abs(), lAbs = l.abs(); int minWidthP = WNafUtil.getWindowSize(kAbs.bitLength(), 8); int minWidthQ = WNafUtil.getWindowSize(lAbs.bitLength(), 8); WNafPreCompInfo infoP = WNafUtil.precompute(P, minWidthP, true); WNafPreCompInfo infoQ = WNafUtil.precompute(Q, minWidthQ, true); // When P, Q are 'promoted' (i.e. reused several times), switch to fixed-point algorithm { ECCurve c = P.getCurve(); int combSize = FixedPointUtil.getCombSize(c); if (!negK && !negL && k.bitLength() <= combSize && l.bitLength() <= combSize && infoP.isPromoted() && infoQ.isPromoted()) { return implShamirsTrickFixedPoint(P, k, Q, l); } } int widthP = Math.min(8, infoP.getWidth()); int widthQ = Math.min(8, infoQ.getWidth()); 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, kAbs); byte[] wnafQ = WNafUtil.generateWindowNaf(widthQ, lAbs); return implShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ, preCompNegQ, wnafQ); } static ECPoint implShamirsTrickWNaf(ECEndomorphism endomorphism, ECPoint P, BigInteger k, BigInteger l) { boolean negK = k.signum() < 0, negL = l.signum() < 0; k = k.abs(); l = l.abs(); int minWidth = WNafUtil.getWindowSize(Math.max(k.bitLength(), l.bitLength()), 8); WNafPreCompInfo infoP = WNafUtil.precompute(P, minWidth, true); ECPoint Q = EndoUtil.mapPoint(endomorphism, P); WNafPreCompInfo infoQ = WNafUtil.precomputeWithPointMap(Q, endomorphism.getPointMap(), infoP, true); int widthP = Math.min(8, infoP.getWidth()); int widthQ = Math.min(8, infoQ.getWidth()); 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); } 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 minWidth = WNafUtil.getWindowSize(ki.bitLength(), 8); WNafPreCompInfo info = WNafUtil.precompute(ps[i], minWidth, true); int width = Math.min(8, info.getWidth()); infos[i] = info; 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]; } if (glvEndomorphism.hasEfficientPointMap()) { return implSumOfMultiplies(glvEndomorphism, ps, abs); } ECPoint[] pqs = new ECPoint[len << 1]; for (int i = 0, j = 0; i < len; ++i) { ECPoint p = ps[i]; ECPoint q = EndoUtil.mapPoint(glvEndomorphism, p); pqs[j++] = p; pqs[j++] = q; } return implSumOfMultiplies(pqs, abs); } static ECPoint implSumOfMultiplies(ECEndomorphism endomorphism, ECPoint[] ps, BigInteger[] ks) { int halfCount = ps.length, fullCount = halfCount << 1; boolean[] negs = new boolean[fullCount]; WNafPreCompInfo[] infos = new WNafPreCompInfo[fullCount]; byte[][] wnafs = new byte[fullCount][]; ECPointMap pointMap = endomorphism.getPointMap(); 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 minWidth = WNafUtil.getWindowSize(Math.max(kj0.bitLength(), kj1.bitLength()), 8); ECPoint P = ps[i]; WNafPreCompInfo infoP = WNafUtil.precompute(P, minWidth, true); ECPoint Q = EndoUtil.mapPoint(endomorphism, P); WNafPreCompInfo infoQ = WNafUtil.precomputeWithPointMap(Q, pointMap, infoP, true); int widthP = Math.min(8, infoP.getWidth()); int widthQ = Math.min(8, infoQ.getWidth()); infos[j0] = infoP; infos[j1] = infoQ; wnafs[j0] = WNafUtil.generateWindowNaf(widthP, kj0); wnafs[j1] = WNafUtil.generateWindowNaf(widthQ, 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; } private static ECPoint implShamirsTrickFixedPoint(ECPoint p, BigInteger k, ECPoint q, BigInteger l) { ECCurve c = p.getCurve(); int combSize = FixedPointUtil.getCombSize(c); if (k.bitLength() > combSize || l.bitLength() > combSize) { /* * 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"); } FixedPointPreCompInfo infoP = FixedPointUtil.precompute(p); FixedPointPreCompInfo infoQ = FixedPointUtil.precompute(q); ECLookupTable lookupTableP = infoP.getLookupTable(); ECLookupTable lookupTableQ = infoQ.getLookupTable(); int widthP = infoP.getWidth(); int widthQ = infoQ.getWidth(); // TODO This shouldn't normally happen, but a better "solution" is desirable anyway if (widthP != widthQ) { FixedPointCombMultiplier m = new FixedPointCombMultiplier(); ECPoint r1 = m.multiply(p, k); ECPoint r2 = m.multiply(q, l); return r1.add(r2); } int width = widthP; int d = (combSize + width - 1) / width; ECPoint R = c.getInfinity(); int fullComb = d * width; int[] K = Nat.fromBigInteger(fullComb, k); int[] L = Nat.fromBigInteger(fullComb, l); int top = fullComb - 1; for (int i = 0; i < d; ++i) { int secretIndexK = 0, secretIndexL = 0; for (int j = top - i; j >= 0; j -= d) { int secretBitK = K[j >>> 5] >>> (j & 0x1F); secretIndexK ^= secretBitK >>> 1; secretIndexK <<= 1; secretIndexK ^= secretBitK; int secretBitL = L[j >>> 5] >>> (j & 0x1F); secretIndexL ^= secretBitL >>> 1; secretIndexL <<= 1; secretIndexL ^= secretBitL; } ECPoint addP = lookupTableP.lookupVar(secretIndexK); ECPoint addQ = lookupTableQ.lookupVar(secretIndexL); ECPoint T = addP.add(addQ); R = R.twicePlus(T); } return R.add(infoP.getOffset()).add(infoQ.getOffset()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy