/* * 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 <arpa/inet.h> #include <fcntl.h> #include <poll.h> #include <pthread.h> #include <sys/socket.h> #include <sys/syscall.h> #include <unistd.h> #ifdef CONSCRYPT_UNBUNDLED #include <dlfcn.h> #endif #include <jni.h> #include <openssl/asn1t.h> #include <openssl/engine.h> #include <openssl/err.h> #include <openssl/evp.h> #include <openssl/hmac.h> #include <openssl/rand.h> #include <openssl/rsa.h> #include <openssl/ssl.h> #include <openssl/x509v3.h> #if defined(OPENSSL_IS_BORINGSSL) #include <openssl/aead.h> #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<void*>(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:..." <CR> * "RSA Session-ID:... Master-Key:..." <CR> * <etc> * 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<unsigned char, OPENSSL_Delete> Unique_OPENSSL_str; struct BIO_Delete { void operator()(BIO* p) const { BIO_free_all(p); } }; typedef UniquePtr<BIO, BIO_Delete> Unique_BIO; struct BIGNUM_Delete { void operator()(BIGNUM* p) const { BN_free(p); } }; typedef UniquePtr<BIGNUM, BIGNUM_Delete> Unique_BIGNUM; struct BN_CTX_Delete { void operator()(BN_CTX* ctx) const { BN_CTX_free(ctx); } }; typedef UniquePtr<BN_CTX, BN_CTX_Delete> Unique_BN_CTX; struct ASN1_INTEGER_Delete { void operator()(ASN1_INTEGER* p) const { ASN1_INTEGER_free(p); } }; typedef UniquePtr<ASN1_INTEGER, ASN1_INTEGER_Delete> Unique_ASN1_INTEGER; struct DSA_Delete { void operator()(DSA* p) const { DSA_free(p); } }; typedef UniquePtr<DSA, DSA_Delete> Unique_DSA; struct EC_GROUP_Delete { void operator()(EC_GROUP* p) const { EC_GROUP_free(p); } }; typedef UniquePtr<EC_GROUP, EC_GROUP_Delete> Unique_EC_GROUP; struct EC_POINT_Delete { void operator()(EC_POINT* p) const { EC_POINT_clear_free(p); } }; typedef UniquePtr<EC_POINT, EC_POINT_Delete> Unique_EC_POINT; struct EC_KEY_Delete { void operator()(EC_KEY* p) const { EC_KEY_free(p); } }; typedef UniquePtr<EC_KEY, EC_KEY_Delete> Unique_EC_KEY; struct EVP_MD_CTX_Delete { void operator()(EVP_MD_CTX* p) const { EVP_MD_CTX_destroy(p); } }; typedef UniquePtr<EVP_MD_CTX, EVP_MD_CTX_Delete> 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<EVP_AEAD_CTX, EVP_AEAD_CTX_Delete> Unique_EVP_AEAD_CTX; #endif struct EVP_CIPHER_CTX_Delete { void operator()(EVP_CIPHER_CTX* p) const { EVP_CIPHER_CTX_free(p); } }; typedef UniquePtr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_Delete> Unique_EVP_CIPHER_CTX; struct EVP_PKEY_Delete { void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); } }; typedef UniquePtr<EVP_PKEY, EVP_PKEY_Delete> 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<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> Unique_PKCS8_PRIV_KEY_INFO; struct RSA_Delete { void operator()(RSA* p) const { RSA_free(p); } }; typedef UniquePtr<RSA, RSA_Delete> Unique_RSA; struct ASN1_BIT_STRING_Delete { void operator()(ASN1_BIT_STRING* p) const { ASN1_BIT_STRING_free(p); } }; typedef UniquePtr<ASN1_BIT_STRING, ASN1_BIT_STRING_Delete> Unique_ASN1_BIT_STRING; struct ASN1_OBJECT_Delete { void operator()(ASN1_OBJECT* p) const { ASN1_OBJECT_free(p); } }; typedef UniquePtr<ASN1_OBJECT, ASN1_OBJECT_Delete> Unique_ASN1_OBJECT; struct ASN1_GENERALIZEDTIME_Delete { void operator()(ASN1_GENERALIZEDTIME* p) const { ASN1_GENERALIZEDTIME_free(p); } }; typedef UniquePtr<ASN1_GENERALIZEDTIME, ASN1_GENERALIZEDTIME_Delete> Unique_ASN1_GENERALIZEDTIME; struct SSL_Delete { void operator()(SSL* p) const { SSL_free(p); } }; typedef UniquePtr<SSL, SSL_Delete> Unique_SSL; struct SSL_CTX_Delete { void operator()(SSL_CTX* p) const { SSL_CTX_free(p); } }; typedef UniquePtr<SSL_CTX, SSL_CTX_Delete> Unique_SSL_CTX; struct X509_Delete { void operator()(X509* p) const { X509_free(p); } }; typedef UniquePtr<X509, X509_Delete> Unique_X509; struct X509_NAME_Delete { void operator()(X509_NAME* p) const { X509_NAME_free(p); } }; typedef UniquePtr<X509_NAME, X509_NAME_Delete> Unique_X509_NAME; struct X509_EXTENSIONS_Delete { void operator()(X509_EXTENSIONS* p) const { sk_X509_EXTENSION_pop_free(p, X509_EXTENSION_free); } }; typedef UniquePtr<X509_EXTENSIONS, X509_EXTENSIONS_Delete> Unique_X509_EXTENSIONS; #if !defined(OPENSSL_IS_BORINGSSL) struct PKCS7_Delete { void operator()(PKCS7* p) const { PKCS7_free(p); } }; typedef UniquePtr<PKCS7, PKCS7_Delete> 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<STACK_OF(SSL_CIPHER), sk_SSL_CIPHER_Delete> Unique_sk_SSL_CIPHER; struct sk_X509_Delete { void operator()(STACK_OF(X509)* p) const { sk_X509_pop_free(p, X509_free); } }; typedef UniquePtr<STACK_OF(X509), sk_X509_Delete> 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<STACK_OF(X509_CRL), sk_X509_CRL_Delete> 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<STACK_OF(X509_NAME), sk_X509_NAME_Delete> 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<STACK_OF(ASN1_OBJECT), sk_ASN1_OBJECT_Delete> 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<STACK_OF(GENERAL_NAME), sk_GENERAL_NAME_Delete> 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<ssize_t>(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<ssize_t>(array.size()) || len < 0 || \ len > static_cast<ssize_t>(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 <code>throwIfNull</code> is * passed as <code>true</code>, then this function will call * <code>throwSSLExceptionStr</code> 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<SSL_CTX*>(static_cast<uintptr_t>(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<SSL*>(static_cast<uintptr_t>(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<SSL_SESSION*>(static_cast<uintptr_t>(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<SSL_CIPHER*>(static_cast<uintptr_t>(ssl_cipher_address)); if ((ssl_cipher == nullptr) && throwIfNull) { JNI_TRACE("ssl_cipher == null"); jniThrowNullPointerException(env, "ssl_cipher == null"); } return ssl_cipher; } template<typename T> static T* fromContextObject(JNIEnv* env, jobject contextObject) { if (contextObject == nullptr) { JNI_TRACE("contextObject == null"); jniThrowNullPointerException(env, "contextObject == null"); return nullptr; } T* ref = reinterpret_cast<T*>(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<const unsigned char*>(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<unsigned char[]> 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<unsigned char*>(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<const uint8_t*>(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<unsigned char*>(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_<TYPE>" * from the OpenSSL ASN.1 API. */ template<typename T> 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<jbyteArray> 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<unsigned char*>(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<typename T, T* (*d2i_func)(T**, const unsigned char**, long)> 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<const unsigned char*>(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<jbooleanArray> bitsRef(env, env->NewBooleanArray(size)); if (bitsRef.get() == nullptr) { return nullptr; } ScopedBooleanArrayRW bitsArray(env, bitsRef.get()); for (int i = 0; i < static_cast<int>(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<void**>(&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<jbyteArray> 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<jbyte*>(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<jbyteArray> 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<const jbyte*>(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<BIO_Stream*>(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<BIO_InputStream*>(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<BIO_OutputStream*>(b->ptr); return stream->write(buf, len); } static int bio_stream_puts(BIO *b, const char *buf) { BIO_OutputStream* stream = static_cast<BIO_OutputStream*>(b->ptr); return stream->write(buf, strlen(buf)); } static int bio_stream_gets(BIO *b, char *buf, int len) { BIO_InputStream* stream = static_cast<BIO_InputStream*>(b->ptr); return stream->gets(buf, len); } static void bio_stream_assign(BIO *b, BIO_Stream* stream) { b->ptr = static_cast<void*>(stream); } static long bio_stream_ctrl(BIO *b, int cmd, long, void *) { BIO_Stream* stream = static_cast<BIO_Stream*>(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<jbyteArray> 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<jbyteArray>(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<jbyteArray> 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<jbyteArray>(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 <openssl/rsa.h> 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<jobject>(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<jbyteArray> signature( env, rawSignDigestWithPrivateKey(env, private_key, reinterpret_cast<const char*>(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<size_t>(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<jobject>(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<jbyteArray> cleartext(env, rsaDecryptWithPrivateKey(env, private_key, padding, reinterpret_cast<const char*>(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<size_t>(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<jobject>(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<jobject>(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<jobject>(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<jbyteArray> signature( env, rawSignDigestWithPrivateKey(env, private_key, reinterpret_cast<const char*>(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<size_t>(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<const unsigned char*>(signatureBytes.get()); long siglen = static_cast<long>(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<KeyExData*>(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<KeyExData*>(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<jbyteArray> signature( env, rawSignDigestWithPrivateKey( env, ex_data->private_key, reinterpret_cast<const char*>(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<size_t>(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<jbyteArray> cleartext( env, rsaDecryptWithPrivateKey( env, ex_data->private_key, padding, reinterpret_cast<const char*>(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<KeyExData*>(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<KeyExData*>(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<jbyteArray> signature( env, rawSignDigestWithPrivateKey(env, private_key, reinterpret_cast<const char*>(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<uintptr_t>(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<ENGINE*>(static_cast<uintptr_t>(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<ENGINE*>(static_cast<uintptr_t>(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<ENGINE*>(static_cast<uintptr_t>(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<ENGINE*>(static_cast<uintptr_t>(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<ENGINE*>(static_cast<uintptr_t>(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<uintptr_t>(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<uintptr_t>(pkey.release()); #endif #endif } #if !defined(OPENSSL_IS_BORINGSSL) static jstring NativeCrypto_ENGINE_get_id(JNIEnv* env, jclass, jlong engineRef) { ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(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<jstring> 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<jstring> 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<ENGINE*>(static_cast<uintptr_t>(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<ScopedUtfChars> 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<uintptr_t>(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<EC_GROUP>(env, groupRef); if (group == nullptr) { return 0; } const EC_POINT* pubkey = pubkeyRef == nullptr ? nullptr : fromContextObject<EC_POINT>(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<uintptr_t>(pkey.release()); } static int NativeCrypto_EVP_PKEY_type(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(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<EVP_PKEY>(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<EVP_PKEY>(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<EVP_PKEY*>(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<EVP_PKEY>(env, pkey1Ref); if (pkey1 == nullptr) { JNI_TRACE("EVP_PKEY_cmp => pkey1 == NULL"); return 0; } EVP_PKEY* pkey2 = fromContextObject<EVP_PKEY>(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<EVP_PKEY>(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<PKCS8_PRIV_KEY_INFO>(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<const unsigned char*>(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<uintptr_t>(pkey.release()); } /* * static native byte[] i2d_PUBKEY(int) */ static jbyteArray NativeCrypto_i2d_PUBKEY(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef); JNI_TRACE("i2d_PUBKEY(%p)", pkey); if (pkey == nullptr) { return nullptr; } return ASN1ToByteArray<EVP_PKEY>(env, pkey, reinterpret_cast<int (*) (EVP_PKEY*, uint8_t **)>(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<const unsigned char*>(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<uintptr_t>(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<uintptr_t>(pkey.release()); } static jlong NativeCrypto_getECPrivateKeyWrapper(JNIEnv* env, jclass, jobject javaKey, jobject groupRef) { EC_GROUP* group = fromContextObject<EC_GROUP>(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<uintptr_t>(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<uintptr_t>(pkey.release()); } static jint NativeCrypto_RSA_size(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(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<jint>(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<EVP_PKEY>(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<size_t>(flen), #else static_cast<int>(flen), #endif reinterpret_cast<const unsigned char*>(from.get()), reinterpret_cast<unsigned char*>(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<jint>(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<EVP_PKEY>(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<EVP_PKEY>(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<uintptr_t>(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<uintptr_t>(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<EC_GROUP>(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<EC_GROUP>(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<point_conversion_form_t>(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<EC_GROUP>(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<EC_GROUP>(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<EC_GROUP>(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<EC_GROUP>(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<EC_GROUP>(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<EC_GROUP>(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<EC_GROUP*>(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<EC_GROUP>(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<uintptr_t>(dup.release()); } static jlong NativeCrypto_EC_POINT_new(JNIEnv* env, jclass, jobject groupRef) { const EC_GROUP* group = fromContextObject<EC_GROUP>(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<uintptr_t>(point); } static void NativeCrypto_EC_POINT_clear_free(JNIEnv* env, jclass, jlong groupRef) { EC_POINT* group = reinterpret_cast<EC_POINT*>(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<EC_GROUP>(env, groupRef); if (group == nullptr) { return; } EC_POINT* point = fromContextObject<EC_POINT>(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<EC_GROUP>(env, groupRef); if (group == nullptr) { return nullptr; } const EC_POINT* point = fromContextObject<EC_POINT>(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<EC_GROUP>(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<uintptr_t>(pkey.release()); } static jlong NativeCrypto_EC_KEY_get1_group(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(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<uintptr_t>(group); } static jbyteArray NativeCrypto_EC_KEY_get_private_key(JNIEnv* env, jclass, jobject pkeyRef) { EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(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<EVP_PKEY>(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<uintptr_t>(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<EVP_PKEY>(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<EVP_PKEY>(env, pubkeyRef); if (pubPkey == nullptr) { JNI_TRACE("ECDH_compute_key => pubPkey == NULL"); return -1; } EVP_PKEY* privPkey = fromContextObject<EVP_PKEY>(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<uintptr_t>(ctx.release()); } static void NativeCrypto_EVP_MD_CTX_cleanup(JNIEnv* env, jclass, jobject ctxRef) { EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(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<EVP_MD_CTX*>(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<EVP_MD_CTX>(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<EVP_MD_CTX>(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<EVP_MD_CTX>(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<unsigned char*>(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<EVP_MD_CTX>(env, evpMdCtxRef); const EVP_MD* evp_md = reinterpret_cast<const EVP_MD*>(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<uintptr_t>(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<uintptr_t>(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<EVP_MD*>(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<EVP_MD*>(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<EVP_MD_CTX>(env, evpMdCtxRef); if (mdCtx == nullptr) { JNI_TRACE("%s => mdCtx == NULL", jniName); return 0; } const EVP_MD* md = reinterpret_cast<const EVP_MD*>(evpMdRef); EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef); if (pkey == 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<jlong>(pctx); } static jlong NativeCrypto_EVP_DigestSignInit(JNIEnv* env, jclass, jobject evpMdCtxRef, const jlong evpMdRef, jobject pkeyRef) { return evpDigestSignVerifyInit( env, EVP_DigestSignInit, "EVP_DigestSignInit", evpMdCtxRef, evpMdRef, pkeyRef); } static jlong NativeCrypto_EVP_DigestVerifyInit(JNIEnv* env, jclass, jobject evpMdCtxRef, const jlong evpMdRef, jobject pkeyRef) { return evpDigestSignVerifyInit( env, EVP_DigestVerifyInit, "EVP_DigestVerifyInit", evpMdCtxRef, evpMdRef, pkeyRef); } static void evpUpdate(JNIEnv* env, jobject evpMdCtxRef, jlong inPtr, jint inLength, const char *jniName, int (*update_func)(EVP_MD_CTX*, const void *, size_t)) { EVP_MD_CTX* mdCtx = fromContextObject<EVP_MD_CTX>(env, evpMdCtxRef); const void *p = reinterpret_cast<const void *>(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<EVP_MD_CTX>(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<const unsigned char *>(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<EVP_MD_CTX>(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<unsigned char[]> 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<jbyteArray> 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<jbyte*>(buffer.get())); JNI_TRACE("EVP_DigestSignFinal(%p) => %p", mdCtx, sigJavaBytes.get()); return sigJavaBytes.release(); } static jboolean NativeCrypto_EVP_DigestVerifyFinal(JNIEnv* env, jclass, jobject evpMdCtxRef, jbyteArray signature, jint offset, jint len) { EVP_MD_CTX* mdCtx = fromContextObject<EVP_MD_CTX>(env, evpMdCtxRef); JNI_TRACE("EVP_DigestVerifyFinal(%p)", mdCtx); if (mdCtx == 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<const unsigned char *>(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<unsigned char *>(reinterpret_cast<const unsigned char *>(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<EVP_PKEY_CTX*>(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<int>(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<EVP_PKEY_CTX*>(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<int>(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<EVP_PKEY_CTX*>(ctx); EVP_MD* md = reinterpret_cast<EVP_MD*>(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<uintptr_t>(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<uintptr_t>(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<EVP_CIPHER_CTX>(env, ctxRef); const EVP_CIPHER* evpCipher = reinterpret_cast<const EVP_CIPHER*>(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<unsigned char[]> 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<unsigned char[]> 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<EVP_CIPHER_CTX>(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<unsigned char*>(outBytes.get()); const unsigned char* in = reinterpret_cast<const unsigned char*>(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<EVP_CIPHER_CTX>(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<unsigned char*>(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<const EVP_CIPHER*>(evpCipherRef); JNI_TRACE("EVP_CIPHER_iv_length(%p)", evpCipher); if (evpCipher == nullptr) { jniThrowNullPointerException(env, "evpCipher == null"); JNI_TRACE("EVP_CIPHER_iv_length => evpCipher == null"); return 0; } const int ivLength = EVP_CIPHER_iv_length(evpCipher); JNI_TRACE("EVP_CIPHER_iv_length(%p) => %d", evpCipher, ivLength); return ivLength; } static jlong NativeCrypto_EVP_CIPHER_CTX_new(JNIEnv* env, jclass) { JNI_TRACE("EVP_CIPHER_CTX_new()"); Unique_EVP_CIPHER_CTX ctx(EVP_CIPHER_CTX_new()); if (ctx.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate cipher context"); JNI_TRACE("EVP_CipherInit_ex => context allocation error"); return 0; } JNI_TRACE("EVP_CIPHER_CTX_new() => %p", ctx.get()); return reinterpret_cast<uintptr_t>(ctx.release()); } static jint NativeCrypto_EVP_CIPHER_CTX_block_size(JNIEnv* env, jclass, jobject ctxRef) { EVP_CIPHER_CTX* ctx = fromContextObject<EVP_CIPHER_CTX>(env, ctxRef); JNI_TRACE("EVP_CIPHER_CTX_block_size(%p)", ctx); if (ctx == nullptr) { JNI_TRACE("ctx=%p EVP_CIPHER_CTX_block_size => ctx == null", ctx); return 0; } int blockSize = EVP_CIPHER_CTX_block_size(ctx); JNI_TRACE("EVP_CIPHER_CTX_block_size(%p) => %d", ctx, blockSize); return blockSize; } static jint NativeCrypto_get_EVP_CIPHER_CTX_buf_len(JNIEnv* env, jclass, jobject ctxRef) { EVP_CIPHER_CTX* ctx = fromContextObject<EVP_CIPHER_CTX>(env, ctxRef); JNI_TRACE("get_EVP_CIPHER_CTX_buf_len(%p)", ctx); if (ctx == nullptr) { JNI_TRACE("ctx=%p get_EVP_CIPHER_CTX_buf_len => ctx == null", ctx); return 0; } int buf_len = ctx->buf_len; JNI_TRACE("get_EVP_CIPHER_CTX_buf_len(%p) => %d", ctx, buf_len); return buf_len; } static jboolean NativeCrypto_get_EVP_CIPHER_CTX_final_used(JNIEnv* env, jclass, jobject ctxRef) { EVP_CIPHER_CTX* ctx = fromContextObject<EVP_CIPHER_CTX>(env, ctxRef); JNI_TRACE("get_EVP_CIPHER_CTX_final_used(%p)", ctx); if (ctx == nullptr) { JNI_TRACE("ctx=%p get_EVP_CIPHER_CTX_final_used => ctx == null", ctx); return 0; } bool final_used = ctx->final_used != 0; JNI_TRACE("get_EVP_CIPHER_CTX_final_used(%p) => %d", ctx, final_used); return final_used; } static void NativeCrypto_EVP_CIPHER_CTX_set_padding(JNIEnv* env, jclass, jobject ctxRef, jboolean enablePaddingBool) { EVP_CIPHER_CTX* ctx = fromContextObject<EVP_CIPHER_CTX>(env, ctxRef); jint enablePadding = enablePaddingBool ? 1 : 0; JNI_TRACE("EVP_CIPHER_CTX_set_padding(%p, %d)", ctx, enablePadding); if (ctx == nullptr) { JNI_TRACE("ctx=%p EVP_CIPHER_CTX_set_padding => ctx == null", ctx); return; } EVP_CIPHER_CTX_set_padding(ctx, enablePadding); // Not void, but always returns 1. JNI_TRACE("EVP_CIPHER_CTX_set_padding(%p, %d) => success", ctx, enablePadding); } static void NativeCrypto_EVP_CIPHER_CTX_set_key_length(JNIEnv* env, jclass, jobject ctxRef, jint keySizeBits) { EVP_CIPHER_CTX* ctx = fromContextObject<EVP_CIPHER_CTX>(env, ctxRef); JNI_TRACE("EVP_CIPHER_CTX_set_key_length(%p, %d)", ctx, keySizeBits); if (ctx == nullptr) { JNI_TRACE("ctx=%p EVP_CIPHER_CTX_set_key_length => ctx == null", ctx); return; } if (!EVP_CIPHER_CTX_set_key_length(ctx, keySizeBits)) { throwExceptionIfNecessary(env, "NativeCrypto_EVP_CIPHER_CTX_set_key_length"); JNI_TRACE("NativeCrypto_EVP_CIPHER_CTX_set_key_length => threw error"); return; } JNI_TRACE("EVP_CIPHER_CTX_set_key_length(%p, %d) => success", ctx, keySizeBits); } static void NativeCrypto_EVP_CIPHER_CTX_free(JNIEnv*, jclass, jlong ctxRef) { EVP_CIPHER_CTX* ctx = reinterpret_cast<EVP_CIPHER_CTX*>(ctxRef); JNI_TRACE("EVP_CIPHER_CTX_free(%p)", ctx); EVP_CIPHER_CTX_free(ctx); } static jlong NativeCrypto_EVP_aead_aes_128_gcm(JNIEnv* env, jclass) { #if defined(OPENSSL_IS_BORINGSSL) UNUSED_ARGUMENT(env); const EVP_AEAD* ctx = EVP_aead_aes_128_gcm(); JNI_TRACE("EVP_aead_aes_128_gcm => ctx=%p", ctx); return reinterpret_cast<jlong>(ctx); #else jniThrowRuntimeException(env, "Not supported for OpenSSL"); return 0; #endif } static jlong NativeCrypto_EVP_aead_aes_256_gcm(JNIEnv* env, jclass) { #if defined(OPENSSL_IS_BORINGSSL) UNUSED_ARGUMENT(env); const EVP_AEAD* ctx = EVP_aead_aes_256_gcm(); JNI_TRACE("EVP_aead_aes_256_gcm => ctx=%p", ctx); return reinterpret_cast<jlong>(ctx); #else jniThrowRuntimeException(env, "Not supported for OpenSSL"); return 0; #endif } static jlong NativeCrypto_EVP_AEAD_CTX_init(JNIEnv* env, jclass, jlong evpAeadRef, jbyteArray keyArray, jint tagLen) { #if defined(OPENSSL_IS_BORINGSSL) const EVP_AEAD* evpAead = reinterpret_cast<const EVP_AEAD*>(evpAeadRef); JNI_TRACE("EVP_AEAD_CTX_init(%p, %p, %d)", evpAead, keyArray, tagLen); ScopedByteArrayRO keyBytes(env, keyArray); if (keyBytes.get() == nullptr) { return 0; } Unique_EVP_AEAD_CTX aeadCtx(reinterpret_cast<EVP_AEAD_CTX*>( OPENSSL_malloc(sizeof(EVP_AEAD_CTX)))); memset(aeadCtx.get(), 0, sizeof(EVP_AEAD_CTX)); const uint8_t* tmp = reinterpret_cast<const uint8_t*>(keyBytes.get()); int ret = EVP_AEAD_CTX_init(aeadCtx.get(), evpAead, tmp, keyBytes.size(), tagLen, nullptr); if (ret != 1) { throwExceptionIfNecessary(env, "EVP_AEAD_CTX_init"); JNI_TRACE("EVP_AEAD_CTX_init(%p, %p, %d) => fail EVP_AEAD_CTX_init", evpAead, keyArray, tagLen); return 0; } JNI_TRACE("EVP_AEAD_CTX_init(%p, %p, %d) => %p", evpAead, keyArray, tagLen, aeadCtx.get()); return reinterpret_cast<jlong>(aeadCtx.release()); #else UNUSED_ARGUMENT(env); UNUSED_ARGUMENT(evpAeadRef); UNUSED_ARGUMENT(keyArray); UNUSED_ARGUMENT(tagLen); jniThrowRuntimeException(env, "Not supported for OpenSSL"); return 0; #endif } static void NativeCrypto_EVP_AEAD_CTX_cleanup(JNIEnv* env, jclass, jlong evpAeadCtxRef) { #if defined(OPENSSL_IS_BORINGSSL) EVP_AEAD_CTX* evpAeadCtx = reinterpret_cast<EVP_AEAD_CTX*>(evpAeadCtxRef); JNI_TRACE("EVP_AEAD_CTX_cleanup(%p)", evpAeadCtx); if (evpAeadCtx == nullptr) { jniThrowNullPointerException(env, "evpAead == null"); return; } EVP_AEAD_CTX_cleanup(evpAeadCtx); OPENSSL_free(evpAeadCtx); #else UNUSED_ARGUMENT(env); UNUSED_ARGUMENT(evpAeadCtxRef); jniThrowRuntimeException(env, "Not supported for OpenSSL"); #endif } static jint NativeCrypto_EVP_AEAD_max_overhead(JNIEnv* env, jclass, jlong evpAeadRef) { #if defined(OPENSSL_IS_BORINGSSL) const EVP_AEAD* evpAead = reinterpret_cast<const EVP_AEAD*>(evpAeadRef); JNI_TRACE("EVP_AEAD_max_overhead(%p)", evpAead); if (evpAead == nullptr) { jniThrowNullPointerException(env, "evpAead == null"); return 0; } int maxOverhead = EVP_AEAD_max_overhead(evpAead); JNI_TRACE("EVP_AEAD_max_overhead(%p) => %d", evpAead, maxOverhead); return maxOverhead; #else UNUSED_ARGUMENT(env); UNUSED_ARGUMENT(evpAeadRef); jniThrowRuntimeException(env, "Not supported for OpenSSL"); return 0; #endif } static jint NativeCrypto_EVP_AEAD_nonce_length(JNIEnv* env, jclass, jlong evpAeadRef) { #if defined(OPENSSL_IS_BORINGSSL) const EVP_AEAD* evpAead = reinterpret_cast<const EVP_AEAD*>(evpAeadRef); JNI_TRACE("EVP_AEAD_nonce_length(%p)", evpAead); if (evpAead == nullptr) { jniThrowNullPointerException(env, "evpAead == null"); return 0; } int nonceLength = EVP_AEAD_nonce_length(evpAead); JNI_TRACE("EVP_AEAD_nonce_length(%p) => %d", evpAead, nonceLength); return nonceLength; #else UNUSED_ARGUMENT(env); UNUSED_ARGUMENT(evpAeadRef); jniThrowRuntimeException(env, "Not supported for OpenSSL"); return 0; #endif } static jint NativeCrypto_EVP_AEAD_max_tag_len(JNIEnv* env, jclass, jlong evpAeadRef) { #if defined(OPENSSL_IS_BORINGSSL) const EVP_AEAD* evpAead = reinterpret_cast<const EVP_AEAD*>(evpAeadRef); JNI_TRACE("EVP_AEAD_max_tag_len(%p)", evpAead); if (evpAead == nullptr) { jniThrowNullPointerException(env, "evpAead == null"); return 0; } int maxTagLen = EVP_AEAD_max_tag_len(evpAead); JNI_TRACE("EVP_AEAD_max_tag_len(%p) => %d", evpAead, maxTagLen); return maxTagLen; #else UNUSED_ARGUMENT(env); UNUSED_ARGUMENT(evpAeadRef); jniThrowRuntimeException(env, "Not supported for OpenSSL"); return 0; #endif } #if defined(OPENSSL_IS_BORINGSSL) typedef int (*evp_aead_ctx_op_func)(const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out_len, const uint8_t *nonce, size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *ad, size_t ad_len); static jint evp_aead_ctx_op(JNIEnv* env, jobject ctxRef, jbyteArray outArray, jint outOffset, jbyteArray nonceArray, jbyteArray inArray, jint inOffset, jint inLength, jbyteArray aadArray, evp_aead_ctx_op_func realFunc) { EVP_AEAD_CTX* ctx = fromContextObject<EVP_AEAD_CTX>(env, ctxRef); JNI_TRACE("evp_aead_ctx_op(%p, %p, %d, %p, %p, %d, %d, %p)", ctx, outArray, outOffset, nonceArray, inArray, inOffset, inLength, aadArray); ScopedByteArrayRW outBytes(env, outArray); if (outBytes.get() == nullptr) { return 0; } if (ARRAY_OFFSET_INVALID(outBytes, outOffset)) { JNI_TRACE("evp_aead_ctx_op(%p, %p, %d, %p, %p, %d, %d, %p)", ctx, outArray, outOffset, nonceArray, inArray, inOffset, inLength, aadArray); jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "out"); return 0; } ScopedByteArrayRO inBytes(env, inArray); if (inBytes.get() == nullptr) { return 0; } if (ARRAY_OFFSET_LENGTH_INVALID(inBytes, inOffset, inLength)) { JNI_TRACE("evp_aead_ctx_op(%p, %p, %d, %p, %p, %d, %d, %p)", ctx, outArray, outOffset, nonceArray, inArray, inOffset, inLength, aadArray); jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "in"); return 0; } UniquePtr<ScopedByteArrayRO> aad; const uint8_t* aad_chars = nullptr; size_t aad_chars_size = 0; if (aadArray != nullptr) { aad.reset(new ScopedByteArrayRO(env, aadArray)); aad_chars = reinterpret_cast<const uint8_t*>(aad->get()); if (aad_chars == nullptr) { return 0; } aad_chars_size = aad->size(); } ScopedByteArrayRO nonceBytes(env, nonceArray); if (nonceBytes.get() == nullptr) { return 0; } uint8_t* outTmp = reinterpret_cast<uint8_t*>(outBytes.get()); const uint8_t* inTmp = reinterpret_cast<const uint8_t*>(inBytes.get()); const uint8_t* nonceTmp = reinterpret_cast<const uint8_t*>(nonceBytes.get()); size_t actualOutLength; int ret = realFunc(ctx, outTmp + outOffset, &actualOutLength, outBytes.size() - outOffset, nonceTmp, nonceBytes.size(), inTmp + inOffset, inLength, aad_chars, aad_chars_size); if (ret != 1) { throwExceptionIfNecessary(env, "evp_aead_ctx_op"); } JNI_TRACE("evp_aead_ctx_op(%p, %p, %d, %p, %p, %d, %d, %p) => ret=%d, outLength=%zd", ctx, outArray, outOffset, nonceArray, inArray, inOffset, inLength, aadArray, ret, actualOutLength); return static_cast<jlong>(actualOutLength); } #endif static jint NativeCrypto_EVP_AEAD_CTX_seal(JNIEnv* env, jclass, jobject ctxRef, jbyteArray outArray, jint outOffset, jbyteArray nonceArray, jbyteArray inArray, jint inOffset, jint inLength, jbyteArray aadArray) { #if defined(OPENSSL_IS_BORINGSSL) return evp_aead_ctx_op(env, ctxRef, outArray, outOffset, nonceArray, inArray, inOffset, inLength, aadArray, EVP_AEAD_CTX_seal); #else UNUSED_ARGUMENT(env); UNUSED_ARGUMENT(ctxRef); UNUSED_ARGUMENT(outArray); UNUSED_ARGUMENT(outOffset); UNUSED_ARGUMENT(nonceArray); UNUSED_ARGUMENT(inArray); UNUSED_ARGUMENT(inOffset); UNUSED_ARGUMENT(inLength); UNUSED_ARGUMENT(aadArray); jniThrowRuntimeException(env, "Not supported for OpenSSL"); return 0; #endif } static jint NativeCrypto_EVP_AEAD_CTX_open(JNIEnv* env, jclass, jobject ctxRef, jbyteArray outArray, jint outOffset, jbyteArray nonceArray, jbyteArray inArray, jint inOffset, jint inLength, jbyteArray aadArray) { #if defined(OPENSSL_IS_BORINGSSL) return evp_aead_ctx_op(env, ctxRef, outArray, outOffset, nonceArray, inArray, inOffset, inLength, aadArray, EVP_AEAD_CTX_open); #else UNUSED_ARGUMENT(env); UNUSED_ARGUMENT(ctxRef); UNUSED_ARGUMENT(outArray); UNUSED_ARGUMENT(outOffset); UNUSED_ARGUMENT(nonceArray); UNUSED_ARGUMENT(inArray); UNUSED_ARGUMENT(inOffset); UNUSED_ARGUMENT(inLength); UNUSED_ARGUMENT(aadArray); jniThrowRuntimeException(env, "Not supported for OpenSSL"); return 0; #endif } static jlong NativeCrypto_HMAC_CTX_new(JNIEnv* env, jclass) { JNI_TRACE("HMAC_CTX_new"); HMAC_CTX* hmacCtx = reinterpret_cast<HMAC_CTX*>(OPENSSL_malloc(sizeof(HMAC_CTX))); if (hmacCtx == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate HMAC_CTX"); return 0; } HMAC_CTX_init(hmacCtx); return reinterpret_cast<jlong>(hmacCtx); } static void NativeCrypto_HMAC_CTX_free(JNIEnv*, jclass, jlong hmacCtxRef) { HMAC_CTX* hmacCtx = reinterpret_cast<HMAC_CTX*>(hmacCtxRef); JNI_TRACE("HMAC_CTX_free(%p)", hmacCtx); if (hmacCtx == nullptr) { return; } HMAC_CTX_cleanup(hmacCtx); OPENSSL_free(hmacCtx); } static void NativeCrypto_HMAC_Init_ex(JNIEnv* env, jclass, jobject hmacCtxRef, jbyteArray keyArray, jobject evpMdRef) { HMAC_CTX* hmacCtx = fromContextObject<HMAC_CTX>(env, hmacCtxRef); const EVP_MD* md = reinterpret_cast<const EVP_MD*>(evpMdRef); JNI_TRACE("HMAC_Init_ex(%p, %p, %p)", hmacCtx, keyArray, md); if (hmacCtx == nullptr) { jniThrowNullPointerException(env, "hmacCtx == null"); return; } ScopedByteArrayRO keyBytes(env, keyArray); if (keyBytes.get() == nullptr) { return; } const uint8_t* keyPtr = reinterpret_cast<const uint8_t*>(keyBytes.get()); if (!HMAC_Init_ex(hmacCtx, keyPtr, keyBytes.size(), md, nullptr)) { throwExceptionIfNecessary(env, "HMAC_Init_ex"); JNI_TRACE("HMAC_Init_ex(%p, %p, %p) => fail HMAC_Init_ex", hmacCtx, keyArray, md); return; } } static void NativeCrypto_HMAC_UpdateDirect(JNIEnv* env, jclass, jobject hmacCtxRef, jlong inPtr, int inLength) { HMAC_CTX* hmacCtx = fromContextObject<HMAC_CTX>(env, hmacCtxRef); const uint8_t* p = reinterpret_cast<const uint8_t*>(inPtr); JNI_TRACE("HMAC_UpdateDirect(%p, %p, %d)", hmacCtx, p, inLength); if (hmacCtx == nullptr) { return; } if (p == nullptr) { jniThrowNullPointerException(env, nullptr); return; } if (!HMAC_Update(hmacCtx, p, inLength)) { JNI_TRACE("HMAC_UpdateDirect(%p, %p, %d) => threw exception", hmacCtx, p, inLength); throwExceptionIfNecessary(env, "HMAC_UpdateDirect"); return; } } static void NativeCrypto_HMAC_Update(JNIEnv* env, jclass, jobject hmacCtxRef, jbyteArray inArray, jint inOffset, int inLength) { HMAC_CTX* hmacCtx = fromContextObject<HMAC_CTX>(env, hmacCtxRef); JNI_TRACE("HMAC_Update(%p, %p, %d, %d)", hmacCtx, inArray, inOffset, inLength); if (hmacCtx == nullptr) { return; } ScopedByteArrayRO inBytes(env, inArray); if (inBytes.get() == nullptr) { return; } if (ARRAY_OFFSET_LENGTH_INVALID(inBytes, inOffset, inLength)) { jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "inBytes"); return; } const uint8_t* inPtr = reinterpret_cast<const uint8_t*>(inBytes.get()); if (!HMAC_Update(hmacCtx, inPtr + inOffset, inLength)) { JNI_TRACE("HMAC_Update(%p, %p, %d, %d) => threw exception", hmacCtx, inArray, inOffset, inLength); throwExceptionIfNecessary(env, "HMAC_Update"); return; } } static jbyteArray NativeCrypto_HMAC_Final(JNIEnv* env, jclass, jobject hmacCtxRef) { HMAC_CTX* hmacCtx = fromContextObject<HMAC_CTX>(env, hmacCtxRef); JNI_TRACE("HMAC_Final(%p)", hmacCtx); if (hmacCtx == nullptr) { return nullptr; } uint8_t result[EVP_MAX_MD_SIZE]; unsigned len; if (!HMAC_Final(hmacCtx, result, &len)) { JNI_TRACE("HMAC_Final(%p) => threw exception", hmacCtx); throwExceptionIfNecessary(env, "HMAC_Final"); return nullptr; } ScopedLocalRef<jbyteArray> resultArray(env, env->NewByteArray(len)); if (resultArray.get() == nullptr) { return nullptr; } ScopedByteArrayRW resultBytes(env, resultArray.get()); if (resultBytes.get() == nullptr) { return nullptr; } memcpy(resultBytes.get(), result, len); return resultArray.release(); } /** * public static native void RAND_seed(byte[]); */ #if !defined(OPENSSL_IS_BORINGSSL) static void NativeCrypto_RAND_seed(JNIEnv* env, jclass, jbyteArray seed) { JNI_TRACE("NativeCrypto_RAND_seed seed=%p", seed); ScopedByteArrayRO randseed(env, seed); if (randseed.get() == NULL) { return; } RAND_seed(randseed.get(), randseed.size()); } #else static void NativeCrypto_RAND_seed(JNIEnv*, jclass, jbyteArray) { } #endif static jint NativeCrypto_RAND_load_file(JNIEnv* env, jclass, jstring filename, jlong max_bytes) { JNI_TRACE("NativeCrypto_RAND_load_file filename=%p max_bytes=%lld", filename, (long long) max_bytes); #if !defined(OPENSSL_IS_BORINGSSL) ScopedUtfChars file(env, filename); if (file.c_str() == NULL) { return -1; } int result = RAND_load_file(file.c_str(), max_bytes); JNI_TRACE("NativeCrypto_RAND_load_file file=%s => %d", file.c_str(), result); return result; #else UNUSED_ARGUMENT(env); UNUSED_ARGUMENT(filename); // OpenSSLRandom calls this and checks the return value. return static_cast<jint>(max_bytes); #endif } static void NativeCrypto_RAND_bytes(JNIEnv* env, jclass, jbyteArray output) { JNI_TRACE("NativeCrypto_RAND_bytes(%p)", output); ScopedByteArrayRW outputBytes(env, output); if (outputBytes.get() == nullptr) { return; } unsigned char* tmp = reinterpret_cast<unsigned char*>(outputBytes.get()); if (RAND_bytes(tmp, outputBytes.size()) <= 0) { throwExceptionIfNecessary(env, "NativeCrypto_RAND_bytes"); JNI_TRACE("tmp=%p NativeCrypto_RAND_bytes => threw error", tmp); return; } JNI_TRACE("NativeCrypto_RAND_bytes(%p) => success", output); } static jint NativeCrypto_OBJ_txt2nid(JNIEnv* env, jclass, jstring oidStr) { JNI_TRACE("OBJ_txt2nid(%p)", oidStr); ScopedUtfChars oid(env, oidStr); if (oid.c_str() == nullptr) { return 0; } int nid = OBJ_txt2nid(oid.c_str()); JNI_TRACE("OBJ_txt2nid(%s) => %d", oid.c_str(), nid); return nid; } static jstring NativeCrypto_OBJ_txt2nid_longName(JNIEnv* env, jclass, jstring oidStr) { JNI_TRACE("OBJ_txt2nid_longName(%p)", oidStr); ScopedUtfChars oid(env, oidStr); if (oid.c_str() == nullptr) { return nullptr; } JNI_TRACE("OBJ_txt2nid_longName(%s)", oid.c_str()); int nid = OBJ_txt2nid(oid.c_str()); if (nid == NID_undef) { JNI_TRACE("OBJ_txt2nid_longName(%s) => NID_undef", oid.c_str()); freeOpenSslErrorState(); return nullptr; } const char* longName = OBJ_nid2ln(nid); JNI_TRACE("OBJ_txt2nid_longName(%s) => %s", oid.c_str(), longName); return env->NewStringUTF(longName); } static jstring ASN1_OBJECT_to_OID_string(JNIEnv* env, const ASN1_OBJECT* obj) { /* * The OBJ_obj2txt API doesn't "measure" if you pass in NULL as the buffer. * Just make a buffer that's large enough here. The documentation recommends * 80 characters. */ char output[128]; int ret = OBJ_obj2txt(output, sizeof(output), obj, 1); if (ret < 0) { throwExceptionIfNecessary(env, "ASN1_OBJECT_to_OID_string"); return nullptr; } else if (size_t(ret) >= sizeof(output)) { jniThrowRuntimeException(env, "ASN1_OBJECT_to_OID_string buffer too small"); return nullptr; } JNI_TRACE("ASN1_OBJECT_to_OID_string(%p) => %s", obj, output); return env->NewStringUTF(output); } static jlong NativeCrypto_create_BIO_InputStream(JNIEnv* env, jclass, jobject streamObj, jboolean isFinite) { JNI_TRACE("create_BIO_InputStream(%p)", streamObj); if (streamObj == nullptr) { jniThrowNullPointerException(env, "stream == null"); return 0; } Unique_BIO bio(BIO_new(&stream_bio_method)); if (bio.get() == nullptr) { return 0; } bio_stream_assign(bio.get(), new BIO_InputStream(streamObj, isFinite)); JNI_TRACE("create_BIO_InputStream(%p) => %p", streamObj, bio.get()); return static_cast<jlong>(reinterpret_cast<uintptr_t>(bio.release())); } static jlong NativeCrypto_create_BIO_OutputStream(JNIEnv* env, jclass, jobject streamObj) { JNI_TRACE("create_BIO_OutputStream(%p)", streamObj); if (streamObj == nullptr) { jniThrowNullPointerException(env, "stream == null"); return 0; } Unique_BIO bio(BIO_new(&stream_bio_method)); if (bio.get() == nullptr) { return 0; } bio_stream_assign(bio.get(), new BIO_OutputStream(streamObj)); JNI_TRACE("create_BIO_OutputStream(%p) => %p", streamObj, bio.get()); return static_cast<jlong>(reinterpret_cast<uintptr_t>(bio.release())); } static int NativeCrypto_BIO_read(JNIEnv* env, jclass, jlong bioRef, jbyteArray outputJavaBytes) { BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); JNI_TRACE("BIO_read(%p, %p)", bio, outputJavaBytes); if (outputJavaBytes == nullptr) { jniThrowNullPointerException(env, "output == null"); JNI_TRACE("BIO_read(%p, %p) => output == null", bio, outputJavaBytes); return 0; } int outputSize = env->GetArrayLength(outputJavaBytes); UniquePtr<unsigned char[]> buffer(new unsigned char[outputSize]); if (buffer.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate buffer for read"); return 0; } int read = BIO_read(bio, buffer.get(), outputSize); if (read <= 0) { throwIOException(env, "BIO_read"); JNI_TRACE("BIO_read(%p, %p) => threw IO exception", bio, outputJavaBytes); return 0; } env->SetByteArrayRegion(outputJavaBytes, 0, read, reinterpret_cast<jbyte*>(buffer.get())); JNI_TRACE("BIO_read(%p, %p) => %d", bio, outputJavaBytes, read); return read; } static void NativeCrypto_BIO_write(JNIEnv* env, jclass, jlong bioRef, jbyteArray inputJavaBytes, jint offset, jint length) { BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); JNI_TRACE("BIO_write(%p, %p, %d, %d)", bio, inputJavaBytes, offset, length); if (inputJavaBytes == nullptr) { jniThrowNullPointerException(env, "input == null"); return; } int inputSize = env->GetArrayLength(inputJavaBytes); if (offset < 0 || offset > inputSize || length < 0 || length > inputSize - offset) { jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "inputJavaBytes"); JNI_TRACE("BIO_write(%p, %p, %d, %d) => IOOB", bio, inputJavaBytes, offset, length); return; } UniquePtr<unsigned char[]> buffer(new unsigned char[length]); if (buffer.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate buffer for write"); return; } env->GetByteArrayRegion(inputJavaBytes, offset, length, reinterpret_cast<jbyte*>(buffer.get())); if (BIO_write(bio, buffer.get(), length) != length) { freeOpenSslErrorState(); throwIOException(env, "BIO_write"); JNI_TRACE("BIO_write(%p, %p, %d, %d) => IO error", bio, inputJavaBytes, offset, length); return; } JNI_TRACE("BIO_write(%p, %p, %d, %d) => success", bio, inputJavaBytes, offset, length); } static void NativeCrypto_BIO_free_all(JNIEnv* env, jclass, jlong bioRef) { BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); JNI_TRACE("BIO_free_all(%p)", bio); if (bio == nullptr) { jniThrowNullPointerException(env, "bio == null"); return; } BIO_free_all(bio); } static jstring X509_NAME_to_jstring(JNIEnv* env, X509_NAME* name, unsigned long flags) { JNI_TRACE("X509_NAME_to_jstring(%p)", name); Unique_BIO buffer(BIO_new(BIO_s_mem())); if (buffer.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate BIO"); JNI_TRACE("X509_NAME_to_jstring(%p) => threw error", name); return nullptr; } /* Don't interpret the string. */ flags &= ~(ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_ESC_MSB); /* Write in given format and null terminate. */ X509_NAME_print_ex(buffer.get(), name, 0, flags); BIO_write(buffer.get(), "\0", 1); char *tmp; BIO_get_mem_data(buffer.get(), &tmp); JNI_TRACE("X509_NAME_to_jstring(%p) => \"%s\"", name, tmp); return env->NewStringUTF(tmp); } /** * Converts GENERAL_NAME items to the output format expected in * X509Certificate#getSubjectAlternativeNames and * X509Certificate#getIssuerAlternativeNames return. */ static jobject GENERAL_NAME_to_jobject(JNIEnv* env, GENERAL_NAME* gen) { switch (gen->type) { case GEN_EMAIL: case GEN_DNS: case GEN_URI: { // This must not be a T61String and must not contain NULLs. const char* data = reinterpret_cast<const char*>(ASN1_STRING_data(gen->d.ia5)); ssize_t len = ASN1_STRING_length(gen->d.ia5); if ((len == static_cast<ssize_t>(strlen(data))) && (ASN1_PRINTABLE_type(ASN1_STRING_data(gen->d.ia5), len) != V_ASN1_T61STRING)) { JNI_TRACE("GENERAL_NAME_to_jobject(%p) => Email/DNS/URI \"%s\"", gen, data); return env->NewStringUTF(data); } else { jniThrowException(env, "java/security/cert/CertificateParsingException", "Invalid dNSName encoding"); JNI_TRACE("GENERAL_NAME_to_jobject(%p) => Email/DNS/URI invalid", gen); return nullptr; } } case GEN_DIRNAME: /* Write in RFC 2253 format */ return X509_NAME_to_jstring(env, gen->d.directoryName, XN_FLAG_RFC2253); case GEN_IPADD: { const void *ip = reinterpret_cast<const void *>(gen->d.ip->data); if (gen->d.ip->length == 4) { // IPv4 UniquePtr<char[]> buffer(new char[INET_ADDRSTRLEN]); if (inet_ntop(AF_INET, ip, buffer.get(), INET_ADDRSTRLEN) != nullptr) { JNI_TRACE("GENERAL_NAME_to_jobject(%p) => IPv4 %s", gen, buffer.get()); return env->NewStringUTF(buffer.get()); } else { JNI_TRACE("GENERAL_NAME_to_jobject(%p) => IPv4 failed %s", gen, strerror(errno)); } } else if (gen->d.ip->length == 16) { // IPv6 UniquePtr<char[]> buffer(new char[INET6_ADDRSTRLEN]); if (inet_ntop(AF_INET6, ip, buffer.get(), INET6_ADDRSTRLEN) != nullptr) { JNI_TRACE("GENERAL_NAME_to_jobject(%p) => IPv6 %s", gen, buffer.get()); return env->NewStringUTF(buffer.get()); } else { JNI_TRACE("GENERAL_NAME_to_jobject(%p) => IPv6 failed %s", gen, strerror(errno)); } } /* Invalid IP encodings are pruned out without throwing an exception. */ return nullptr; } case GEN_RID: return ASN1_OBJECT_to_OID_string(env, gen->d.registeredID); case GEN_OTHERNAME: case GEN_X400: default: return ASN1ToByteArray<GENERAL_NAME>(env, gen, i2d_GENERAL_NAME); } return nullptr; } #define GN_STACK_SUBJECT_ALT_NAME 1 #define GN_STACK_ISSUER_ALT_NAME 2 static jobjectArray NativeCrypto_get_X509_GENERAL_NAME_stack(JNIEnv* env, jclass, jlong x509Ref, jint type) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_GENERAL_NAME_stack(%p, %d)", x509, type); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("get_X509_GENERAL_NAME_stack(%p, %d) => x509 == null", x509, type); return nullptr; } X509_check_ca(x509); STACK_OF(GENERAL_NAME)* gn_stack; Unique_sk_GENERAL_NAME stackHolder; if (type == GN_STACK_SUBJECT_ALT_NAME) { gn_stack = x509->altname; } else if (type == GN_STACK_ISSUER_ALT_NAME) { stackHolder.reset(static_cast<STACK_OF(GENERAL_NAME)*>( X509_get_ext_d2i(x509, NID_issuer_alt_name, nullptr, NULL))); gn_stack = stackHolder.get(); } else { JNI_TRACE("get_X509_GENERAL_NAME_stack(%p, %d) => unknown type", x509, type); return nullptr; } int count = sk_GENERAL_NAME_num(gn_stack); if (count <= 0) { JNI_TRACE("get_X509_GENERAL_NAME_stack(%p, %d) => null (no entries)", x509, type); return nullptr; } /* * Keep track of how many originally so we can ignore any invalid * values later. */ const int origCount = count; ScopedLocalRef<jobjectArray> joa(env, env->NewObjectArray(count, objectArrayClass, nullptr)); for (int i = 0, j = 0; i < origCount; i++, j++) { GENERAL_NAME* gen = sk_GENERAL_NAME_value(gn_stack, i); ScopedLocalRef<jobject> val(env, GENERAL_NAME_to_jobject(env, gen)); if (env->ExceptionCheck()) { JNI_TRACE("get_X509_GENERAL_NAME_stack(%p, %d) => threw exception parsing gen name", x509, type); return nullptr; } /* * If it's NULL, we'll have to skip this, reduce the number of total * entries, and fix up the array later. */ if (val.get() == nullptr) { j--; count--; continue; } ScopedLocalRef<jobjectArray> item(env, env->NewObjectArray(2, objectClass, nullptr)); ScopedLocalRef<jobject> type(env, env->CallStaticObjectMethod(integerClass, integer_valueOfMethod, gen->type)); env->SetObjectArrayElement(item.get(), 0, type.get()); env->SetObjectArrayElement(item.get(), 1, val.get()); env->SetObjectArrayElement(joa.get(), j, item.get()); } if (count == 0) { JNI_TRACE("get_X509_GENERAL_NAME_stack(%p, %d) shrunk from %d to 0; returning NULL", x509, type, origCount); joa.reset(nullptr); } else if (origCount != count) { JNI_TRACE("get_X509_GENERAL_NAME_stack(%p, %d) shrunk from %d to %d", x509, type, origCount, count); ScopedLocalRef<jobjectArray> joa_copy( env, env->NewObjectArray(count, objectArrayClass, nullptr)); for (int i = 0; i < count; i++) { ScopedLocalRef<jobject> item(env, env->GetObjectArrayElement(joa.get(), i)); env->SetObjectArrayElement(joa_copy.get(), i, item.get()); } joa.reset(joa_copy.release()); } JNI_TRACE("get_X509_GENERAL_NAME_stack(%p, %d) => %d entries", x509, type, count); return joa.release(); } static jlong NativeCrypto_X509_get_notBefore(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_get_notBefore(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("X509_get_notBefore(%p) => x509 == null", x509); return 0; } ASN1_TIME* notBefore = X509_get_notBefore(x509); JNI_TRACE("X509_get_notBefore(%p) => %p", x509, notBefore); return reinterpret_cast<uintptr_t>(notBefore); } static jlong NativeCrypto_X509_get_notAfter(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_get_notAfter(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("X509_get_notAfter(%p) => x509 == null", x509); return 0; } ASN1_TIME* notAfter = X509_get_notAfter(x509); JNI_TRACE("X509_get_notAfter(%p) => %p", x509, notAfter); return reinterpret_cast<uintptr_t>(notAfter); } static long NativeCrypto_X509_get_version(JNIEnv*, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_get_version(%p)", x509); long version = X509_get_version(x509); JNI_TRACE("X509_get_version(%p) => %ld", x509, version); return version; } template<typename T> static jbyteArray get_X509Type_serialNumber(JNIEnv* env, T* x509Type, ASN1_INTEGER* (*get_serial_func)(T*)) { JNI_TRACE("get_X509Type_serialNumber(%p)", x509Type); if (x509Type == nullptr) { jniThrowNullPointerException(env, "x509Type == null"); JNI_TRACE("get_X509Type_serialNumber(%p) => x509Type == null", x509Type); return nullptr; } ASN1_INTEGER* serialNumber = get_serial_func(x509Type); Unique_BIGNUM serialBn(ASN1_INTEGER_to_BN(serialNumber, nullptr)); if (serialBn.get() == nullptr) { JNI_TRACE("X509_get_serialNumber(%p) => threw exception", x509Type); return nullptr; } ScopedLocalRef<jbyteArray> serialArray(env, bignumToArray(env, serialBn.get(), "serialBn")); if (env->ExceptionCheck()) { JNI_TRACE("X509_get_serialNumber(%p) => threw exception", x509Type); return nullptr; } JNI_TRACE("X509_get_serialNumber(%p) => %p", x509Type, serialArray.get()); return serialArray.release(); } /* OpenSSL includes set_serialNumber but not get. */ #if !defined(X509_REVOKED_get_serialNumber) static ASN1_INTEGER* X509_REVOKED_get_serialNumber(X509_REVOKED* x) { return x->serialNumber; } #endif static jbyteArray NativeCrypto_X509_get_serialNumber(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_get_serialNumber(%p)", x509); return get_X509Type_serialNumber<X509>(env, x509, X509_get_serialNumber); } static jbyteArray NativeCrypto_X509_REVOKED_get_serialNumber(JNIEnv* env, jclass, jlong x509RevokedRef) { X509_REVOKED* revoked = reinterpret_cast<X509_REVOKED*>(static_cast<uintptr_t>(x509RevokedRef)); JNI_TRACE("X509_REVOKED_get_serialNumber(%p)", revoked); return get_X509Type_serialNumber<X509_REVOKED>(env, revoked, X509_REVOKED_get_serialNumber); } static void NativeCrypto_X509_verify(JNIEnv* env, jclass, jlong x509Ref, jobject pkeyRef) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef); JNI_TRACE("X509_verify(%p, %p)", x509, pkey); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("X509_verify(%p, %p) => x509 == null", x509, pkey); return; } if (pkey == nullptr) { JNI_TRACE("X509_verify(%p, %p) => pkey == null", x509, pkey); return; } if (X509_verify(x509, pkey) != 1) { throwExceptionIfNecessary(env, "X509_verify"); JNI_TRACE("X509_verify(%p, %p) => verify failure", x509, pkey); } else { JNI_TRACE("X509_verify(%p, %p) => verify success", x509, pkey); } } static jbyteArray NativeCrypto_get_X509_cert_info_enc(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_cert_info_enc(%p)", x509); return ASN1ToByteArray<X509_CINF>(env, x509->cert_info, i2d_X509_CINF); } static jint NativeCrypto_get_X509_ex_flags(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_ex_flags(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("get_X509_ex_flags(%p) => x509 == null", x509); return 0; } X509_check_ca(x509); return x509->ex_flags; } static jboolean NativeCrypto_X509_check_issued(JNIEnv*, jclass, jlong x509Ref1, jlong x509Ref2) { X509* x509_1 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref1)); X509* x509_2 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref2)); JNI_TRACE("X509_check_issued(%p, %p)", x509_1, x509_2); int ret = X509_check_issued(x509_1, x509_2); JNI_TRACE("X509_check_issued(%p, %p) => %d", x509_1, x509_2, ret); return ret; } static void get_X509_signature(X509 *x509, ASN1_BIT_STRING** signature) { *signature = x509->signature; } static void get_X509_CRL_signature(X509_CRL *crl, ASN1_BIT_STRING** signature) { *signature = crl->signature; } template<typename T> static jbyteArray get_X509Type_signature(JNIEnv* env, T* x509Type, void (*get_signature_func)(T*, ASN1_BIT_STRING**)) { JNI_TRACE("get_X509Type_signature(%p)", x509Type); if (x509Type == nullptr) { jniThrowNullPointerException(env, "x509Type == null"); JNI_TRACE("get_X509Type_signature(%p) => x509Type == null", x509Type); return nullptr; } ASN1_BIT_STRING* signature; get_signature_func(x509Type, &signature); ScopedLocalRef<jbyteArray> signatureArray(env, env->NewByteArray(signature->length)); if (env->ExceptionCheck()) { JNI_TRACE("get_X509Type_signature(%p) => threw exception", x509Type); return nullptr; } ScopedByteArrayRW signatureBytes(env, signatureArray.get()); if (signatureBytes.get() == nullptr) { JNI_TRACE("get_X509Type_signature(%p) => using byte array failed", x509Type); return nullptr; } memcpy(signatureBytes.get(), signature->data, signature->length); JNI_TRACE("get_X509Type_signature(%p) => %p (%d bytes)", x509Type, signatureArray.get(), signature->length); return signatureArray.release(); } static jbyteArray NativeCrypto_get_X509_signature(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_signature(%p)", x509); return get_X509Type_signature<X509>(env, x509, get_X509_signature); } static jbyteArray NativeCrypto_get_X509_CRL_signature(JNIEnv* env, jclass, jlong x509CrlRef) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("get_X509_CRL_signature(%p)", crl); return get_X509Type_signature<X509_CRL>(env, crl, get_X509_CRL_signature); } static jlong NativeCrypto_X509_CRL_get0_by_cert(JNIEnv* env, jclass, jlong x509crlRef, jlong x509Ref) { X509_CRL* x509crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509crlRef)); X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_CRL_get0_by_cert(%p, %p)", x509crl, x509); if (x509crl == nullptr) { jniThrowNullPointerException(env, "x509crl == null"); JNI_TRACE("X509_CRL_get0_by_cert(%p, %p) => x509crl == null", x509crl, x509); return 0; } else if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("X509_CRL_get0_by_cert(%p, %p) => x509 == null", x509crl, x509); return 0; } X509_REVOKED* revoked = nullptr; int ret = X509_CRL_get0_by_cert(x509crl, &revoked, x509); if (ret == 0) { JNI_TRACE("X509_CRL_get0_by_cert(%p, %p) => none", x509crl, x509); return 0; } JNI_TRACE("X509_CRL_get0_by_cert(%p, %p) => %p", x509crl, x509, revoked); return reinterpret_cast<uintptr_t>(revoked); } static jlong NativeCrypto_X509_CRL_get0_by_serial(JNIEnv* env, jclass, jlong x509crlRef, jbyteArray serialArray) { X509_CRL* x509crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509crlRef)); JNI_TRACE("X509_CRL_get0_by_serial(%p, %p)", x509crl, serialArray); if (x509crl == nullptr) { jniThrowNullPointerException(env, "x509crl == null"); JNI_TRACE("X509_CRL_get0_by_serial(%p, %p) => crl == null", x509crl, serialArray); return 0; } Unique_BIGNUM serialBn(BN_new()); if (serialBn.get() == nullptr) { JNI_TRACE("X509_CRL_get0_by_serial(%p, %p) => BN allocation failed", x509crl, serialArray); return 0; } BIGNUM* serialBare = serialBn.get(); if (!arrayToBignum(env, serialArray, &serialBare)) { if (!env->ExceptionCheck()) { jniThrowNullPointerException(env, "serial == null"); } JNI_TRACE("X509_CRL_get0_by_serial(%p, %p) => BN conversion failed", x509crl, serialArray); return 0; } Unique_ASN1_INTEGER serialInteger(BN_to_ASN1_INTEGER(serialBn.get(), nullptr)); if (serialInteger.get() == nullptr) { JNI_TRACE("X509_CRL_get0_by_serial(%p, %p) => BN conversion failed", x509crl, serialArray); return 0; } X509_REVOKED* revoked = nullptr; int ret = X509_CRL_get0_by_serial(x509crl, &revoked, serialInteger.get()); if (ret == 0) { JNI_TRACE("X509_CRL_get0_by_serial(%p, %p) => none", x509crl, serialArray); return 0; } JNI_TRACE("X509_CRL_get0_by_cert(%p, %p) => %p", x509crl, serialArray, revoked); return reinterpret_cast<uintptr_t>(revoked); } /* This appears to be missing from OpenSSL. */ #if !defined(X509_REVOKED_dup) && !defined(OPENSSL_IS_BORINGSSL) X509_REVOKED* X509_REVOKED_dup(X509_REVOKED* x) { return reinterpret_cast<X509_REVOKED*>(ASN1_item_dup(ASN1_ITEM_rptr(X509_REVOKED), x)); } #endif static jlongArray NativeCrypto_X509_CRL_get_REVOKED(JNIEnv* env, jclass, jlong x509CrlRef) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("X509_CRL_get_REVOKED(%p)", crl); if (crl == nullptr) { jniThrowNullPointerException(env, "crl == null"); return nullptr; } STACK_OF(X509_REVOKED)* stack = X509_CRL_get_REVOKED(crl); if (stack == nullptr) { JNI_TRACE("X509_CRL_get_REVOKED(%p) => stack is null", crl); return nullptr; } size_t size = sk_X509_REVOKED_num(stack); ScopedLocalRef<jlongArray> revokedArray(env, env->NewLongArray(size)); ScopedLongArrayRW revoked(env, revokedArray.get()); for (size_t i = 0; i < size; i++) { X509_REVOKED* item = reinterpret_cast<X509_REVOKED*>(sk_X509_REVOKED_value(stack, i)); revoked[i] = reinterpret_cast<uintptr_t>(X509_REVOKED_dup(item)); } JNI_TRACE("X509_CRL_get_REVOKED(%p) => %p [size=%zd]", stack, revokedArray.get(), size); return revokedArray.release(); } static jbyteArray NativeCrypto_i2d_X509_CRL(JNIEnv* env, jclass, jlong x509CrlRef) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("i2d_X509_CRL(%p)", crl); return ASN1ToByteArray<X509_CRL>(env, crl, i2d_X509_CRL); } static void NativeCrypto_X509_CRL_free(JNIEnv* env, jclass, jlong x509CrlRef) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("X509_CRL_free(%p)", crl); if (crl == nullptr) { jniThrowNullPointerException(env, "crl == null"); JNI_TRACE("X509_CRL_free(%p) => crl == null", crl); return; } X509_CRL_free(crl); } static void NativeCrypto_X509_CRL_print(JNIEnv* env, jclass, jlong bioRef, jlong x509CrlRef) { BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("X509_CRL_print(%p, %p)", bio, crl); if (bio == nullptr) { jniThrowNullPointerException(env, "bio == null"); JNI_TRACE("X509_CRL_print(%p, %p) => bio == null", bio, crl); return; } if (crl == nullptr) { jniThrowNullPointerException(env, "crl == null"); JNI_TRACE("X509_CRL_print(%p, %p) => crl == null", bio, crl); return; } if (!X509_CRL_print(bio, crl)) { throwExceptionIfNecessary(env, "X509_CRL_print"); JNI_TRACE("X509_CRL_print(%p, %p) => threw error", bio, crl); } else { JNI_TRACE("X509_CRL_print(%p, %p) => success", bio, crl); } } static jstring NativeCrypto_get_X509_CRL_sig_alg_oid(JNIEnv* env, jclass, jlong x509CrlRef) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("get_X509_CRL_sig_alg_oid(%p)", crl); if (crl == nullptr || crl->sig_alg == nullptr) { jniThrowNullPointerException(env, "crl == NULL || crl->sig_alg == NULL"); JNI_TRACE("get_X509_CRL_sig_alg_oid(%p) => crl == NULL", crl); return nullptr; } return ASN1_OBJECT_to_OID_string(env, crl->sig_alg->algorithm); } static jbyteArray NativeCrypto_get_X509_CRL_sig_alg_parameter(JNIEnv* env, jclass, jlong x509CrlRef) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("get_X509_CRL_sig_alg_parameter(%p)", crl); if (crl == nullptr) { jniThrowNullPointerException(env, "crl == null"); JNI_TRACE("get_X509_CRL_sig_alg_parameter(%p) => crl == null", crl); return nullptr; } if (crl->sig_alg->parameter == nullptr) { JNI_TRACE("get_X509_CRL_sig_alg_parameter(%p) => null", crl); return nullptr; } return ASN1ToByteArray<ASN1_TYPE>(env, crl->sig_alg->parameter, i2d_ASN1_TYPE); } static jbyteArray NativeCrypto_X509_CRL_get_issuer_name(JNIEnv* env, jclass, jlong x509CrlRef) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("X509_CRL_get_issuer_name(%p)", crl); return ASN1ToByteArray<X509_NAME>(env, X509_CRL_get_issuer(crl), i2d_X509_NAME); } static long NativeCrypto_X509_CRL_get_version(JNIEnv*, jclass, jlong x509CrlRef) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("X509_CRL_get_version(%p)", crl); long version = X509_CRL_get_version(crl); JNI_TRACE("X509_CRL_get_version(%p) => %ld", crl, version); return version; } template<typename T, int (*get_ext_by_OBJ_func)(T*, ASN1_OBJECT*, int), X509_EXTENSION* (*get_ext_func)(T*, int)> static X509_EXTENSION *X509Type_get_ext(JNIEnv* env, T* x509Type, jstring oidString) { JNI_TRACE("X509Type_get_ext(%p)", x509Type); if (x509Type == nullptr) { jniThrowNullPointerException(env, "x509 == null"); return nullptr; } ScopedUtfChars oid(env, oidString); if (oid.c_str() == nullptr) { return nullptr; } Unique_ASN1_OBJECT asn1(OBJ_txt2obj(oid.c_str(), 1)); if (asn1.get() == nullptr) { JNI_TRACE("X509Type_get_ext(%p, %s) => oid conversion failed", x509Type, oid.c_str()); freeOpenSslErrorState(); return nullptr; } int extIndex = get_ext_by_OBJ_func(x509Type, (ASN1_OBJECT*) asn1.get(), -1); if (extIndex == -1) { JNI_TRACE("X509Type_get_ext(%p, %s) => ext not found", x509Type, oid.c_str()); return nullptr; } X509_EXTENSION* ext = get_ext_func(x509Type, extIndex); JNI_TRACE("X509Type_get_ext(%p, %s) => %p", x509Type, oid.c_str(), ext); return ext; } template<typename T, int (*get_ext_by_OBJ_func)(T*, ASN1_OBJECT*, int), X509_EXTENSION* (*get_ext_func)(T*, int)> static jbyteArray X509Type_get_ext_oid(JNIEnv* env, T* x509Type, jstring oidString) { X509_EXTENSION* ext = X509Type_get_ext<T, get_ext_by_OBJ_func, get_ext_func>(env, x509Type, oidString); if (ext == nullptr) { JNI_TRACE("X509Type_get_ext_oid(%p, %p) => fetching extension failed", x509Type, oidString); return nullptr; } JNI_TRACE("X509Type_get_ext_oid(%p, %p) => %p", x509Type, oidString, ext->value); return ASN1ToByteArray<ASN1_OCTET_STRING>(env, ext->value, i2d_ASN1_OCTET_STRING); } static jlong NativeCrypto_X509_CRL_get_ext(JNIEnv* env, jclass, jlong x509CrlRef, jstring oid) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("X509_CRL_get_ext(%p, %p)", crl, oid); X509_EXTENSION* ext = X509Type_get_ext<X509_CRL, X509_CRL_get_ext_by_OBJ, X509_CRL_get_ext>( env, crl, oid); JNI_TRACE("X509_CRL_get_ext(%p, %p) => %p", crl, oid, ext); return reinterpret_cast<uintptr_t>(ext); } static jlong NativeCrypto_X509_REVOKED_get_ext(JNIEnv* env, jclass, jlong x509RevokedRef, jstring oid) { X509_REVOKED* revoked = reinterpret_cast<X509_REVOKED*>(static_cast<uintptr_t>(x509RevokedRef)); JNI_TRACE("X509_REVOKED_get_ext(%p, %p)", revoked, oid); X509_EXTENSION* ext = X509Type_get_ext<X509_REVOKED, X509_REVOKED_get_ext_by_OBJ, X509_REVOKED_get_ext>(env, revoked, oid); JNI_TRACE("X509_REVOKED_get_ext(%p, %p) => %p", revoked, oid, ext); return reinterpret_cast<uintptr_t>(ext); } static jlong NativeCrypto_X509_REVOKED_dup(JNIEnv* env, jclass, jlong x509RevokedRef) { X509_REVOKED* revoked = reinterpret_cast<X509_REVOKED*>(static_cast<uintptr_t>(x509RevokedRef)); JNI_TRACE("X509_REVOKED_dup(%p)", revoked); if (revoked == nullptr) { jniThrowNullPointerException(env, "revoked == null"); JNI_TRACE("X509_REVOKED_dup(%p) => revoked == null", revoked); return 0; } X509_REVOKED* dup = X509_REVOKED_dup(revoked); JNI_TRACE("X509_REVOKED_dup(%p) => %p", revoked, dup); return reinterpret_cast<uintptr_t>(dup); } static jlong NativeCrypto_get_X509_REVOKED_revocationDate(JNIEnv* env, jclass, jlong x509RevokedRef) { X509_REVOKED* revoked = reinterpret_cast<X509_REVOKED*>(static_cast<uintptr_t>(x509RevokedRef)); JNI_TRACE("get_X509_REVOKED_revocationDate(%p)", revoked); if (revoked == nullptr) { jniThrowNullPointerException(env, "revoked == null"); JNI_TRACE("get_X509_REVOKED_revocationDate(%p) => revoked == null", revoked); return 0; } JNI_TRACE("get_X509_REVOKED_revocationDate(%p) => %p", revoked, revoked->revocationDate); return reinterpret_cast<uintptr_t>(revoked->revocationDate); } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wwrite-strings" static void NativeCrypto_X509_REVOKED_print(JNIEnv* env, jclass, jlong bioRef, jlong x509RevokedRef) { BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); X509_REVOKED* revoked = reinterpret_cast<X509_REVOKED*>(static_cast<uintptr_t>(x509RevokedRef)); JNI_TRACE("X509_REVOKED_print(%p, %p)", bio, revoked); if (bio == nullptr) { jniThrowNullPointerException(env, "bio == null"); JNI_TRACE("X509_REVOKED_print(%p, %p) => bio == null", bio, revoked); return; } if (revoked == nullptr) { jniThrowNullPointerException(env, "revoked == null"); JNI_TRACE("X509_REVOKED_print(%p, %p) => revoked == null", bio, revoked); return; } BIO_printf(bio, "Serial Number: "); i2a_ASN1_INTEGER(bio, revoked->serialNumber); BIO_printf(bio, "\nRevocation Date: "); ASN1_TIME_print(bio, revoked->revocationDate); BIO_printf(bio, "\n"); X509V3_extensions_print(bio, "CRL entry extensions", revoked->extensions, 0, 0); } #pragma GCC diagnostic pop static jbyteArray NativeCrypto_get_X509_CRL_crl_enc(JNIEnv* env, jclass, jlong x509CrlRef) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("get_X509_CRL_crl_enc(%p)", crl); return ASN1ToByteArray<X509_CRL_INFO>(env, crl->crl, i2d_X509_CRL_INFO); } static void NativeCrypto_X509_CRL_verify(JNIEnv* env, jclass, jlong x509CrlRef, jobject pkeyRef) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef); JNI_TRACE("X509_CRL_verify(%p, %p)", crl, pkey); if (crl == nullptr) { jniThrowNullPointerException(env, "crl == null"); JNI_TRACE("X509_CRL_verify(%p, %p) => crl == null", crl, pkey); return; } if (pkey == nullptr) { JNI_TRACE("X509_CRL_verify(%p, %p) => pkey == null", crl, pkey); return; } if (X509_CRL_verify(crl, pkey) != 1) { throwExceptionIfNecessary(env, "X509_CRL_verify"); JNI_TRACE("X509_CRL_verify(%p, %p) => verify failure", crl, pkey); } else { JNI_TRACE("X509_CRL_verify(%p, %p) => verify success", crl, pkey); } } static jlong NativeCrypto_X509_CRL_get_lastUpdate(JNIEnv* env, jclass, jlong x509CrlRef) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("X509_CRL_get_lastUpdate(%p)", crl); if (crl == nullptr) { jniThrowNullPointerException(env, "crl == null"); JNI_TRACE("X509_CRL_get_lastUpdate(%p) => crl == null", crl); return 0; } ASN1_TIME* lastUpdate = X509_CRL_get_lastUpdate(crl); JNI_TRACE("X509_CRL_get_lastUpdate(%p) => %p", crl, lastUpdate); return reinterpret_cast<uintptr_t>(lastUpdate); } static jlong NativeCrypto_X509_CRL_get_nextUpdate(JNIEnv* env, jclass, jlong x509CrlRef) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("X509_CRL_get_nextUpdate(%p)", crl); if (crl == nullptr) { jniThrowNullPointerException(env, "crl == null"); JNI_TRACE("X509_CRL_get_nextUpdate(%p) => crl == null", crl); return 0; } ASN1_TIME* nextUpdate = X509_CRL_get_nextUpdate(crl); JNI_TRACE("X509_CRL_get_nextUpdate(%p) => %p", crl, nextUpdate); return reinterpret_cast<uintptr_t>(nextUpdate); } static jbyteArray NativeCrypto_i2d_X509_REVOKED(JNIEnv* env, jclass, jlong x509RevokedRef) { X509_REVOKED* x509Revoked = reinterpret_cast<X509_REVOKED*>(static_cast<uintptr_t>(x509RevokedRef)); JNI_TRACE("i2d_X509_REVOKED(%p)", x509Revoked); return ASN1ToByteArray<X509_REVOKED>(env, x509Revoked, i2d_X509_REVOKED); } static jint NativeCrypto_X509_supported_extension(JNIEnv* env, jclass, jlong x509ExtensionRef) { X509_EXTENSION* ext = reinterpret_cast<X509_EXTENSION*>(static_cast<uintptr_t>(x509ExtensionRef)); if (ext == nullptr) { jniThrowNullPointerException(env, "ext == NULL"); return 0; } return X509_supported_extension(ext); } static inline void get_ASN1_TIME_data(char **data, int* output, size_t len) { char c = **data; **data = '\0'; *data -= len; *output = atoi(*data); *(*data + len) = c; } static void NativeCrypto_ASN1_TIME_to_Calendar(JNIEnv* env, jclass, jlong asn1TimeRef, jobject calendar) { ASN1_TIME* asn1Time = reinterpret_cast<ASN1_TIME*>(static_cast<uintptr_t>(asn1TimeRef)); JNI_TRACE("ASN1_TIME_to_Calendar(%p, %p)", asn1Time, calendar); if (asn1Time == nullptr) { jniThrowNullPointerException(env, "asn1Time == null"); return; } Unique_ASN1_GENERALIZEDTIME gen(ASN1_TIME_to_generalizedtime(asn1Time, nullptr)); if (gen.get() == nullptr) { jniThrowNullPointerException(env, "asn1Time == null"); return; } if (gen->length < 14 || gen->data == nullptr) { jniThrowNullPointerException(env, "gen->length < 14 || gen->data == NULL"); return; } int sec, min, hour, mday, mon, year; char *p = (char*) &gen->data[14]; get_ASN1_TIME_data(&p, &sec, 2); get_ASN1_TIME_data(&p, &min, 2); get_ASN1_TIME_data(&p, &hour, 2); get_ASN1_TIME_data(&p, &mday, 2); get_ASN1_TIME_data(&p, &mon, 2); get_ASN1_TIME_data(&p, &year, 4); env->CallVoidMethod(calendar, calendar_setMethod, year, mon - 1, mday, hour, min, sec); } static jstring NativeCrypto_OBJ_txt2nid_oid(JNIEnv* env, jclass, jstring oidStr) { JNI_TRACE("OBJ_txt2nid_oid(%p)", oidStr); ScopedUtfChars oid(env, oidStr); if (oid.c_str() == nullptr) { return nullptr; } JNI_TRACE("OBJ_txt2nid_oid(%s)", oid.c_str()); int nid = OBJ_txt2nid(oid.c_str()); if (nid == NID_undef) { JNI_TRACE("OBJ_txt2nid_oid(%s) => NID_undef", oid.c_str()); freeOpenSslErrorState(); return nullptr; } const ASN1_OBJECT* obj = OBJ_nid2obj(nid); if (obj == nullptr) { throwExceptionIfNecessary(env, "OBJ_nid2obj"); return nullptr; } ScopedLocalRef<jstring> ouputStr(env, ASN1_OBJECT_to_OID_string(env, obj)); JNI_TRACE("OBJ_txt2nid_oid(%s) => %p", oid.c_str(), ouputStr.get()); return ouputStr.release(); } static jstring NativeCrypto_X509_NAME_print_ex(JNIEnv* env, jclass, jlong x509NameRef, jlong jflags) { X509_NAME* x509name = reinterpret_cast<X509_NAME*>(static_cast<uintptr_t>(x509NameRef)); unsigned long flags = static_cast<unsigned long>(jflags); JNI_TRACE("X509_NAME_print_ex(%p, %ld)", x509name, flags); if (x509name == nullptr) { jniThrowNullPointerException(env, "x509name == null"); JNI_TRACE("X509_NAME_print_ex(%p, %ld) => x509name == null", x509name, flags); return nullptr; } return X509_NAME_to_jstring(env, x509name, flags); } template <typename T, T* (*d2i_func)(BIO*, T**)> static jlong d2i_ASN1Object_to_jlong(JNIEnv* env, jlong bioRef) { BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); JNI_TRACE("d2i_ASN1Object_to_jlong(%p)", bio); if (bio == nullptr) { jniThrowNullPointerException(env, "bio == null"); return 0; } T* x = d2i_func(bio, nullptr); if (x == nullptr) { throwExceptionIfNecessary(env, "d2i_ASN1Object_to_jlong"); return 0; } return reinterpret_cast<uintptr_t>(x); } static jlong NativeCrypto_d2i_X509_CRL_bio(JNIEnv* env, jclass, jlong bioRef) { return d2i_ASN1Object_to_jlong<X509_CRL, d2i_X509_CRL_bio>(env, bioRef); } static jlong NativeCrypto_d2i_X509_bio(JNIEnv* env, jclass, jlong bioRef) { return d2i_ASN1Object_to_jlong<X509, d2i_X509_bio>(env, bioRef); } static jlong NativeCrypto_d2i_X509(JNIEnv* env, jclass, jbyteArray certBytes) { X509* x = ByteArrayToASN1<X509, d2i_X509>(env, certBytes); return reinterpret_cast<uintptr_t>(x); } static jbyteArray NativeCrypto_i2d_X509(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("i2d_X509(%p)", x509); return ASN1ToByteArray<X509>(env, x509, i2d_X509); } static jbyteArray NativeCrypto_i2d_X509_PUBKEY(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("i2d_X509_PUBKEY(%p)", x509); return ASN1ToByteArray<X509_PUBKEY>(env, X509_get_X509_PUBKEY(x509), i2d_X509_PUBKEY); } template<typename T, T* (*PEM_read_func)(BIO*, T**, pem_password_cb*, void*)> static jlong PEM_to_jlong(JNIEnv* env, jlong bioRef) { BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); JNI_TRACE("PEM_to_jlong(%p)", bio); if (bio == nullptr) { jniThrowNullPointerException(env, "bio == null"); JNI_TRACE("PEM_to_jlong(%p) => bio == null", bio); return 0; } T* x = PEM_read_func(bio, nullptr, nullptr, nullptr); if (x == nullptr) { throwExceptionIfNecessary(env, "PEM_to_jlong"); // Sometimes the PEM functions fail without pushing an error if (!env->ExceptionCheck()) { jniThrowRuntimeException(env, "Failure parsing PEM"); } JNI_TRACE("PEM_to_jlong(%p) => threw exception", bio); return 0; } JNI_TRACE("PEM_to_jlong(%p) => %p", bio, x); return reinterpret_cast<uintptr_t>(x); } static jlong NativeCrypto_PEM_read_bio_X509(JNIEnv* env, jclass, jlong bioRef) { JNI_TRACE("PEM_read_bio_X509(0x%llx)", (long long) bioRef); return PEM_to_jlong<X509, PEM_read_bio_X509>(env, bioRef); } static jlong NativeCrypto_PEM_read_bio_X509_CRL(JNIEnv* env, jclass, jlong bioRef) { JNI_TRACE("PEM_read_bio_X509_CRL(0x%llx)", (long long) bioRef); return PEM_to_jlong<X509_CRL, PEM_read_bio_X509_CRL>(env, bioRef); } static jlong NativeCrypto_PEM_read_bio_PUBKEY(JNIEnv* env, jclass, jlong bioRef) { JNI_TRACE("PEM_read_bio_PUBKEY(0x%llx)", (long long) bioRef); return PEM_to_jlong<EVP_PKEY, PEM_read_bio_PUBKEY>(env, bioRef); } static jlong NativeCrypto_PEM_read_bio_PrivateKey(JNIEnv* env, jclass, jlong bioRef) { JNI_TRACE("PEM_read_bio_PrivateKey(0x%llx)", (long long) bioRef); return PEM_to_jlong<EVP_PKEY, PEM_read_bio_PrivateKey>(env, bioRef); } template <typename T, typename T_stack> static jlongArray PKCS7_to_ItemArray(JNIEnv* env, T_stack* stack, T* (*dup_func)(T*)) { if (stack == nullptr) { return nullptr; } ScopedLocalRef<jlongArray> ref_array(env, nullptr); size_t size = sk_num(reinterpret_cast<_STACK*>(stack)); ref_array.reset(env->NewLongArray(size)); ScopedLongArrayRW items(env, ref_array.get()); for (size_t i = 0; i < size; i++) { T* item = reinterpret_cast<T*>(sk_value(reinterpret_cast<_STACK*>(stack), i)); items[i] = reinterpret_cast<uintptr_t>(dup_func(item)); } JNI_TRACE("PKCS7_to_ItemArray(%p) => %p [size=%zd]", stack, ref_array.get(), size); return ref_array.release(); } #define PKCS7_CERTS 1 #define PKCS7_CRLS 2 static jbyteArray NativeCrypto_i2d_PKCS7(JNIEnv* env, jclass, jlongArray certsArray) { #if !defined(OPENSSL_IS_BORINGSSL) JNI_TRACE("i2d_PKCS7(%p)", certsArray); Unique_PKCS7 pkcs7(PKCS7_new()); if (pkcs7.get() == NULL) { jniThrowNullPointerException(env, "pkcs7 == null"); JNI_TRACE("i2d_PKCS7(%p) => pkcs7 == null", certsArray); return NULL; } if (PKCS7_set_type(pkcs7.get(), NID_pkcs7_signed) != 1) { throwExceptionIfNecessary(env, "PKCS7_set_type"); return NULL; } // The EncapsulatedContentInfo must be present in the output, but OpenSSL // will fill in a zero-length OID if you don't call PKCS7_set_content on the // outer PKCS7 container. So we construct an empty PKCS7 data container and // set it as the content. Unique_PKCS7 pkcs7Data(PKCS7_new()); if (PKCS7_set_type(pkcs7Data.get(), NID_pkcs7_data) != 1) { throwExceptionIfNecessary(env, "PKCS7_set_type data"); return NULL; } if (PKCS7_set_content(pkcs7.get(), pkcs7Data.get()) != 1) { throwExceptionIfNecessary(env, "PKCS7_set_content"); return NULL; } OWNERSHIP_TRANSFERRED(pkcs7Data); ScopedLongArrayRO certs(env, certsArray); for (size_t i = 0; i < certs.size(); i++) { X509* item = reinterpret_cast<X509*>(certs[i]); if (PKCS7_add_certificate(pkcs7.get(), item) != 1) { throwExceptionIfNecessary(env, "i2d_PKCS7"); return NULL; } } JNI_TRACE("i2d_PKCS7(%p) => %zd certs", certsArray, certs.size()); return ASN1ToByteArray<PKCS7>(env, pkcs7.get(), i2d_PKCS7); #else // OPENSSL_IS_BORINGSSL STACK_OF(X509) *stack = sk_X509_new_null(); ScopedLongArrayRO certs(env, certsArray); for (size_t i = 0; i < certs.size(); i++) { X509* item = reinterpret_cast<X509*>(certs[i]); if (sk_X509_push(stack, item) == 0) { sk_X509_free(stack); throwExceptionIfNecessary(env, "sk_X509_push"); return nullptr; } } CBB out; CBB_init(&out, 1024 * certs.size()); if (!PKCS7_bundle_certificates(&out, stack)) { CBB_cleanup(&out); sk_X509_free(stack); throwExceptionIfNecessary(env, "PKCS7_bundle_certificates"); return nullptr; } sk_X509_free(stack); uint8_t *derBytes; size_t derLen; if (!CBB_finish(&out, &derBytes, &derLen)) { CBB_cleanup(&out); throwExceptionIfNecessary(env, "CBB_finish"); return nullptr; } ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(derLen)); if (byteArray.get() == nullptr) { JNI_TRACE("creating byte array failed"); return nullptr; } ScopedByteArrayRW bytes(env, byteArray.get()); if (bytes.get() == nullptr) { JNI_TRACE("using byte array failed"); return nullptr; } uint8_t* p = reinterpret_cast<unsigned char*>(bytes.get()); memcpy(p, derBytes, derLen); return byteArray.release(); #endif // OPENSSL_IS_BORINGSSL } #if !defined(OPENSSL_IS_BORINGSSL) static STACK_OF(X509)* PKCS7_get_certs(PKCS7* pkcs7) { if (PKCS7_type_is_signed(pkcs7)) { return pkcs7->d.sign->cert; } else if (PKCS7_type_is_signedAndEnveloped(pkcs7)) { return pkcs7->d.signed_and_enveloped->cert; } else { JNI_TRACE("PKCS7_get_certs(%p) => unknown PKCS7 type", pkcs7); return NULL; } } static STACK_OF(X509_CRL)* PKCS7_get_CRLs(PKCS7* pkcs7) { if (PKCS7_type_is_signed(pkcs7)) { return pkcs7->d.sign->crl; } else if (PKCS7_type_is_signedAndEnveloped(pkcs7)) { return pkcs7->d.signed_and_enveloped->crl; } else { JNI_TRACE("PKCS7_get_CRLs(%p) => unknown PKCS7 type", pkcs7); return NULL; } } #endif static jlongArray NativeCrypto_PEM_read_bio_PKCS7(JNIEnv* env, jclass, jlong bioRef, jint which) { BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); JNI_TRACE("PEM_read_bio_PKCS7_CRLs(%p)", bio); if (bio == nullptr) { jniThrowNullPointerException(env, "bio == null"); JNI_TRACE("PEM_read_bio_PKCS7_CRLs(%p) => bio == null", bio); return nullptr; } #if !defined(OPENSSL_IS_BORINGSSL) Unique_PKCS7 pkcs7(PEM_read_bio_PKCS7(bio, NULL, NULL, NULL)); if (pkcs7.get() == NULL) { throwExceptionIfNecessary(env, "PEM_read_bio_PKCS7_CRLs"); JNI_TRACE("PEM_read_bio_PKCS7_CRLs(%p) => threw exception", bio); return 0; } switch (which) { case PKCS7_CERTS: return PKCS7_to_ItemArray<X509, STACK_OF(X509)>(env, PKCS7_get_certs(pkcs7.get()), X509_dup); case PKCS7_CRLS: return PKCS7_to_ItemArray<X509_CRL, STACK_OF(X509_CRL)>(env, PKCS7_get_CRLs(pkcs7.get()), X509_CRL_dup); default: jniThrowRuntimeException(env, "unknown PKCS7 field"); return NULL; } #else if (which == PKCS7_CERTS) { Unique_sk_X509 outCerts(sk_X509_new_null()); if (!PKCS7_get_PEM_certificates(outCerts.get(), bio)) { throwExceptionIfNecessary(env, "PKCS7_get_PEM_certificates"); return nullptr; } return PKCS7_to_ItemArray<X509, STACK_OF(X509)>(env, outCerts.get(), X509_dup); } else if (which == PKCS7_CRLS) { Unique_sk_X509_CRL outCRLs(sk_X509_CRL_new_null()); if (!PKCS7_get_PEM_CRLs(outCRLs.get(), bio)) { throwExceptionIfNecessary(env, "PKCS7_get_PEM_CRLs"); return nullptr; } return PKCS7_to_ItemArray<X509_CRL, STACK_OF(X509_CRL)>( env, outCRLs.get(), X509_CRL_dup); } else { jniThrowRuntimeException(env, "unknown PKCS7 field"); return nullptr; } #endif } static jlongArray NativeCrypto_d2i_PKCS7_bio(JNIEnv* env, jclass, jlong bioRef, jint which) { BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); JNI_TRACE("d2i_PKCS7_bio(%p, %d)", bio, which); if (bio == nullptr) { jniThrowNullPointerException(env, "bio == null"); JNI_TRACE("d2i_PKCS7_bio(%p, %d) => bio == null", bio, which); return nullptr; } #if !defined(OPENSSL_IS_BORINGSSL) Unique_PKCS7 pkcs7(d2i_PKCS7_bio(bio, NULL)); if (pkcs7.get() == NULL) { throwExceptionIfNecessary(env, "d2i_PKCS7_bio"); JNI_TRACE("d2i_PKCS7_bio(%p, %d) => threw exception", bio, which); return 0; } switch (which) { case PKCS7_CERTS: JNI_TRACE("d2i_PKCS7_bio(%p, %d) => returned", bio, which); return PKCS7_to_ItemArray<X509, STACK_OF(X509)>(env, PKCS7_get_certs(pkcs7.get()), X509_dup); case PKCS7_CRLS: JNI_TRACE("d2i_PKCS7_bio(%p, %d) => returned", bio, which); return PKCS7_to_ItemArray<X509_CRL, STACK_OF(X509_CRL)>(env, PKCS7_get_CRLs(pkcs7.get()), X509_CRL_dup); default: jniThrowRuntimeException(env, "unknown PKCS7 field"); return NULL; } #else uint8_t *data; size_t len; if (!BIO_read_asn1(bio, &data, &len, 256 * 1024 * 1024 /* max length, 256MB for sanity */)) { if (!throwExceptionIfNecessary(env, "Error reading PKCS#7 data")) { throwParsingException(env, "Error reading PKCS#7 data"); } JNI_TRACE("d2i_PKCS7_bio(%p, %d) => error reading BIO", bio, which); return nullptr; } Unique_OPENSSL_str data_storage(data); CBS cbs; CBS_init(&cbs, data, len); if (which == PKCS7_CERTS) { Unique_sk_X509 outCerts(sk_X509_new_null()); if (!PKCS7_get_certificates(outCerts.get(), &cbs)) { if (!throwExceptionIfNecessary(env, "PKCS7_get_certificates")) { throwParsingException(env, "Error parsing PKCS#7 certificate data"); } JNI_TRACE("d2i_PKCS7_bio(%p, %d) => error reading certs", bio, which); return nullptr; } JNI_TRACE("d2i_PKCS7_bio(%p, %d) => success certs", bio, which); return PKCS7_to_ItemArray<X509, STACK_OF(X509)>(env, outCerts.get(), X509_dup); } else if (which == PKCS7_CRLS) { Unique_sk_X509_CRL outCRLs(sk_X509_CRL_new_null()); if (!PKCS7_get_CRLs(outCRLs.get(), &cbs)) { if (!throwExceptionIfNecessary(env, "PKCS7_get_CRLs")) { throwParsingException(env, "Error parsing PKCS#7 CRL data"); } JNI_TRACE("d2i_PKCS7_bio(%p, %d) => error reading CRLs", bio, which); return nullptr; } JNI_TRACE("d2i_PKCS7_bio(%p, %d) => success CRLs", bio, which); return PKCS7_to_ItemArray<X509_CRL, STACK_OF(X509_CRL)>( env, outCRLs.get(), X509_CRL_dup); } else { jniThrowRuntimeException(env, "unknown PKCS7 field"); return nullptr; } #endif } typedef STACK_OF(X509) PKIPATH; ASN1_ITEM_TEMPLATE(PKIPATH) = ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, PkiPath, X509) ASN1_ITEM_TEMPLATE_END(PKIPATH) static jlongArray NativeCrypto_ASN1_seq_unpack_X509_bio(JNIEnv* env, jclass, jlong bioRef) { BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); JNI_TRACE("ASN1_seq_unpack_X509_bio(%p)", bio); Unique_sk_X509 path((PKIPATH*)ASN1_item_d2i_bio(ASN1_ITEM_rptr(PKIPATH), bio, nullptr)); if (path.get() == nullptr) { throwExceptionIfNecessary(env, "ASN1_seq_unpack_X509_bio"); JNI_TRACE("ASN1_seq_unpack_X509_bio(%p) => threw error", bio); return nullptr; } size_t size = sk_X509_num(path.get()); ScopedLocalRef<jlongArray> certArray(env, env->NewLongArray(size)); ScopedLongArrayRW certs(env, certArray.get()); for (size_t i = 0; i < size; i++) { X509* item = reinterpret_cast<X509*>(sk_X509_shift(path.get())); certs[i] = reinterpret_cast<uintptr_t>(item); } JNI_TRACE("ASN1_seq_unpack_X509_bio(%p) => returns %zd items", bio, size); return certArray.release(); } static jbyteArray NativeCrypto_ASN1_seq_pack_X509(JNIEnv* env, jclass, jlongArray certs) { JNI_TRACE("ASN1_seq_pack_X509(%p)", certs); ScopedLongArrayRO certsArray(env, certs); if (certsArray.get() == nullptr) { JNI_TRACE("ASN1_seq_pack_X509(%p) => failed to get certs array", certs); return nullptr; } Unique_sk_X509 certStack(sk_X509_new_null()); if (certStack.get() == nullptr) { JNI_TRACE("ASN1_seq_pack_X509(%p) => failed to make cert stack", certs); return nullptr; } #if !defined(OPENSSL_IS_BORINGSSL) for (size_t i = 0; i < certsArray.size(); i++) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(certsArray[i])); sk_X509_push(certStack.get(), X509_dup_nocopy(x509)); } int len; Unique_OPENSSL_str encoded(ASN1_seq_pack( reinterpret_cast<STACK_OF(OPENSSL_BLOCK)*>( reinterpret_cast<uintptr_t>(certStack.get())), reinterpret_cast<int (*)(void*, unsigned char**)>(i2d_X509), NULL, &len)); if (encoded.get() == NULL || len < 0) { JNI_TRACE("ASN1_seq_pack_X509(%p) => trouble encoding", certs); return NULL; } uint8_t *out = encoded.get(); size_t out_len = len; #else CBB result, seq_contents; if (!CBB_init(&result, 2048 * certsArray.size())) { JNI_TRACE("ASN1_seq_pack_X509(%p) => CBB_init failed", certs); return nullptr; } if (!CBB_add_asn1(&result, &seq_contents, CBS_ASN1_SEQUENCE)) { CBB_cleanup(&result); return nullptr; } for (size_t i = 0; i < certsArray.size(); i++) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(certsArray[i])); uint8_t *buf; int len = i2d_X509(x509, nullptr); if (len < 0 || !CBB_add_space(&seq_contents, &buf, len) || i2d_X509(x509, &buf) < 0) { CBB_cleanup(&result); return nullptr; } } uint8_t *out; size_t out_len; if (!CBB_finish(&result, &out, &out_len)) { CBB_cleanup(&result); return nullptr; } UniquePtr<uint8_t> out_storage(out); #endif ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(out_len)); if (byteArray.get() == nullptr) { JNI_TRACE("ASN1_seq_pack_X509(%p) => creating byte array failed", certs); return nullptr; } ScopedByteArrayRW bytes(env, byteArray.get()); if (bytes.get() == nullptr) { JNI_TRACE("ASN1_seq_pack_X509(%p) => using byte array failed", certs); return nullptr; } uint8_t *p = reinterpret_cast<uint8_t*>(bytes.get()); memcpy(p, out, out_len); return byteArray.release(); } static void NativeCrypto_X509_free(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_free(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("X509_free(%p) => x509 == null", x509); return; } X509_free(x509); } static jlong NativeCrypto_X509_dup(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_dup(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("X509_dup(%p) => x509 == null", x509); return 0; } return reinterpret_cast<uintptr_t>(X509_dup(x509)); } static jint NativeCrypto_X509_cmp(JNIEnv* env, jclass, jlong x509Ref1, jlong x509Ref2) { X509* x509_1 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref1)); X509* x509_2 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref2)); JNI_TRACE("X509_cmp(%p, %p)", x509_1, x509_2); if (x509_1 == nullptr) { jniThrowNullPointerException(env, "x509_1 == null"); JNI_TRACE("X509_cmp(%p, %p) => x509_1 == null", x509_1, x509_2); return -1; } if (x509_2 == nullptr) { jniThrowNullPointerException(env, "x509_2 == null"); JNI_TRACE("X509_cmp(%p, %p) => x509_2 == null", x509_1, x509_2); return -1; } int ret = X509_cmp(x509_1, x509_2); JNI_TRACE("X509_cmp(%p, %p) => %d", x509_1, x509_2, ret); return ret; } static void NativeCrypto_X509_delete_ext(JNIEnv* env, jclass, jlong x509Ref, jstring oidString) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_delete_ext(%p, %p)", x509, oidString); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("X509_delete_ext(%p, %p) => x509 == null", x509, oidString); return; } ScopedUtfChars oid(env, oidString); if (oid.c_str() == nullptr) { JNI_TRACE("X509_delete_ext(%p, %p) => oidString == null", x509, oidString); return; } Unique_ASN1_OBJECT obj(OBJ_txt2obj(oid.c_str(), 1 /* allow numerical form only */)); if (obj.get() == nullptr) { JNI_TRACE("X509_delete_ext(%p, %s) => oid conversion failed", x509, oid.c_str()); freeOpenSslErrorState(); jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid OID."); return; } int extIndex = X509_get_ext_by_OBJ(x509, obj.get(), -1); if (extIndex == -1) { JNI_TRACE("X509_delete_ext(%p, %s) => ext not found", x509, oid.c_str()); return; } X509_EXTENSION* ext = X509_delete_ext(x509, extIndex); if (ext != nullptr) { X509_EXTENSION_free(ext); // Invalidate the cached encoding #if defined(OPENSSL_IS_BORINGSSL) X509_CINF_set_modified(X509_get_cert_info(x509)); #else x509->cert_info->enc.modified = 1; #endif } } static void NativeCrypto_X509_print_ex(JNIEnv* env, jclass, jlong bioRef, jlong x509Ref, jlong nmflagJava, jlong certflagJava) { BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); long nmflag = static_cast<long>(nmflagJava); long certflag = static_cast<long>(certflagJava); JNI_TRACE("X509_print_ex(%p, %p, %ld, %ld)", bio, x509, nmflag, certflag); if (bio == nullptr) { jniThrowNullPointerException(env, "bio == null"); JNI_TRACE("X509_print_ex(%p, %p, %ld, %ld) => bio == null", bio, x509, nmflag, certflag); return; } if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("X509_print_ex(%p, %p, %ld, %ld) => x509 == null", bio, x509, nmflag, certflag); return; } if (!X509_print_ex(bio, x509, nmflag, certflag)) { throwExceptionIfNecessary(env, "X509_print_ex"); JNI_TRACE("X509_print_ex(%p, %p, %ld, %ld) => threw error", bio, x509, nmflag, certflag); } else { JNI_TRACE("X509_print_ex(%p, %p, %ld, %ld) => success", bio, x509, nmflag, certflag); } } static jlong NativeCrypto_X509_get_pubkey(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_get_pubkey(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("X509_get_pubkey(%p) => x509 == null", x509); return 0; } Unique_EVP_PKEY pkey(X509_get_pubkey(x509)); if (pkey.get() == nullptr) { #if defined(OPENSSL_IS_BORINGSSL) const uint32_t last_error = ERR_peek_last_error(); const uint32_t first_error = ERR_peek_error(); if ((ERR_GET_LIB(last_error) == ERR_LIB_EVP && ERR_GET_REASON(last_error) == EVP_R_UNKNOWN_PUBLIC_KEY_TYPE) || (ERR_GET_LIB(first_error) == ERR_LIB_EC && ERR_GET_REASON(first_error) == EC_R_UNKNOWN_GROUP)) { freeOpenSslErrorState(); throwNoSuchAlgorithmException(env, "X509_get_pubkey"); return 0; } #endif throwExceptionIfNecessary(env, "X509_get_pubkey"); return 0; } JNI_TRACE("X509_get_pubkey(%p) => %p", x509, pkey.get()); return reinterpret_cast<uintptr_t>(pkey.release()); } static jbyteArray NativeCrypto_X509_get_issuer_name(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_get_issuer_name(%p)", x509); return ASN1ToByteArray<X509_NAME>(env, X509_get_issuer_name(x509), i2d_X509_NAME); } static jbyteArray NativeCrypto_X509_get_subject_name(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_get_subject_name(%p)", x509); return ASN1ToByteArray<X509_NAME>(env, X509_get_subject_name(x509), i2d_X509_NAME); } static jstring NativeCrypto_get_X509_pubkey_oid(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_pubkey_oid(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("get_X509_pubkey_oid(%p) => x509 == null", x509); return nullptr; } X509_PUBKEY* pubkey = X509_get_X509_PUBKEY(x509); return ASN1_OBJECT_to_OID_string(env, pubkey->algor->algorithm); } static jstring NativeCrypto_get_X509_sig_alg_oid(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_sig_alg_oid(%p)", x509); if (x509 == nullptr || x509->sig_alg == nullptr) { jniThrowNullPointerException(env, "x509 == NULL || x509->sig_alg == NULL"); JNI_TRACE("get_X509_sig_alg_oid(%p) => x509 == NULL", x509); return nullptr; } return ASN1_OBJECT_to_OID_string(env, x509->sig_alg->algorithm); } static jbyteArray NativeCrypto_get_X509_sig_alg_parameter(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_sig_alg_parameter(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("get_X509_sig_alg_parameter(%p) => x509 == null", x509); return nullptr; } if (x509->sig_alg->parameter == nullptr) { JNI_TRACE("get_X509_sig_alg_parameter(%p) => null", x509); return nullptr; } return ASN1ToByteArray<ASN1_TYPE>(env, x509->sig_alg->parameter, i2d_ASN1_TYPE); } static jbooleanArray NativeCrypto_get_X509_issuerUID(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_issuerUID(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("get_X509_issuerUID(%p) => x509 == null", x509); return nullptr; } if (x509->cert_info->issuerUID == nullptr) { JNI_TRACE("get_X509_issuerUID(%p) => null", x509); return nullptr; } return ASN1BitStringToBooleanArray(env, x509->cert_info->issuerUID); } static jbooleanArray NativeCrypto_get_X509_subjectUID(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_subjectUID(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("get_X509_subjectUID(%p) => x509 == null", x509); return nullptr; } if (x509->cert_info->subjectUID == nullptr) { JNI_TRACE("get_X509_subjectUID(%p) => null", x509); return nullptr; } return ASN1BitStringToBooleanArray(env, x509->cert_info->subjectUID); } static jbooleanArray NativeCrypto_get_X509_ex_kusage(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_ex_kusage(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("get_X509_ex_kusage(%p) => x509 == null", x509); return nullptr; } Unique_ASN1_BIT_STRING bitStr( static_cast<ASN1_BIT_STRING*>(X509_get_ext_d2i(x509, NID_key_usage, nullptr, NULL))); if (bitStr.get() == nullptr) { JNI_TRACE("get_X509_ex_kusage(%p) => null", x509); return nullptr; } return ASN1BitStringToBooleanArray(env, bitStr.get()); } static jobjectArray NativeCrypto_get_X509_ex_xkusage(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_ex_xkusage(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("get_X509_ex_xkusage(%p) => x509 == null", x509); return nullptr; } Unique_sk_ASN1_OBJECT objArray(static_cast<STACK_OF(ASN1_OBJECT)*>( X509_get_ext_d2i(x509, NID_ext_key_usage, nullptr, NULL))); if (objArray.get() == nullptr) { JNI_TRACE("get_X509_ex_xkusage(%p) => null", x509); return nullptr; } size_t size = sk_ASN1_OBJECT_num(objArray.get()); ScopedLocalRef<jobjectArray> exKeyUsage(env, env->NewObjectArray(size, stringClass, nullptr)); if (exKeyUsage.get() == nullptr) { return nullptr; } for (size_t i = 0; i < size; i++) { ScopedLocalRef<jstring> oidStr(env, ASN1_OBJECT_to_OID_string(env, sk_ASN1_OBJECT_value(objArray.get(), i))); env->SetObjectArrayElement(exKeyUsage.get(), i, oidStr.get()); } JNI_TRACE("get_X509_ex_xkusage(%p) => success (%zd entries)", x509, size); return exKeyUsage.release(); } static jint NativeCrypto_get_X509_ex_pathlen(JNIEnv* env, jclass, jlong x509Ref) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_ex_pathlen(%p)", x509); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("get_X509_ex_pathlen(%p) => x509 == null", x509); return 0; } /* Just need to do this to cache the ex_* values. */ X509_check_ca(x509); JNI_TRACE("get_X509_ex_pathlen(%p) => %ld", x509, x509->ex_pathlen); return x509->ex_pathlen; } static jbyteArray NativeCrypto_X509_get_ext_oid(JNIEnv* env, jclass, jlong x509Ref, jstring oidString) { X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_get_ext_oid(%p, %p)", x509, oidString); return X509Type_get_ext_oid<X509, X509_get_ext_by_OBJ, X509_get_ext>(env, x509, oidString); } static jbyteArray NativeCrypto_X509_CRL_get_ext_oid(JNIEnv* env, jclass, jlong x509CrlRef, jstring oidString) { X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("X509_CRL_get_ext_oid(%p, %p)", crl, oidString); return X509Type_get_ext_oid<X509_CRL, X509_CRL_get_ext_by_OBJ, X509_CRL_get_ext>(env, crl, oidString); } static jbyteArray NativeCrypto_X509_REVOKED_get_ext_oid(JNIEnv* env, jclass, jlong x509RevokedRef, jstring oidString) { X509_REVOKED* revoked = reinterpret_cast<X509_REVOKED*>(static_cast<uintptr_t>(x509RevokedRef)); JNI_TRACE("X509_REVOKED_get_ext_oid(%p, %p)", revoked, oidString); return X509Type_get_ext_oid<X509_REVOKED, X509_REVOKED_get_ext_by_OBJ, X509_REVOKED_get_ext>( env, revoked, oidString); } template<typename T, int (*get_ext_by_critical_func)(T*, int, int), X509_EXTENSION* (*get_ext_func)(T*, int)> static jobjectArray get_X509Type_ext_oids(JNIEnv* env, jlong x509Ref, jint critical) { T* x509 = reinterpret_cast<T*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509Type_ext_oids(%p, %d)", x509, critical); if (x509 == nullptr) { jniThrowNullPointerException(env, "x509 == null"); JNI_TRACE("get_X509Type_ext_oids(%p, %d) => x509 == null", x509, critical); return nullptr; } int lastPos = -1; int count = 0; while ((lastPos = get_ext_by_critical_func(x509, critical, lastPos)) != -1) { count++; } JNI_TRACE("get_X509Type_ext_oids(%p, %d) has %d entries", x509, critical, count); ScopedLocalRef<jobjectArray> joa(env, env->NewObjectArray(count, stringClass, nullptr)); if (joa.get() == nullptr) { JNI_TRACE("get_X509Type_ext_oids(%p, %d) => fail to allocate result array", x509, critical); return nullptr; } lastPos = -1; count = 0; while ((lastPos = get_ext_by_critical_func(x509, critical, lastPos)) != -1) { X509_EXTENSION* ext = get_ext_func(x509, lastPos); ScopedLocalRef<jstring> extOid(env, ASN1_OBJECT_to_OID_string(env, ext->object)); if (extOid.get() == nullptr) { JNI_TRACE("get_X509Type_ext_oids(%p) => couldn't get OID", x509); return nullptr; } env->SetObjectArrayElement(joa.get(), count++, extOid.get()); } JNI_TRACE("get_X509Type_ext_oids(%p, %d) => success", x509, critical); return joa.release(); } static jobjectArray NativeCrypto_get_X509_ext_oids(JNIEnv* env, jclass, jlong x509Ref, jint critical) { JNI_TRACE("get_X509_ext_oids(0x%llx, %d)", (long long) x509Ref, critical); return get_X509Type_ext_oids<X509, X509_get_ext_by_critical, X509_get_ext>(env, x509Ref, critical); } static jobjectArray NativeCrypto_get_X509_CRL_ext_oids(JNIEnv* env, jclass, jlong x509CrlRef, jint critical) { JNI_TRACE("get_X509_CRL_ext_oids(0x%llx, %d)", (long long) x509CrlRef, critical); return get_X509Type_ext_oids<X509_CRL, X509_CRL_get_ext_by_critical, X509_CRL_get_ext>(env, x509CrlRef, critical); } static jobjectArray NativeCrypto_get_X509_REVOKED_ext_oids(JNIEnv* env, jclass, jlong x509RevokedRef, jint critical) { JNI_TRACE("get_X509_CRL_ext_oids(0x%llx, %d)", (long long) x509RevokedRef, critical); return get_X509Type_ext_oids<X509_REVOKED, X509_REVOKED_get_ext_by_critical, X509_REVOKED_get_ext>(env, x509RevokedRef, critical); } #ifdef WITH_JNI_TRACE /** * Based on example logging call back from SSL_CTX_set_info_callback man page */ static void info_callback_LOG(const SSL* s __attribute__ ((unused)), int where, int ret) { int w = where & ~SSL_ST_MASK; const char* str; if (w & SSL_ST_CONNECT) { str = "SSL_connect"; } else if (w & SSL_ST_ACCEPT) { str = "SSL_accept"; } else { str = "undefined"; } if (where & SSL_CB_LOOP) { JNI_TRACE("ssl=%p %s:%s %s", s, str, SSL_state_string(s), SSL_state_string_long(s)); } else if (where & SSL_CB_ALERT) { str = (where & SSL_CB_READ) ? "read" : "write"; JNI_TRACE("ssl=%p SSL3 alert %s:%s:%s %s %s", s, str, SSL_alert_type_string(ret), SSL_alert_desc_string(ret), SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); } else if (where & SSL_CB_EXIT) { if (ret == 0) { JNI_TRACE("ssl=%p %s:failed exit in %s %s", s, str, SSL_state_string(s), SSL_state_string_long(s)); } else if (ret < 0) { JNI_TRACE("ssl=%p %s:error exit in %s %s", s, str, SSL_state_string(s), SSL_state_string_long(s)); } else if (ret == 1) { JNI_TRACE("ssl=%p %s:ok exit in %s %s", s, str, SSL_state_string(s), SSL_state_string_long(s)); } else { JNI_TRACE("ssl=%p %s:unknown exit %d in %s %s", s, str, ret, SSL_state_string(s), SSL_state_string_long(s)); } } else if (where & SSL_CB_HANDSHAKE_START) { JNI_TRACE("ssl=%p handshake start in %s %s", s, SSL_state_string(s), SSL_state_string_long(s)); } else if (where & SSL_CB_HANDSHAKE_DONE) { JNI_TRACE("ssl=%p handshake done in %s %s", s, SSL_state_string(s), SSL_state_string_long(s)); } else { JNI_TRACE("ssl=%p %s:unknown where %d in %s %s", s, str, where, SSL_state_string(s), SSL_state_string_long(s)); } } #endif /** * Returns an array containing all the X509 certificate references */ static jlongArray getCertificateRefs(JNIEnv* env, const STACK_OF(X509)* chain) { if (chain == nullptr) { // Chain can be NULL if the associated cipher doesn't do certs. return nullptr; } ssize_t count = sk_X509_num(chain); if (count <= 0) { return nullptr; } ScopedLocalRef<jlongArray> refArray(env, env->NewLongArray(count)); ScopedLongArrayRW refs(env, refArray.get()); if (refs.get() == nullptr) { return nullptr; } for (ssize_t i = 0; i < count; i++) { refs[i] = reinterpret_cast<uintptr_t>(X509_dup_nocopy(sk_X509_value(chain, i))); } return refArray.release(); } /** * Returns an array containing all the X500 principal's bytes. */ static jobjectArray getPrincipalBytes(JNIEnv* env, const STACK_OF(X509_NAME)* names) { if (names == nullptr) { return nullptr; } int count = sk_X509_NAME_num(names); if (count <= 0) { return nullptr; } ScopedLocalRef<jobjectArray> joa(env, env->NewObjectArray(count, byteArrayClass, nullptr)); if (joa.get() == nullptr) { return nullptr; } for (int i = 0; i < count; i++) { X509_NAME* principal = sk_X509_NAME_value(names, i); ScopedLocalRef<jbyteArray> byteArray(env, ASN1ToByteArray<X509_NAME>(env, principal, i2d_X509_NAME)); if (byteArray.get() == nullptr) { return nullptr; } env->SetObjectArrayElement(joa.get(), i, byteArray.get()); } return joa.release(); } /** * Our additional application data needed for getting synchronization right. * This maybe warrants a bit of lengthy prose: * * (1) We use a flag to reflect whether we consider the SSL connection alive. * Any read or write attempt loops will be cancelled once this flag becomes 0. * * (2) We use an int to count the number of threads that are blocked by the * underlying socket. This may be at most two (one reader and one writer), since * the Java layer ensures that no more threads will enter the native code at the * same time. * * (3) The pipe is used primarily as a means of cancelling a blocking select() * when we want to close the connection (aka "emergency button"). It is also * necessary for dealing with a possible race condition situation: There might * be cases where both threads see an SSL_ERROR_WANT_READ or * SSL_ERROR_WANT_WRITE. Both will enter a select() with the proper argument. * If one leaves the select() successfully before the other enters it, the * "success" event is already consumed and the second thread will be blocked, * possibly forever (depending on network conditions). * * The idea for solving the problem looks like this: Whenever a thread is * successful in moving around data on the network, and it knows there is * another thread stuck in a select(), it will write a byte to the pipe, waking * up the other thread. A thread that returned from select(), on the other hand, * knows whether it's been woken up by the pipe. If so, it will consume the * byte, and the original state of affairs has been restored. * * The pipe may seem like a bit of overhead, but it fits in nicely with the * other file descriptors of the select(), so there's only one condition to wait * for. * * (4) Finally, a mutex is needed to make sure that at most one thread is in * either SSL_read() or SSL_write() at any given time. This is an OpenSSL * requirement. We use the same mutex to guard the field for counting the * waiting threads. * * Note: The current implementation assumes that we don't have to deal with * problems induced by multiple cores or processors and their respective * memory caches. One possible problem is that of inconsistent views on the * "aliveAndKicking" field. This could be worked around by also enclosing all * accesses to that field inside a lock/unlock sequence of our mutex, but * currently this seems a bit like overkill. Marking volatile at the very least. * * During handshaking, additional fields are used to up-call into * Java to perform certificate verification and handshake * completion. These are also used in any renegotiation. * * (5) the JNIEnv so we can invoke the Java callback * * (6) a NativeCrypto.SSLHandshakeCallbacks instance for callbacks from native to Java * * (7) a java.io.FileDescriptor wrapper to check for socket close * * We store the NPN protocols list so we can either send it (from the server) or * select a protocol (on the client). We eagerly acquire a pointer to the array * data so the callback doesn't need to acquire resources that it cannot * release. * * Because renegotiation can be requested by the peer at any time, * care should be taken to maintain an appropriate JNIEnv on any * downcall to openssl since it could result in an upcall to Java. The * current code does try to cover these cases by conditionally setting * the JNIEnv on calls that can read and write to the SSL such as * SSL_do_handshake, SSL_read, SSL_write, and SSL_shutdown. * * Finally, we have two emphemeral keys setup by OpenSSL callbacks: * * (8) a set of ephemeral RSA keys that is lazily generated if a peer * wants to use an exportable RSA cipher suite. * * (9) a set of ephemeral EC keys that is lazily generated if a peer * wants to use an TLS_ECDHE_* cipher suite. * */ class AppData { public: volatile int aliveAndKicking; int waitingThreads; int fdsEmergency[2]; MUTEX_TYPE mutex; JNIEnv* env; jobject sslHandshakeCallbacks; jbyteArray npnProtocolsArray; jbyte* npnProtocolsData; size_t npnProtocolsLength; jbyteArray alpnProtocolsArray; jbyte* alpnProtocolsData; size_t alpnProtocolsLength; Unique_RSA ephemeralRsa; /** * Creates the application data context for the SSL*. */ public: static AppData* create() { UniquePtr<AppData> appData(new AppData()); if (pipe(appData.get()->fdsEmergency) == -1) { ALOGE("AppData::create pipe(2) failed: %s", strerror(errno)); return nullptr; } if (!setBlocking(appData.get()->fdsEmergency[0], false)) { ALOGE("AppData::create fcntl(2) failed: %s", strerror(errno)); return nullptr; } if (MUTEX_SETUP(appData.get()->mutex) == -1) { ALOGE("pthread_mutex_init(3) failed: %s", strerror(errno)); return nullptr; } return appData.release(); } ~AppData() { aliveAndKicking = 0; if (fdsEmergency[0] != -1) { close(fdsEmergency[0]); } if (fdsEmergency[1] != -1) { close(fdsEmergency[1]); } clearCallbackState(); MUTEX_CLEANUP(mutex); } private: AppData() : aliveAndKicking(1), waitingThreads(0), env(nullptr), sslHandshakeCallbacks(nullptr), npnProtocolsArray(nullptr), npnProtocolsData(nullptr), npnProtocolsLength(-1), alpnProtocolsArray(nullptr), alpnProtocolsData(nullptr), alpnProtocolsLength(-1), ephemeralRsa(nullptr) { fdsEmergency[0] = -1; fdsEmergency[1] = -1; } public: /** * Used to set the SSL-to-Java callback state before each SSL_* * call that may result in a callback. It should be cleared after * the operation returns with clearCallbackState. * * @param env The JNIEnv * @param shc The SSLHandshakeCallbacks * @param fd The FileDescriptor * @param npnProtocols NPN protocols so that they may be advertised (by the * server) or selected (by the client). Has no effect * unless NPN is enabled. * @param alpnProtocols ALPN protocols so that they may be advertised (by the * server) or selected (by the client). Passing non-NULL * enables ALPN. */ bool setCallbackState(JNIEnv* e, jobject shc, jobject fd, jbyteArray npnProtocols, jbyteArray alpnProtocols) { UniquePtr<NetFd> netFd; if (fd != nullptr) { netFd.reset(new NetFd(e, fd)); if (netFd->isClosed()) { JNI_TRACE("appData=%p setCallbackState => netFd->isClosed() == true", this); return false; } } env = e; sslHandshakeCallbacks = shc; if (npnProtocols != nullptr) { npnProtocolsData = e->GetByteArrayElements(npnProtocols, nullptr); if (npnProtocolsData == nullptr) { clearCallbackState(); JNI_TRACE("appData=%p setCallbackState => npnProtocolsData == NULL", this); return false; } npnProtocolsArray = npnProtocols; npnProtocolsLength = e->GetArrayLength(npnProtocols); } if (alpnProtocols != nullptr) { alpnProtocolsData = e->GetByteArrayElements(alpnProtocols, nullptr); if (alpnProtocolsData == nullptr) { clearCallbackState(); JNI_TRACE("appData=%p setCallbackState => alpnProtocolsData == NULL", this); return false; } alpnProtocolsArray = alpnProtocols; alpnProtocolsLength = e->GetArrayLength(alpnProtocols); } return true; } void clearCallbackState() { sslHandshakeCallbacks = nullptr; if (npnProtocolsArray != nullptr) { env->ReleaseByteArrayElements(npnProtocolsArray, npnProtocolsData, JNI_ABORT); npnProtocolsArray = nullptr; npnProtocolsData = nullptr; npnProtocolsLength = -1; } if (alpnProtocolsArray != nullptr) { env->ReleaseByteArrayElements(alpnProtocolsArray, alpnProtocolsData, JNI_ABORT); alpnProtocolsArray = nullptr; alpnProtocolsData = nullptr; alpnProtocolsLength = -1; } env = nullptr; } }; /** * Dark magic helper function that checks, for a given SSL session, whether it * can SSL_read() or SSL_write() without blocking. Takes into account any * concurrent attempts to close the SSLSocket from the Java side. This is * needed to get rid of the hangs that occur when thread #1 closes the SSLSocket * while thread #2 is sitting in a blocking read or write. The type argument * specifies whether we are waiting for readability or writability. It expects * to be passed either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, since we * only need to wait in case one of these problems occurs. * * @param env * @param type Either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE * @param fdObject The FileDescriptor, since appData->fileDescriptor should be NULL * @param appData The application data structure with mutex info etc. * @param timeout_millis The timeout value for poll call, with the special value * 0 meaning no timeout at all (wait indefinitely). Note: This is * the Java semantics of the timeout value, not the usual * poll() semantics. * @return The result of the inner poll() call, * THROW_SOCKETEXCEPTION if a SocketException was thrown, -1 on * additional errors */ static int sslSelect(JNIEnv* env, int type, jobject fdObject, AppData* appData, int timeout_millis) { // This loop is an expanded version of the NET_FAILURE_RETRY // macro. It cannot simply be used in this case because poll // cannot be restarted without recreating the pollfd structure. int result; struct pollfd fds[2]; do { NetFd fd(env, fdObject); if (fd.isClosed()) { result = THROWN_EXCEPTION; break; } int intFd = fd.get(); JNI_TRACE("sslSelect type=%s fd=%d appData=%p timeout_millis=%d", (type == SSL_ERROR_WANT_READ) ? "READ" : "WRITE", intFd, appData, timeout_millis); memset(&fds, 0, sizeof(fds)); fds[0].fd = intFd; if (type == SSL_ERROR_WANT_READ) { fds[0].events = POLLIN | POLLPRI; } else { fds[0].events = POLLOUT | POLLPRI; } fds[1].fd = appData->fdsEmergency[0]; fds[1].events = POLLIN | POLLPRI; // Converting from Java semantics to Posix semantics. if (timeout_millis <= 0) { timeout_millis = -1; } #ifndef CONSCRYPT_UNBUNDLED AsynchronousCloseMonitor monitor(intFd); #else CompatibilityCloseMonitor monitor(intFd); #endif result = poll(fds, sizeof(fds)/sizeof(fds[0]), timeout_millis); JNI_TRACE("sslSelect %s fd=%d appData=%p timeout_millis=%d => %d", (type == SSL_ERROR_WANT_READ) ? "READ" : "WRITE", fd.get(), appData, timeout_millis, result); if (result == -1) { if (fd.isClosed()) { result = THROWN_EXCEPTION; break; } if (errno != EINTR) { break; } } } while (result == -1); UniqueMutex appDataLock(&appData->mutex); if (result > 0) { // We have been woken up by a token in the emergency pipe. We // can't be sure the token is still in the pipe at this point // because it could have already been read by the thread that // originally wrote it if it entered sslSelect and acquired // the mutex before we did. Thus we cannot safely read from // the pipe in a blocking way (so we make the pipe // non-blocking at creation). if (fds[1].revents & POLLIN) { char token; do { (void) read(appData->fdsEmergency[0], &token, 1); } while (errno == EINTR); } } // Tell the world that there is now one thread less waiting for the // underlying network. appData->waitingThreads--; return result; } /** * Helper function that wakes up a thread blocked in select(), in case there is * one. Is being called by sslRead() and sslWrite() as well as by JNI glue * before closing the connection. * * @param data The application data structure with mutex info etc. */ static void sslNotify(AppData* appData) { // Write a byte to the emergency pipe, so a concurrent select() can return. // Note we have to restore the errno of the original system call, since the // caller relies on it for generating error messages. int errnoBackup = errno; char token = '*'; do { errno = 0; (void) write(appData->fdsEmergency[1], &token, 1); } while (errno == EINTR); errno = errnoBackup; } static AppData* toAppData(const SSL* ssl) { return reinterpret_cast<AppData*>(SSL_get_app_data(ssl)); } /** * Verify the X509 certificate via SSL_CTX_set_cert_verify_callback */ static int cert_verify_callback(X509_STORE_CTX* x509_store_ctx, void* arg __attribute__ ((unused))) { /* Get the correct index to the SSLobject stored into X509_STORE_CTX. */ SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(x509_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); JNI_TRACE("ssl=%p cert_verify_callback x509_store_ctx=%p arg=%p", ssl, x509_store_ctx, arg); AppData* appData = toAppData(ssl); JNIEnv* env = appData->env; if (env == nullptr) { ALOGE("AppData->env missing in cert_verify_callback"); JNI_TRACE("ssl=%p cert_verify_callback => 0", ssl); return 0; } jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks; jclass cls = env->GetObjectClass(sslHandshakeCallbacks); jmethodID methodID = env->GetMethodID(cls, "verifyCertificateChain", "(J[JLjava/lang/String;)V"); jlongArray refArray = getCertificateRefs(env, x509_store_ctx->untrusted); #if !defined(OPENSSL_IS_BORINGSSL) const char* authMethod = SSL_authentication_method(ssl); #else const SSL_CIPHER *cipher = ssl->s3->tmp.new_cipher; const char *authMethod = SSL_CIPHER_get_kx_name(cipher); #endif JNI_TRACE("ssl=%p cert_verify_callback calling verifyCertificateChain authMethod=%s", ssl, authMethod); jstring authMethodString = env->NewStringUTF(authMethod); env->CallVoidMethod(sslHandshakeCallbacks, methodID, static_cast<jlong>(reinterpret_cast<uintptr_t>(SSL_get1_session(ssl))), refArray, authMethodString); int result = (env->ExceptionCheck()) ? 0 : 1; JNI_TRACE("ssl=%p cert_verify_callback => %d", ssl, result); return result; } /** * Call back to watch for handshake to be completed. This is necessary for * False Start support, since SSL_do_handshake returns before the handshake is * completed in this case. */ static void info_callback(const SSL* ssl, int where, int ret) { JNI_TRACE("ssl=%p info_callback where=0x%x ret=%d", ssl, where, ret); #ifdef WITH_JNI_TRACE info_callback_LOG(ssl, where, ret); #endif if (!(where & SSL_CB_HANDSHAKE_DONE) && !(where & SSL_CB_HANDSHAKE_START)) { JNI_TRACE("ssl=%p info_callback ignored", ssl); return; } AppData* appData = toAppData(ssl); JNIEnv* env = appData->env; if (env == nullptr) { ALOGE("AppData->env missing in info_callback"); JNI_TRACE("ssl=%p info_callback env error", ssl); return; } if (env->ExceptionCheck()) { JNI_TRACE("ssl=%p info_callback already pending exception", ssl); return; } jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks; jclass cls = env->GetObjectClass(sslHandshakeCallbacks); jmethodID methodID = env->GetMethodID(cls, "onSSLStateChange", "(JII)V"); JNI_TRACE("ssl=%p info_callback calling onSSLStateChange", ssl); env->CallVoidMethod(sslHandshakeCallbacks, methodID, reinterpret_cast<jlong>(ssl), where, ret); if (env->ExceptionCheck()) { JNI_TRACE("ssl=%p info_callback exception", ssl); } JNI_TRACE("ssl=%p info_callback completed", ssl); } /** * Call back to ask for a client certificate. There are three possible exit codes: * * 1 is success. x509Out and pkeyOut should point to the correct private key and certificate. * 0 is unable to find key. x509Out and pkeyOut should be NULL. * -1 is error and it doesn't matter what x509Out and pkeyOut are. */ static int client_cert_cb(SSL* ssl, X509** x509Out, EVP_PKEY** pkeyOut) { JNI_TRACE("ssl=%p client_cert_cb x509Out=%p pkeyOut=%p", ssl, x509Out, pkeyOut); /* Clear output of key and certificate in case of early exit due to error. */ *x509Out = nullptr; *pkeyOut = nullptr; AppData* appData = toAppData(ssl); JNIEnv* env = appData->env; if (env == nullptr) { ALOGE("AppData->env missing in client_cert_cb"); JNI_TRACE("ssl=%p client_cert_cb env error => 0", ssl); return 0; } if (env->ExceptionCheck()) { JNI_TRACE("ssl=%p client_cert_cb already pending exception => 0", ssl); return -1; } jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks; jclass cls = env->GetObjectClass(sslHandshakeCallbacks); jmethodID methodID = env->GetMethodID(cls, "clientCertificateRequested", "([B[[B)V"); // Call Java callback which can use SSL_use_certificate and SSL_use_PrivateKey to set values #if !defined(OPENSSL_IS_BORINGSSL) const char* ctype = NULL; char ssl2_ctype = SSL3_CT_RSA_SIGN; int ctype_num = 0; jobjectArray issuers = NULL; switch (ssl->version) { case SSL2_VERSION: ctype = &ssl2_ctype; ctype_num = 1; break; case SSL3_VERSION: case TLS1_VERSION: case TLS1_1_VERSION: case TLS1_2_VERSION: case DTLS1_VERSION: ctype = ssl->s3->tmp.ctype; ctype_num = ssl->s3->tmp.ctype_num; issuers = getPrincipalBytes(env, ssl->s3->tmp.ca_names); break; } #else const uint8_t* ctype = nullptr; int ctype_num = SSL_get0_certificate_types(ssl, &ctype); jobjectArray issuers = getPrincipalBytes(env, SSL_get_client_CA_list(ssl)); #endif #ifdef WITH_JNI_TRACE for (int i = 0; i < ctype_num; i++) { JNI_TRACE("ssl=%p clientCertificateRequested keyTypes[%d]=%d", ssl, i, ctype[i]); } #endif jbyteArray keyTypes = env->NewByteArray(ctype_num); if (keyTypes == nullptr) { JNI_TRACE("ssl=%p client_cert_cb bytes == null => 0", ssl); return 0; } env->SetByteArrayRegion(keyTypes, 0, ctype_num, reinterpret_cast<const jbyte*>(ctype)); JNI_TRACE("ssl=%p clientCertificateRequested calling clientCertificateRequested " "keyTypes=%p issuers=%p", ssl, keyTypes, issuers); env->CallVoidMethod(sslHandshakeCallbacks, methodID, keyTypes, issuers); if (env->ExceptionCheck()) { JNI_TRACE("ssl=%p client_cert_cb exception => 0", ssl); return -1; } // Check for values set from Java X509* certificate = SSL_get_certificate(ssl); EVP_PKEY* privatekey = SSL_get_privatekey(ssl); int result = 0; if (certificate != nullptr && privatekey != nullptr) { *x509Out = certificate; *pkeyOut = privatekey; result = 1; } else { // Some error conditions return NULL, so make sure it doesn't linger. freeOpenSslErrorState(); } JNI_TRACE("ssl=%p client_cert_cb => *x509=%p *pkey=%p %d", ssl, *x509Out, *pkeyOut, result); return result; } /** * Pre-Shared Key (PSK) client callback. */ static unsigned int psk_client_callback(SSL* ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len) { JNI_TRACE("ssl=%p psk_client_callback", ssl); AppData* appData = toAppData(ssl); JNIEnv* env = appData->env; if (env == nullptr) { ALOGE("AppData->env missing in psk_client_callback"); JNI_TRACE("ssl=%p psk_client_callback env error", ssl); return 0; } if (env->ExceptionCheck()) { JNI_TRACE("ssl=%p psk_client_callback already pending exception", ssl); return 0; } jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks; jclass cls = env->GetObjectClass(sslHandshakeCallbacks); jmethodID methodID = env->GetMethodID(cls, "clientPSKKeyRequested", "(Ljava/lang/String;[B[B)I"); JNI_TRACE("ssl=%p psk_client_callback calling clientPSKKeyRequested", ssl); ScopedLocalRef<jstring> identityHintJava(env, (hint != nullptr) ? env->NewStringUTF(hint) : nullptr); ScopedLocalRef<jbyteArray> identityJava(env, env->NewByteArray(max_identity_len)); if (identityJava.get() == nullptr) { JNI_TRACE("ssl=%p psk_client_callback failed to allocate identity bufffer", ssl); return 0; } ScopedLocalRef<jbyteArray> keyJava(env, env->NewByteArray(max_psk_len)); if (keyJava.get() == nullptr) { JNI_TRACE("ssl=%p psk_client_callback failed to allocate key bufffer", ssl); return 0; } jint keyLen = env->CallIntMethod(sslHandshakeCallbacks, methodID, identityHintJava.get(), identityJava.get(), keyJava.get()); if (env->ExceptionCheck()) { JNI_TRACE("ssl=%p psk_client_callback exception", ssl); return 0; } if (keyLen <= 0) { JNI_TRACE("ssl=%p psk_client_callback failed to get key", ssl); return 0; } else if ((unsigned int) keyLen > max_psk_len) { JNI_TRACE("ssl=%p psk_client_callback got key which is too long", ssl); return 0; } ScopedByteArrayRO keyJavaRo(env, keyJava.get()); if (keyJavaRo.get() == nullptr) { JNI_TRACE("ssl=%p psk_client_callback failed to get key bytes", ssl); return 0; } memcpy(psk, keyJavaRo.get(), keyLen); ScopedByteArrayRO identityJavaRo(env, identityJava.get()); if (identityJavaRo.get() == nullptr) { JNI_TRACE("ssl=%p psk_client_callback failed to get identity bytes", ssl); return 0; } memcpy(identity, identityJavaRo.get(), max_identity_len); JNI_TRACE("ssl=%p psk_client_callback completed", ssl); return keyLen; } /** * Pre-Shared Key (PSK) server callback. */ static unsigned int psk_server_callback(SSL* ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len) { JNI_TRACE("ssl=%p psk_server_callback", ssl); AppData* appData = toAppData(ssl); JNIEnv* env = appData->env; if (env == nullptr) { ALOGE("AppData->env missing in psk_server_callback"); JNI_TRACE("ssl=%p psk_server_callback env error", ssl); return 0; } if (env->ExceptionCheck()) { JNI_TRACE("ssl=%p psk_server_callback already pending exception", ssl); return 0; } jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks; jclass cls = env->GetObjectClass(sslHandshakeCallbacks); jmethodID methodID = env->GetMethodID( cls, "serverPSKKeyRequested", "(Ljava/lang/String;Ljava/lang/String;[B)I"); JNI_TRACE("ssl=%p psk_server_callback calling serverPSKKeyRequested", ssl); const char* identityHint = SSL_get_psk_identity_hint(ssl); // identityHint = NULL; // identity = NULL; ScopedLocalRef<jstring> identityHintJava( env, (identityHint != nullptr) ? env->NewStringUTF(identityHint) : nullptr); ScopedLocalRef<jstring> identityJava( env, (identity != nullptr) ? env->NewStringUTF(identity) : nullptr); ScopedLocalRef<jbyteArray> keyJava(env, env->NewByteArray(max_psk_len)); if (keyJava.get() == nullptr) { JNI_TRACE("ssl=%p psk_server_callback failed to allocate key bufffer", ssl); return 0; } jint keyLen = env->CallIntMethod(sslHandshakeCallbacks, methodID, identityHintJava.get(), identityJava.get(), keyJava.get()); if (env->ExceptionCheck()) { JNI_TRACE("ssl=%p psk_server_callback exception", ssl); return 0; } if (keyLen <= 0) { JNI_TRACE("ssl=%p psk_server_callback failed to get key", ssl); return 0; } else if ((unsigned int) keyLen > max_psk_len) { JNI_TRACE("ssl=%p psk_server_callback got key which is too long", ssl); return 0; } ScopedByteArrayRO keyJavaRo(env, keyJava.get()); if (keyJavaRo.get() == nullptr) { JNI_TRACE("ssl=%p psk_server_callback failed to get key bytes", ssl); return 0; } memcpy(psk, keyJavaRo.get(), keyLen); JNI_TRACE("ssl=%p psk_server_callback completed", ssl); return keyLen; } static RSA* rsaGenerateKey(int keylength) { Unique_BIGNUM bn(BN_new()); if (bn.get() == nullptr) { return nullptr; } int setWordResult = BN_set_word(bn.get(), RSA_F4); if (setWordResult != 1) { return nullptr; } Unique_RSA rsa(RSA_new()); if (rsa.get() == nullptr) { return nullptr; } int generateResult = RSA_generate_key_ex(rsa.get(), keylength, bn.get(), nullptr); if (generateResult != 1) { return nullptr; } return rsa.release(); } /** * Call back to ask for an ephemeral RSA key for SSL_RSA_EXPORT_WITH_RC4_40_MD5 (aka EXP-RC4-MD5) */ static RSA* tmp_rsa_callback(SSL* ssl __attribute__ ((unused)), int is_export __attribute__ ((unused)), int keylength) { JNI_TRACE("ssl=%p tmp_rsa_callback is_export=%d keylength=%d", ssl, is_export, keylength); AppData* appData = toAppData(ssl); if (appData->ephemeralRsa.get() == nullptr) { JNI_TRACE("ssl=%p tmp_rsa_callback generating ephemeral RSA key", ssl); appData->ephemeralRsa.reset(rsaGenerateKey(keylength)); } JNI_TRACE("ssl=%p tmp_rsa_callback => %p", ssl, appData->ephemeralRsa.get()); return appData->ephemeralRsa.get(); } static DH* dhGenerateParameters(int keylength) { #if !defined(OPENSSL_IS_BORINGSSL) /* * The SSL_CTX_set_tmp_dh_callback(3SSL) man page discusses two * different options for generating DH keys. One is generating the * keys using a single set of DH parameters. However, generating * DH parameters is slow enough (minutes) that they suggest doing * it once at install time. The other is to generate DH keys from * DSA parameters. Generating DSA parameters is faster than DH * parameters, but to prevent small subgroup attacks, they needed * to be regenerated for each set of DH keys. Setting the * SSL_OP_SINGLE_DH_USE option make sure OpenSSL will call back * for new DH parameters every type it needs to generate DH keys. */ // Fast path but must have SSL_OP_SINGLE_DH_USE set Unique_DSA dsa(DSA_new()); if (!DSA_generate_parameters_ex(dsa.get(), keylength, NULL, 0, NULL, NULL, NULL)) { return NULL; } DH* dh = DSA_dup_DH(dsa.get()); return dh; #else /* At the time of writing, OpenSSL and BoringSSL are hard coded to request * a 1024-bit DH. */ if (keylength <= 1024) { return DH_get_1024_160(nullptr); } if (keylength <= 2048) { return DH_get_2048_224(nullptr); } /* In the case of a large request, return the strongest DH group that * we have predefined. Generating a group takes far too long to be * reasonable. */ return DH_get_2048_256(nullptr); #endif } /** * Call back to ask for Diffie-Hellman parameters */ static DH* tmp_dh_callback(SSL* ssl __attribute__ ((unused)), int is_export __attribute__ ((unused)), int keylength) { JNI_TRACE("ssl=%p tmp_dh_callback is_export=%d keylength=%d", ssl, is_export, keylength); DH* tmp_dh = dhGenerateParameters(keylength); JNI_TRACE("ssl=%p tmp_dh_callback => %p", ssl, tmp_dh); return tmp_dh; } static jint NativeCrypto_EVP_has_aes_hardware(JNIEnv*, jclass) { int ret = 0; #if defined(OPENSSL_IS_BORINGSSL) ret = EVP_has_aes_hardware(); #endif JNI_TRACE("EVP_has_aes_hardware => %d", ret); return ret; } /* * public static native int SSL_CTX_new(); */ static jlong NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass) { Unique_SSL_CTX sslCtx(SSL_CTX_new(SSLv23_method())); if (sslCtx.get() == nullptr) { throwExceptionIfNecessary(env, "SSL_CTX_new"); return 0; } SSL_CTX_set_options(sslCtx.get(), SSL_OP_ALL // Note: We explicitly do not allow SSLv2 to be used. | SSL_OP_NO_SSLv2 // We also disable session tickets for better compatibility b/2682876 | SSL_OP_NO_TICKET // We also disable compression for better compatibility b/2710492 b/2710497 | SSL_OP_NO_COMPRESSION // Because dhGenerateParameters uses DSA_generate_parameters_ex | SSL_OP_SINGLE_DH_USE // Generate a fresh ECDH keypair for each key exchange. | SSL_OP_SINGLE_ECDH_USE); int mode = SSL_CTX_get_mode(sslCtx.get()); /* * Turn on "partial write" mode. This means that SSL_write() will * behave like Posix write() and possibly return after only * writing a partial buffer. Note: The alternative, perhaps * surprisingly, is not that SSL_write() always does full writes * but that it will force you to retry write calls having * preserved the full state of the original call. (This is icky * and undesirable.) */ mode |= SSL_MODE_ENABLE_PARTIAL_WRITE; // Reuse empty buffers within the SSL_CTX to save memory mode |= SSL_MODE_RELEASE_BUFFERS; #if defined(OPENSSL_IS_BORINGSSL) // Enable False Start. mode |= SSL_MODE_ENABLE_FALSE_START; #endif SSL_CTX_set_mode(sslCtx.get(), mode); SSL_CTX_set_cert_verify_callback(sslCtx.get(), cert_verify_callback, nullptr); SSL_CTX_set_info_callback(sslCtx.get(), info_callback); SSL_CTX_set_client_cert_cb(sslCtx.get(), client_cert_cb); SSL_CTX_set_tmp_rsa_callback(sslCtx.get(), tmp_rsa_callback); SSL_CTX_set_tmp_dh_callback(sslCtx.get(), tmp_dh_callback); // If negotiating ECDH, use P-256. Unique_EC_KEY ec(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); if (ec.get() == nullptr) { throwExceptionIfNecessary(env, "EC_KEY_new_by_curve_name"); return 0; } SSL_CTX_set_tmp_ecdh(sslCtx.get(), ec.get()); JNI_TRACE("NativeCrypto_SSL_CTX_new => %p", sslCtx.get()); return (jlong) sslCtx.release(); } /** * public static native void SSL_CTX_free(long ssl_ctx) */ static void NativeCrypto_SSL_CTX_free(JNIEnv* env, jclass, jlong ssl_ctx_address) { SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true); JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_free", ssl_ctx); if (ssl_ctx == nullptr) { return; } SSL_CTX_free(ssl_ctx); } static void NativeCrypto_SSL_CTX_set_session_id_context(JNIEnv* env, jclass, jlong ssl_ctx_address, jbyteArray sid_ctx) { SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true); JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_session_id_context sid_ctx=%p", ssl_ctx, sid_ctx); if (ssl_ctx == nullptr) { return; } ScopedByteArrayRO buf(env, sid_ctx); if (buf.get() == nullptr) { JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_session_id_context => threw exception", ssl_ctx); return; } unsigned int length = buf.size(); if (length > SSL_MAX_SSL_SESSION_ID_LENGTH) { jniThrowException(env, "java/lang/IllegalArgumentException", "length > SSL_MAX_SSL_SESSION_ID_LENGTH"); JNI_TRACE("NativeCrypto_SSL_CTX_set_session_id_context => length = %d", length); return; } const unsigned char* bytes = reinterpret_cast<const unsigned char*>(buf.get()); int result = SSL_CTX_set_session_id_context(ssl_ctx, bytes, length); if (result == 0) { throwExceptionIfNecessary(env, "NativeCrypto_SSL_CTX_set_session_id_context"); return; } JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_session_id_context => ok", ssl_ctx); } /** * public static native int SSL_new(long ssl_ctx) throws SSLException; */ static jlong NativeCrypto_SSL_new(JNIEnv* env, jclass, jlong ssl_ctx_address) { SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true); JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new", ssl_ctx); if (ssl_ctx == nullptr) { return 0; } Unique_SSL ssl(SSL_new(ssl_ctx)); if (ssl.get() == nullptr) { throwSSLExceptionWithSslErrors(env, nullptr, SSL_ERROR_NONE, "Unable to create SSL structure"); JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => NULL", ssl_ctx); return 0; } /* * Create our special application data. */ AppData* appData = AppData::create(); if (appData == nullptr) { throwSSLExceptionStr(env, "Unable to create application data"); freeOpenSslErrorState(); JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new appData => 0", ssl_ctx); return 0; } SSL_set_app_data(ssl.get(), reinterpret_cast<char*>(appData)); /* * Java code in class OpenSSLSocketImpl does the verification. Since * the callbacks do all the verification of the chain, this flag * simply controls whether to send protocol-level alerts or not. * SSL_VERIFY_NONE means don't send alerts and anything else means send * alerts. */ SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, nullptr); JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => ssl=%p appData=%p", ssl_ctx, ssl.get(), appData); return (jlong) ssl.release(); } static void NativeCrypto_SSL_enable_tls_channel_id(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_NativeCrypto_SSL_enable_tls_channel_id", ssl); if (ssl == nullptr) { return; } long ret = SSL_enable_tls_channel_id(ssl); if (ret != 1L) { ALOGE("%s", ERR_error_string(ERR_peek_error(), nullptr)); throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error enabling Channel ID"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_enable_tls_channel_id => error", ssl); return; } } static jbyteArray NativeCrypto_SSL_get_tls_channel_id(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_NativeCrypto_SSL_get_tls_channel_id", ssl); if (ssl == nullptr) { return nullptr; } // Channel ID is 64 bytes long. Unfortunately, OpenSSL doesn't declare this length // as a constant anywhere. jbyteArray javaBytes = env->NewByteArray(64); ScopedByteArrayRW bytes(env, javaBytes); if (bytes.get() == nullptr) { JNI_TRACE("NativeCrypto_SSL_get_tls_channel_id(%p) => NULL", ssl); return nullptr; } unsigned char* tmp = reinterpret_cast<unsigned char*>(bytes.get()); // Unfortunately, the SSL_get_tls_channel_id method below always returns 64 (upon success) // regardless of the number of bytes copied into the output buffer "tmp". Thus, the correctness // of this code currently relies on the "tmp" buffer being exactly 64 bytes long. long ret = SSL_get_tls_channel_id(ssl, tmp, 64); if (ret == 0) { // Channel ID either not set or did not verify JNI_TRACE("NativeCrypto_SSL_get_tls_channel_id(%p) => not available", ssl); return nullptr; } else if (ret != 64) { ALOGE("%s", ERR_error_string(ERR_peek_error(), nullptr)); throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error getting Channel ID"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_tls_channel_id => error, returned %ld", ssl, ret); return nullptr; } JNI_TRACE("ssl=%p NativeCrypto_NativeCrypto_SSL_get_tls_channel_id() => %p", ssl, javaBytes); return javaBytes; } static void NativeCrypto_SSL_set1_tls_channel_id(JNIEnv* env, jclass, jlong ssl_address, jobject pkeyRef) { SSL* ssl = to_SSL(env, ssl_address, true); EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef); JNI_TRACE("ssl=%p SSL_set1_tls_channel_id privatekey=%p", ssl, pkey); if (ssl == nullptr) { return; } if (pkey == nullptr) { JNI_TRACE("ssl=%p SSL_set1_tls_channel_id => pkey == null", ssl); return; } #if !defined(OPENSSL_IS_BORINGSSL) // SSL_set1_tls_channel_id requires ssl->server to be set to 0. // Unfortunately, the default value is 1 and it's only changed to 0 just // before the handshake starts (see NativeCrypto_SSL_do_handshake). ssl->server = 0; #endif long ret = SSL_set1_tls_channel_id(ssl, pkey); if (ret != 1L) { ALOGE("%s", ERR_error_string(ERR_peek_error(), nullptr)); throwSSLExceptionWithSslErrors( env, ssl, SSL_ERROR_NONE, "Error setting private key for Channel ID"); safeSslClear(ssl); JNI_TRACE("ssl=%p SSL_set1_tls_channel_id => error", ssl); return; } // SSL_set1_tls_channel_id expects to take ownership of the EVP_PKEY, but // we have an external reference from the caller such as an OpenSSLKey, // so we manually increment the reference count here. EVP_PKEY_up_ref(pkey); JNI_TRACE("ssl=%p SSL_set1_tls_channel_id => ok", ssl); } static void NativeCrypto_SSL_use_PrivateKey(JNIEnv* env, jclass, jlong ssl_address, jobject pkeyRef) { SSL* ssl = to_SSL(env, ssl_address, true); EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef); JNI_TRACE("ssl=%p SSL_use_PrivateKey privatekey=%p", ssl, pkey); if (ssl == nullptr) { return; } if (pkey == nullptr) { JNI_TRACE("ssl=%p SSL_use_PrivateKey => pkey == null", ssl); return; } int ret = SSL_use_PrivateKey(ssl, pkey); if (ret != 1) { ALOGE("%s", ERR_error_string(ERR_peek_error(), nullptr)); throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error setting private key"); safeSslClear(ssl); JNI_TRACE("ssl=%p SSL_use_PrivateKey => error", ssl); return; } // SSL_use_PrivateKey expects to take ownership of the EVP_PKEY, // but we have an external reference from the caller such as an // OpenSSLKey, so we manually increment the reference count here. EVP_PKEY_up_ref(pkey); JNI_TRACE("ssl=%p SSL_use_PrivateKey => ok", ssl); } static void NativeCrypto_SSL_use_certificate(JNIEnv* env, jclass, jlong ssl_address, jlongArray certificatesJava) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate certificates=%p", ssl, certificatesJava); if (ssl == nullptr) { return; } if (certificatesJava == nullptr) { jniThrowNullPointerException(env, "certificates == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => certificates == null", ssl); return; } size_t length = env->GetArrayLength(certificatesJava); if (length == 0) { jniThrowException(env, "java/lang/IllegalArgumentException", "certificates.length == 0"); JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => certificates.length == 0", ssl); return; } ScopedLongArrayRO certificates(env, certificatesJava); if (certificates.get() == nullptr) { JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => certificates == null", ssl); return; } Unique_X509 serverCert( X509_dup_nocopy(reinterpret_cast<X509*>(static_cast<uintptr_t>(certificates[0])))); if (serverCert.get() == nullptr) { // Note this shouldn't happen since we checked the number of certificates above. jniThrowOutOfMemory(env, "Unable to allocate local certificate chain"); JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => chain allocation error", ssl); return; } int ret = SSL_use_certificate(ssl, serverCert.get()); if (ret != 1) { ALOGE("%s", ERR_error_string(ERR_peek_error(), nullptr)); throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error setting certificate"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => SSL_use_certificate error", ssl); return; } OWNERSHIP_TRANSFERRED(serverCert); #if !defined(OPENSSL_IS_BORINGSSL) Unique_sk_X509 chain(sk_X509_new_null()); if (chain.get() == NULL) { jniThrowOutOfMemory(env, "Unable to allocate local certificate chain"); JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => chain allocation error", ssl); return; } for (size_t i = 1; i < length; i++) { Unique_X509 cert( X509_dup_nocopy(reinterpret_cast<X509*>(static_cast<uintptr_t>(certificates[i])))); if (cert.get() == NULL || !sk_X509_push(chain.get(), cert.get())) { ALOGE("%s", ERR_error_string(ERR_peek_error(), NULL)); throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error parsing certificate"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => certificates parsing error", ssl); return; } OWNERSHIP_TRANSFERRED(cert); } int chainResult = SSL_use_certificate_chain(ssl, chain.get()); if (chainResult == 0) { throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error setting certificate chain"); JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => SSL_use_certificate_chain error", ssl); return; } OWNERSHIP_TRANSFERRED(chain); #else for (size_t i = 1; i < length; i++) { Unique_X509 cert( X509_dup_nocopy(reinterpret_cast<X509*>(static_cast<uintptr_t>(certificates[i])))); if (cert.get() == nullptr || !SSL_add0_chain_cert(ssl, cert.get())) { ALOGE("%s", ERR_error_string(ERR_peek_error(), nullptr)); throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error parsing certificate"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => certificates parsing error", ssl); return; } OWNERSHIP_TRANSFERRED(cert); } #endif JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => ok", ssl); } static void NativeCrypto_SSL_check_private_key(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_check_private_key", ssl); if (ssl == nullptr) { return; } int ret = SSL_check_private_key(ssl); if (ret != 1) { throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error checking private key"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_check_private_key => error", ssl); return; } JNI_TRACE("ssl=%p NativeCrypto_SSL_check_private_key => ok", ssl); } static void NativeCrypto_SSL_set_client_CA_list(JNIEnv* env, jclass, jlong ssl_address, jobjectArray principals) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_client_CA_list principals=%p", ssl, principals); if (ssl == nullptr) { return; } if (principals == nullptr) { jniThrowNullPointerException(env, "principals == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_client_CA_list => principals == null", ssl); return; } int length = env->GetArrayLength(principals); if (length == 0) { jniThrowException(env, "java/lang/IllegalArgumentException", "principals.length == 0"); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_client_CA_list => principals.length == 0", ssl); return; } Unique_sk_X509_NAME principalsStack(sk_X509_NAME_new_null()); if (principalsStack.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate principal stack"); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_client_CA_list => stack allocation error", ssl); return; } for (int i = 0; i < length; i++) { ScopedLocalRef<jbyteArray> principal(env, reinterpret_cast<jbyteArray>(env->GetObjectArrayElement(principals, i))); if (principal.get() == nullptr) { jniThrowNullPointerException(env, "principals element == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_client_CA_list => principals element null", ssl); return; } ScopedByteArrayRO buf(env, principal.get()); if (buf.get() == nullptr) { JNI_TRACE("ssl=%p NativeCrypto_SSL_set_client_CA_list => threw exception", ssl); return; } const unsigned char* tmp = reinterpret_cast<const unsigned char*>(buf.get()); Unique_X509_NAME principalX509Name(d2i_X509_NAME(nullptr, &tmp, buf.size())); if (principalX509Name.get() == nullptr) { ALOGE("%s", ERR_error_string(ERR_peek_error(), nullptr)); throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error parsing principal"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_client_CA_list => principals parsing error", ssl); return; } if (!sk_X509_NAME_push(principalsStack.get(), principalX509Name.release())) { jniThrowOutOfMemory(env, "Unable to push principal"); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_client_CA_list => principal push error", ssl); return; } } SSL_set_client_CA_list(ssl, principalsStack.release()); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_client_CA_list => ok", ssl); } /** * public static native long SSL_get_mode(long ssl); */ static jlong NativeCrypto_SSL_get_mode(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_mode", ssl); if (ssl == nullptr) { return 0; } long mode = SSL_get_mode(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_mode => 0x%lx", ssl, mode); return mode; } /** * public static native long SSL_set_mode(long ssl, long mode); */ static jlong NativeCrypto_SSL_set_mode(JNIEnv* env, jclass, jlong ssl_address, jlong mode) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_mode mode=0x%llx", ssl, (long long) mode); if (ssl == nullptr) { return 0; } long result = SSL_set_mode(ssl, mode); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_mode => 0x%lx", ssl, result); return result; } /** * public static native long SSL_clear_mode(long ssl, long mode); */ static jlong NativeCrypto_SSL_clear_mode(JNIEnv* env, jclass, jlong ssl_address, jlong mode) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_mode mode=0x%llx", ssl, (long long) mode); if (ssl == nullptr) { return 0; } long result = SSL_clear_mode(ssl, mode); JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_mode => 0x%lx", ssl, result); return result; } /** * public static native long SSL_get_options(long ssl); */ static jlong NativeCrypto_SSL_get_options(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_options", ssl); if (ssl == nullptr) { return 0; } long options = SSL_get_options(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_options => 0x%lx", ssl, options); return options; } /** * public static native long SSL_set_options(long ssl, long options); */ static jlong NativeCrypto_SSL_set_options(JNIEnv* env, jclass, jlong ssl_address, jlong options) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_options options=0x%llx", ssl, (long long) options); if (ssl == nullptr) { return 0; } long result = SSL_set_options(ssl, options); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_options => 0x%lx", ssl, result); return result; } /** * public static native long SSL_clear_options(long ssl, long options); */ static jlong NativeCrypto_SSL_clear_options(JNIEnv* env, jclass, jlong ssl_address, jlong options) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_options options=0x%llx", ssl, (long long) options); if (ssl == nullptr) { return 0; } long result = SSL_clear_options(ssl, options); JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_options => 0x%lx", ssl, result); return result; } /** * public static native void SSL_enable_signed_cert_timestamps(long ssl); */ static void NativeCrypto_SSL_enable_signed_cert_timestamps(JNIEnv *env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_enable_signed_cert_timestamps", ssl); if (ssl == nullptr) { return; } #if defined(OPENSSL_IS_BORINGSSL) SSL_enable_signed_cert_timestamps(ssl); #endif } /** * public static native byte[] SSL_get_signed_cert_timestamp_list(long ssl); */ static jbyteArray NativeCrypto_SSL_get_signed_cert_timestamp_list(JNIEnv *env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_signed_cert_timestamp_list", ssl); if (ssl == nullptr) { return nullptr; } #if defined(OPENSSL_IS_BORINGSSL) const uint8_t *data; size_t data_len; SSL_get0_signed_cert_timestamp_list(ssl, &data, &data_len); if (data_len == 0) { JNI_TRACE("NativeCrypto_SSL_get_signed_cert_timestamp_list(%p) => NULL", ssl); return nullptr; } jbyteArray result = env->NewByteArray(data_len); if (result != nullptr) { env->SetByteArrayRegion(result, 0, data_len, (const jbyte*)data); } return result; #else return NULL; #endif } /* * public static native void SSL_CTX_set_signed_cert_timestamp_list(long ssl, byte[] response); */ static void NativeCrypto_SSL_CTX_set_signed_cert_timestamp_list(JNIEnv *env, jclass, jlong ssl_ctx_address, jbyteArray list) { SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true); JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_signed_cert_timestamp_list", ssl_ctx); if (ssl_ctx == nullptr) { return; } ScopedByteArrayRO listBytes(env, list); if (listBytes.get() == nullptr) { JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_signed_cert_timestamp_list =>" " list == NULL", ssl_ctx); return; } #if defined(OPENSSL_IS_BORINGSSL) if (!SSL_CTX_set_signed_cert_timestamp_list(ssl_ctx, reinterpret_cast<const uint8_t *>(listBytes.get()), listBytes.size())) { JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_signed_cert_timestamp_list => fail", ssl_ctx); } else { JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_signed_cert_timestamp_list => ok", ssl_ctx); } #endif } /* * public static native void SSL_enable_ocsp_stapling(long ssl); */ static void NativeCrypto_SSL_enable_ocsp_stapling(JNIEnv *env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_enable_ocsp_stapling", ssl); if (ssl == nullptr) { return; } #if defined(OPENSSL_IS_BORINGSSL) SSL_enable_ocsp_stapling(ssl); #endif } /* * public static native byte[] SSL_get_ocsp_response(long ssl); */ static jbyteArray NativeCrypto_SSL_get_ocsp_response(JNIEnv *env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_ocsp_response", ssl); if (ssl == nullptr) { return nullptr; } #if defined(OPENSSL_IS_BORINGSSL) const uint8_t *data; size_t data_len; SSL_get0_ocsp_response(ssl, &data, &data_len); if (data_len == 0) { JNI_TRACE("NativeCrypto_SSL_get_ocsp_response(%p) => NULL", ssl); return nullptr; } ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(data_len)); if (byteArray.get() == nullptr) { JNI_TRACE("NativeCrypto_SSL_get_ocsp_response(%p) => creating byte array failed", ssl); return nullptr; } env->SetByteArrayRegion(byteArray.get(), 0, data_len, (const jbyte*)data); JNI_TRACE("NativeCrypto_SSL_get_ocsp_response(%p) => %p [size=%zd]", ssl, byteArray.get(), data_len); return byteArray.release(); #else return NULL; #endif } /* * public static native void SSL_CTX_set_ocsp_response(long ssl, byte[] response); */ static void NativeCrypto_SSL_CTX_set_ocsp_response(JNIEnv *env, jclass, jlong ssl_ctx_address, jbyteArray response) { SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true); JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_ocsp_response", ssl_ctx); if (ssl_ctx == nullptr) { return; } ScopedByteArrayRO responseBytes(env, response); if (responseBytes.get() == nullptr) { JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_ocsp_response => response == NULL", ssl_ctx); return; } #if defined(OPENSSL_IS_BORINGSSL) if (!SSL_CTX_set_ocsp_response(ssl_ctx, reinterpret_cast<const uint8_t *>(responseBytes.get()), responseBytes.size())) { JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_ocsp_response => fail", ssl_ctx); } else { JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_ocsp_response => ok", ssl_ctx); } #endif } static void NativeCrypto_SSL_use_psk_identity_hint(JNIEnv* env, jclass, jlong ssl_address, jstring identityHintJava) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_use_psk_identity_hint identityHint=%p", ssl, identityHintJava); if (ssl == nullptr) { return; } int ret; if (identityHintJava == nullptr) { ret = SSL_use_psk_identity_hint(ssl, nullptr); } else { ScopedUtfChars identityHint(env, identityHintJava); if (identityHint.c_str() == nullptr) { throwSSLExceptionStr(env, "Failed to obtain identityHint bytes"); return; } ret = SSL_use_psk_identity_hint(ssl, identityHint.c_str()); } if (ret != 1) { int sslErrorCode = SSL_get_error(ssl, ret); throwSSLExceptionWithSslErrors(env, ssl, sslErrorCode, "Failed to set PSK identity hint"); safeSslClear(ssl); } } static void NativeCrypto_set_SSL_psk_client_callback_enabled(JNIEnv* env, jclass, jlong ssl_address, jboolean enabled) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_set_SSL_psk_client_callback_enabled(%d)", ssl, enabled); if (ssl == nullptr) { return; } SSL_set_psk_client_callback(ssl, (enabled) ? psk_client_callback : nullptr); } static void NativeCrypto_set_SSL_psk_server_callback_enabled(JNIEnv* env, jclass, jlong ssl_address, jboolean enabled) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_set_SSL_psk_server_callback_enabled(%d)", ssl, enabled); if (ssl == nullptr) { return; } SSL_set_psk_server_callback(ssl, (enabled) ? psk_server_callback : nullptr); } static jlongArray NativeCrypto_SSL_get_ciphers(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_ciphers", ssl); STACK_OF(SSL_CIPHER)* cipherStack = SSL_get_ciphers(ssl); int count = (cipherStack != nullptr) ? sk_SSL_CIPHER_num(cipherStack) : 0; ScopedLocalRef<jlongArray> ciphersArray(env, env->NewLongArray(count)); ScopedLongArrayRW ciphers(env, ciphersArray.get()); for (int i = 0; i < count; i++) { ciphers[i] = reinterpret_cast<jlong>(sk_SSL_CIPHER_value(cipherStack, i)); } JNI_TRACE("NativeCrypto_SSL_get_ciphers(%p) => %p [size=%d]", ssl, ciphersArray.get(), count); return ciphersArray.release(); } /** * Sets the ciphers suites that are enabled in the SSL */ static void NativeCrypto_SSL_set_cipher_lists(JNIEnv* env, jclass, jlong ssl_address, jobjectArray cipherSuites) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_cipher_lists cipherSuites=%p", ssl, cipherSuites); if (ssl == nullptr) { return; } if (cipherSuites == nullptr) { jniThrowNullPointerException(env, "cipherSuites == null"); return; } int length = env->GetArrayLength(cipherSuites); /* * Special case for empty cipher list. This is considered an error by the * SSL_set_cipher_list API, but Java allows this silly configuration. * However, the SSL cipher list is still set even when SSL_set_cipher_list * returns 0 in this case. Just to make sure, we check the resulting cipher * list to make sure it's zero length. */ if (length == 0) { JNI_TRACE("ssl=%p NativeCrypto_SSL_set_cipher_lists cipherSuites=empty", ssl); SSL_set_cipher_list(ssl, ""); freeOpenSslErrorState(); if (sk_SSL_CIPHER_num(SSL_get_ciphers(ssl)) != 0) { JNI_TRACE("ssl=%p NativeCrypto_SSL_set_cipher_lists cipherSuites=empty => error", ssl); jniThrowRuntimeException(env, "SSL_set_cipher_list did not update ciphers!"); } return; } static const char noSSLv2[] = "!SSLv2"; size_t cipherStringLen = strlen(noSSLv2); for (int i = 0; i < length; i++) { ScopedLocalRef<jstring> cipherSuite(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(cipherSuites, i))); ScopedUtfChars c(env, cipherSuite.get()); if (c.c_str() == nullptr) { return; } if (cipherStringLen + 1 < cipherStringLen) { jniThrowException(env, "java/lang/IllegalArgumentException", "Overflow in cipher suite strings"); return; } cipherStringLen += 1; /* For the separating colon */ if (cipherStringLen + c.size() < cipherStringLen) { jniThrowException(env, "java/lang/IllegalArgumentException", "Overflow in cipher suite strings"); return; } cipherStringLen += c.size(); } if (cipherStringLen + 1 < cipherStringLen) { jniThrowException(env, "java/lang/IllegalArgumentException", "Overflow in cipher suite strings"); return; } cipherStringLen += 1; /* For final NUL. */ UniquePtr<char[]> cipherString(new char[cipherStringLen]); if (cipherString.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to alloc cipher string"); return; } memcpy(cipherString.get(), noSSLv2, strlen(noSSLv2)); size_t j = strlen(noSSLv2); for (int i = 0; i < length; i++) { ScopedLocalRef<jstring> cipherSuite(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(cipherSuites, i))); ScopedUtfChars c(env, cipherSuite.get()); cipherString[j++] = ':'; memcpy(&cipherString[j], c.c_str(), c.size()); j += c.size(); } cipherString[j++] = 0; if (j != cipherStringLen) { jniThrowException(env, "java/lang/IllegalArgumentException", "Internal error"); return; } JNI_TRACE("ssl=%p NativeCrypto_SSL_set_cipher_lists cipherSuites=%s", ssl, cipherString.get()); if (!SSL_set_cipher_list(ssl, cipherString.get())) { freeOpenSslErrorState(); jniThrowException(env, "java/lang/IllegalArgumentException", "Illegal cipher suite strings."); return; } } static void NativeCrypto_SSL_set_accept_state(JNIEnv* env, jclass, jlong sslRef) { SSL* ssl = to_SSL(env, sslRef, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_accept_state", ssl); if (ssl == nullptr) { return; } SSL_set_accept_state(ssl); } static void NativeCrypto_SSL_set_connect_state(JNIEnv* env, jclass, jlong sslRef) { SSL* ssl = to_SSL(env, sslRef, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_connect_state", ssl); if (ssl == nullptr) { return; } SSL_set_connect_state(ssl); } /** * Sets certificate expectations, especially for server to request client auth */ static void NativeCrypto_SSL_set_verify(JNIEnv* env, jclass, jlong ssl_address, jint mode) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_verify mode=%x", ssl, mode); if (ssl == nullptr) { return; } SSL_set_verify(ssl, (int)mode, nullptr); } /** * Sets the ciphers suites that are enabled in the SSL */ static void NativeCrypto_SSL_set_session(JNIEnv* env, jclass, jlong ssl_address, jlong ssl_session_address) { SSL* ssl = to_SSL(env, ssl_address, true); SSL_SESSION* ssl_session = to_SSL_SESSION(env, ssl_session_address, false); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_session ssl_session=%p", ssl, ssl_session); if (ssl == nullptr) { return; } int ret = SSL_set_session(ssl, ssl_session); if (ret != 1) { /* * Translate the error, and throw if it turns out to be a real * problem. */ int sslErrorCode = SSL_get_error(ssl, ret); if (sslErrorCode != SSL_ERROR_ZERO_RETURN) { throwSSLExceptionWithSslErrors(env, ssl, sslErrorCode, "SSL session set"); safeSslClear(ssl); } } } /** * Sets the ciphers suites that are enabled in the SSL */ static void NativeCrypto_SSL_set_session_creation_enabled(JNIEnv* env, jclass, jlong ssl_address, jboolean creation_enabled) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_session_creation_enabled creation_enabled=%d", ssl, creation_enabled); if (ssl == nullptr) { return; } #if !defined(OPENSSL_IS_BORINGSSL) SSL_set_session_creation_enabled(ssl, creation_enabled); #else if (creation_enabled) { SSL_clear_mode(ssl, SSL_MODE_NO_SESSION_CREATION); } else { SSL_set_mode(ssl, SSL_MODE_NO_SESSION_CREATION); } #endif } static jboolean NativeCrypto_SSL_session_reused(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_session_reused", ssl); if (ssl == nullptr) { return JNI_FALSE; } int reused = SSL_session_reused(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_session_reused => %d", ssl, reused); return reused == 1 ? JNI_TRUE : JNI_FALSE; } static void NativeCrypto_SSL_set_reject_peer_renegotiations(JNIEnv* env, jclass, jlong ssl_address, jboolean reject_renegotiations) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_reject_peer_renegotiations reject_renegotiations=%d", ssl, reject_renegotiations); if (ssl == nullptr) { return; } #if defined(OPENSSL_IS_BORINGSSL) SSL_set_reject_peer_renegotiations(ssl, reject_renegotiations); #else (void) reject_renegotiations; /* OpenSSL doesn't support this call and accepts renegotiation requests by * default. */ #endif } static void NativeCrypto_SSL_set_tlsext_host_name(JNIEnv* env, jclass, jlong ssl_address, jstring hostname) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_tlsext_host_name hostname=%p", ssl, hostname); if (ssl == nullptr) { return; } ScopedUtfChars hostnameChars(env, hostname); if (hostnameChars.c_str() == nullptr) { return; } JNI_TRACE("ssl=%p NativeCrypto_SSL_set_tlsext_host_name hostnameChars=%s", ssl, hostnameChars.c_str()); int ret = SSL_set_tlsext_host_name(ssl, hostnameChars.c_str()); if (ret != 1) { throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error setting host name"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_set_tlsext_host_name => error", ssl); return; } JNI_TRACE("ssl=%p NativeCrypto_SSL_set_tlsext_host_name => ok", ssl); } static jstring NativeCrypto_SSL_get_servername(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_servername", ssl); if (ssl == nullptr) { return nullptr; } const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_servername => %s", ssl, servername); return env->NewStringUTF(servername); } /** * A common selection path for both NPN and ALPN since they're essentially the * same protocol. The list of protocols in "primary" is considered the order * which should take precedence. */ static int proto_select(SSL* ssl __attribute__ ((unused)), unsigned char **out, unsigned char *outLength, const unsigned char *primary, const unsigned int primaryLength, const unsigned char *secondary, const unsigned int secondaryLength) { if (primary != nullptr && secondary != nullptr) { JNI_TRACE("primary=%p, length=%d", primary, primaryLength); int status = SSL_select_next_proto(out, outLength, primary, primaryLength, secondary, secondaryLength); switch (status) { case OPENSSL_NPN_NEGOTIATED: JNI_TRACE("ssl=%p proto_select NPN/ALPN negotiated", ssl); return SSL_TLSEXT_ERR_OK; break; case OPENSSL_NPN_UNSUPPORTED: JNI_TRACE("ssl=%p proto_select NPN/ALPN unsupported", ssl); break; case OPENSSL_NPN_NO_OVERLAP: JNI_TRACE("ssl=%p proto_select NPN/ALPN no overlap", ssl); break; } } else { if (out != nullptr && outLength != nullptr) { *out = nullptr; *outLength = 0; } JNI_TRACE("protocols=NULL"); } return SSL_TLSEXT_ERR_NOACK; } /** * Callback for the server to select an ALPN protocol. */ static int alpn_select_callback(SSL* ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *) { JNI_TRACE("ssl=%p alpn_select_callback", ssl); AppData* appData = toAppData(ssl); JNI_TRACE("AppData=%p", appData); return proto_select(ssl, const_cast<unsigned char **>(out), outlen, reinterpret_cast<unsigned char*>(appData->alpnProtocolsData), appData->alpnProtocolsLength, in, inlen); } /** * Callback for the client to select an NPN protocol. */ static int next_proto_select_callback(SSL* ssl, unsigned char** out, unsigned char* outlen, const unsigned char* in, unsigned int inlen, void*) { JNI_TRACE("ssl=%p next_proto_select_callback", ssl); AppData* appData = toAppData(ssl); JNI_TRACE("AppData=%p", appData); #if !defined(OPENSSL_IS_BORINGSSL) // Enable False Start on the client if the server understands NPN. Unlike BoringSSL, // OpenSSL doesn't implement this check internally. // http://www.imperialviolet.org/2012/04/11/falsestart.html SSL_set_mode(ssl, SSL_MODE_HANDSHAKE_CUTTHROUGH); #endif return proto_select(ssl, out, outlen, in, inlen, reinterpret_cast<unsigned char*>(appData->npnProtocolsData), appData->npnProtocolsLength); } /** * Callback for the server to advertise available protocols. */ static int next_protos_advertised_callback(SSL* ssl, const unsigned char **out, unsigned int *outlen, void *) { JNI_TRACE("ssl=%p next_protos_advertised_callback", ssl); AppData* appData = toAppData(ssl); unsigned char* npnProtocols = reinterpret_cast<unsigned char*>(appData->npnProtocolsData); if (npnProtocols != nullptr) { *out = npnProtocols; *outlen = appData->npnProtocolsLength; return SSL_TLSEXT_ERR_OK; } else { *out = nullptr; *outlen = 0; return SSL_TLSEXT_ERR_NOACK; } } static void NativeCrypto_SSL_CTX_enable_npn(JNIEnv* env, jclass, jlong ssl_ctx_address) { SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true); if (ssl_ctx == nullptr) { return; } SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_callback, nullptr); // client SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_protos_advertised_callback, nullptr); // server } static void NativeCrypto_SSL_CTX_disable_npn(JNIEnv* env, jclass, jlong ssl_ctx_address) { SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true); if (ssl_ctx == nullptr) { return; } SSL_CTX_set_next_proto_select_cb(ssl_ctx, nullptr, nullptr); // client SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, nullptr, nullptr); // server } static jbyteArray NativeCrypto_SSL_get_npn_negotiated_protocol(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_npn_negotiated_protocol", ssl); if (ssl == nullptr) { return nullptr; } const jbyte* npn; unsigned npnLength; SSL_get0_next_proto_negotiated(ssl, reinterpret_cast<const unsigned char**>(&npn), &npnLength); if (npnLength == 0) { return nullptr; } jbyteArray result = env->NewByteArray(npnLength); if (result != nullptr) { env->SetByteArrayRegion(result, 0, npnLength, npn); } return result; } static int NativeCrypto_SSL_set_alpn_protos(JNIEnv* env, jclass, jlong ssl_address, jbyteArray protos) { SSL* ssl = to_SSL(env, ssl_address, true); if (ssl == nullptr) { return 0; } JNI_TRACE("ssl=%p SSL_set_alpn_protos protos=%p", ssl, protos); if (protos == nullptr) { JNI_TRACE("ssl=%p SSL_set_alpn_protos protos=NULL", ssl); return 1; } ScopedByteArrayRO protosBytes(env, protos); if (protosBytes.get() == nullptr) { JNI_TRACE("ssl=%p SSL_set_alpn_protos protos=%p => protosBytes == NULL", ssl, protos); return 0; } const unsigned char *tmp = reinterpret_cast<const unsigned char*>(protosBytes.get()); int ret = SSL_set_alpn_protos(ssl, tmp, protosBytes.size()); JNI_TRACE("ssl=%p SSL_set_alpn_protos protos=%p => ret=%d", ssl, protos, ret); return ret; } static jbyteArray NativeCrypto_SSL_get0_alpn_selected(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p SSL_get0_alpn_selected", ssl); if (ssl == nullptr) { return nullptr; } const jbyte* npn; unsigned npnLength; SSL_get0_alpn_selected(ssl, reinterpret_cast<const unsigned char**>(&npn), &npnLength); if (npnLength == 0) { return nullptr; } jbyteArray result = env->NewByteArray(npnLength); if (result != nullptr) { env->SetByteArrayRegion(result, 0, npnLength, npn); } return result; } #ifdef WITH_JNI_TRACE_KEYS static inline char hex_char(unsigned char in) { if (in < 10) { return '0' + in; } else if (in <= 0xF0) { return 'A' + in - 10; } else { return '?'; } } static void hex_string(char **dest, unsigned char* input, int len) { *dest = (char*) malloc(len * 2 + 1); char *output = *dest; for (int i = 0; i < len; i++) { *output++ = hex_char(input[i] >> 4); *output++ = hex_char(input[i] & 0xF); } *output = '\0'; } static void debug_print_session_key(SSL_SESSION* session) { char *session_id_str; char *master_key_str; const char *key_type; char *keyline; hex_string(&session_id_str, session->session_id, session->session_id_length); hex_string(&master_key_str, session->master_key, session->master_key_length); X509* peer = SSL_SESSION_get0_peer(session); EVP_PKEY* pkey = X509_PUBKEY_get(peer->cert_info->key); switch (EVP_PKEY_type(pkey->type)) { case EVP_PKEY_RSA: key_type = "RSA"; break; case EVP_PKEY_DSA: key_type = "DSA"; break; case EVP_PKEY_EC: key_type = "EC"; break; default: key_type = "Unknown"; break; } asprintf(&keyline, "%s Session-ID:%s Master-Key:%s\n", key_type, session_id_str, master_key_str); JNI_TRACE("ssl_session=%p %s", session, keyline); free(session_id_str); free(master_key_str); free(keyline); } #endif /* WITH_JNI_TRACE_KEYS */ /** * Perform SSL handshake */ static jlong NativeCrypto_SSL_do_handshake_bio(JNIEnv* env, jclass, jlong ssl_address, jlong rbioRef, jlong wbioRef, jobject shc, jboolean client_mode, jbyteArray npnProtocols, jbyteArray alpnProtocols) { SSL* ssl = to_SSL(env, ssl_address, true); BIO* rbio = reinterpret_cast<BIO*>(rbioRef); BIO* wbio = reinterpret_cast<BIO*>(wbioRef); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio rbio=%p wbio=%p shc=%p client_mode=%d npn=%p", ssl, rbio, wbio, shc, client_mode, npnProtocols); if (ssl == nullptr) { return 0; } if (shc == nullptr) { jniThrowNullPointerException(env, "sslHandshakeCallbacks == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio sslHandshakeCallbacks == null => 0", ssl); return 0; } if (rbio == nullptr || wbio == nullptr) { jniThrowNullPointerException(env, "rbio == null || wbio == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio => rbio == null || wbio == NULL", ssl); return 0; } AppData* appData = toAppData(ssl); if (appData == nullptr) { throwSSLExceptionStr(env, "Unable to retrieve application data"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake appData => 0", ssl); return 0; } UniqueMutex appDataLock(&appData->mutex); if (!client_mode && alpnProtocols != nullptr) { SSL_CTX_set_alpn_select_cb(SSL_get_SSL_CTX(ssl), alpn_select_callback, nullptr); } int ret = 0; errno = 0; if (!appData->setCallbackState(env, shc, nullptr, npnProtocols, alpnProtocols)) { freeOpenSslErrorState(); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio setCallbackState => 0", ssl); return 0; } ScopedSslBio sslBio(ssl, rbio, wbio); ret = SSL_do_handshake(ssl); appData->clearCallbackState(); // cert_verify_callback threw exception if (env->ExceptionCheck()) { freeOpenSslErrorState(); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio exception => 0", ssl); return 0; } if (ret <= 0) { // error. See SSL_do_handshake(3SSL) man page. // error case OpenSslError sslError(ssl, ret); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio ret=%d errno=%d sslError=%d", ssl, ret, errno, sslError.get()); /* * If SSL_do_handshake doesn't succeed due to the socket being * either unreadable or unwritable, we need to exit to allow * the SSLEngine code to wrap or unwrap. */ if (sslError.get() == SSL_ERROR_NONE || (sslError.get() == SSL_ERROR_SYSCALL && errno == 0) || (sslError.get() == SSL_ERROR_ZERO_RETURN)) { throwSSLHandshakeExceptionStr(env, "Connection closed by peer"); safeSslClear(ssl); } else if (sslError.get() != SSL_ERROR_WANT_READ && sslError.get() != SSL_ERROR_WANT_WRITE) { throwSSLExceptionWithSslErrors(env, ssl, sslError.release(), "SSL handshake terminated", throwSSLHandshakeExceptionStr); safeSslClear(ssl); } JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio error => 0", ssl); return 0; } // success. handshake completed SSL_SESSION* ssl_session = SSL_get1_session(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio => ssl_session=%p", ssl, ssl_session); #ifdef WITH_JNI_TRACE_KEYS debug_print_session_key(ssl_session); #endif return reinterpret_cast<uintptr_t>(ssl_session); } /** * Perform SSL handshake */ static jlong NativeCrypto_SSL_do_handshake(JNIEnv* env, jclass, jlong ssl_address, jobject fdObject, jobject shc, jint timeout_millis, jboolean client_mode, jbyteArray npnProtocols, jbyteArray alpnProtocols) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake fd=%p shc=%p timeout_millis=%d client_mode=%d npn=%p", ssl, fdObject, shc, timeout_millis, client_mode, npnProtocols); if (ssl == nullptr) { return 0; } if (fdObject == nullptr) { jniThrowNullPointerException(env, "fd == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake fd == null => 0", ssl); return 0; } if (shc == nullptr) { jniThrowNullPointerException(env, "sslHandshakeCallbacks == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake sslHandshakeCallbacks == null => 0", ssl); return 0; } NetFd fd(env, fdObject); if (fd.isClosed()) { // SocketException thrown by NetFd.isClosed safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake fd.isClosed() => 0", ssl); return 0; } int ret = SSL_set_fd(ssl, fd.get()); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake s=%d", ssl, fd.get()); if (ret != 1) { throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error setting the file descriptor"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake SSL_set_fd => 0", ssl); return 0; } /* * Make socket non-blocking, so SSL_connect SSL_read() and SSL_write() don't hang * forever and we can use select() to find out if the socket is ready. */ if (!setBlocking(fd.get(), false)) { throwSSLExceptionStr(env, "Unable to make socket non blocking"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake setBlocking => 0", ssl); return 0; } AppData* appData = toAppData(ssl); if (appData == nullptr) { throwSSLExceptionStr(env, "Unable to retrieve application data"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake appData => 0", ssl); return 0; } if (client_mode) { SSL_set_connect_state(ssl); } else { SSL_set_accept_state(ssl); if (alpnProtocols != nullptr) { SSL_CTX_set_alpn_select_cb(SSL_get_SSL_CTX(ssl), alpn_select_callback, nullptr); } } ret = 0; OpenSslError sslError; while (appData->aliveAndKicking) { errno = 0; if (!appData->setCallbackState(env, shc, fdObject, npnProtocols, alpnProtocols)) { // SocketException thrown by NetFd.isClosed safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake setCallbackState => 0", ssl); return 0; } ret = SSL_do_handshake(ssl); appData->clearCallbackState(); // cert_verify_callback threw exception if (env->ExceptionCheck()) { freeOpenSslErrorState(); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake exception => 0", ssl); return 0; } // success case if (ret == 1) { break; } // retry case if (errno == EINTR) { continue; } // error case sslError.reset(ssl, ret); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake ret=%d errno=%d sslError=%d timeout_millis=%d", ssl, ret, errno, sslError.get(), timeout_millis); /* * If SSL_do_handshake doesn't succeed due to the socket being * either unreadable or unwritable, we use sslSelect to * wait for it to become ready. If that doesn't happen * before the specified timeout or an error occurs, we * cancel the handshake. Otherwise we try the SSL_connect * again. */ if (sslError.get() == SSL_ERROR_WANT_READ || sslError.get() == SSL_ERROR_WANT_WRITE) { appData->waitingThreads++; int selectResult = sslSelect(env, sslError.get(), fdObject, appData, timeout_millis); if (selectResult == THROWN_EXCEPTION) { // SocketException thrown by NetFd.isClosed safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake sslSelect => 0", ssl); return 0; } if (selectResult == -1) { throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_SYSCALL, "handshake error", throwSSLHandshakeExceptionStr); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake selectResult == -1 => 0", ssl); return 0; } if (selectResult == 0) { throwSocketTimeoutException(env, "SSL handshake timed out"); freeOpenSslErrorState(); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake selectResult == 0 => 0", ssl); return 0; } } else { // ALOGE("Unknown error %d during handshake", error); break; } } // clean error. See SSL_do_handshake(3SSL) man page. if (ret == 0) { /* * The other side closed the socket before the handshake could be * completed, but everything is within the bounds of the TLS protocol. * We still might want to find out the real reason of the failure. */ if (sslError.get() == SSL_ERROR_NONE || (sslError.get() == SSL_ERROR_SYSCALL && errno == 0) || (sslError.get() == SSL_ERROR_ZERO_RETURN)) { throwSSLHandshakeExceptionStr(env, "Connection closed by peer"); } else { throwSSLExceptionWithSslErrors(env, ssl, sslError.release(), "SSL handshake terminated", throwSSLHandshakeExceptionStr); } safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake clean error => 0", ssl); return 0; } // unclean error. See SSL_do_handshake(3SSL) man page. if (ret < 0) { /* * Translate the error and throw exception. We are sure it is an error * at this point. */ throwSSLExceptionWithSslErrors(env, ssl, sslError.release(), "SSL handshake aborted", throwSSLHandshakeExceptionStr); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake unclean error => 0", ssl); return 0; } SSL_SESSION* ssl_session = SSL_get1_session(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => ssl_session=%p", ssl, ssl_session); #ifdef WITH_JNI_TRACE_KEYS debug_print_session_key(ssl_session); #endif return (jlong) ssl_session; } /** * Perform SSL renegotiation */ static void NativeCrypto_SSL_renegotiate(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_renegotiate", ssl); if (ssl == nullptr) { return; } int result = SSL_renegotiate(ssl); if (result != 1) { throwSSLExceptionStr(env, "Problem with SSL_renegotiate"); return; } // first call asks client to perform renegotiation int ret = SSL_do_handshake(ssl); if (ret != 1) { OpenSslError sslError(ssl, ret); throwSSLExceptionWithSslErrors(env, ssl, sslError.release(), "Problem with SSL_do_handshake after SSL_renegotiate"); return; } // if client agrees, set ssl state and perform renegotiation SSL_set_state(ssl, SSL_ST_ACCEPT); SSL_do_handshake(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_renegotiate =>", ssl); } /** * public static native byte[][] SSL_get_certificate(long ssl); */ static jlongArray NativeCrypto_SSL_get_certificate(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate", ssl); if (ssl == nullptr) { return nullptr; } X509* certificate = SSL_get_certificate(ssl); if (certificate == nullptr) { JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => NULL", ssl); // SSL_get_certificate can return NULL during an error as well. freeOpenSslErrorState(); return nullptr; } Unique_sk_X509 chain(sk_X509_new_null()); if (chain.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate local certificate chain"); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => threw exception", ssl); return nullptr; } if (!sk_X509_push(chain.get(), X509_dup_nocopy(certificate))) { jniThrowOutOfMemory(env, "Unable to push local certificate"); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => NULL", ssl); return nullptr; } #if !defined(OPENSSL_IS_BORINGSSL) STACK_OF(X509)* cert_chain = SSL_get_certificate_chain(ssl, certificate); for (int i=0; i<sk_X509_num(cert_chain); i++) { if (!sk_X509_push(chain.get(), X509_dup_nocopy(sk_X509_value(cert_chain, i)))) { jniThrowOutOfMemory(env, "Unable to push local certificate chain"); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => NULL", ssl); return NULL; } } #else STACK_OF(X509)* cert_chain = nullptr; if (!SSL_get0_chain_certs(ssl, &cert_chain)) { JNI_TRACE("ssl=%p NativeCrypto_SSL_get0_chain_certs => NULL", ssl); freeOpenSslErrorState(); return nullptr; } for (size_t i=0; i<sk_X509_num(cert_chain); i++) { if (!sk_X509_push(chain.get(), X509_dup_nocopy(sk_X509_value(cert_chain, i)))) { jniThrowOutOfMemory(env, "Unable to push local certificate chain"); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => NULL", ssl); return nullptr; } } #endif jlongArray refArray = getCertificateRefs(env, chain.get()); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => %p", ssl, refArray); return refArray; } #if !defined(OPENSSL_IS_BORINGSSL) // Compatibility shim for SSL_is_server, available in BoringSSL (and OpenSSL 1.0.2). static int SSL_is_server(SSL* ssl) { return ssl->server; } #endif // Fills a long[] with the peer certificates in the chain. static jlongArray NativeCrypto_SSL_get_peer_cert_chain(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain", ssl); if (ssl == nullptr) { return nullptr; } STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl); Unique_sk_X509 chain_copy(nullptr); if (SSL_is_server(ssl)) { X509* x509 = SSL_get_peer_certificate(ssl); if (x509 == nullptr) { JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => NULL", ssl); return nullptr; } chain_copy.reset(sk_X509_new_null()); if (chain_copy.get() == nullptr) { jniThrowOutOfMemory(env, "Unable to allocate peer certificate chain"); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => certificate dup error", ssl); return nullptr; } size_t chain_size = sk_X509_num(chain); for (size_t i = 0; i < chain_size; i++) { if (!sk_X509_push(chain_copy.get(), X509_dup_nocopy(sk_X509_value(chain, i)))) { jniThrowOutOfMemory(env, "Unable to push server's peer certificate chain"); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => certificate chain push error", ssl); return nullptr; } } if (!sk_X509_push(chain_copy.get(), X509_dup_nocopy(x509))) { jniThrowOutOfMemory(env, "Unable to push server's peer certificate"); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => certificate push error", ssl); return nullptr; } chain = chain_copy.get(); } jlongArray refArray = getCertificateRefs(env, chain); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => %p", ssl, refArray); return refArray; } static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* buf, jint len, OpenSslError& sslError, int read_timeout_millis) { JNI_TRACE("ssl=%p sslRead buf=%p len=%d", ssl, buf, len); if (len == 0) { // Don't bother doing anything in this case. return 0; } BIO* rbio = SSL_get_rbio(ssl); BIO* wbio = SSL_get_wbio(ssl); AppData* appData = toAppData(ssl); JNI_TRACE("ssl=%p sslRead appData=%p", ssl, appData); if (appData == nullptr) { return THROW_SSLEXCEPTION; } while (appData->aliveAndKicking) { errno = 0; UniqueMutex appDataLock(&appData->mutex); if (!SSL_is_init_finished(ssl) && !SSL_cutthrough_complete(ssl) && !SSL_renegotiate_pending(ssl)) { JNI_TRACE("ssl=%p sslRead => init is not finished (state=0x%x)", ssl, SSL_get_state(ssl)); return THROW_SSLEXCEPTION; } unsigned int bytesMoved = BIO_number_read(rbio) + BIO_number_written(wbio); if (!appData->setCallbackState(env, shc, fdObject, nullptr, nullptr)) { return THROWN_EXCEPTION; } int result = SSL_read(ssl, buf, len); appData->clearCallbackState(); // callbacks can happen if server requests renegotiation if (env->ExceptionCheck()) { safeSslClear(ssl); JNI_TRACE("ssl=%p sslRead => THROWN_EXCEPTION", ssl); return THROWN_EXCEPTION; } sslError.reset(ssl, result); JNI_TRACE("ssl=%p sslRead SSL_read result=%d sslError=%d", ssl, result, sslError.get()); #ifdef WITH_JNI_TRACE_DATA for (int i = 0; i < result; i+= WITH_JNI_TRACE_DATA_CHUNK_SIZE) { int n = result - i; if (n > WITH_JNI_TRACE_DATA_CHUNK_SIZE) { n = WITH_JNI_TRACE_DATA_CHUNK_SIZE; } JNI_TRACE("ssl=%p sslRead data: %d:\n%.*s", ssl, n, n, buf+i); } #endif // If we have been successful in moving data around, check whether it // might make sense to wake up other blocked threads, so they can give // it a try, too. if (BIO_number_read(rbio) + BIO_number_written(wbio) != bytesMoved && appData->waitingThreads > 0) { sslNotify(appData); } // If we are blocked by the underlying socket, tell the world that // there will be one more waiting thread now. if (sslError.get() == SSL_ERROR_WANT_READ || sslError.get() == SSL_ERROR_WANT_WRITE) { appData->waitingThreads++; } appDataLock.unlock(); switch (sslError.get()) { // Successfully read at least one byte. case SSL_ERROR_NONE: { return result; } // Read zero bytes. End of stream reached. case SSL_ERROR_ZERO_RETURN: { return -1; } // Need to wait for availability of underlying layer, then retry. case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: { int selectResult = sslSelect(env, sslError.get(), fdObject, appData, read_timeout_millis); if (selectResult == THROWN_EXCEPTION) { return THROWN_EXCEPTION; } if (selectResult == -1) { return THROW_SSLEXCEPTION; } if (selectResult == 0) { return THROW_SOCKETTIMEOUTEXCEPTION; } break; } // A problem occurred during a system call, but this is not // necessarily an error. case SSL_ERROR_SYSCALL: { // Connection closed without proper shutdown. Tell caller we // have reached end-of-stream. if (result == 0) { return -1; } // System call has been interrupted. Simply retry. if (errno == EINTR) { break; } // Note that for all other system call errors we fall through // to the default case, which results in an Exception. FALLTHROUGH_INTENDED; } // Everything else is basically an error. default: { return THROW_SSLEXCEPTION; } } } return -1; } static jint NativeCrypto_SSL_read_BIO(JNIEnv* env, jclass, jlong sslRef, jbyteArray destJava, jint destOffset, jint destLength, jlong sourceBioRef, jlong sinkBioRef, jobject shc) { SSL* ssl = to_SSL(env, sslRef, true); BIO* rbio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(sourceBioRef)); BIO* wbio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(sinkBioRef)); JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO dest=%p sourceBio=%p sinkBio=%p shc=%p", ssl, destJava, rbio, wbio, shc); if (ssl == nullptr) { return 0; } if (rbio == nullptr || wbio == nullptr) { jniThrowNullPointerException(env, "rbio == null || wbio == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => rbio == null || wbio == null", ssl); return -1; } if (shc == nullptr) { jniThrowNullPointerException(env, "sslHandshakeCallbacks == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => sslHandshakeCallbacks == null", ssl); return -1; } ScopedByteArrayRW dest(env, destJava); if (dest.get() == nullptr) { JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => threw exception", ssl); return -1; } if (ARRAY_OFFSET_LENGTH_INVALID(dest, destOffset, destLength)) { JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => destOffset=%d, destLength=%d, size=%zd", ssl, destOffset, destLength, dest.size()); jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", nullptr); return -1; } AppData* appData = toAppData(ssl); if (appData == nullptr) { throwSSLExceptionStr(env, "Unable to retrieve application data"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => appData == NULL", ssl); return -1; } errno = 0; UniqueMutex appDataLock(&appData->mutex); if (!appData->setCallbackState(env, shc, nullptr, nullptr, nullptr)) { throwSSLExceptionStr(env, "Unable to set callback state"); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => set callback state failed", ssl); return -1; } ScopedSslBio sslBio(ssl, rbio, wbio); int result = SSL_read(ssl, dest.get() + destOffset, destLength); appData->clearCallbackState(); // callbacks can happen if server requests renegotiation if (env->ExceptionCheck()) { safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => threw exception", ssl); return THROWN_EXCEPTION; } OpenSslError sslError(ssl, result); JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO SSL_read result=%d sslError=%d", ssl, result, sslError.get()); #ifdef WITH_JNI_TRACE_DATA for (int i = 0; i < result; i+= WITH_JNI_TRACE_DATA_CHUNK_SIZE) { int n = result - i; if (n > WITH_JNI_TRACE_DATA_CHUNK_SIZE) { n = WITH_JNI_TRACE_DATA_CHUNK_SIZE; } JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO data: %d:\n%.*s", ssl, n, n, dest.get() + i); } #endif switch (sslError.get()) { // Successfully read at least one byte. case SSL_ERROR_NONE: break; // Read zero bytes. End of stream reached. case SSL_ERROR_ZERO_RETURN: result = -1; break; // Need to wait for availability of underlying layer, then retry. case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: result = 0; break; // A problem occurred during a system call, but this is not // necessarily an error. case SSL_ERROR_SYSCALL: { // Connection closed without proper shutdown. Tell caller we // have reached end-of-stream. if (result == 0) { result = -1; break; } else if (errno == EINTR) { // System call has been interrupted. Simply retry. result = 0; break; } // Note that for all other system call errors we fall through // to the default case, which results in an Exception. FALLTHROUGH_INTENDED; } // Everything else is basically an error. default: { throwSSLExceptionWithSslErrors(env, ssl, sslError.release(), "Read error"); return -1; } } JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => %d", ssl, result); return result; } /** * OpenSSL read function (2): read into buffer at offset n chunks. * Returns 1 (success) or value <= 0 (failure). */ static jint NativeCrypto_SSL_read(JNIEnv* env, jclass, jlong ssl_address, jobject fdObject, jobject shc, jbyteArray b, jint offset, jint len, jint read_timeout_millis) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_read fd=%p shc=%p b=%p offset=%d len=%d read_timeout_millis=%d", ssl, fdObject, shc, b, offset, len, read_timeout_millis); if (ssl == nullptr) { return 0; } if (fdObject == nullptr) { jniThrowNullPointerException(env, "fd == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_read => fd == null", ssl); return 0; } if (shc == nullptr) { jniThrowNullPointerException(env, "sslHandshakeCallbacks == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_read => sslHandshakeCallbacks == null", ssl); return 0; } ScopedByteArrayRW bytes(env, b); if (bytes.get() == nullptr) { JNI_TRACE("ssl=%p NativeCrypto_SSL_read => threw exception", ssl); return 0; } OpenSslError sslError; int ret = sslRead(env, ssl, fdObject, shc, reinterpret_cast<char*>(bytes.get() + offset), len, sslError, read_timeout_millis); int result; switch (ret) { case THROW_SSLEXCEPTION: // See sslRead() regarding improper failure to handle normal cases. throwSSLExceptionWithSslErrors(env, ssl, sslError.release(), "Read error"); result = -1; break; case THROW_SOCKETTIMEOUTEXCEPTION: throwSocketTimeoutException(env, "Read timed out"); result = -1; break; case THROWN_EXCEPTION: // SocketException thrown by NetFd.isClosed // or RuntimeException thrown by callback result = -1; break; default: result = ret; break; } JNI_TRACE("ssl=%p NativeCrypto_SSL_read => %d", ssl, result); return result; } static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const char* buf, jint len, OpenSslError& sslError, int write_timeout_millis) { JNI_TRACE("ssl=%p sslWrite buf=%p len=%d write_timeout_millis=%d", ssl, buf, len, write_timeout_millis); if (len == 0) { // Don't bother doing anything in this case. return 0; } BIO* rbio = SSL_get_rbio(ssl); BIO* wbio = SSL_get_wbio(ssl); AppData* appData = toAppData(ssl); JNI_TRACE("ssl=%p sslWrite appData=%p", ssl, appData); if (appData == nullptr) { return THROW_SSLEXCEPTION; } int count = len; while (appData->aliveAndKicking && len > 0) { errno = 0; UniqueMutex appDataLock(&appData->mutex); if (!SSL_is_init_finished(ssl) && !SSL_cutthrough_complete(ssl) && !SSL_renegotiate_pending(ssl)) { JNI_TRACE("ssl=%p sslWrite => init is not finished (state=0x%x)", ssl, SSL_get_state(ssl)); return THROW_SSLEXCEPTION; } unsigned int bytesMoved = BIO_number_read(rbio) + BIO_number_written(wbio); if (!appData->setCallbackState(env, shc, fdObject, nullptr, nullptr)) { return THROWN_EXCEPTION; } JNI_TRACE("ssl=%p sslWrite SSL_write len=%d", ssl, len); int result = SSL_write(ssl, buf, len); appData->clearCallbackState(); // callbacks can happen if server requests renegotiation if (env->ExceptionCheck()) { safeSslClear(ssl); JNI_TRACE("ssl=%p sslWrite exception => THROWN_EXCEPTION", ssl); return THROWN_EXCEPTION; } sslError.reset(ssl, result); JNI_TRACE("ssl=%p sslWrite SSL_write result=%d sslError=%d", ssl, result, sslError.get()); #ifdef WITH_JNI_TRACE_DATA for (int i = 0; i < result; i+= WITH_JNI_TRACE_DATA_CHUNK_SIZE) { int n = result - i; if (n > WITH_JNI_TRACE_DATA_CHUNK_SIZE) { n = WITH_JNI_TRACE_DATA_CHUNK_SIZE; } JNI_TRACE("ssl=%p sslWrite data: %d:\n%.*s", ssl, n, n, buf+i); } #endif // If we have been successful in moving data around, check whether it // might make sense to wake up other blocked threads, so they can give // it a try, too. if (BIO_number_read(rbio) + BIO_number_written(wbio) != bytesMoved && appData->waitingThreads > 0) { sslNotify(appData); } // If we are blocked by the underlying socket, tell the world that // there will be one more waiting thread now. if (sslError.get() == SSL_ERROR_WANT_READ || sslError.get() == SSL_ERROR_WANT_WRITE) { appData->waitingThreads++; } appDataLock.unlock(); switch (sslError.get()) { // Successfully wrote at least one byte. case SSL_ERROR_NONE: { buf += result; len -= result; break; } // Wrote zero bytes. End of stream reached. case SSL_ERROR_ZERO_RETURN: { return -1; } // Need to wait for availability of underlying layer, then retry. // The concept of a write timeout doesn't really make sense, and // it's also not standard Java behavior, so we wait forever here. case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: { int selectResult = sslSelect(env, sslError.get(), fdObject, appData, write_timeout_millis); if (selectResult == THROWN_EXCEPTION) { return THROWN_EXCEPTION; } if (selectResult == -1) { return THROW_SSLEXCEPTION; } if (selectResult == 0) { return THROW_SOCKETTIMEOUTEXCEPTION; } break; } // A problem occurred during a system call, but this is not // necessarily an error. case SSL_ERROR_SYSCALL: { // Connection closed without proper shutdown. Tell caller we // have reached end-of-stream. if (result == 0) { return -1; } // System call has been interrupted. Simply retry. if (errno == EINTR) { break; } // Note that for all other system call errors we fall through // to the default case, which results in an Exception. FALLTHROUGH_INTENDED; } // Everything else is basically an error. default: { return THROW_SSLEXCEPTION; } } } JNI_TRACE("ssl=%p sslWrite => count=%d", ssl, count); return count; } /** * OpenSSL write function (2): write into buffer at offset n chunks. */ static int NativeCrypto_SSL_write_BIO(JNIEnv* env, jclass, jlong sslRef, jbyteArray sourceJava, jint len, jlong sinkBioRef, jobject shc) { SSL* ssl = to_SSL(env, sslRef, true); BIO* wbio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(sinkBioRef)); JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO source=%p len=%d wbio=%p shc=%p", ssl, sourceJava, len, wbio, shc); if (ssl == nullptr) { return -1; } if (wbio == nullptr) { jniThrowNullPointerException(env, "wbio == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO => wbio == null", ssl); return -1; } if (shc == nullptr) { jniThrowNullPointerException(env, "sslHandshakeCallbacks == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO => sslHandshakeCallbacks == null", ssl); return -1; } AppData* appData = toAppData(ssl); if (appData == nullptr) { throwSSLExceptionStr(env, "Unable to retrieve application data"); safeSslClear(ssl); freeOpenSslErrorState(); JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO appData => NULL", ssl); return -1; } errno = 0; UniqueMutex appDataLock(&appData->mutex); if (!appData->setCallbackState(env, shc, nullptr, nullptr, nullptr)) { throwSSLExceptionStr(env, "Unable to set appdata callback"); freeOpenSslErrorState(); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO => appData can't set callback", ssl); return -1; } ScopedByteArrayRO source(env, sourceJava); if (source.get() == nullptr) { JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO => threw exception", ssl); return -1; } #if defined(OPENSSL_IS_BORINGSSL) Unique_BIO nullBio(BIO_new_mem_buf(nullptr, 0)); #else Unique_BIO nullBio(BIO_new(BIO_s_null())); #endif ScopedSslBio sslBio(ssl, nullBio.get(), wbio); int result = SSL_write(ssl, reinterpret_cast<const char*>(source.get()), len); appData->clearCallbackState(); // callbacks can happen if server requests renegotiation if (env->ExceptionCheck()) { freeOpenSslErrorState(); safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO exception => exception pending (reneg)", ssl); return -1; } OpenSslError sslError(ssl, result); JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO SSL_write result=%d sslError=%d", ssl, result, sslError.get()); #ifdef WITH_JNI_TRACE_DATA for (int i = 0; i < result; i+= WITH_JNI_TRACE_DATA_CHUNK_SIZE) { int n = result - i; if (n > WITH_JNI_TRACE_DATA_CHUNK_SIZE) { n = WITH_JNI_TRACE_DATA_CHUNK_SIZE; } JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO data: %d:\n%.*s", ssl, n, n, source.get() + i); } #endif switch (sslError.get()) { case SSL_ERROR_NONE: return result; // Wrote zero bytes. End of stream reached. case SSL_ERROR_ZERO_RETURN: return -1; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: return 0; case SSL_ERROR_SYSCALL: { // Connection closed without proper shutdown. Tell caller we // have reached end-of-stream. if (result == 0) { return -1; } // System call has been interrupted. Simply retry. if (errno == EINTR) { return 0; } // Note that for all other system call errors we fall through // to the default case, which results in an Exception. FALLTHROUGH_INTENDED; } // Everything else is basically an error. default: { throwSSLExceptionWithSslErrors(env, ssl, sslError.release(), "Write error"); break; } } return -1; } /** * OpenSSL write function (2): write into buffer at offset n chunks. */ static void NativeCrypto_SSL_write(JNIEnv* env, jclass, jlong ssl_address, jobject fdObject, jobject shc, jbyteArray b, jint offset, jint len, jint write_timeout_millis) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_write fd=%p shc=%p b=%p offset=%d len=%d write_timeout_millis=%d", ssl, fdObject, shc, b, offset, len, write_timeout_millis); if (ssl == nullptr) { return; } if (fdObject == nullptr) { jniThrowNullPointerException(env, "fd == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_write => fd == null", ssl); return; } if (shc == nullptr) { jniThrowNullPointerException(env, "sslHandshakeCallbacks == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_write => sslHandshakeCallbacks == null", ssl); return; } ScopedByteArrayRO bytes(env, b); if (bytes.get() == nullptr) { JNI_TRACE("ssl=%p NativeCrypto_SSL_write => threw exception", ssl); return; } OpenSslError sslError; int ret = sslWrite(env, ssl, fdObject, shc, reinterpret_cast<const char*>(bytes.get() + offset), len, sslError, write_timeout_millis); switch (ret) { case THROW_SSLEXCEPTION: // See sslWrite() regarding improper failure to handle normal cases. throwSSLExceptionWithSslErrors(env, ssl, sslError.release(), "Write error"); break; case THROW_SOCKETTIMEOUTEXCEPTION: throwSocketTimeoutException(env, "Write timed out"); break; case THROWN_EXCEPTION: // SocketException thrown by NetFd.isClosed break; default: break; } } /** * Interrupt any pending I/O before closing the socket. */ static void NativeCrypto_SSL_interrupt( JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, false); JNI_TRACE("ssl=%p NativeCrypto_SSL_interrupt", ssl); if (ssl == nullptr) { return; } /* * Mark the connection as quasi-dead, then send something to the emergency * file descriptor, so any blocking select() calls are woken up. */ AppData* appData = toAppData(ssl); if (appData != nullptr) { appData->aliveAndKicking = 0; // At most two threads can be waiting. sslNotify(appData); sslNotify(appData); } } /** * OpenSSL close SSL socket function. */ static void NativeCrypto_SSL_shutdown(JNIEnv* env, jclass, jlong ssl_address, jobject fdObject, jobject shc) { SSL* ssl = to_SSL(env, ssl_address, false); JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown fd=%p shc=%p", ssl, fdObject, shc); if (ssl == nullptr) { return; } if (fdObject == nullptr) { jniThrowNullPointerException(env, "fd == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown => fd == null", ssl); return; } if (shc == nullptr) { jniThrowNullPointerException(env, "sslHandshakeCallbacks == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown => sslHandshakeCallbacks == null", ssl); return; } AppData* appData = toAppData(ssl); if (appData != nullptr) { if (!appData->setCallbackState(env, shc, fdObject, nullptr, nullptr)) { // SocketException thrown by NetFd.isClosed freeOpenSslErrorState(); safeSslClear(ssl); return; } /* * Try to make socket blocking again. OpenSSL literature recommends this. */ int fd = SSL_get_fd(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown s=%d", ssl, fd); if (fd != -1) { setBlocking(fd, true); } int ret = SSL_shutdown(ssl); appData->clearCallbackState(); // callbacks can happen if server requests renegotiation if (env->ExceptionCheck()) { safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown => exception", ssl); return; } switch (ret) { case 0: /* * Shutdown was not successful (yet), but there also * is no error. Since we can't know whether the remote * server is actually still there, and we don't want to * get stuck forever in a second SSL_shutdown() call, we * simply return. This is not security a problem as long * as we close the underlying socket, which we actually * do, because that's where we are just coming from. */ break; case 1: /* * Shutdown was successful. We can safely return. Hooray! */ break; default: /* * Everything else is a real error condition. We should * let the Java layer know about this by throwing an * exception. */ int sslError = SSL_get_error(ssl, ret); throwSSLExceptionWithSslErrors(env, ssl, sslError, "SSL shutdown failed"); break; } } freeOpenSslErrorState(); safeSslClear(ssl); } /** * OpenSSL close SSL socket function. */ static void NativeCrypto_SSL_shutdown_BIO(JNIEnv* env, jclass, jlong ssl_address, jlong rbioRef, jlong wbioRef, jobject shc) { SSL* ssl = to_SSL(env, ssl_address, false); BIO* rbio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(rbioRef)); BIO* wbio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(wbioRef)); JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown rbio=%p wbio=%p shc=%p", ssl, rbio, wbio, shc); if (ssl == nullptr) { return; } if (rbio == nullptr || wbio == nullptr) { jniThrowNullPointerException(env, "rbio == null || wbio == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown => rbio == null || wbio == null", ssl); return; } if (shc == nullptr) { jniThrowNullPointerException(env, "sslHandshakeCallbacks == null"); JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown => sslHandshakeCallbacks == null", ssl); return; } AppData* appData = toAppData(ssl); if (appData != nullptr) { UniqueMutex appDataLock(&appData->mutex); if (!appData->setCallbackState(env, shc, nullptr, nullptr, nullptr)) { // SocketException thrown by NetFd.isClosed freeOpenSslErrorState(); safeSslClear(ssl); return; } ScopedSslBio scopedBio(ssl, rbio, wbio); int ret = SSL_shutdown(ssl); appData->clearCallbackState(); // callbacks can happen if server requests renegotiation if (env->ExceptionCheck()) { safeSslClear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown => exception", ssl); return; } switch (ret) { case 0: /* * Shutdown was not successful (yet), but there also * is no error. Since we can't know whether the remote * server is actually still there, and we don't want to * get stuck forever in a second SSL_shutdown() call, we * simply return. This is not security a problem as long * as we close the underlying socket, which we actually * do, because that's where we are just coming from. */ break; case 1: /* * Shutdown was successful. We can safely return. Hooray! */ break; default: /* * Everything else is a real error condition. We should * let the Java layer know about this by throwing an * exception. */ int sslError = SSL_get_error(ssl, ret); throwSSLExceptionWithSslErrors(env, ssl, sslError, "SSL shutdown failed"); break; } } freeOpenSslErrorState(); safeSslClear(ssl); } static jint NativeCrypto_SSL_get_shutdown(JNIEnv* env, jclass, jlong ssl_address) { const SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_shutdown", ssl); if (ssl == nullptr) { jniThrowNullPointerException(env, "ssl == null"); return 0; } int status = SSL_get_shutdown(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_get_shutdown => %d", ssl, status); return static_cast<jint>(status); } /** * public static native void SSL_free(long ssl); */ static void NativeCrypto_SSL_free(JNIEnv* env, jclass, jlong ssl_address) { SSL* ssl = to_SSL(env, ssl_address, true); JNI_TRACE("ssl=%p NativeCrypto_SSL_free", ssl); if (ssl == nullptr) { return; } AppData* appData = toAppData(ssl); SSL_set_app_data(ssl, nullptr); delete appData; SSL_free(ssl); } /** * Gets and returns in a byte array the ID of the actual SSL session. */ static jbyteArray NativeCrypto_SSL_SESSION_session_id(JNIEnv* env, jclass, jlong ssl_session_address) { SSL_SESSION* ssl_session = to_SSL_SESSION(env, ssl_session_address, true); JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_session_id", ssl_session); if (ssl_session == nullptr) { return nullptr; } jbyteArray result = env->NewByteArray(ssl_session->session_id_length); if (result != nullptr) { jbyte* src = reinterpret_cast<jbyte*>(ssl_session->session_id); env->SetByteArrayRegion(result, 0, ssl_session->session_id_length, src); } JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_session_id => %p session_id_length=%d", ssl_session, result, ssl_session->session_id_length); return result; } /** * Gets and returns in a long integer the creation's time of the * actual SSL session. */ static jlong NativeCrypto_SSL_SESSION_get_time(JNIEnv* env, jclass, jlong ssl_session_address) { SSL_SESSION* ssl_session = to_SSL_SESSION(env, ssl_session_address, true); JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_time", ssl_session); if (ssl_session == nullptr) { return 0; } // result must be jlong, not long or *1000 will overflow jlong result = SSL_SESSION_get_time(ssl_session); result *= 1000; // OpenSSL uses seconds, Java uses milliseconds. JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_time => %lld", ssl_session, (long long) result); return result; } /** * Gets and returns in a string the version of the SSL protocol. If it * returns the string "unknown" it means that no connection is established. */ static jstring NativeCrypto_SSL_SESSION_get_version(JNIEnv* env, jclass, jlong ssl_session_address) { SSL_SESSION* ssl_session = to_SSL_SESSION(env, ssl_session_address, true); JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_version", ssl_session); if (ssl_session == nullptr) { return nullptr; } const char* protocol = SSL_SESSION_get_version(ssl_session); JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_version => %s", ssl_session, protocol); return env->NewStringUTF(protocol); } /** * Gets and returns in a string the cipher negotiated for the SSL session. */ static jstring NativeCrypto_SSL_SESSION_cipher(JNIEnv* env, jclass, jlong ssl_session_address) { SSL_SESSION* ssl_session = to_SSL_SESSION(env, ssl_session_address, true); JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_cipher", ssl_session); if (ssl_session == nullptr) { return nullptr; } const SSL_CIPHER* cipher = ssl_session->cipher; const char* name = SSL_CIPHER_get_name(cipher); JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_cipher => %s", ssl_session, name); return env->NewStringUTF(name); } static jstring NativeCrypto_get_SSL_SESSION_tlsext_hostname(JNIEnv* env, jclass, jlong sessionJava) { SSL_SESSION* ssl_session = to_SSL_SESSION(env, sessionJava, true); JNI_TRACE("ssl_session=%p NativeCrypto_get_SSL_SESSION_tlsext_hostname", ssl_session); if (ssl_session == nullptr || ssl_session->tlsext_hostname == nullptr) { JNI_TRACE("ssl_session=%p NativeCrypto_get_SSL_SESSION_tlsext_hostname => null", ssl_session); return nullptr; } JNI_TRACE("ssl_session=%p NativeCrypto_get_SSL_SESSION_tlsext_hostname => \"%s\"", ssl_session, ssl_session->tlsext_hostname); return env->NewStringUTF(ssl_session->tlsext_hostname); } /** * Frees the SSL session. */ static void NativeCrypto_SSL_SESSION_free(JNIEnv* env, jclass, jlong ssl_session_address) { SSL_SESSION* ssl_session = to_SSL_SESSION(env, ssl_session_address, true); JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_free", ssl_session); if (ssl_session == nullptr) { return; } SSL_SESSION_free(ssl_session); } /** * Serializes the native state of the session (ID, cipher, and keys but * not certificates). Returns a byte[] containing the DER-encoded state. * See apache mod_ssl. */ static jbyteArray NativeCrypto_i2d_SSL_SESSION(JNIEnv* env, jclass, jlong ssl_session_address) { SSL_SESSION* ssl_session = to_SSL_SESSION(env, ssl_session_address, true); JNI_TRACE("ssl_session=%p NativeCrypto_i2d_SSL_SESSION", ssl_session); if (ssl_session == nullptr) { return nullptr; } return ASN1ToByteArray<SSL_SESSION>(env, ssl_session, i2d_SSL_SESSION); } /** * Deserialize the session. */ static jlong NativeCrypto_d2i_SSL_SESSION(JNIEnv* env, jclass, jbyteArray javaBytes) { JNI_TRACE("NativeCrypto_d2i_SSL_SESSION bytes=%p", javaBytes); ScopedByteArrayRO bytes(env, javaBytes); if (bytes.get() == nullptr) { JNI_TRACE("NativeCrypto_d2i_SSL_SESSION => threw exception"); return 0; } const unsigned char* ucp = reinterpret_cast<const unsigned char*>(bytes.get()); SSL_SESSION* ssl_session = d2i_SSL_SESSION(nullptr, &ucp, bytes.size()); #if !defined(OPENSSL_IS_BORINGSSL) // Initialize SSL_SESSION cipher field based on cipher_id http://b/7091840 if (ssl_session != NULL) { // based on ssl_get_prev_session uint32_t cipher_id_network_order = htonl(ssl_session->cipher_id); uint8_t* cipher_id_byte_pointer = reinterpret_cast<uint8_t*>(&cipher_id_network_order); if (ssl_session->ssl_version >= SSL3_VERSION_MAJOR) { cipher_id_byte_pointer += 2; // skip first two bytes for SSL3+ } else { cipher_id_byte_pointer += 1; // skip first byte for SSL2 } ssl_session->cipher = SSLv23_method()->get_cipher_by_char(cipher_id_byte_pointer); JNI_TRACE("NativeCrypto_d2i_SSL_SESSION cipher_id=%lx hton=%x 0=%x 1=%x cipher=%s", ssl_session->cipher_id, cipher_id_network_order, cipher_id_byte_pointer[0], cipher_id_byte_pointer[1], SSL_CIPHER_get_name(ssl_session->cipher)); } #endif if (ssl_session == nullptr || ucp != (reinterpret_cast<const unsigned char*>(bytes.get()) + bytes.size())) { if (!throwExceptionIfNecessary(env, "d2i_SSL_SESSION", throwIOException)) { throwIOException(env, "d2i_SSL_SESSION"); } JNI_TRACE("NativeCrypto_d2i_SSL_SESSION => failure to convert"); return 0L; } JNI_TRACE("NativeCrypto_d2i_SSL_SESSION => %p", ssl_session); return reinterpret_cast<uintptr_t>(ssl_session); } static jlong NativeCrypto_ERR_peek_last_error(JNIEnv*, jclass) { return ERR_peek_last_error(); } static jstring NativeCrypto_SSL_CIPHER_get_kx_name(JNIEnv* env, jclass, jlong cipher_address) { const SSL_CIPHER* cipher = to_SSL_CIPHER(env, cipher_address, true); const char* kx_name = nullptr; #if defined(OPENSSL_IS_BORINGSSL) kx_name = SSL_CIPHER_get_kx_name(cipher); #else kx_name = SSL_CIPHER_authentication_method(cipher); #endif return env->NewStringUTF(kx_name); } static jobjectArray NativeCrypto_get_cipher_names(JNIEnv *env, jclass, jstring selectorJava) { ScopedUtfChars selector(env, selectorJava); if (selector.c_str() == nullptr) { jniThrowException(env, "java/lang/IllegalArgumentException", "selector == NULL"); return nullptr; } JNI_TRACE("NativeCrypto_get_cipher_names %s", selector.c_str()); Unique_SSL_CTX sslCtx(SSL_CTX_new(SSLv23_method())); Unique_SSL ssl(SSL_new(sslCtx.get())); if (!SSL_set_cipher_list(ssl.get(), selector.c_str())) { jniThrowException(env, "java/lang/IllegalArgumentException", "Unable to set SSL cipher list"); return nullptr; } STACK_OF(SSL_CIPHER) *ciphers = SSL_get_ciphers(ssl.get()); size_t size = sk_SSL_CIPHER_num(ciphers); ScopedLocalRef<jobjectArray> cipherNamesArray(env, env->NewObjectArray(size, stringClass, nullptr)); if (cipherNamesArray.get() == nullptr) { return nullptr; } for (size_t i = 0; i < size; i++) { const char *name = SSL_CIPHER_get_name(sk_SSL_CIPHER_value(ciphers, i)); ScopedLocalRef<jstring> cipherName(env, env->NewStringUTF(name)); env->SetObjectArrayElement(cipherNamesArray.get(), i, cipherName.get()); } JNI_TRACE("NativeCrypto_get_cipher_names(%s) => success (%zd entries)", selector.c_str(), size); return cipherNamesArray.release(); } #if defined(OPENSSL_IS_BORINGSSL) /** * Compare the given CertID with a certificate and it's issuer. * True is returned if the CertID matches. */ static bool ocsp_cert_id_matches_certificate(CBS *cert_id, X509 *x509, X509 *issuerX509) { // Get the hash algorithm used by this CertID CBS hash_algorithm, hash; if (!CBS_get_asn1(cert_id, &hash_algorithm, CBS_ASN1_SEQUENCE) || !CBS_get_asn1(&hash_algorithm, &hash, CBS_ASN1_OBJECT)) { return false; } // Get the issuer's name hash from the CertID CBS issuer_name_hash; if (!CBS_get_asn1(cert_id, &issuer_name_hash, CBS_ASN1_OCTETSTRING)) { return false; } // Get the issuer's key hash from the CertID CBS issuer_key_hash; if (!CBS_get_asn1(cert_id, &issuer_key_hash, CBS_ASN1_OCTETSTRING)) { return false; } // Get the serial number from the CertID CBS serial; if (!CBS_get_asn1(cert_id, &serial, CBS_ASN1_INTEGER)) { return false; } // Compare the certificate's serial number with the one from the Cert ID const uint8_t *p = CBS_data(&serial); Unique_ASN1_INTEGER serial_number(c2i_ASN1_INTEGER(nullptr, &p, CBS_len(&serial))); ASN1_INTEGER *expected_serial_number = X509_get_serialNumber(x509); if (serial_number.get() == nullptr || ASN1_INTEGER_cmp(expected_serial_number, serial_number.get()) != 0) { return false; } // Find the hash algorithm to be used const EVP_MD *digest = EVP_get_digestbynid(OBJ_cbs2nid(&hash)); if (digest == nullptr) { return false; } // Hash the issuer's name and compare the hash with the one from the Cert ID uint8_t md[EVP_MAX_MD_SIZE]; X509_NAME *issuer_name = X509_get_subject_name(issuerX509); if (!X509_NAME_digest(issuer_name, digest, md, nullptr) || !CBS_mem_equal(&issuer_name_hash, md, EVP_MD_size(digest))) { return false; } // Same thing with the issuer's key ASN1_BIT_STRING *issuer_key = X509_get0_pubkey_bitstr(issuerX509); if (!EVP_Digest(issuer_key->data, issuer_key->length, md, nullptr, digest, nullptr) || !CBS_mem_equal(&issuer_key_hash, md, EVP_MD_size(digest))) { return false; } return true; } /** * Get a SingleResponse whose CertID matches the given certificate and issuer from a * SEQUENCE OF SingleResponse. * * If found, |out_single_response| is set to the response, and true is returned. Otherwise if an * error occured or no response matches the certificate, false is returned and |out_single_response| * is unchanged. */ static bool find_ocsp_single_response(CBS* responses, X509 *x509, X509 *issuerX509, CBS *out_single_response) { // Iterate over all the SingleResponses, until one matches the certificate while (CBS_len(responses) > 0) { // Get the next available SingleResponse from the sequence CBS single_response; if (!CBS_get_asn1(responses, &single_response, CBS_ASN1_SEQUENCE)) { return false; } // Make a copy of the stream so we pass it back to the caller CBS single_response_original = single_response; // Get the SingleResponse's CertID // If this fails ignore the current response and move to the next one CBS cert_id; if (!CBS_get_asn1(&single_response, &cert_id, CBS_ASN1_SEQUENCE)) { continue; } // Compare the CertID with the given certificate and issuer if (ocsp_cert_id_matches_certificate(&cert_id, x509, issuerX509)) { *out_single_response = single_response_original; return true; } } return false; } /** * Get the BasicOCSPResponse from an OCSPResponse. * If parsing succeeds and the response is of type basic, |basic_response| is set to it, and true is * returned. */ static bool get_ocsp_basic_response(CBS *ocsp_response, CBS *basic_response) { CBS tagged_response_bytes, response_bytes, response_type, response; // Get the ResponseBytes out of the OCSPResponse if (!CBS_get_asn1(ocsp_response, nullptr /* responseStatus */, CBS_ASN1_ENUMERATED) || !CBS_get_asn1(ocsp_response, &tagged_response_bytes, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) || !CBS_get_asn1(&tagged_response_bytes, &response_bytes, CBS_ASN1_SEQUENCE)) { return false; } // Parse the response type and data out of the ResponseBytes if (!CBS_get_asn1(&response_bytes, &response_type, CBS_ASN1_OBJECT) || !CBS_get_asn1(&response_bytes, &response, CBS_ASN1_OCTETSTRING)) { return false; } // Only basic OCSP responses are supported if (OBJ_cbs2nid(&response_type) != NID_id_pkix_OCSP_basic) { return false; } // Parse the octet string as a BasicOCSPResponse return CBS_get_asn1(&response, basic_response, CBS_ASN1_SEQUENCE); } /** * Get the SEQUENCE OF SingleResponse from a BasicOCSPResponse. * If parsing succeeds, |single_responses| is set to point to the sequence of SingleResponse, and * true is returned. */ static bool get_ocsp_single_responses(CBS *basic_response, CBS *single_responses) { // Parse the ResponseData out of the BasicOCSPResponse. Ignore the rest. CBS response_data; if (!CBS_get_asn1(basic_response, &response_data, CBS_ASN1_SEQUENCE)) { return false; } // Skip the version, responderID and producedAt fields if (!CBS_get_optional_asn1(&response_data, nullptr /* version */, nullptr, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) || !CBS_get_any_asn1_element(&response_data, nullptr /* responderID */, nullptr, nullptr) || !CBS_get_any_asn1_element(&response_data, nullptr /* producedAt */, nullptr, nullptr)) { return false; } // Extract the list of SingleResponse. return CBS_get_asn1(&response_data, single_responses, CBS_ASN1_SEQUENCE); } /** * Get the SEQUENCE OF Extension from a SingleResponse. * If parsing succeeds, |extensions| is set to point the the extension sequence and true is * returned. */ static bool get_ocsp_single_response_extensions(CBS *single_response, CBS *extensions) { // Skip the certID, certStatus, thisUpdate and optional nextUpdate fields. if (!CBS_get_any_asn1_element(single_response, nullptr /* certID */, nullptr, nullptr) || !CBS_get_any_asn1_element(single_response, nullptr /* certStatus */, nullptr, nullptr) || !CBS_get_any_asn1_element(single_response, nullptr /* thisUpdate */, nullptr, nullptr) || !CBS_get_optional_asn1(single_response, nullptr /* nextUpdate */, nullptr, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) { return false; } // Get the list of Extension return CBS_get_asn1(single_response, extensions, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1); } /* * X509v3_get_ext_by_OBJ and X509v3_get_ext take const arguments, unlike the other *_get_ext * functions. * This means they cannot be used with X509Type_get_ext_oid, so these wrapper functions are used * instead. */ static int _X509v3_get_ext_by_OBJ(X509_EXTENSIONS *exts, ASN1_OBJECT *obj, int lastpos) { return X509v3_get_ext_by_OBJ(exts, obj, lastpos); } static X509_EXTENSION *_X509v3_get_ext(X509_EXTENSIONS* exts, int loc) { return X509v3_get_ext(exts, loc); } /* public static native byte[] get_ocsp_single_extension(byte[] ocspData, String oid, long x509Ref, long issuerX509Ref); */ static jbyteArray NativeCrypto_get_ocsp_single_extension(JNIEnv *env, jclass, jbyteArray ocspDataBytes, jstring oid, jlong x509Ref, jlong issuerX509Ref) { ScopedByteArrayRO ocspData(env, ocspDataBytes); if (ocspData.get() == nullptr) { return nullptr; } CBS cbs; CBS_init(&cbs, reinterpret_cast<const uint8_t*>(ocspData.get()), ocspData.size()); // Start parsing the OCSPResponse CBS ocsp_response; if (!CBS_get_asn1(&cbs, &ocsp_response, CBS_ASN1_SEQUENCE)) { return nullptr; } // Get the BasicOCSPResponse from the OCSP Response CBS basic_response; if (!get_ocsp_basic_response(&ocsp_response, &basic_response)) { return nullptr; } // Get the list of SingleResponses from the BasicOCSPResponse CBS responses; if (!get_ocsp_single_responses(&basic_response, &responses)) { return nullptr; } // Find the response matching the certificate X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); X509* issuerX509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(issuerX509Ref)); CBS single_response; if (!find_ocsp_single_response(&responses, x509, issuerX509, &single_response)) { return nullptr; } // Get the extensions from the SingleResponse CBS extensions; if (!get_ocsp_single_response_extensions(&single_response, &extensions)) { return nullptr; } const uint8_t* ptr = CBS_data(&extensions); Unique_X509_EXTENSIONS x509_exts(d2i_X509_EXTENSIONS(nullptr, &ptr, CBS_len(&extensions))); if (x509_exts.get() == nullptr) { return nullptr; } return X509Type_get_ext_oid<X509_EXTENSIONS, _X509v3_get_ext_by_OBJ, _X509v3_get_ext>( env, x509_exts.get(), oid); } #else static jbyteArray NativeCrypto_get_ocsp_single_extension(JNIEnv*, jclass, jbyteArray, jstring, jlong, jlong) { return NULL; } #endif static jlong NativeCrypto_getDirectBufferAddress(JNIEnv *env, jclass, jobject buffer) { return reinterpret_cast<jlong>(env->GetDirectBufferAddress(buffer)); } #define FILE_DESCRIPTOR "Ljava/io/FileDescriptor;" #define SSL_CALLBACKS "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeCrypto$SSLHandshakeCallbacks;" #define REF_EC_GROUP "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EC_GROUP;" #define REF_EC_POINT "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EC_POINT;" #define REF_EVP_AEAD_CTX "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_AEAD_CTX;" #define REF_EVP_CIPHER_CTX "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_CIPHER_CTX;" #define REF_EVP_PKEY "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_PKEY;" #define REF_HMAC_CTX "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$HMAC_CTX;" static JNINativeMethod sNativeCryptoMethods[] = { NATIVE_METHOD(NativeCrypto, clinit, "()Z"), NATIVE_METHOD(NativeCrypto, ENGINE_load_dynamic, "()V"), NATIVE_METHOD(NativeCrypto, ENGINE_by_id, "(Ljava/lang/String;)J"), NATIVE_METHOD(NativeCrypto, ENGINE_add, "(J)I"), NATIVE_METHOD(NativeCrypto, ENGINE_init, "(J)I"), NATIVE_METHOD(NativeCrypto, ENGINE_finish, "(J)I"), NATIVE_METHOD(NativeCrypto, ENGINE_free, "(J)I"), NATIVE_METHOD(NativeCrypto, ENGINE_load_private_key, "(JLjava/lang/String;)J"), NATIVE_METHOD(NativeCrypto, ENGINE_get_id, "(J)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, ENGINE_ctrl_cmd_string, "(JLjava/lang/String;Ljava/lang/String;I)I"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_RSA, "([B[B[B[B[B[B[B[B)J"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_EC_KEY, "(" REF_EC_GROUP REF_EC_POINT "[B)J"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_type, "(" REF_EVP_PKEY ")I"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_size, "(" REF_EVP_PKEY ")I"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_print_public, "(" REF_EVP_PKEY ")Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_print_params, "(" REF_EVP_PKEY ")Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_free, "(J)V"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_cmp, "(" REF_EVP_PKEY REF_EVP_PKEY ")I"), NATIVE_METHOD(NativeCrypto, i2d_PKCS8_PRIV_KEY_INFO, "(" REF_EVP_PKEY ")[B"), NATIVE_METHOD(NativeCrypto, d2i_PKCS8_PRIV_KEY_INFO, "([B)J"), NATIVE_METHOD(NativeCrypto, i2d_PUBKEY, "(" REF_EVP_PKEY ")[B"), NATIVE_METHOD(NativeCrypto, d2i_PUBKEY, "([B)J"), NATIVE_METHOD(NativeCrypto, PEM_read_bio_PUBKEY, "(J)J"), NATIVE_METHOD(NativeCrypto, PEM_read_bio_PrivateKey, "(J)J"), NATIVE_METHOD(NativeCrypto, getRSAPrivateKeyWrapper, "(Ljava/security/PrivateKey;[B)J"), NATIVE_METHOD(NativeCrypto, getECPrivateKeyWrapper, "(Ljava/security/PrivateKey;" REF_EC_GROUP ")J"), NATIVE_METHOD(NativeCrypto, RSA_generate_key_ex, "(I[B)J"), NATIVE_METHOD(NativeCrypto, RSA_size, "(" REF_EVP_PKEY ")I"), NATIVE_METHOD(NativeCrypto, RSA_private_encrypt, "(I[B[B" REF_EVP_PKEY "I)I"), NATIVE_METHOD(NativeCrypto, RSA_public_decrypt, "(I[B[B" REF_EVP_PKEY "I)I"), NATIVE_METHOD(NativeCrypto, RSA_public_encrypt, "(I[B[B" REF_EVP_PKEY "I)I"), NATIVE_METHOD(NativeCrypto, RSA_private_decrypt, "(I[B[B" REF_EVP_PKEY "I)I"), NATIVE_METHOD(NativeCrypto, get_RSA_private_params, "(" REF_EVP_PKEY ")[[B"), NATIVE_METHOD(NativeCrypto, get_RSA_public_params, "(" REF_EVP_PKEY ")[[B"), NATIVE_METHOD(NativeCrypto, EC_GROUP_new_by_curve_name, "(Ljava/lang/String;)J"), NATIVE_METHOD(NativeCrypto, EC_GROUP_new_arbitrary, "([B[B[B[B[B[BI)J"), NATIVE_METHOD(NativeCrypto, EC_GROUP_set_asn1_flag, "(" REF_EC_GROUP "I)V"), NATIVE_METHOD(NativeCrypto, EC_GROUP_set_point_conversion_form, "(" REF_EC_GROUP "I)V"), NATIVE_METHOD(NativeCrypto, EC_GROUP_get_curve_name, "(" REF_EC_GROUP ")Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, EC_GROUP_get_curve, "(" REF_EC_GROUP ")[[B"), NATIVE_METHOD(NativeCrypto, EC_GROUP_get_order, "(" REF_EC_GROUP ")[B"), NATIVE_METHOD(NativeCrypto, EC_GROUP_get_degree, "(" REF_EC_GROUP ")I"), NATIVE_METHOD(NativeCrypto, EC_GROUP_get_cofactor, "(" REF_EC_GROUP ")[B"), NATIVE_METHOD(NativeCrypto, EC_GROUP_clear_free, "(J)V"), NATIVE_METHOD(NativeCrypto, EC_GROUP_get_generator, "(" REF_EC_GROUP ")J"), NATIVE_METHOD(NativeCrypto, get_EC_GROUP_type, "(" REF_EC_GROUP ")I"), NATIVE_METHOD(NativeCrypto, EC_POINT_new, "(" REF_EC_GROUP ")J"), NATIVE_METHOD(NativeCrypto, EC_POINT_clear_free, "(J)V"), NATIVE_METHOD(NativeCrypto, EC_POINT_set_affine_coordinates, "(" REF_EC_GROUP REF_EC_POINT "[B[B)V"), NATIVE_METHOD(NativeCrypto, EC_POINT_get_affine_coordinates, "(" REF_EC_GROUP REF_EC_POINT ")[[B"), NATIVE_METHOD(NativeCrypto, EC_KEY_generate_key, "(" REF_EC_GROUP ")J"), NATIVE_METHOD(NativeCrypto, EC_KEY_get1_group, "(" REF_EVP_PKEY ")J"), NATIVE_METHOD(NativeCrypto, EC_KEY_get_private_key, "(" REF_EVP_PKEY ")[B"), NATIVE_METHOD(NativeCrypto, EC_KEY_get_public_key, "(" REF_EVP_PKEY ")J"), NATIVE_METHOD(NativeCrypto, EC_KEY_set_nonce_from_hash, "(" REF_EVP_PKEY "Z)V"), NATIVE_METHOD(NativeCrypto, ECDH_compute_key, "([BI" REF_EVP_PKEY REF_EVP_PKEY ")I"), NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_create, "()J"), NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_cleanup, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;)V"), NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_destroy, "(J)V"), NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_copy_ex, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;)I"), NATIVE_METHOD(NativeCrypto, EVP_DigestInit_ex, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;J)I"), NATIVE_METHOD(NativeCrypto, EVP_DigestUpdate, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[BII)V"), NATIVE_METHOD(NativeCrypto, EVP_DigestUpdateDirect, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;JI)V"), NATIVE_METHOD(NativeCrypto, EVP_DigestFinal_ex, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[BI)I"), NATIVE_METHOD(NativeCrypto, EVP_get_digestbyname, "(Ljava/lang/String;)J"), NATIVE_METHOD(NativeCrypto, EVP_MD_block_size, "(J)I"), NATIVE_METHOD(NativeCrypto, EVP_MD_size, "(J)I"), NATIVE_METHOD(NativeCrypto, EVP_DigestSignInit, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;J" REF_EVP_PKEY ")J"), NATIVE_METHOD(NativeCrypto, EVP_DigestSignUpdate, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[BII)V"), NATIVE_METHOD(NativeCrypto, EVP_DigestSignUpdateDirect, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;JI)V"), NATIVE_METHOD(NativeCrypto, EVP_DigestSignFinal, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;)[B"), NATIVE_METHOD(NativeCrypto, EVP_DigestVerifyInit, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;J" REF_EVP_PKEY ")J"), NATIVE_METHOD(NativeCrypto, EVP_DigestVerifyUpdate, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[BII)V"), NATIVE_METHOD(NativeCrypto, EVP_DigestVerifyUpdateDirect, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;JI)V"), NATIVE_METHOD(NativeCrypto, EVP_DigestVerifyFinal, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_MD_CTX;[BII)Z"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_CTX_set_rsa_padding, "(JI)V"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_CTX_set_rsa_pss_saltlen, "(JI)V"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_CTX_set_rsa_mgf1_md, "(JJ)V"), NATIVE_METHOD(NativeCrypto, EVP_get_cipherbyname, "(Ljava/lang/String;)J"), NATIVE_METHOD(NativeCrypto, EVP_CipherInit_ex, "(" REF_EVP_CIPHER_CTX "J[B[BZ)V"), NATIVE_METHOD(NativeCrypto, EVP_CipherUpdate, "(" REF_EVP_CIPHER_CTX "[BI[BII)I"), NATIVE_METHOD(NativeCrypto, EVP_CipherFinal_ex, "(" REF_EVP_CIPHER_CTX "[BI)I"), NATIVE_METHOD(NativeCrypto, EVP_CIPHER_iv_length, "(J)I"), NATIVE_METHOD(NativeCrypto, EVP_CIPHER_CTX_new, "()J"), NATIVE_METHOD(NativeCrypto, EVP_CIPHER_CTX_block_size, "(" REF_EVP_CIPHER_CTX ")I"), NATIVE_METHOD(NativeCrypto, get_EVP_CIPHER_CTX_buf_len, "(" REF_EVP_CIPHER_CTX ")I"), NATIVE_METHOD(NativeCrypto, get_EVP_CIPHER_CTX_final_used, "(" REF_EVP_CIPHER_CTX ")Z"), NATIVE_METHOD(NativeCrypto, EVP_CIPHER_CTX_set_padding, "(" REF_EVP_CIPHER_CTX "Z)V"), NATIVE_METHOD(NativeCrypto, EVP_CIPHER_CTX_set_key_length, "(" REF_EVP_CIPHER_CTX "I)V"), NATIVE_METHOD(NativeCrypto, EVP_CIPHER_CTX_free, "(J)V"), NATIVE_METHOD(NativeCrypto, EVP_aead_aes_128_gcm, "()J"), NATIVE_METHOD(NativeCrypto, EVP_aead_aes_256_gcm, "()J"), NATIVE_METHOD(NativeCrypto, EVP_AEAD_CTX_init, "(J[BI)J"), NATIVE_METHOD(NativeCrypto, EVP_AEAD_CTX_cleanup, "(J)V"), NATIVE_METHOD(NativeCrypto, EVP_AEAD_max_overhead, "(J)I"), NATIVE_METHOD(NativeCrypto, EVP_AEAD_nonce_length, "(J)I"), NATIVE_METHOD(NativeCrypto, EVP_AEAD_max_tag_len, "(J)I"), NATIVE_METHOD(NativeCrypto, EVP_AEAD_CTX_seal, "(" REF_EVP_AEAD_CTX "[BI[B[BII[B)I"), NATIVE_METHOD(NativeCrypto, EVP_AEAD_CTX_open, "(" REF_EVP_AEAD_CTX "[BI[B[BII[B)I"), NATIVE_METHOD(NativeCrypto, HMAC_CTX_new, "()J"), NATIVE_METHOD(NativeCrypto, HMAC_CTX_free, "(J)V"), NATIVE_METHOD(NativeCrypto, HMAC_Init_ex, "(" REF_HMAC_CTX "[BJ)V"), NATIVE_METHOD(NativeCrypto, HMAC_Update, "(" REF_HMAC_CTX "[BII)V"), NATIVE_METHOD(NativeCrypto, HMAC_UpdateDirect, "(" REF_HMAC_CTX "JI)V"), NATIVE_METHOD(NativeCrypto, HMAC_Final, "(" REF_HMAC_CTX ")[B"), NATIVE_METHOD(NativeCrypto, RAND_seed, "([B)V"), NATIVE_METHOD(NativeCrypto, RAND_load_file, "(Ljava/lang/String;J)I"), NATIVE_METHOD(NativeCrypto, RAND_bytes, "([B)V"), NATIVE_METHOD(NativeCrypto, OBJ_txt2nid, "(Ljava/lang/String;)I"), NATIVE_METHOD(NativeCrypto, OBJ_txt2nid_longName, "(Ljava/lang/String;)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, OBJ_txt2nid_oid, "(Ljava/lang/String;)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, create_BIO_InputStream, ("(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLBIOInputStream;Z)J")), NATIVE_METHOD(NativeCrypto, create_BIO_OutputStream, "(Ljava/io/OutputStream;)J"), NATIVE_METHOD(NativeCrypto, BIO_read, "(J[B)I"), NATIVE_METHOD(NativeCrypto, BIO_write, "(J[BII)V"), NATIVE_METHOD(NativeCrypto, BIO_free_all, "(J)V"), NATIVE_METHOD(NativeCrypto, X509_NAME_print_ex, "(JJ)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, d2i_X509_bio, "(J)J"), NATIVE_METHOD(NativeCrypto, d2i_X509, "([B)J"), NATIVE_METHOD(NativeCrypto, i2d_X509, "(J)[B"), NATIVE_METHOD(NativeCrypto, i2d_X509_PUBKEY, "(J)[B"), NATIVE_METHOD(NativeCrypto, PEM_read_bio_X509, "(J)J"), NATIVE_METHOD(NativeCrypto, PEM_read_bio_PKCS7, "(JI)[J"), NATIVE_METHOD(NativeCrypto, d2i_PKCS7_bio, "(JI)[J"), NATIVE_METHOD(NativeCrypto, i2d_PKCS7, "([J)[B"), NATIVE_METHOD(NativeCrypto, ASN1_seq_unpack_X509_bio, "(J)[J"), NATIVE_METHOD(NativeCrypto, ASN1_seq_pack_X509, "([J)[B"), NATIVE_METHOD(NativeCrypto, X509_free, "(J)V"), NATIVE_METHOD(NativeCrypto, X509_dup, "(J)J"), NATIVE_METHOD(NativeCrypto, X509_cmp, "(JJ)I"), NATIVE_METHOD(NativeCrypto, X509_print_ex, "(JJJJ)V"), NATIVE_METHOD(NativeCrypto, X509_get_pubkey, "(J)J"), NATIVE_METHOD(NativeCrypto, X509_get_issuer_name, "(J)[B"), NATIVE_METHOD(NativeCrypto, X509_get_subject_name, "(J)[B"), NATIVE_METHOD(NativeCrypto, get_X509_pubkey_oid, "(J)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, get_X509_sig_alg_oid, "(J)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, get_X509_sig_alg_parameter, "(J)[B"), NATIVE_METHOD(NativeCrypto, get_X509_issuerUID, "(J)[Z"), NATIVE_METHOD(NativeCrypto, get_X509_subjectUID, "(J)[Z"), NATIVE_METHOD(NativeCrypto, get_X509_ex_kusage, "(J)[Z"), NATIVE_METHOD(NativeCrypto, get_X509_ex_xkusage, "(J)[Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, get_X509_ex_pathlen, "(J)I"), NATIVE_METHOD(NativeCrypto, X509_get_ext_oid, "(JLjava/lang/String;)[B"), NATIVE_METHOD(NativeCrypto, X509_CRL_get_ext_oid, "(JLjava/lang/String;)[B"), NATIVE_METHOD(NativeCrypto, X509_delete_ext, "(JLjava/lang/String;)V"), NATIVE_METHOD(NativeCrypto, get_X509_CRL_crl_enc, "(J)[B"), NATIVE_METHOD(NativeCrypto, X509_CRL_verify, "(J" REF_EVP_PKEY ")V"), NATIVE_METHOD(NativeCrypto, X509_CRL_get_lastUpdate, "(J)J"), NATIVE_METHOD(NativeCrypto, X509_CRL_get_nextUpdate, "(J)J"), NATIVE_METHOD(NativeCrypto, X509_REVOKED_get_ext_oid, "(JLjava/lang/String;)[B"), NATIVE_METHOD(NativeCrypto, X509_REVOKED_get_serialNumber, "(J)[B"), NATIVE_METHOD(NativeCrypto, X509_REVOKED_print, "(JJ)V"), NATIVE_METHOD(NativeCrypto, get_X509_REVOKED_revocationDate, "(J)J"), NATIVE_METHOD(NativeCrypto, get_X509_ext_oids, "(JI)[Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, get_X509_CRL_ext_oids, "(JI)[Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, get_X509_REVOKED_ext_oids, "(JI)[Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, get_X509_GENERAL_NAME_stack, "(JI)[[Ljava/lang/Object;"), NATIVE_METHOD(NativeCrypto, X509_get_notBefore, "(J)J"), NATIVE_METHOD(NativeCrypto, X509_get_notAfter, "(J)J"), NATIVE_METHOD(NativeCrypto, X509_get_version, "(J)J"), NATIVE_METHOD(NativeCrypto, X509_get_serialNumber, "(J)[B"), NATIVE_METHOD(NativeCrypto, X509_verify, "(J" REF_EVP_PKEY ")V"), NATIVE_METHOD(NativeCrypto, get_X509_cert_info_enc, "(J)[B"), NATIVE_METHOD(NativeCrypto, get_X509_signature, "(J)[B"), NATIVE_METHOD(NativeCrypto, get_X509_CRL_signature, "(J)[B"), NATIVE_METHOD(NativeCrypto, get_X509_ex_flags, "(J)I"), NATIVE_METHOD(NativeCrypto, X509_check_issued, "(JJ)I"), NATIVE_METHOD(NativeCrypto, d2i_X509_CRL_bio, "(J)J"), NATIVE_METHOD(NativeCrypto, PEM_read_bio_X509_CRL, "(J)J"), NATIVE_METHOD(NativeCrypto, X509_CRL_get0_by_cert, "(JJ)J"), NATIVE_METHOD(NativeCrypto, X509_CRL_get0_by_serial, "(J[B)J"), NATIVE_METHOD(NativeCrypto, X509_CRL_get_REVOKED, "(J)[J"), NATIVE_METHOD(NativeCrypto, i2d_X509_CRL, "(J)[B"), NATIVE_METHOD(NativeCrypto, X509_CRL_free, "(J)V"), NATIVE_METHOD(NativeCrypto, X509_CRL_print, "(JJ)V"), NATIVE_METHOD(NativeCrypto, get_X509_CRL_sig_alg_oid, "(J)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, get_X509_CRL_sig_alg_parameter, "(J)[B"), NATIVE_METHOD(NativeCrypto, X509_CRL_get_issuer_name, "(J)[B"), NATIVE_METHOD(NativeCrypto, X509_CRL_get_version, "(J)J"), NATIVE_METHOD(NativeCrypto, X509_CRL_get_ext, "(JLjava/lang/String;)J"), NATIVE_METHOD(NativeCrypto, X509_REVOKED_get_ext, "(JLjava/lang/String;)J"), NATIVE_METHOD(NativeCrypto, X509_REVOKED_dup, "(J)J"), NATIVE_METHOD(NativeCrypto, i2d_X509_REVOKED, "(J)[B"), NATIVE_METHOD(NativeCrypto, X509_supported_extension, "(J)I"), NATIVE_METHOD(NativeCrypto, ASN1_TIME_to_Calendar, "(JLjava/util/Calendar;)V"), NATIVE_METHOD(NativeCrypto, EVP_has_aes_hardware, "()I"), NATIVE_METHOD(NativeCrypto, SSL_CTX_new, "()J"), NATIVE_METHOD(NativeCrypto, SSL_CTX_free, "(J)V"), NATIVE_METHOD(NativeCrypto, SSL_CTX_set_session_id_context, "(J[B)V"), NATIVE_METHOD(NativeCrypto, SSL_new, "(J)J"), NATIVE_METHOD(NativeCrypto, SSL_enable_tls_channel_id, "(J)V"), NATIVE_METHOD(NativeCrypto, SSL_get_tls_channel_id, "(J)[B"), NATIVE_METHOD(NativeCrypto, SSL_set1_tls_channel_id, "(J" REF_EVP_PKEY ")V"), NATIVE_METHOD(NativeCrypto, SSL_use_PrivateKey, "(J" REF_EVP_PKEY ")V"), NATIVE_METHOD(NativeCrypto, SSL_use_certificate, "(J[J)V"), NATIVE_METHOD(NativeCrypto, SSL_check_private_key, "(J)V"), NATIVE_METHOD(NativeCrypto, SSL_set_client_CA_list, "(J[[B)V"), NATIVE_METHOD(NativeCrypto, SSL_get_mode, "(J)J"), NATIVE_METHOD(NativeCrypto, SSL_set_mode, "(JJ)J"), NATIVE_METHOD(NativeCrypto, SSL_clear_mode, "(JJ)J"), NATIVE_METHOD(NativeCrypto, SSL_get_options, "(J)J"), NATIVE_METHOD(NativeCrypto, SSL_set_options, "(JJ)J"), NATIVE_METHOD(NativeCrypto, SSL_clear_options, "(JJ)J"), NATIVE_METHOD(NativeCrypto, SSL_enable_signed_cert_timestamps, "(J)V"), NATIVE_METHOD(NativeCrypto, SSL_get_signed_cert_timestamp_list, "(J)[B"), NATIVE_METHOD(NativeCrypto, SSL_CTX_set_signed_cert_timestamp_list, "(J[B)V"), NATIVE_METHOD(NativeCrypto, SSL_enable_ocsp_stapling, "(J)V"), NATIVE_METHOD(NativeCrypto, SSL_get_ocsp_response, "(J)[B"), NATIVE_METHOD(NativeCrypto, SSL_CTX_set_ocsp_response, "(J[B)V"), NATIVE_METHOD(NativeCrypto, SSL_use_psk_identity_hint, "(JLjava/lang/String;)V"), NATIVE_METHOD(NativeCrypto, set_SSL_psk_client_callback_enabled, "(JZ)V"), NATIVE_METHOD(NativeCrypto, set_SSL_psk_server_callback_enabled, "(JZ)V"), NATIVE_METHOD(NativeCrypto, SSL_set_cipher_lists, "(J[Ljava/lang/String;)V"), NATIVE_METHOD(NativeCrypto, SSL_get_ciphers, "(J)[J"), NATIVE_METHOD(NativeCrypto, SSL_set_accept_state, "(J)V"), NATIVE_METHOD(NativeCrypto, SSL_set_connect_state, "(J)V"), NATIVE_METHOD(NativeCrypto, SSL_set_verify, "(JI)V"), NATIVE_METHOD(NativeCrypto, SSL_set_session, "(JJ)V"), NATIVE_METHOD(NativeCrypto, SSL_set_session_creation_enabled, "(JZ)V"), NATIVE_METHOD(NativeCrypto, SSL_session_reused, "(J)Z"), NATIVE_METHOD(NativeCrypto, SSL_set_reject_peer_renegotiations, "(JZ)V"), NATIVE_METHOD(NativeCrypto, SSL_set_tlsext_host_name, "(JLjava/lang/String;)V"), NATIVE_METHOD(NativeCrypto, SSL_get_servername, "(J)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, SSL_do_handshake, "(J" FILE_DESCRIPTOR SSL_CALLBACKS "IZ[B[B)J"), NATIVE_METHOD(NativeCrypto, SSL_do_handshake_bio, "(JJJ" SSL_CALLBACKS "Z[B[B)J"), NATIVE_METHOD(NativeCrypto, SSL_renegotiate, "(J)V"), NATIVE_METHOD(NativeCrypto, SSL_get_certificate, "(J)[J"), NATIVE_METHOD(NativeCrypto, SSL_get_peer_cert_chain, "(J)[J"), NATIVE_METHOD(NativeCrypto, SSL_read, "(J" FILE_DESCRIPTOR SSL_CALLBACKS "[BIII)I"), NATIVE_METHOD(NativeCrypto, SSL_read_BIO, "(J[BIIJJ" SSL_CALLBACKS ")I"), NATIVE_METHOD(NativeCrypto, SSL_write, "(J" FILE_DESCRIPTOR SSL_CALLBACKS "[BIII)V"), NATIVE_METHOD(NativeCrypto, SSL_write_BIO, "(J[BIJ" SSL_CALLBACKS ")I"), NATIVE_METHOD(NativeCrypto, SSL_interrupt, "(J)V"), NATIVE_METHOD(NativeCrypto, SSL_shutdown, "(J" FILE_DESCRIPTOR SSL_CALLBACKS ")V"), NATIVE_METHOD(NativeCrypto, SSL_shutdown_BIO, "(JJJ" SSL_CALLBACKS ")V"), NATIVE_METHOD(NativeCrypto, SSL_get_shutdown, "(J)I"), NATIVE_METHOD(NativeCrypto, SSL_free, "(J)V"), NATIVE_METHOD(NativeCrypto, SSL_SESSION_session_id, "(J)[B"), NATIVE_METHOD(NativeCrypto, SSL_SESSION_get_time, "(J)J"), NATIVE_METHOD(NativeCrypto, SSL_SESSION_get_version, "(J)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, SSL_SESSION_cipher, "(J)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, get_SSL_SESSION_tlsext_hostname, "(J)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, SSL_SESSION_free, "(J)V"), NATIVE_METHOD(NativeCrypto, i2d_SSL_SESSION, "(J)[B"), NATIVE_METHOD(NativeCrypto, d2i_SSL_SESSION, "([B)J"), NATIVE_METHOD(NativeCrypto, SSL_CTX_enable_npn, "(J)V"), NATIVE_METHOD(NativeCrypto, SSL_CTX_disable_npn, "(J)V"), NATIVE_METHOD(NativeCrypto, SSL_get_npn_negotiated_protocol, "(J)[B"), NATIVE_METHOD(NativeCrypto, SSL_set_alpn_protos, "(J[B)I"), NATIVE_METHOD(NativeCrypto, SSL_get0_alpn_selected, "(J)[B"), NATIVE_METHOD(NativeCrypto, ERR_peek_last_error, "()J"), NATIVE_METHOD(NativeCrypto, SSL_CIPHER_get_kx_name, "(J)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, get_cipher_names, "(Ljava/lang/String;)[Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, get_ocsp_single_extension, "([BLjava/lang/String;JJ)[B"), NATIVE_METHOD(NativeCrypto, getDirectBufferAddress, "(Ljava/nio/Buffer;)J"), }; static jclass getGlobalRefToClass(JNIEnv* env, const char* className) { ScopedLocalRef<jclass> localClass(env, env->FindClass(className)); jclass globalRef = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get())); if (globalRef == nullptr) { ALOGE("failed to find class %s", className); abort(); } return globalRef; } static jmethodID getMethodRef(JNIEnv* env, jclass clazz, const char* name, const char* sig) { jmethodID localMethod = env->GetMethodID(clazz, name, sig); if (localMethod == nullptr) { ALOGE("could not find method %s", name); abort(); } return localMethod; } static jfieldID getFieldRef(JNIEnv* env, jclass clazz, const char* name, const char* sig) { jfieldID localField = env->GetFieldID(clazz, name, sig); if (localField == nullptr) { ALOGE("could not find field %s", name); abort(); } return localField; } static void initialize_conscrypt(JNIEnv* env) { jniRegisterNativeMethods(env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeCrypto", sNativeCryptoMethods, NELEM(sNativeCryptoMethods)); cryptoUpcallsClass = getGlobalRefToClass(env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/CryptoUpcalls"); nativeRefClass = getGlobalRefToClass(env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef"); openSslInputStreamClass = getGlobalRefToClass(env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLBIOInputStream"); nativeRef_context = getFieldRef(env, nativeRefClass, "context", "J"); calendar_setMethod = getMethodRef(env, calendarClass, "set", "(IIIIII)V"); inputStream_readMethod = getMethodRef(env, inputStreamClass, "read", "([B)I"); integer_valueOfMethod = env->GetStaticMethodID(integerClass, "valueOf", "(I)Ljava/lang/Integer;"); openSslInputStream_readLineMethod = getMethodRef(env, openSslInputStreamClass, "gets", "([B)I"); outputStream_writeMethod = getMethodRef(env, outputStreamClass, "write", "([B)V"); outputStream_flushMethod = getMethodRef(env, outputStreamClass, "flush", "()V"); #ifdef CONSCRYPT_UNBUNDLED findAsynchronousCloseMonitorFuncs(); #endif } static jclass findClass(JNIEnv* env, const char* name) { ScopedLocalRef<jclass> localClass(env, env->FindClass(name)); jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get())); if (result == nullptr) { ALOGE("failed to find class '%s'", name); abort(); } return result; } #ifdef STATIC_LIB // Give client libs everything they need to initialize our JNI int libconscrypt_JNI_OnLoad(JavaVM *vm, void*) { #else // Use JNI_OnLoad for when we're standalone int JNI_OnLoad(JavaVM *vm, void*) { JNI_TRACE("JNI_OnLoad NativeCrypto"); #endif gJavaVM = vm; JNIEnv *env; if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) { ALOGE("Could not get JNIEnv"); return JNI_ERR; } byteArrayClass = findClass(env, "[B"); calendarClass = findClass(env, "java/util/Calendar"); inputStreamClass = findClass(env, "java/io/InputStream"); integerClass = findClass(env, "java/lang/Integer"); objectClass = findClass(env, "java/lang/Object"); objectArrayClass = findClass(env, "[Ljava/lang/Object;"); outputStreamClass = findClass(env, "java/io/OutputStream"); stringClass = findClass(env, "java/lang/String"); initialize_conscrypt(env); return JNI_VERSION_1_6; } /* vim: softtabstop=4:shiftwidth=4:expandtab */ /* Local Variables: */ /* mode: c++ */ /* tab-width: 4 */ /* indent-tabs-mode: nil */ /* c-basic-offset: 4 */ /* End: */