Switch from EVP_[Sign|Verify] to EVP_Digest[Sign|Verify].

This switches Conscrypt's Signature implementations from the older
EVP_Sign/EVP_Verify API to the newer EVP_DigestSign/EVP_DigestVerify
API. The main factor driving this switch is to expose RSASSA-PSS
which does not work via the old API.

In particular, this change:
* adds EVP_DigestSign* and EVP_DigestVerify* to NativeCrypto. Some of
  these NativeCrypto functions were already there but weren't used.
  This made it easier to adjust their signatures to best results.
* switches Signature implementation from EVP_Sign/EVP_Verify to
  EVP_DigestSign/EVP_DigestVerify.
* removes EVP_Sign* and EVP_Verify* from NativeCrypto because they are
  no longer used.
* inlines NativeCrypto's evpInit into its EVP_DigestInit_ex because
  the latter became the only user of evpInit after the cleanup.

Change-Id: Id29ea4fc2bc5b1cd81daaee8b475fd147616de51
diff --git a/src/main/java/org/conscrypt/NativeCrypto.java b/src/main/java/org/conscrypt/NativeCrypto.java
index ae02008..24f91db 100644
--- a/src/main/java/org/conscrypt/NativeCrypto.java
+++ b/src/main/java/org/conscrypt/NativeCrypto.java
@@ -256,38 +256,30 @@
     public static native int EVP_DigestFinal_ex(NativeRef.EVP_MD_CTX ctx, byte[] hash,
             int offset);
 
-    // --- MAC handling functions ----------------------------------------------
-
-    public static native void EVP_DigestSignInit(NativeRef.EVP_MD_CTX evp_md_ctx,
-            long evp_md, NativeRef.EVP_PKEY evp_pkey);
-
-    public static native void EVP_DigestSignUpdate(NativeRef.EVP_MD_CTX evp_md_ctx,
-            byte[] in);
-
-    public static native byte[] EVP_DigestSignFinal(NativeRef.EVP_MD_CTX evp_md_ctx);
-
     // --- Signature handling functions ----------------------------------------
 
-    public static native int EVP_SignInit(NativeRef.EVP_MD_CTX ctx, long evpRef);
+    public static native long EVP_DigestSignInit(NativeRef.EVP_MD_CTX ctx,
+            long evpMdRef, NativeRef.EVP_PKEY key);
 
-    public static native void EVP_SignUpdate(NativeRef.EVP_MD_CTX ctx, byte[] buffer,
-            int offset, int length);
+    public static native long EVP_DigestVerifyInit(NativeRef.EVP_MD_CTX ctx,
+            long evpMdRef, NativeRef.EVP_PKEY key);
 
-    public static native void EVP_SignUpdateDirect(NativeRef.EVP_MD_CTX ctx, long ptr, int length);
-
-    public static native int EVP_SignFinal(NativeRef.EVP_MD_CTX ctx, byte[] signature,
-            int offset, NativeRef.EVP_PKEY key);
-
-    public static native int EVP_VerifyInit(NativeRef.EVP_MD_CTX ctx, long evpRef);
-
-    public static native void EVP_VerifyUpdate(NativeRef.EVP_MD_CTX ctx,
+    public static native void EVP_DigestSignUpdate(NativeRef.EVP_MD_CTX ctx,
             byte[] buffer, int offset, int length);
 
-    public static native void EVP_VerifyUpdateDirect(NativeRef.EVP_MD_CTX ctx,
+    public static native void EVP_DigestSignUpdateDirect(NativeRef.EVP_MD_CTX ctx,
             long ptr, int length);
 
-    public static native int EVP_VerifyFinal(NativeRef.EVP_MD_CTX ctx,
-            byte[] signature, int offset, int length, NativeRef.EVP_PKEY key);
+    public static native void EVP_DigestVerifyUpdate(NativeRef.EVP_MD_CTX ctx,
+            byte[] buffer, int offset, int length);
+
+    public static native void EVP_DigestVerifyUpdateDirect(NativeRef.EVP_MD_CTX ctx,
+            long ptr, int length);
+
+    public static native byte[] EVP_DigestSignFinal(NativeRef.EVP_MD_CTX ctx);
+
+    public static native boolean EVP_DigestVerifyFinal(NativeRef.EVP_MD_CTX ctx,
+            byte[] signature, int offset, int length);
 
     // --- Block ciphers -------------------------------------------------------
 
diff --git a/src/main/java/org/conscrypt/OpenSSLSignature.java b/src/main/java/org/conscrypt/OpenSSLSignature.java
index 8e30b5e..f3d0cc6 100644
--- a/src/main/java/org/conscrypt/OpenSSLSignature.java
+++ b/src/main/java/org/conscrypt/OpenSSLSignature.java
@@ -19,7 +19,6 @@
 import java.nio.ByteBuffer;
 import java.security.InvalidKeyException;
 import java.security.InvalidParameterException;
-import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.SignatureException;
@@ -32,7 +31,7 @@
 public class OpenSSLSignature extends SignatureSpi {
     private static enum EngineType {
         RSA, EC,
-    };
+    }
 
     private NativeRef.EVP_MD_CTX ctx;
 
@@ -47,9 +46,9 @@
     private final EngineType engineType;
 
     /**
-     * Holds the OpenSSL name of the algorithm (lower case, no dashes).
+     * Digest algorithm (reference to {@code EVP_MD}).
      */
-    private final long evpAlgorithm;
+    private final long evpMdRef;
 
     /**
      * Holds a dummy buffer for writing single bytes to the digest.
@@ -62,27 +61,42 @@
     private boolean signing;
 
     /**
+     * Public key algorithm context (reference to {@code EVP_PKEY_CTX}).
+     */
+    private long evpPkeyCtx;
+
+    /**
      * Creates a new OpenSSLSignature instance for the given algorithm name.
      *
-     * @param algorithm OpenSSL name of the algorithm, e.g. "RSA-SHA1".
+     * @param evpMdRef digest algorithm ({@code EVP_MD} reference).
      */
-    private OpenSSLSignature(long algorithm, EngineType engineType)
-            throws NoSuchAlgorithmException {
+    private OpenSSLSignature(long evpMdRef, EngineType engineType) {
         this.engineType = engineType;
-        this.evpAlgorithm = algorithm;
+        this.evpMdRef = evpMdRef;
     }
 
     private final void resetContext() {
         NativeRef.EVP_MD_CTX ctxLocal = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
         if (signing) {
             enableDSASignatureNonceHardeningIfApplicable();
-            NativeCrypto.EVP_SignInit(ctxLocal, evpAlgorithm);
+            evpPkeyCtx = NativeCrypto.EVP_DigestSignInit(ctxLocal, evpMdRef, key.getNativeRef());
         } else {
-            NativeCrypto.EVP_VerifyInit(ctxLocal, evpAlgorithm);
+            evpPkeyCtx = NativeCrypto.EVP_DigestVerifyInit(ctxLocal, evpMdRef, key.getNativeRef());
         }
+        configureEVP_PKEY_CTX(evpPkeyCtx);
         this.ctx = ctxLocal;
     }
 
+    /**
+     * Configures the public key algorithm context ({@code EVP_PKEY_CTX}) associated with this
+     * operation.
+     *
+     * <p>The default implementation does nothing.
+     *
+     * @param ctx reference to the context ({@code EVP_PKEY_CTX}).
+     */
+    protected void configureEVP_PKEY_CTX(long ctx) {}
+
     @Override
     protected void engineUpdate(byte input) {
         singleByte[0] = input;
@@ -93,9 +107,9 @@
     protected void engineUpdate(byte[] input, int offset, int len) {
         final NativeRef.EVP_MD_CTX ctxLocal = ctx;
         if (signing) {
-            NativeCrypto.EVP_SignUpdate(ctxLocal, input, offset, len);
+            NativeCrypto.EVP_DigestSignUpdate(ctxLocal, input, offset, len);
         } else {
-            NativeCrypto.EVP_VerifyUpdate(ctxLocal, input, offset, len);
+            NativeCrypto.EVP_DigestVerifyUpdate(ctxLocal, input, offset, len);
         }
     }
 
@@ -136,13 +150,14 @@
 
         final NativeRef.EVP_MD_CTX ctxLocal = ctx;
         if (signing) {
-            NativeCrypto.EVP_SignUpdateDirect(ctxLocal, ptr, len);
+            NativeCrypto.EVP_DigestSignUpdateDirect(ctxLocal, ptr, len);
         } else {
-            NativeCrypto.EVP_VerifyUpdateDirect(ctxLocal, ptr, len);
+            NativeCrypto.EVP_DigestVerifyUpdateDirect(ctxLocal, ptr, len);
         }
         input.position(position + len);
     }
 
+    @Deprecated
     @Override
     protected Object engineGetParameter(String param) throws InvalidParameterException {
         return null;
@@ -206,27 +221,16 @@
         initInternal(OpenSSLKey.fromPublicKey(publicKey), false);
     }
 
+    @Deprecated
     @Override
     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("Need RSA or EC private key");
-        }
-
         final NativeRef.EVP_MD_CTX ctxLocal = ctx;
         try {
-            byte[] buffer = new byte[NativeCrypto.EVP_PKEY_size(key.getNativeRef())];
-            int bytesWritten = NativeCrypto.EVP_SignFinal(ctxLocal, buffer, 0,
-                    key.getNativeRef());
-
-            byte[] signature = new byte[bytesWritten];
-            System.arraycopy(buffer, 0, signature, 0, bytesWritten);
-
-            return signature;
+            return NativeCrypto.EVP_DigestSignFinal(ctxLocal);
         } catch (Exception ex) {
             throw new SignatureException(ex);
         } finally {
@@ -240,15 +244,9 @@
 
     @Override
     protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
-        if (key == null) {
-            // This can't actually happen, but you never know...
-            throw new SignatureException("Need RSA or EC public key");
-        }
-
+        final NativeRef.EVP_MD_CTX ctxLocal = ctx;
         try {
-            int result = NativeCrypto.EVP_VerifyFinal(ctx, sigBytes, 0, sigBytes.length,
-                    key.getNativeRef());
-            return result == 1;
+            return NativeCrypto.EVP_DigestVerifyFinal(ctxLocal, sigBytes, 0, sigBytes.length);
         } catch (Exception ex) {
             throw new SignatureException(ex);
         } finally {
@@ -262,67 +260,67 @@
 
     public static final class MD5RSA extends OpenSSLSignature {
         private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("MD5");
-        public MD5RSA() throws NoSuchAlgorithmException {
+        public MD5RSA() {
             super(EVP_MD, EngineType.RSA);
         }
     }
     public static final class SHA1RSA extends OpenSSLSignature {
         private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA1");
-        public SHA1RSA() throws NoSuchAlgorithmException {
+        public SHA1RSA() {
             super(EVP_MD, EngineType.RSA);
         }
     }
     public static final class SHA224RSA extends OpenSSLSignature {
         private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA224");
-        public SHA224RSA() throws NoSuchAlgorithmException {
+        public SHA224RSA() {
             super(EVP_MD, EngineType.RSA);
         }
     }
     public static final class SHA256RSA extends OpenSSLSignature {
         private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA256");
-        public SHA256RSA() throws NoSuchAlgorithmException {
+        public SHA256RSA() {
             super(EVP_MD, EngineType.RSA);
         }
     }
     public static final class SHA384RSA extends OpenSSLSignature {
         private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA384");
-        public SHA384RSA() throws NoSuchAlgorithmException {
+        public SHA384RSA() {
             super(EVP_MD, EngineType.RSA);
         }
     }
     public static final class SHA512RSA extends OpenSSLSignature {
         private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA512");
-        public SHA512RSA() throws NoSuchAlgorithmException {
+        public SHA512RSA() {
             super(EVP_MD, EngineType.RSA);
         }
     }
     public static final class SHA1ECDSA extends OpenSSLSignature {
         private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA1");
-        public SHA1ECDSA() throws NoSuchAlgorithmException {
+        public SHA1ECDSA() {
             super(EVP_MD, EngineType.EC);
         }
     }
     public static final class SHA224ECDSA extends OpenSSLSignature {
         private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA224");
-        public SHA224ECDSA() throws NoSuchAlgorithmException {
+        public SHA224ECDSA() {
             super(EVP_MD, EngineType.EC);
         }
     }
     public static final class SHA256ECDSA extends OpenSSLSignature {
         private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA256");
-        public SHA256ECDSA() throws NoSuchAlgorithmException {
+        public SHA256ECDSA() {
             super(EVP_MD, EngineType.EC);
         }
     }
     public static final class SHA384ECDSA extends OpenSSLSignature {
         private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA384");
-        public SHA384ECDSA() throws NoSuchAlgorithmException {
+        public SHA384ECDSA() {
             super(EVP_MD, EngineType.EC);
         }
     }
     public static final class SHA512ECDSA extends OpenSSLSignature {
         private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA512");
-        public SHA512ECDSA() throws NoSuchAlgorithmException {
+        public SHA512ECDSA() {
             super(EVP_MD, EngineType.EC);
         }
     }
diff --git a/src/main/native/org_conscrypt_NativeCrypto.cpp b/src/main/native/org_conscrypt_NativeCrypto.cpp
index dac1f2b..7546b1a 100644
--- a/src/main/native/org_conscrypt_NativeCrypto.cpp
+++ b/src/main/native/org_conscrypt_NativeCrypto.cpp
@@ -4296,45 +4296,32 @@
     return bytesWritten;
 }
 
-static jint evpInit(JNIEnv* env, jobject evpMdCtxRef, jlong evpMdRef, const char* jniName,
-        int (*init_func)(EVP_MD_CTX*, const EVP_MD*, ENGINE*)) {
+static jint NativeCrypto_EVP_DigestInit_ex(JNIEnv* env, jclass, jobject evpMdCtxRef,
+        jlong evpMdRef) {
     EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, evpMdCtxRef);
     const EVP_MD* evp_md = reinterpret_cast<const EVP_MD*>(evpMdRef);
-    JNI_TRACE_MD("%s(%p, %p)", jniName, ctx, evp_md);
+    JNI_TRACE_MD("EVP_DigestInit_ex(%p, %p)", ctx, evp_md);
 
     if (ctx == NULL) {
-        JNI_TRACE("%s(%p) => ctx == NULL", jniName, evp_md);
+        JNI_TRACE("EVP_DigestInit_ex(%p) => ctx == NULL", evp_md);
         return 0;
     } else if (evp_md == NULL) {
         jniThrowNullPointerException(env, "evp_md == null");
         return 0;
     }
 
-    int ok = init_func(ctx, evp_md, NULL);
+    int ok = EVP_DigestInit_ex(ctx, evp_md, NULL);
     if (ok == 0) {
-        bool exception = throwExceptionIfNecessary(env, jniName);
+        bool exception = throwExceptionIfNecessary(env, "EVP_DigestInit_ex");
         if (exception) {
-            JNI_TRACE("%s(%p) => threw exception", jniName, evp_md);
+            JNI_TRACE("EVP_DigestInit_ex(%p) => threw exception", evp_md);
             return 0;
         }
     }
-    JNI_TRACE_MD("%s(%p, %p) => %d", jniName, ctx, evp_md, ok);
+    JNI_TRACE_MD("EVP_DigestInit_ex(%p, %p) => %d", ctx, evp_md, ok);
     return ok;
 }
 
-static jint NativeCrypto_EVP_DigestInit_ex(JNIEnv* env, jclass, jobject evpMdCtxRef,
-        jlong evpMdRef) {
-    return evpInit(env, evpMdCtxRef, evpMdRef, "EVP_DigestInit_ex", EVP_DigestInit_ex);
-}
-
-static jint NativeCrypto_EVP_SignInit(JNIEnv* env, jclass, jobject evpMdCtxRef, jlong evpMdRef) {
-    return evpInit(env, evpMdCtxRef, evpMdRef, "EVP_SignInit", EVP_DigestInit_ex);
-}
-
-static jint NativeCrypto_EVP_VerifyInit(JNIEnv* env, jclass, jobject evpMdCtxRef, jlong evpMdRef) {
-    return evpInit(env, evpMdCtxRef, evpMdRef, "EVP_VerifyInit", EVP_DigestInit_ex);
-}
-
 /*
  * public static native int EVP_get_digestbyname(java.lang.String)
  */
@@ -4423,34 +4410,51 @@
     return result;
 }
 
-static void NativeCrypto_EVP_DigestSignInit(JNIEnv* env, jclass, jobject evpMdCtxRef,
-        const jlong evpMdRef, jobject pkeyRef) {
+static jlong evpDigestSignVerifyInit(
+        JNIEnv* env,
+        int (*init_func)(EVP_MD_CTX*, EVP_PKEY_CTX**, const EVP_MD*, ENGINE*, EVP_PKEY*),
+        const char* jniName,
+        jobject evpMdCtxRef, jlong evpMdRef, jobject pkeyRef) {
     EVP_MD_CTX* mdCtx = fromContextObject<EVP_MD_CTX>(env, evpMdCtxRef);
     if (mdCtx == NULL) {
-        JNI_TRACE("EVP_DigestSignInit => mdCtx == NULL");
-        return;
+        JNI_TRACE("%s => mdCtx == NULL", jniName);
+        return 0;
     }
     const EVP_MD* md = reinterpret_cast<const EVP_MD*>(evpMdRef);
     EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
     if (pkey == NULL) {
-        JNI_TRACE("ctx=%p EVP_DigestSignInit => pkey == NULL", mdCtx);
-        return;
+        JNI_TRACE("ctx=%p $s => pkey == NULL", mdCtx, jniName);
+        return 0;
     }
-    JNI_TRACE("EVP_DigestSignInit(%p, %p, %p) <- ptr", mdCtx, md, pkey);
+    JNI_TRACE("%s(%p, %p, %p) <- ptr", jniName, mdCtx, md, pkey);
 
     if (md == NULL) {
-        JNI_TRACE("ctx=%p EVP_DigestSignInit => md == NULL", mdCtx);
+        JNI_TRACE("ctx=%p %s => md == NULL", mdCtx, jniName);
         jniThrowNullPointerException(env, "md == null");
-        return;
+        return 0;
     }
 
-    if (EVP_DigestSignInit(mdCtx, (EVP_PKEY_CTX **) NULL, md, (ENGINE *) NULL, pkey) <= 0) {
-        JNI_TRACE("ctx=%p EVP_DigestSignInit => threw exception", mdCtx);
-        throwExceptionIfNecessary(env, "EVP_DigestSignInit");
-        return;
+    EVP_PKEY_CTX* pctx = NULL;
+    if (init_func(mdCtx, &pctx, md, (ENGINE *) NULL, pkey) <= 0) {
+        JNI_TRACE("ctx=%p %s => threw exception", mdCtx, jniName);
+        throwExceptionIfNecessary(env, jniName);
+        return 0;
     }
 
-    JNI_TRACE("EVP_DigestSignInit(%p, %p, %p) => success", mdCtx, md, pkey);
+    JNI_TRACE("%s(%p, %p, %p) => success", jniName, mdCtx, md, pkey);
+    return reinterpret_cast<jlong>(pctx);
+}
+
+static jlong NativeCrypto_EVP_DigestSignInit(JNIEnv* env, jclass, jobject evpMdCtxRef,
+        const jlong evpMdRef, jobject pkeyRef) {
+    return evpDigestSignVerifyInit(
+            env, EVP_DigestSignInit, "EVP_DigestSignInit", evpMdCtxRef, evpMdRef, pkeyRef);
+}
+
+static jlong NativeCrypto_EVP_DigestVerifyInit(JNIEnv* env, jclass, jobject evpMdCtxRef,
+        const jlong evpMdRef, jobject pkeyRef) {
+    return evpDigestSignVerifyInit(
+            env, EVP_DigestVerifyInit, "EVP_DigestVerifyInit", evpMdCtxRef, evpMdRef, pkeyRef);
 }
 
 static void evpUpdate(JNIEnv* env, jobject evpMdCtxRef, jlong inPtr, jint inLength,
@@ -4521,18 +4525,25 @@
 static void NativeCrypto_EVP_DigestSignUpdate(JNIEnv* env, jclass, jobject evpMdCtxRef,
         jbyteArray inJavaBytes, jint inOffset, jint inLength) {
     evpUpdate(env, evpMdCtxRef, inJavaBytes, inOffset, inLength, "EVP_DigestSignUpdate",
-            EVP_DigestUpdate);
+            EVP_DigestSignUpdate);
 }
 
-static void NativeCrypto_EVP_SignUpdateDirect(JNIEnv* env, jclass, jobject evpMdCtxRef,
+static void NativeCrypto_EVP_DigestSignUpdateDirect(JNIEnv* env, jclass, jobject evpMdCtxRef,
         jlong inPtr, jint inLength) {
-    evpUpdate(env, evpMdCtxRef, inPtr, inLength, "EVP_SignUpdateDirect", EVP_DigestUpdate);
+    evpUpdate(env, evpMdCtxRef, inPtr, inLength, "EVP_DigestSignUpdateDirect",
+            EVP_DigestSignUpdate);
 }
 
-static void NativeCrypto_EVP_SignUpdate(JNIEnv* env, jclass, jobject evpMdCtxRef,
+static void NativeCrypto_EVP_DigestVerifyUpdate(JNIEnv* env, jclass, jobject evpMdCtxRef,
         jbyteArray inJavaBytes, jint inOffset, jint inLength) {
-    evpUpdate(env, evpMdCtxRef, inJavaBytes, inOffset, inLength, "EVP_SignUpdate",
-            EVP_DigestUpdate);
+    evpUpdate(env, evpMdCtxRef, inJavaBytes, inOffset, inLength, "EVP_DigestVerifyUpdate",
+            EVP_DigestVerifyUpdate);
+}
+
+static void NativeCrypto_EVP_DigestVerifyUpdateDirect(JNIEnv* env, jclass, jobject evpMdCtxRef,
+        jlong inPtr, jint inLength) {
+    evpUpdate(env, evpMdCtxRef, inPtr, inLength, "EVP_DigestVerifyUpdateDirect",
+            EVP_DigestVerifyUpdate);
 }
 
 static jbyteArray NativeCrypto_EVP_DigestSignFinal(JNIEnv* env, jclass, jobject evpMdCtxRef)
@@ -4544,163 +4555,78 @@
          return NULL;
     }
 
-    size_t len;
-    if (EVP_DigestSignFinal(mdCtx, NULL, &len) != 1) {
+    size_t maxLen;
+    if (EVP_DigestSignFinal(mdCtx, NULL, &maxLen) != 1) {
         JNI_TRACE("ctx=%p EVP_DigestSignFinal => threw exception", mdCtx);
         throwExceptionIfNecessary(env, "EVP_DigestSignFinal");
+        return NULL;
+    }
+
+    UniquePtr<unsigned char[]> buffer(new unsigned char[maxLen]);
+    if (buffer.get() == NULL) {
+        jniThrowOutOfMemory(env, "Unable to allocate signature buffer");
         return 0;
     }
-    ScopedLocalRef<jbyteArray> outJavaBytes(env, env->NewByteArray(len));
-    if (outJavaBytes.get() == NULL) {
-        return NULL;
-    }
-    ScopedByteArrayRW outBytes(env, outJavaBytes.get());
-    if (outBytes.get() == NULL) {
-        return NULL;
-    }
-    unsigned char *tmp = reinterpret_cast<unsigned char*>(outBytes.get());
-    if (EVP_DigestSignFinal(mdCtx, tmp, &len) != 1) {
+    size_t actualLen;
+    if (EVP_DigestSignFinal(mdCtx, buffer.get(), &actualLen) != 1) {
         JNI_TRACE("ctx=%p EVP_DigestSignFinal => threw exception", mdCtx);
         throwExceptionIfNecessary(env, "EVP_DigestSignFinal");
+        return NULL;
+    }
+    if (actualLen > maxLen)  {
+        JNI_TRACE("ctx=%p EVP_DigestSignFinal => signature too long: %d vs %d",
+                  actualLen, maxLen);
+        throwExceptionIfNecessary(env, "EVP_DigestSignFinal signature too long");
+        return NULL;
+    }
+
+    ScopedLocalRef<jbyteArray> sigJavaBytes(env, env->NewByteArray(actualLen));
+    if (sigJavaBytes.get() == NULL) {
+        jniThrowOutOfMemory(env, "Failed to allocate signature byte[]");
+        return NULL;
+    }
+    env->SetByteArrayRegion(
+            sigJavaBytes.get(), 0, actualLen, reinterpret_cast<jbyte*>(buffer.get()));
+
+    JNI_TRACE("EVP_DigestSignFinal(%p) => %p", mdCtx, sigJavaBytes.get());
+    return sigJavaBytes.release();
+}
+
+static jboolean NativeCrypto_EVP_DigestVerifyFinal(JNIEnv* env, jclass, jobject evpMdCtxRef,
+        jbyteArray signature, jint offset, jint len)
+{
+    EVP_MD_CTX* mdCtx = fromContextObject<EVP_MD_CTX>(env, evpMdCtxRef);
+    JNI_TRACE("EVP_DigestVerifyFinal(%p)", mdCtx);
+
+    if (mdCtx == NULL) {
+         return 0;
+    }
+
+    ScopedByteArrayRO sigBytes(env, signature);
+    if (sigBytes.get() == NULL) {
         return 0;
     }
 
-    JNI_TRACE("EVP_DigestSignFinal(%p) => %p", mdCtx, outJavaBytes.get());
-    return outJavaBytes.release();
-}
-
-/*
- * public static native int EVP_SignFinal(long, byte[], int, long)
- */
-static jint NativeCrypto_EVP_SignFinal(JNIEnv* env, jclass, jobject ctxRef, jbyteArray signature,
-        jint offset, jobject pkeyRef) {
-    JNI_TRACE("NativeCrypto_EVP_SignFinal(%p, %p, %d, %p)", ctxRef, signature, offset, pkeyRef);
-    EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, ctxRef);
-    if (ctx == NULL) {
-        return -1;
-    }
-    EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
-    JNI_TRACE("NativeCrypto_EVP_SignFinal(%p, %p, %d, %p) <- ptr", ctx, signature, offset, pkey);
-
-    if (pkey == NULL) {
-        return -1;
+    if (ARRAY_OFFSET_LENGTH_INVALID(sigBytes, offset, len)) {
+        jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "signature");
+        return 0;
     }
 
-    ScopedByteArrayRW signatureBytes(env, signature);
-    if (signatureBytes.get() == NULL) {
-        return -1;
-    }
-    unsigned int bytesWritten = -1;
-    int ok = EVP_SignFinal(ctx,
-                           reinterpret_cast<unsigned char*>(signatureBytes.get() + offset),
-                           &bytesWritten,
-                           pkey);
-    if (ok != 1) {
-        throwExceptionIfNecessary(env, "NativeCrypto_EVP_SignFinal");
-    }
-    JNI_TRACE("NativeCrypto_EVP_SignFinal(%p, %p, %d, %p) => %u",
-              ctx, signature, offset, pkey, bytesWritten);
-
-    return bytesWritten;
-}
-
-/*
- * public static native void EVP_VerifyUpdateDirect(long, long, int)
- */
-static void NativeCrypto_EVP_VerifyUpdateDirect(JNIEnv* env, jclass, jobject ctxRef,
-                                                jlong ptr, jint length) {
-    EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, ctxRef);
-    const void *p = reinterpret_cast<const void *>(ptr);
-    JNI_TRACE("NativeCrypto_EVP_VerifyUpdateDirect(%p, %p, %d)", ctx, p, length);
-
-    if (ctx == NULL) {
-        return;
+    const unsigned char *sigBuf = reinterpret_cast<const unsigned char *>(sigBytes.get());
+    int err = EVP_DigestVerifyFinal(mdCtx, sigBuf + offset, len);
+    jboolean result;
+    if (err == 1) {
+        result = 1;
+    } else if (err == 0) {
+        result = 0;
+    } else {
+        JNI_TRACE("ctx=%p EVP_DigestVerifyFinal => threw exception", mdCtx);
+        throwExceptionIfNecessary(env, "EVP_DigestVerifyFinal");
+        return 0;
     }
 
-    if (p == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return;
-    }
-
-    int ok = EVP_VerifyUpdate(ctx, p, length);
-    if (ok == 0) {
-        throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyUpdateDirect");
-    }
-}
-
-/*
- * public static native void EVP_VerifyUpdate(long, byte[], int, int)
- */
-static void NativeCrypto_EVP_VerifyUpdate(JNIEnv* env, jclass, jobject ctxRef,
-                                          jbyteArray buffer, jint offset, jint length) {
-    EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, ctxRef);
-    JNI_TRACE("NativeCrypto_EVP_VerifyUpdate(%p, %p, %d, %d)", ctx, buffer, offset, length);
-
-    if (ctx == NULL) {
-        return;
-    } else if (buffer == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return;
-    }
-
-    ScopedByteArrayRO bufferBytes(env, buffer);
-    if (bufferBytes.get() == NULL) {
-        return;
-    }
-    if (ARRAY_OFFSET_LENGTH_INVALID(bufferBytes, offset, length)) {
-        jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
-        return;
-    }
-
-    int ok = EVP_VerifyUpdate(ctx,
-                              reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset),
-                              length);
-    if (ok == 0) {
-        throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyUpdate");
-    }
-}
-
-/*
- * public static native int EVP_VerifyFinal(long, byte[], int, int, long)
- */
-static jint NativeCrypto_EVP_VerifyFinal(JNIEnv* env, jclass, jobject ctxRef, jbyteArray buffer,
-                                        jint offset, jint length, jobject pkeyRef) {
-    JNI_TRACE("NativeCrypto_EVP_VerifyFinal(%p, %p, %d, %d, %p)",
-              ctxRef, buffer, offset, length, pkeyRef);
-    EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, ctxRef);
-    if (ctx == NULL) {
-        return -1;
-    }
-    EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
-    if (pkey == NULL) {
-        return -1;
-    }
-    JNI_TRACE("NativeCrypto_EVP_VerifyFinal(%p, %p, %d, %d, %p) <- ptr",
-              ctx, buffer, offset, length, pkey);
-
-    if (buffer == NULL) {
-        jniThrowNullPointerException(env, "buffer == null");
-        return -1;
-    }
-
-    ScopedByteArrayRO bufferBytes(env, buffer);
-    if (bufferBytes.get() == NULL) {
-        return -1;
-    }
-
-    int ok = EVP_VerifyFinal(ctx,
-                             reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset),
-                             length,
-                             pkey);
-    // The upper (Java language) layer should take care of throwing the
-    // expected exceptions before calling to this, so we just clear
-    // the OpenSSL/BoringSSL error stack here.
-    freeOpenSslErrorState();
-
-    JNI_TRACE("NativeCrypto_EVP_VerifyFinal(%p, %p, %d, %d, %p) => %d",
-              ctx, buffer, offset, length, pkey, ok);
-
-    return ok;
+    JNI_TRACE("EVP_DigestVerifyFinal(%p) => %d", mdCtx, result);
+    return result;
 }
 
 static jlong NativeCrypto_EVP_get_cipherbyname(JNIEnv* env, jclass, jstring algorithm) {
@@ -11242,17 +11168,14 @@
     NATIVE_METHOD(NativeCrypto, EVP_get_digestbyname, "(Ljava/lang/String;)J"),
     NATIVE_METHOD(NativeCrypto, EVP_MD_block_size, "(J)I"),
     NATIVE_METHOD(NativeCrypto, EVP_MD_size, "(J)I"),
-    NATIVE_METHOD(NativeCrypto, EVP_SignInit, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;J)I"),
-    NATIVE_METHOD(NativeCrypto, EVP_SignUpdate, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[BII)V"),
-    NATIVE_METHOD(NativeCrypto, EVP_SignUpdateDirect, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;JI)V"),
-    NATIVE_METHOD(NativeCrypto, EVP_SignFinal, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[BI" REF_EVP_PKEY ")I"),
-    NATIVE_METHOD(NativeCrypto, EVP_VerifyInit, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;J)I"),
-    NATIVE_METHOD(NativeCrypto, EVP_VerifyUpdate, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[BII)V"),
-    NATIVE_METHOD(NativeCrypto, EVP_VerifyUpdateDirect, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;JI)V"),
-    NATIVE_METHOD(NativeCrypto, EVP_VerifyFinal, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[BII" REF_EVP_PKEY ")I"),
-    NATIVE_METHOD(NativeCrypto, EVP_DigestSignInit, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;J" REF_EVP_PKEY ")V"),
-    NATIVE_METHOD(NativeCrypto, EVP_DigestSignUpdate, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[B)V"),
+    NATIVE_METHOD(NativeCrypto, EVP_DigestSignInit, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;J" REF_EVP_PKEY ")J"),
+    NATIVE_METHOD(NativeCrypto, EVP_DigestSignUpdate, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[BII)V"),
+    NATIVE_METHOD(NativeCrypto, EVP_DigestSignUpdateDirect, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;JI)V"),
     NATIVE_METHOD(NativeCrypto, EVP_DigestSignFinal, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;)[B"),
+    NATIVE_METHOD(NativeCrypto, EVP_DigestVerifyInit, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;J" REF_EVP_PKEY ")J"),
+    NATIVE_METHOD(NativeCrypto, EVP_DigestVerifyUpdate, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[BII)V"),
+    NATIVE_METHOD(NativeCrypto, EVP_DigestVerifyUpdateDirect, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;JI)V"),
+    NATIVE_METHOD(NativeCrypto, EVP_DigestVerifyFinal, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[BII)Z"),
     NATIVE_METHOD(NativeCrypto, EVP_get_cipherbyname, "(Ljava/lang/String;)J"),
     NATIVE_METHOD(NativeCrypto, EVP_CipherInit_ex, "(" REF_EVP_CIPHER_CTX "J[B[BZ)V"),
     NATIVE_METHOD(NativeCrypto, EVP_CipherUpdate, "(" REF_EVP_CIPHER_CTX "[BI[BII)I"),
diff --git a/src/test/java/org/conscrypt/NativeCryptoTest.java b/src/test/java/org/conscrypt/NativeCryptoTest.java
index b8f0bf9..bd66c32 100644
--- a/src/test/java/org/conscrypt/NativeCryptoTest.java
+++ b/src/test/java/org/conscrypt/NativeCryptoTest.java
@@ -2645,13 +2645,37 @@
         }
     }
 
-    public void test_EVP_SignInit() throws Exception {
+    public void test_EVP_DigestSignInit() throws Exception {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        kpg.initialize(512);
+
+        KeyPair kp = kpg.generateKeyPair();
+        RSAPrivateCrtKey privKey = (RSAPrivateCrtKey) kp.getPrivate();
+
+        NativeRef.EVP_PKEY pkey;
+        pkey = new NativeRef.EVP_PKEY(NativeCrypto.EVP_PKEY_new_RSA(
+                privKey.getModulus().toByteArray(),
+                privKey.getPublicExponent().toByteArray(),
+                privKey.getPrivateExponent().toByteArray(),
+                privKey.getPrimeP().toByteArray(),
+                privKey.getPrimeQ().toByteArray(),
+                privKey.getPrimeExponentP().toByteArray(),
+                privKey.getPrimeExponentQ().toByteArray(),
+                privKey.getCrtCoefficient().toByteArray()));
+        assertNotNull(pkey);
+
         final NativeRef.EVP_MD_CTX ctx = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
-        assertEquals(1,
-                NativeCrypto.EVP_SignInit(ctx, NativeCrypto.EVP_get_digestbyname("sha256")));
+        long evpMd = NativeCrypto.EVP_get_digestbyname("sha256");
+        NativeCrypto.EVP_DigestSignInit(ctx, evpMd, pkey);
 
         try {
-            NativeCrypto.EVP_SignInit(ctx, 0);
+            NativeCrypto.EVP_DigestSignInit(ctx, 0, pkey);
+            fail();
+        } catch (RuntimeException expected) {
+        }
+
+        try {
+            NativeCrypto.EVP_DigestSignInit(ctx, evpMd, null);
             fail();
         } catch (RuntimeException expected) {
         }