< prev index next >

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

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

@@ -30,13 +30,15 @@
 import java.math.BigInteger;
 
 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
  * following algorithm names:
  *

@@ -145,11 +147,11 @@
         }
 
         // 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;
             }
             System.arraycopy(b, off, precomputedDigest, offset, len);

@@ -170,11 +172,11 @@
             byteBuffer.get(precomputedDigest, offset, len);
             offset += len;
         }
 
         @Override
-        protected void resetDigest(){
+        protected void resetDigest() {
             offset = 0;
         }
 
         // Returns the precomputed message digest value.
         @Override

@@ -220,18 +222,18 @@
     }
 
     // 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);
         }
     }
 
     // Nested class for SHA256withECDSA signatures
     public static final class SHA256 extends ECDSASignature {

@@ -276,11 +278,11 @@
     }
 
     // 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
         // algorithm (e.g. P-256 for SHA256withECDSA)
         this.privateKey = null;

@@ -288,18 +290,18 @@
     }
 
     // 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
         // algorithm (e.g. P-256 for SHA256withECDSA)
         this.publicKey = null;

@@ -335,11 +337,11 @@
     }
 
     // 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;
     }
 
     // update the signature with the plaintext data. See JCA doc

@@ -352,39 +354,104 @@
 
         messageDigest.update(byteBuffer);
         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
         int timingArgument = random.nextInt();
         // 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 {
             return encodeSignature(sig);
         }

@@ -398,11 +465,11 @@
         ECParameterSpec params = publicKey.getParams();
         // DER OID
         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());
         }
 
         byte[] sig;

@@ -421,27 +488,27 @@
 
     // set parameter, not supported. See JCA doc
     @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");
         }
     }
 
     // get parameter, not supported. See JCA doc
     @Override
     @Deprecated
     protected Object engineGetParameter(String param)
-            throws InvalidParameterException {
+    throws InvalidParameterException {
         throw new UnsupportedOperationException("getParameter() not supported");
     }
 
     @Override
     protected AlgorithmParameters engineGetParameters() {

@@ -462,11 +529,11 @@
 
             DerOutputStream out = new DerOutputStream(signature.length + 10);
             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();
 
         } catch (Exception e) {
             throw new SignatureException("Could not encode signature", e);

@@ -495,13 +562,13 @@
             byte[] sBytes = trimZeroes(s.toByteArray());
             int k = Math.max(rBytes.length, sBytes.length);
             // 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) {
             throw new SignatureException("Invalid encoding for signature", e);
         }

@@ -537,23 +604,23 @@
      *     these bits is used by the countermeasures.
      *
      * @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).
      * @param encodedParams the curve's DER encoded object identifier.
      *
      * @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;
 }
< prev index next >