8208698: Improved ECC Implementation
Summary: New implementation of ECDH and ECDSA forsome prime-order curves
Reviewed-by: ascarpino
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java Tue Dec 11 10:29:08 2018 -0500
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java Tue Dec 11 09:42:45 2018 -0500
@@ -25,14 +25,19 @@
package sun.security.ec;
+import java.math.*;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
+import java.util.Optional;
import javax.crypto.*;
import javax.crypto.spec.*;
+import sun.security.util.ArrayUtil;
import sun.security.util.ECUtil;
+import sun.security.util.math.*;
+import sun.security.ec.point.*;
/**
* KeyAgreement implementation for ECDH.
@@ -44,8 +49,8 @@
// private key, if initialized
private ECPrivateKey privateKey;
- // encoded public point, non-null between doPhase() & generateSecret() only
- private byte[] publicValue;
+ // public key, non-null between doPhase() & generateSecret() only
+ private ECPublicKey publicKey;
// length of the secret to be derived
private int secretLen;
@@ -65,7 +70,7 @@
("Key must be instance of PrivateKey");
}
privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key);
- publicValue = null;
+ publicKey = null;
}
// see JCE spec
@@ -87,7 +92,7 @@
if (privateKey == null) {
throw new IllegalStateException("Not initialized");
}
- if (publicValue != null) {
+ if (publicKey != null) {
throw new IllegalStateException("Phase already executed");
}
if (!lastPhase) {
@@ -99,42 +104,74 @@
("Key must be a PublicKey with algorithm EC");
}
- ECPublicKey ecKey = (ECPublicKey)key;
- ECParameterSpec params = ecKey.getParams();
+ this.publicKey = (ECPublicKey) key;
- if (ecKey instanceof ECPublicKeyImpl) {
- publicValue = ((ECPublicKeyImpl)ecKey).getEncodedPublicValue();
- } else { // instanceof ECPublicKey
- publicValue =
- ECUtil.encodePoint(ecKey.getW(), params.getCurve());
- }
+ ECParameterSpec params = publicKey.getParams();
int keyLenBits = params.getCurve().getField().getFieldSize();
secretLen = (keyLenBits + 7) >> 3;
return null;
}
+ private static void validateCoordinate(BigInteger c, BigInteger mod) {
+ if (c.compareTo(BigInteger.ZERO) < 0) {
+ throw new ProviderException("invalid coordinate");
+ }
+
+ if (c.compareTo(mod) >= 0) {
+ throw new ProviderException("invalid coordinate");
+ }
+ }
+
+ /*
+ * Check whether a public key is valid. Throw ProviderException
+ * if it is not valid or could not be validated.
+ */
+ private static void validate(ECOperations ops, ECPublicKey key) {
+
+ // ensure that integers are in proper range
+ BigInteger x = key.getW().getAffineX();
+ BigInteger y = key.getW().getAffineY();
+
+ BigInteger p = ops.getField().getSize();
+ validateCoordinate(x, p);
+ validateCoordinate(y, p);
+
+ // ensure the point is on the curve
+ EllipticCurve curve = key.getParams().getCurve();
+ BigInteger rhs = x.modPow(BigInteger.valueOf(3), p).add(curve.getA()
+ .multiply(x)).add(curve.getB()).mod(p);
+ BigInteger lhs = y.modPow(BigInteger.valueOf(2), p).mod(p);
+ if (!rhs.equals(lhs)) {
+ throw new ProviderException("point is not on curve");
+ }
+
+ // check the order of the point
+ ImmutableIntegerModuloP xElem = ops.getField().getElement(x);
+ ImmutableIntegerModuloP yElem = ops.getField().getElement(y);
+ AffinePoint affP = new AffinePoint(xElem, yElem);
+ byte[] order = key.getParams().getOrder().toByteArray();
+ ArrayUtil.reverse(order);
+ Point product = ops.multiply(affP, order);
+ if (!ops.isNeutral(product)) {
+ throw new ProviderException("point has incorrect order");
+ }
+
+ }
+
// see JCE spec
@Override
protected byte[] engineGenerateSecret() throws IllegalStateException {
- if ((privateKey == null) || (publicValue == null)) {
+ if ((privateKey == null) || (publicKey == null)) {
throw new IllegalStateException("Not initialized correctly");
}
- byte[] s = privateKey.getS().toByteArray();
- byte[] encodedParams = // DER OID
- ECUtil.encodeECParameterSpec(null, privateKey.getParams());
-
- try {
-
- byte[] result = deriveKey(s, publicValue, encodedParams);
- publicValue = null;
- return result;
-
- } catch (GeneralSecurityException e) {
- throw new ProviderException("Could not derive key", e);
- }
-
+ Optional<byte[]> resultOpt = deriveKeyImpl(privateKey, publicKey);
+ byte[] result = resultOpt.orElseGet(
+ () -> deriveKeyNative(privateKey, publicKey)
+ );
+ publicKey = null;
+ return result;
}
// see JCE spec
@@ -143,7 +180,8 @@
offset) throws IllegalStateException, ShortBufferException {
if (offset + secretLen > sharedSecret.length) {
throw new ShortBufferException("Need " + secretLen
- + " bytes, only " + (sharedSecret.length - offset) + " available");
+ + " bytes, only " + (sharedSecret.length - offset)
+ + " available");
}
byte[] secret = engineGenerateSecret();
System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
@@ -165,6 +203,78 @@
return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret");
}
+ private static
+ Optional<byte[]> deriveKeyImpl(ECPrivateKey priv, ECPublicKey pubKey) {
+
+ ECParameterSpec ecSpec = priv.getParams();
+ EllipticCurve curve = ecSpec.getCurve();
+ Optional<ECOperations> opsOpt = ECOperations.forParameters(ecSpec);
+ if (opsOpt.isEmpty()) {
+ return Optional.empty();
+ }
+ ECOperations ops = opsOpt.get();
+ if (! (priv instanceof ECPrivateKeyImpl)) {
+ return Optional.empty();
+ }
+ ECPrivateKeyImpl privImpl = (ECPrivateKeyImpl) priv;
+ byte[] sArr = privImpl.getArrayS();
+
+ // to match the native implementation, validate the public key here
+ // and throw ProviderException if it is invalid
+ validate(ops, pubKey);
+
+ IntegerFieldModuloP field = ops.getField();
+ // convert s array into field element and multiply by the cofactor
+ MutableIntegerModuloP scalar = field.getElement(sArr).mutable();
+ SmallValue cofactor =
+ field.getSmallValue(priv.getParams().getCofactor());
+ scalar.setProduct(cofactor);
+ int keySize = (curve.getField().getFieldSize() + 7) / 8;
+ byte[] privArr = scalar.asByteArray(keySize);
+
+ ImmutableIntegerModuloP x =
+ field.getElement(pubKey.getW().getAffineX());
+ ImmutableIntegerModuloP y =
+ field.getElement(pubKey.getW().getAffineY());
+ AffinePoint affPub = new AffinePoint(x, y);
+ Point product = ops.multiply(affPub, privArr);
+ if (ops.isNeutral(product)) {
+ throw new ProviderException("Product is zero");
+ }
+ AffinePoint affProduct = product.asAffine();
+
+ byte[] result = affProduct.getX().asByteArray(keySize);
+ ArrayUtil.reverse(result);
+
+ return Optional.of(result);
+ }
+
+ private static
+ byte[] deriveKeyNative(ECPrivateKey privateKey, ECPublicKey publicKey) {
+
+ ECParameterSpec params = privateKey.getParams();
+ byte[] s = privateKey.getS().toByteArray();
+ byte[] encodedParams = // DER OID
+ ECUtil.encodeECParameterSpec(null, params);
+
+ byte[] publicValue;
+ if (publicKey instanceof ECPublicKeyImpl) {
+ ECPublicKeyImpl ecPub = (ECPublicKeyImpl) publicKey;
+ publicValue = ecPub.getEncodedPublicValue();
+ } else { // instanceof ECPublicKey
+ publicValue =
+ ECUtil.encodePoint(publicKey.getW(), params.getCurve());
+ }
+
+ try {
+ return deriveKey(s, publicValue, encodedParams);
+
+ } catch (GeneralSecurityException e) {
+ throw new ProviderException("Could not derive key", e);
+ }
+ }
+
+
/**
* Generates a secret key using the public and private keys.
*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java Tue Dec 11 09:42:45 2018 -0500
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ec;
+
+import sun.security.ec.point.*;
+import sun.security.util.ArrayUtil;
+import sun.security.util.math.*;
+import static sun.security.ec.ECOperations.IntermediateValueException;
+
+import java.security.ProviderException;
+import java.security.spec.*;
+import java.util.Optional;
+
+public class ECDSAOperations {
+
+ public static class Seed {
+ private final byte[] seedValue;
+
+ public Seed(byte[] seedValue) {
+ this.seedValue = seedValue;
+ }
+
+ public byte[] getSeedValue() {
+ return seedValue;
+ }
+ }
+
+ public static class Nonce {
+ private final byte[] nonceValue;
+
+ public Nonce(byte[] nonceValue) {
+ this.nonceValue = nonceValue;
+ }
+
+ public byte[] getNonceValue() {
+ return nonceValue;
+ }
+ }
+
+ private final ECOperations ecOps;
+ private final AffinePoint basePoint;
+
+ public ECDSAOperations(ECOperations ecOps, ECPoint basePoint) {
+ this.ecOps = ecOps;
+ this.basePoint = toAffinePoint(basePoint, ecOps.getField());
+ }
+
+ public ECOperations getEcOperations() {
+ return ecOps;
+ }
+
+ public AffinePoint basePointMultiply(byte[] scalar) {
+ return ecOps.multiply(basePoint, scalar).asAffine();
+ }
+
+ public static AffinePoint toAffinePoint(ECPoint point,
+ IntegerFieldModuloP field) {
+
+ ImmutableIntegerModuloP affineX = field.getElement(point.getAffineX());
+ ImmutableIntegerModuloP affineY = field.getElement(point.getAffineY());
+ return new AffinePoint(affineX, affineY);
+ }
+
+ public static
+ Optional<ECDSAOperations> forParameters(ECParameterSpec ecParams) {
+ Optional<ECOperations> curveOps =
+ ECOperations.forParameters(ecParams);
+ return curveOps.map(
+ ops -> new ECDSAOperations(ops, ecParams.getGenerator())
+ );
+ }
+
+ /**
+ *
+ * Sign a digest using the provided private key and seed.
+ * IMPORTANT: The private key is a scalar represented using a
+ * little-endian byte array. This is backwards from the conventional
+ * representation in ECDSA. The routines that produce and consume this
+ * value uses little-endian, so this deviation from convention removes
+ * the requirement to swap the byte order. The returned signature is in
+ * the conventional byte order.
+ *
+ * @param privateKey the private key scalar as a little-endian byte array
+ * @param digest the digest to be signed
+ * @param seed the seed that will be used to produce the nonce. This object
+ * should contain an array that is at least 64 bits longer than
+ * the number of bits required to represent the group order.
+ * @return the ECDSA signature value
+ * @throws IntermediateValueException if the signature cannot be produced
+ * due to an unacceptable intermediate or final value. If this
+ * exception is thrown, then the caller should discard the nonnce and
+ * try again with an entirely new nonce value.
+ */
+ public byte[] signDigest(byte[] privateKey, byte[] digest, Seed seed)
+ throws IntermediateValueException {
+
+ byte[] nonceArr = ecOps.seedToScalar(seed.getSeedValue());
+
+ Nonce nonce = new Nonce(nonceArr);
+ return signDigest(privateKey, digest, nonce);
+ }
+
+ /**
+ *
+ * Sign a digest using the provided private key and nonce.
+ * IMPORTANT: The private key and nonce are scalars represented by a
+ * little-endian byte array. This is backwards from the conventional
+ * representation in ECDSA. The routines that produce and consume these
+ * values use little-endian, so this deviation from convention removes
+ * the requirement to swap the byte order. The returned signature is in
+ * the conventional byte order.
+ *
+ * @param privateKey the private key scalar as a little-endian byte array
+ * @param digest the digest to be signed
+ * @param nonce the nonce object containing a little-endian scalar value.
+ * @return the ECDSA signature value
+ * @throws IntermediateValueException if the signature cannot be produced
+ * due to an unacceptable intermediate or final value. If this
+ * exception is thrown, then the caller should discard the nonnce and
+ * try again with an entirely new nonce value.
+ */
+ public byte[] signDigest(byte[] privateKey, byte[] digest, Nonce nonce)
+ throws IntermediateValueException {
+
+ IntegerFieldModuloP orderField = ecOps.getOrderField();
+ int orderBits = orderField.getSize().bitLength();
+ if (orderBits % 8 != 0 && orderBits < digest.length * 8) {
+ // This implementation does not support truncating digests to
+ // a length that is not a multiple of 8.
+ throw new ProviderException("Invalid digest length");
+ }
+
+ byte[] k = nonce.getNonceValue();
+ // check nonce length
+ int length = (orderField.getSize().bitLength() + 7) / 8;
+ if (k.length != length) {
+ throw new ProviderException("Incorrect nonce length");
+ }
+
+ MutablePoint R = ecOps.multiply(basePoint, k);
+ IntegerModuloP r = R.asAffine().getX();
+ // put r into the correct field by fully reducing to an array
+ byte[] temp = new byte[length];
+ r.asByteArray(temp);
+ r = orderField.getElement(temp);
+ // store r in result
+ r.asByteArray(temp);
+ byte[] result = new byte[2 * length];
+ ArrayUtil.reverse(temp);
+ System.arraycopy(temp, 0, result, 0, length);
+ // compare r to 0
+ if (ECOperations.allZero(temp)) {
+ throw new IntermediateValueException();
+ }
+
+ IntegerModuloP dU = orderField.getElement(privateKey);
+ int lengthE = Math.min(length, digest.length);
+ byte[] E = new byte[lengthE];
+ System.arraycopy(digest, 0, E, 0, lengthE);
+ ArrayUtil.reverse(E);
+ IntegerModuloP e = orderField.getElement(E);
+ IntegerModuloP kElem = orderField.getElement(k);
+ IntegerModuloP kInv = kElem.multiplicativeInverse();
+ MutableIntegerModuloP s = r.mutable();
+ s.setProduct(dU).setSum(e).setProduct(kInv);
+ // store s in result
+ s.asByteArray(temp);
+ ArrayUtil.reverse(temp);
+ System.arraycopy(temp, 0, result, length, length);
+ // compare s to 0
+ if (ECOperations.allZero(temp)) {
+ throw new IntermediateValueException();
+ }
+
+ return result;
+
+ }
+
+}
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java Tue Dec 11 10:29:08 2018 -0500
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java Tue Dec 11 09:42:45 2018 -0500
@@ -32,9 +32,11 @@
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
+import java.util.Optional;
import sun.security.jca.JCAUtil;
import sun.security.util.*;
+import static sun.security.ec.ECOperations.IntermediateValueException;
/**
* ECDSA signature implementation. This class currently supports the
@@ -147,7 +149,7 @@
// Stores the precomputed message digest value.
@Override
protected void engineUpdate(byte[] b, int off, int len)
- throws SignatureException {
+ throws SignatureException {
if (offset >= precomputedDigest.length) {
offset = RAW_ECDSA_MAX + 1;
return;
@@ -172,7 +174,7 @@
}
@Override
- protected void resetDigest(){
+ protected void resetDigest() {
offset = 0;
}
@@ -222,14 +224,14 @@
// Nested class for SHA224withECDSA signatures
public static final class SHA224 extends ECDSASignature {
public SHA224() {
- super("SHA-224");
+ super("SHA-224");
}
}
// Nested class for SHA224withECDSAinP1363Format signatures
public static final class SHA224inP1363Format extends ECDSASignature {
public SHA224inP1363Format() {
- super("SHA-224", true);
+ super("SHA-224", true);
}
}
@@ -278,7 +280,7 @@
// initialize for verification. See JCA doc
@Override
protected void engineInitVerify(PublicKey publicKey)
- throws InvalidKeyException {
+ throws InvalidKeyException {
this.publicKey = (ECPublicKey) ECKeyFactory.toECKey(publicKey);
// Should check that the supplied key is appropriate for signature
@@ -290,14 +292,14 @@
// initialize for signing. See JCA doc
@Override
protected void engineInitSign(PrivateKey privateKey)
- throws InvalidKeyException {
+ throws InvalidKeyException {
engineInitSign(privateKey, null);
}
// initialize for signing. See JCA doc
@Override
protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
- throws InvalidKeyException {
+ throws InvalidKeyException {
this.privateKey = (ECPrivateKey) ECKeyFactory.toECKey(privateKey);
// Should check that the supplied key is appropriate for signature
@@ -337,7 +339,7 @@
// update the signature with the plaintext data. See JCA doc
@Override
protected void engineUpdate(byte[] b, int off, int len)
- throws SignatureException {
+ throws SignatureException {
messageDigest.update(b, off, len);
needsReset = true;
}
@@ -354,20 +356,67 @@
needsReset = true;
}
- // sign the data and return the signature. See JCA doc
- @Override
- protected byte[] engineSign() throws SignatureException {
+ private byte[] signDigestImpl(ECDSAOperations ops, int seedBits,
+ byte[] digest, ECPrivateKeyImpl privImpl, SecureRandom random)
+ throws SignatureException {
+
+ byte[] seedBytes = new byte[(seedBits + 7) / 8];
+ byte[] s = privImpl.getArrayS();
+
+ // Attempt to create the signature in a loop that uses new random input
+ // each time. The chance of failure is very small assuming the
+ // implementation derives the nonce using extra bits
+ int numAttempts = 128;
+ for (int i = 0; i < numAttempts; i++) {
+ random.nextBytes(seedBytes);
+ ECDSAOperations.Seed seed = new ECDSAOperations.Seed(seedBytes);
+ try {
+ return ops.signDigest(s, digest, seed);
+ } catch (IntermediateValueException ex) {
+ // try again in the next iteration
+ }
+ }
+
+ throw new SignatureException("Unable to produce signature after "
+ + numAttempts + " attempts");
+ }
+
+
+ private Optional<byte[]> signDigestImpl(ECPrivateKey privateKey,
+ byte[] digest, SecureRandom random) throws SignatureException {
+
+ if (! (privateKey instanceof ECPrivateKeyImpl)) {
+ return Optional.empty();
+ }
+ ECPrivateKeyImpl privImpl = (ECPrivateKeyImpl) privateKey;
+ ECParameterSpec params = privateKey.getParams();
+
+ // seed is the key size + 64 bits
+ int seedBits = params.getOrder().bitLength() + 64;
+ Optional<ECDSAOperations> opsOpt =
+ ECDSAOperations.forParameters(params);
+ if (opsOpt.isEmpty()) {
+ return Optional.empty();
+ } else {
+ byte[] sig = signDigestImpl(opsOpt.get(), seedBits, digest,
+ privImpl, random);
+ return Optional.of(sig);
+ }
+ }
+
+ private byte[] signDigestNative(ECPrivateKey privateKey, byte[] digest,
+ SecureRandom random) throws SignatureException {
+
byte[] s = privateKey.getS().toByteArray();
ECParameterSpec params = privateKey.getParams();
+
// DER OID
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
int keySize = params.getCurve().getField().getFieldSize();
// seed is twice the key size (in bytes) plus 1
byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2];
- if (random == null) {
- random = JCAUtil.getSecureRandom();
- }
+
random.nextBytes(seed);
// random bits needed for timing countermeasures
@@ -375,14 +424,32 @@
// values must be non-zero to enable countermeasures
timingArgument |= 1;
- byte[] sig;
try {
- sig = signDigest(getDigestValue(), s, encodedParams, seed,
+ return signDigest(digest, s, encodedParams, seed,
timingArgument);
} catch (GeneralSecurityException e) {
throw new SignatureException("Could not sign data", e);
}
+ }
+
+ // sign the data and return the signature. See JCA doc
+ @Override
+ protected byte[] engineSign() throws SignatureException {
+
+ if (random == null) {
+ random = JCAUtil.getSecureRandom();
+ }
+
+ byte[] digest = getDigestValue();
+ Optional<byte[]> sigOpt = signDigestImpl(privateKey, digest, random);
+ byte[] sig;
+ if (sigOpt.isPresent()) {
+ sig = sigOpt.get();
+ } else {
+ sig = signDigestNative(privateKey, digest, random);
+ }
+
if (p1363Format) {
return sig;
} else {
@@ -400,7 +467,7 @@
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
if (publicKey instanceof ECPublicKeyImpl) {
- w = ((ECPublicKeyImpl)publicKey).getEncodedPublicValue();
+ w = ((ECPublicKeyImpl) publicKey).getEncodedPublicValue();
} else { // instanceof ECPublicKey
w = ECUtil.encodePoint(publicKey.getW(), params.getCurve());
}
@@ -423,13 +490,13 @@
@Override
@Deprecated
protected void engineSetParameter(String param, Object value)
- throws InvalidParameterException {
+ throws InvalidParameterException {
throw new UnsupportedOperationException("setParameter() not supported");
}
@Override
protected void engineSetParameter(AlgorithmParameterSpec params)
- throws InvalidAlgorithmParameterException {
+ throws InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException("No parameter accepted");
}
@@ -439,7 +506,7 @@
@Override
@Deprecated
protected Object engineGetParameter(String param)
- throws InvalidParameterException {
+ throws InvalidParameterException {
throw new UnsupportedOperationException("getParameter() not supported");
}
@@ -464,7 +531,7 @@
out.putInteger(r);
out.putInteger(s);
DerValue result =
- new DerValue(DerValue.tag_Sequence, out.toByteArray());
+ new DerValue(DerValue.tag_Sequence, out.toByteArray());
return result.toByteArray();
@@ -497,9 +564,9 @@
// r and s each occupy half the array
byte[] result = new byte[k << 1];
System.arraycopy(rBytes, 0, result, k - rBytes.length,
- rBytes.length);
+ rBytes.length);
System.arraycopy(sBytes, 0, result, result.length - sBytes.length,
- sBytes.length);
+ sBytes.length);
return result;
} catch (Exception e) {
@@ -539,13 +606,13 @@
* @return byte[] the signature.
*/
private static native byte[] signDigest(byte[] digest, byte[] s,
- byte[] encodedParams, byte[] seed, int timing)
- throws GeneralSecurityException;
+ byte[] encodedParams, byte[] seed, int timing)
+ throws GeneralSecurityException;
/**
* Verifies the signed digest using the public key.
*
- * @param signedDigest the signature to be verified. It is encoded
+ * @param signature the signature to be verified. It is encoded
* as a concatenation of the key's R and S values.
* @param digest the digest to be used.
* @param w the public key's W point (in uncompressed form).
@@ -554,6 +621,6 @@
* @return boolean true if the signature is successfully verified.
*/
private static native boolean verifySignedDigest(byte[] signature,
- byte[] digest, byte[] w, byte[] encodedParams)
- throws GeneralSecurityException;
+ byte[] digest, byte[] w, byte[] encodedParams)
+ throws GeneralSecurityException;
}
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java Tue Dec 11 10:29:08 2018 -0500
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java Tue Dec 11 09:42:45 2018 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,13 +33,15 @@
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.*;
+import java.util.Optional;
-import sun.security.ec.ECPrivateKeyImpl;
-import sun.security.ec.ECPublicKeyImpl;
import sun.security.jca.JCAUtil;
-import sun.security.util.ECParameters;
import sun.security.util.ECUtil;
+import sun.security.util.math.*;
+import sun.security.ec.point.*;
import static sun.security.util.SecurityProviderConstants.DEF_EC_KEY_SIZE;
+import static sun.security.ec.ECOperations.IntermediateValueException;
/**
* EC keypair generator.
@@ -90,14 +92,14 @@
ECParameterSpec ecSpec = null;
if (params instanceof ECParameterSpec) {
- ecSpec = ECUtil.getECParameterSpec(null,
- (ECParameterSpec)params);
+ ECParameterSpec ecParams = (ECParameterSpec) params;
+ ecSpec = ECUtil.getECParameterSpec(null, ecParams);
if (ecSpec == null) {
throw new InvalidAlgorithmParameterException(
"Unsupported curve: " + params);
}
} else if (params instanceof ECGenParameterSpec) {
- String name = ((ECGenParameterSpec)params).getName();
+ String name = ((ECGenParameterSpec) params).getName();
ecSpec = ECUtil.getECParameterSpec(null, name);
if (ecSpec == null) {
throw new InvalidAlgorithmParameterException(
@@ -112,8 +114,7 @@
ensureCurveIsSupported(ecSpec);
this.params = ecSpec;
- this.keySize =
- ((ECParameterSpec)this.params).getCurve().getField().getFieldSize();
+ this.keySize = ecSpec.getCurve().getField().getFieldSize();
this.random = random;
}
@@ -141,39 +142,97 @@
@Override
public KeyPair generateKeyPair() {
- byte[] encodedParams =
- ECUtil.encodeECParameterSpec(null, (ECParameterSpec)params);
-
- // seed is twice the key size (in bytes) plus 1
- byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2];
if (random == null) {
random = JCAUtil.getSecureRandom();
}
- random.nextBytes(seed);
try {
-
- Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed);
+ Optional<KeyPair> kp = generateKeyPairImpl(random);
+ if (kp.isPresent()) {
+ return kp.get();
+ }
+ return generateKeyPairNative(random);
+ } catch (Exception ex) {
+ throw new ProviderException(ex);
+ }
+ }
- // The 'params' object supplied above is equivalent to the native
- // one so there is no need to fetch it.
- // keyBytes[0] is the encoding of the native private key
- BigInteger s = new BigInteger(1, (byte[])keyBytes[0]);
+ private byte[] generatePrivateScalar(SecureRandom random,
+ ECOperations ecOps, int seedSize) {
+ // Attempt to create the private scalar in a loop that uses new random
+ // input each time. The chance of failure is very small assuming the
+ // implementation derives the nonce using extra bits
+ int numAttempts = 128;
+ byte[] seedArr = new byte[seedSize];
+ for (int i = 0; i < numAttempts; i++) {
+ random.nextBytes(seedArr);
+ try {
+ return ecOps.seedToScalar(seedArr);
+ } catch (IntermediateValueException ex) {
+ // try again in the next iteration
+ }
+ }
- PrivateKey privateKey =
- new ECPrivateKeyImpl(s, (ECParameterSpec)params);
+ throw new ProviderException("Unable to produce private key after "
+ + numAttempts + " attempts");
+ }
+
+ private Optional<KeyPair> generateKeyPairImpl(SecureRandom random)
+ throws InvalidKeyException {
+
+ ECParameterSpec ecParams = (ECParameterSpec) params;
- // keyBytes[1] is the encoding of the native public key
- ECPoint w = ECUtil.decodePoint((byte[])keyBytes[1],
- ((ECParameterSpec)params).getCurve());
- PublicKey publicKey =
- new ECPublicKeyImpl(w, (ECParameterSpec)params);
+ Optional<ECOperations> opsOpt = ECOperations.forParameters(ecParams);
+ if (opsOpt.isEmpty()) {
+ return Optional.empty();
+ }
+ ECOperations ops = opsOpt.get();
+ IntegerFieldModuloP field = ops.getField();
+ int numBits = ecParams.getOrder().bitLength();
+ int seedBits = numBits + 64;
+ 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);
+ AffinePoint affPub = pub.asAffine();
+
+ PrivateKey privateKey = new ECPrivateKeyImpl(privArr, ecParams);
+
+ ECPoint w = new ECPoint(affPub.getX().asBigInteger(),
+ affPub.getY().asBigInteger());
+ PublicKey publicKey = new ECPublicKeyImpl(w, ecParams);
- return new KeyPair(publicKey, privateKey);
+ return Optional.of(new KeyPair(publicKey, privateKey));
+ }
+
+ private KeyPair generateKeyPairNative(SecureRandom random)
+ throws Exception {
+
+ ECParameterSpec ecParams = (ECParameterSpec) params;
+ byte[] encodedParams = ECUtil.encodeECParameterSpec(null, ecParams);
+
+ // seed is twice the key size (in bytes) plus 1
+ byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2];
+ random.nextBytes(seed);
+ Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed);
- } catch (Exception e) {
- throw new ProviderException(e);
- }
+ // The 'params' object supplied above is equivalent to the native
+ // one so there is no need to fetch it.
+ // keyBytes[0] is the encoding of the native private key
+ BigInteger s = new BigInteger(1, (byte[]) keyBytes[0]);
+
+ PrivateKey privateKey = new ECPrivateKeyImpl(s, ecParams);
+
+ // keyBytes[1] is the encoding of the native public key
+ byte[] pubKey = (byte[]) keyBytes[1];
+ ECPoint w = ECUtil.decodePoint(pubKey, ecParams.getCurve());
+ PublicKey publicKey = new ECPublicKeyImpl(w, ecParams);
+
+ return new KeyPair(publicKey, privateKey);
}
private void checkKeySize(int keySize) throws InvalidParameterException {
@@ -190,7 +249,9 @@
/**
* Checks whether the curve in the encoded parameters is supported by the
- * native implementation.
+ * native implementation. Some curve operations will be performed by the
+ * Java implementation, but not all of them. So native support is still
+ * required for all curves.
*
* @param encodedParams encoded parameters in the same form accepted
* by generateECKeyPair
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java Tue Dec 11 09:42:45 2018 -0500
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ec;
+
+import sun.security.ec.point.*;
+import sun.security.util.math.*;
+import sun.security.util.math.intpoly.*;
+
+import java.math.BigInteger;
+import java.security.ProviderException;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.EllipticCurve;
+import java.util.Map;
+import java.util.Optional;
+
+/*
+ * Elliptic curve point arithmetic for prime-order curves where a=-3.
+ * Formulas are derived from "Complete addition formulas for prime order
+ * elliptic curves" by Renes, Costello, and Batina.
+ */
+
+public class ECOperations {
+
+ /*
+ * An exception indicating a problem with an intermediate value produced
+ * by some part of the computation. For example, the signing operation
+ * will throw this exception to indicate that the r or s value is 0, and
+ * that the signing operation should be tried again with a different nonce.
+ */
+ static class IntermediateValueException extends Exception {
+ private static final long serialVersionUID = 1;
+ }
+
+ static final Map<BigInteger, IntegerFieldModuloP> fields = Map.of(
+ IntegerPolynomialP256.MODULUS, new IntegerPolynomialP256(),
+ IntegerPolynomialP384.MODULUS, new IntegerPolynomialP384(),
+ IntegerPolynomialP521.MODULUS, new IntegerPolynomialP521()
+ );
+
+ static final Map<BigInteger, IntegerFieldModuloP> orderFields = Map.of(
+ P256OrderField.MODULUS, new P256OrderField(),
+ P384OrderField.MODULUS, new P384OrderField(),
+ P521OrderField.MODULUS, new P521OrderField()
+ );
+
+ public static Optional<ECOperations> forParameters(ECParameterSpec params) {
+
+ EllipticCurve curve = params.getCurve();
+ if (!(curve.getField() instanceof ECFieldFp)) {
+ return Optional.empty();
+ }
+ ECFieldFp primeField = (ECFieldFp) curve.getField();
+
+ BigInteger three = BigInteger.valueOf(3);
+ if (!primeField.getP().subtract(curve.getA()).equals(three)) {
+ return Optional.empty();
+ }
+ IntegerFieldModuloP field = fields.get(primeField.getP());
+ if (field == null) {
+ return Optional.empty();
+ }
+
+ IntegerFieldModuloP orderField = orderFields.get(params.getOrder());
+ if (orderField == null) {
+ return Optional.empty();
+ }
+
+ ImmutableIntegerModuloP b = field.getElement(curve.getB());
+ ECOperations ecOps = new ECOperations(b, orderField);
+ return Optional.of(ecOps);
+ }
+
+ final ImmutableIntegerModuloP b;
+ final SmallValue one;
+ final SmallValue two;
+ final SmallValue three;
+ final SmallValue four;
+ final ProjectivePoint.Immutable neutral;
+ private final IntegerFieldModuloP orderField;
+
+ public ECOperations(IntegerModuloP b, IntegerFieldModuloP orderField) {
+ this.b = b.fixed();
+ this.orderField = orderField;
+
+ this.one = b.getField().getSmallValue(1);
+ this.two = b.getField().getSmallValue(2);
+ this.three = b.getField().getSmallValue(3);
+ this.four = b.getField().getSmallValue(4);
+
+ IntegerFieldModuloP field = b.getField();
+ this.neutral = new ProjectivePoint.Immutable(field.get0(),
+ field.get1(), field.get0());
+ }
+
+ public IntegerFieldModuloP getField() {
+ return b.getField();
+ }
+ public IntegerFieldModuloP getOrderField() {
+ return orderField;
+ }
+
+ protected ProjectivePoint.Immutable getNeutral() {
+ return neutral;
+ }
+
+ public boolean isNeutral(Point p) {
+ ProjectivePoint<?> pp = (ProjectivePoint<?>) p;
+
+ IntegerModuloP z = pp.getZ();
+
+ IntegerFieldModuloP field = z.getField();
+ int byteLength = (field.getSize().bitLength() + 7) / 8;
+ byte[] zBytes = z.asByteArray(byteLength);
+ return allZero(zBytes);
+ }
+
+ byte[] seedToScalar(byte[] seedBytes)
+ throws IntermediateValueException {
+
+ // Produce a nonce from the seed using FIPS 186-4,section B.5.1:
+ // Per-Message Secret Number Generation Using Extra Random Bits
+ // or
+ // Produce a scalar from the seed using FIPS 186-4, section B.4.1:
+ // Key Pair Generation Using Extra Random Bits
+
+ // To keep the implementation simple, sample in the range [0,n)
+ // and throw IntermediateValueException in the (unlikely) event
+ // that the result is 0.
+
+ // Get 64 extra bits and reduce in to the nonce
+ int seedBits = orderField.getSize().bitLength() + 64;
+ if (seedBytes.length * 8 < seedBits) {
+ throw new ProviderException("Incorrect seed length: " +
+ seedBytes.length * 8 + " < " + seedBits);
+ }
+
+ // input conversion only works on byte boundaries
+ // clear high-order bits of last byte so they don't influence nonce
+ int lastByteBits = seedBits % 8;
+ if (lastByteBits != 0) {
+ int lastByteIndex = seedBits / 8;
+ byte mask = (byte) (0xFF >>> (8 - lastByteBits));
+ seedBytes[lastByteIndex] &= mask;
+ }
+
+ int seedLength = (seedBits + 7) / 8;
+ IntegerModuloP scalarElem =
+ orderField.getElement(seedBytes, 0, seedLength, (byte) 0);
+ int scalarLength = (orderField.getSize().bitLength() + 7) / 8;
+ byte[] scalarArr = new byte[scalarLength];
+ scalarElem.asByteArray(scalarArr);
+ if (ECOperations.allZero(scalarArr)) {
+ throw new IntermediateValueException();
+ }
+ return scalarArr;
+ }
+
+ /*
+ * Compare all values in the array to 0 without branching on any value
+ *
+ */
+ public static boolean allZero(byte[] arr) {
+ byte acc = 0;
+ for (int i = 0; i < arr.length; i++) {
+ acc |= arr[i];
+ }
+ 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.
+ *
+ * @param affineP the point
+ * @param s the scalar as a little-endian array
+ * @return the product
+ */
+ public MutablePoint multiply(AffinePoint affineP, 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++) {
+ 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;
+
+ }
+
+ /*
+ * Point double
+ */
+ private void setDouble(ProjectivePoint.Mutable p, MutableIntegerModuloP t0,
+ MutableIntegerModuloP t1, MutableIntegerModuloP t2,
+ MutableIntegerModuloP t3, MutableIntegerModuloP t4) {
+
+ t0.setValue(p.getX()).setSquare();
+ t1.setValue(p.getY()).setSquare();
+ t2.setValue(p.getZ()).setSquare();
+ t3.setValue(p.getX()).setProduct(p.getY());
+ t4.setValue(p.getY()).setProduct(p.getZ());
+
+ t3.setSum(t3);
+ p.getZ().setProduct(p.getX());
+
+ p.getZ().setProduct(two);
+
+ p.getY().setValue(t2).setProduct(b);
+ p.getY().setDifference(p.getZ());
+
+ p.getX().setValue(p.getY()).setProduct(two);
+ p.getY().setSum(p.getX());
+ p.getY().setReduced();
+ p.getX().setValue(t1).setDifference(p.getY());
+
+ p.getY().setSum(t1);
+ p.getY().setProduct(p.getX());
+ p.getX().setProduct(t3);
+
+ t3.setValue(t2).setProduct(two);
+ t2.setSum(t3);
+ p.getZ().setProduct(b);
+
+ t2.setReduced();
+ p.getZ().setDifference(t2);
+ p.getZ().setDifference(t0);
+ t3.setValue(p.getZ()).setProduct(two);
+ p.getZ().setReduced();
+ p.getZ().setSum(t3);
+ t0.setProduct(three);
+
+ t0.setDifference(t2);
+ t0.setProduct(p.getZ());
+ p.getY().setSum(t0);
+
+ t4.setSum(t4);
+ p.getZ().setProduct(t4);
+
+ p.getX().setDifference(p.getZ());
+ p.getZ().setValue(t4).setProduct(t1);
+
+ p.getZ().setProduct(four);
+
+ }
+
+ /*
+ * Mixed point addition. This method constructs new temporaries each time
+ * it is called. For better efficiency, the method that reuses temporaries
+ * should be used if more than one sum will be computed.
+ */
+ public void setSum(MutablePoint p, AffinePoint p2) {
+
+ IntegerModuloP zero = p.getField().get0();
+ MutableIntegerModuloP t0 = zero.mutable();
+ MutableIntegerModuloP t1 = zero.mutable();
+ MutableIntegerModuloP t2 = zero.mutable();
+ MutableIntegerModuloP t3 = zero.mutable();
+ MutableIntegerModuloP t4 = zero.mutable();
+ setSum((ProjectivePoint.Mutable) p, p2, t0, t1, t2, t3, t4);
+
+ }
+
+ /*
+ * Mixed point addition
+ */
+ private void setSum(ProjectivePoint.Mutable p, AffinePoint p2,
+ MutableIntegerModuloP t0, MutableIntegerModuloP t1,
+ MutableIntegerModuloP t2, MutableIntegerModuloP t3,
+ MutableIntegerModuloP t4) {
+
+ t0.setValue(p.getX()).setProduct(p2.getX());
+ t1.setValue(p.getY()).setProduct(p2.getY());
+ t3.setValue(p2.getX()).setSum(p2.getY());
+ t4.setValue(p.getX()).setSum(p.getY());
+ p.getX().setReduced();
+ t3.setProduct(t4);
+ t4.setValue(t0).setSum(t1);
+
+ t3.setDifference(t4);
+ t4.setValue(p2.getY()).setProduct(p.getZ());
+ t4.setSum(p.getY());
+
+ p.getY().setValue(p2.getX()).setProduct(p.getZ());
+ p.getY().setSum(p.getX());
+ t2.setValue(p.getZ());
+ p.getZ().setProduct(b);
+
+ p.getX().setValue(p.getY()).setDifference(p.getZ());
+ p.getX().setReduced();
+ p.getZ().setValue(p.getX()).setProduct(two);
+ p.getX().setSum(p.getZ());
+
+ p.getZ().setValue(t1).setDifference(p.getX());
+ p.getX().setSum(t1);
+ p.getY().setProduct(b);
+
+ t1.setValue(t2).setProduct(two);
+ t2.setSum(t1);
+ t2.setReduced();
+ p.getY().setDifference(t2);
+
+ p.getY().setDifference(t0);
+ p.getY().setReduced();
+ t1.setValue(p.getY()).setProduct(two);
+ p.getY().setSum(t1);
+
+ t1.setValue(t0).setProduct(two);
+ t0.setSum(t1);
+ t0.setDifference(t2);
+
+ t1.setValue(t4).setProduct(p.getY());
+ t2.setValue(t0).setProduct(p.getY());
+ p.getY().setValue(p.getX()).setProduct(p.getZ());
+
+ p.getY().setSum(t2);
+ p.getX().setProduct(t3);
+ p.getX().setDifference(t1);
+
+ p.getZ().setProduct(t4);
+ t1.setValue(t3).setProduct(t0);
+ p.getZ().setSum(t1);
+
+ }
+
+ /*
+ * Projective point addition
+ */
+ private void setSum(ProjectivePoint.Mutable p, ProjectivePoint.Mutable p2,
+ MutableIntegerModuloP t0, MutableIntegerModuloP t1,
+ MutableIntegerModuloP t2, MutableIntegerModuloP t3,
+ MutableIntegerModuloP t4) {
+
+ t0.setValue(p.getX()).setProduct(p2.getX());
+ t1.setValue(p.getY()).setProduct(p2.getY());
+ t2.setValue(p.getZ()).setProduct(p2.getZ());
+
+ t3.setValue(p.getX()).setSum(p.getY());
+ t4.setValue(p2.getX()).setSum(p2.getY());
+ t3.setProduct(t4);
+
+ t4.setValue(t0).setSum(t1);
+ t3.setDifference(t4);
+ t4.setValue(p.getY()).setSum(p.getZ());
+
+ p.getY().setValue(p2.getY()).setSum(p2.getZ());
+ t4.setProduct(p.getY());
+ p.getY().setValue(t1).setSum(t2);
+
+ t4.setDifference(p.getY());
+ p.getX().setSum(p.getZ());
+ p.getY().setValue(p2.getX()).setSum(p2.getZ());
+
+ p.getX().setProduct(p.getY());
+ p.getY().setValue(t0).setSum(t2);
+ p.getY().setAdditiveInverse().setSum(p.getX());
+ p.getY().setReduced();
+
+ p.getZ().setValue(t2).setProduct(b);
+ p.getX().setValue(p.getY()).setDifference(p.getZ());
+ p.getZ().setValue(p.getX()).setProduct(two);
+
+ p.getX().setSum(p.getZ());
+ p.getX().setReduced();
+ p.getZ().setValue(t1).setDifference(p.getX());
+ p.getX().setSum(t1);
+
+ p.getY().setProduct(b);
+ t1.setValue(t2).setSum(t2);
+ t2.setSum(t1);
+ t2.setReduced();
+
+ p.getY().setDifference(t2);
+ p.getY().setDifference(t0);
+ p.getY().setReduced();
+ t1.setValue(p.getY()).setSum(p.getY());
+
+ p.getY().setSum(t1);
+ t1.setValue(t0).setProduct(two);
+ t0.setSum(t1);
+
+ t0.setDifference(t2);
+ t1.setValue(t4).setProduct(p.getY());
+ t2.setValue(t0).setProduct(p.getY());
+
+ p.getY().setValue(p.getX()).setProduct(p.getZ());
+ p.getY().setSum(t2);
+ p.getX().setProduct(t3);
+
+ p.getX().setDifference(t1);
+ p.getZ().setProduct(t4);
+ t1.setValue(t3).setProduct(t0);
+
+ p.getZ().setSum(t1);
+
+ }
+}
+
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java Tue Dec 11 10:29:08 2018 -0500
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java Tue Dec 11 09:42:45 2018 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,11 +32,7 @@
import java.security.interfaces.*;
import java.security.spec.*;
-import sun.security.util.DerInputStream;
-import sun.security.util.DerOutputStream;
-import sun.security.util.DerValue;
-import sun.security.util.ECParameters;
-import sun.security.util.ECUtil;
+import sun.security.util.*;
import sun.security.x509.AlgorithmId;
import sun.security.pkcs.PKCS8Key;
@@ -68,6 +64,7 @@
private static final long serialVersionUID = 88695385615075129L;
private BigInteger s; // private value
+ private byte[] arrayS; // private value as a little-endian array
private ECParameterSpec params;
/**
@@ -85,13 +82,25 @@
throws InvalidKeyException {
this.s = s;
this.params = params;
- // generate the encoding
+ makeEncoding(s);
+
+ }
+
+ ECPrivateKeyImpl(byte[] s, ECParameterSpec params)
+ throws InvalidKeyException {
+ this.arrayS = s.clone();
+ this.params = params;
+ makeEncoding(s);
+ }
+
+ private void makeEncoding(byte[] s) throws InvalidKeyException {
algid = new AlgorithmId
- (AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
+ (AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
try {
DerOutputStream out = new DerOutputStream();
out.putInteger(1); // version 1
- byte[] privBytes = ECUtil.trimZeroes(s.toByteArray());
+ byte[] privBytes = s.clone();
+ ArrayUtil.reverse(privBytes);
out.putOctetString(privBytes);
DerValue val =
new DerValue(DerValue.tag_Sequence, out.toByteArray());
@@ -102,6 +111,31 @@
}
}
+ private void makeEncoding(BigInteger s) throws InvalidKeyException {
+ algid = new AlgorithmId
+ (AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
+ try {
+ byte[] sArr = s.toByteArray();
+ // convert to fixed-length array
+ int numOctets = (params.getOrder().bitLength() + 7) / 8;
+ byte[] sOctets = new byte[numOctets];
+ int inPos = Math.max(sArr.length - sOctets.length, 0);
+ int outPos = Math.max(sOctets.length - sArr.length, 0);
+ int length = Math.min(sArr.length, sOctets.length);
+ System.arraycopy(sArr, inPos, sOctets, outPos, length);
+
+ DerOutputStream out = new DerOutputStream();
+ out.putInteger(1); // version 1
+ out.putOctetString(sOctets);
+ DerValue val =
+ new DerValue(DerValue.tag_Sequence, out.toByteArray());
+ key = val.toByteArray();
+ } catch (IOException exc) {
+ // should never occur
+ throw new InvalidKeyException(exc);
+ }
+ }
+
// see JCA doc
public String getAlgorithm() {
return "EC";
@@ -109,9 +143,26 @@
// see JCA doc
public BigInteger getS() {
+ if (s == null) {
+ byte[] arrCopy = arrayS.clone();
+ ArrayUtil.reverse(arrCopy);
+ s = new BigInteger(1, arrCopy);
+ }
return s;
}
+ public byte[] getArrayS() {
+ if (arrayS == null) {
+ byte[] arr = getS().toByteArray();
+ ArrayUtil.reverse(arr);
+ int byteLength = (params.getOrder().bitLength() + 7) / 8;
+ arrayS = new byte[byteLength];
+ int length = Math.min(byteLength, arr.length);
+ System.arraycopy(arr, 0, arrayS, 0, length);
+ }
+ return arrayS.clone();
+ }
+
// see JCA doc
public ECParameterSpec getParams() {
return params;
@@ -133,12 +184,13 @@
throw new IOException("Version must be 1");
}
byte[] privData = data.getOctetString();
- s = new BigInteger(1, privData);
+ ArrayUtil.reverse(privData);
+ arrayS = privData;
while (data.available() != 0) {
DerValue value = data.getDerValue();
- if (value.isContextSpecific((byte)0)) {
+ if (value.isContextSpecific((byte) 0)) {
// ignore for now
- } else if (value.isContextSpecific((byte)1)) {
+ } else if (value.isContextSpecific((byte) 1)) {
// ignore for now
} else {
throw new InvalidKeyException("Unexpected value: " + value);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/AffinePoint.java Tue Dec 11 09:42:45 2018 -0500
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.security.ec.point;
+
+import sun.security.util.math.ImmutableIntegerModuloP;
+
+import java.util.Objects;
+
+/**
+ * Elliptic curve point represented using affine coordinates (x, y). This class
+ * is not part of the sun.security.ec.point.Point hierarchy because it is not
+ * used to hold intermediate values during point arithmetic, and so it does not
+ * have a mutable form.
+ */
+public class AffinePoint {
+
+ private final ImmutableIntegerModuloP x;
+ private final ImmutableIntegerModuloP y;
+
+ public AffinePoint(ImmutableIntegerModuloP x, ImmutableIntegerModuloP y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public ImmutableIntegerModuloP getX() {
+ return x;
+ }
+
+ public ImmutableIntegerModuloP getY() {
+ return y;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof AffinePoint)) {
+ return false;
+ }
+ AffinePoint p = (AffinePoint) obj;
+ boolean xEquals = x.asBigInteger().equals(p.x.asBigInteger());
+ boolean yEquals = y.asBigInteger().equals(p.y.asBigInteger());
+ return xEquals && yEquals;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(x, y);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + x.asBigInteger().toString() + "," +
+ y.asBigInteger().toString() + ")";
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/ImmutablePoint.java Tue Dec 11 09:42:45 2018 -0500
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ec.point;
+
+/**
+ * An interface for immutable points on an elliptic curve over a finite field.
+ */
+public interface ImmutablePoint extends Point {
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/MutablePoint.java Tue Dec 11 09:42:45 2018 -0500
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ec.point;
+
+/**
+ * An interface for mutable points on an elliptic curve over a finite field.
+ */
+public interface MutablePoint extends Point {
+
+ MutablePoint setValue(AffinePoint p);
+ MutablePoint setValue(Point p);
+ MutablePoint conditionalSet(Point p, int set);
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/Point.java Tue Dec 11 09:42:45 2018 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ec.point;
+
+import sun.security.util.math.IntegerFieldModuloP;
+
+/**
+ * A base interface for points on an elliptic curve over a finite field.
+ * Implementations may use different representations for points, and this
+ * interface creates a common API for manipulating points. This API has no
+ * methods for point arithmetic, which depends on group structure and curve
+ * parameters in addition to point representation.
+ */
+public interface Point {
+
+ IntegerFieldModuloP getField();
+ AffinePoint asAffine();
+
+ ImmutablePoint fixed();
+ MutablePoint mutable();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/ProjectivePoint.java Tue Dec 11 09:42:45 2018 -0500
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.security.ec.point;
+
+import sun.security.util.math.*;
+
+/**
+ * Elliptic curve point in projective coordinates (X, Y, Z) where
+ * an affine point (x, y) is represented using any (X, Y, Z) s.t.
+ * x = X/Z and y = Y/Z.
+ */
+public abstract class ProjectivePoint
+ <T extends IntegerModuloP> implements Point {
+
+ protected final T x;
+ protected final T y;
+ protected final T z;
+
+ protected ProjectivePoint(T x, T y, T z) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ @Override
+ public IntegerFieldModuloP getField() {
+ return this.x.getField();
+ }
+
+ @Override
+ public Immutable fixed() {
+ return new Immutable(x.fixed(), y.fixed(), z.fixed());
+ }
+
+ @Override
+ public Mutable mutable() {
+ return new Mutable(x.mutable(), y.mutable(), z.mutable());
+ }
+
+ public T getX() {
+ return x;
+ }
+
+ public T getY() {
+ return y;
+ }
+
+ public T getZ() {
+ return z;
+ }
+
+ public AffinePoint asAffine() {
+ IntegerModuloP zInv = z.multiplicativeInverse();
+ return new AffinePoint(x.multiply(zInv), y.multiply(zInv));
+ }
+
+ public static class Immutable
+ extends ProjectivePoint<ImmutableIntegerModuloP>
+ implements ImmutablePoint {
+
+ public Immutable(ImmutableIntegerModuloP x,
+ ImmutableIntegerModuloP y,
+ ImmutableIntegerModuloP z) {
+ super(x, y, z);
+ }
+ }
+
+ public static class Mutable
+ extends ProjectivePoint<MutableIntegerModuloP>
+ implements MutablePoint {
+
+ public Mutable(MutableIntegerModuloP x,
+ MutableIntegerModuloP y,
+ MutableIntegerModuloP z) {
+ super(x, y, z);
+ }
+
+ public Mutable(IntegerFieldModuloP field) {
+ super(field.get0().mutable(),
+ field.get0().mutable(),
+ field.get0().mutable());
+ }
+
+ @Override
+ public Mutable conditionalSet(Point p, int set) {
+ if (!(p instanceof ProjectivePoint)) {
+ throw new RuntimeException("Incompatible point");
+ }
+ @SuppressWarnings("unchecked")
+ ProjectivePoint<IntegerModuloP> pp =
+ (ProjectivePoint<IntegerModuloP>) p;
+ return conditionalSet(pp, set);
+ }
+
+ private <T extends IntegerModuloP>
+ Mutable conditionalSet(ProjectivePoint<T> pp, int set) {
+
+ x.conditionalSet(pp.x, set);
+ y.conditionalSet(pp.y, set);
+ z.conditionalSet(pp.z, set);
+
+ return this;
+ }
+
+ @Override
+ public Mutable setValue(AffinePoint p) {
+ x.setValue(p.getX());
+ y.setValue(p.getY());
+ z.setValue(p.getX().getField().get1());
+
+ return this;
+ }
+
+ @Override
+ public Mutable setValue(Point p) {
+ if (!(p instanceof ProjectivePoint)) {
+ throw new RuntimeException("Incompatible point");
+ }
+ @SuppressWarnings("unchecked")
+ ProjectivePoint<IntegerModuloP> pp =
+ (ProjectivePoint<IntegerModuloP>) p;
+ return setValue(pp);
+ }
+
+ private <T extends IntegerModuloP>
+ Mutable setValue(ProjectivePoint<T> pp) {
+
+ x.setValue(pp.x);
+ y.setValue(pp.y);
+ z.setValue(pp.z);
+
+ return this;
+ }
+
+ }
+
+}