blob: af856117bcaa595e30344a77b927bbd7f27fdb13 [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 <unistd.h>
#ifdef CONSCRYPT_UNBUNDLED
#include <dlfcn.h>
#endif
#include <jni.h>
#include <openssl/asn1t.h>
#include <openssl/dsa.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include "crypto/ecdsa/ecs_locl.h"
#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 <android/log.h>
#define ALOG(priority, tag, ...) \
__android_log_print(ANDROID_##priority, tag, __VA_ARGS__)
#define ALOGD(...) \
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define ALOGE(...) \
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define ALOGV(...) \
__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#endif
#ifndef CONSCRYPT_UNBUNDLED
#include "JNIHelp.h"
#include "JniConstants.h"
#include "JniException.h"
#else
#define NATIVE_METHOD(className, functionName, signature) \
{ #functionName, 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"
#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 openSslNativeReferenceClass;
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 openSslNativeReference_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 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_clear_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;
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 PKCS7_Delete {
void operator()(PKCS7* p) const {
PKCS7_free(p);
}
};
typedef UniquePtr<PKCS7, PKCS7_Delete> Unique_PKCS7;
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;
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)
/**
* 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_state(0);
}
/**
* 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 void jniThrowOutOfMemory(JNIEnv* env, const char* message) {
jniThrowException(env, "java/lang/OutOfMemoryError", message);
}
/**
* Throws a BadPaddingException with the given string as a message.
*/
static void throwBadPaddingException(JNIEnv* env, const char* message) {
JNI_TRACE("throwBadPaddingException %s", message);
jniThrowException(env, "javax/crypto/BadPaddingException", message);
}
/**
* Throws a SignatureException with the given string as a message.
*/
static void throwSignatureException(JNIEnv* env, const char* message) {
JNI_TRACE("throwSignatureException %s", message);
jniThrowException(env, "java/security/SignatureException", message);
}
/**
* Throws a InvalidKeyException with the given string as a message.
*/
static void throwInvalidKeyException(JNIEnv* env, const char* message) {
JNI_TRACE("throwInvalidKeyException %s", message);
jniThrowException(env, "java/security/InvalidKeyException", message);
}
/**
* Throws a SignatureException with the given string as a message.
*/
static void throwIllegalBlockSizeException(JNIEnv* env, const char* message) {
JNI_TRACE("throwIllegalBlockSizeException %s", message);
jniThrowException(env, "javax/crypto/IllegalBlockSizeException", message);
}
/**
* Throws a NoSuchAlgorithmException with the given string as a message.
*/
static void throwNoSuchAlgorithmException(JNIEnv* env, const char* message) {
JNI_TRACE("throwUnknownAlgorithmException %s", message);
jniThrowException(env, "java/security/NoSuchAlgorithmException", message);
}
static void throwForAsn1Error(JNIEnv* env, int reason, const char *message) {
switch (reason) {
case ASN1_R_UNABLE_TO_DECODE_RSA_KEY:
case ASN1_R_UNABLE_TO_DECODE_RSA_PRIVATE_KEY:
case ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE:
case ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE:
case ASN1_R_WRONG_PUBLIC_KEY_TYPE:
throwInvalidKeyException(env, message);
break;
case ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM:
throwNoSuchAlgorithmException(env, message);
break;
default:
jniThrowRuntimeException(env, message);
break;
}
}
static void throwForEvpError(JNIEnv* env, int reason, const char *message) {
switch (reason) {
case EVP_R_BAD_DECRYPT:
throwBadPaddingException(env, message);
break;
case EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
case EVP_R_WRONG_FINAL_BLOCK_LENGTH:
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:
throwInvalidKeyException(env, message);
break;
case EVP_R_WRONG_PUBLIC_KEY_TYPE:
throwSignatureException(env, message);
break;
case EVP_R_UNSUPPORTED_ALGORITHM:
throwNoSuchAlgorithmException(env, message);
break;
default:
jniThrowRuntimeException(env, message);
break;
}
}
static void throwForRsaError(JNIEnv* env, int reason, const char *message) {
switch (reason) {
case RSA_R_BLOCK_TYPE_IS_NOT_01:
case RSA_R_BLOCK_TYPE_IS_NOT_02:
case RSA_R_PKCS_DECODING_ERROR:
throwBadPaddingException(env, message);
break;
case RSA_R_ALGORITHM_MISMATCH:
case RSA_R_BAD_SIGNATURE:
case RSA_R_DATA_GREATER_THAN_MOD_LEN:
case RSA_R_DATA_TOO_LARGE_FOR_MODULUS:
case RSA_R_INVALID_MESSAGE_LENGTH:
case RSA_R_WRONG_SIGNATURE_LENGTH:
throwSignatureException(env, message);
break;
case RSA_R_UNKNOWN_ALGORITHM_TYPE:
throwNoSuchAlgorithmException(env, message);
break;
case RSA_R_MODULUS_TOO_LARGE:
case RSA_R_NO_PUBLIC_EXPONENT:
throwInvalidKeyException(env, message);
break;
default:
jniThrowRuntimeException(env, message);
break;
}
}
static void throwForX509Error(JNIEnv* env, int reason, const char *message) {
switch (reason) {
case X509_R_UNSUPPORTED_ALGORITHM:
throwNoSuchAlgorithmException(env, message);
break;
default:
jniThrowRuntimeException(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))) {
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);
break;
case ERR_LIB_ASN1:
throwForAsn1Error(env, reason, message);
break;
case ERR_LIB_EVP:
throwForEvpError(env, reason, message);
break;
case ERR_LIB_X509:
throwForX509Error(env, reason, message);
break;
case ERR_LIB_DSA:
throwInvalidKeyException(env, message);
break;
default:
jniThrowRuntimeException(env, message);
break;
}
result = true;
}
freeOpenSslErrorState();
return result;
}
/**
* Throws an SocketTimeoutException with the given string as a message.
*/
static void throwSocketTimeoutException(JNIEnv* env, const char* message) {
JNI_TRACE("throwSocketTimeoutException %s", message);
jniThrowException(env, "java/net/SocketTimeoutException", message);
}
/**
* Throws a javax.net.ssl.SSLException with the given string as a message.
*/
static void throwSSLHandshakeExceptionStr(JNIEnv* env, const char* message) {
JNI_TRACE("throwSSLExceptionStr %s", message);
jniThrowException(env, "javax/net/ssl/SSLHandshakeException", message);
}
/**
* Throws a javax.net.ssl.SSLException with the given string as a message.
*/
static void throwSSLExceptionStr(JNIEnv* env, const char* message) {
JNI_TRACE("throwSSLExceptionStr %s", message);
jniThrowException(env, "javax/net/ssl/SSLException", message);
}
/**
* Throws a javax.net.ssl.SSLProcotolException with the given string as a message.
*/
static void throwSSLProtocolExceptionStr(JNIEnv* env, const char* message) {
JNI_TRACE("throwSSLProtocolExceptionStr %s", message);
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 void throwSSLExceptionWithSslErrors(JNIEnv* env, SSL* ssl, int sslErrorCode,
const char* message, void (*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
actualThrow(env, message);
ALOGV("%s: ssl=%p: %s", message, ssl, sslErrorStr);
freeOpenSslErrorState();
return;
}
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;
}
}
if (sslErrorCode == SSL_ERROR_SSL) {
throwSSLProtocolExceptionStr(env, allocStr);
} else {
actualThrow(env, allocStr);
}
ALOGV("%s", allocStr);
free(allocStr);
freeOpenSslErrorState();
}
/**
* 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) {
T* ref = reinterpret_cast<T*>(env->GetLongField(contextObject, openSslNativeReference_context));
if (ref == NULL) {
JNI_TRACE("ctx == null");
jniThrowNullPointerException(env, "ctx == 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;
}
/**
* 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, int (*i2d_func)(T*, unsigned char**)>
jbyteArray ASN1ToByteArray(JNIEnv* env, T* obj) {
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;
}
CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509);
return x509;
}
/*
* 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);
CRYPTO_add(&rbio->references,1,CRYPTO_LOCK_BIO);
CRYPTO_add(&wbio->references,1,CRYPTO_LOCK_BIO);
}
~ScopedSslBio() {
SSL_set_bio(ssl_, NULL, NULL);
}
private:
SSL* const ssl_;
};
/**
* Obtains the current thread's JNIEnv
*/
static JNIEnv* getJNIEnv() {
JNIEnv* env;
if (gJavaVM->AttachCurrentThread(&env, NULL) < 0) {
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) :
BIO_Stream(stream) {
}
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;
}
private:
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) {
// EOF is indicated by -1 with a BIO flag.
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()));
}
static jbyteArray rawCipherWithPrivateKey(JNIEnv* env, jobject privateKey, jboolean encrypt,
const char* ciphertext, size_t ciphertext_len) {
ScopedLocalRef<jbyteArray> ciphertextArray(env, env->NewByteArray(ciphertext_len));
if (env->ExceptionCheck()) {
JNI_TRACE("rawCipherWithPrivateKey(%p) => threw exception", privateKey);
return NULL;
}
{
ScopedByteArrayRW ciphertextBytes(env, ciphertextArray.get());
if (ciphertextBytes.get() == NULL) {
JNI_TRACE("rawCipherWithPrivateKey(%p) => using byte array failed", privateKey);
return NULL;
}
memcpy(ciphertextBytes.get(), ciphertext, ciphertext_len);
}
jmethodID rawCipherMethod = env->GetStaticMethodID(cryptoUpcallsClass,
"rawCipherWithPrivateKey", "(Ljava/security/PrivateKey;Z[B)[B");
if (rawCipherMethod == NULL) {
ALOGE("Could not find rawCipherWithPrivateKey");
return NULL;
}
return reinterpret_cast<jbyteArray>(env->CallStaticObjectMethod(
cryptoUpcallsClass, rawCipherMethod, privateKey, encrypt, ciphertextArray.get()));
}
// *********************************************
// From keystore_openssl.cpp in Chromium source.
// *********************************************
// 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) {
if (padding != RSA_PKCS1_PADDING) {
RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, 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 RsaMethodPrivDec!");
RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, ERR_R_INTERNAL_ERROR);
return -1;
}
JNIEnv* env = getJNIEnv();
if (env == NULL) {
return -1;
}
// For RSA keys, this function behaves as RSA_private_decrypt with
// PKCS#1 padding.
ScopedLocalRef<jbyteArray> cleartext(env, rawCipherWithPrivateKey(env, private_key, false,
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,
};
// Custom DSA_METHOD that uses the platform APIs.
// Note that for now, only signing through DSA_sign() is really supported.
// all other method pointers are either stubs returning errors, or no-ops.
// See <openssl/dsa.h> for exact declaration of DSA_METHOD.
//
// Note: There is no DSA_set_app_data() and DSA_get_app_data() functions,
// but RSA_set_app_data() is defined as a simple macro that calls
// RSA_set_ex_data() with a hard-coded index of 0, so this code
// does the same thing here.
DSA_SIG* DsaMethodDoSign(const unsigned char* dgst,
int dlen,
DSA* dsa) {
// Extract the JNI reference to the PrivateKey object.
jobject private_key = reinterpret_cast<jobject>(DSA_get_ex_data(dsa, 0));
if (private_key == NULL) return NULL;
JNIEnv* env = getJNIEnv();
if (env == NULL) {
return NULL;
}
// Sign the message with it, calling platform APIs.
ScopedLocalRef<jbyteArray> signature(
env, rawSignDigestWithPrivateKey(env, private_key, reinterpret_cast<const char*>(dgst),
dlen));
if (signature.get() == NULL) {
ALOGE("Could not sign message in DsaMethodDoSign!");
return NULL;
}
ScopedByteArrayRO signatureBytes(env, signature.get());
// Note: With DSA, the actual signature might be smaller than DSA_size().
size_t max_expected_size = static_cast<size_t>(DSA_size(dsa));
if (signatureBytes.size() > max_expected_size) {
ALOGE("DSA Signature size mismatch, actual: %zd, expected <= %zd", signatureBytes.size(),
max_expected_size);
return NULL;
}
// Convert the signature into a DSA_SIG object.
const unsigned char* sigbuf = reinterpret_cast<const unsigned char*>(signatureBytes.get());
int siglen = static_cast<size_t>(signatureBytes.size());
DSA_SIG* dsa_sig = d2i_DSA_SIG(NULL, &sigbuf, siglen);
return dsa_sig;
}
int DsaMethodSignSetup(DSA* /* dsa */,
BN_CTX* /* ctx_in */,
BIGNUM** /* kinvp */,
BIGNUM** /* rp */,
const unsigned char* /* dgst */,
int /* dlen */) {
DSAerr(DSA_F_DSA_SIGN_SETUP, DSA_R_INVALID_DIGEST_TYPE);
return -1;
}
int DsaMethodDoVerify(const unsigned char* /* dgst */,
int /* dgst_len */,
DSA_SIG* /* sig */,
DSA* /* dsa */) {
DSAerr(DSA_F_DSA_DO_VERIFY, DSA_R_INVALID_DIGEST_TYPE);
return -1;
}
int DsaMethodFinish(DSA* dsa) {
// Free the global JNI reference that was created with this
// wrapper key.
jobject key = reinterpret_cast<jobject>(DSA_get_ex_data(dsa, 0));
if (key != NULL) {
DSA_set_ex_data(dsa, 0, 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 DSA_METHOD android_dsa_method = {
/* .name = */ "Android signing-only DSA method",
/* .dsa_do_sign = */ DsaMethodDoSign,
/* .dsa_sign_setup = */ DsaMethodSignSetup,
/* .dsa_do_verify = */ DsaMethodDoVerify,
/* .dsa_mod_exp = */ NULL,
/* .bn_mod_exp = */ NULL,
/* .init = */ NULL, // nothing to do here.
/* .finish = */ DsaMethodFinish,
/* .flags = */ 0,
/* .app_data = */ NULL,
/* .dsa_paramgem = */ NULL,
/* .dsa_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 */,
void* /* argp */) {
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 */,
void* /* argp */) {
// 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,
};
#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 unsigned long id_function(void) {
return ((unsigned long)THREAD_ID);
}
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_set_id_callback(id_function);
CRYPTO_set_locking_callback(locking_function);
return 1;
}
int THREAD_cleanup(void) {
if (!mutex_buf) {
return 0;
}
CRYPTO_set_id_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 void NativeCrypto_clinit(JNIEnv*, jclass)
{
SSL_load_error_strings();
ERR_load_crypto_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
THREAD_setup();
}
static void NativeCrypto_ENGINE_load_dynamic(JNIEnv*, jclass) {
JNI_TRACE("ENGINE_load_dynamic()");
ENGINE_load_dynamic();
}
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);
}
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;
}
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;
}
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;
}
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;
}
static jlong NativeCrypto_ENGINE_load_private_key(JNIEnv* env, jclass, jlong engineRef,
jstring idJava) {
ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef));
JNI_TRACE("ENGINE_load_private_key(%p, %p)", e, idJava);
ScopedUtfChars id(env, idJava);
if (id.c_str() == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", "id == NULL");
return 0;
}
Unique_EVP_PKEY pkey(ENGINE_load_private_key(e, id.c_str(), NULL, NULL));
if (pkey.get() == NULL) {
throwExceptionIfNecessary(env, "ENGINE_load_private_key");
return 0;
}
JNI_TRACE("ENGINE_load_private_key(%p, %p) => %p", e, idJava, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
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();
}
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;
}
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());
}
/**
* public static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g,
* byte[] pub_key, byte[] priv_key);
*/
static jlong NativeCrypto_EVP_PKEY_new_DSA(JNIEnv* env, jclass,
jbyteArray p, jbyteArray q, jbyteArray g,
jbyteArray pub_key, jbyteArray priv_key) {
JNI_TRACE("EVP_PKEY_new_DSA(p=%p, q=%p, g=%p, pub_key=%p, priv_key=%p)",
p, q, g, pub_key, priv_key);
Unique_DSA dsa(DSA_new());
if (dsa.get() == NULL) {
jniThrowRuntimeException(env, "DSA_new failed");
return 0;
}
if (!arrayToBignum(env, p, &dsa->p)) {
return 0;
}
if (!arrayToBignum(env, q, &dsa->q)) {
return 0;
}
if (!arrayToBignum(env, g, &dsa->g)) {
return 0;
}
if (pub_key != NULL && !arrayToBignum(env, pub_key, &dsa->pub_key)) {
return 0;
}
if (priv_key != NULL && !arrayToBignum(env, priv_key, &dsa->priv_key)) {
return 0;
}
if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL
|| (dsa->pub_key == NULL && dsa->priv_key == NULL)) {
jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
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_DSA(pkey.get(), dsa.get()) != 1) {
jniThrowRuntimeException(env, "EVP_PKEY_assign_DSA failed");
return 0;
}
OWNERSHIP_TRANSFERRED(dsa);
JNI_TRACE("EVP_PKEY_new_DSA(p=%p, q=%p, g=%p, pub_key=%p, priv_key=%p) => %p",
p, q, 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. If the public exponent is also available, OpenSSL will do RSA
* blinding. Enable it if possible.
*/
if (rsa->d != NULL) {
if (rsa->e != NULL) {
JNI_TRACE("EVP_PKEY_new_RSA(...) enabling RSA blinding => %p", rsa.get());
RSA_blinding_on(rsa.get(), NULL);
} else {
JNI_TRACE("EVP_PKEY_new_RSA(...) disabling RSA blinding => %p", rsa.get());
RSA_blinding_off(rsa.get());
}
}
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, jlong groupRef,
jlong pubkeyRef, jbyteArray keyJavaBytes) {
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
const EC_POINT* pubkey = reinterpret_cast<const EC_POINT*>(pubkeyRef);
JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p)", 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);
throwExceptionIfNecessary(env, "EC_KEY_set_group");
return 0;
}
if (pubkey != NULL) {
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() != NULL) {
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 == NULL) {
Unique_EC_POINT calcPubkey(EC_POINT_new(group));
if (!EC_POINT_mul(group, calcPubkey.get(), key.get(), NULL, NULL, NULL)) {
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() == NULL) {
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 jlong NativeCrypto_EVP_PKEY_new_mac_key(JNIEnv* env, jclass, jint pkeyType,
jbyteArray keyJavaBytes)
{
JNI_TRACE("EVP_PKEY_new_mac_key(%d, %p)", pkeyType, keyJavaBytes);
ScopedByteArrayRO key(env, keyJavaBytes);
if (key.get() == NULL) {
return 0;
}
const unsigned char* tmp = reinterpret_cast<const unsigned char*>(key.get());
Unique_EVP_PKEY pkey(EVP_PKEY_new_mac_key(pkeyType, (ENGINE *) NULL, tmp, key.size()));
if (pkey.get() == NULL) {
JNI_TRACE("EVP_PKEY_new_mac_key(%d, %p) => threw error", pkeyType, keyJavaBytes);
throwExceptionIfNecessary(env, "ENGINE_load_private_key");
return 0;
}
JNI_TRACE("EVP_PKEY_new_mac_key(%d, %p) => %p", pkeyType, keyJavaBytes, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
static int NativeCrypto_EVP_PKEY_type(JNIEnv* env, jclass, jlong pkeyRef) {
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("EVP_PKEY_type(%p)", pkey);
if (pkey == NULL) {
jniThrowNullPointerException(env, NULL);
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, jlong pkeyRef) {
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("EVP_PKEY_size(%p)", pkey);
if (pkey == NULL) {
jniThrowNullPointerException(env, NULL);
return -1;
}
int result = EVP_PKEY_size(pkey);
JNI_TRACE("EVP_PKEY_size(%p) => %d", pkey, result);
return result;
}
static jstring NativeCrypto_EVP_PKEY_print_public(JNIEnv* env, jclass, jlong pkeyRef) {
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("EVP_PKEY_print_public(%p)", pkey);
if (pkey == NULL) {
jniThrowNullPointerException(env, "pkey == null");
return NULL;
}
Unique_BIO buffer(BIO_new(BIO_s_mem()));
if (buffer.get() == NULL) {
jniThrowOutOfMemory(env, "Unable to allocate BIO");
return NULL;
}
if (EVP_PKEY_print_public(buffer.get(), pkey, 0, (ASN1_PCTX*) NULL) != 1) {
throwExceptionIfNecessary(env, "EVP_PKEY_print_public");
return NULL;
}
// 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("EVP_PKEY_print_public(%p) => \"%s\"", pkey, tmp);
return description;
}
static jstring NativeCrypto_EVP_PKEY_print_private(JNIEnv* env, jclass, jlong pkeyRef) {
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("EVP_PKEY_print_private(%p)", pkey);
if (pkey == NULL) {
jniThrowNullPointerException(env, "pkey == null");
return NULL;
}
Unique_BIO buffer(BIO_new(BIO_s_mem()));
if (buffer.get() == NULL) {
jniThrowOutOfMemory(env, "Unable to allocate BIO");
return NULL;
}
if (EVP_PKEY_print_private(buffer.get(), pkey, 0, (ASN1_PCTX*) NULL) != 1) {
throwExceptionIfNecessary(env, "EVP_PKEY_print_private");
return NULL;
}
// 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("EVP_PKEY_print_private(%p) => \"%s\"", pkey, tmp);
return description;
}
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 != NULL) {
EVP_PKEY_free(pkey);
}
}
static jint NativeCrypto_EVP_PKEY_cmp(JNIEnv* env, jclass, jlong pkey1Ref, jlong pkey2Ref) {
EVP_PKEY* pkey1 = reinterpret_cast<EVP_PKEY*>(pkey1Ref);
EVP_PKEY* pkey2 = reinterpret_cast<EVP_PKEY*>(pkey2Ref);
JNI_TRACE("EVP_PKEY_cmp(%p, %p)", pkey1, pkey2);
if (pkey1 == NULL) {
JNI_TRACE("EVP_PKEY_cmp(%p, %p) => failed pkey1 == NULL", pkey1, pkey2);
jniThrowNullPointerException(env, "pkey1 == NULL");
return -1;
} else if (pkey2 == NULL) {
JNI_TRACE("EVP_PKEY_cmp(%p, %p) => failed pkey2 == NULL", pkey1, pkey2);
jniThrowNullPointerException(env, "pkey2 == NULL");
return -1;
}
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, jlong pkeyRef) {
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("i2d_PKCS8_PRIV_KEY_INFO(%p)", pkey);
if (pkey == NULL) {
jniThrowNullPointerException(env, NULL);
return NULL;
}
Unique_PKCS8_PRIV_KEY_INFO pkcs8(EVP_PKEY2PKCS8(pkey));
if (pkcs8.get() == NULL) {
throwExceptionIfNecessary(env, "NativeCrypto_i2d_PKCS8_PRIV_KEY_INFO");
JNI_TRACE("key=%p i2d_PKCS8_PRIV_KEY_INFO => error from key to PKCS8", pkey);
return NULL;
}
return ASN1ToByteArray<PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO>(env, pkcs8.get());
}
/*
* 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() == NULL) {
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(NULL, &tmp, bytes.size()));
if (pkcs8.get() == NULL) {
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() == NULL) {
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, jlong pkeyRef) {
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("i2d_PUBKEY(%p)", pkey);
return ASN1ToByteArray<EVP_PKEY, i2d_PUBKEY>(env, pkey);
}
/*
* 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() == NULL) {
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(NULL, &tmp, bytes.size()));
if (pkey.get() == NULL) {
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);
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));
Unique_EVP_PKEY pkey(EVP_PKEY_new());
if (pkey.get() == NULL) {
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_getDSAPrivateKeyWrapper(JNIEnv* env, jclass, jobject javaKey,
jbyteArray qBytes) {
JNI_TRACE("getDSAPrivateKeyWrapper(%p, %p)", javaKey, qBytes);
Unique_DSA dsa(DSA_new());
if (dsa.get() == NULL) {
jniThrowOutOfMemory(env, "Unable to allocate DSA key");
return 0;
}
if (!arrayToBignum(env, qBytes, &dsa->q)) {
return 0;
}
DSA_set_method(dsa.get(), &android_dsa_method);
DSA_set_ex_data(dsa.get(), 0, env->NewGlobalRef(javaKey));
Unique_EVP_PKEY pkey(EVP_PKEY_new());
if (pkey.get() == NULL) {
JNI_TRACE("getDSAPrivateKeyWrapper failed");
jniThrowRuntimeException(env, "NativeCrypto_getDSAPrivateKeyWrapper failed");
freeOpenSslErrorState();
return 0;
}
if (EVP_PKEY_assign_DSA(pkey.get(), dsa.get()) != 1) {
jniThrowRuntimeException(env, "getDSAPrivateKeyWrapper failed");
return 0;
}
OWNERSHIP_TRANSFERRED(dsa);
return reinterpret_cast<uintptr_t>(pkey.release());
}
static jlong NativeCrypto_getECPrivateKeyWrapper(JNIEnv* env, jclass, jobject javaKey, jlong groupRef) {
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
JNI_TRACE("getECPrivateKeyWrapper(%p, %p)", javaKey, group);
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));
Unique_EVP_PKEY pkey(EVP_PKEY_new());
if (pkey.get() == NULL) {
JNI_TRACE("getDSAPrivateKeyWrapper failed");
jniThrowRuntimeException(env, "NativeCrypto_getDSAPrivateKeyWrapper 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 = NULL;
if (!arrayToBignum(env, publicExponent, &eRef)) {
return 0;
}
Unique_BIGNUM e(eRef);
Unique_RSA rsa(RSA_new());
if (rsa.get() == NULL) {
jniThrowOutOfMemory(env, "Unable to allocate RSA key");
return 0;
}
if (RSA_generate_key_ex(rsa.get(), modulusBits, e.get(), NULL) < 0) {
throwExceptionIfNecessary(env, "RSA_generate_key_ex");
return 0;
}