--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/ec/ECDSASignature.java Tue Aug 11 16:52:26 2009 +0100
@@ -0,0 +1,447 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.security.ec;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.math.BigInteger;
+import java.util.Arrays;
+
+import java.security.*;
+import java.security.interfaces.*;
+import java.security.spec.*;
+
+import sun.security.jca.JCAUtil;
+import sun.security.util.*;
+import sun.security.x509.AlgorithmId;
+
+/**
+ * ECDSA signature implementation. This class currently supports the
+ * following algorithm names:
+ *
+ * . "NONEwithECDSA"
+ * . "SHA1withECDSA"
+ * . "SHA256withECDSA"
+ * . "SHA384withECDSA"
+ * . "SHA512withECDSA"
+ *
+ * @since 1.7
+ */
+abstract class ECDSASignature extends SignatureSpi {
+
+ // flag indicating whether the native ECC implementation is present
+ private static boolean implementationPresent = true;
+ static {
+ try {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("sunecc");
+ return null;
+ }
+ });
+ } catch (UnsatisfiedLinkError e) {
+ implementationPresent = false;
+ }
+ }
+
+ // message digest implementation we use
+ private final MessageDigest messageDigest;
+
+ // supplied entropy
+ private SecureRandom random;
+
+ // flag indicating whether the digest has been reset
+ private boolean needsReset;
+
+ // private key, if initialized for signing
+ private ECPrivateKey privateKey;
+
+ // public key, if initialized for verifying
+ private ECPublicKey publicKey;
+
+ /**
+ * Constructs a new ECDSASignature. Used by Raw subclass.
+ *
+ * @exception ProviderException if the native ECC library is unavailable.
+ */
+ ECDSASignature() {
+ if (!implementationPresent) {
+ throw new
+ ProviderException("ECDSA implementation is not available");
+ }
+ messageDigest = null;
+ }
+
+ /**
+ * Constructs a new ECDSASignature. Used by subclasses.
+ *
+ * @exception ProviderException if the native ECC library is unavailable.
+ */
+ ECDSASignature(String digestName) {
+ if (!implementationPresent) {
+ throw new
+ ProviderException("ECDSA implementation is not available");
+ }
+
+ try {
+ messageDigest = MessageDigest.getInstance(digestName);
+ } catch (NoSuchAlgorithmException e) {
+ throw new ProviderException(e);
+ }
+ needsReset = false;
+ }
+
+ // Nested class for NONEwithECDSA signatures
+ public static final class Raw extends ECDSASignature {
+
+ // the longest supported digest is 512 bits (SHA-512)
+ private static final int RAW_ECDSA_MAX = 64;
+
+ private final byte[] precomputedDigest;
+ private int offset = 0;
+
+ public Raw() {
+ precomputedDigest = new byte[RAW_ECDSA_MAX];
+ }
+
+ // Stores the precomputed message digest value.
+ @Override
+ protected void engineUpdate(byte b) throws SignatureException {
+ if (offset >= precomputedDigest.length) {
+ offset = RAW_ECDSA_MAX + 1;
+ return;
+ }
+ precomputedDigest[offset++] = b;
+ }
+
+ // Stores the precomputed message digest value.
+ @Override
+ protected void engineUpdate(byte[] b, int off, int len)
+ throws SignatureException {
+ if (offset >= precomputedDigest.length) {
+ offset = RAW_ECDSA_MAX + 1;
+ return;
+ }
+ System.arraycopy(b, off, precomputedDigest, offset, len);
+ offset += len;
+ }
+
+ // Stores the precomputed message digest value.
+ @Override
+ protected void engineUpdate(ByteBuffer byteBuffer) {
+ int len = byteBuffer.remaining();
+ if (len <= 0) {
+ return;
+ }
+ if (offset + len >= precomputedDigest.length) {
+ offset = RAW_ECDSA_MAX + 1;
+ return;
+ }
+ byteBuffer.get(precomputedDigest, offset, len);
+ offset += len;
+ }
+
+ @Override
+ protected void resetDigest(){
+ offset = 0;
+ }
+
+ // Returns the precomputed message digest value.
+ @Override
+ protected byte[] getDigestValue() throws SignatureException {
+ if (offset > RAW_ECDSA_MAX) {
+ throw new SignatureException("Message digest is too long");
+
+ }
+ byte[] result = new byte[offset];
+ System.arraycopy(precomputedDigest, 0, result, 0, offset);
+ offset = 0;
+
+ return result;
+ }
+ }
+
+ // Nested class for SHA1withECDSA signatures
+ public static final class SHA1 extends ECDSASignature {
+ public SHA1() {
+ super("SHA1");
+ }
+ }
+
+ // Nested class for SHA256withECDSA signatures
+ public static final class SHA256 extends ECDSASignature {
+ public SHA256() {
+ super("SHA-256");
+ }
+ }
+
+ // Nested class for SHA384withECDSA signatures
+ public static final class SHA384 extends ECDSASignature {
+ public SHA384() {
+ super("SHA-384");
+ }
+ }
+
+ // Nested class for SHA512withECDSA signatures
+ public static final class SHA512 extends ECDSASignature {
+ public SHA512() {
+ super("SHA-512");
+ }
+ }
+
+ // initialize for verification. See JCA doc
+ @Override
+ protected void engineInitVerify(PublicKey publicKey)
+ 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;
+ resetDigest();
+ }
+
+ // initialize for signing. See JCA doc
+ @Override
+ protected void engineInitSign(PrivateKey privateKey)
+ throws InvalidKeyException {
+ engineInitSign(privateKey, null);
+ }
+
+ // initialize for signing. See JCA doc
+ @Override
+ protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
+ 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;
+ this.random = random;
+ resetDigest();
+ }
+
+ /**
+ * Resets the message digest if needed.
+ */
+ protected void resetDigest() {
+ if (needsReset) {
+ if (messageDigest != null) {
+ messageDigest.reset();
+ }
+ needsReset = false;
+ }
+ }
+
+ /**
+ * Returns the message digest value.
+ */
+ protected byte[] getDigestValue() throws SignatureException {
+ needsReset = false;
+ return messageDigest.digest();
+ }
+
+ // update the signature with the plaintext data. See JCA doc
+ @Override
+ protected void engineUpdate(byte b) throws SignatureException {
+ messageDigest.update(b);
+ needsReset = true;
+ }
+
+ // update the signature with the plaintext data. See JCA doc
+ @Override
+ protected void engineUpdate(byte[] b, int off, int len)
+ throws SignatureException {
+ messageDigest.update(b, off, len);
+ needsReset = true;
+ }
+
+ // update the signature with the plaintext data. See JCA doc
+ @Override
+ protected void engineUpdate(ByteBuffer byteBuffer) {
+ int len = byteBuffer.remaining();
+ if (len <= 0) {
+ return;
+ }
+
+ messageDigest.update(byteBuffer);
+ needsReset = true;
+ }
+
+ // sign the data and return the signature. See JCA doc
+ @Override
+ protected byte[] engineSign() throws SignatureException {
+ byte[] s = privateKey.getS().toByteArray();
+ ECParameterSpec params = privateKey.getParams();
+ byte[] encodedParams = ECParameters.encodeParameters(params); // DER OID
+ int keySize = params.getCurve().getField().getFieldSize();
+
+ // seed is twice the key size (in bytes)
+ byte[] seed = new byte[((keySize + 7) >> 3) * 2];
+ if (random == null) {
+ random = JCAUtil.getSecureRandom();
+ }
+ random.nextBytes(seed);
+
+ try {
+
+ return encodeSignature(
+ signDigest(getDigestValue(), s, encodedParams, seed));
+
+ } catch (GeneralSecurityException e) {
+ throw new SignatureException("Could not sign data", e);
+ }
+ }
+
+ // verify the data and return the result. See JCA doc
+ @Override
+ protected boolean engineVerify(byte[] signature) throws SignatureException {
+
+ byte[] w;
+ ECParameterSpec params = publicKey.getParams();
+ byte[] encodedParams = ECParameters.encodeParameters(params); // DER OID
+
+ if (publicKey instanceof ECPublicKeyImpl) {
+ w = ((ECPublicKeyImpl)publicKey).getEncodedPublicValue();
+ } else { // instanceof ECPublicKey
+ w = ECParameters.encodePoint(publicKey.getW(), params.getCurve());
+ }
+
+ try {
+
+ return verifySignedDigest(
+ decodeSignature(signature), getDigestValue(), w, encodedParams);
+
+ } catch (GeneralSecurityException e) {
+ throw new SignatureException("Could not verify signature", e);
+ }
+ }
+
+ // set parameter, not supported. See JCA doc
+ @Override
+ protected void engineSetParameter(String param, Object value)
+ throws InvalidParameterException {
+ throw new UnsupportedOperationException("setParameter() not supported");
+ }
+
+ // get parameter, not supported. See JCA doc
+ @Override
+ protected Object engineGetParameter(String param)
+ throws InvalidParameterException {
+ throw new UnsupportedOperationException("getParameter() not supported");
+ }
+
+ // Convert the concatenation of R and S into their DER encoding
+ private byte[] encodeSignature(byte[] signature) throws SignatureException {
+ try {
+
+ int n = signature.length >> 1;
+ byte[] bytes = new byte[n];
+ System.arraycopy(signature, 0, bytes, 0, n);
+ BigInteger r = new BigInteger(1, bytes);
+ System.arraycopy(signature, n, bytes, 0, n);
+ BigInteger s = new BigInteger(1, bytes);
+
+ DerOutputStream out = new DerOutputStream(signature.length + 10);
+ out.putInteger(r);
+ out.putInteger(s);
+ DerValue result =
+ new DerValue(DerValue.tag_Sequence, out.toByteArray());
+
+ return result.toByteArray();
+
+ } catch (Exception e) {
+ throw new SignatureException("Could not encode signature", e);
+ }
+ }
+
+ // Convert the DER encoding of R and S into a concatenation of R and S
+ private byte[] decodeSignature(byte[] signature) throws SignatureException {
+
+ try {
+ DerInputStream in = new DerInputStream(signature);
+ DerValue[] values = in.getSequence(2);
+ BigInteger r = values[0].getPositiveBigInteger();
+ BigInteger s = values[1].getPositiveBigInteger();
+ // trim leading zeroes
+ byte[] rBytes = trimZeroes(r.toByteArray());
+ 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);
+ System.arraycopy(sBytes, 0, result, result.length - sBytes.length,
+ sBytes.length);
+ return result;
+
+ } catch (Exception e) {
+ throw new SignatureException("Could not decode signature", e);
+ }
+ }
+
+ // trim leading (most significant) zeroes from the result
+ private static byte[] trimZeroes(byte[] b) {
+ int i = 0;
+ while ((i < b.length - 1) && (b[i] == 0)) {
+ i++;
+ }
+ if (i == 0) {
+ return b;
+ }
+ byte[] t = new byte[b.length - i];
+ System.arraycopy(b, i, t, 0, t.length);
+ return t;
+ }
+
+ /**
+ * Signs the digest using the private key.
+ *
+ * @param digest the digest to be signed.
+ * @param s the private key's S value.
+ * @param encodedParams the curve's DER encoded object identifier.
+ * @param seed the random seed.
+ *
+ * @return byte[] the signature.
+ */
+ private static native byte[] signDigest(byte[] digest, byte[] s,
+ byte[] encodedParams, byte[] seed) throws GeneralSecurityException;
+
+ /**
+ * Verifies the signed digest using the public key.
+ *
+ * @param signedDigest 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;
+}