blob: df78810c4a9063a135f86e1450141c703e0810cd [file] [log] [blame]
/*
* 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 <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)
#include "crypto/ecdsa/ecs_locl.h"
#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 DH_Delete {
void operator()(DH* p) const {
DH_free(p);
}
};
typedef UniquePtr<DH, DH_Delete> Unique_DH;
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;
#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(NULL);
}
/**
* 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);
}
#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 == NULL) {
message = "SSL error";
}
// First consult the SSL error code for the general message.
const char* sslErrorStr = NULL;
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 == NULL) ? "" : 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 == NULL) && 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 == NULL) && 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 == NULL) && 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 == NULL) && 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 == NULL) {
JNI_TRACE("contextObject == null");
jniThrowNullPointerException(env, "contextObject == null");
return NULL;
}
T* ref = reinterpret_cast<T*>(env->GetLongField(contextObject, nativeRef_context));
if (ref == NULL) {
JNI_TRACE("ref == null");
jniThrowNullPointerException(env, "ref == null");
return NULL;
}
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 == NULL) {
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() == NULL) {
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 == NULL) {
*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 == NULL) {
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() == NULL) {
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 == NULL) {
jniThrowNullPointerException(env, sourceName);
return NULL;
}
size_t numBytes = BN_num_bytes(source) + 1;
jbyteArray javaBytes = env->NewByteArray(numBytes);
ScopedByteArrayRW bytes(env, javaBytes);
if (bytes.get() == NULL) {
JNI_TRACE("bignumToArray(%p, %s) => NULL", source, sourceName);
return NULL;
}
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 NULL;
}
// 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 == NULL) {
jniThrowNullPointerException(env, "ASN1 input == null");
JNI_TRACE("ASN1ToByteArray(%p) => null input", obj);
return NULL;
}
int derLen = i2d_func(obj, NULL);
if (derLen < 0) {
throwExceptionIfNecessary(env, "ASN1ToByteArray");
JNI_TRACE("ASN1ToByteArray(%p) => measurement failed", obj);
return NULL;
}
ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(derLen));
if (byteArray.get() == NULL) {
JNI_TRACE("ASN1ToByteArray(%p) => creating byte array failed", obj);
return NULL;
}
ScopedByteArrayRW bytes(env, byteArray.get());
if (bytes.get() == NULL) {
JNI_TRACE("ASN1ToByteArray(%p) => using byte array failed", obj);
return NULL;
}
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 NULL;
}
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() == NULL) {
JNI_TRACE("ByteArrayToASN1(%p) => using byte array failed", byteArray);
return 0;
}
const unsigned char* tmp = reinterpret_cast<const unsigned char*>(bytes.get());
return d2i_func(NULL, &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() == NULL) {
return NULL;
}
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 == NULL) {
return NULL;
}
#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_, NULL, NULL);
}
private:
SSL* const ssl_;
};
/**
* Obtains the current thread's JNIEnv
*/
static JNIEnv* getJNIEnv() {
JNIEnv* env;
#ifdef ANDROID
if (gJavaVM->AttachCurrentThread(&env, NULL) < 0) {
#else
if (gJavaVM->AttachCurrentThread(reinterpret_cast<void**>(&env), NULL) < 0) {
#endif
ALOGE("Could not attach JavaVM to find current JNIEnv");
return NULL;
}
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 == NULL) {
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 == NULL) {
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() == NULL) {
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 == NULL) {
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() == NULL) {
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 = NULL;
b->flags = 0;
return 1;
}
static int bio_stream_destroy(BIO *b) {
if (b == NULL) {
return 0;
}
if (b->ptr != NULL) {
delete static_cast<BIO_Stream*>(b->ptr);
b->ptr = NULL;
}
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 */
NULL, /* 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 NULL;
}
{
ScopedByteArrayRW messageBytes(env, messageArray.get());
if (messageBytes.get() == NULL) {
JNI_TRACE("rawSignDigestWithPrivateKey(%p) => using byte array failed", privateKey);
return NULL;
}
memcpy(messageBytes.get(), message, message_len);
}
jmethodID rawSignMethod = env->GetStaticMethodID(cryptoUpcallsClass,
"rawSignDigestWithPrivateKey", "(Ljava/security/PrivateKey;[B)[B");
if (rawSignMethod == NULL) {
ALOGE("Could not find rawSignDigestWithPrivateKey");
return NULL;
}
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 NULL;
}
{
ScopedByteArrayRW ciphertextBytes(env, ciphertextArray.get());
if (ciphertextBytes.get() == NULL) {
JNI_TRACE("rsaDecryptWithPrivateKey(%p) => using byte array failed", privateKey);
return NULL;
}
memcpy(ciphertextBytes.get(), ciphertext, ciphertext_len);
}
jmethodID rsaDecryptMethod = env->GetStaticMethodID(cryptoUpcallsClass,
"rsaDecryptWithPrivateKey", "(Ljava/security/PrivateKey;I[B)[B");
if (rsaDecryptMethod == NULL) {
ALOGE("Could not find rsaDecryptWithPrivateKey");
return NULL;
}
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)
const void* /* argp */) {
#else /* defined(OPENSSL_IS_BORINGSSL) */
void* /* argp */) {
#endif /* defined(OPENSSL_IS_BORINGSSL) */
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)
const void* /* argp */) {
#else /* defined(OPENSSL_IS_BORINGSSL) */
void* /* argp */) {
#endif /* defined(OPENSSL_IS_BORINGSSL) */
// 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 != NULL) {
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;
}
#if defined(BORINGSSL_201509)
// Newer versions of BoringSSL have dropped the function code. */
#undef OPENSSL_PUT_ERROR
#define OPENSSL_PUT_ERROR(library, func, reason) \
ERR_put_error(ERR_LIB_##library, reason, OPENSSL_CURRENT_FUNCTION, __FILE__, __LINE__)
#endif
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, encrypt, 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, sign_raw, 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, sign_raw, ERR_R_INTERNAL_ERROR);
return 0;
}
JNIEnv* env = getJNIEnv();
if (env == NULL) {
OPENSSL_PUT_ERROR(RSA, sign_raw, 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() == NULL) {
OPENSSL_PUT_ERROR(RSA, sign_raw, 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, sign_raw, ERR_R_INTERNAL_ERROR);
return 0;
}
if (max_out < expected_size) {
OPENSSL_PUT_ERROR(RSA, sign_raw, 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, decrypt, ERR_R_INTERNAL_ERROR);
return 0;
}
JNIEnv* env = getJNIEnv();
if (env == NULL) {
OPENSSL_PUT_ERROR(RSA, decrypt, 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() == NULL) {
OPENSSL_PUT_ERROR(RSA, decrypt, ERR_R_INTERNAL_ERROR);
return 0;
}
ScopedByteArrayRO cleartextBytes(env, cleartext.get());
if (max_out < cleartextBytes.size()) {
OPENSSL_PUT_ERROR(RSA, decrypt, 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, verify_raw, RSA_R_UNKNOWN_ALGORITHM_TYPE);
return 0;
}
const RSA_METHOD android_rsa_method = {
{
0 /* references */,
1 /* is_static */
} /* common */,
NULL /* app_data */,
NULL /* init */,
NULL /* finish */,
RsaMethodSize,
NULL /* sign */,
NULL /* verify */,
RsaMethodEncrypt,
RsaMethodSignRaw,
RsaMethodDecrypt,
RsaMethodVerifyRaw,
NULL /* mod_exp */,
NULL /* bn_mod_exp */,
NULL /* private_transform */,
RSA_FLAG_OPAQUE,
NULL /* keygen */,
#if defined(BORINGSSL_201509)
NULL /* multi_prime_keygen */,
#endif
NULL /* 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 == NULL) {
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() == NULL) {
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_do_verify, ECDSA_R_NOT_IMPLEMENTED);
return 0;
}
const ECDSA_METHOD android_ecdsa_method = {
{
0 /* references */,
1 /* is_static */
} /* common */,
NULL /* app_data */,
NULL /* init */,
NULL /* finish */,
EcdsaMethodGroupOrderSize,
EcdsaMethodSign,
EcdsaMethodVerify,
ECDSA_FLAG_OPAQUE,
};
void init_engine_globals() {
g_rsa_exdata_index =
RSA_get_ex_new_index(0 /* argl */, NULL /* argp */, NULL /* new_func */,
ExDataDup, ExDataFree);
g_ecdsa_exdata_index =
EC_KEY_get_ex_new_index(0 /* argl */, NULL /* argp */,
NULL /* 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 = NULL;
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]);
}
}
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(NULL);
CRYPTO_set_locking_callback(NULL);
for (int i = 0; i < CRYPTO_num_locks( ); i++) {
MUTEX_CLEANUP(mutex_buf[i]);
}
free(mutex_buf);
mutex_buf = NULL;
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() == NULL) {
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() == NULL) {
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
static jlong NativeCrypto_EVP_PKEY_new_DH(JNIEnv* env, jclass,
jbyteArray p, jbyteArray g,
jbyteArray pub_key, jbyteArray priv_key) {
JNI_TRACE("EVP_PKEY_new_DH(p=%p, g=%p, pub_key=%p, priv_key=%p)",
p, g, pub_key, priv_key);
Unique_DH dh(DH_new());
if (dh.get() == NULL) {
jniThrowRuntimeException(env, "DH_new failed");
return 0;
}
if (!arrayToBignum(env, p, &dh->p)) {
return 0;
}
if (!arrayToBignum(env, g, &dh->g)) {
return 0;
}
if (pub_key != NULL && !arrayToBignum(env, pub_key, &dh->pub_key)) {
return 0;
}
if (priv_key != NULL && !arrayToBignum(env, priv_key, &dh->priv_key)) {
return 0;
}
if (dh->p == NULL || dh->g == NULL
|| (pub_key != NULL && dh->pub_key == NULL)
|| (priv_key != NULL && dh->priv_key == NULL)) {
jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
return 0;
}
/* The public key can be recovered if the private key is available. */
if (dh->pub_key == NULL && dh->priv_key != NULL) {
if (!DH_generate_key(dh.get())) {
jniThrowRuntimeException(env, "EVP_PKEY_new_DH failed during pub_key generation");
return 0;
}
}
Unique_EVP_PKEY pkey(EVP_PKEY_new());
if (pkey.get() == NULL) {
jniThrowRuntimeException(env, "EVP_PKEY_new failed");
return 0;
}
if (EVP_PKEY_assign_DH(pkey.get(), dh.get()) != 1) {
jniThrowRuntimeException(env, "EVP_PKEY_assign_DH failed");
return 0;
}
OWNERSHIP_TRANSFERRED(dh);
JNI_TRACE("EVP_PKEY_new_DH(p=%p, g=%p, pub_key=%p, priv_key=%p) => %p",
p, g, pub_key, priv_key, pkey.get());
return reinterpret_cast<jlong>(pkey.release());
}
/**
* 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() == NULL) {
jniThrowRuntimeException(env, "RSA_new failed");
return 0;
}
if (e == NULL && d == NULL) {
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 != NULL && !arrayToBignum(env, e, &rsa->e)) {
return 0;
}
if (d != NULL && !arrayToBignum(env, d, &rsa->d)) {
return 0;
}
if (p != NULL && !arrayToBignum(env, p, &rsa->p)) {
return 0;
}
if (q != NULL && !arrayToBignum(env, q, &rsa->q)) {
return 0;
}
if (dmp1 != NULL && !arrayToBignum(env, dmp1, &rsa->dmp1)) {
return 0;
}
if (dmq1 != NULL && !arrayToBignum(env, dmq1, &rsa->dmq1)) {
return 0;
}
if (iqmp != NULL && !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 == NULL || (rsa->e == NULL && rsa->d == NULL)) {
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 != NULL && rsa->e == NULL) {
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() == NULL) {
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 == NULL) {
return 0;
}
const EC_POINT* pubkey = pubkeyRef == NULL ? NULL :
fromContextObject<EC_POINT>(env, pubkeyRef);
JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) <- ptr", group, pubkey, keyJavaBytes);
Unique_BIGNUM key(NULL);
if (keyJavaBytes != NULL) {
BIGNUM* keyRef = NULL;
if (!arrayToBignum(env, keyJavaBytes, &keyRef)) {
return 0;
}
key.reset(keyRef);
}
Unique_EC_KEY eckey(EC_KEY_new());
if (eckey.get() == NULL) {
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