HELLO·Android
系统源代码
IT资讯
技术文章
我的收藏
注册
登录
-
我收藏的文章
创建代码块
我的代码块
我的账号
Nougat 7.0
|
7.0.0_r31
下载
查看原文件
收藏
根目录
external
conscrypt
src
main
native
org_conscrypt_NativeCrypto.cpp
/* * Copyright (C) 2007-2008 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. */ /** * Native glue for Java class org.conscrypt.NativeCrypto */ #define TO_STRING1(x) #x #define TO_STRING(x) TO_STRING1(x) #ifndef JNI_JARJAR_PREFIX #ifndef CONSCRYPT_NOT_UNBUNDLED #define CONSCRYPT_UNBUNDLED #endif #define JNI_JARJAR_PREFIX #endif #define LOG_TAG "NativeCrypto" #include
#include
#include
#include
#include
#include
#include
#ifdef CONSCRYPT_UNBUNDLED #include
#endif #include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(OPENSSL_IS_BORINGSSL) #include
#endif #if !defined(OPENSSL_IS_BORINGSSL) #if defined(GOOGLE_INTERNAL) #include "third_party/openssl/openssl/src/crypto/ecdsa/ecs_locl.h" #else #include "crypto/ecdsa/ecs_locl.h" #endif #endif #ifndef CONSCRYPT_UNBUNDLED /* If we're compiled unbundled from Android system image, we use the * CompatibilityCloseMonitor */ #include "AsynchronousCloseMonitor.h" #endif #ifndef CONSCRYPT_UNBUNDLED #include "cutils/log.h" #else #include "log_compat.h" #endif #ifndef CONSCRYPT_UNBUNDLED #include "JNIHelp.h" #include "JniConstants.h" #include "JniException.h" #else #define NATIVE_METHOD(className, functionName, signature) \ { (char*) #functionName, (char*) signature, reinterpret_cast
(className ## _ ## functionName) } #define REGISTER_NATIVE_METHODS(jni_class_name) \ RegisterNativeMethods(env, jni_class_name, gMethods, arraysize(gMethods)) #endif #include "ScopedLocalRef.h" #include "ScopedPrimitiveArray.h" #include "ScopedUtfChars.h" #include "UniquePtr.h" #include "NetFd.h" #include "macros.h" #undef WITH_JNI_TRACE #undef WITH_JNI_TRACE_MD #undef WITH_JNI_TRACE_DATA /* * How to use this for debugging with Wireshark: * * 1. Pull lines from logcat to a file that looks like (without quotes): * "RSA Session-ID:... Master-Key:..."
* "RSA Session-ID:... Master-Key:..."
*
* 2. Start Wireshark * 3. Go to Edit -> Preferences -> SSL -> (Pre-)Master-Key log and fill in * the file you put the lines in above. * 4. Follow the stream that corresponds to the desired "Session-ID" in * the Server Hello. */ #undef WITH_JNI_TRACE_KEYS #ifdef WITH_JNI_TRACE #define JNI_TRACE(...) \ ((void)ALOG(LOG_INFO, LOG_TAG "-jni", __VA_ARGS__)) #else #define JNI_TRACE(...) ((void)0) #endif #ifdef WITH_JNI_TRACE_MD #define JNI_TRACE_MD(...) \ ((void)ALOG(LOG_INFO, LOG_TAG "-jni", __VA_ARGS__)); #else #define JNI_TRACE_MD(...) ((void)0) #endif // don't overwhelm logcat #define WITH_JNI_TRACE_DATA_CHUNK_SIZE 512 static JavaVM* gJavaVM; static jclass cryptoUpcallsClass; static jclass openSslInputStreamClass; static jclass nativeRefClass; static jclass byteArrayClass; static jclass calendarClass; static jclass objectClass; static jclass objectArrayClass; static jclass integerClass; static jclass inputStreamClass; static jclass outputStreamClass; static jclass stringClass; static jfieldID nativeRef_context; static jmethodID calendar_setMethod; static jmethodID inputStream_readMethod; static jmethodID integer_valueOfMethod; static jmethodID openSslInputStream_readLineMethod; static jmethodID outputStream_writeMethod; static jmethodID outputStream_flushMethod; struct OPENSSL_Delete { void operator()(void* p) const { OPENSSL_free(p); } }; typedef UniquePtr
Unique_OPENSSL_str; struct BIO_Delete { void operator()(BIO* p) const { BIO_free_all(p); } }; typedef UniquePtr
Unique_BIO; struct BIGNUM_Delete { void operator()(BIGNUM* p) const { BN_free(p); } }; typedef UniquePtr
Unique_BIGNUM; struct BN_CTX_Delete { void operator()(BN_CTX* ctx) const { BN_CTX_free(ctx); } }; typedef UniquePtr
Unique_BN_CTX; struct ASN1_INTEGER_Delete { void operator()(ASN1_INTEGER* p) const { ASN1_INTEGER_free(p); } }; typedef UniquePtr
Unique_ASN1_INTEGER; struct DSA_Delete { void operator()(DSA* p) const { DSA_free(p); } }; typedef UniquePtr
Unique_DSA; struct EC_GROUP_Delete { void operator()(EC_GROUP* p) const { EC_GROUP_free(p); } }; typedef UniquePtr
Unique_EC_GROUP; struct EC_POINT_Delete { void operator()(EC_POINT* p) const { EC_POINT_clear_free(p); } }; typedef UniquePtr
Unique_EC_POINT; struct EC_KEY_Delete { void operator()(EC_KEY* p) const { EC_KEY_free(p); } }; typedef UniquePtr
Unique_EC_KEY; struct EVP_MD_CTX_Delete { void operator()(EVP_MD_CTX* p) const { EVP_MD_CTX_destroy(p); } }; typedef UniquePtr
Unique_EVP_MD_CTX; #if defined(OPENSSL_IS_BORINGSSL) struct EVP_AEAD_CTX_Delete { void operator()(EVP_AEAD_CTX* p) const { EVP_AEAD_CTX_cleanup(p); delete p; } }; typedef UniquePtr
Unique_EVP_AEAD_CTX; #endif struct EVP_CIPHER_CTX_Delete { void operator()(EVP_CIPHER_CTX* p) const { EVP_CIPHER_CTX_free(p); } }; typedef UniquePtr
Unique_EVP_CIPHER_CTX; struct EVP_PKEY_Delete { void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); } }; typedef UniquePtr
Unique_EVP_PKEY; struct PKCS8_PRIV_KEY_INFO_Delete { void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); } }; typedef UniquePtr
Unique_PKCS8_PRIV_KEY_INFO; struct RSA_Delete { void operator()(RSA* p) const { RSA_free(p); } }; typedef UniquePtr
Unique_RSA; struct ASN1_BIT_STRING_Delete { void operator()(ASN1_BIT_STRING* p) const { ASN1_BIT_STRING_free(p); } }; typedef UniquePtr
Unique_ASN1_BIT_STRING; struct ASN1_OBJECT_Delete { void operator()(ASN1_OBJECT* p) const { ASN1_OBJECT_free(p); } }; typedef UniquePtr
Unique_ASN1_OBJECT; struct ASN1_GENERALIZEDTIME_Delete { void operator()(ASN1_GENERALIZEDTIME* p) const { ASN1_GENERALIZEDTIME_free(p); } }; typedef UniquePtr
Unique_ASN1_GENERALIZEDTIME; struct SSL_Delete { void operator()(SSL* p) const { SSL_free(p); } }; typedef UniquePtr
Unique_SSL; struct SSL_CTX_Delete { void operator()(SSL_CTX* p) const { SSL_CTX_free(p); } }; typedef UniquePtr
Unique_SSL_CTX; struct X509_Delete { void operator()(X509* p) const { X509_free(p); } }; typedef UniquePtr
Unique_X509; struct X509_NAME_Delete { void operator()(X509_NAME* p) const { X509_NAME_free(p); } }; typedef UniquePtr
Unique_X509_NAME; struct X509_EXTENSIONS_Delete { void operator()(X509_EXTENSIONS* p) const { sk_X509_EXTENSION_pop_free(p, X509_EXTENSION_free); } }; typedef UniquePtr
Unique_X509_EXTENSIONS; #if !defined(OPENSSL_IS_BORINGSSL) struct PKCS7_Delete { void operator()(PKCS7* p) const { PKCS7_free(p); } }; typedef UniquePtr
Unique_PKCS7; #endif struct sk_SSL_CIPHER_Delete { void operator()(STACK_OF(SSL_CIPHER)* p) const { // We don't own SSL_CIPHER references, so no need for pop_free sk_SSL_CIPHER_free(p); } }; typedef UniquePtr
Unique_sk_SSL_CIPHER; struct sk_X509_Delete { void operator()(STACK_OF(X509)* p) const { sk_X509_pop_free(p, X509_free); } }; typedef UniquePtr
Unique_sk_X509; #if defined(OPENSSL_IS_BORINGSSL) struct sk_X509_CRL_Delete { void operator()(STACK_OF(X509_CRL)* p) const { sk_X509_CRL_pop_free(p, X509_CRL_free); } }; typedef UniquePtr
Unique_sk_X509_CRL; #endif struct sk_X509_NAME_Delete { void operator()(STACK_OF(X509_NAME)* p) const { sk_X509_NAME_pop_free(p, X509_NAME_free); } }; typedef UniquePtr
Unique_sk_X509_NAME; struct sk_ASN1_OBJECT_Delete { void operator()(STACK_OF(ASN1_OBJECT)* p) const { sk_ASN1_OBJECT_pop_free(p, ASN1_OBJECT_free); } }; typedef UniquePtr
Unique_sk_ASN1_OBJECT; struct sk_GENERAL_NAME_Delete { void operator()(STACK_OF(GENERAL_NAME)* p) const { sk_GENERAL_NAME_pop_free(p, GENERAL_NAME_free); } }; typedef UniquePtr
Unique_sk_GENERAL_NAME; /** * Many OpenSSL APIs take ownership of an argument on success but don't free the argument * on failure. This means we need to tell our scoped pointers when we've transferred ownership, * without triggering a warning by not using the result of release(). */ #define OWNERSHIP_TRANSFERRED(obj) \ do { typeof (obj.release()) _dummy __attribute__((unused)) = obj.release(); } while(0) /** * UNUSED_ARGUMENT can be used to mark an, otherwise unused, argument as "used" * for the purposes of -Werror=unused-parameter. This can be needed when an * argument's use is based on an #ifdef. */ #define UNUSED_ARGUMENT(x) ((void)(x)); /** * Check array bounds for arguments when an array and offset are given. */ #define ARRAY_OFFSET_INVALID(array, offset) (offset < 0 || \ offset > static_cast
(array.size())) /** * Check array bounds for arguments when an array, offset, and length are given. */ #define ARRAY_OFFSET_LENGTH_INVALID(array, offset, len) (offset < 0 || \ offset > static_cast
(array.size()) || len < 0 || \ len > static_cast
(array.size()) - offset) /** * Frees the SSL error state. * * OpenSSL keeps an "error stack" per thread, and given that this code * can be called from arbitrary threads that we don't keep track of, * we err on the side of freeing the error state promptly (instead of, * say, at thread death). */ static void freeOpenSslErrorState(void) { ERR_clear_error(); ERR_remove_thread_state(nullptr); } /** * Manages the freeing of the OpenSSL error stack. This allows you to * instantiate this object during an SSL call that may fail and not worry * about manually calling freeOpenSslErrorState() later. * * As an optimization, you can also call .release() for passing as an * argument to things that free the error stack state as a side-effect. */ class OpenSslError { public: OpenSslError() : sslError_(SSL_ERROR_NONE), released_(false) { } OpenSslError(SSL* ssl, int returnCode) : sslError_(SSL_ERROR_NONE), released_(false) { reset(ssl, returnCode); } ~OpenSslError() { if (!released_ && sslError_ != SSL_ERROR_NONE) { freeOpenSslErrorState(); } } int get() const { return sslError_; } void reset(SSL* ssl, int returnCode) { if (returnCode <= 0) { sslError_ = SSL_get_error(ssl, returnCode); } else { sslError_ = SSL_ERROR_NONE; } } int release() { released_ = true; return sslError_; } private: int sslError_; bool released_; }; /** * Throws a OutOfMemoryError with the given string as a 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 int throwBadPaddingException(JNIEnv* env, const char* message) { JNI_TRACE("throwBadPaddingException %s", message); return jniThrowException(env, "javax/crypto/BadPaddingException", message); } /** * Throws a SignatureException with the given string as a message. */ static int throwSignatureException(JNIEnv* env, const char* message) { JNI_TRACE("throwSignatureException %s", message); return jniThrowException(env, "java/security/SignatureException", message); } /** * Throws a InvalidKeyException with the given string as a message. */ static int throwInvalidKeyException(JNIEnv* env, const char* message) { JNI_TRACE("throwInvalidKeyException %s", message); return jniThrowException(env, "java/security/InvalidKeyException", message); } /** * Throws a SignatureException with the given string as a message. */ static int throwIllegalBlockSizeException(JNIEnv* env, const char* message) { JNI_TRACE("throwIllegalBlockSizeException %s", message); return jniThrowException(env, "javax/crypto/IllegalBlockSizeException", message); } /** * Throws a NoSuchAlgorithmException with the given string as a message. */ static int throwNoSuchAlgorithmException(JNIEnv* env, const char* message) { JNI_TRACE("throwUnknownAlgorithmException %s", message); return jniThrowException(env, "java/security/NoSuchAlgorithmException", message); } /** * Throws an IOException with the given string as a message. */ static int throwIOException(JNIEnv* env, const char* message) { JNI_TRACE("throwIOException %s", message); return jniThrowException(env, "java/io/IOException", message); } #if defined(OPENSSL_IS_BORINGSSL) /** * Throws a ParsingException with the given string as a message. */ static int throwParsingException(JNIEnv* env, const char* message) { return jniThrowException( env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLX509CertificateFactory$ParsingException", message); } #endif static int throwForAsn1Error(JNIEnv* env, int reason, const char *message, int (*defaultThrow)(JNIEnv*, const char*)) { switch (reason) { case ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE: #if defined(ASN1_R_UNABLE_TO_DECODE_RSA_KEY) case ASN1_R_UNABLE_TO_DECODE_RSA_KEY: #endif #if defined(ASN1_R_WRONG_PUBLIC_KEY_TYPE) case ASN1_R_WRONG_PUBLIC_KEY_TYPE: #endif #if defined(ASN1_R_UNABLE_TO_DECODE_RSA_PRIVATE_KEY) case ASN1_R_UNABLE_TO_DECODE_RSA_PRIVATE_KEY: #endif #if defined(ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE) case ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE: #endif return throwInvalidKeyException(env, message); break; #if defined(ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM) case ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM: return throwNoSuchAlgorithmException(env, message); break; #endif } return defaultThrow(env, message); } #if defined(OPENSSL_IS_BORINGSSL) static int throwForCipherError(JNIEnv* env, int reason, const char *message, int (*defaultThrow)(JNIEnv*, const char*)) { switch (reason) { case CIPHER_R_BAD_DECRYPT: return throwBadPaddingException(env, message); break; case CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: case CIPHER_R_WRONG_FINAL_BLOCK_LENGTH: return throwIllegalBlockSizeException(env, message); break; case CIPHER_R_AES_KEY_SETUP_FAILED: case CIPHER_R_BAD_KEY_LENGTH: case CIPHER_R_UNSUPPORTED_KEY_SIZE: return throwInvalidKeyException(env, message); break; } return defaultThrow(env, message); } static int throwForEvpError(JNIEnv* env, int reason, const char *message, int (*defaultThrow)(JNIEnv*, const char*)) { switch (reason) { case EVP_R_MISSING_PARAMETERS: return throwInvalidKeyException(env, message); break; case EVP_R_UNSUPPORTED_ALGORITHM: #if defined(EVP_R_X931_UNSUPPORTED) case EVP_R_X931_UNSUPPORTED: #endif return throwNoSuchAlgorithmException(env, message); break; #if defined(EVP_R_WRONG_PUBLIC_KEY_TYPE) case EVP_R_WRONG_PUBLIC_KEY_TYPE: return throwInvalidKeyException(env, message); break; #endif #if defined(EVP_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM) case EVP_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM: return throwNoSuchAlgorithmException(env, message); break; #endif default: return defaultThrow(env, message); break; } } #else static int throwForEvpError(JNIEnv* env, int reason, const char *message, int (*defaultThrow)(JNIEnv*, const char*)) { switch (reason) { case EVP_R_BAD_DECRYPT: return throwBadPaddingException(env, message); break; case EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: case EVP_R_WRONG_FINAL_BLOCK_LENGTH: return throwIllegalBlockSizeException(env, message); break; case EVP_R_BAD_KEY_LENGTH: case EVP_R_BN_DECODE_ERROR: case EVP_R_BN_PUBKEY_ERROR: case EVP_R_INVALID_KEY_LENGTH: case EVP_R_MISSING_PARAMETERS: case EVP_R_UNSUPPORTED_KEY_SIZE: case EVP_R_UNSUPPORTED_KEYLENGTH: return throwInvalidKeyException(env, message); break; case EVP_R_WRONG_PUBLIC_KEY_TYPE: return throwSignatureException(env, message); break; case EVP_R_UNSUPPORTED_ALGORITHM: return throwNoSuchAlgorithmException(env, message); break; default: return defaultThrow(env, message); break; } } #endif 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_PKCS_DECODING_ERROR: #if defined(RSA_R_BLOCK_TYPE_IS_NOT_02) case RSA_R_BLOCK_TYPE_IS_NOT_02: #endif return throwBadPaddingException(env, message); break; case RSA_R_BAD_SIGNATURE: case RSA_R_DATA_TOO_LARGE_FOR_MODULUS: case RSA_R_INVALID_MESSAGE_LENGTH: case RSA_R_WRONG_SIGNATURE_LENGTH: #if !defined(OPENSSL_IS_BORINGSSL) case RSA_R_ALGORITHM_MISMATCH: case RSA_R_DATA_GREATER_THAN_MOD_LEN: #endif return throwSignatureException(env, message); break; case RSA_R_UNKNOWN_ALGORITHM_TYPE: return throwNoSuchAlgorithmException(env, message); break; case RSA_R_MODULUS_TOO_LARGE: case RSA_R_NO_PUBLIC_EXPONENT: return throwInvalidKeyException(env, message); break; } return defaultThrow(env, message); } static int throwForX509Error(JNIEnv* env, int reason, const char *message, int (*defaultThrow)(JNIEnv*, const char*)) { switch (reason) { case X509_R_UNSUPPORTED_ALGORITHM: return throwNoSuchAlgorithmException(env, message); break; default: return defaultThrow(env, message); break; } } /* * Checks this thread's OpenSSL error queue and throws a RuntimeException if * necessary. * * @return true if an exception was thrown, false if not. */ static bool throwExceptionIfNecessary(JNIEnv* env, const char* location __attribute__ ((unused)), int (*defaultThrow)(JNIEnv*, const char*) = jniThrowRuntimeException) { const char* file; int line; const char* data; int flags; unsigned long error = ERR_get_error_line_data(&file, &line, &data, &flags); int result = false; if (error != 0) { char message[256]; ERR_error_string_n(error, message, sizeof(message)); int library = ERR_GET_LIB(error); int reason = ERR_GET_REASON(error); JNI_TRACE("OpenSSL error in %s error=%lx library=%x reason=%x (%s:%d): %s %s", location, error, library, reason, file, line, message, (flags & ERR_TXT_STRING) ? data : "(no data)"); switch (library) { case ERR_LIB_RSA: throwForRsaError(env, reason, message, defaultThrow); break; case ERR_LIB_ASN1: throwForAsn1Error(env, reason, message, defaultThrow); break; #if defined(OPENSSL_IS_BORINGSSL) case ERR_LIB_CIPHER: throwForCipherError(env, reason, message, defaultThrow); break; #endif case ERR_LIB_EVP: throwForEvpError(env, reason, message, defaultThrow); break; case ERR_LIB_X509: throwForX509Error(env, reason, message, defaultThrow); break; case ERR_LIB_DSA: throwInvalidKeyException(env, message); break; default: defaultThrow(env, message); break; } result = true; } freeOpenSslErrorState(); return result; } /** * Throws an SocketTimeoutException with the given string as a message. */ static int throwSocketTimeoutException(JNIEnv* env, const char* message) { JNI_TRACE("throwSocketTimeoutException %s", message); return jniThrowException(env, "java/net/SocketTimeoutException", message); } /** * Throws a javax.net.ssl.SSLException with the given string as a message. */ static int throwSSLHandshakeExceptionStr(JNIEnv* env, const char* message) { JNI_TRACE("throwSSLExceptionStr %s", message); return jniThrowException(env, "javax/net/ssl/SSLHandshakeException", message); } /** * Throws a javax.net.ssl.SSLException with the given string as a message. */ static int throwSSLExceptionStr(JNIEnv* env, const char* message) { JNI_TRACE("throwSSLExceptionStr %s", message); return jniThrowException(env, "javax/net/ssl/SSLException", message); } /** * Throws a javax.net.ssl.SSLProcotolException with the given string as a message. */ static int throwSSLProtocolExceptionStr(JNIEnv* env, const char* message) { JNI_TRACE("throwSSLProtocolExceptionStr %s", message); return jniThrowException(env, "javax/net/ssl/SSLProtocolException", message); } /** * Throws an SSLException with a message constructed from the current * SSL errors. This will also log the errors. * * @param env the JNI environment * @param ssl the possibly NULL SSL * @param sslErrorCode error code returned from SSL_get_error() or * SSL_ERROR_NONE to probe with ERR_get_error * @param message null-ok; general error message */ static int throwSSLExceptionWithSslErrors(JNIEnv* env, SSL* ssl, int sslErrorCode, const char* message, int (*actualThrow)(JNIEnv*, const char*) = throwSSLExceptionStr) { if (message == nullptr) { message = "SSL error"; } // First consult the SSL error code for the general message. const char* sslErrorStr = nullptr; switch (sslErrorCode) { case SSL_ERROR_NONE: if (ERR_peek_error() == 0) { sslErrorStr = "OK"; } else { sslErrorStr = ""; } break; case SSL_ERROR_SSL: sslErrorStr = "Failure in SSL library, usually a protocol error"; break; case SSL_ERROR_WANT_READ: sslErrorStr = "SSL_ERROR_WANT_READ occurred. You should never see this."; break; case SSL_ERROR_WANT_WRITE: sslErrorStr = "SSL_ERROR_WANT_WRITE occurred. You should never see this."; break; case SSL_ERROR_WANT_X509_LOOKUP: sslErrorStr = "SSL_ERROR_WANT_X509_LOOKUP occurred. You should never see this."; break; case SSL_ERROR_SYSCALL: sslErrorStr = "I/O error during system call"; break; case SSL_ERROR_ZERO_RETURN: sslErrorStr = "SSL_ERROR_ZERO_RETURN occurred. You should never see this."; break; case SSL_ERROR_WANT_CONNECT: sslErrorStr = "SSL_ERROR_WANT_CONNECT occurred. You should never see this."; break; case SSL_ERROR_WANT_ACCEPT: sslErrorStr = "SSL_ERROR_WANT_ACCEPT occurred. You should never see this."; break; default: sslErrorStr = "Unknown SSL error"; } // Prepend either our explicit message or a default one. char* str; if (asprintf(&str, "%s: ssl=%p: %s", message, ssl, sslErrorStr) <= 0) { // problem with asprintf, just throw argument message, log everything int ret = actualThrow(env, message); ALOGV("%s: ssl=%p: %s", message, ssl, sslErrorStr); freeOpenSslErrorState(); return ret; } char* allocStr = str; // For protocol errors, SSL might have more information. if (sslErrorCode == SSL_ERROR_NONE || sslErrorCode == SSL_ERROR_SSL) { // Append each error as an additional line to the message. for (;;) { char errStr[256]; const char* file; int line; const char* data; int flags; unsigned long err = ERR_get_error_line_data(&file, &line, &data, &flags); if (err == 0) { break; } ERR_error_string_n(err, errStr, sizeof(errStr)); int ret = asprintf(&str, "%s\n%s (%s:%d %p:0x%08x)", (allocStr == nullptr) ? "" : allocStr, errStr, file, line, (flags & ERR_TXT_STRING) ? data : "(no data)", flags); if (ret < 0) { break; } free(allocStr); allocStr = str; } // For errors during system calls, errno might be our friend. } else if (sslErrorCode == SSL_ERROR_SYSCALL) { if (asprintf(&str, "%s, %s", allocStr, strerror(errno)) >= 0) { free(allocStr); allocStr = str; } // If the error code is invalid, print it. } else if (sslErrorCode > SSL_ERROR_WANT_ACCEPT) { if (asprintf(&str, ", error code is %d", sslErrorCode) >= 0) { free(allocStr); allocStr = str; } } int ret; if (sslErrorCode == SSL_ERROR_SSL) { ret = throwSSLProtocolExceptionStr(env, allocStr); } else { ret = actualThrow(env, allocStr); } ALOGV("%s", allocStr); free(allocStr); freeOpenSslErrorState(); return ret; } /** * Helper function that grabs the casts an ssl pointer and then checks for nullness. * If this function returns NULL and
throwIfNull
is * passed as
true
, then this function will call *
throwSSLExceptionStr
before returning, so in this case of * NULL, a caller of this function should simply return and allow JNI * to do its thing. * * @param env the JNI environment * @param ssl_address; the ssl_address pointer as an integer * @param throwIfNull whether to throw if the SSL pointer is NULL * @returns the pointer, which may be NULL */ static SSL_CTX* to_SSL_CTX(JNIEnv* env, jlong ssl_ctx_address, bool throwIfNull) { SSL_CTX* ssl_ctx = reinterpret_cast
(static_cast
(ssl_ctx_address)); if ((ssl_ctx == nullptr) && throwIfNull) { JNI_TRACE("ssl_ctx == null"); jniThrowNullPointerException(env, "ssl_ctx == null"); } return ssl_ctx; } static SSL* to_SSL(JNIEnv* env, jlong ssl_address, bool throwIfNull) { SSL* ssl = reinterpret_cast
(static_cast
(ssl_address)); if ((ssl == nullptr) && throwIfNull) { JNI_TRACE("ssl == null"); jniThrowNullPointerException(env, "ssl == null"); } return ssl; } static SSL_SESSION* to_SSL_SESSION(JNIEnv* env, jlong ssl_session_address, bool throwIfNull) { SSL_SESSION* ssl_session = reinterpret_cast
(static_cast
(ssl_session_address)); if ((ssl_session == nullptr) && throwIfNull) { JNI_TRACE("ssl_session == null"); jniThrowNullPointerException(env, "ssl_session == null"); } return ssl_session; } static SSL_CIPHER* to_SSL_CIPHER(JNIEnv* env, jlong ssl_cipher_address, bool throwIfNull) { SSL_CIPHER* ssl_cipher = reinterpret_cast
(static_cast
(ssl_cipher_address)); if ((ssl_cipher == nullptr) && throwIfNull) { JNI_TRACE("ssl_cipher == null"); jniThrowNullPointerException(env, "ssl_cipher == null"); } return ssl_cipher; } template
static T* fromContextObject(JNIEnv* env, jobject contextObject) { if (contextObject == nullptr) { JNI_TRACE("contextObject == null"); jniThrowNullPointerException(env, "contextObject == null"); return nullptr; } T* ref = reinterpret_cast
(env->GetLongField(contextObject, nativeRef_context)); if (ref == nullptr) { JNI_TRACE("ref == null"); jniThrowNullPointerException(env, "ref == null"); return nullptr; } return ref; } /** * Converts a Java byte[] two's complement to an OpenSSL BIGNUM. This will * allocate the BIGNUM if *dest == NULL. Returns true on success. If the * return value is false, there is a pending exception. */ static bool arrayToBignum(JNIEnv* env, jbyteArray source, BIGNUM** dest) { JNI_TRACE("arrayToBignum(%p, %p)", source, dest); if (dest == nullptr) { JNI_TRACE("arrayToBignum(%p, %p) => dest is null!", source, dest); jniThrowNullPointerException(env, "dest == null"); return false; } JNI_TRACE("arrayToBignum(%p, %p) *dest == %p", source, dest, *dest); ScopedByteArrayRO sourceBytes(env, source); if (sourceBytes.get() == nullptr) { JNI_TRACE("arrayToBignum(%p, %p) => NULL", source, dest); return false; } const unsigned char* tmp = reinterpret_cast
(sourceBytes.get()); size_t tmpSize = sourceBytes.size(); /* if the array is empty, it is zero. */ if (tmpSize == 0) { if (*dest == nullptr) { *dest = BN_new(); } BN_zero(*dest); return true; } UniquePtr
twosComplement; bool negative = (tmp[0] & 0x80) != 0; if (negative) { // Need to convert to two's complement. twosComplement.reset(new unsigned char[tmpSize]); unsigned char* twosBytes = reinterpret_cast
(twosComplement.get()); memcpy(twosBytes, tmp, tmpSize); tmp = twosBytes; bool carry = true; for (ssize_t i = tmpSize - 1; i >= 0; i--) { twosBytes[i] ^= 0xFF; if (carry) { carry = (++twosBytes[i]) == 0; } } } BIGNUM *ret = BN_bin2bn(tmp, tmpSize, *dest); if (ret == nullptr) { jniThrowRuntimeException(env, "Conversion to BIGNUM failed"); JNI_TRACE("arrayToBignum(%p, %p) => threw exception", source, dest); return false; } BN_set_negative(ret, negative ? 1 : 0); *dest = ret; JNI_TRACE("arrayToBignum(%p, %p) => *dest = %p", source, dest, ret); return true; } #if defined(OPENSSL_IS_BORINGSSL) /** * arrayToBignumSize sets |*out_size| to the size of the big-endian number * contained in |source|. It returns true on success and sets an exception and * returns false otherwise. */ static bool arrayToBignumSize(JNIEnv* env, jbyteArray source, size_t* out_size) { JNI_TRACE("arrayToBignumSize(%p, %p)", source, out_size); ScopedByteArrayRO sourceBytes(env, source); if (sourceBytes.get() == nullptr) { JNI_TRACE("arrayToBignum(%p, %p) => NULL", source, out_size); return false; } const uint8_t* tmp = reinterpret_cast
(sourceBytes.get()); size_t tmpSize = sourceBytes.size(); if (tmpSize == 0) { *out_size = 0; return true; } if ((tmp[0] & 0x80) != 0) { // Negative numbers are invalid. jniThrowRuntimeException(env, "Negative number"); return false; } while (tmpSize > 0 && tmp[0] == 0) { tmp++; tmpSize--; } *out_size = tmpSize; return true; } #endif /** * Converts an OpenSSL BIGNUM to a Java byte[] array in two's complement. */ static jbyteArray bignumToArray(JNIEnv* env, const BIGNUM* source, const char* sourceName) { JNI_TRACE("bignumToArray(%p, %s)", source, sourceName); if (source == nullptr) { jniThrowNullPointerException(env, sourceName); return nullptr; } size_t numBytes = BN_num_bytes(source) + 1; jbyteArray javaBytes = env->NewByteArray(numBytes); ScopedByteArrayRW bytes(env, javaBytes); if (bytes.get() == nullptr) { JNI_TRACE("bignumToArray(%p, %s) => NULL", source, sourceName); return nullptr; } unsigned char* tmp = reinterpret_cast
(bytes.get()); if (BN_num_bytes(source) > 0 && BN_bn2bin(source, tmp + 1) <= 0) { throwExceptionIfNecessary(env, "bignumToArray"); return nullptr; } // Set the sign and convert to two's complement if necessary for the Java code. if (BN_is_negative(source)) { bool carry = true; for (ssize_t i = numBytes - 1; i >= 0; i--) { tmp[i] ^= 0xFF; if (carry) { carry = (++tmp[i]) == 0; } } *tmp |= 0x80; } else { *tmp = 0x00; } JNI_TRACE("bignumToArray(%p, %s) => %p", source, sourceName, javaBytes); return javaBytes; } /** * Converts various OpenSSL ASN.1 types to a jbyteArray with DER-encoded data * inside. The "i2d_func" function pointer is a function of the "i2d_
" * from the OpenSSL ASN.1 API. */ template
jbyteArray ASN1ToByteArray(JNIEnv* env, T* obj, int (*i2d_func)(T*, unsigned char**)) { if (obj == nullptr) { jniThrowNullPointerException(env, "ASN1 input == null"); JNI_TRACE("ASN1ToByteArray(%p) => null input", obj); return nullptr; } int derLen = i2d_func(obj, nullptr); if (derLen < 0) { throwExceptionIfNecessary(env, "ASN1ToByteArray"); JNI_TRACE("ASN1ToByteArray(%p) => measurement failed", obj); return nullptr; } ScopedLocalRef
byteArray(env, env->NewByteArray(derLen)); if (byteArray.get() == nullptr) { JNI_TRACE("ASN1ToByteArray(%p) => creating byte array failed", obj); return nullptr; } ScopedByteArrayRW bytes(env, byteArray.get()); if (bytes.get() == nullptr) { JNI_TRACE("ASN1ToByteArray(%p) => using byte array failed", obj); return nullptr; } unsigned char* p = reinterpret_cast
(bytes.get()); int ret = i2d_func(obj, &p); if (ret < 0) { throwExceptionIfNecessary(env, "ASN1ToByteArray"); JNI_TRACE("ASN1ToByteArray(%p) => final conversion failed", obj); return nullptr; } JNI_TRACE("ASN1ToByteArray(%p) => success (%d bytes written)", obj, ret); return byteArray.release(); } template
T* ByteArrayToASN1(JNIEnv* env, jbyteArray byteArray) { ScopedByteArrayRO bytes(env, byteArray); if (bytes.get() == nullptr) { JNI_TRACE("ByteArrayToASN1(%p) => using byte array failed", byteArray); return nullptr; } const unsigned char* tmp = reinterpret_cast
(bytes.get()); return d2i_func(nullptr, &tmp, bytes.size()); } /** * Converts ASN.1 BIT STRING to a jbooleanArray. */ jbooleanArray ASN1BitStringToBooleanArray(JNIEnv* env, ASN1_BIT_STRING* bitStr) { int size = bitStr->length * 8; if (bitStr->flags & ASN1_STRING_FLAG_BITS_LEFT) { size -= bitStr->flags & 0x07; } ScopedLocalRef
bitsRef(env, env->NewBooleanArray(size)); if (bitsRef.get() == nullptr) { return nullptr; } ScopedBooleanArrayRW bitsArray(env, bitsRef.get()); for (int i = 0; i < static_cast
(bitsArray.size()); i++) { bitsArray[i] = ASN1_BIT_STRING_get_bit(bitStr, i); } return bitsRef.release(); } /** * Safely clear SSL sessions and throw an error if there was something already * in the error stack. */ static void safeSslClear(SSL* ssl) { if (SSL_clear(ssl) != 1) { freeOpenSslErrorState(); } } /** * To avoid the round-trip to ASN.1 and back in X509_dup, we just up the reference count. */ static X509* X509_dup_nocopy(X509* x509) { if (x509 == nullptr) { return nullptr; } #if defined(OPENSSL_IS_BORINGSSL) return X509_up_ref(x509); #else CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509); return x509; #endif } /* * Sets the read and write BIO for an SSL connection and removes it when it goes out of scope. * We hang on to BIO with a JNI GlobalRef and we want to remove them as soon as possible. */ class ScopedSslBio { public: ScopedSslBio(SSL *ssl, BIO* rbio, BIO* wbio) : ssl_(ssl) { SSL_set_bio(ssl_, rbio, wbio); BIO_up_ref(rbio); BIO_up_ref(wbio); } ~ScopedSslBio() { SSL_set_bio(ssl_, nullptr, nullptr); } private: SSL* const ssl_; }; /** * Obtains the current thread's JNIEnv */ static JNIEnv* getJNIEnv() { JNIEnv* env; #ifdef ANDROID if (gJavaVM->AttachCurrentThread(&env, nullptr) < 0) { #else if (gJavaVM->AttachCurrentThread(reinterpret_cast
(&env), NULL) < 0) { #endif ALOGE("Could not attach JavaVM to find current JNIEnv"); return nullptr; } return env; } /** * BIO for InputStream */ class BIO_Stream { public: BIO_Stream(jobject stream) : mEof(false) { JNIEnv* env = getJNIEnv(); mStream = env->NewGlobalRef(stream); } ~BIO_Stream() { JNIEnv* env = getJNIEnv(); env->DeleteGlobalRef(mStream); } bool isEof() const { JNI_TRACE("isEof? %s", mEof ? "yes" : "no"); return mEof; } int flush() { JNIEnv* env = getJNIEnv(); if (env == nullptr) { return -1; } if (env->ExceptionCheck()) { JNI_TRACE("BIO_Stream::flush called with pending exception"); return -1; } env->CallVoidMethod(mStream, outputStream_flushMethod); if (env->ExceptionCheck()) { return -1; } return 1; } protected: jobject getStream() { return mStream; } void setEof(bool eof) { mEof = eof; } private: jobject mStream; bool mEof; }; class BIO_InputStream : public BIO_Stream { public: BIO_InputStream(jobject stream, bool isFinite) : BIO_Stream(stream), isFinite_(isFinite) { } int read(char *buf, int len) { return read_internal(buf, len, inputStream_readMethod); } int gets(char *buf, int len) { if (len > PEM_LINE_LENGTH) { len = PEM_LINE_LENGTH; } int read = read_internal(buf, len - 1, openSslInputStream_readLineMethod); buf[read] = '\0'; JNI_TRACE("BIO::gets \"%s\"", buf); return read; } bool isFinite() const { return isFinite_; } private: const bool isFinite_; int read_internal(char *buf, int len, jmethodID method) { JNIEnv* env = getJNIEnv(); if (env == nullptr) { JNI_TRACE("BIO_InputStream::read could not get JNIEnv"); return -1; } if (env->ExceptionCheck()) { JNI_TRACE("BIO_InputStream::read called with pending exception"); return -1; } ScopedLocalRef
javaBytes(env, env->NewByteArray(len)); if (javaBytes.get() == nullptr) { JNI_TRACE("BIO_InputStream::read failed call to NewByteArray"); return -1; } jint read = env->CallIntMethod(getStream(), method, javaBytes.get()); if (env->ExceptionCheck()) { JNI_TRACE("BIO_InputStream::read failed call to InputStream#read"); return -1; } /* Java uses -1 to indicate EOF condition. */ if (read == -1) { setEof(true); read = 0; } else if (read > 0) { env->GetByteArrayRegion(javaBytes.get(), 0, read, reinterpret_cast
(buf)); } return read; } public: /** Length of PEM-encoded line (64) plus CR plus NULL */ static const int PEM_LINE_LENGTH = 66; }; class BIO_OutputStream : public BIO_Stream { public: BIO_OutputStream(jobject stream) : BIO_Stream(stream) { } int write(const char *buf, int len) { JNIEnv* env = getJNIEnv(); if (env == nullptr) { JNI_TRACE("BIO_OutputStream::write => could not get JNIEnv"); return -1; } if (env->ExceptionCheck()) { JNI_TRACE("BIO_OutputStream::write => called with pending exception"); return -1; } ScopedLocalRef
javaBytes(env, env->NewByteArray(len)); if (javaBytes.get() == nullptr) { JNI_TRACE("BIO_OutputStream::write => failed call to NewByteArray"); return -1; } env->SetByteArrayRegion(javaBytes.get(), 0, len, reinterpret_cast
(buf)); env->CallVoidMethod(getStream(), outputStream_writeMethod, javaBytes.get()); if (env->ExceptionCheck()) { JNI_TRACE("BIO_OutputStream::write => failed call to OutputStream#write"); return -1; } return len; } }; static int bio_stream_create(BIO *b) { b->init = 1; b->num = 0; b->ptr = nullptr; b->flags = 0; return 1; } static int bio_stream_destroy(BIO *b) { if (b == nullptr) { return 0; } if (b->ptr != nullptr) { delete static_cast
(b->ptr); b->ptr = nullptr; } b->init = 0; b->flags = 0; return 1; } static int bio_stream_read(BIO *b, char *buf, int len) { BIO_clear_retry_flags(b); BIO_InputStream* stream = static_cast
(b->ptr); int ret = stream->read(buf, len); if (ret == 0) { if (stream->isFinite()) { return 0; } // If the BIO_InputStream is not finite then EOF doesn't mean that // there's nothing more coming. BIO_set_retry_read(b); return -1; } return ret; } static int bio_stream_write(BIO *b, const char *buf, int len) { BIO_clear_retry_flags(b); BIO_OutputStream* stream = static_cast
(b->ptr); return stream->write(buf, len); } static int bio_stream_puts(BIO *b, const char *buf) { BIO_OutputStream* stream = static_cast
(b->ptr); return stream->write(buf, strlen(buf)); } static int bio_stream_gets(BIO *b, char *buf, int len) { BIO_InputStream* stream = static_cast
(b->ptr); return stream->gets(buf, len); } static void bio_stream_assign(BIO *b, BIO_Stream* stream) { b->ptr = static_cast
(stream); } static long bio_stream_ctrl(BIO *b, int cmd, long, void *) { BIO_Stream* stream = static_cast
(b->ptr); switch (cmd) { case BIO_CTRL_EOF: return stream->isEof() ? 1 : 0; case BIO_CTRL_FLUSH: return stream->flush(); default: return 0; } } static BIO_METHOD stream_bio_method = { (100 | 0x0400), /* source/sink BIO */ "InputStream/OutputStream BIO", bio_stream_write, /* bio_write */ bio_stream_read, /* bio_read */ bio_stream_puts, /* bio_puts */ bio_stream_gets, /* bio_gets */ bio_stream_ctrl, /* bio_ctrl */ bio_stream_create, /* bio_create */ bio_stream_destroy, /* bio_free */ nullptr, /* no bio_callback_ctrl */ }; static jbyteArray rawSignDigestWithPrivateKey(JNIEnv* env, jobject privateKey, const char* message, size_t message_len) { ScopedLocalRef
messageArray(env, env->NewByteArray(message_len)); if (env->ExceptionCheck()) { JNI_TRACE("rawSignDigestWithPrivateKey(%p) => threw exception", privateKey); return nullptr; } { ScopedByteArrayRW messageBytes(env, messageArray.get()); if (messageBytes.get() == nullptr) { JNI_TRACE("rawSignDigestWithPrivateKey(%p) => using byte array failed", privateKey); return nullptr; } memcpy(messageBytes.get(), message, message_len); } jmethodID rawSignMethod = env->GetStaticMethodID(cryptoUpcallsClass, "rawSignDigestWithPrivateKey", "(Ljava/security/PrivateKey;[B)[B"); if (rawSignMethod == nullptr) { ALOGE("Could not find rawSignDigestWithPrivateKey"); return nullptr; } return reinterpret_cast
(env->CallStaticObjectMethod( cryptoUpcallsClass, rawSignMethod, privateKey, messageArray.get())); } // rsaDecryptWithPrivateKey uses privateKey to decrypt |ciphertext_len| bytes // from |ciphertext|. The ciphertext is expected to be padded using the scheme // given in |padding|, which must be one of |RSA_*_PADDING| constants from // OpenSSL. static jbyteArray rsaDecryptWithPrivateKey(JNIEnv* env, jobject privateKey, jint padding, const char* ciphertext, size_t ciphertext_len) { ScopedLocalRef
ciphertextArray(env, env->NewByteArray(ciphertext_len)); if (env->ExceptionCheck()) { JNI_TRACE("rsaDecryptWithPrivateKey(%p) => threw exception", privateKey); return nullptr; } { ScopedByteArrayRW ciphertextBytes(env, ciphertextArray.get()); if (ciphertextBytes.get() == nullptr) { JNI_TRACE("rsaDecryptWithPrivateKey(%p) => using byte array failed", privateKey); return nullptr; } memcpy(ciphertextBytes.get(), ciphertext, ciphertext_len); } jmethodID rsaDecryptMethod = env->GetStaticMethodID(cryptoUpcallsClass, "rsaDecryptWithPrivateKey", "(Ljava/security/PrivateKey;I[B)[B"); if (rsaDecryptMethod == nullptr) { ALOGE("Could not find rsaDecryptWithPrivateKey"); return nullptr; } return reinterpret_cast
(env->CallStaticObjectMethod( cryptoUpcallsClass, rsaDecryptMethod, privateKey, padding, ciphertextArray.get())); } // ********************************************* // From keystore_openssl.cpp in Chromium source. // ********************************************* #if !defined(OPENSSL_IS_BORINGSSL) // Custom RSA_METHOD that uses the platform APIs. // Note that for now, only signing through RSA_sign() is really supported. // all other method pointers are either stubs returning errors, or no-ops. // See
for exact declaration of RSA_METHOD. int RsaMethodPubEnc(int /* flen */, const unsigned char* /* from */, unsigned char* /* to */, RSA* /* rsa */, int /* padding */) { RSAerr(RSA_F_RSA_PUBLIC_ENCRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED); return -1; } int RsaMethodPubDec(int /* flen */, const unsigned char* /* from */, unsigned char* /* to */, RSA* /* rsa */, int /* padding */) { RSAerr(RSA_F_RSA_PUBLIC_DECRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED); return -1; } // See RSA_eay_private_encrypt in // third_party/openssl/openssl/crypto/rsa/rsa_eay.c for the default // implementation of this function. int RsaMethodPrivEnc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { if (padding != RSA_PKCS1_PADDING) { // TODO(davidben): If we need to, we can implement RSA_NO_PADDING // by using javax.crypto.Cipher and picking either the // "RSA/ECB/NoPadding" or "RSA/ECB/PKCS1Padding" transformation as // appropriate. I believe support for both of these was added in // the same Android version as the "NONEwithRSA" // java.security.Signature algorithm, so the same version checks // for GetRsaLegacyKey should work. RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); return -1; } // Retrieve private key JNI reference. jobject private_key = reinterpret_cast
(RSA_get_app_data(rsa)); if (!private_key) { ALOGE("Null JNI reference passed to RsaMethodPrivEnc!"); RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR); return -1; } JNIEnv* env = getJNIEnv(); if (env == NULL) { return -1; } // For RSA keys, this function behaves as RSA_private_encrypt with // PKCS#1 padding. ScopedLocalRef
signature( env, rawSignDigestWithPrivateKey(env, private_key, reinterpret_cast
(from), flen)); if (signature.get() == NULL) { ALOGE("Could not sign message in RsaMethodPrivEnc!"); RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR); return -1; } ScopedByteArrayRO signatureBytes(env, signature.get()); size_t expected_size = static_cast
(RSA_size(rsa)); if (signatureBytes.size() > expected_size) { ALOGE("RSA Signature size mismatch, actual: %zd, expected <= %zd", signatureBytes.size(), expected_size); RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR); return -1; } // Copy result to OpenSSL-provided buffer. rawSignDigestWithPrivateKey // should pad with leading 0s, but if it doesn't, pad the result. size_t zero_pad = expected_size - signatureBytes.size(); memset(to, 0, zero_pad); memcpy(to + zero_pad, signatureBytes.get(), signatureBytes.size()); return expected_size; } int RsaMethodPrivDec(int flen, const unsigned char* from, unsigned char* to, RSA* rsa, int padding) { // Retrieve private key JNI reference. jobject private_key = reinterpret_cast
(RSA_get_app_data(rsa)); if (!private_key) { ALOGE("Null JNI reference passed to RsaMethodPrivDec!"); RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, ERR_R_INTERNAL_ERROR); return -1; } JNIEnv* env = getJNIEnv(); if (env == NULL) { return -1; } // This function behaves as RSA_private_decrypt. ScopedLocalRef
cleartext(env, rsaDecryptWithPrivateKey(env, private_key, padding, reinterpret_cast
(from), flen)); if (cleartext.get() == NULL) { ALOGE("Could not decrypt message in RsaMethodPrivDec!"); RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, ERR_R_INTERNAL_ERROR); return -1; } ScopedByteArrayRO cleartextBytes(env, cleartext.get()); size_t expected_size = static_cast
(RSA_size(rsa)); if (cleartextBytes.size() > expected_size) { ALOGE("RSA ciphertext size mismatch, actual: %zd, expected <= %zd", cleartextBytes.size(), expected_size); RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, ERR_R_INTERNAL_ERROR); return -1; } // Copy result to OpenSSL-provided buffer. memcpy(to, cleartextBytes.get(), cleartextBytes.size()); return cleartextBytes.size(); } int RsaMethodInit(RSA*) { return 0; } int RsaMethodFinish(RSA* rsa) { // Ensure the global JNI reference created with this wrapper is // properly destroyed with it. jobject key = reinterpret_cast
(RSA_get_app_data(rsa)); if (key != NULL) { RSA_set_app_data(rsa, NULL); JNIEnv* env = getJNIEnv(); env->DeleteGlobalRef(key); } // Actual return value is ignored by OpenSSL. There are no docs // explaining what this is supposed to be. return 0; } const RSA_METHOD android_rsa_method = { /* .name = */ "Android signing-only RSA method", /* .rsa_pub_enc = */ RsaMethodPubEnc, /* .rsa_pub_dec = */ RsaMethodPubDec, /* .rsa_priv_enc = */ RsaMethodPrivEnc, /* .rsa_priv_dec = */ RsaMethodPrivDec, /* .rsa_mod_exp = */ NULL, /* .bn_mod_exp = */ NULL, /* .init = */ RsaMethodInit, /* .finish = */ RsaMethodFinish, // This flag is necessary to tell OpenSSL to avoid checking the content // (i.e. internal fields) of the private key. Otherwise, it will complain // it's not valid for the certificate. /* .flags = */ RSA_METHOD_FLAG_NO_CHECK, /* .app_data = */ NULL, /* .rsa_sign = */ NULL, /* .rsa_verify = */ NULL, /* .rsa_keygen = */ NULL, }; // Used to ensure that the global JNI reference associated with a custom // EC_KEY + ECDSA_METHOD wrapper is released when its EX_DATA is destroyed // (this function is called when EVP_PKEY_free() is called on the wrapper). void ExDataFree(void* /* parent */, void* ptr, CRYPTO_EX_DATA* ad, int idx, long /* argl */, #if defined(OPENSSL_IS_BORINGSSL) || defined(GOOGLE_INTERNAL) const void* /* argp */) { #else /* defined(OPENSSL_IS_BORINGSSL) || defined(GOOGLE_INTERNAL) */ void* /* argp */) { #endif /* defined(OPENSSL_IS_BORINGSSL) || defined(GOOGLE_INTERNAL) */ jobject private_key = reinterpret_cast
(ptr); if (private_key == NULL) return; CRYPTO_set_ex_data(ad, idx, NULL); JNIEnv* env = getJNIEnv(); env->DeleteGlobalRef(private_key); } int ExDataDup(CRYPTO_EX_DATA* /* to */, CRYPTO_EX_DATA* /* from */, void* /* from_d */, int /* idx */, long /* argl */, #if defined(OPENSSL_IS_BORINGSSL) || defined(GOOGLE_INTERNAL) const void* /* argp */) { #else /* defined(OPENSSL_IS_BORINGSSL) || defined(GOOGLE_INTERNAL) */ void* /* argp */) { #endif /* defined(OPENSSL_IS_BORINGSSL) || defined(GOOGLE_INTERNAL) */ // This callback shall never be called with the current OpenSSL // implementation (the library only ever duplicates EX_DATA items // for SSL and BIO objects). But provide this to catch regressions // in the future. // Return value is currently ignored by OpenSSL. return 0; } class EcdsaExDataIndex { public: int ex_data_index() { return ex_data_index_; } static EcdsaExDataIndex& Instance() { static EcdsaExDataIndex singleton; return singleton; } private: EcdsaExDataIndex() { ex_data_index_ = ECDSA_get_ex_new_index(0, NULL, NULL, ExDataDup, ExDataFree); } EcdsaExDataIndex(EcdsaExDataIndex const&); ~EcdsaExDataIndex() {} EcdsaExDataIndex& operator=(EcdsaExDataIndex const&); int ex_data_index_; }; // Returns the index of the custom EX_DATA used to store the JNI reference. int EcdsaGetExDataIndex(void) { EcdsaExDataIndex& exData = EcdsaExDataIndex::Instance(); return exData.ex_data_index(); } ECDSA_SIG* EcdsaMethodDoSign(const unsigned char* dgst, int dgst_len, const BIGNUM* /* inv */, const BIGNUM* /* rp */, EC_KEY* eckey) { // Retrieve private key JNI reference. jobject private_key = reinterpret_cast
(ECDSA_get_ex_data(eckey, EcdsaGetExDataIndex())); if (!private_key) { ALOGE("Null JNI reference passed to EcdsaMethodDoSign!"); return NULL; } JNIEnv* env = getJNIEnv(); if (env == NULL) { return NULL; } // Sign message with it through JNI. ScopedLocalRef
signature( env, rawSignDigestWithPrivateKey(env, private_key, reinterpret_cast
(dgst), dgst_len)); if (signature.get() == NULL) { ALOGE("Could not sign message in EcdsaMethodDoSign!"); return NULL; } ScopedByteArrayRO signatureBytes(env, signature.get()); // Note: With ECDSA, the actual signature may be smaller than // ECDSA_size(). size_t max_expected_size = static_cast
(ECDSA_size(eckey)); if (signatureBytes.size() > max_expected_size) { ALOGE("ECDSA Signature size mismatch, actual: %zd, expected <= %zd", signatureBytes.size(), max_expected_size); return NULL; } // Convert signature to ECDSA_SIG object const unsigned char* sigbuf = reinterpret_cast
(signatureBytes.get()); long siglen = static_cast
(signatureBytes.size()); return d2i_ECDSA_SIG(NULL, &sigbuf, siglen); } int EcdsaMethodSignSetup(EC_KEY* /* eckey */, BN_CTX* /* ctx */, BIGNUM** /* kinv */, BIGNUM** /* r */, const unsigned char*, int) { ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ECDSA_R_ERR_EC_LIB); return -1; } int EcdsaMethodDoVerify(const unsigned char* /* dgst */, int /* dgst_len */, const ECDSA_SIG* /* sig */, EC_KEY* /* eckey */) { ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ECDSA_R_ERR_EC_LIB); return -1; } const ECDSA_METHOD android_ecdsa_method = { /* .name = */ "Android signing-only ECDSA method", /* .ecdsa_do_sign = */ EcdsaMethodDoSign, /* .ecdsa_sign_setup = */ EcdsaMethodSignSetup, /* .ecdsa_do_verify = */ EcdsaMethodDoVerify, /* .flags = */ 0, /* .app_data = */ NULL, }; #else /* OPENSSL_IS_BORINGSSL */ namespace { ENGINE *g_engine; int g_rsa_exdata_index; int g_ecdsa_exdata_index; pthread_once_t g_engine_once = PTHREAD_ONCE_INIT; void init_engine_globals(); void ensure_engine_globals() { pthread_once(&g_engine_once, init_engine_globals); } // KeyExData contains the data that is contained in the EX_DATA of the RSA // and ECDSA objects that are created to wrap Android system keys. struct KeyExData { // private_key contains a reference to a Java, private-key object. jobject private_key; // cached_size contains the "size" of the key. This is the size of the // modulus (in bytes) for RSA, or the group order size for ECDSA. This // avoids calling into Java to calculate the size. size_t cached_size; }; // ExDataDup is called when one of the RSA or EC_KEY objects is duplicated. We // don't support this and it should never happen. int ExDataDup(CRYPTO_EX_DATA* /* to */, const CRYPTO_EX_DATA* /* from */, void** /* from_d */, int /* index */, long /* argl */, void* /* argp */) { return 0; } // ExDataFree is called when one of the RSA or EC_KEY objects is freed. void ExDataFree(void* /* parent */, void* ptr, CRYPTO_EX_DATA* /* ad */, int /* index */, long /* argl */, void* /* argp */) { // Ensure the global JNI reference created with this wrapper is // properly destroyed with it. KeyExData *ex_data = reinterpret_cast
(ptr); if (ex_data != nullptr) { JNIEnv* env = getJNIEnv(); env->DeleteGlobalRef(ex_data->private_key); delete ex_data; } } KeyExData* RsaGetExData(const RSA* rsa) { return reinterpret_cast
(RSA_get_ex_data(rsa, g_rsa_exdata_index)); } size_t RsaMethodSize(const RSA *rsa) { const KeyExData *ex_data = RsaGetExData(rsa); return ex_data->cached_size; } int RsaMethodEncrypt(RSA* /* rsa */, size_t* /* out_len */, uint8_t* /* out */, size_t /* max_out */, const uint8_t* /* in */, size_t /* in_len */, int /* padding */) { OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE); return 0; } int RsaMethodSignRaw(RSA* rsa, size_t* out_len, uint8_t* out, size_t max_out, const uint8_t* in, size_t in_len, int padding) { if (padding != RSA_PKCS1_PADDING) { // TODO(davidben): If we need to, we can implement RSA_NO_PADDING // by using javax.crypto.Cipher and picking either the // "RSA/ECB/NoPadding" or "RSA/ECB/PKCS1Padding" transformation as // appropriate. I believe support for both of these was added in // the same Android version as the "NONEwithRSA" // java.security.Signature algorithm, so the same version checks // for GetRsaLegacyKey should work. OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_PADDING_TYPE); return 0; } // Retrieve private key JNI reference. const KeyExData *ex_data = RsaGetExData(rsa); if (!ex_data || !ex_data->private_key) { OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); return 0; } JNIEnv* env = getJNIEnv(); if (env == nullptr) { OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); return 0; } // For RSA keys, this function behaves as RSA_private_encrypt with // PKCS#1 padding. ScopedLocalRef
signature( env, rawSignDigestWithPrivateKey( env, ex_data->private_key, reinterpret_cast
(in), in_len)); if (signature.get() == nullptr) { OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); return 0; } ScopedByteArrayRO result(env, signature.get()); size_t expected_size = static_cast
(RSA_size(rsa)); if (result.size() > expected_size) { OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); return 0; } if (max_out < expected_size) { OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE); return 0; } // Copy result to OpenSSL-provided buffer. RawSignDigestWithPrivateKey // should pad with leading 0s, but if it doesn't, pad the result. size_t zero_pad = expected_size - result.size(); memset(out, 0, zero_pad); memcpy(out + zero_pad, &result[0], result.size()); *out_len = expected_size; return 1; } int RsaMethodDecrypt(RSA* rsa, size_t* out_len, uint8_t* out, size_t max_out, const uint8_t* in, size_t in_len, int padding) { // Retrieve private key JNI reference. const KeyExData *ex_data = RsaGetExData(rsa); if (!ex_data || !ex_data->private_key) { OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); return 0; } JNIEnv* env = getJNIEnv(); if (env == nullptr) { OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); return 0; } // This function behaves as RSA_private_decrypt. ScopedLocalRef
cleartext( env, rsaDecryptWithPrivateKey( env, ex_data->private_key, padding, reinterpret_cast
(in), in_len)); if (cleartext.get() == nullptr) { OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); return 0; } ScopedByteArrayRO cleartextBytes(env, cleartext.get()); if (max_out < cleartextBytes.size()) { OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE); return 0; } // Copy result to OpenSSL-provided buffer. memcpy(out, cleartextBytes.get(), cleartextBytes.size()); *out_len = cleartextBytes.size(); return 1; } int RsaMethodVerifyRaw(RSA* /* rsa */, size_t* /* out_len */, uint8_t* /* out */, size_t /* max_out */, const uint8_t* /* in */, size_t /* in_len */, int /* padding */) { OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE); return 0; } const RSA_METHOD android_rsa_method = { { 0 /* references */, 1 /* is_static */ } /* common */, nullptr /* app_data */, nullptr /* init */, nullptr /* finish */, RsaMethodSize, nullptr /* sign */, nullptr /* verify */, RsaMethodEncrypt, RsaMethodSignRaw, RsaMethodDecrypt, RsaMethodVerifyRaw, nullptr /* mod_exp */, nullptr /* bn_mod_exp */, nullptr /* private_transform */, RSA_FLAG_OPAQUE, nullptr /* keygen */, nullptr /* multi_prime_keygen */, nullptr /* supports_digest */, }; // Custom ECDSA_METHOD that uses the platform APIs. // Note that for now, only signing through ECDSA_sign() is really supported. // all other method pointers are either stubs returning errors, or no-ops. jobject EcKeyGetKey(const EC_KEY* ec_key) { KeyExData* ex_data = reinterpret_cast
(EC_KEY_get_ex_data( ec_key, g_ecdsa_exdata_index)); return ex_data->private_key; } size_t EcdsaMethodGroupOrderSize(const EC_KEY* ec_key) { KeyExData* ex_data = reinterpret_cast
(EC_KEY_get_ex_data( ec_key, g_ecdsa_exdata_index)); return ex_data->cached_size; } int EcdsaMethodSign(const uint8_t* digest, size_t digest_len, uint8_t* sig, unsigned int* sig_len, EC_KEY* ec_key) { // Retrieve private key JNI reference. jobject private_key = EcKeyGetKey(ec_key); if (!private_key) { ALOGE("Null JNI reference passed to EcdsaMethodSign!"); return 0; } JNIEnv* env = getJNIEnv(); if (env == nullptr) { return 0; } // Sign message with it through JNI. ScopedLocalRef
signature( env, rawSignDigestWithPrivateKey(env, private_key, reinterpret_cast
(digest), digest_len)); if (signature.get() == nullptr) { ALOGE("Could not sign message in EcdsaMethodDoSign!"); return 0; } ScopedByteArrayRO signatureBytes(env, signature.get()); // Note: With ECDSA, the actual signature may be smaller than // ECDSA_size(). size_t max_expected_size = ECDSA_size(ec_key); if (signatureBytes.size() > max_expected_size) { ALOGE("ECDSA Signature size mismatch, actual: %zd, expected <= %zd", signatureBytes.size(), max_expected_size); return 0; } memcpy(sig, signatureBytes.get(), signatureBytes.size()); *sig_len = signatureBytes.size(); return 1; } int EcdsaMethodVerify(const uint8_t* /* digest */, size_t /* digest_len */, const uint8_t* /* sig */, size_t /* sig_len */, EC_KEY* /* ec_key */) { OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_NOT_IMPLEMENTED); return 0; } const ECDSA_METHOD android_ecdsa_method = { { 0 /* references */, 1 /* is_static */ } /* common */, nullptr /* app_data */, nullptr /* init */, nullptr /* finish */, EcdsaMethodGroupOrderSize, EcdsaMethodSign, EcdsaMethodVerify, ECDSA_FLAG_OPAQUE, }; void init_engine_globals() { g_rsa_exdata_index = RSA_get_ex_new_index(0 /* argl */, nullptr /* argp */, nullptr /* new_func */, ExDataDup, ExDataFree); g_ecdsa_exdata_index = EC_KEY_get_ex_new_index(0 /* argl */, nullptr /* argp */, nullptr /* new_func */, ExDataDup, ExDataFree); g_engine = ENGINE_new(); ENGINE_set_RSA_method(g_engine, &android_rsa_method, sizeof(android_rsa_method)); ENGINE_set_ECDSA_method(g_engine, &android_ecdsa_method, sizeof(android_ecdsa_method)); } } // anonymous namespace #endif #ifdef CONSCRYPT_UNBUNDLED /* * This is a big hack; don't learn from this. Basically what happened is we do * not have an API way to insert ourselves into the AsynchronousCloseMonitor * that's compiled into the native libraries for libcore when we're unbundled. * So we try to look up the symbol from the main library to find it. */ typedef void (*acm_ctor_func)(void*, int); typedef void (*acm_dtor_func)(void*); static acm_ctor_func async_close_monitor_ctor = NULL; static acm_dtor_func async_close_monitor_dtor = NULL; class CompatibilityCloseMonitor { public: CompatibilityCloseMonitor(int fd) { if (async_close_monitor_ctor != NULL) { async_close_monitor_ctor(objBuffer, fd); } } ~CompatibilityCloseMonitor() { if (async_close_monitor_dtor != NULL) { async_close_monitor_dtor(objBuffer); } } private: char objBuffer[256]; #if 0 static_assert(sizeof(objBuffer) > 2*sizeof(AsynchronousCloseMonitor), "CompatibilityCloseMonitor must be larger than the actual object"); #endif }; static void findAsynchronousCloseMonitorFuncs() { void *lib = dlopen("libjavacore.so", RTLD_NOW); if (lib != NULL) { async_close_monitor_ctor = (acm_ctor_func) dlsym(lib, "_ZN24AsynchronousCloseMonitorC1Ei"); async_close_monitor_dtor = (acm_dtor_func) dlsym(lib, "_ZN24AsynchronousCloseMonitorD1Ev"); } } #endif /** * Copied from libnativehelper NetworkUtilites.cpp */ static bool setBlocking(int fd, bool blocking) { int flags = fcntl(fd, F_GETFL); if (flags == -1) { return false; } if (!blocking) { flags |= O_NONBLOCK; } else { flags &= ~O_NONBLOCK; } int rc = fcntl(fd, F_SETFL, flags); return (rc != -1); } /** * OpenSSL locking support. Taken from the O'Reilly book by Viega et al., but I * suppose there are not many other ways to do this on a Linux system (modulo * isomorphism). */ #define MUTEX_TYPE pthread_mutex_t #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) #define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) #define THREAD_ID pthread_self() #define THROW_SSLEXCEPTION (-2) #define THROW_SOCKETTIMEOUTEXCEPTION (-3) #define THROWN_EXCEPTION (-4) static MUTEX_TYPE* mutex_buf = nullptr; static void locking_function(int mode, int n, const char*, int) { if (mode & CRYPTO_LOCK) { MUTEX_LOCK(mutex_buf[n]); } else { MUTEX_UNLOCK(mutex_buf[n]); } } /* * Wrapper for pthread_mutex_t to assist in unlocking in all paths. */ class UniqueMutex { public: explicit UniqueMutex(pthread_mutex_t* mutex) : mutex_(mutex) { int err = pthread_mutex_lock(mutex_); if (err != 0) { ALOGE("failure obtaining mutex in %s: %d", __func__, err); abort(); } owns_ = true; } void unlock() { if (owns_) { owns_ = false; int err = pthread_mutex_unlock(mutex_); if (err != 0) { ALOGE("failure releasing mutex in %s: %d", __func__, err); abort(); } } } ~UniqueMutex() { unlock(); } private: pthread_mutex_t* const mutex_; bool owns_; }; static void threadid_callback(CRYPTO_THREADID *threadid) { #if defined(__APPLE__) uint64_t owner; int rc = pthread_threadid_np(NULL, &owner); // Requires Mac OS 10.6 if (rc == 0) { CRYPTO_THREADID_set_numeric(threadid, owner); } else { ALOGE("Error calling pthread_threadid_np"); } #else // bionic exposes gettid(), but glibc doesn't CRYPTO_THREADID_set_numeric(threadid, syscall(__NR_gettid)); #endif } int THREAD_setup(void) { mutex_buf = new MUTEX_TYPE[CRYPTO_num_locks()]; if (!mutex_buf) { return 0; } for (int i = 0; i < CRYPTO_num_locks(); ++i) { MUTEX_SETUP(mutex_buf[i]); } CRYPTO_THREADID_set_callback(threadid_callback); CRYPTO_set_locking_callback(locking_function); return 1; } int THREAD_cleanup(void) { if (!mutex_buf) { return 0; } CRYPTO_THREADID_set_callback(nullptr); CRYPTO_set_locking_callback(nullptr); for (int i = 0; i < CRYPTO_num_locks( ); i++) { MUTEX_CLEANUP(mutex_buf[i]); } free(mutex_buf); mutex_buf = nullptr; return 1; } /** * Initialization phase for every OpenSSL job: Loads the Error strings, the * crypto algorithms and reset the OpenSSL library */ static jboolean NativeCrypto_clinit(JNIEnv*, jclass) { SSL_load_error_strings(); ERR_load_crypto_strings(); SSL_library_init(); OpenSSL_add_all_algorithms(); THREAD_setup(); #if !defined(OPENSSL_IS_BORINGSSL) return JNI_FALSE; #else return JNI_TRUE; #endif } static void NativeCrypto_ENGINE_load_dynamic(JNIEnv*, jclass) { #if !defined(OPENSSL_IS_BORINGSSL) JNI_TRACE("ENGINE_load_dynamic()"); ENGINE_load_dynamic(); #endif } #if !defined(OPENSSL_IS_BORINGSSL) static jlong NativeCrypto_ENGINE_by_id(JNIEnv* env, jclass, jstring idJava) { JNI_TRACE("ENGINE_by_id(%p)", idJava); ScopedUtfChars id(env, idJava); if (id.c_str() == NULL) { JNI_TRACE("ENGINE_by_id(%p) => id == null", idJava); return 0; } JNI_TRACE("ENGINE_by_id(\"%s\")", id.c_str()); ENGINE* e = ENGINE_by_id(id.c_str()); if (e == NULL) { freeOpenSslErrorState(); } JNI_TRACE("ENGINE_by_id(\"%s\") => %p", id.c_str(), e); return reinterpret_cast
(e); } #else static jlong NativeCrypto_ENGINE_by_id(JNIEnv*, jclass, jstring) { return 0; } #endif #if !defined(OPENSSL_IS_BORINGSSL) static jint NativeCrypto_ENGINE_add(JNIEnv* env, jclass, jlong engineRef) { ENGINE* e = reinterpret_cast
(static_cast
(engineRef)); JNI_TRACE("ENGINE_add(%p)", e); if (e == NULL) { jniThrowException(env, "java/lang/IllegalArgumentException", "engineRef == 0"); return 0; } int ret = ENGINE_add(e); /* * We tolerate errors, because the most likely error is that * the ENGINE is already in the list. */ freeOpenSslErrorState(); JNI_TRACE("ENGINE_add(%p) => %d", e, ret); return ret; } #else static jint NativeCrypto_ENGINE_add(JNIEnv*, jclass, jlong) { return 0; } #endif #if !defined(OPENSSL_IS_BORINGSSL) static jint NativeCrypto_ENGINE_init(JNIEnv* env, jclass, jlong engineRef) { ENGINE* e = reinterpret_cast
(static_cast
(engineRef)); JNI_TRACE("ENGINE_init(%p)", e); if (e == NULL) { jniThrowException(env, "java/lang/IllegalArgumentException", "engineRef == 0"); return 0; } int ret = ENGINE_init(e); JNI_TRACE("ENGINE_init(%p) => %d", e, ret); return ret; } #else static jint NativeCrypto_ENGINE_init(JNIEnv*, jclass, jlong) { return 0; } #endif #if !defined(OPENSSL_IS_BORINGSSL) static jint NativeCrypto_ENGINE_finish(JNIEnv* env, jclass, jlong engineRef) { ENGINE* e = reinterpret_cast
(static_cast
(engineRef)); JNI_TRACE("ENGINE_finish(%p)", e); if (e == NULL) { jniThrowException(env, "java/lang/IllegalArgumentException", "engineRef == 0"); return 0; } int ret = ENGINE_finish(e); JNI_TRACE("ENGINE_finish(%p) => %d", e, ret); return ret; } #else static jint NativeCrypto_ENGINE_finish(JNIEnv*, jclass, jlong) { return 0; } #endif #if !defined(OPENSSL_IS_BORINGSSL) static jint NativeCrypto_ENGINE_free(JNIEnv* env, jclass, jlong engineRef) { ENGINE* e = reinterpret_cast
(static_cast
(engineRef)); JNI_TRACE("ENGINE_free(%p)", e); if (e == NULL) { jniThrowException(env, "java/lang/IllegalArgumentException", "engineRef == 0"); return 0; } int ret = ENGINE_free(e); JNI_TRACE("ENGINE_free(%p) => %d", e, ret); return ret; } #else static jint NativeCrypto_ENGINE_free(JNIEnv*, jclass, jlong) { return 0; } #endif #if defined(OPENSSL_IS_BORINGSSL) extern "C" { /* EVP_PKEY_from_keystore is from system/security/keystore-engine. */ extern EVP_PKEY* EVP_PKEY_from_keystore(const char *key_id); } #endif static jlong NativeCrypto_ENGINE_load_private_key(JNIEnv* env, jclass, jlong engineRef, jstring idJava) { ScopedUtfChars id(env, idJava); if (id.c_str() == nullptr) { jniThrowException(env, "java/lang/IllegalArgumentException", "id == NULL"); return 0; } #if !defined(OPENSSL_IS_BORINGSSL) ENGINE* e = reinterpret_cast
(static_cast
(engineRef)); JNI_TRACE("ENGINE_load_private_key(%p, %p)", e, idJava); Unique_EVP_PKEY pkey(ENGINE_load_private_key(e, id.c_str(), NULL, NULL)); if (pkey.get() == NULL) { throwExceptionIfNecessary(env, "ENGINE_load_private_key", throwInvalidKeyException); return 0; } JNI_TRACE("ENGINE_load_private_key(%p, %p) => %p", e, idJava, pkey.get()); return reinterpret_cast
(pkey.release()); #else UNUSED_ARGUMENT(engineRef); #if defined(NO_KEYSTORE_ENGINE) jniThrowRuntimeException(env, "No keystore ENGINE support compiled in"); return 0; #else Unique_EVP_PKEY pkey(EVP_PKEY_from_keystore(id.c_str())); if (pkey.get() == nullptr) { throwExceptionIfNecessary(env, "ENGINE_load_private_key", throwInvalidKeyException); return 0; } return reinterpret_cast
(pkey.release()); #endif #endif } #if !defined(OPENSSL_IS_BORINGSSL) static jstring NativeCrypto_ENGINE_get_id(JNIEnv* env, jclass, jlong engineRef) { ENGINE* e = reinterpret_cast
(static_cast
(engineRef)); JNI_TRACE("ENGINE_get_id(%p)", e); if (e == NULL) { jniThrowNullPointerException(env, "engine == null"); JNI_TRACE("ENGINE_get_id(%p) => engine == null", e); return NULL; } const char *id = ENGINE_get_id(e); ScopedLocalRef
idJava(env, env->NewStringUTF(id)); JNI_TRACE("ENGINE_get_id(%p) => \"%s\"", e, id); return idJava.release(); } #else static jstring NativeCrypto_ENGINE_get_id(JNIEnv* env, jclass, jlong) { ScopedLocalRef
idJava(env, env->NewStringUTF("keystore")); return idJava.release(); } #endif #if !defined(OPENSSL_IS_BORINGSSL) static jint NativeCrypto_ENGINE_ctrl_cmd_string(JNIEnv* env, jclass, jlong engineRef, jstring cmdJava, jstring argJava, jint cmd_optional) { ENGINE* e = reinterpret_cast
(static_cast
(engineRef)); JNI_TRACE("ENGINE_ctrl_cmd_string(%p, %p, %p, %d)", e, cmdJava, argJava, cmd_optional); if (e == NULL) { jniThrowNullPointerException(env, "engine == null"); JNI_TRACE("ENGINE_ctrl_cmd_string(%p, %p, %p, %d) => engine == null", e, cmdJava, argJava, cmd_optional); return 0; } ScopedUtfChars cmdChars(env, cmdJava); if (cmdChars.c_str() == NULL) { return 0; } UniquePtr
arg; const char* arg_c_str = NULL; if (argJava != NULL) { arg.reset(new ScopedUtfChars(env, argJava)); arg_c_str = arg->c_str(); if (arg_c_str == NULL) { return 0; } } JNI_TRACE("ENGINE_ctrl_cmd_string(%p, \"%s\", \"%s\", %d)", e, cmdChars.c_str(), arg_c_str, cmd_optional); int ret = ENGINE_ctrl_cmd_string(e, cmdChars.c_str(), arg_c_str, cmd_optional); if (ret != 1) { throwExceptionIfNecessary(env, "ENGINE_ctrl_cmd_string"); JNI_TRACE("ENGINE_ctrl_cmd_string(%p, \"%s\", \"%s\", %d) => threw error", e, cmdChars.c_str(), arg_c_str, cmd_optional); return 0; } JNI_TRACE("ENGINE_ctrl_cmd_string(%p, \"%s\", \"%s\", %d) => %d", e, cmdChars.c_str(), arg_c_str, cmd_optional, ret); return ret; } #else static jint NativeCrypto_ENGINE_ctrl_cmd_string(JNIEnv*, jclass, jlong, jstring, jstring, jint) { return 0; } #endif /** * private static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q); */ static jlong NativeCrypto_EVP_PKEY_new_RSA(JNIEnv* env, jclass, jbyteArray n, jbyteArray e, jbyteArray d, jbyteArray p, jbyteArray q, jbyteArray dmp1, jbyteArray dmq1, jbyteArray iqmp) { JNI_TRACE("EVP_PKEY_new_RSA(n=%p, e=%p, d=%p, p=%p, q=%p, dmp1=%p, dmq1=%p, iqmp=%p)", n, e, d, p, q, dmp1, dmq1, iqmp); Unique_RSA rsa(RSA_new()); if (rsa.get() == nullptr) { jniThrowRuntimeException(env, "RSA_new failed"); return 0; } if (e == nullptr && d == nullptr) { jniThrowException(env, "java/lang/IllegalArgumentException", "e == NULL && d == NULL"); JNI_TRACE("NativeCrypto_EVP_PKEY_new_RSA => e == NULL && d == NULL"); return 0; } if (!arrayToBignum(env, n, &rsa->n)) { return 0; } if (e != nullptr && !arrayToBignum(env, e, &rsa->e)) { return 0; } if (d != nullptr && !arrayToBignum(env, d, &rsa->d)) { return 0; } if (p != nullptr && !arrayToBignum(env, p, &rsa->p)) { return 0; } if (q != nullptr && !arrayToBignum(env, q, &rsa->q)) { return 0; } if (dmp1 != nullptr && !arrayToBignum(env, dmp1, &rsa->dmp1)) { return 0; } if (dmq1 != nullptr && !arrayToBignum(env, dmq1, &rsa->dmq1)) { return 0; } if (iqmp != nullptr && !arrayToBignum(env, iqmp, &rsa->iqmp)) { return 0; } #ifdef WITH_JNI_TRACE if (p != NULL && q != NULL) { int check = RSA_check_key(rsa.get()); JNI_TRACE("EVP_PKEY_new_RSA(...) RSA_check_key returns %d", check); } #endif if (rsa->n == nullptr || (rsa->e == nullptr && rsa->d == nullptr)) { jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM"); return 0; } /* * If the private exponent is available, there is the potential to do signing * operations. However, we can only do blinding if the public exponent is also * available. Disable blinding if the public exponent isn't available. * * TODO[kroot]: We should try to recover the public exponent by trying * some common ones such 3, 17, or 65537. */ if (rsa->d != nullptr && rsa->e == nullptr) { JNI_TRACE("EVP_PKEY_new_RSA(...) disabling RSA blinding => %p", rsa.get()); rsa->flags |= RSA_FLAG_NO_BLINDING; } Unique_EVP_PKEY pkey(EVP_PKEY_new()); if (pkey.get() == nullptr) { jniThrowRuntimeException(env, "EVP_PKEY_new failed"); return 0; } if (EVP_PKEY_assign_RSA(pkey.get(), rsa.get()) != 1) { jniThrowRuntimeException(env, "EVP_PKEY_new failed"); return 0; } OWNERSHIP_TRANSFERRED(rsa); JNI_TRACE("EVP_PKEY_new_RSA(n=%p, e=%p, d=%p, p=%p, q=%p dmp1=%p, dmq1=%p, iqmp=%p) => %p", n, e, d, p, q, dmp1, dmq1, iqmp, pkey.get()); return reinterpret_cast
(pkey.release()); } static jlong NativeCrypto_EVP_PKEY_new_EC_KEY(JNIEnv* env, jclass, jobject groupRef, jobject pubkeyRef, jbyteArray keyJavaBytes) { JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p)", groupRef, pubkeyRef, keyJavaBytes); const EC_GROUP* group = fromContextObject
(env, groupRef); if (group == nullptr) { return 0; } const EC_POINT* pubkey = pubkeyRef == nullptr ? nullptr : fromContextObject
(env, pubkeyRef); JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) <- ptr", group, pubkey, keyJavaBytes); Unique_BIGNUM key(nullptr); if (keyJavaBytes != nullptr) { BIGNUM* keyRef = nullptr; if (!arrayToBignum(env, keyJavaBytes, &keyRef)) { return 0; } key.reset(keyRef); } Unique_EC_KEY eckey(EC_KEY_new()); if (eckey.get() == nullptr) { jniThrowRuntimeException(env, "EC_KEY_new failed"); return 0; } if (EC_KEY_set_group(eckey.get(), group) != 1) { JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) > EC_KEY_set_group failed", group, pubkey, keyJavaBytes); throwExceptionIfNecessary(env, "EC_KEY_set_group"); return 0; } if (pubkey != nullptr) { if (EC_KEY_set_public_key(eckey.get(), pubkey) != 1) { JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) => EC_KEY_set_private_key failed", group, pubkey, keyJavaBytes); throwExceptionIfNecessary(env, "EC_KEY_set_public_key"); return 0; } } if (key.get() != nullptr) { if (EC_KEY_set_private_key(eckey.get(), key.get()) != 1) { JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) => EC_KEY_set_private_key failed", group, pubkey, keyJavaBytes); throwExceptionIfNecessary(env, "EC_KEY_set_private_key"); return 0; } if (pubkey == nullptr) { Unique_EC_POINT calcPubkey(EC_POINT_new(group)); if (!EC_POINT_mul(group, calcPubkey.get(), key.get(), nullptr, nullptr, nullptr)) { JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) => can't calulate public key", group, pubkey, keyJavaBytes); throwExceptionIfNecessary(env, "EC_KEY_set_private_key"); return 0; } EC_KEY_set_public_key(eckey.get(), calcPubkey.get()); } } if (!EC_KEY_check_key(eckey.get())) { JNI_TRACE("EVP_KEY_new_EC_KEY(%p, %p, %p) => invalid key created", group, pubkey, keyJavaBytes); throwExceptionIfNecessary(env, "EC_KEY_check_key"); return 0; } Unique_EVP_PKEY pkey(EVP_PKEY_new()); if (pkey.get() == nullptr) { JNI_TRACE("EVP_PKEY_new_EC(%p, %p, %p) => threw error", group, pubkey, keyJavaBytes); throwExceptionIfNecessary(env, "EVP_PKEY_new failed"); return 0; } if (EVP_PKEY_assign_EC_KEY(pkey.get(), eckey.get()) != 1) { JNI_TRACE("EVP_PKEY_new_EC(%p, %p, %p) => threw error", group, pubkey, keyJavaBytes); jniThrowRuntimeException(env, "EVP_PKEY_assign_EC_KEY failed"); return 0; } OWNERSHIP_TRANSFERRED(eckey); JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) => %p", group, pubkey, keyJavaBytes, pkey.get()); return reinterpret_cast
(pkey.release()); } static int NativeCrypto_EVP_PKEY_type(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("EVP_PKEY_type(%p)", pkey); if (pkey == nullptr) { return -1; } int result = EVP_PKEY_type(pkey->type); JNI_TRACE("EVP_PKEY_type(%p) => %d", pkey, result); return result; } /** * private static native int EVP_PKEY_size(int pkey); */ static int NativeCrypto_EVP_PKEY_size(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("EVP_PKEY_size(%p)", pkey); if (pkey == nullptr) { return -1; } int result = EVP_PKEY_size(pkey); JNI_TRACE("EVP_PKEY_size(%p) => %d", pkey, result); return result; } typedef int print_func(BIO*, const EVP_PKEY*, int, ASN1_PCTX*); static jstring evp_print_func(JNIEnv* env, jobject pkeyRef, print_func* func, const char* debug_name) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("%s(%p)", debug_name, pkey); if (pkey == nullptr) { return nullptr; } Unique_BIO buffer(BIO_new(BIO_s_mem())); if (buffer.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate BIO"); return nullptr; } if (func(buffer.get(), pkey, 0, (ASN1_PCTX*)nullptr) != 1) { throwExceptionIfNecessary(env, debug_name); return nullptr; } // Null terminate this BIO_write(buffer.get(), "\0", 1); char *tmp; BIO_get_mem_data(buffer.get(), &tmp); jstring description = env->NewStringUTF(tmp); JNI_TRACE("%s(%p) => \"%s\"", debug_name, pkey, tmp); return description; } static jstring NativeCrypto_EVP_PKEY_print_public(JNIEnv* env, jclass, jobject pkeyRef) { return evp_print_func(env, pkeyRef, EVP_PKEY_print_public, "EVP_PKEY_print_public"); } static jstring NativeCrypto_EVP_PKEY_print_params(JNIEnv* env, jclass, jobject pkeyRef) { return evp_print_func(env, pkeyRef, EVP_PKEY_print_params, "EVP_PKEY_print_params"); } static void NativeCrypto_EVP_PKEY_free(JNIEnv*, jclass, jlong pkeyRef) { EVP_PKEY* pkey = reinterpret_cast
(pkeyRef); JNI_TRACE("EVP_PKEY_free(%p)", pkey); if (pkey != nullptr) { EVP_PKEY_free(pkey); } } static jint NativeCrypto_EVP_PKEY_cmp(JNIEnv* env, jclass, jobject pkey1Ref, jobject pkey2Ref) { JNI_TRACE("EVP_PKEY_cmp(%p, %p)", pkey1Ref, pkey2Ref); EVP_PKEY* pkey1 = fromContextObject
(env, pkey1Ref); if (pkey1 == nullptr) { JNI_TRACE("EVP_PKEY_cmp => pkey1 == NULL"); return 0; } EVP_PKEY* pkey2 = fromContextObject
(env, pkey2Ref); if (pkey2 == nullptr) { JNI_TRACE("EVP_PKEY_cmp => pkey2 == NULL"); return 0; } JNI_TRACE("EVP_PKEY_cmp(%p, %p) <- ptr", pkey1, pkey2); int result = EVP_PKEY_cmp(pkey1, pkey2); JNI_TRACE("EVP_PKEY_cmp(%p, %p) => %d", pkey1, pkey2, result); return result; } /* * static native byte[] i2d_PKCS8_PRIV_KEY_INFO(int, byte[]) */ static jbyteArray NativeCrypto_i2d_PKCS8_PRIV_KEY_INFO(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("i2d_PKCS8_PRIV_KEY_INFO(%p)", pkey); if (pkey == nullptr) { return nullptr; } Unique_PKCS8_PRIV_KEY_INFO pkcs8(EVP_PKEY2PKCS8(pkey)); if (pkcs8.get() == nullptr) { throwExceptionIfNecessary(env, "NativeCrypto_i2d_PKCS8_PRIV_KEY_INFO"); JNI_TRACE("key=%p i2d_PKCS8_PRIV_KEY_INFO => error from key to PKCS8", pkey); return nullptr; } return ASN1ToByteArray
(env, pkcs8.get(), i2d_PKCS8_PRIV_KEY_INFO); } /* * static native int d2i_PKCS8_PRIV_KEY_INFO(byte[]) */ static jlong NativeCrypto_d2i_PKCS8_PRIV_KEY_INFO(JNIEnv* env, jclass, jbyteArray keyJavaBytes) { JNI_TRACE("d2i_PKCS8_PRIV_KEY_INFO(%p)", keyJavaBytes); ScopedByteArrayRO bytes(env, keyJavaBytes); if (bytes.get() == nullptr) { JNI_TRACE("bytes=%p d2i_PKCS8_PRIV_KEY_INFO => threw exception", keyJavaBytes); return 0; } const unsigned char* tmp = reinterpret_cast
(bytes.get()); Unique_PKCS8_PRIV_KEY_INFO pkcs8(d2i_PKCS8_PRIV_KEY_INFO(nullptr, &tmp, bytes.size())); if (pkcs8.get() == nullptr) { throwExceptionIfNecessary(env, "d2i_PKCS8_PRIV_KEY_INFO"); JNI_TRACE("ssl=%p d2i_PKCS8_PRIV_KEY_INFO => error from DER to PKCS8", keyJavaBytes); return 0; } Unique_EVP_PKEY pkey(EVP_PKCS82PKEY(pkcs8.get())); if (pkey.get() == nullptr) { throwExceptionIfNecessary(env, "d2i_PKCS8_PRIV_KEY_INFO"); JNI_TRACE("ssl=%p d2i_PKCS8_PRIV_KEY_INFO => error from PKCS8 to key", keyJavaBytes); return 0; } JNI_TRACE("bytes=%p d2i_PKCS8_PRIV_KEY_INFO => %p", keyJavaBytes, pkey.get()); return reinterpret_cast
(pkey.release()); } /* * static native byte[] i2d_PUBKEY(int) */ static jbyteArray NativeCrypto_i2d_PUBKEY(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("i2d_PUBKEY(%p)", pkey); if (pkey == nullptr) { return nullptr; } return ASN1ToByteArray
(env, pkey, reinterpret_cast
(i2d_PUBKEY)); } /* * static native int d2i_PUBKEY(byte[]) */ static jlong NativeCrypto_d2i_PUBKEY(JNIEnv* env, jclass, jbyteArray javaBytes) { JNI_TRACE("d2i_PUBKEY(%p)", javaBytes); ScopedByteArrayRO bytes(env, javaBytes); if (bytes.get() == nullptr) { JNI_TRACE("d2i_PUBKEY(%p) => threw error", javaBytes); return 0; } const unsigned char* tmp = reinterpret_cast
(bytes.get()); Unique_EVP_PKEY pkey(d2i_PUBKEY(nullptr, &tmp, bytes.size())); if (pkey.get() == nullptr) { JNI_TRACE("bytes=%p d2i_PUBKEY => threw exception", javaBytes); throwExceptionIfNecessary(env, "d2i_PUBKEY"); return 0; } return reinterpret_cast
(pkey.release()); } static jlong NativeCrypto_getRSAPrivateKeyWrapper(JNIEnv* env, jclass, jobject javaKey, jbyteArray modulusBytes) { JNI_TRACE("getRSAPrivateKeyWrapper(%p, %p)", javaKey, modulusBytes); #if !defined(OPENSSL_IS_BORINGSSL) Unique_RSA rsa(RSA_new()); if (rsa.get() == NULL) { jniThrowOutOfMemory(env, "Unable to allocate RSA key"); return 0; } RSA_set_method(rsa.get(), &android_rsa_method); if (!arrayToBignum(env, modulusBytes, &rsa->n)) { return 0; } RSA_set_app_data(rsa.get(), env->NewGlobalRef(javaKey)); #else size_t cached_size; if (!arrayToBignumSize(env, modulusBytes, &cached_size)) { JNI_TRACE("getRSAPrivateKeyWrapper failed"); return 0; } ensure_engine_globals(); Unique_RSA rsa(RSA_new_method(g_engine)); if (rsa.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate RSA key"); return 0; } auto ex_data = new KeyExData; ex_data->private_key = env->NewGlobalRef(javaKey); ex_data->cached_size = cached_size; RSA_set_ex_data(rsa.get(), g_rsa_exdata_index, ex_data); #endif Unique_EVP_PKEY pkey(EVP_PKEY_new()); if (pkey.get() == nullptr) { JNI_TRACE("getRSAPrivateKeyWrapper failed"); jniThrowRuntimeException(env, "NativeCrypto_getRSAPrivateKeyWrapper failed"); freeOpenSslErrorState(); return 0; } if (EVP_PKEY_assign_RSA(pkey.get(), rsa.get()) != 1) { jniThrowRuntimeException(env, "getRSAPrivateKeyWrapper failed"); return 0; } OWNERSHIP_TRANSFERRED(rsa); return reinterpret_cast
(pkey.release()); } static jlong NativeCrypto_getECPrivateKeyWrapper(JNIEnv* env, jclass, jobject javaKey, jobject groupRef) { EC_GROUP* group = fromContextObject
(env, groupRef); JNI_TRACE("getECPrivateKeyWrapper(%p, %p)", javaKey, group); if (group == nullptr) { return 0; } #if !defined(OPENSSL_IS_BORINGSSL) Unique_EC_KEY ecKey(EC_KEY_new()); if (ecKey.get() == NULL) { jniThrowOutOfMemory(env, "Unable to allocate EC key"); return 0; } JNI_TRACE("EC_GROUP_get_curve_name(%p)", group); if (group == NULL) { JNI_TRACE("EC_GROUP_get_curve_name => group == NULL"); jniThrowNullPointerException(env, "group == NULL"); return 0; } EC_KEY_set_group(ecKey.get(), group); ECDSA_set_method(ecKey.get(), &android_ecdsa_method); ECDSA_set_ex_data(ecKey.get(), EcdsaGetExDataIndex(), env->NewGlobalRef(javaKey)); #else ensure_engine_globals(); Unique_EC_KEY ecKey(EC_KEY_new_method(g_engine)); if (ecKey.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate EC key"); return 0; } auto ex_data = new KeyExData; ex_data->private_key = env->NewGlobalRef(javaKey); if (!EC_KEY_set_ex_data(ecKey.get(), g_ecdsa_exdata_index, ex_data)) { env->DeleteGlobalRef(ex_data->private_key); delete ex_data; jniThrowRuntimeException(env, "EC_KEY_set_ex_data"); return 0; } BIGNUM order; BN_init(&order); if (!EC_GROUP_get_order(group, &order, nullptr)) { BN_free(&order); jniThrowRuntimeException(env, "EC_GROUP_get_order failed"); return 0; } ex_data->cached_size = BN_num_bytes(&order); BN_free(&order); #endif Unique_EVP_PKEY pkey(EVP_PKEY_new()); if (pkey.get() == nullptr) { JNI_TRACE("getECPrivateKeyWrapper failed"); jniThrowRuntimeException(env, "NativeCrypto_getECPrivateKeyWrapper failed"); freeOpenSslErrorState(); return 0; } if (EVP_PKEY_assign_EC_KEY(pkey.get(), ecKey.get()) != 1) { jniThrowRuntimeException(env, "getECPrivateKeyWrapper failed"); return 0; } OWNERSHIP_TRANSFERRED(ecKey); return reinterpret_cast
(pkey.release()); } /* * public static native int RSA_generate_key(int modulusBits, byte[] publicExponent); */ static jlong NativeCrypto_RSA_generate_key_ex(JNIEnv* env, jclass, jint modulusBits, jbyteArray publicExponent) { JNI_TRACE("RSA_generate_key_ex(%d, %p)", modulusBits, publicExponent); BIGNUM* eRef = nullptr; if (!arrayToBignum(env, publicExponent, &eRef)) { return 0; } Unique_BIGNUM e(eRef); Unique_RSA rsa(RSA_new()); if (rsa.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate RSA key"); return 0; } if (RSA_generate_key_ex(rsa.get(), modulusBits, e.get(), nullptr) < 0) { throwExceptionIfNecessary(env, "RSA_generate_key_ex"); return 0; } Unique_EVP_PKEY pkey(EVP_PKEY_new()); if (pkey.get() == nullptr) { jniThrowRuntimeException(env, "RSA_generate_key_ex failed"); return 0; } if (EVP_PKEY_assign_RSA(pkey.get(), rsa.get()) != 1) { jniThrowRuntimeException(env, "RSA_generate_key_ex failed"); return 0; } OWNERSHIP_TRANSFERRED(rsa); JNI_TRACE("RSA_generate_key_ex(n=%d, e=%p) => %p", modulusBits, publicExponent, pkey.get()); return reinterpret_cast
(pkey.release()); } static jint NativeCrypto_RSA_size(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("RSA_size(%p)", pkey); if (pkey == nullptr) { return 0; } Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey)); if (rsa.get() == nullptr) { jniThrowRuntimeException(env, "RSA_size failed"); return 0; } return static_cast
(RSA_size(rsa.get())); } #if defined(OPENSSL_IS_BORINGSSL) typedef int RSACryptOperation(size_t flen, const unsigned char* from, unsigned char* to, RSA* rsa, int padding); #else typedef int RSACryptOperation(int flen, const unsigned char* from, unsigned char* to, RSA* rsa, int padding); #endif static jint RSA_crypt_operation(RSACryptOperation operation, const char* caller, JNIEnv* env, jint flen, jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jobject pkeyRef, jint padding) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("%s(%d, %p, %p, %p)", caller, flen, fromJavaBytes, toJavaBytes, pkey); if (pkey == nullptr) { return -1; } Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey)); if (rsa.get() == nullptr) { return -1; } ScopedByteArrayRO from(env, fromJavaBytes); if (from.get() == nullptr) { return -1; } ScopedByteArrayRW to(env, toJavaBytes); if (to.get() == nullptr) { return -1; } int resultSize = operation( #if defined(OPENSSL_IS_BORINGSSL) static_cast
(flen), #else static_cast
(flen), #endif reinterpret_cast
(from.get()), reinterpret_cast
(to.get()), rsa.get(), padding); if (resultSize == -1) { if (throwExceptionIfNecessary(env, caller)) { JNI_TRACE("%s => threw error", caller); } else { throwBadPaddingException(env, caller); JNI_TRACE("%s => threw padding exception", caller); } return -1; } JNI_TRACE("%s(%d, %p, %p, %p) => %d", caller, flen, fromJavaBytes, toJavaBytes, pkey, resultSize); return static_cast
(resultSize); } static jint NativeCrypto_RSA_private_encrypt(JNIEnv* env, jclass, jint flen, jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jobject pkeyRef, jint padding) { return RSA_crypt_operation(RSA_private_encrypt, __FUNCTION__, env, flen, fromJavaBytes, toJavaBytes, pkeyRef, padding); } static jint NativeCrypto_RSA_public_decrypt(JNIEnv* env, jclass, jint flen, jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jobject pkeyRef, jint padding) { return RSA_crypt_operation(RSA_public_decrypt, __FUNCTION__, env, flen, fromJavaBytes, toJavaBytes, pkeyRef, padding); } static jint NativeCrypto_RSA_public_encrypt(JNIEnv* env, jclass, jint flen, jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jobject pkeyRef, jint padding) { return RSA_crypt_operation(RSA_public_encrypt, __FUNCTION__, env, flen, fromJavaBytes, toJavaBytes, pkeyRef, padding); } static jint NativeCrypto_RSA_private_decrypt(JNIEnv* env, jclass, jint flen, jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jobject pkeyRef, jint padding) { return RSA_crypt_operation(RSA_private_decrypt, __FUNCTION__, env, flen, fromJavaBytes, toJavaBytes, pkeyRef, padding); } /* * public static native byte[][] get_RSA_public_params(long); */ static jobjectArray NativeCrypto_get_RSA_public_params(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("get_RSA_public_params(%p)", pkey); if (pkey == nullptr) { return nullptr; } Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey)); if (rsa.get() == nullptr) { throwExceptionIfNecessary(env, "get_RSA_public_params failed"); return nullptr; } jobjectArray joa = env->NewObjectArray(2, byteArrayClass, nullptr); if (joa == nullptr) { return nullptr; } jbyteArray n = bignumToArray(env, rsa->n, "n"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 0, n); jbyteArray e = bignumToArray(env, rsa->e, "e"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 1, e); return joa; } /* * public static native byte[][] get_RSA_private_params(long); */ static jobjectArray NativeCrypto_get_RSA_private_params(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("get_RSA_public_params(%p)", pkey); if (pkey == nullptr) { return nullptr; } Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey)); if (rsa.get() == nullptr) { throwExceptionIfNecessary(env, "get_RSA_public_params failed"); return nullptr; } jobjectArray joa = env->NewObjectArray(8, byteArrayClass, nullptr); if (joa == nullptr) { return nullptr; } jbyteArray n = bignumToArray(env, rsa->n, "n"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 0, n); if (rsa->e != nullptr) { jbyteArray e = bignumToArray(env, rsa->e, "e"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 1, e); } if (rsa->d != nullptr) { jbyteArray d = bignumToArray(env, rsa->d, "d"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 2, d); } if (rsa->p != nullptr) { jbyteArray p = bignumToArray(env, rsa->p, "p"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 3, p); } if (rsa->q != nullptr) { jbyteArray q = bignumToArray(env, rsa->q, "q"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 4, q); } if (rsa->dmp1 != nullptr) { jbyteArray dmp1 = bignumToArray(env, rsa->dmp1, "dmp1"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 5, dmp1); } if (rsa->dmq1 != nullptr) { jbyteArray dmq1 = bignumToArray(env, rsa->dmq1, "dmq1"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 6, dmq1); } if (rsa->iqmp != nullptr) { jbyteArray iqmp = bignumToArray(env, rsa->iqmp, "iqmp"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 7, iqmp); } return joa; } #define EC_CURVE_GFP 1 #define EC_CURVE_GF2M 2 /** * Return group type or 0 if unknown group. * EC_GROUP_GFP or EC_GROUP_GF2M */ #if !defined(OPENSSL_IS_BORINGSSL) static int get_EC_GROUP_type(const EC_GROUP* group) { const int curve_nid = EC_METHOD_get_field_type(EC_GROUP_method_of(group)); if (curve_nid == NID_X9_62_prime_field) { return EC_CURVE_GFP; } else if (curve_nid == NID_X9_62_characteristic_two_field) { return EC_CURVE_GF2M; } return 0; } #else static int get_EC_GROUP_type(const EC_GROUP*) { return EC_CURVE_GFP; } #endif static jlong NativeCrypto_EC_GROUP_new_by_curve_name(JNIEnv* env, jclass, jstring curveNameJava) { JNI_TRACE("EC_GROUP_new_by_curve_name(%p)", curveNameJava); ScopedUtfChars curveName(env, curveNameJava); if (curveName.c_str() == nullptr) { return 0; } JNI_TRACE("EC_GROUP_new_by_curve_name(%s)", curveName.c_str()); int nid = OBJ_sn2nid(curveName.c_str()); if (nid == NID_undef) { JNI_TRACE("EC_GROUP_new_by_curve_name(%s) => unknown NID name", curveName.c_str()); return 0; } EC_GROUP* group = EC_GROUP_new_by_curve_name(nid); if (group == nullptr) { JNI_TRACE("EC_GROUP_new_by_curve_name(%s) => unknown NID %d", curveName.c_str(), nid); freeOpenSslErrorState(); return 0; } JNI_TRACE("EC_GROUP_new_by_curve_name(%s) => %p", curveName.c_str(), group); return reinterpret_cast
(group); } #if !defined(OPENSSL_IS_BORINGSSL) || !defined(BORINGSSL_201512) // Compatibility shim for EC_GROUP_new_arbitrary using the old two-step API. static EC_GROUP* EC_GROUP_new_arbitrary( const BIGNUM* p, const BIGNUM* a, const BIGNUM* b, const BIGNUM* gx, const BIGNUM* gy, const BIGNUM* order, const BIGNUM* cofactor) { Unique_BN_CTX ctx(BN_CTX_new()); if (ctx.get() == nullptr) { return nullptr; } Unique_EC_GROUP group(EC_GROUP_new_curve_GFp(p, a, b, ctx.get())); if (group.get() == nullptr) { return nullptr; } Unique_EC_POINT generator(EC_POINT_new(group.get())); if (generator.get() == nullptr || !EC_POINT_set_affine_coordinates_GFp(group.get(), generator.get(), gx, gy, ctx.get()) || !EC_GROUP_set_generator(group.get(), generator.get(), order, cofactor)) { return nullptr; } return group.release(); } #endif static jlong NativeCrypto_EC_GROUP_new_arbitrary( JNIEnv* env, jclass, jbyteArray pBytes, jbyteArray aBytes, jbyteArray bBytes, jbyteArray xBytes, jbyteArray yBytes, jbyteArray orderBytes, jint cofactorInt) { BIGNUM *p = nullptr, *a = nullptr, *b = nullptr, *x = nullptr, *y = nullptr; BIGNUM *order = nullptr, *cofactor = nullptr; JNI_TRACE("EC_GROUP_new_arbitrary"); if (cofactorInt < 1) { jniThrowException(env, "java/lang/IllegalArgumentException", "cofactor < 1"); return 0; } cofactor = BN_new(); if (cofactor == nullptr) { return 0; } int ok = 1; if (!arrayToBignum(env, pBytes, &p) || !arrayToBignum(env, aBytes, &a) || !arrayToBignum(env, bBytes, &b) || !arrayToBignum(env, xBytes, &x) || !arrayToBignum(env, yBytes, &y) || !arrayToBignum(env, orderBytes, &order) || !BN_set_word(cofactor, cofactorInt)) { ok = 0; } Unique_BIGNUM pStorage(p); Unique_BIGNUM aStorage(a); Unique_BIGNUM bStorage(b); Unique_BIGNUM xStorage(x); Unique_BIGNUM yStorage(y); Unique_BIGNUM orderStorage(order); Unique_BIGNUM cofactorStorage(cofactor); if (!ok) { return 0; } Unique_EC_GROUP group(EC_GROUP_new_arbitrary(p, a, b, x, y, order, cofactor)); if (group.get() == nullptr) { JNI_TRACE("EC_GROUP_new_arbitrary => NULL"); throwExceptionIfNecessary(env, "EC_GROUP_new_arbitrary"); return 0; } JNI_TRACE("EC_GROUP_new_arbitrary => %p", group.get()); return reinterpret_cast
(group.release()); } #if !defined(OPENSSL_IS_BORINGSSL) static void NativeCrypto_EC_GROUP_set_asn1_flag(JNIEnv* env, jclass, jobject groupRef, jint flag) { EC_GROUP* group = fromContextObject
(env, groupRef); JNI_TRACE("EC_GROUP_set_asn1_flag(%p, %d)", group, flag); if (group == NULL) { JNI_TRACE("EC_GROUP_set_asn1_flag => group == NULL"); return; } EC_GROUP_set_asn1_flag(group, flag); JNI_TRACE("EC_GROUP_set_asn1_flag(%p, %d) => success", group, flag); } #else static void NativeCrypto_EC_GROUP_set_asn1_flag(JNIEnv*, jclass, jobject, jint) { } #endif #if !defined(OPENSSL_IS_BORINGSSL) static void NativeCrypto_EC_GROUP_set_point_conversion_form(JNIEnv* env, jclass, jobject groupRef, jint form) { EC_GROUP* group = fromContextObject
(env, groupRef); JNI_TRACE("EC_GROUP_set_point_conversion_form(%p, %d)", group, form); if (group == NULL) { JNI_TRACE("EC_GROUP_set_point_conversion_form => group == NULL"); return; } EC_GROUP_set_point_conversion_form(group, static_cast
(form)); JNI_TRACE("EC_GROUP_set_point_conversion_form(%p, %d) => success", group, form); } #else static void NativeCrypto_EC_GROUP_set_point_conversion_form(JNIEnv*, jclass, jobject, jint) { } #endif static jstring NativeCrypto_EC_GROUP_get_curve_name(JNIEnv* env, jclass, jobject groupRef) { const EC_GROUP* group = fromContextObject
(env, groupRef); JNI_TRACE("EC_GROUP_get_curve_name(%p)", group); if (group == nullptr) { JNI_TRACE("EC_GROUP_get_curve_name => group == NULL"); return nullptr; } int nid = EC_GROUP_get_curve_name(group); if (nid == NID_undef) { JNI_TRACE("EC_GROUP_get_curve_name(%p) => unnamed curve", group); return nullptr; } const char* shortName = OBJ_nid2sn(nid); JNI_TRACE("EC_GROUP_get_curve_name(%p) => \"%s\"", group, shortName); return env->NewStringUTF(shortName); } static jobjectArray NativeCrypto_EC_GROUP_get_curve(JNIEnv* env, jclass, jobject groupRef) { const EC_GROUP* group = fromContextObject
(env, groupRef); JNI_TRACE("EC_GROUP_get_curve(%p)", group); if (group == nullptr) { JNI_TRACE("EC_GROUP_get_curve => group == NULL"); return nullptr; } Unique_BIGNUM p(BN_new()); Unique_BIGNUM a(BN_new()); Unique_BIGNUM b(BN_new()); if (get_EC_GROUP_type(group) != EC_CURVE_GFP) { jniThrowRuntimeException(env, "invalid group"); return nullptr; } int ret = EC_GROUP_get_curve_GFp(group, p.get(), a.get(), b.get(), (BN_CTX*)nullptr); if (ret != 1) { throwExceptionIfNecessary(env, "EC_GROUP_get_curve"); return nullptr; } jobjectArray joa = env->NewObjectArray(3, byteArrayClass, nullptr); if (joa == nullptr) { return nullptr; } jbyteArray pArray = bignumToArray(env, p.get(), "p"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 0, pArray); jbyteArray aArray = bignumToArray(env, a.get(), "a"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 1, aArray); jbyteArray bArray = bignumToArray(env, b.get(), "b"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 2, bArray); JNI_TRACE("EC_GROUP_get_curve(%p) => %p", group, joa); return joa; } static jbyteArray NativeCrypto_EC_GROUP_get_order(JNIEnv* env, jclass, jobject groupRef) { const EC_GROUP* group = fromContextObject
(env, groupRef); JNI_TRACE("EC_GROUP_get_order(%p)", group); if (group == nullptr) { return nullptr; } Unique_BIGNUM order(BN_new()); if (order.get() == nullptr) { JNI_TRACE("EC_GROUP_get_order(%p) => can't create BN", group); jniThrowOutOfMemory(env, "BN_new"); return nullptr; } if (EC_GROUP_get_order(group, order.get(), nullptr) != 1) { JNI_TRACE("EC_GROUP_get_order(%p) => threw error", group); throwExceptionIfNecessary(env, "EC_GROUP_get_order"); return nullptr; } jbyteArray orderArray = bignumToArray(env, order.get(), "order"); if (env->ExceptionCheck()) { return nullptr; } JNI_TRACE("EC_GROUP_get_order(%p) => %p", group, orderArray); return orderArray; } static jint NativeCrypto_EC_GROUP_get_degree(JNIEnv* env, jclass, jobject groupRef) { const EC_GROUP* group = fromContextObject
(env, groupRef); JNI_TRACE("EC_GROUP_get_degree(%p)", group); if (group == nullptr) { return 0; } jint degree = EC_GROUP_get_degree(group); if (degree == 0) { JNI_TRACE("EC_GROUP_get_degree(%p) => unsupported", group); jniThrowRuntimeException(env, "not supported"); return 0; } JNI_TRACE("EC_GROUP_get_degree(%p) => %d", group, degree); return degree; } static jbyteArray NativeCrypto_EC_GROUP_get_cofactor(JNIEnv* env, jclass, jobject groupRef) { const EC_GROUP* group = fromContextObject
(env, groupRef); JNI_TRACE("EC_GROUP_get_cofactor(%p)", group); if (group == nullptr) { return nullptr; } Unique_BIGNUM cofactor(BN_new()); if (cofactor.get() == nullptr) { JNI_TRACE("EC_GROUP_get_cofactor(%p) => can't create BN", group); jniThrowOutOfMemory(env, "BN_new"); return nullptr; } if (EC_GROUP_get_cofactor(group, cofactor.get(), nullptr) != 1) { JNI_TRACE("EC_GROUP_get_cofactor(%p) => threw error", group); throwExceptionIfNecessary(env, "EC_GROUP_get_cofactor"); return nullptr; } jbyteArray cofactorArray = bignumToArray(env, cofactor.get(), "cofactor"); if (env->ExceptionCheck()) { return nullptr; } JNI_TRACE("EC_GROUP_get_cofactor(%p) => %p", group, cofactorArray); return cofactorArray; } static jint NativeCrypto_get_EC_GROUP_type(JNIEnv* env, jclass, jobject groupRef) { const EC_GROUP* group = fromContextObject
(env, groupRef); JNI_TRACE("get_EC_GROUP_type(%p)", group); if (group == nullptr) { return 0; } int type = get_EC_GROUP_type(group); if (type == 0) { JNI_TRACE("get_EC_GROUP_type(%p) => curve type", group); jniThrowRuntimeException(env, "unknown curve type"); } else { JNI_TRACE("get_EC_GROUP_type(%p) => %d", group, type); } return type; } static void NativeCrypto_EC_GROUP_clear_free(JNIEnv* env, jclass, jlong groupRef) { EC_GROUP* group = reinterpret_cast
(groupRef); JNI_TRACE("EC_GROUP_clear_free(%p)", group); if (group == nullptr) { JNI_TRACE("EC_GROUP_clear_free => group == NULL"); jniThrowNullPointerException(env, "group == NULL"); return; } EC_GROUP_free(group); JNI_TRACE("EC_GROUP_clear_free(%p) => success", group); } static jlong NativeCrypto_EC_GROUP_get_generator(JNIEnv* env, jclass, jobject groupRef) { const EC_GROUP* group = fromContextObject
(env, groupRef); JNI_TRACE("EC_GROUP_get_generator(%p)", group); if (group == nullptr) { JNI_TRACE("EC_POINT_get_generator(%p) => group == null", group); return 0; } const EC_POINT* generator = EC_GROUP_get0_generator(group); Unique_EC_POINT dup(EC_POINT_dup(generator, group)); if (dup.get() == nullptr) { JNI_TRACE("EC_GROUP_get_generator(%p) => oom error", group); jniThrowOutOfMemory(env, "unable to dupe generator"); return 0; } JNI_TRACE("EC_GROUP_get_generator(%p) => %p", group, dup.get()); return reinterpret_cast
(dup.release()); } static jlong NativeCrypto_EC_POINT_new(JNIEnv* env, jclass, jobject groupRef) { const EC_GROUP* group = fromContextObject
(env, groupRef); JNI_TRACE("EC_POINT_new(%p)", group); if (group == nullptr) { JNI_TRACE("EC_POINT_new(%p) => group == null", group); return 0; } EC_POINT* point = EC_POINT_new(group); if (point == nullptr) { jniThrowOutOfMemory(env, "Unable create an EC_POINT"); return 0; } return reinterpret_cast
(point); } static void NativeCrypto_EC_POINT_clear_free(JNIEnv* env, jclass, jlong groupRef) { EC_POINT* group = reinterpret_cast
(groupRef); JNI_TRACE("EC_POINT_clear_free(%p)", group); if (group == nullptr) { JNI_TRACE("EC_POINT_clear_free => group == NULL"); jniThrowNullPointerException(env, "group == NULL"); return; } EC_POINT_free(group); JNI_TRACE("EC_POINT_clear_free(%p) => success", group); } static void NativeCrypto_EC_POINT_set_affine_coordinates(JNIEnv* env, jclass, jobject groupRef, jobject pointRef, jbyteArray xjavaBytes, jbyteArray yjavaBytes) { JNI_TRACE("EC_POINT_set_affine_coordinates(%p, %p, %p, %p)", groupRef, pointRef, xjavaBytes, yjavaBytes); const EC_GROUP* group = fromContextObject
(env, groupRef); if (group == nullptr) { return; } EC_POINT* point = fromContextObject
(env, pointRef); if (point == nullptr) { return; } JNI_TRACE("EC_POINT_set_affine_coordinates(%p, %p, %p, %p) <- ptr", group, point, xjavaBytes, yjavaBytes); BIGNUM* xRef = nullptr; if (!arrayToBignum(env, xjavaBytes, &xRef)) { return; } Unique_BIGNUM x(xRef); BIGNUM* yRef = nullptr; if (!arrayToBignum(env, yjavaBytes, &yRef)) { return; } Unique_BIGNUM y(yRef); int ret; switch (get_EC_GROUP_type(group)) { case EC_CURVE_GFP: ret = EC_POINT_set_affine_coordinates_GFp(group, point, x.get(), y.get(), nullptr); break; #if !defined(OPENSSL_NO_EC2M) case EC_CURVE_GF2M: ret = EC_POINT_set_affine_coordinates_GF2m(group, point, x.get(), y.get(), NULL); break; #endif default: jniThrowRuntimeException(env, "invalid curve type"); return; } if (ret != 1) { throwExceptionIfNecessary(env, "EC_POINT_set_affine_coordinates"); } JNI_TRACE("EC_POINT_set_affine_coordinates(%p, %p, %p, %p) => %d", group, point, xjavaBytes, yjavaBytes, ret); } static jobjectArray NativeCrypto_EC_POINT_get_affine_coordinates(JNIEnv* env, jclass, jobject groupRef, jobject pointRef) { JNI_TRACE("EC_POINT_get_affine_coordinates(%p, %p)", groupRef, pointRef); const EC_GROUP* group = fromContextObject
(env, groupRef); if (group == nullptr) { return nullptr; } const EC_POINT* point = fromContextObject
(env, pointRef); if (point == nullptr) { return nullptr; } JNI_TRACE("EC_POINT_get_affine_coordinates(%p, %p) <- ptr", group, point); Unique_BIGNUM x(BN_new()); Unique_BIGNUM y(BN_new()); int ret; switch (get_EC_GROUP_type(group)) { case EC_CURVE_GFP: ret = EC_POINT_get_affine_coordinates_GFp(group, point, x.get(), y.get(), nullptr); break; default: jniThrowRuntimeException(env, "invalid curve type"); return nullptr; } if (ret != 1) { JNI_TRACE("EC_POINT_get_affine_coordinates(%p, %p)", group, point); throwExceptionIfNecessary(env, "EC_POINT_get_affine_coordinates"); return nullptr; } jobjectArray joa = env->NewObjectArray(2, byteArrayClass, nullptr); if (joa == nullptr) { return nullptr; } jbyteArray xBytes = bignumToArray(env, x.get(), "x"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 0, xBytes); jbyteArray yBytes = bignumToArray(env, y.get(), "y"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 1, yBytes); JNI_TRACE("EC_POINT_get_affine_coordinates(%p, %p) => %p", group, point, joa); return joa; } static jlong NativeCrypto_EC_KEY_generate_key(JNIEnv* env, jclass, jobject groupRef) { const EC_GROUP* group = fromContextObject
(env, groupRef); JNI_TRACE("EC_KEY_generate_key(%p)", group); if (group == nullptr) { return 0; } Unique_EC_KEY eckey(EC_KEY_new()); if (eckey.get() == nullptr) { JNI_TRACE("EC_KEY_generate_key(%p) => EC_KEY_new() oom", group); jniThrowOutOfMemory(env, "Unable to create an EC_KEY"); return 0; } if (EC_KEY_set_group(eckey.get(), group) != 1) { JNI_TRACE("EC_KEY_generate_key(%p) => EC_KEY_set_group error", group); throwExceptionIfNecessary(env, "EC_KEY_set_group"); return 0; } if (EC_KEY_generate_key(eckey.get()) != 1) { JNI_TRACE("EC_KEY_generate_key(%p) => EC_KEY_generate_key error", group); throwExceptionIfNecessary(env, "EC_KEY_set_group"); return 0; } Unique_EVP_PKEY pkey(EVP_PKEY_new()); if (pkey.get() == nullptr) { JNI_TRACE("EC_KEY_generate_key(%p) => threw error", group); throwExceptionIfNecessary(env, "EC_KEY_generate_key"); return 0; } if (EVP_PKEY_assign_EC_KEY(pkey.get(), eckey.get()) != 1) { jniThrowRuntimeException(env, "EVP_PKEY_assign_EC_KEY failed"); return 0; } OWNERSHIP_TRANSFERRED(eckey); JNI_TRACE("EC_KEY_generate_key(%p) => %p", group, pkey.get()); return reinterpret_cast
(pkey.release()); } static jlong NativeCrypto_EC_KEY_get1_group(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("EC_KEY_get1_group(%p)", pkey); if (pkey == nullptr) { JNI_TRACE("EC_KEY_get1_group(%p) => pkey == null", pkey); return 0; } if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) { jniThrowRuntimeException(env, "not EC key"); JNI_TRACE("EC_KEY_get1_group(%p) => not EC key (type == %d)", pkey, EVP_PKEY_type(pkey->type)); return 0; } EC_GROUP* group = EC_GROUP_dup(EC_KEY_get0_group(pkey->pkey.ec)); JNI_TRACE("EC_KEY_get1_group(%p) => %p", pkey, group); return reinterpret_cast
(group); } static jbyteArray NativeCrypto_EC_KEY_get_private_key(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("EC_KEY_get_private_key(%p)", pkey); if (pkey == nullptr) { JNI_TRACE("EC_KEY_get_private_key => pkey == NULL"); return nullptr; } Unique_EC_KEY eckey(EVP_PKEY_get1_EC_KEY(pkey)); if (eckey.get() == nullptr) { throwExceptionIfNecessary(env, "EVP_PKEY_get1_EC_KEY"); return nullptr; } const BIGNUM *privkey = EC_KEY_get0_private_key(eckey.get()); jbyteArray privBytes = bignumToArray(env, privkey, "privkey"); if (env->ExceptionCheck()) { JNI_TRACE("EC_KEY_get_private_key(%p) => threw error", pkey); return nullptr; } JNI_TRACE("EC_KEY_get_private_key(%p) => %p", pkey, privBytes); return privBytes; } static jlong NativeCrypto_EC_KEY_get_public_key(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("EC_KEY_get_public_key(%p)", pkey); if (pkey == nullptr) { JNI_TRACE("EC_KEY_get_public_key => pkey == NULL"); return 0; } Unique_EC_KEY eckey(EVP_PKEY_get1_EC_KEY(pkey)); if (eckey.get() == nullptr) { throwExceptionIfNecessary(env, "EVP_PKEY_get1_EC_KEY"); return 0; } Unique_EC_POINT dup(EC_POINT_dup(EC_KEY_get0_public_key(eckey.get()), EC_KEY_get0_group(eckey.get()))); if (dup.get() == nullptr) { JNI_TRACE("EC_KEY_get_public_key(%p) => can't dup public key", pkey); jniThrowRuntimeException(env, "EC_POINT_dup"); return 0; } JNI_TRACE("EC_KEY_get_public_key(%p) => %p", pkey, dup.get()); return reinterpret_cast
(dup.release()); } #if !defined(OPENSSL_IS_BORINGSSL) static void NativeCrypto_EC_KEY_set_nonce_from_hash(JNIEnv* env, jclass, jobject pkeyRef, jboolean enabled) { EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); JNI_TRACE("EC_KEY_set_nonce_from_hash(%p, %d)", pkey, enabled ? 1 : 0); if (pkey == NULL) { JNI_TRACE("EC_KEY_set_nonce_from_hash => pkey == NULL"); return; } Unique_EC_KEY eckey(EVP_PKEY_get1_EC_KEY(pkey)); if (eckey.get() == NULL) { throwExceptionIfNecessary(env, "EVP_PKEY_get1_EC_KEY"); return; } EC_KEY_set_nonce_from_hash(eckey.get(), enabled ? 1 : 0); } #else static void NativeCrypto_EC_KEY_set_nonce_from_hash(JNIEnv*, jclass, jobject, jboolean) { } #endif static jint NativeCrypto_ECDH_compute_key(JNIEnv* env, jclass, jbyteArray outArray, jint outOffset, jobject pubkeyRef, jobject privkeyRef) { JNI_TRACE("ECDH_compute_key(%p, %d, %p, %p)", outArray, outOffset, pubkeyRef, privkeyRef); EVP_PKEY* pubPkey = fromContextObject
(env, pubkeyRef); if (pubPkey == nullptr) { JNI_TRACE("ECDH_compute_key => pubPkey == NULL"); return -1; } EVP_PKEY* privPkey = fromContextObject
(env, privkeyRef); if (privPkey == nullptr) { JNI_TRACE("ECDH_compute_key => privPkey == NULL"); return -1; } JNI_TRACE("ECDH_compute_key(%p, %d, %p, %p) <- ptr", outArray, outOffset, pubPkey, privPkey); ScopedByteArrayRW out(env, outArray); if (out.get() == nullptr) { JNI_TRACE("ECDH_compute_key(%p, %d, %p, %p) can't get output buffer", outArray, outOffset, pubPkey, privPkey); return -1; } if (ARRAY_OFFSET_INVALID(out, outOffset)) { jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", nullptr); return -1; } if (pubPkey == nullptr) { JNI_TRACE("ECDH_compute_key(%p) => pubPkey == null", pubPkey); jniThrowNullPointerException(env, "pubPkey == null"); return -1; } Unique_EC_KEY pubkey(EVP_PKEY_get1_EC_KEY(pubPkey)); if (pubkey.get() == nullptr) { JNI_TRACE("ECDH_compute_key(%p) => can't get public key", pubPkey); throwExceptionIfNecessary(env, "EVP_PKEY_get1_EC_KEY public", throwInvalidKeyException); return -1; } const EC_POINT* pubkeyPoint = EC_KEY_get0_public_key(pubkey.get()); if (pubkeyPoint == nullptr) { JNI_TRACE("ECDH_compute_key(%p) => can't get public key point", pubPkey); throwExceptionIfNecessary(env, "EVP_PKEY_get1_EC_KEY public", throwInvalidKeyException); return -1; } if (privPkey == nullptr) { JNI_TRACE("ECDH_compute_key(%p) => privKey == null", pubPkey); jniThrowNullPointerException(env, "privPkey == null"); return -1; } Unique_EC_KEY privkey(EVP_PKEY_get1_EC_KEY(privPkey)); if (privkey.get() == nullptr) { throwExceptionIfNecessary(env, "EVP_PKEY_get1_EC_KEY private", throwInvalidKeyException); return -1; } int outputLength = ECDH_compute_key(&out[outOffset], out.size() - outOffset, pubkeyPoint, privkey.get(), nullptr // No KDF ); if (outputLength == -1) { JNI_TRACE("ECDH_compute_key(%p) => outputLength = -1", pubPkey); throwExceptionIfNecessary(env, "ECDH_compute_key", throwInvalidKeyException); return -1; } JNI_TRACE("ECDH_compute_key(%p) => outputLength=%d", pubPkey, outputLength); return outputLength; } static jlong NativeCrypto_EVP_MD_CTX_create(JNIEnv* env, jclass) { JNI_TRACE_MD("EVP_MD_CTX_create()"); Unique_EVP_MD_CTX ctx(EVP_MD_CTX_create()); if (ctx.get() == nullptr) { jniThrowOutOfMemory(env, "Unable create a EVP_MD_CTX"); return 0; } JNI_TRACE_MD("EVP_MD_CTX_create() => %p", ctx.get()); return reinterpret_cast
(ctx.release()); } static void NativeCrypto_EVP_MD_CTX_cleanup(JNIEnv* env, jclass, jobject ctxRef) { EVP_MD_CTX* ctx = fromContextObject
(env, ctxRef); JNI_TRACE_MD("EVP_MD_CTX_cleanup(%p)", ctx); if (ctx != nullptr) { EVP_MD_CTX_cleanup(ctx); } } static void NativeCrypto_EVP_MD_CTX_destroy(JNIEnv*, jclass, jlong ctxRef) { EVP_MD_CTX* ctx = reinterpret_cast
(ctxRef); JNI_TRACE_MD("EVP_MD_CTX_destroy(%p)", ctx); if (ctx != nullptr) { EVP_MD_CTX_destroy(ctx); } } static jint NativeCrypto_EVP_MD_CTX_copy_ex(JNIEnv* env, jclass, jobject dstCtxRef, jobject srcCtxRef) { JNI_TRACE_MD("EVP_MD_CTX_copy_ex(%p. %p)", dstCtxRef, srcCtxRef); EVP_MD_CTX* dst_ctx = fromContextObject
(env, dstCtxRef); if (dst_ctx == nullptr) { JNI_TRACE_MD("EVP_MD_CTX_copy_ex => dst_ctx == NULL"); return 0; } const EVP_MD_CTX* src_ctx = fromContextObject
(env, srcCtxRef); if (src_ctx == nullptr) { JNI_TRACE_MD("EVP_MD_CTX_copy_ex => src_ctx == NULL"); return 0; } JNI_TRACE_MD("EVP_MD_CTX_copy_ex(%p. %p) <- ptr", dst_ctx, src_ctx); int result = EVP_MD_CTX_copy_ex(dst_ctx, src_ctx); if (result == 0) { jniThrowRuntimeException(env, "Unable to copy EVP_MD_CTX"); freeOpenSslErrorState(); } JNI_TRACE_MD("EVP_MD_CTX_copy_ex(%p, %p) => %d", dst_ctx, src_ctx, result); return result; } /* * public static native int EVP_DigestFinal_ex(long, byte[], int) */ static jint NativeCrypto_EVP_DigestFinal_ex(JNIEnv* env, jclass, jobject ctxRef, jbyteArray hash, jint offset) { EVP_MD_CTX* ctx = fromContextObject
(env, ctxRef); JNI_TRACE_MD("EVP_DigestFinal_ex(%p, %p, %d)", ctx, hash, offset); if (ctx == nullptr) { JNI_TRACE("EVP_DigestFinal_ex => ctx == NULL"); return -1; } else if (hash == nullptr) { jniThrowNullPointerException(env, "hash == null"); return -1; } ScopedByteArrayRW hashBytes(env, hash); if (hashBytes.get() == nullptr) { return -1; } unsigned int bytesWritten = -1; int ok = EVP_DigestFinal_ex(ctx, reinterpret_cast
(hashBytes.get() + offset), &bytesWritten); if (ok == 0) { throwExceptionIfNecessary(env, "EVP_DigestFinal_ex"); } JNI_TRACE_MD("EVP_DigestFinal_ex(%p, %p, %d) => %d (%d)", ctx, hash, offset, bytesWritten, ok); return bytesWritten; } static jint NativeCrypto_EVP_DigestInit_ex(JNIEnv* env, jclass, jobject evpMdCtxRef, jlong evpMdRef) { EVP_MD_CTX* ctx = fromContextObject
(env, evpMdCtxRef); const EVP_MD* evp_md = reinterpret_cast
(evpMdRef); JNI_TRACE_MD("EVP_DigestInit_ex(%p, %p)", ctx, evp_md); if (ctx == nullptr) { JNI_TRACE("EVP_DigestInit_ex(%p) => ctx == NULL", evp_md); return 0; } else if (evp_md == nullptr) { jniThrowNullPointerException(env, "evp_md == null"); return 0; } int ok = EVP_DigestInit_ex(ctx, evp_md, nullptr); if (ok == 0) { bool exception = throwExceptionIfNecessary(env, "EVP_DigestInit_ex"); if (exception) { JNI_TRACE("EVP_DigestInit_ex(%p) => threw exception", evp_md); return 0; } } JNI_TRACE_MD("EVP_DigestInit_ex(%p, %p) => %d", ctx, evp_md, ok); return ok; } /* * public static native int EVP_get_digestbyname(java.lang.String) */ static jlong NativeCrypto_EVP_get_digestbyname(JNIEnv* env, jclass, jstring algorithm) { JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%p)", algorithm); if (algorithm == nullptr) { jniThrowNullPointerException(env, nullptr); return -1; } ScopedUtfChars algorithmChars(env, algorithm); if (algorithmChars.c_str() == nullptr) { return 0; } JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%s)", algorithmChars.c_str()); #if !defined(OPENSSL_IS_BORINGSSL) const EVP_MD* evp_md = EVP_get_digestbyname(algorithmChars.c_str()); if (evp_md == NULL) { jniThrowRuntimeException(env, "Hash algorithm not found"); return 0; } JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%s) => %p", algorithmChars.c_str(), evp_md); return reinterpret_cast
(evp_md); #else const char *alg = algorithmChars.c_str(); const EVP_MD *md; if (strcasecmp(alg, "md4") == 0) { md = EVP_md4(); } else if (strcasecmp(alg, "md5") == 0) { md = EVP_md5(); } else if (strcasecmp(alg, "sha1") == 0) { md = EVP_sha1(); } else if (strcasecmp(alg, "sha224") == 0) { md = EVP_sha224(); } else if (strcasecmp(alg, "sha256") == 0) { md = EVP_sha256(); } else if (strcasecmp(alg, "sha384") == 0) { md = EVP_sha384(); } else if (strcasecmp(alg, "sha512") == 0) { md = EVP_sha512(); } else { JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%s) => error", alg); jniThrowRuntimeException(env, "Hash algorithm not found"); return 0; } return reinterpret_cast
(md); #endif } /* * public static native int EVP_MD_size(long) */ static jint NativeCrypto_EVP_MD_size(JNIEnv* env, jclass, jlong evpMdRef) { EVP_MD* evp_md = reinterpret_cast
(evpMdRef); JNI_TRACE("NativeCrypto_EVP_MD_size(%p)", evp_md); if (evp_md == nullptr) { jniThrowNullPointerException(env, nullptr); return -1; } int result = EVP_MD_size(evp_md); JNI_TRACE("NativeCrypto_EVP_MD_size(%p) => %d", evp_md, result); return result; } /* * public static int void EVP_MD_block_size(long) */ static jint NativeCrypto_EVP_MD_block_size(JNIEnv* env, jclass, jlong evpMdRef) { EVP_MD* evp_md = reinterpret_cast
(evpMdRef); JNI_TRACE("NativeCrypto_EVP_MD_block_size(%p)", evp_md); if (evp_md == nullptr) { jniThrowNullPointerException(env, nullptr); return -1; } int result = EVP_MD_block_size(evp_md); JNI_TRACE("NativeCrypto_EVP_MD_block_size(%p) => %d", evp_md, result); return result; } 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
(env, evpMdCtxRef); if (mdCtx == nullptr) { JNI_TRACE("%s => mdCtx == NULL", jniName); return 0; } const EVP_MD* md = reinterpret_cast
(evpMdRef); EVP_PKEY* pkey = fromContextObject
(env, pkeyRef); if (pkey == nullptr) { JNI_TRACE("ctx=%p %s => pkey == NULL", mdCtx, jniName); return 0; } JNI_TRACE("%s(%p, %p, %p) <- ptr", jniName, mdCtx, md, pkey); if (md == nullptr) { JNI_TRACE("ctx=%p %s => md == NULL", mdCtx, jniName); jniThrowNullPointerException(env, "md == null"); return 0; } EVP_PKEY_CTX* pctx = nullptr; if (init_func(mdCtx, &pctx, md, (ENGINE*)nullptr, pkey) <= 0) { JNI_TRACE("ctx=%p %s => threw exception", mdCtx, jniName); throwExceptionIfNecessary(env, jniName); return 0; } JNI_TRACE("%s(%p, %p, %p) => success", jniName, mdCtx, md, pkey); return reinterpret_cast
(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, const char *jniName, int (*update_func)(EVP_MD_CTX*, const void *, size_t)) { EVP_MD_CTX* mdCtx = fromContextObject
(env, evpMdCtxRef); const void *p = reinterpret_cast
(inPtr); JNI_TRACE_MD("%s(%p, %p, %d)", jniName, mdCtx, p, inLength); if (mdCtx == nullptr) { return; } if (p == nullptr) { jniThrowNullPointerException(env, nullptr); return; } if (!update_func(mdCtx, p, inLength)) { JNI_TRACE("ctx=%p %s => threw exception", mdCtx, jniName); throwExceptionIfNecessary(env, jniName); } JNI_TRACE_MD("%s(%p, %p, %d) => success", jniName, mdCtx, p, inLength); } static void evpUpdate(JNIEnv* env, jobject evpMdCtxRef, jbyteArray inJavaBytes, jint inOffset, jint inLength, const char *jniName, int (*update_func)(EVP_MD_CTX*, const void *, size_t)) { EVP_MD_CTX* mdCtx = fromContextObject
(env, evpMdCtxRef); JNI_TRACE_MD("%s(%p, %p, %d, %d)", jniName, mdCtx, inJavaBytes, inOffset, inLength); if (mdCtx == nullptr) { return; } ScopedByteArrayRO inBytes(env, inJavaBytes); if (inBytes.get() == nullptr) { return; } if (ARRAY_OFFSET_LENGTH_INVALID(inBytes, inOffset, inLength)) { jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "inBytes"); return; } const unsigned char *tmp = reinterpret_cast
(inBytes.get()); if (!update_func(mdCtx, tmp + inOffset, inLength)) { JNI_TRACE("ctx=%p %s => threw exception", mdCtx, jniName); throwExceptionIfNecessary(env, jniName); } JNI_TRACE_MD("%s(%p, %p, %d, %d) => success", jniName, mdCtx, inJavaBytes, inOffset, inLength); } static void NativeCrypto_EVP_DigestUpdateDirect(JNIEnv* env, jclass, jobject evpMdCtxRef, jlong inPtr, jint inLength) { evpUpdate(env, evpMdCtxRef, inPtr, inLength, "EVP_DigestUpdateDirect", EVP_DigestUpdate); } static void NativeCrypto_EVP_DigestUpdate(JNIEnv* env, jclass, jobject evpMdCtxRef, jbyteArray inJavaBytes, jint inOffset, jint inLength) { evpUpdate(env, evpMdCtxRef, inJavaBytes, inOffset, inLength, "EVP_DigestUpdate", EVP_DigestUpdate); } // EVP_DigestSignUpdate and EVP_DigestVerifyUpdate are functions in BoringSSl but not in OpenSSL. // The reason for the two wrapper functions below is that we need a function pointer which can be // provided to evpUpdate. // TODO: Remove these two wrapper functions once Conscrypt no longer supports OpenSSL or once // OpenSSL offers EVP_DigestSignUpdate and EVP_DigestVerifyUpdate as functions rather than macros. static int evpDigestSignUpdate(EVP_MD_CTX* ctx, const void* d, size_t cnt) { return EVP_DigestSignUpdate(ctx, d, cnt); } static int evpDigestVerifyUpdate(EVP_MD_CTX* ctx, const void* d, size_t cnt) { return EVP_DigestVerifyUpdate(ctx, d, cnt); } static void NativeCrypto_EVP_DigestSignUpdate(JNIEnv* env, jclass, jobject evpMdCtxRef, jbyteArray inJavaBytes, jint inOffset, jint inLength) { evpUpdate(env, evpMdCtxRef, inJavaBytes, inOffset, inLength, "EVP_DigestSignUpdate", evpDigestSignUpdate); } static void NativeCrypto_EVP_DigestSignUpdateDirect(JNIEnv* env, jclass, jobject evpMdCtxRef, jlong inPtr, jint inLength) { evpUpdate(env, evpMdCtxRef, inPtr, inLength, "EVP_DigestSignUpdateDirect", evpDigestSignUpdate); } static void NativeCrypto_EVP_DigestVerifyUpdate(JNIEnv* env, jclass, jobject evpMdCtxRef, jbyteArray inJavaBytes, jint inOffset, jint inLength) { evpUpdate(env, evpMdCtxRef, inJavaBytes, inOffset, inLength, "EVP_DigestVerifyUpdate", evpDigestVerifyUpdate); } static void NativeCrypto_EVP_DigestVerifyUpdateDirect(JNIEnv* env, jclass, jobject evpMdCtxRef, jlong inPtr, jint inLength) { evpUpdate(env, evpMdCtxRef, inPtr, inLength, "EVP_DigestVerifyUpdateDirect", evpDigestVerifyUpdate); } static jbyteArray NativeCrypto_EVP_DigestSignFinal(JNIEnv* env, jclass, jobject evpMdCtxRef) { EVP_MD_CTX* mdCtx = fromContextObject
(env, evpMdCtxRef); JNI_TRACE("EVP_DigestSignFinal(%p)", mdCtx); if (mdCtx == nullptr) { return nullptr; } size_t maxLen; if (EVP_DigestSignFinal(mdCtx, nullptr, &maxLen) != 1) { JNI_TRACE("ctx=%p EVP_DigestSignFinal => threw exception", mdCtx); throwExceptionIfNecessary(env, "EVP_DigestSignFinal"); return nullptr; } UniquePtr
buffer(new unsigned char[maxLen]); if (buffer.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate signature buffer"); return nullptr; } size_t actualLen(maxLen); if (EVP_DigestSignFinal(mdCtx, buffer.get(), &actualLen) != 1) { JNI_TRACE("ctx=%p EVP_DigestSignFinal => threw exception", mdCtx); throwExceptionIfNecessary(env, "EVP_DigestSignFinal"); return nullptr; } if (actualLen > maxLen) { JNI_TRACE("ctx=%p EVP_DigestSignFinal => signature too long: %zd vs %zd", mdCtx, actualLen, maxLen); jniThrowRuntimeException(env, "EVP_DigestSignFinal signature too long"); return nullptr; } ScopedLocalRef
sigJavaBytes(env, env->NewByteArray(actualLen)); if (sigJavaBytes.get() == nullptr) { jniThrowOutOfMemory(env, "Failed to allocate signature byte[]"); return nullptr; } env->SetByteArrayRegion( sigJavaBytes.get(), 0, actualLen, reinterpret_cast
(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
(env, evpMdCtxRef); JNI_TRACE("EVP_DigestVerifyFinal(%p)", mdCtx); if (mdCtx == nullptr) { return 0; } ScopedByteArrayRO sigBytes(env, signature); if (sigBytes.get() == nullptr) { return 0; } if (ARRAY_OFFSET_LENGTH_INVALID(sigBytes, offset, len)) { jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "signature"); return 0; } #if defined(OPENSSL_IS_BORINGSSL) const unsigned char *sigBuf = reinterpret_cast
(sigBytes.get()); #else // Older versions of OpenSSL's EVP_DigestVerifyFinal take a non-const unsigned char *... // TODO: Remove the const_cast and if-else-endif once OpenSSL is upgraded. unsigned char *sigBuf = const_cast
(reinterpret_cast
(sigBytes.get())); #endif int err = EVP_DigestVerifyFinal(mdCtx, sigBuf + offset, len); jboolean result; if (err == 1) { // Signature verified result = 1; } else if (err == 0) { // Signature did not verify result = 0; } else { // Error while verifying signature JNI_TRACE("ctx=%p EVP_DigestVerifyFinal => threw exception", mdCtx); throwExceptionIfNecessary(env, "EVP_DigestVerifyFinal"); return 0; } // If the signature did not verify, BoringSSL error queue contains an error (BAD_SIGNATURE). // Clear the error queue to prevent its state from affecting future operations. freeOpenSslErrorState(); JNI_TRACE("EVP_DigestVerifyFinal(%p) => %d", mdCtx, result); return result; } static void NativeCrypto_EVP_PKEY_CTX_set_rsa_padding(JNIEnv* env, jclass, jlong ctx, jint pad) { EVP_PKEY_CTX* pkeyCtx = reinterpret_cast
(ctx); JNI_TRACE("EVP_PKEY_CTX_set_rsa_padding(%p, %d)", pkeyCtx, pad); if (pkeyCtx == nullptr) { jniThrowNullPointerException(env, "ctx == null"); return; } int result = EVP_PKEY_CTX_set_rsa_padding(pkeyCtx, reinterpret_cast
(pad)); if (result <= 0) { JNI_TRACE("ctx=%p EVP_PKEY_CTX_set_rsa_padding => threw exception", pkeyCtx); throwExceptionIfNecessary(env, "EVP_PKEY_CTX_set_rsa_padding"); return; } JNI_TRACE("EVP_PKEY_CTX_set_rsa_padding(%p, %d) => success", pkeyCtx, pad); } static void NativeCrypto_EVP_PKEY_CTX_set_rsa_pss_saltlen(JNIEnv* env, jclass, jlong ctx, jint len) { EVP_PKEY_CTX* pkeyCtx = reinterpret_cast
(ctx); JNI_TRACE("EVP_PKEY_CTX_set_rsa_pss_saltlen(%p, %d)", pkeyCtx, len); if (pkeyCtx == nullptr) { jniThrowNullPointerException(env, "ctx == null"); return; } int result = EVP_PKEY_CTX_set_rsa_pss_saltlen(pkeyCtx, reinterpret_cast
(len)); if (result <= 0) { JNI_TRACE("ctx=%p EVP_PKEY_CTX_set_rsa_pss_saltlen => threw exception", pkeyCtx); throwExceptionIfNecessary(env, "EVP_PKEY_CTX_set_rsa_pss_saltlen"); return; } JNI_TRACE("EVP_PKEY_CTX_set_rsa_pss_saltlen(%p, %d) => success", pkeyCtx, len); } static void NativeCrypto_EVP_PKEY_CTX_set_rsa_mgf1_md(JNIEnv* env, jclass, jlong ctx, jlong mdCtx) { EVP_PKEY_CTX* pkeyCtx = reinterpret_cast
(ctx); EVP_MD* md = reinterpret_cast
(mdCtx); JNI_TRACE("EVP_PKEY_CTX_set_rsa_mgf1_md(%p, %p)", pkeyCtx, md); if (pkeyCtx == nullptr) { jniThrowNullPointerException(env, "ctx == null"); return; } if (md == nullptr) { jniThrowNullPointerException(env, "mdCtx == null"); return; } int result = EVP_PKEY_CTX_set_rsa_mgf1_md(pkeyCtx, md); if (result <= 0) { JNI_TRACE("ctx=%p EVP_PKEY_CTX_set_rsa_mgf1_md => threw exception", pkeyCtx); throwExceptionIfNecessary(env, "EVP_PKEY_CTX_set_rsa_mgf1_md"); return; } JNI_TRACE("EVP_PKEY_CTX_set_rsa_mgf1_md(%p, %p) => success", pkeyCtx, md); } static jlong NativeCrypto_EVP_get_cipherbyname(JNIEnv* env, jclass, jstring algorithm) { JNI_TRACE("EVP_get_cipherbyname(%p)", algorithm); #if !defined(OPENSSL_IS_BORINGSSL) if (algorithm == NULL) { JNI_TRACE("EVP_get_cipherbyname(%p) => threw exception algorithm == null", algorithm); jniThrowNullPointerException(env, NULL); return -1; } ScopedUtfChars algorithmChars(env, algorithm); if (algorithmChars.c_str() == NULL) { return 0; } JNI_TRACE("EVP_get_cipherbyname(%p) => algorithm = %s", algorithm, algorithmChars.c_str()); const EVP_CIPHER* evp_cipher = EVP_get_cipherbyname(algorithmChars.c_str()); if (evp_cipher == NULL) { freeOpenSslErrorState(); } JNI_TRACE("EVP_get_cipherbyname(%s) => %p", algorithmChars.c_str(), evp_cipher); return reinterpret_cast
(evp_cipher); #else ScopedUtfChars scoped_alg(env, algorithm); const char *alg = scoped_alg.c_str(); const EVP_CIPHER *cipher; if (strcasecmp(alg, "rc4") == 0) { cipher = EVP_rc4(); } else if (strcasecmp(alg, "des-cbc") == 0) { cipher = EVP_des_cbc(); } else if (strcasecmp(alg, "des-ede-cbc") == 0) { cipher = EVP_des_ede_cbc(); } else if (strcasecmp(alg, "des-ede3-cbc") == 0) { cipher = EVP_des_ede3_cbc(); } else if (strcasecmp(alg, "aes-128-ecb") == 0) { cipher = EVP_aes_128_ecb(); } else if (strcasecmp(alg, "aes-128-cbc") == 0) { cipher = EVP_aes_128_cbc(); } else if (strcasecmp(alg, "aes-128-ctr") == 0) { cipher = EVP_aes_128_ctr(); } else if (strcasecmp(alg, "aes-128-gcm") == 0) { cipher = EVP_aes_128_gcm(); } else if (strcasecmp(alg, "aes-192-ecb") == 0) { cipher = EVP_aes_192_ecb(); } else if (strcasecmp(alg, "aes-192-cbc") == 0) { cipher = EVP_aes_192_cbc(); } else if (strcasecmp(alg, "aes-192-ctr") == 0) { cipher = EVP_aes_192_ctr(); } else if (strcasecmp(alg, "aes-192-gcm") == 0) { cipher = EVP_aes_192_gcm(); } else if (strcasecmp(alg, "aes-256-ecb") == 0) { cipher = EVP_aes_256_ecb(); } else if (strcasecmp(alg, "aes-256-cbc") == 0) { cipher = EVP_aes_256_cbc(); } else if (strcasecmp(alg, "aes-256-ctr") == 0) { cipher = EVP_aes_256_ctr(); } else if (strcasecmp(alg, "aes-256-gcm") == 0) { cipher = EVP_aes_256_gcm(); } else { JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%s) => error", alg); return 0; } return reinterpret_cast
(cipher); #endif } static void NativeCrypto_EVP_CipherInit_ex(JNIEnv* env, jclass, jobject ctxRef, jlong evpCipherRef, jbyteArray keyArray, jbyteArray ivArray, jboolean encrypting) { EVP_CIPHER_CTX* ctx = fromContextObject
(env, ctxRef); const EVP_CIPHER* evpCipher = reinterpret_cast
(evpCipherRef); JNI_TRACE("EVP_CipherInit_ex(%p, %p, %p, %p, %d)", ctx, evpCipher, keyArray, ivArray, encrypting ? 1 : 0); if (ctx == nullptr) { JNI_TRACE("EVP_CipherUpdate => ctx == null"); return; } // The key can be null if we need to set extra parameters. UniquePtr
keyPtr; if (keyArray != nullptr) { ScopedByteArrayRO keyBytes(env, keyArray); if (keyBytes.get() == nullptr) { return; } keyPtr.reset(new unsigned char[keyBytes.size()]); memcpy(keyPtr.get(), keyBytes.get(), keyBytes.size()); } // The IV can be null if we're using ECB. UniquePtr
ivPtr; if (ivArray != nullptr) { ScopedByteArrayRO ivBytes(env, ivArray); if (ivBytes.get() == nullptr) { return; } ivPtr.reset(new unsigned char[ivBytes.size()]); memcpy(ivPtr.get(), ivBytes.get(), ivBytes.size()); } if (!EVP_CipherInit_ex(ctx, evpCipher, nullptr, keyPtr.get(), ivPtr.get(), encrypting ? 1 : 0)) { throwExceptionIfNecessary(env, "EVP_CipherInit_ex"); JNI_TRACE("EVP_CipherInit_ex => error initializing cipher"); return; } JNI_TRACE("EVP_CipherInit_ex(%p, %p, %p, %p, %d) => success", ctx, evpCipher, keyArray, ivArray, encrypting ? 1 : 0); } /* * public static native int EVP_CipherUpdate(long ctx, byte[] out, int outOffset, byte[] in, * int inOffset, int inLength); */ static jint NativeCrypto_EVP_CipherUpdate(JNIEnv* env, jclass, jobject ctxRef, jbyteArray outArray, jint outOffset, jbyteArray inArray, jint inOffset, jint inLength) { EVP_CIPHER_CTX* ctx = fromContextObject
(env, ctxRef); JNI_TRACE("EVP_CipherUpdate(%p, %p, %d, %p, %d)", ctx, outArray, outOffset, inArray, inOffset); if (ctx == nullptr) { JNI_TRACE("ctx=%p EVP_CipherUpdate => ctx == null", ctx); return 0; } ScopedByteArrayRO inBytes(env, inArray); if (inBytes.get() == nullptr) { return 0; } if (ARRAY_OFFSET_LENGTH_INVALID(inBytes, inOffset, inLength)) { jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "inBytes"); return 0; } ScopedByteArrayRW outBytes(env, outArray); if (outBytes.get() == nullptr) { return 0; } if (ARRAY_OFFSET_LENGTH_INVALID(outBytes, outOffset, inLength)) { jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "outBytes"); return 0; } JNI_TRACE("ctx=%p EVP_CipherUpdate in=%p in.length=%zd inOffset=%zd inLength=%zd out=%p out.length=%zd outOffset=%zd", ctx, inBytes.get(), inBytes.size(), inOffset, inLength, outBytes.get(), outBytes.size(), outOffset); unsigned char* out = reinterpret_cast
(outBytes.get()); const unsigned char* in = reinterpret_cast
(inBytes.get()); int outl; if (!EVP_CipherUpdate(ctx, out + outOffset, &outl, in + inOffset, inLength)) { throwExceptionIfNecessary(env, "EVP_CipherUpdate"); JNI_TRACE("ctx=%p EVP_CipherUpdate => threw error", ctx); return 0; } JNI_TRACE("EVP_CipherUpdate(%p, %p, %d, %p, %d) => %d", ctx, outArray, outOffset, inArray, inOffset, outl); return outl; } static jint NativeCrypto_EVP_CipherFinal_ex(JNIEnv* env, jclass, jobject ctxRef, jbyteArray outArray, jint outOffset) { EVP_CIPHER_CTX* ctx = fromContextObject
(env, ctxRef); JNI_TRACE("EVP_CipherFinal_ex(%p, %p, %d)", ctx, outArray, outOffset); if (ctx == nullptr) { JNI_TRACE("ctx=%p EVP_CipherFinal_ex => ctx == null", ctx); return 0; } ScopedByteArrayRW outBytes(env, outArray); if (outBytes.get() == nullptr) { return 0; } unsigned char* out = reinterpret_cast
(outBytes.get()); int outl; if (!EVP_CipherFinal_ex(ctx, out + outOffset, &outl)) { if (throwExceptionIfNecessary(env, "EVP_CipherFinal_ex")) { JNI_TRACE("ctx=%p EVP_CipherFinal_ex => threw error", ctx); } else { throwBadPaddingException(env, "EVP_CipherFinal_ex"); JNI_TRACE("ctx=%p EVP_CipherFinal_ex => threw padding exception", ctx); } return 0; } JNI_TRACE("EVP_CipherFinal(%p, %p, %d) => %d", ctx, outArray, outOffset, outl); return outl; } static jint NativeCrypto_EVP_CIPHER_iv_length(JNIEnv* env, jclass, jlong evpCipherRef) { const EVP_CIPHER* evpCipher = reinterpret_cast