Add Signature NONEwithECDSA. (#205)
* Add Signature NONEwithECDSA.
Followed the pattern of OpenSSLSignatureRawRSA.
This is implemented but not yet enabled in the provider, so that it doesn't
yet show up in devices.
* Remove unused imports
* Add license header
diff --git a/common/src/jni/main/cpp/NativeCrypto.cpp b/common/src/jni/main/cpp/NativeCrypto.cpp
index 6cb7e73..97aa036 100644
--- a/common/src/jni/main/cpp/NativeCrypto.cpp
+++ b/common/src/jni/main/cpp/NativeCrypto.cpp
@@ -2082,6 +2082,116 @@
return outputLength;
}
+static jint NativeCrypto_ECDSA_size(JNIEnv* env, jclass, jobject pkeyRef) {
+ EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
+ JNI_TRACE("ECDSA_size(%p)", pkey);
+
+ if (pkey == nullptr) {
+ return 0;
+ }
+
+ bssl::UniquePtr<EC_KEY> ec_key(EVP_PKEY_get1_EC_KEY(pkey));
+ if (ec_key.get() == nullptr) {
+ Errors::jniThrowRuntimeException(env, "ECDSA_size failed");
+ return 0;
+ }
+
+ size_t size = ECDSA_size(ec_key.get());
+
+ JNI_TRACE("ECDSA_size(%p) => %zu", pkey, size);
+ return static_cast<jint>(size);
+}
+
+static jint NativeCrypto_ECDSA_sign(JNIEnv* env, jclass, jbyteArray data, jbyteArray sig,
+ jobject pkeyRef) {
+ EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
+ JNI_TRACE("ECDSA_sign(%p, %p, %p)", data, sig, pkey);
+
+ if (pkey == nullptr) {
+ return -1;
+ }
+
+ bssl::UniquePtr<EC_KEY> ec_key(EVP_PKEY_get1_EC_KEY(pkey));
+ if (ec_key.get() == nullptr) {
+ return -1;
+ }
+
+ ScopedByteArrayRO data_array(env, data);
+ if (data_array.get() == nullptr) {
+ return -1;
+ }
+
+ ScopedByteArrayRW sig_array(env, sig);
+ if (sig_array.get() == nullptr) {
+ return -1;
+ }
+
+ unsigned int sig_size;
+ int result = ECDSA_sign(0, reinterpret_cast<const unsigned char*>(data_array.get()),
+ data_array.size(), reinterpret_cast<unsigned char*>(sig_array.get()),
+ &sig_size, ec_key.get());
+ if (result == 0) {
+ if (Errors::throwExceptionIfNecessary(env, "ECDSA_sign")) {
+ JNI_TRACE("ECDSA_sign => threw error");
+ }
+ return -1;
+ }
+
+ JNI_TRACE("ECDSA_sign(%p, %p, %p) => %d", data, sig, pkey, sig_size);
+ return static_cast<jint>(sig_size);
+}
+
+static jint NativeCrypto_ECDSA_verify(JNIEnv* env, jclass, jbyteArray data, jbyteArray sig,
+ jobject pkeyRef) {
+ EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
+ JNI_TRACE("ECDSA_verify(%p, %p, %p)", data, sig, pkey);
+
+ if (pkey == nullptr) {
+ return -1;
+ }
+
+ bssl::UniquePtr<EC_KEY> ec_key(EVP_PKEY_get1_EC_KEY(pkey));
+ if (ec_key.get() == nullptr) {
+ return -1;
+ }
+
+ ScopedByteArrayRO data_array(env, data);
+ if (data_array.get() == nullptr) {
+ return -1;
+ }
+
+ ScopedByteArrayRO sig_array(env, sig);
+ if (sig_array.get() == nullptr) {
+ return -1;
+ }
+
+ int result =
+ ECDSA_verify(0, reinterpret_cast<const unsigned char*>(data_array.get()),
+ data_array.size(), reinterpret_cast<const unsigned char*>(sig_array.get()),
+ sig_array.size(), ec_key.get());
+
+ if (result == 0) {
+ unsigned long error = ERR_peek_last_error();
+ if ((ERR_GET_LIB(error) == ERR_LIB_ECDSA) &&
+ (ERR_GET_REASON(error) == ECDSA_R_BAD_SIGNATURE)) {
+ // This error just means the signature didn't verify, so clear the error and return
+ // a failed verification
+ ERR_clear_error();
+ JNI_TRACE("ECDSA_verify(%p, %p, %p) => %d", data, sig, pkey, result);
+ return 0;
+ }
+ if (Errors::throwExceptionIfNecessary(env, "ECDSA_verify")) {
+ JNI_TRACE("ECDSA_verify => threw error");
+ } else {
+ return 0;
+ }
+ return -1;
+ }
+
+ JNI_TRACE("ECDSA_verify(%p, %p, %p) => %d", data, sig, pkey, result);
+ return static_cast<jint>(result);
+}
+
static jlong NativeCrypto_EVP_MD_CTX_create(JNIEnv* env, jclass) {
JNI_TRACE_MD("EVP_MD_CTX_create()");
@@ -9043,6 +9153,9 @@
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EC_KEY_get_private_key, "(" REF_EVP_PKEY ")[B"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EC_KEY_get_public_key, "(" REF_EVP_PKEY ")J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, ECDH_compute_key, "([BI" REF_EVP_PKEY REF_EVP_PKEY ")I"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, ECDSA_size, "(" REF_EVP_PKEY ")I"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, ECDSA_sign, "([B[B" REF_EVP_PKEY ")I"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, ECDSA_verify, "([B[B" REF_EVP_PKEY ")I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_create, "()J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_cleanup, "(" REF_EVP_MD_CTX ")V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_destroy, "(J)V"),
diff --git a/common/src/main/java/org/conscrypt/NativeCrypto.java b/common/src/main/java/org/conscrypt/NativeCrypto.java
index 54eeb85..e0ad406 100644
--- a/common/src/main/java/org/conscrypt/NativeCrypto.java
+++ b/common/src/main/java/org/conscrypt/NativeCrypto.java
@@ -167,6 +167,12 @@
static native int ECDH_compute_key(byte[] out, int outOffset, NativeRef.EVP_PKEY publicKeyRef,
NativeRef.EVP_PKEY privateKeyRef) throws InvalidKeyException;
+ static native int ECDSA_size(NativeRef.EVP_PKEY pkey);
+
+ static native int ECDSA_sign(byte[] data, byte[] sig, NativeRef.EVP_PKEY pkey);
+
+ static native int ECDSA_verify(byte[] data, byte[] sig, NativeRef.EVP_PKEY pkey);
+
// --- Message digest functions --------------
// These return const references
diff --git a/common/src/main/java/org/conscrypt/OpenSSLProvider.java b/common/src/main/java/org/conscrypt/OpenSSLProvider.java
index 15fea2c..5209c80 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLProvider.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLProvider.java
@@ -178,6 +178,9 @@
putRAWRSASignatureImplClass("OpenSSLSignatureRawRSA");
+ // TODO: Enable
+ // putSignatureImplClass("NONEwithECDSA", "OpenSSLSignatureRawECDSA");
+
putSignatureImplClass("SHA1withECDSA", "OpenSSLSignature$SHA1ECDSA");
put("Alg.Alias.Signature.ECDSA", "SHA1withECDSA");
put("Alg.Alias.Signature.ECDSAwithSHA1", "SHA1withECDSA");
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSignatureRawECDSA.java b/common/src/main/java/org/conscrypt/OpenSSLSignatureRawECDSA.java
new file mode 100644
index 0000000..df35818
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/OpenSSLSignatureRawECDSA.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.conscrypt;
+
+import java.io.ByteArrayOutputStream;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+
+/**
+ * Implements the JDK Signature interface needed for RAW ECDSA signature
+ * generation and verification using BoringSSL.
+ *
+ * @hide
+ */
+@Internal
+public class OpenSSLSignatureRawECDSA extends SignatureSpi {
+ /**
+ * The current OpenSSL key we're operating on.
+ */
+ private OpenSSLKey key;
+
+ /**
+ * Buffer to hold value to be signed or verified.
+ */
+ private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+ @Override
+ protected void engineUpdate(byte input) {
+ buffer.write(input);
+ }
+
+ @Override
+ protected void engineUpdate(byte[] input, int offset, int len) {
+ buffer.write(input, offset, len);
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ protected Object engineGetParameter(String param) throws InvalidParameterException {
+ return null;
+ }
+
+ private static OpenSSLKey verifyKey(OpenSSLKey key) throws InvalidKeyException {
+ int pkeyType = NativeCrypto.EVP_PKEY_type(key.getNativeRef());
+ if (pkeyType != NativeConstants.EVP_PKEY_EC) {
+ throw new InvalidKeyException("Non-EC key used to initialize EC signature.");
+ }
+ return key;
+ }
+
+ @Override
+ protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+ key = verifyKey(OpenSSLKey.fromPrivateKey(privateKey));
+ }
+
+ @Override
+ protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+ key = verifyKey(OpenSSLKey.fromPublicKey(publicKey));
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
+ }
+
+ @Override
+ protected byte[] engineSign() throws SignatureException {
+ if (key == null) {
+ // This can't actually happen, but you never know...
+ throw new SignatureException("No key provided");
+ }
+
+ int output_size = NativeCrypto.ECDSA_size(key.getNativeRef());
+ byte[] outputBuffer = new byte[output_size];
+ try {
+ int bytes_written =
+ NativeCrypto.ECDSA_sign(buffer.toByteArray(), outputBuffer, key.getNativeRef());
+ if (bytes_written < 0) {
+ throw new SignatureException("Could not compute signature.");
+ }
+ // There's no guarantee that the signature will be ECDSA_size bytes long,
+ // that's just the maximum possible length of a signature. Only return the bytes
+ // that were actually produced.
+ if (bytes_written != output_size) {
+ byte[] newBuffer = new byte[bytes_written];
+ System.arraycopy(outputBuffer, 0, newBuffer, 0, bytes_written);
+ outputBuffer = newBuffer;
+ }
+ return outputBuffer;
+ } catch (Exception ex) {
+ throw new SignatureException(ex);
+ } finally {
+ buffer.reset();
+ }
+ }
+
+ @Override
+ protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+ if (key == null) {
+ // This can't actually happen, but you never know...
+ throw new SignatureException("No key provided");
+ }
+
+ try {
+ int result =
+ NativeCrypto.ECDSA_verify(buffer.toByteArray(), sigBytes, key.getNativeRef());
+ if (result == -1) {
+ throw new SignatureException("Could not verify signature.");
+ }
+ return result == 1;
+ } catch (Exception ex) {
+ throw new SignatureException(ex);
+ } finally {
+ buffer.reset();
+ }
+ }
+}