< prev index next >

src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java

Print this page
rev 51879 : 8208698: Improved ECC Implementation
Summary: New implementation of ECDH and ECDSA forsome prime-order curves
Reviewed-by: ascarpino

@@ -1,7 +1,7 @@
 /*
- * 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
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -31,17 +31,19 @@
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECGenParameterSpec;
 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.
  * Standard algorithm, minimum key length is 112 bits, maximum is 571 bits.
  *

@@ -88,18 +90,18 @@
             throws InvalidAlgorithmParameterException {
 
         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(
                     "Unknown curve name: " + name);
             }

@@ -110,12 +112,11 @@
 
         // Not all known curves are supported by the native implementation
         ensureCurveIsSupported(ecSpec);
         this.params = ecSpec;
 
-        this.keySize =
-            ((ECParameterSpec)this.params).getCurve().getField().getFieldSize();
+        this.keySize = ecSpec.getCurve().getField().getFieldSize();
         this.random = random;
     }
 
     private static void ensureCurveIsSupported(ECParameterSpec ecSpec)
         throws InvalidAlgorithmParameterException {

@@ -139,43 +140,101 @@
 
     // generate the keypair. See JCA doc
     @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 {
+            Optional<KeyPair> kp = generateKeyPairImpl(random);
+            if (kp.isPresent()) {
+                return kp.get();
+            }
+            return generateKeyPairNative(random);
+        } catch (Exception ex) {
+            throw new ProviderException(ex);
+        }
+    }
+
+    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
+            }
+        }
 
-            Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed);
+        throw new ProviderException("Unable to produce private key after "
+                                         + numAttempts + " attempts");
+    }
 
-            // 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, (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);
+    private Optional<KeyPair> generateKeyPairImpl(SecureRandom random)
+        throws InvalidKeyException {
 
-            return new KeyPair(publicKey, privateKey);
+        ECParameterSpec ecParams = (ECParameterSpec) params;
 
-        } catch (Exception e) {
-            throw new ProviderException(e);
+        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 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);
+
+        // 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 {
         if (keySize < KEY_SIZE_MIN) {
             throw new InvalidParameterException

@@ -188,11 +247,13 @@
         this.keySize = keySize;
     }
 
     /**
      * 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
      * @return true if and only if generateECKeyPair will succeed for
      *    the supplied parameters
< prev index next >