diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java index f8f37259b7d50..fbdd2bdff3786 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java @@ -206,12 +206,9 @@ private static void validate(ECOperations ops, ECPublicKey key) // // Compute nQ (using elliptic curve arithmetic), and verify that // nQ is the identity element. - ImmutableIntegerModuloP xElem = ops.getField().getElement(x); - ImmutableIntegerModuloP yElem = ops.getField().getElement(y); - AffinePoint affP = new AffinePoint(xElem, yElem); byte[] order = spec.getOrder().toByteArray(); ArrayUtil.reverse(order); - Point product = ops.multiply(affP, order); + Point product = ops.multiply(key.getW(), order); if (!ops.isNeutral(product)) { throw new InvalidKeyException("Point has incorrect order"); } @@ -275,12 +272,8 @@ byte[] deriveKeyImpl(ECPrivateKey priv, ECOperations ops, scalar.setProduct(cofactor); int keySize = (priv.getParams().getCurve().getField().getFieldSize() + 7) / 8; - ImmutableIntegerModuloP x = - field.getElement(pubKey.getW().getAffineX()); - ImmutableIntegerModuloP y = - field.getElement(pubKey.getW().getAffineY()); - Point product = ops.multiply(new AffinePoint(x, y), - scalar.asByteArray(keySize)); + Point product = + ops.multiply(pubKey.getW(), scalar.asByteArray(keySize)); if (ops.isNeutral(product)) { throw new InvalidKeyException("Product is zero"); } diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java index f58b557dcf79c..7bbcbd032f125 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java @@ -243,9 +243,6 @@ public boolean verifySignedDigest(byte[] digest, byte[] sig, ECPoint pp) { ImmutableIntegerModuloP u1 = e.multiply(sInv); ImmutableIntegerModuloP u2 = ri.multiply(sInv); - AffinePoint pub = new AffinePoint(field.getElement(pp.getAffineX()), - field.getElement(pp.getAffineY())); - byte[] temp1 = new byte[length]; b2a(u1, orderField, temp1); @@ -253,7 +250,7 @@ public boolean verifySignedDigest(byte[] digest, byte[] sig, ECPoint pp) { b2a(u2, orderField, temp2); MutablePoint p1 = ecOps.multiply(basePoint, temp1); - MutablePoint p2 = ecOps.multiply(pub, temp2); + MutablePoint p2 = ecOps.multiply(pp, temp2); ecOps.setSum(p1, p2.asAffine()); IntegerModuloP result = p1.asAffine().getX(); diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java index 665680d0840fe..126800613d2f2 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java @@ -191,11 +191,7 @@ private Optional generateKeyPairImpl(SecureRandom random) int seedSize = (seedBits + 7) / 8; byte[] privArr = generatePrivateScalar(random, ops, seedSize); - ECPoint genPoint = ecParams.getGenerator(); - ImmutableIntegerModuloP x = field.getElement(genPoint.getAffineX()); - ImmutableIntegerModuloP y = field.getElement(genPoint.getAffineY()); - AffinePoint affGen = new AffinePoint(x, y); - Point pub = ops.multiply(affGen, privArr); + Point pub = ops.multiply(ecParams.getGenerator(), privArr); AffinePoint affPub = pub.asAffine(); PrivateKey privateKey = new ECPrivateKeyImpl(privArr, ecParams); diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java index 2ef88c228aeae..34ba2e05dd0ee 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java @@ -26,6 +26,8 @@ package sun.security.ec; import sun.security.ec.point.*; +import sun.security.util.CurveDB; +import sun.security.util.KnownOIDs; import sun.security.util.ArrayUtil; import sun.security.util.math.*; import sun.security.util.math.intpoly.*; @@ -46,6 +48,10 @@ */ public class ECOperations { + private static final ECOperations secp256r1Ops = + new ECOperations(IntegerPolynomialP256.ONE.getElement( + CurveDB.lookup(KnownOIDs.secp256r1.value()).getCurve().getB()), + P256OrderField.ONE); /* * An exception indicating a problem with an intermediate value produced @@ -72,10 +78,9 @@ static class IntermediateValueException extends Exception { public static Optional forParameters(ECParameterSpec params) { EllipticCurve curve = params.getCurve(); - if (!(curve.getField() instanceof ECFieldFp)) { + if (!(curve.getField() instanceof ECFieldFp primeField)) { return Optional.empty(); } - ECFieldFp primeField = (ECFieldFp) curve.getField(); BigInteger three = BigInteger.valueOf(3); if (!primeField.getP().subtract(curve.getA()).equals(three)) { @@ -193,35 +198,6 @@ public static boolean allZero(byte[] arr) { return acc == 0; } - /* - * 4-bit branchless array lookup for projective points. - */ - private void lookup4(ProjectivePoint.Immutable[] arr, int index, - ProjectivePoint.Mutable result, IntegerModuloP zero) { - - for (int i = 0; i < 16; i++) { - int xor = index ^ i; - int bit3 = (xor & 0x8) >>> 3; - int bit2 = (xor & 0x4) >>> 2; - int bit1 = (xor & 0x2) >>> 1; - int bit0 = (xor & 0x1); - int inverse = bit0 | bit1 | bit2 | bit3; - int set = 1 - inverse; - - ProjectivePoint.Immutable pi = arr[i]; - result.conditionalSet(pi, set); - } - } - - private void double4(ProjectivePoint.Mutable p, MutableIntegerModuloP t0, - MutableIntegerModuloP t1, MutableIntegerModuloP t2, - MutableIntegerModuloP t3, MutableIntegerModuloP t4) { - - for (int i = 0; i < 4; i++) { - setDouble(p, t0, t1, t2, t3, t4); - } - } - /** * Multiply an affine point by a scalar and return the result as a mutable * point. @@ -231,58 +207,11 @@ private void double4(ProjectivePoint.Mutable p, MutableIntegerModuloP t0, * @return the product */ public MutablePoint multiply(AffinePoint affineP, byte[] s) { + return PointMultiplier.of(this, affineP).pointMultiply(s); + } - // 4-bit windowed multiply with branchless lookup. - // The mixed addition is faster, so it is used to construct the array - // at the beginning of the operation. - - IntegerFieldModuloP field = affineP.getX().getField(); - ImmutableIntegerModuloP zero = field.get0(); - // temporaries - MutableIntegerModuloP t0 = zero.mutable(); - MutableIntegerModuloP t1 = zero.mutable(); - MutableIntegerModuloP t2 = zero.mutable(); - MutableIntegerModuloP t3 = zero.mutable(); - MutableIntegerModuloP t4 = zero.mutable(); - - ProjectivePoint.Mutable result = new ProjectivePoint.Mutable(field); - result.getY().setValue(field.get1().mutable()); - - ProjectivePoint.Immutable[] pointMultiples = - new ProjectivePoint.Immutable[16]; - // 0P is neutral---same as initial result value - pointMultiples[0] = result.fixed(); - - ProjectivePoint.Mutable ps = new ProjectivePoint.Mutable(field); - ps.setValue(affineP); - // 1P = P - pointMultiples[1] = ps.fixed(); - - // the rest are calculated using mixed point addition - for (int i = 2; i < 16; i++) { - setSum(ps, affineP, t0, t1, t2, t3, t4); - pointMultiples[i] = ps.fixed(); - } - - ProjectivePoint.Mutable lookupResult = ps.mutable(); - - for (int i = s.length - 1; i >= 0; i--) { - - double4(result, t0, t1, t2, t3, t4); - - int high = (0xFF & s[i]) >>> 4; - lookup4(pointMultiples, high, lookupResult, zero); - setSum(result, lookupResult, t0, t1, t2, t3, t4); - - double4(result, t0, t1, t2, t3, t4); - - int low = 0xF & s[i]; - lookup4(pointMultiples, low, lookupResult, zero); - setSum(result, lookupResult, t0, t1, t2, t3, t4); - } - - return result; - + public MutablePoint multiply(ECPoint ecPoint, byte[] s) { + return PointMultiplier.of(this, ecPoint).pointMultiply(s); } /* @@ -404,7 +333,6 @@ private void setSum(ProjectivePoint.Mutable p, AffinePoint p2, p.getZ().setProduct(t4); t3.setProduct(t0); p.getZ().setSum(t3); - } /* @@ -470,7 +398,6 @@ private void setSum(ProjectivePoint.Mutable p, ProjectivePoint.Mutable p2, t3.setProduct(t0); p.getZ().setSum(t3); - } // The extra step in the Full Public key validation as described in @@ -486,5 +413,290 @@ public boolean checkOrder(ECPoint point) { ArrayUtil.reverse(scalar); return isNeutral(this.multiply(ap, scalar)); } -} + sealed interface PointMultiplier { + Map multipliers = Map.of( + Secp256R1GeneratorMultiplier.generator, + Secp256R1GeneratorMultiplier.multiplier); + + // Multiply the point by a scalar and return the result as a mutable + // point. The multiplier point is specified by the implementation of + // this interface, which could be a general EC point or EC generator + // point. + // + // Multiply the ECPoint (that is specified in the implementation) by + // a scalar and return the result as a ProjectivePoint.Mutable point. + // The point to be multiplied can be a general EC point or the + // generator of a named EC group. The scalar multiplier is an integer + // in little endian byte array representation. + ProjectivePoint.Mutable pointMultiply(byte[] scalar); + + static PointMultiplier of(ECOperations ecOps, AffinePoint affPoint) { + PointMultiplier multiplier = multipliers.get(affPoint.toECPoint()); + if (multiplier == null) { + multiplier = new Default(ecOps, affPoint); + } + + return multiplier; + } + + static PointMultiplier of(ECOperations ecOps, ECPoint ecPoint) { + PointMultiplier multiplier = multipliers.get(ecPoint); + if (multiplier == null) { + AffinePoint affPoint = + AffinePoint.fromECPoint(ecPoint, ecOps.getField()); + multiplier = new Default(ecOps, affPoint); + } + + return multiplier; + } + + private static void lookup( + ProjectivePoint.Immutable[] ips, int index, + ProjectivePoint.Mutable result) { + for (int i = 0; i < 16; i++) { + int xor = index ^ i; + int bit3 = (xor & 0x8) >>> 3; + int bit2 = (xor & 0x4) >>> 2; + int bit1 = (xor & 0x2) >>> 1; + int bit0 = (xor & 0x1); + int inverse = bit0 | bit1 | bit2 | bit3; + int set = 1 - inverse; + + ProjectivePoint.Immutable pi = ips[i]; + result.conditionalSet(pi, set); + } + } + + final class Default implements PointMultiplier { + private final AffinePoint affineP; + private final ECOperations ecOps; + + private Default(ECOperations ecOps, AffinePoint affineP) { + this.ecOps = ecOps; + this.affineP = affineP; + } + + @Override + public ProjectivePoint.Mutable pointMultiply(byte[] s) { + // 4-bit windowed multiply with branchless lookup. + // The mixed addition is faster, so it is used to construct + // the array at the beginning of the operation. + + IntegerFieldModuloP field = affineP.getX().getField(); + ImmutableIntegerModuloP zero = field.get0(); + // temporaries + MutableIntegerModuloP t0 = zero.mutable(); + MutableIntegerModuloP t1 = zero.mutable(); + MutableIntegerModuloP t2 = zero.mutable(); + MutableIntegerModuloP t3 = zero.mutable(); + MutableIntegerModuloP t4 = zero.mutable(); + + ProjectivePoint.Mutable result = + new ProjectivePoint.Mutable(field); + result.getY().setValue(field.get1().mutable()); + + ProjectivePoint.Immutable[] pointMultiples = + new ProjectivePoint.Immutable[16]; + // 0P is neutral---same as initial result value + pointMultiples[0] = result.fixed(); + + ProjectivePoint.Mutable ps = new ProjectivePoint.Mutable(field); + ps.setValue(affineP); + // 1P = P + pointMultiples[1] = ps.fixed(); + + // the rest are calculated using mixed point addition + for (int i = 2; i < 16; i++) { + ecOps.setSum(ps, affineP, t0, t1, t2, t3, t4); + pointMultiples[i] = ps.fixed(); + } + + ProjectivePoint.Mutable lookupResult = ps.mutable(); + + for (int i = s.length - 1; i >= 0; i--) { + double4(result, t0, t1, t2, t3, t4); + + int high = (0xFF & s[i]) >>> 4; + lookup(pointMultiples, high, lookupResult); + ecOps.setSum(result, lookupResult, t0, t1, t2, t3, t4); + + double4(result, t0, t1, t2, t3, t4); + + int low = 0xF & s[i]; + lookup(pointMultiples, low, lookupResult); + ecOps.setSum(result, lookupResult, t0, t1, t2, t3, t4); + } + + return result; + } + + private void double4(ProjectivePoint.Mutable p, + MutableIntegerModuloP t0, MutableIntegerModuloP t1, + MutableIntegerModuloP t2, MutableIntegerModuloP t3, + MutableIntegerModuloP t4) { + for (int i = 0; i < 4; i++) { + ecOps.setDouble(p, t0, t1, t2, t3, t4); + } + } + } + + final class Secp256R1GeneratorMultiplier implements PointMultiplier { + private static final ECPoint generator = + CurveDB.lookup("secp256r1").getGenerator(); + private static final PointMultiplier multiplier = + new Secp256R1GeneratorMultiplier(); + + private static final ImmutableIntegerModuloP zero = + IntegerPolynomialP256.ONE.get0(); + private static final ImmutableIntegerModuloP one = + IntegerPolynomialP256.ONE.get1(); + + @Override + public ProjectivePoint.Mutable pointMultiply(byte[] s) { + MutableIntegerModuloP t0 = zero.mutable(); + MutableIntegerModuloP t1 = zero.mutable(); + MutableIntegerModuloP t2 = zero.mutable(); + MutableIntegerModuloP t3 = zero.mutable(); + MutableIntegerModuloP t4 = zero.mutable(); + + ProjectivePoint.Mutable d = new ProjectivePoint.Mutable( + zero.mutable(), + one.mutable(), + zero.mutable()); + ProjectivePoint.Mutable r = d.mutable(); + for (int i = 15; i >= 0; i--) { + secp256r1Ops.setDouble(d, t0, t1, t2, t3, t4); + for (int j = 3; j >= 0; j--) { + int pos = i + j * 16; + int index = (bit(s, pos + 192) << 3) | + (bit(s, pos + 128) << 2) | + (bit(s, pos + 64) << 1) | + bit(s, pos); + + lookup(P256.points[j], index, r); + secp256r1Ops.setSum(d, r, t0, t1, t2, t3, t4); + } + } + + return d; + } + + private static int bit(byte[] k, int i) { + return (k[i >> 3] >> (i & 0x07)) & 0x01; + } + + // Lazy loading of the tables. + private static final class P256 { + // Pre-computed table to speed up the point multiplication. + // + // This is a 4x16 array of ProjectivePoint.Immutable elements. + // The first row contains the following multiples of the + // generator. + // + // index | point + // --------+---------------- + // 0x0000 | 0G + // 0x0001 | 1G + // 0x0002 | (2^64)G + // 0x0003 | (2^64 + 1)G + // 0x0004 | 2^128G + // 0x0005 | (2^128 + 1)G + // 0x0006 | (2^128 + 2^64)G + // 0x0007 | (2^128 + 2^64 + 1)G + // 0x0008 | 2^192G + // 0x0009 | (2^192 + 1)G + // 0x000A | (2^192 + 2^64)G + // 0x000B | (2^192 + 2^64 + 1)G + // 0x000C | (2^192 + 2^128)G + // 0x000D | (2^192 + 2^128 + 1)G + // 0x000E | (2^192 + 2^128 + 2^64)G + // 0x000F | (2^192 + 2^128 + 2^64 + 1)G + // + // For the other 3 rows, points[i][j] = 2^16 * (points[i-1][j]. + private static final ProjectivePoint.Immutable[][] points; + + // Generate the pre-computed tables. This block may be + // replaced with hard-coded tables in order to speed up + // the class loading. + static { + points = new ProjectivePoint.Immutable[4][16]; + BigInteger[] factors = new BigInteger[] { + BigInteger.ONE, + BigInteger.TWO.pow(64), + BigInteger.TWO.pow(128), + BigInteger.TWO.pow(192) + }; + + BigInteger[] base = new BigInteger[16]; + base[0] = BigInteger.ZERO; + base[1] = BigInteger.ONE; + base[2] = factors[1]; + for (int i = 3; i < 16; i++) { + base[i] = BigInteger.ZERO; + for (int k = 0; k < 4; k++) { + if (((i >>> k) & 0x01) != 0) { + base[i] = base[i].add(factors[k]); + } + } + } + + for (int d = 0; d < 4; d++) { + for (int w = 0; w < 16; w++) { + BigInteger bi = base[w]; + if (d != 0) { + bi = bi.multiply(BigInteger.TWO.pow(d * 16)); + } + if (w == 0) { + points[d][0] = new ProjectivePoint.Immutable( + zero.fixed(), one.fixed(), zero.fixed()); + } else { + PointMultiplier multiplier = new Default( + secp256r1Ops, AffinePoint.fromECPoint( + generator, zero.getField())); + byte[] s = bi.toByteArray(); + ArrayUtil.reverse(s); + ProjectivePoint.Mutable m = + multiplier.pointMultiply(s); + points[d][w] = m.setValue(m.asAffine()).fixed(); + } + } + } + + // Check that the tables are correctly generated. + if (ECOperations.class.desiredAssertionStatus()) { + verifyTables(base); + } + } + + private static void verifyTables(BigInteger[] base) { + for (int d = 0; d < 4; d++) { + for (int w = 0; w < 16; w++) { + BigInteger bi = base[w]; + if (d != 0) { + bi = bi.multiply(BigInteger.TWO.pow(d * 16)); + } + if (w != 0) { + byte[] s = new byte[32]; + byte[] b = bi.toByteArray(); + ArrayUtil.reverse(b); + System.arraycopy(b, 0, s, 0, b.length); + + ProjectivePoint.Mutable m = + multiplier.pointMultiply(s); + ProjectivePoint.Immutable v = + m.setValue(m.asAffine()).fixed(); + if (!v.getX().asBigInteger().equals( + points[d][w].getX().asBigInteger()) || + !v.getY().asBigInteger().equals( + points[d][w].getY().asBigInteger())) { + throw new RuntimeException(); + } + } + } + } + } + } + } + } +} diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/point/AffinePoint.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/AffinePoint.java index a8b74bdd4c5d1..bc227b0babfca 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/point/AffinePoint.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/AffinePoint.java @@ -25,7 +25,9 @@ package sun.security.ec.point; import sun.security.util.math.ImmutableIntegerModuloP; +import sun.security.util.math.IntegerFieldModuloP; +import java.security.spec.ECPoint; import java.util.Objects; /** @@ -44,6 +46,17 @@ public AffinePoint(ImmutableIntegerModuloP x, ImmutableIntegerModuloP y) { this.y = y; } + public static AffinePoint fromECPoint( + ECPoint ecPoint, IntegerFieldModuloP field) { + return new AffinePoint( + field.getElement(ecPoint.getAffineX()), + field.getElement(ecPoint.getAffineY())); + } + + public ECPoint toECPoint() { + return new ECPoint(x.asBigInteger(), y.asBigInteger()); + } + public ImmutableIntegerModuloP getX() { return x; }