From 0720d11b8b976a73480c6ea03185019b021c39d5 Mon Sep 17 00:00:00 2001 From: Kostas Tsiounis Date: Thu, 7 Mar 2024 10:20:58 -0500 Subject: [PATCH] Add support for native ECDSA signature --- .../jdk/crypto/jniprovider/NativeCrypto.java | 13 + .../share/native/libjncrypto/NativeCrypto.c | 175 ++++- .../sun/security/ec/NativeECDSASignature.java | 647 ++++++++++++++++++ .../share/classes/sun/security/ec/SunEC.java | 194 ++++-- 4 files changed, 973 insertions(+), 56 deletions(-) create mode 100644 closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECDSASignature.java diff --git a/closed/src/java.base/share/classes/jdk/crypto/jniprovider/NativeCrypto.java b/closed/src/java.base/share/classes/jdk/crypto/jniprovider/NativeCrypto.java index a920446155..c8358b4b4d 100644 --- a/closed/src/java.base/share/classes/jdk/crypto/jniprovider/NativeCrypto.java +++ b/closed/src/java.base/share/classes/jdk/crypto/jniprovider/NativeCrypto.java @@ -416,6 +416,19 @@ public final native int PBEDerive(byte[] password, int id, int hashAlgorithm); + /* Native ECDSA interfaces. */ + public final native int ECDSASign(long key, + byte[] digest, + int digestLen, + byte[] signature, + int sigLen); + + public final native int ECDSAVerify(long key, + byte[] digest, + int digestLen, + byte[] signature, + int sigLen); + /* Native XDH (X25519, X448) interfaces. */ public final native int XDHCreateKeys(byte[] privateKey, int privateKeyLength, diff --git a/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c b/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c index 6e524ec223..4fc972d9fe 100644 --- a/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c +++ b/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c @@ -155,6 +155,14 @@ typedef int OSSL_EC_KEY_check_key_t(const EC_KEY *); typedef int EC_set_public_key_t(EC_KEY *, BIGNUM *, BIGNUM *, int); typedef const BIGNUM *OSSL_EC_KEY_get0_private_key_t(const EC_KEY *); +typedef ECDSA_SIG *OSSL_ECDSA_do_sign_t(const unsigned char *, int, EC_KEY *); +typedef int OSSL_ECDSA_do_verify_t(const unsigned char *, int, const ECDSA_SIG *, EC_KEY *); +typedef ECDSA_SIG *OSSL_ECDSA_SIG_new_t(void); +typedef void OSSL_ECDSA_SIG_free_t(ECDSA_SIG *); +typedef const BIGNUM *OSSL_ECDSA_SIG_get0_r_t(const ECDSA_SIG *); +typedef const BIGNUM *OSSL_ECDSA_SIG_get0_s_t(const ECDSA_SIG *); +typedef int OSSL_ECDSA_SIG_set0_t(ECDSA_SIG *, BIGNUM *, BIGNUM *); + typedef EVP_PKEY_CTX *OSSL_EVP_PKEY_CTX_new_t(EVP_PKEY *, ENGINE *); typedef EVP_PKEY_CTX *OSSL_EVP_PKEY_CTX_new_id_t(int, ENGINE *); typedef int OSSL_EVP_PKEY_keygen_init_t(EVP_PKEY_CTX *); @@ -282,6 +290,15 @@ OSSL_EC_KEY_check_key_t* OSSL_EC_KEY_check_key; EC_set_public_key_t* EC_set_public_key; OSSL_EC_KEY_get0_private_key_t *OSSL_EC_KEY_get0_private_key; +/* Define pointers for OpenSSL functions to handle ECDSA algorithm. */ +OSSL_ECDSA_do_sign_t *OSSL_ECDSA_do_sign; +OSSL_ECDSA_do_verify_t *OSSL_ECDSA_do_verify; +OSSL_ECDSA_SIG_new_t *OSSL_ECDSA_SIG_new; +OSSL_ECDSA_SIG_free_t *OSSL_ECDSA_SIG_free; +OSSL_ECDSA_SIG_get0_r_t *OSSL_ECDSA_SIG_get0_r; +OSSL_ECDSA_SIG_get0_s_t *OSSL_ECDSA_SIG_get0_s; +OSSL_ECDSA_SIG_set0_t *OSSL_ECDSA_SIG_set0; + /* Define pointers for OpenSSL functions to handle XDH algorithm. */ OSSL_EVP_PKEY_CTX_new_t *OSSL_EVP_PKEY_CTX_new; OSSL_EVP_PKEY_CTX_new_id_t *OSSL_EVP_PKEY_CTX_new_id; @@ -572,7 +589,7 @@ JNIEXPORT jlong JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_loadCrypto OSSL_ECGF2M = JNI_TRUE; } - /* Load the functions symbols for OpenSSL XDH algorithm. (Need OpenSSL 1.1.x or above). */ + /* Load the functions symbols for OpenSSL XDH and ECDSA algorithms. (Need OpenSSL 1.1.x or above). */ if (ossl_ver >= OPENSSL_VERSION_1_1_1) { OSSL_EVP_PKEY_CTX_new = (OSSL_EVP_PKEY_CTX_new_t *)find_crypto_symbol(crypto_library, "EVP_PKEY_CTX_new"); OSSL_EVP_PKEY_CTX_new_id = (OSSL_EVP_PKEY_CTX_new_id_t *)find_crypto_symbol(crypto_library, "EVP_PKEY_CTX_new_id"); @@ -587,6 +604,14 @@ JNIEXPORT jlong JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_loadCrypto OSSL_EVP_PKEY_derive_set_peer = (OSSL_EVP_PKEY_derive_set_peer_t *)find_crypto_symbol(crypto_library, "EVP_PKEY_derive_set_peer"); OSSL_EVP_PKEY_derive = (OSSL_EVP_PKEY_derive_t *)find_crypto_symbol(crypto_library, "EVP_PKEY_derive"); OSSL_EVP_PKEY_free = (OSSL_EVP_PKEY_free_t *)find_crypto_symbol(crypto_library, "EVP_PKEY_free"); + + OSSL_ECDSA_do_sign = (OSSL_ECDSA_do_sign_t *)find_crypto_symbol(crypto_library, "ECDSA_do_sign"); + OSSL_ECDSA_do_verify = (OSSL_ECDSA_do_verify_t *)find_crypto_symbol(crypto_library, "ECDSA_do_verify"); + OSSL_ECDSA_SIG_new = (OSSL_ECDSA_SIG_new_t *)find_crypto_symbol(crypto_library, "ECDSA_SIG_new"); + OSSL_ECDSA_SIG_free = (OSSL_ECDSA_SIG_free_t *)find_crypto_symbol(crypto_library, "ECDSA_SIG_free"); + OSSL_ECDSA_SIG_get0_r = (OSSL_ECDSA_SIG_get0_r_t *)find_crypto_symbol(crypto_library, "ECDSA_SIG_get0_r"); + OSSL_ECDSA_SIG_get0_s = (OSSL_ECDSA_SIG_get0_s_t *)find_crypto_symbol(crypto_library, "ECDSA_SIG_get0_s"); + OSSL_ECDSA_SIG_set0 = (OSSL_ECDSA_SIG_set0_t *)find_crypto_symbol(crypto_library, "ECDSA_SIG_set0"); } else { OSSL_EVP_PKEY_CTX_new = NULL; OSSL_EVP_PKEY_CTX_new_id = NULL; @@ -601,6 +626,14 @@ JNIEXPORT jlong JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_loadCrypto OSSL_EVP_PKEY_derive_set_peer = NULL; OSSL_EVP_PKEY_derive = NULL; OSSL_EVP_PKEY_free = NULL; + + OSSL_ECDSA_do_sign = NULL; + OSSL_ECDSA_do_verify = NULL; + OSSL_ECDSA_SIG_new = NULL; + OSSL_ECDSA_SIG_free = NULL; + OSSL_ECDSA_SIG_get0_r = NULL; + OSSL_ECDSA_SIG_get0_s = NULL; + OSSL_ECDSA_SIG_set0 = NULL; } /* Load the functions symbols for OpenSSL PBE algorithm. */ @@ -685,7 +718,14 @@ JNIEXPORT jlong JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_loadCrypto (NULL == OSSL_EVP_PKEY_derive_init) || (NULL == OSSL_EVP_PKEY_derive_set_peer) || (NULL == OSSL_EVP_PKEY_derive) || - (NULL == OSSL_EVP_PKEY_free))) || + (NULL == OSSL_EVP_PKEY_free) || + (NULL == OSSL_ECDSA_do_sign) || + (NULL == OSSL_ECDSA_do_verify) || + (NULL == OSSL_ECDSA_SIG_new) || + (NULL == OSSL_ECDSA_SIG_free) || + (NULL == OSSL_ECDSA_SIG_get0_r) || + (NULL == OSSL_ECDSA_SIG_get0_s) || + (NULL == OSSL_ECDSA_SIG_set0))) || /* Check symbols that are only available in OpenSSL 1.1.x and above */ ((ossl_ver >= OPENSSL_VERSION_1_1_0) && ((NULL == OSSL_chacha20) || (NULL == OSSL_chacha20_poly1305))) || /* Check symbols that are only available in OpenSSL 1.0.x and above */ @@ -3093,6 +3133,137 @@ Java_jdk_crypto_jniprovider_NativeCrypto_PBEDerive return ret; } +/* Create an ECDSA Signature + * + * Class: jdk_crypto_jniprovider_NativeCrypto + * Method: ECDSASign + * Signature: (J[BI[B)I + */ +JNIEXPORT jint JNICALL +Java_jdk_crypto_jniprovider_NativeCrypto_ECDSASign + (JNIEnv *env, jclass obj, jlong key, jbyteArray digest, jint digestLen, jbyteArray sig, jint sigLen) +{ + jint ret = -1; + + unsigned char *nativeDigest = NULL; + unsigned char *nativeSig = NULL; + EC_KEY *privateKey = (EC_KEY *)(intptr_t)key; + ECDSA_SIG *signature = NULL; + const BIGNUM *rBN = NULL; + const BIGNUM *sBN = NULL; + + nativeDigest = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, digest, 0)); + if (NULL == nativeDigest) { + goto cleanup; + } + + signature = (*OSSL_ECDSA_do_sign)(nativeDigest, digestLen, privateKey); + if (NULL == signature) { + printf("Failed to create an ECDSA Signature.\n"); + goto cleanup; + } + + rBN = (*OSSL_ECDSA_SIG_get0_r)(signature); + sBN = (*OSSL_ECDSA_SIG_get0_s)(signature); + + nativeSig = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, sig, 0)); + if (NULL == nativeSig) { + goto cleanup; + } + + ret = getArrayFromBN(rBN, nativeSig, sigLen / 2); + if (-1 == ret) { + goto cleanup; + } + + ret = getArrayFromBN(sBN, &nativeSig[sigLen / 2], sigLen / 2); + if (-1 == ret) { + goto cleanup; + } + + ret = sigLen; + +cleanup: + if (NULL != nativeSig) { + (*env)->ReleasePrimitiveArrayCritical(env, sig, nativeSig, 0); + } + + if (NULL != signature) { + (*OSSL_ECDSA_SIG_free)(signature); + } + + if (NULL != nativeDigest) { + (*env)->ReleasePrimitiveArrayCritical(env, digest, nativeDigest, JNI_ABORT); + } + + return ret; +} + +/* Verify an ECDSA Signature + * + * Class: jdk_crypto_jniprovider_NativeCrypto + * Method: ECDSAVerify + * Signature: (J[BI[B)I + */ +JNIEXPORT jint JNICALL +Java_jdk_crypto_jniprovider_NativeCrypto_ECDSAVerify + (JNIEnv *env, jclass obj, jlong key, jbyteArray digest, jint digestLen, jbyteArray sig, jint sigLen) +{ + jint ret = -1; + + unsigned char *nativeDigest = NULL; + unsigned char *nativeSig = NULL; + EC_KEY *publicKey = (EC_KEY *)(intptr_t)key; + ECDSA_SIG *signature = NULL; + BIGNUM *rBN = NULL; + BIGNUM *sBN = NULL; + + nativeSig = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, sig, 0)); + if (NULL == nativeSig) { + goto cleanup; + } + + rBN = (*OSSL_BN_bin2bn)(nativeSig, sigLen / 2, NULL); + sBN = (*OSSL_BN_bin2bn)(&nativeSig[sigLen / 2], sigLen / 2, NULL); + signature = (*OSSL_ECDSA_SIG_new)(); + if (0 == (*OSSL_ECDSA_SIG_set0)(signature, rBN, sBN)) { + goto cleanup; + } + + nativeDigest = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, digest, 0)); + if (NULL == nativeDigest) { + goto cleanup; + } + + ret = (*OSSL_ECDSA_do_verify)(nativeDigest, digestLen, signature, publicKey); + +cleanup: + if (NULL != nativeDigest) { + (*env)->ReleasePrimitiveArrayCritical(env, digest, nativeDigest, JNI_ABORT); + } + + if (NULL != signature) { + // The BIGNUM structs will be freed by the signature. + sBN = NULL; + rBN = NULL; + (*OSSL_ECDSA_SIG_free)(signature); + } + + // In case the BIGNUM structs weren't freed by the signature. + if (NULL != sBN) { + (*OSSL_BN_free)(sBN); + } + if (NULL != rBN) { + (*OSSL_BN_free)(rBN); + } + + if (NULL != nativeSig) { + (*env)->ReleasePrimitiveArrayCritical(env, sig, nativeSig, JNI_ABORT); + } + + return ret; +} + /* Create a pair of private and public keys for XDH Key Agreement. * * Class: jdk_crypto_jniprovider_NativeCrypto diff --git a/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECDSASignature.java b/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECDSASignature.java new file mode 100644 index 0000000000..4d12d16b85 --- /dev/null +++ b/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECDSASignature.java @@ -0,0 +1,647 @@ +/* + * Copyright (c) 2009, 2020, 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. + */ + +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2024, 2024 All Rights Reserved + * =========================================================================== + */ + +package sun.security.ec; + +import java.nio.ByteBuffer; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.security.SignatureSpi; +import java.security.interfaces.ECKey; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.util.Optional; + +import jdk.crypto.jniprovider.NativeCrypto; +import sun.security.provider.Sun; +import sun.security.util.ECUtil; + +/** + * ECDSA signature implementation. This class currently supports the + * following algorithm names: + * + * + * + * @since 1.7 + */ +abstract class NativeECDSASignature extends SignatureSpi { + + private static NativeCrypto nativeCrypto; + private static final boolean nativeCryptTrace = NativeCrypto.isTraceEnabled(); + + // 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; + + // private key impl, if initialized for signing + private ECPrivateKeyImpl privateKeyImpl; + + // public key, if initialized for verifying + private ECPublicKey publicKey; + + // public key impl, if initialized for verifying + private ECPublicKeyImpl publicKeyImpl; + + // signature parameters + private ECParameterSpec sigParams; + + // the format (i.e., true for the IEEE P1363 format and false for ASN.1) + private final boolean p1363Format; + + // the Java implementation, if needed + private ECDSASignature javaImplementation; + + /** + * Constructs a new NativeECDSASignature. + * + * @exception ProviderException if the native ECC library is unavailable. + */ + NativeECDSASignature() { + this(false); + } + + /** + * Constructs a new NativeECDSASignature that will use the specified + * signature format. {@code p1363Format} should be {@code true} to + * use the IEEE P1363 format. If {@code p1363Format} is {@code false}, + * the DER-encoded ASN.1 format will be used. This constructor is + * used by the RawECDSA subclasses. + */ + NativeECDSASignature(boolean p1363Format) { + this.messageDigest = null; + this.p1363Format = p1363Format; + } + + /** + * Constructs a new NativeECDSASignature. Used by subclasses. + */ + NativeECDSASignature(String digestName) { + this(digestName, false); + } + + /** + * Constructs a new NativeECDSASignature that will use the specified + * digest and signature format. {@code p1363Format} should be + * {@code true} to use the IEEE P1363 format. If {@code p1363Format} + * is {@code false}, the DER-encoded ASN.1 format will be used. This + * constructor is used by subclasses. + */ + NativeECDSASignature(String digestName, boolean p1363Format) { + try { + this.messageDigest = MessageDigest.getInstance(digestName); + } catch (NoSuchAlgorithmException e) { + throw new ProviderException(e); + } + this.needsReset = false; + this.p1363Format = p1363Format; + } + + // Class for Raw ECDSA signatures. + static class RawECDSA extends NativeECDSASignature { + + // the longest supported digest is 512 bits (SHA-512) + private static final int RAW_ECDSA_MAX = 64; + + private final byte[] precomputedDigest; + private int offset; + + RawECDSA(boolean p1363Format) { + super(p1363Format); + 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 (len >= (precomputedDigest.length - offset)) { + 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 NONEwithECDSA signatures. + public static final class Raw extends RawECDSA { + public Raw() { + super(false); + } + } + + // Nested class for NONEwithECDSAinP1363Format signatures. + public static final class RawinP1363Format extends RawECDSA { + public RawinP1363Format() { + super(true); + } + } + + // Nested class for SHA1withECDSA signatures. + public static final class SHA1 extends NativeECDSASignature { + public SHA1() { + super("SHA1"); + } + } + + // Nested class for SHA1withECDSAinP1363Format signatures. + public static final class SHA1inP1363Format extends NativeECDSASignature { + public SHA1inP1363Format() { + super("SHA1", true); + } + } + + // Nested class for SHA224withECDSA signatures. + public static final class SHA224 extends NativeECDSASignature { + public SHA224() { + super("SHA-224"); + } + } + + // Nested class for SHA224withECDSAinP1363Format signatures. + public static final class SHA224inP1363Format extends NativeECDSASignature { + public SHA224inP1363Format() { + super("SHA-224", true); + } + } + + // Nested class for SHA256withECDSA signatures. + public static final class SHA256 extends NativeECDSASignature { + public SHA256() { + super("SHA-256"); + } + } + + // Nested class for SHA256withECDSAinP1363Format signatures. + public static final class SHA256inP1363Format extends NativeECDSASignature { + public SHA256inP1363Format() { + super("SHA-256", true); + } + } + + // Nested class for SHA384withECDSA signatures. + public static final class SHA384 extends NativeECDSASignature { + public SHA384() { + super("SHA-384"); + } + } + + // Nested class for SHA384withECDSAinP1363Format signatures. + public static final class SHA384inP1363Format extends NativeECDSASignature { + public SHA384inP1363Format() { + super("SHA-384", true); + } + } + + // Nested class for SHA512withECDSA signatures. + public static final class SHA512 extends NativeECDSASignature { + public SHA512() { + super("SHA-512"); + } + } + + // Nested class for SHA512withECDSAinP1363Format signatures. + public static final class SHA512inP1363Format extends NativeECDSASignature { + public SHA512inP1363Format() { + super("SHA-512", true); + } + } + + // Initialize for verification. See JCA doc. + @Override + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + ECPublicKey key = (ECPublicKey) ECKeyFactory.toECKey(publicKey); + if (!isCompatible(this.sigParams, key.getParams())) { + throw new InvalidKeyException("Key params does not match signature params"); + } + + // Should check that the supplied key is appropriate for signature + // algorithm (e.g. P-256 for SHA256withECDSA). + this.publicKey = key; + this.privateKey = null; + resetDigest(); + + if (key instanceof ECPublicKeyImpl) { + ECPublicKeyImpl keyImpl = (ECPublicKeyImpl) key; + this.publicKeyImpl = keyImpl; + this.privateKeyImpl = null; + this.javaImplementation = null; + if (nativeCryptTrace) { + System.err.println("InitVerify: Using native crypto implementation for verifying signature."); + } + } else { + this.javaImplementation = getJavaInstance(); + this.javaImplementation.engineInitVerify(publicKey); + } + } + + // 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 { + if (random == null) { + if (nativeCryptTrace) { + System.err.println("No SecureRandom implementation was provided during" + + " initialization. Using OpenSSL."); + } + } else if ((random.getProvider() instanceof Sun) + && ("NativePRNG".equals(random.getAlgorithm()) || "DRBG".equals(random.getAlgorithm())) + ) { + if (nativeCryptTrace) { + System.err.println("Default SecureRandom implementation was provided during" + + " initialization. Using OpenSSL."); + } + } else { + if (nativeCryptTrace) { + System.err.println("SecureRandom implementation was provided during" + + " initialization. Using Java implementation instead of OpenSSL."); + } + this.javaImplementation = getJavaInstance(); + this.javaImplementation.engineInitSign(privateKey, random); + return; + } + + ECPrivateKey key = (ECPrivateKey) ECKeyFactory.toECKey(privateKey); + if (!isCompatible(this.sigParams, key.getParams())) { + throw new InvalidKeyException("Key params does not match signature params"); + } + + // Should check that the supplied key is appropriate for signature + // algorithm (e.g. P-256 for SHA256withECDSA). + this.privateKey = key; + this.publicKey = null; + this.random = random; + resetDigest(); + + if (key instanceof ECPrivateKeyImpl) { + ECPrivateKeyImpl keyImpl = (ECPrivateKeyImpl) key; + this.publicKeyImpl = null; + this.privateKeyImpl = keyImpl; + this.javaImplementation = null; + if (nativeCryptTrace) { + System.err.println("InitSign: Using native crypto implementation for verifying signature."); + } + } else { + this.javaImplementation = getJavaInstance(); + this.javaImplementation.engineInitSign(privateKey, random); + } + } + + /** + * 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 { + if (this.javaImplementation != null) { + this.javaImplementation.engineUpdate(b); + } else { + 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 { + if (this.javaImplementation != null) { + this.javaImplementation.engineUpdate(b, off, len); + } else { + messageDigest.update(b, off, len); + needsReset = true; + } + } + + // Update the signature with the plaintext data. See JCA doc. + @Override + protected void engineUpdate(ByteBuffer byteBuffer) { + if (this.javaImplementation != null) { + this.javaImplementation.engineUpdate(byteBuffer); + } else { + int len = byteBuffer.remaining(); + if (len <= 0) { + return; + } + + messageDigest.update(byteBuffer); + needsReset = true; + } + } + + private static boolean isCompatible(ECParameterSpec sigParams, + ECParameterSpec keyParams) { + if (sigParams == null) { + // no restriction on key param + return true; + } + return ECUtil.equals(sigParams, keyParams); + } + + // Sign the data and return the signature. See JCA doc. + @Override + protected byte[] engineSign() throws SignatureException { + if (this.javaImplementation != null) { + return this.javaImplementation.engineSign(); + } + + long nativePrivateKey = privateKeyImpl.getNativePtr(); + byte[] digest = getDigestValue(); + int digestLen = digest.length; + ECParameterSpec params = privateKey.getParams(); + int sigLen = ((params.getOrder().bitLength() + 7) / 8) * 2; + byte[] sig = new byte[sigLen]; + + if (nativePrivateKey == -1) { + throw new ProviderException("Keys could not be converted to native OpenSSL format"); + } + if (nativeCryptTrace) { + System.err.println("Sign: Keys were successfully converted to native OpenSSL format."); + } + + if (nativeCrypto == null) { + nativeCrypto = NativeCrypto.getNativeCrypto(); + } + + int ret; + synchronized (this.privateKey) { + ret = nativeCrypto.ECDSASign(nativePrivateKey, digest, digestLen, sig, sig.length); + } + if (ret == -1) { + throw new ProviderException("An error occured when creating signature"); + } + + if (nativeCryptTrace) { + System.err.println("Sign: Signature was successfully created."); + } + + if (p1363Format) { + return sig; + } else { + return ECUtil.encodeSignature(sig); + } + } + + // Verify the data and return the result. See JCA doc. + @Override + protected boolean engineVerify(byte[] signature) throws SignatureException { + if (this.javaImplementation != null) { + return this.javaImplementation.engineVerify(signature); + } + + long nativePublicKey = publicKeyImpl.getNativePtr(); + if (nativePublicKey == -1) { + throw new ProviderException("Could not convert keys to native format"); + } + if (nativeCryptTrace) { + System.err.println("Verify: Keys were successfully converted to native OpenSSL format."); + } + + if (nativeCrypto == null) { + nativeCrypto = NativeCrypto.getNativeCrypto(); + } + + byte[] sig; + if (p1363Format) { + sig = signature; + } else { + sig = ECUtil.decodeSignature(signature); + } + + byte[] digest = getDigestValue(); + int digestLen = digest.length; + + int ret; + synchronized (this.publicKey) { + ret = nativeCrypto.ECDSAVerify(nativePublicKey, digest, digestLen, sig, sig.length); + } + + if (ret == 1) { + if (nativeCryptTrace) { + System.err.println("Verify: Signature was successfully verified."); + } + return true; + } else if (ret == 0) { + if (nativeCryptTrace) { + System.err.println("Verify: Signature verification was unsuccessful."); + } + return false; + } else { + throw new ProviderException("An error occured when verifying signature"); + } + } + + // Set parameter, not supported. See JCA doc. + @Deprecated + @Override + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + throw new UnsupportedOperationException("setParameter() not supported"); + } + + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (params == null) { + sigParams = null; + return; + } + if (!(params instanceof ECParameterSpec)) { + throw new InvalidAlgorithmParameterException( + "Parameters must be of type ECParameterSpec"); + } + ECKey key = (this.privateKey == null) ? this.publicKey : this.privateKey; + if ((key != null) && !isCompatible((ECParameterSpec) params, key.getParams())) { + throw new InvalidAlgorithmParameterException + ("Signature params does not match key params"); + } + sigParams = (ECParameterSpec) params; + } + + // Get parameter, not supported. See JCA doc. + @Deprecated + @Override + protected Object engineGetParameter(String param) + throws InvalidParameterException { + throw new UnsupportedOperationException("getParameter() not supported"); + } + + @Override + protected AlgorithmParameters engineGetParameters() { + if (sigParams == null) { + return null; + } + try { + AlgorithmParameters ap = AlgorithmParameters.getInstance("EC"); + ap.init(sigParams); + return ap; + } catch (Exception e) { + // Should never happen. + throw new ProviderException("Error retrieving EC parameters", e); + } + } + + private ECDSASignature getJavaInstance() { + if (this.messageDigest == null) { + return this.p1363Format + ? new ECDSASignature.RawinP1363Format() + : new ECDSASignature.Raw(); + } else { + String mdAlgo = messageDigest.getAlgorithm(); + switch (mdAlgo) { + case "SHA1": + return this.p1363Format + ? new ECDSASignature.SHA1inP1363Format() + : new ECDSASignature.SHA1(); + case "SHA-224": + return this.p1363Format + ? new ECDSASignature.SHA224inP1363Format() + : new ECDSASignature.SHA224(); + case "SHA-256": + return this.p1363Format + ? new ECDSASignature.SHA256inP1363Format() + : new ECDSASignature.SHA256(); + case "SHA-384": + return this.p1363Format + ? new ECDSASignature.SHA384inP1363Format() + : new ECDSASignature.SHA384(); + case "SHA-512": + return this.p1363Format + ? new ECDSASignature.SHA512inP1363Format() + : new ECDSASignature.SHA512(); + default: + throw new ProviderException("Unexpected algorithm: " + mdAlgo); + } + } + } +} diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/SunEC.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/SunEC.java index 7507b09c20..6665d4c2d4 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/SunEC.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/SunEC.java @@ -82,6 +82,11 @@ public final class SunEC extends Provider { */ private static final boolean useNativeECKeyGen = NativeCrypto.isAlgorithmEnabled("jdk.nativeECKeyGen", "SunEC"); + /* The property 'jdk.nativeECDSA' is used to control enablement of the native + * ECDSA signature implementation. + */ + private static final boolean useNativeECDSA = NativeCrypto.isAlgorithmEnabled("jdk.nativeECDSA", "SunEC"); + /* The property 'jdk.nativeXDHKeyAgreement' is used to control enablement of the native * XDH key agreement. XDH key agreement is only supported in OpenSSL 1.1.1 and above. */ @@ -141,24 +146,63 @@ public Object newInstance(Object ctrParamObj) if (inP1363) { algo = algo.substring(0, algo.length() - 13); } - if (algo.equals("SHA1withECDSA")) { - return (inP1363? new ECDSASignature.SHA1inP1363Format() : - new ECDSASignature.SHA1()); - } else if (algo.equals("SHA224withECDSA")) { - return (inP1363? new ECDSASignature.SHA224inP1363Format() : - new ECDSASignature.SHA224()); - } else if (algo.equals("SHA256withECDSA")) { - return (inP1363? new ECDSASignature.SHA256inP1363Format() : - new ECDSASignature.SHA256()); - } else if (algo.equals("SHA384withECDSA")) { - return (inP1363? new ECDSASignature.SHA384inP1363Format() : - new ECDSASignature.SHA384()); - } else if (algo.equals("SHA512withECDSA")) { - return (inP1363? new ECDSASignature.SHA512inP1363Format() : - new ECDSASignature.SHA512()); - } else if (algo.equals("NONEwithECDSA")) { - return (inP1363? new ECDSASignature.RawinP1363Format() : - new ECDSASignature.Raw()); + if (useNativeECDSA + && (NativeCrypto.getVersionIfAvailable() >= NativeCrypto.OPENSSL_VERSION_1_1_1) + ) { + if (nativeCryptTrace) { + System.err.println("ECDSA Signature - Using OpenSSL integration."); + } + if (algo.equals("SHA1withECDSA")) { + return inP1363 + ? new NativeECDSASignature.SHA1inP1363Format() + : new NativeECDSASignature.SHA1(); + } else if (algo.equals("SHA224withECDSA")) { + return inP1363 + ? new NativeECDSASignature.SHA224inP1363Format() + : new NativeECDSASignature.SHA224(); + } else if (algo.equals("SHA256withECDSA")) { + return inP1363 + ? new NativeECDSASignature.SHA256inP1363Format() + : new NativeECDSASignature.SHA256(); + } else if (algo.equals("SHA384withECDSA")) { + return inP1363 + ? new NativeECDSASignature.SHA384inP1363Format() + : new NativeECDSASignature.SHA384(); + } else if (algo.equals("SHA512withECDSA")) { + return inP1363 + ? new NativeECDSASignature.SHA512inP1363Format() + : new NativeECDSASignature.SHA512(); + } else if (algo.equals("NONEwithECDSA")) { + return inP1363 + ? new NativeECDSASignature.RawinP1363Format() + : new NativeECDSASignature.Raw(); + } + } else { + if (algo.equals("SHA1withECDSA")) { + return inP1363 + ? new ECDSASignature.SHA1inP1363Format() + : new ECDSASignature.SHA1(); + } else if (algo.equals("SHA224withECDSA")) { + return inP1363 + ? new ECDSASignature.SHA224inP1363Format() + : new ECDSASignature.SHA224(); + } else if (algo.equals("SHA256withECDSA")) { + return inP1363 + ? new ECDSASignature.SHA256inP1363Format() + : new ECDSASignature.SHA256(); + } else if (algo.equals("SHA384withECDSA")) { + return inP1363 + ? new ECDSASignature.SHA384inP1363Format() + : new ECDSASignature.SHA384(); + } else if (algo.equals("SHA512withECDSA")) { + return inP1363 + ? new ECDSASignature.SHA512inP1363Format() + : new ECDSASignature.SHA512(); + } else if (algo.equals("NONEwithECDSA")) { + return inP1363 + ? new ECDSASignature.RawinP1363Format() + : new ECDSASignature.Raw(); + } } } else if (type.equals("KeyFactory")) { if (algo.equals("EC")) { @@ -399,43 +443,85 @@ void putEntries(boolean useFullImplementation) { /* * Signature engines */ - putService(new ProviderService(this, "Signature", - "NONEwithECDSA", "sun.security.ec.ECDSASignature$Raw", - null, ATTRS)); - putService(new ProviderServiceA(this, "Signature", - "SHA1withECDSA", "sun.security.ec.ECDSASignature$SHA1", - ATTRS)); - putService(new ProviderServiceA(this, "Signature", - "SHA224withECDSA", "sun.security.ec.ECDSASignature$SHA224", - ATTRS)); - putService(new ProviderServiceA(this, "Signature", - "SHA256withECDSA", "sun.security.ec.ECDSASignature$SHA256", - ATTRS)); - putService(new ProviderServiceA(this, "Signature", - "SHA384withECDSA", "sun.security.ec.ECDSASignature$SHA384", - ATTRS)); - putService(new ProviderServiceA(this, "Signature", - "SHA512withECDSA", "sun.security.ec.ECDSASignature$SHA512", - ATTRS)); + if (useNativeECDSA + && (NativeCrypto.getVersionIfAvailable() >= NativeCrypto.OPENSSL_VERSION_1_1_1) + ) { + putService(new ProviderService(this, "Signature", + "NONEwithECDSA", "sun.security.ec.NativeECDSASignature$Raw", + null, ATTRS)); + putService(new ProviderServiceA(this, "Signature", + "SHA1withECDSA", "sun.security.ec.NativeECDSASignature$SHA1", + ATTRS)); + putService(new ProviderServiceA(this, "Signature", + "SHA224withECDSA", "sun.security.ec.NativeECDSASignature$SHA224", + ATTRS)); + putService(new ProviderServiceA(this, "Signature", + "SHA256withECDSA", "sun.security.ec.NativeECDSASignature$SHA256", + ATTRS)); + putService(new ProviderServiceA(this, "Signature", + "SHA384withECDSA", "sun.security.ec.NativeECDSASignature$SHA384", + ATTRS)); + putService(new ProviderServiceA(this, "Signature", + "SHA512withECDSA", "sun.security.ec.NativeECDSASignature$SHA512", + ATTRS)); + + putService(new ProviderService(this, "Signature", + "NONEwithECDSAinP1363Format", + "sun.security.ec.NativeECDSASignature$RawinP1363Format")); + putService(new ProviderService(this, "Signature", + "SHA1withECDSAinP1363Format", + "sun.security.ec.NativeECDSASignature$SHA1inP1363Format")); + putService(new ProviderService(this, "Signature", + "SHA224withECDSAinP1363Format", + "sun.security.ec.NativeECDSASignature$SHA224inP1363Format")); + putService(new ProviderService(this, "Signature", + "SHA256withECDSAinP1363Format", + "sun.security.ec.NativeECDSASignature$SHA256inP1363Format")); + putService(new ProviderService(this, "Signature", + "SHA384withECDSAinP1363Format", + "sun.security.ec.NativeECDSASignature$SHA384inP1363Format")); + putService(new ProviderService(this, "Signature", + "SHA512withECDSAinP1363Format", + "sun.security.ec.NativeECDSASignature$SHA512inP1363Format")); + } else { + putService(new ProviderService(this, "Signature", + "NONEwithECDSA", "sun.security.ec.ECDSASignature$Raw", + null, ATTRS)); + putService(new ProviderServiceA(this, "Signature", + "SHA1withECDSA", "sun.security.ec.ECDSASignature$SHA1", + ATTRS)); + putService(new ProviderServiceA(this, "Signature", + "SHA224withECDSA", "sun.security.ec.ECDSASignature$SHA224", + ATTRS)); + putService(new ProviderServiceA(this, "Signature", + "SHA256withECDSA", "sun.security.ec.ECDSASignature$SHA256", + ATTRS)); + putService(new ProviderServiceA(this, "Signature", + "SHA384withECDSA", "sun.security.ec.ECDSASignature$SHA384", + ATTRS)); + putService(new ProviderServiceA(this, "Signature", + "SHA512withECDSA", "sun.security.ec.ECDSASignature$SHA512", + ATTRS)); - putService(new ProviderService(this, "Signature", - "NONEwithECDSAinP1363Format", - "sun.security.ec.ECDSASignature$RawinP1363Format")); - putService(new ProviderService(this, "Signature", - "SHA1withECDSAinP1363Format", - "sun.security.ec.ECDSASignature$SHA1inP1363Format")); - putService(new ProviderService(this, "Signature", - "SHA224withECDSAinP1363Format", - "sun.security.ec.ECDSASignature$SHA224inP1363Format")); - putService(new ProviderService(this, "Signature", - "SHA256withECDSAinP1363Format", - "sun.security.ec.ECDSASignature$SHA256inP1363Format")); - putService(new ProviderService(this, "Signature", - "SHA384withECDSAinP1363Format", - "sun.security.ec.ECDSASignature$SHA384inP1363Format")); - putService(new ProviderService(this, "Signature", - "SHA512withECDSAinP1363Format", - "sun.security.ec.ECDSASignature$SHA512inP1363Format")); + putService(new ProviderService(this, "Signature", + "NONEwithECDSAinP1363Format", + "sun.security.ec.ECDSASignature$RawinP1363Format")); + putService(new ProviderService(this, "Signature", + "SHA1withECDSAinP1363Format", + "sun.security.ec.ECDSASignature$SHA1inP1363Format")); + putService(new ProviderService(this, "Signature", + "SHA224withECDSAinP1363Format", + "sun.security.ec.ECDSASignature$SHA224inP1363Format")); + putService(new ProviderService(this, "Signature", + "SHA256withECDSAinP1363Format", + "sun.security.ec.ECDSASignature$SHA256inP1363Format")); + putService(new ProviderService(this, "Signature", + "SHA384withECDSAinP1363Format", + "sun.security.ec.ECDSASignature$SHA384inP1363Format")); + putService(new ProviderService(this, "Signature", + "SHA512withECDSAinP1363Format", + "sun.security.ec.ECDSASignature$SHA512inP1363Format")); + } /* * Key Pair Generator engine