< 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 >