NativeCrypto: allow default exceptions

Re-arrange the exception-throwing code to allow functions to pass in a
default exception type instead of always throwing RuntimeException when
a reason code doesn't match exactly with an exception type.

Bug: 20488918
Change-Id: I557def7bbcfb164d2c781e0303431ff7a7793086
diff --git a/src/main/native/org_conscrypt_NativeCrypto.cpp b/src/main/native/org_conscrypt_NativeCrypto.cpp
index 9590cd6..6c70bbd 100644
--- a/src/main/native/org_conscrypt_NativeCrypto.cpp
+++ b/src/main/native/org_conscrypt_NativeCrypto.cpp
@@ -422,51 +422,52 @@
 /**
  * Throws a OutOfMemoryError with the given string as a message.
  */
-static void jniThrowOutOfMemory(JNIEnv* env, const char* message) {
-    jniThrowException(env, "java/lang/OutOfMemoryError", message);
+static int jniThrowOutOfMemory(JNIEnv* env, const char* message) {
+    return jniThrowException(env, "java/lang/OutOfMemoryError", message);
 }
 
 /**
  * Throws a BadPaddingException with the given string as a message.
  */
-static void throwBadPaddingException(JNIEnv* env, const char* message) {
+static int throwBadPaddingException(JNIEnv* env, const char* message) {
     JNI_TRACE("throwBadPaddingException %s", message);
-    jniThrowException(env, "javax/crypto/BadPaddingException", message);
+    return jniThrowException(env, "javax/crypto/BadPaddingException", message);
 }
 
 /**
  * Throws a SignatureException with the given string as a message.
  */
-static void throwSignatureException(JNIEnv* env, const char* message) {
+static int throwSignatureException(JNIEnv* env, const char* message) {
     JNI_TRACE("throwSignatureException %s", message);
-    jniThrowException(env, "java/security/SignatureException", message);
+    return jniThrowException(env, "java/security/SignatureException", message);
 }
 
 /**
  * Throws a InvalidKeyException with the given string as a message.
  */
-static void throwInvalidKeyException(JNIEnv* env, const char* message) {
+static int throwInvalidKeyException(JNIEnv* env, const char* message) {
     JNI_TRACE("throwInvalidKeyException %s", message);
-    jniThrowException(env, "java/security/InvalidKeyException", message);
+    return jniThrowException(env, "java/security/InvalidKeyException", message);
 }
 
 /**
  * Throws a SignatureException with the given string as a message.
  */
-static void throwIllegalBlockSizeException(JNIEnv* env, const char* message) {
+static int throwIllegalBlockSizeException(JNIEnv* env, const char* message) {
     JNI_TRACE("throwIllegalBlockSizeException %s", message);
-    jniThrowException(env, "javax/crypto/IllegalBlockSizeException", message);
+    return jniThrowException(env, "javax/crypto/IllegalBlockSizeException", message);
 }
 
 /**
  * Throws a NoSuchAlgorithmException with the given string as a message.
  */
-static void throwNoSuchAlgorithmException(JNIEnv* env, const char* message) {
+static int throwNoSuchAlgorithmException(JNIEnv* env, const char* message) {
     JNI_TRACE("throwUnknownAlgorithmException %s", message);
-    jniThrowException(env, "java/security/NoSuchAlgorithmException", message);
+    return jniThrowException(env, "java/security/NoSuchAlgorithmException", message);
 }
 
-static void throwForAsn1Error(JNIEnv* env, int reason, const char *message) {
+static int throwForAsn1Error(JNIEnv* env, int reason, const char *message,
+                             int (*defaultThrow)(JNIEnv*, const char*)) {
     switch (reason) {
     case ASN1_R_UNABLE_TO_DECODE_RSA_KEY:
     case ASN1_R_UNABLE_TO_DECODE_RSA_PRIVATE_KEY:
@@ -475,75 +476,74 @@
     // These #if sections can be removed once BoringSSL is landed.
 #if defined(ASN1_R_WRONG_PUBLIC_KEY_TYPE)
     case ASN1_R_WRONG_PUBLIC_KEY_TYPE:
-        throwInvalidKeyException(env, message);
-        break;
 #endif
+        return throwInvalidKeyException(env, message);
+        break;
 #if defined(ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM)
     case ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM:
-        throwNoSuchAlgorithmException(env, message);
+        return throwNoSuchAlgorithmException(env, message);
         break;
 #endif
-    default:
-        jniThrowRuntimeException(env, message);
-        break;
     }
+    return defaultThrow(env, message);
 }
 
 #if defined(OPENSSL_IS_BORINGSSL)
-static void throwForCipherError(JNIEnv* env, int reason, const char *message) {
+static int throwForCipherError(JNIEnv* env, int reason, const char *message,
+                               int (*defaultThrow)(JNIEnv*, const char*)) {
     switch (reason) {
     case CIPHER_R_BAD_DECRYPT:
-        throwBadPaddingException(env, message);
+        return throwBadPaddingException(env, message);
         break;
     case CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
     case CIPHER_R_WRONG_FINAL_BLOCK_LENGTH:
-        throwIllegalBlockSizeException(env, message);
+        return throwIllegalBlockSizeException(env, message);
         break;
     case CIPHER_R_AES_KEY_SETUP_FAILED:
     case CIPHER_R_BAD_KEY_LENGTH:
     case CIPHER_R_UNSUPPORTED_KEY_SIZE:
-        throwInvalidKeyException(env, message);
-        break;
-    default:
-        jniThrowRuntimeException(env, message);
+        return throwInvalidKeyException(env, message);
         break;
     }
+    return defaultThrow(env, message);
 }
 
-static void throwForEvpError(JNIEnv* env, int reason, const char *message) {
+static int throwForEvpError(JNIEnv* env, int reason, const char *message,
+                            int (*defaultThrow)(JNIEnv*, const char*)) {
     switch (reason) {
     case EVP_R_MISSING_PARAMETERS:
-        throwInvalidKeyException(env, message);
+        return throwInvalidKeyException(env, message);
         break;
     case EVP_R_UNSUPPORTED_ALGORITHM:
     case EVP_R_X931_UNSUPPORTED:
-        throwNoSuchAlgorithmException(env, message);
+        return throwNoSuchAlgorithmException(env, message);
         break;
     // These #if sections can be make unconditional once BoringSSL is landed.
 #if defined(EVP_R_WRONG_PUBLIC_KEY_TYPE)
     case EVP_R_WRONG_PUBLIC_KEY_TYPE:
-        throwInvalidKeyException(env, message);
+        return throwInvalidKeyException(env, message);
         break;
 #endif
 #if defined(EVP_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM)
     case EVP_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM:
-        throwNoSuchAlgorithmException(env, message);
+        return throwNoSuchAlgorithmException(env, message);
         break;
 #endif
     default:
-        jniThrowRuntimeException(env, message);
+        return defaultThrow(env, message);
         break;
     }
 }
 #else
-static void throwForEvpError(JNIEnv* env, int reason, const char *message) {
+static int throwForEvpError(JNIEnv* env, int reason, const char *message,
+                            int (*defaultThrow)(JNIEnv*, const char*)) {
     switch (reason) {
     case EVP_R_BAD_DECRYPT:
-        throwBadPaddingException(env, message);
+        return throwBadPaddingException(env, message);
         break;
     case EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
     case EVP_R_WRONG_FINAL_BLOCK_LENGTH:
-        throwIllegalBlockSizeException(env, message);
+        return throwIllegalBlockSizeException(env, message);
         break;
     case EVP_R_BAD_KEY_LENGTH:
     case EVP_R_BN_DECODE_ERROR:
@@ -552,27 +552,28 @@
     case EVP_R_MISSING_PARAMETERS:
     case EVP_R_UNSUPPORTED_KEY_SIZE:
     case EVP_R_UNSUPPORTED_KEYLENGTH:
-        throwInvalidKeyException(env, message);
+        return throwInvalidKeyException(env, message);
         break;
     case EVP_R_WRONG_PUBLIC_KEY_TYPE:
-        throwSignatureException(env, message);
+        return throwSignatureException(env, message);
         break;
     case EVP_R_UNSUPPORTED_ALGORITHM:
-        throwNoSuchAlgorithmException(env, message);
+        return throwNoSuchAlgorithmException(env, message);
         break;
     default:
-        jniThrowRuntimeException(env, message);
+        return defaultThrow(env, message);
         break;
     }
 }
 #endif
 
-static void throwForRsaError(JNIEnv* env, int reason, const char *message) {
+static int throwForRsaError(JNIEnv* env, int reason, const char *message,
+                            int (*defaultThrow)(JNIEnv*, const char*)) {
     switch (reason) {
     case RSA_R_BLOCK_TYPE_IS_NOT_01:
     case RSA_R_BLOCK_TYPE_IS_NOT_02:
     case RSA_R_PKCS_DECODING_ERROR:
-        throwBadPaddingException(env, message);
+        return throwBadPaddingException(env, message);
         break;
     case RSA_R_BAD_SIGNATURE:
     case RSA_R_DATA_TOO_LARGE_FOR_MODULUS:
@@ -582,28 +583,27 @@
     case RSA_R_ALGORITHM_MISMATCH:
     case RSA_R_DATA_GREATER_THAN_MOD_LEN:
 #endif
-        throwSignatureException(env, message);
+        return throwSignatureException(env, message);
         break;
     case RSA_R_UNKNOWN_ALGORITHM_TYPE:
-        throwNoSuchAlgorithmException(env, message);
+        return throwNoSuchAlgorithmException(env, message);
         break;
     case RSA_R_MODULUS_TOO_LARGE:
     case RSA_R_NO_PUBLIC_EXPONENT:
-        throwInvalidKeyException(env, message);
-        break;
-    default:
-        jniThrowRuntimeException(env, message);
+        return throwInvalidKeyException(env, message);
         break;
     }
+    return defaultThrow(env, message);
 }
 
-static void throwForX509Error(JNIEnv* env, int reason, const char *message) {
+static int throwForX509Error(JNIEnv* env, int reason, const char *message,
+                             int (*defaultThrow)(JNIEnv*, const char*)) {
     switch (reason) {
     case X509_R_UNSUPPORTED_ALGORITHM:
-        throwNoSuchAlgorithmException(env, message);
+        return throwNoSuchAlgorithmException(env, message);
         break;
     default:
-        jniThrowRuntimeException(env, message);
+        return defaultThrow(env, message);
         break;
     }
 }
@@ -614,7 +614,8 @@
  *
  * @return true if an exception was thrown, false if not.
  */
-static bool throwExceptionIfNecessary(JNIEnv* env, const char* location  __attribute__ ((unused))) {
+static bool throwExceptionIfNecessary(JNIEnv* env, const char* location  __attribute__ ((unused)),
+        int (*defaultThrow)(JNIEnv*, const char*) = jniThrowRuntimeException) {
     const char* file;
     int line;
     const char* data;
@@ -632,27 +633,27 @@
                   (flags & ERR_TXT_STRING) ? data : "(no data)");
         switch (library) {
         case ERR_LIB_RSA:
-            throwForRsaError(env, reason, message);
+            throwForRsaError(env, reason, message, defaultThrow);
             break;
         case ERR_LIB_ASN1:
-            throwForAsn1Error(env, reason, message);
+            throwForAsn1Error(env, reason, message, defaultThrow);
             break;
 #if defined(OPENSSL_IS_BORINGSSL)
         case ERR_LIB_CIPHER:
-            throwForCipherError(env, reason, message);
+            throwForCipherError(env, reason, message, defaultThrow);
             break;
 #endif
         case ERR_LIB_EVP:
-            throwForEvpError(env, reason, message);
+            throwForEvpError(env, reason, message, defaultThrow);
             break;
         case ERR_LIB_X509:
-            throwForX509Error(env, reason, message);
+            throwForX509Error(env, reason, message, defaultThrow);
             break;
         case ERR_LIB_DSA:
             throwInvalidKeyException(env, message);
             break;
         default:
-            jniThrowRuntimeException(env, message);
+            defaultThrow(env, message);
             break;
         }
         result = true;
@@ -665,33 +666,33 @@
 /**
  * Throws an SocketTimeoutException with the given string as a message.
  */
-static void throwSocketTimeoutException(JNIEnv* env, const char* message) {
+static int throwSocketTimeoutException(JNIEnv* env, const char* message) {
     JNI_TRACE("throwSocketTimeoutException %s", message);
-    jniThrowException(env, "java/net/SocketTimeoutException", message);
+    return jniThrowException(env, "java/net/SocketTimeoutException", message);
 }
 
 /**
  * Throws a javax.net.ssl.SSLException with the given string as a message.
  */
-static void throwSSLHandshakeExceptionStr(JNIEnv* env, const char* message) {
+static int throwSSLHandshakeExceptionStr(JNIEnv* env, const char* message) {
     JNI_TRACE("throwSSLExceptionStr %s", message);
-    jniThrowException(env, "javax/net/ssl/SSLHandshakeException", message);
+    return jniThrowException(env, "javax/net/ssl/SSLHandshakeException", message);
 }
 
 /**
  * Throws a javax.net.ssl.SSLException with the given string as a message.
  */
-static void throwSSLExceptionStr(JNIEnv* env, const char* message) {
+static int throwSSLExceptionStr(JNIEnv* env, const char* message) {
     JNI_TRACE("throwSSLExceptionStr %s", message);
-    jniThrowException(env, "javax/net/ssl/SSLException", message);
+    return jniThrowException(env, "javax/net/ssl/SSLException", message);
 }
 
 /**
  * Throws a javax.net.ssl.SSLProcotolException with the given string as a message.
  */
-static void throwSSLProtocolExceptionStr(JNIEnv* env, const char* message) {
+static int throwSSLProtocolExceptionStr(JNIEnv* env, const char* message) {
     JNI_TRACE("throwSSLProtocolExceptionStr %s", message);
-    jniThrowException(env, "javax/net/ssl/SSLProtocolException", message);
+    return jniThrowException(env, "javax/net/ssl/SSLProtocolException", message);
 }
 
 /**
@@ -704,8 +705,8 @@
  * SSL_ERROR_NONE to probe with ERR_get_error
  * @param message null-ok; general error message
  */
-static void throwSSLExceptionWithSslErrors(JNIEnv* env, SSL* ssl, int sslErrorCode,
-        const char* message, void (*actualThrow)(JNIEnv*, const char*) = throwSSLExceptionStr) {
+static int throwSSLExceptionWithSslErrors(JNIEnv* env, SSL* ssl, int sslErrorCode,
+        const char* message, int (*actualThrow)(JNIEnv*, const char*) = throwSSLExceptionStr) {
 
     if (message == NULL) {
         message = "SSL error";
@@ -753,10 +754,10 @@
     char* str;
     if (asprintf(&str, "%s: ssl=%p: %s", message, ssl, sslErrorStr) <= 0) {
         // problem with asprintf, just throw argument message, log everything
-        actualThrow(env, message);
+        int ret = actualThrow(env, message);
         ALOGV("%s: ssl=%p: %s", message, ssl, sslErrorStr);
         freeOpenSslErrorState();
-        return;
+        return ret;
     }
 
     char* allocStr = str;
@@ -806,15 +807,17 @@
         }
     }
 
+    int ret;
     if (sslErrorCode == SSL_ERROR_SSL) {
-        throwSSLProtocolExceptionStr(env, allocStr);
+        ret = throwSSLProtocolExceptionStr(env, allocStr);
     } else {
-        actualThrow(env, allocStr);
+        ret = actualThrow(env, allocStr);
     }
 
     ALOGV("%s", allocStr);
     free(allocStr);
     freeOpenSslErrorState();
+    return ret;
 }
 
 /**