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.

Bug: 62283964
Test: vogar libcore.java.security.SignatureTest (with provider modified)
Change-Id: I9de6be257de225981c87e167dc70be91af2a20d6
diff --git a/common/src/jni/main/cpp/NativeCrypto.cpp b/common/src/jni/main/cpp/NativeCrypto.cpp
index b8ca3c3..aeb6205 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()");
 
@@ -8976,6 +9086,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 33e470a..b68c594 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 95e18f2..503109d 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLProvider.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLProvider.java
@@ -219,6 +219,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..86d18c0
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/OpenSSLSignatureRawECDSA.java
@@ -0,0 +1,122 @@
+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;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+
+/**
+ * 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();
+        }
+    }
+}