blob: 1ca74f90d7b1a7963ca4715789e24a361417e418 [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
#define CONSCRYPT_UNBUNDLED
#define JNI_JARJAR_PREFIX
#endif
#define LOG_TAG "NativeCrypto"
#include <algorithm>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <vector>
#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 "AsynchronousSocketCloseMonitor.h"
#include "JNIHelp.h"
#include "JniConstants.h"
#include "JniException.h"
#include "NetFd.h"
#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
#include "UniquePtr.h"
#undef WITH_JNI_TRACE
#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__)); \
/*
((void)printf("I/" LOG_TAG "-jni:")); \
((void)printf(__VA_ARGS__)); \
((void)printf("\n"))
*/
#else
#define JNI_TRACE(...) ((void)0)
#endif
// don't overwhelm logcat
#define WITH_JNI_TRACE_DATA_CHUNK_SIZE 512
static JavaVM* gJavaVM;
static jclass openSslOutputStreamClass;
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 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(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_cleanup(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;
class X509Chain {
public:
X509Chain(size_t n) : x509s_(n) {}
~X509Chain() {
for (const auto& x509 : x509s_) {
X509_free(x509);
}
}
X509*& operator[](size_t n) {
return x509s_[n];
}
X509* operator[](size_t n) const {
return x509s_[n];
}
X509* release(size_t i) {
X509* x = x509s_[i];
x509s_[i] = NULL;
return x;
}
private:
std::vector<X509*> x509s_;
};
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 {
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_free(p);
}
};
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_free(p);
}
};
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_free(p);
}
};
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_free(p);
}
};
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) \
typeof (obj.release()) _dummy __attribute__((unused)) = obj.release()
/**
* 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);
}
/**
* 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:
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:
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 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) {
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
throwSSLExceptionStr(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 {
throwSSLExceptionStr(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;
}
/**
* Converts a Java byte[] to an OpenSSL BIGNUM, allocating the BIGNUM on the
* fly. 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);
ScopedByteArrayRO sourceBytes(env, source);
if (sourceBytes.get() == NULL) {
JNI_TRACE("arrayToBignum(%p) => NULL", source);
return false;
}
*dest = BN_bin2bn(reinterpret_cast<const unsigned char*>(sourceBytes.get()),
sourceBytes.size(),
NULL);
if (*dest == NULL) {
jniThrowRuntimeException(env, "Conversion to BIGNUM failed");
JNI_TRACE("arrayToBignum(%p) => threw exception", source);
return false;
}
JNI_TRACE("arrayToBignum(%p) => %p", source, *dest);
return true;
}
/**
* Converts an OpenSSL BIGNUM to a Java byte[] array.
*/
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;
}
jbyteArray javaBytes = env->NewByteArray(BN_num_bytes(source) + 1);
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());
// Set the sign for the Java code.
if (BN_is_negative(source)) {
*tmp = 0xFF;
} else {
*tmp = 0x00;
}
if (BN_num_bytes(source) > 0 && BN_bn2bin(source, tmp + 1) <= 0) {
throwExceptionIfNecessary(env, "bignumToArray");
return NULL;
}
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();
}
/**
* BIO for InputStream
*/
class BIO_Stream {
public:
BIO_Stream(jobject stream) :
mEof(false) {
JNIEnv* env = getEnv();
mStream = env->NewGlobalRef(stream);
}
~BIO_Stream() {
JNIEnv* env = getEnv();
env->DeleteGlobalRef(mStream);
}
bool isEof() const {
JNI_TRACE("isEof? %s", mEof ? "yes" : "no");
return mEof;
}
int flush() {
JNIEnv* env = getEnv();
if (env == NULL) {
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;
}
JNIEnv* getEnv() {
JNIEnv* env;
if (gJavaVM->AttachCurrentThread(&env, NULL) < 0) {
return NULL;
}
return env;
}
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 = getEnv();
if (env == NULL) {
JNI_TRACE("BIO_InputStream::read could not get JNIEnv");
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 = getEnv();
if (env == NULL) {
JNI_TRACE("BIO_OutputStream::write => could not get JNIEnv");
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_InputStream* stream = static_cast<BIO_InputStream*>(b->ptr);
return stream->read(buf, len);
}
static int bio_stream_write(BIO *b, const char *buf, int len) {
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 */
};
/**
* 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;
}
/**
* 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;
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());
}
/*
* 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;
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;
}
Unique_EVP_PKEY pkey(EVP_PKEY_new());
if (pkey.get() == NULL) {
jniThrowRuntimeException(env, "RSA_generate_key_ex failed");
return 0;
}
if (EVP_PKEY_assign_RSA(pkey.get(), rsa.get()) != 1) {
jniThrowRuntimeException(env, "RSA_generate_key_ex failed");
return 0;
}
OWNERSHIP_TRANSFERRED(rsa);
JNI_TRACE("RSA_generate_key_ex(n=%d, e=%p) => %p", modulusBits, publicExponent, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
static jint NativeCrypto_RSA_size(JNIEnv* env, jclass, jlong pkeyRef) {
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("RSA_size(%p)", pkey);
Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
if (rsa.get() == NULL) {
jniThrowRuntimeException(env, "RSA_size failed");
return 0;
}
return static_cast<jint>(RSA_size(rsa.get()));
}
typedef int RSACryptOperation(int flen, const unsigned char* from, unsigned char* to, RSA* rsa,
int padding);
static jint RSA_crypt_operation(RSACryptOperation operation,
const char* caller __attribute__ ((unused)), JNIEnv* env, jint flen,
jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jlong pkeyRef, jint padding) {
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("%s(%d, %p, %p, %p)", caller, flen, fromJavaBytes, toJavaBytes, pkey);
Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
if (rsa.get() == NULL) {
return -1;
}
ScopedByteArrayRO from(env, fromJavaBytes);
if (from.get() == NULL) {
return -1;
}
ScopedByteArrayRW to(env, toJavaBytes);
if (to.get() == NULL) {
return -1;
}
int resultSize = operation(static_cast<int>(flen),
reinterpret_cast<const unsigned char*>(from.get()),
reinterpret_cast<unsigned char*>(to.get()), rsa.get(), padding);
if (resultSize == -1) {
JNI_TRACE("%s => failed", caller);
throwExceptionIfNecessary(env, "RSA_crypt_operation");
return -1;
}
JNI_TRACE("%s(%d, %p, %p, %p) => %d", caller, flen, fromJavaBytes, toJavaBytes, pkey,
resultSize);
return static_cast<jint>(resultSize);
}
static jint NativeCrypto_RSA_private_encrypt(JNIEnv* env, jclass, jint flen,
jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jlong pkeyRef, jint padding) {
return RSA_crypt_operation(RSA_private_encrypt, __FUNCTION__,
env, flen, fromJavaBytes, toJavaBytes, pkeyRef, padding);
}
static jint NativeCrypto_RSA_public_decrypt(JNIEnv* env, jclass, jint flen,
jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jlong pkeyRef, jint padding) {
return RSA_crypt_operation(RSA_public_decrypt, __FUNCTION__,
env, flen, fromJavaBytes, toJavaBytes, pkeyRef, padding);
}
static jint NativeCrypto_RSA_public_encrypt(JNIEnv* env, jclass, jint flen,
jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jlong pkeyRef, jint padding) {
return RSA_crypt_operation(RSA_public_encrypt, __FUNCTION__,
env, flen, fromJavaBytes, toJavaBytes, pkeyRef, padding);
}
static jint NativeCrypto_RSA_private_decrypt(JNIEnv* env, jclass, jint flen,
jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jlong pkeyRef, jint padding) {
return RSA_crypt_operation(RSA_private_decrypt, __FUNCTION__,
env, flen, fromJavaBytes, toJavaBytes, pkeyRef, padding);
}
/*
* public static native byte[][] get_RSA_public_params(int);
*/
static jobjectArray NativeCrypto_get_RSA_public_params(JNIEnv* env, jclass, jlong pkeyRef) {
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("get_RSA_public_params(%p)", pkey);
if (pkey == NULL) {
jniThrowNullPointerException(env, "pkey == null");
return 0;
}
Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
if (rsa.get() == NULL) {
throwExceptionIfNecessary(env, "get_RSA_public_params failed");
return 0;
}
jobjectArray joa = env->NewObjectArray(2, byteArrayClass, NULL);
if (joa == NULL) {
return NULL;
}
jbyteArray n = bignumToArray(env, rsa->n, "n");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 0, n);
jbyteArray e = bignumToArray(env, rsa->e, "e");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 1, e);
return joa;
}
/*
* public static native byte[][] get_RSA_private_params(int);
*/
static jobjectArray NativeCrypto_get_RSA_private_params(JNIEnv* env, jclass, jlong pkeyRef) {
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("get_RSA_public_params(%p)", pkey);
if (pkey == NULL) {
jniThrowNullPointerException(env, "pkey == null");
return 0;
}
Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
if (rsa.get() == NULL) {
throwExceptionIfNecessary(env, "get_RSA_public_params failed");
return 0;
}
jobjectArray joa = env->NewObjectArray(8, byteArrayClass, NULL);
if (joa == NULL) {
return NULL;
}
jbyteArray n = bignumToArray(env, rsa->n, "n");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 0, n);
if (rsa->e != NULL) {
jbyteArray e = bignumToArray(env, rsa->e, "e");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 1, e);
}
if (rsa->d != NULL) {
jbyteArray d = bignumToArray(env, rsa->d, "d");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 2, d);
}
if (rsa->p != NULL) {
jbyteArray p = bignumToArray(env, rsa->p, "p");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 3, p);
}
if (rsa->q != NULL) {
jbyteArray q = bignumToArray(env, rsa->q, "q");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 4, q);
}
if (rsa->dmp1 != NULL) {
jbyteArray dmp1 = bignumToArray(env, rsa->dmp1, "dmp1");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 5, dmp1);
}
if (rsa->dmq1 != NULL) {
jbyteArray dmq1 = bignumToArray(env, rsa->dmq1, "dmq1");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 6, dmq1);
}
if (rsa->iqmp != NULL) {
jbyteArray iqmp = bignumToArray(env, rsa->iqmp, "iqmp");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 7, iqmp);
}
return joa;
}
/*
* public static native int DSA_generate_key(int, byte[]);
*/
static jlong NativeCrypto_DSA_generate_key(JNIEnv* env, jclass, jint primeBits,
jbyteArray seedJavaBytes, jbyteArray gBytes, jbyteArray pBytes, jbyteArray qBytes) {
JNI_TRACE("DSA_generate_key(%d, %p, %p, %p, %p)", primeBits, seedJavaBytes,
gBytes, pBytes, qBytes);
UniquePtr<unsigned char[]> seedPtr;
unsigned long seedSize = 0;
if (seedJavaBytes != NULL) {
ScopedByteArrayRO seed(env, seedJavaBytes);
if (seed.get() == NULL) {
return 0;
}
seedSize = seed.size();
seedPtr.reset(new unsigned char[seedSize]);
memcpy(seedPtr.get(), seed.get(), seedSize);
}
Unique_DSA dsa(DSA_new());
if (dsa.get() == NULL) {
JNI_TRACE("DSA_generate_key failed");
jniThrowOutOfMemory(env, "Unable to allocate DSA key");
freeOpenSslErrorState();
return 0;
}
if (gBytes != NULL && pBytes != NULL && qBytes != NULL) {
JNI_TRACE("DSA_generate_key parameters specified");
if (!arrayToBignum(env, gBytes, &dsa->g)) {
return 0;
}
if (!arrayToBignum(env, pBytes, &dsa->p)) {
return 0;
}
if (!arrayToBignum(env, qBytes, &dsa->q)) {
return 0;
}
} else {
JNI_TRACE("DSA_generate_key generating parameters");
if (!DSA_generate_parameters_ex(dsa.get(), primeBits, seedPtr.get(), seedSize, NULL, NULL, NULL)) {
JNI_TRACE("DSA_generate_key => param generation failed");
throwExceptionIfNecessary(env, "NativeCrypto_DSA_generate_parameters_ex failed");
return 0;
}
}
if (!DSA_generate_key(dsa.get())) {
JNI_TRACE("DSA_generate_key failed");
throwExceptionIfNecessary(env, "NativeCrypto_DSA_generate_key failed");
return 0;
}
Unique_EVP_PKEY pkey(EVP_PKEY_new());
if (pkey.get() == NULL) {
JNI_TRACE("DSA_generate_key failed");
jniThrowRuntimeException(env, "NativeCrypto_DSA_generate_key failed");
freeOpenSslErrorState();
return 0;
}
if (EVP_PKEY_assign_DSA(pkey.get(), dsa.get()) != 1) {
JNI_TRACE("DSA_generate_key failed");
throwExceptionIfNecessary(env, "NativeCrypto_DSA_generate_key failed");
return 0;
}
OWNERSHIP_TRANSFERRED(dsa);
JNI_TRACE("DSA_generate_key(n=%d, e=%p) => %p", primeBits, seedPtr.get(), pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
/*
* public static native byte[][] get_DSA_params(int);
*/
static jobjectArray NativeCrypto_get_DSA_params(JNIEnv* env, jclass, jlong pkeyRef) {
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("get_DSA_params(%p)", pkey);
Unique_DSA dsa(EVP_PKEY_get1_DSA(pkey));
if (dsa.get() == NULL) {
throwExceptionIfNecessary(env, "get_DSA_params failed");
return 0;
}
jobjectArray joa = env->NewObjectArray(5, byteArrayClass, NULL);
if (joa == NULL) {
return NULL;
}
if (dsa->g != NULL) {
jbyteArray g = bignumToArray(env, dsa->g, "g");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 0, g);
}
if (dsa->p != NULL) {
jbyteArray p = bignumToArray(env, dsa->p, "p");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 1, p);
}
if (dsa->q != NULL) {
jbyteArray q = bignumToArray(env, dsa->q, "q");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 2, q);
}
if (dsa->pub_key != NULL) {
jbyteArray pub_key = bignumToArray(env, dsa->pub_key, "pub_key");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 3, pub_key);
}
if (dsa->priv_key != NULL) {
jbyteArray priv_key = bignumToArray(env, dsa->priv_key, "priv_key");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 4, priv_key);
}
return joa;
}
#define EC_CURVE_GFP 1
#define EC_CURVE_GF2M 2
/**
* Return group type or 0 if unknown group.
* EC_GROUP_GFP or EC_GROUP_GF2M
*/
static int get_EC_GROUP_type(const EC_GROUP* group)
{
const EC_METHOD* method = EC_GROUP_method_of(group);
if (method == EC_GFp_nist_method()
|| method == EC_GFp_mont_method()
|| method == EC_GFp_simple_method()) {
return EC_CURVE_GFP;
} else if (method == EC_GF2m_simple_method()) {
return EC_CURVE_GF2M;
}
return 0;
}
static jlong NativeCrypto_EC_GROUP_new_by_curve_name(JNIEnv* env, jclass, jstring curveNameJava)
{
JNI_TRACE("EC_GROUP_new_by_curve_name(%p)", curveNameJava);
ScopedUtfChars curveName(env, curveNameJava);
if (curveName.c_str() == NULL) {
return 0;
}
JNI_TRACE("EC_GROUP_new_by_curve_name(%s)", curveName.c_str());
int nid = OBJ_sn2nid(curveName.c_str());
if (nid == NID_undef) {
JNI_TRACE("EC_GROUP_new_by_curve_name(%s) => unknown NID name", curveName.c_str());
return 0;
}
EC_GROUP* group = EC_GROUP_new_by_curve_name(nid);
if (group == NULL) {
JNI_TRACE("EC_GROUP_new_by_curve_name(%s) => unknown NID %d", curveName.c_str(), nid);
freeOpenSslErrorState();
return 0;
}
JNI_TRACE("EC_GROUP_new_by_curve_name(%s) => %p", curveName.c_str(), group);
return reinterpret_cast<uintptr_t>(group);
}
static void NativeCrypto_EC_GROUP_set_asn1_flag(JNIEnv* env, jclass, jlong groupRef,
jint flag)
{
EC_GROUP* group = reinterpret_cast<EC_GROUP*>(groupRef);
JNI_TRACE("EC_GROUP_set_asn1_flag(%p, %d)", group, flag);
if (group == NULL) {
JNI_TRACE("EC_GROUP_set_asn1_flag => group == NULL");
jniThrowNullPointerException(env, "group == NULL");
return;
}
EC_GROUP_set_asn1_flag(group, flag);
JNI_TRACE("EC_GROUP_set_asn1_flag(%p, %d) => success", group, flag);
}
static void NativeCrypto_EC_GROUP_set_point_conversion_form(JNIEnv* env, jclass,
jlong groupRef, jint form)
{
EC_GROUP* group = reinterpret_cast<EC_GROUP*>(groupRef);
JNI_TRACE("EC_GROUP_set_point_conversion_form(%p, %d)", group, form);
if (group == NULL) {
JNI_TRACE("EC_GROUP_set_point_conversion_form => group == NULL");
jniThrowNullPointerException(env, "group == NULL");
return;
}
EC_GROUP_set_point_conversion_form(group, static_cast<point_conversion_form_t>(form));
JNI_TRACE("EC_GROUP_set_point_conversion_form(%p, %d) => success", group, form);
}
static jlong NativeCrypto_EC_GROUP_new_curve(JNIEnv* env, jclass, jint type, jbyteArray pJava,
jbyteArray aJava, jbyteArray bJava)
{
JNI_TRACE("EC_GROUP_new_curve(%d, %p, %p, %p)", type, pJava, aJava, bJava);
BIGNUM* pRef;
if (!arrayToBignum(env, pJava, &pRef)) {
return 0;
}
Unique_BIGNUM p(pRef);
BIGNUM* aRef;
if (!arrayToBignum(env, aJava, &aRef)) {
return 0;
}
Unique_BIGNUM a(aRef);
BIGNUM* bRef;
if (!arrayToBignum(env, bJava, &bRef)) {
return 0;
}
Unique_BIGNUM b(bRef);
EC_GROUP* group;
switch (type) {
case EC_CURVE_GFP:
group = EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), (BN_CTX*) NULL);
break;
case EC_CURVE_GF2M:
group = EC_GROUP_new_curve_GF2m(p.get(), a.get(), b.get(), (BN_CTX*) NULL);
break;
default:
jniThrowRuntimeException(env, "invalid group");
return 0;
}
if (group == NULL) {
throwExceptionIfNecessary(env, "EC_GROUP_new_curve");
}
JNI_TRACE("EC_GROUP_new_curve(%d, %p, %p, %p) => %p", type, pJava, aJava, bJava, group);
return reinterpret_cast<uintptr_t>(group);
}
static jlong NativeCrypto_EC_GROUP_dup(JNIEnv* env, jclass, jlong groupRef) {
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
JNI_TRACE("EC_GROUP_dup(%p)", group);
if (group == NULL) {
JNI_TRACE("EC_GROUP_dup => group == NULL");
jniThrowNullPointerException(env, "group == NULL");
return 0;
}
EC_GROUP* groupDup = EC_GROUP_dup(group);
JNI_TRACE("EC_GROUP_dup(%p) => %p", group, groupDup);
return reinterpret_cast<uintptr_t>(groupDup);
}
static jstring NativeCrypto_EC_GROUP_get_curve_name(JNIEnv* env, jclass, jlong groupRef) {
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
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;
}
int nid = EC_GROUP_get_curve_name(group);
if (nid == NID_undef) {
JNI_TRACE("EC_GROUP_get_curve_name(%p) => unnamed curve", group);
return NULL;
}
const char* shortName = OBJ_nid2sn(nid);
JNI_TRACE("EC_GROUP_get_curve_name(%p) => \"%s\"", group, shortName);
return env->NewStringUTF(shortName);
}
static jobjectArray NativeCrypto_EC_GROUP_get_curve(JNIEnv* env, jclass, jlong groupRef)
{
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
JNI_TRACE("EC_GROUP_get_curve(%p)", group);
Unique_BIGNUM p(BN_new());
Unique_BIGNUM a(BN_new());
Unique_BIGNUM b(BN_new());
int ret;
switch (get_EC_GROUP_type(group)) {
case EC_CURVE_GFP:
ret = EC_GROUP_get_curve_GFp(group, p.get(), a.get(), b.get(), (BN_CTX*) NULL);
break;
case EC_CURVE_GF2M:
ret = EC_GROUP_get_curve_GF2m(group, p.get(), a.get(), b.get(), (BN_CTX*)NULL);
break;
default:
jniThrowRuntimeException(env, "invalid group");
return NULL;
}
if (ret != 1) {
throwExceptionIfNecessary(env, "EC_GROUP_get_curve");
return NULL;
}
jobjectArray joa = env->NewObjectArray(3, byteArrayClass, NULL);
if (joa == NULL) {
return NULL;
}
jbyteArray pArray = bignumToArray(env, p.get(), "p");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 0, pArray);
jbyteArray aArray = bignumToArray(env, a.get(), "a");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 1, aArray);
jbyteArray bArray = bignumToArray(env, b.get(), "b");
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(joa, 2, bArray);
JNI_TRACE("EC_GROUP_get_curve(%p) => %p", group, joa);
return joa;
}
static jbyteArray NativeCrypto_EC_GROUP_get_order(JNIEnv* env, jclass, jlong groupRef)
{
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
JNI_TRACE("EC_GROUP_get_order(%p)", group);
Unique_BIGNUM order(BN_new());
if (order.get() == NULL) {
JNI_TRACE("EC_GROUP_get_order(%p) => can't create BN", group);
jniThrowOutOfMemory(env, "BN_new");
return NULL;
}
if (EC_GROUP_get_order(group, order.get(), NULL) != 1) {
JNI_TRACE("EC_GROUP_get_order(%p) => threw error", group);
throwExceptionIfNecessary(env, "EC_GROUP_get_order");
return NULL;
}
jbyteArray orderArray = bignumToArray(env, order.get(), "order");
if (env->ExceptionCheck()) {
return NULL;
}
JNI_TRACE("EC_GROUP_get_order(%p) => %p", group, orderArray);
return orderArray;
}
static jint NativeCrypto_EC_GROUP_get_degree(JNIEnv* env, jclass, jlong groupRef)
{
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
JNI_TRACE("EC_GROUP_get_degree(%p)", group);
jint degree = EC_GROUP_get_degree(group);
if (degree == 0) {
JNI_TRACE("EC_GROUP_get_degree(%p) => unsupported", group);
jniThrowRuntimeException(env, "not supported");
return 0;
}
JNI_TRACE("EC_GROUP_get_degree(%p) => %d", group, degree);
return degree;
}
static jbyteArray NativeCrypto_EC_GROUP_get_cofactor(JNIEnv* env, jclass, jlong groupRef)
{
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
JNI_TRACE("EC_GROUP_get_cofactor(%p)", group);
Unique_BIGNUM cofactor(BN_new());
if (cofactor.get() == NULL) {
JNI_TRACE("EC_GROUP_get_cofactor(%p) => can't create BN", group);
jniThrowOutOfMemory(env, "BN_new");
return NULL;
}
if (EC_GROUP_get_cofactor(group, cofactor.get(), NULL) != 1) {
JNI_TRACE("EC_GROUP_get_cofactor(%p) => threw error", group);
throwExceptionIfNecessary(env, "EC_GROUP_get_cofactor");
return NULL;
}
jbyteArray cofactorArray = bignumToArray(env, cofactor.get(), "cofactor");
if (env->ExceptionCheck()) {
return NULL;
}
JNI_TRACE("EC_GROUP_get_cofactor(%p) => %p", group, cofactorArray);
return cofactorArray;
}
static jint NativeCrypto_get_EC_GROUP_type(JNIEnv* env, jclass, jlong groupRef)
{
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
JNI_TRACE("get_EC_GROUP_type(%p)", group);
int type = get_EC_GROUP_type(group);
if (type == 0) {
JNI_TRACE("get_EC_GROUP_type(%p) => curve type", group);
jniThrowRuntimeException(env, "unknown curve type");
} else {
JNI_TRACE("get_EC_GROUP_type(%p) => %d", group, type);
}
return type;
}
static void NativeCrypto_EC_GROUP_clear_free(JNIEnv* env, jclass, jlong groupRef)
{
EC_GROUP* group = reinterpret_cast<EC_GROUP*>(groupRef);
JNI_TRACE("EC_GROUP_clear_free(%p)", group);
if (group == NULL) {
JNI_TRACE("EC_GROUP_clear_free => group == NULL");
jniThrowNullPointerException(env, "group == NULL");
return;
}
EC_GROUP_clear_free(group);
JNI_TRACE("EC_GROUP_clear_free(%p) => success", group);
}
static jboolean NativeCrypto_EC_GROUP_cmp(JNIEnv* env, jclass, jlong group1Ref, jlong group2Ref)
{
const EC_GROUP* group1 = reinterpret_cast<const EC_GROUP*>(group1Ref);
const EC_GROUP* group2 = reinterpret_cast<const EC_GROUP*>(group2Ref);
JNI_TRACE("EC_GROUP_cmp(%p, %p)", group1, group2);
if (group1 == NULL || group2 == NULL) {
JNI_TRACE("EC_GROUP_cmp(%p, %p) => group1 == null || group2 == null", group1, group2);
jniThrowNullPointerException(env, "group1 == null || group2 == null");
return false;
}
int ret = EC_GROUP_cmp(group1, group2, (BN_CTX*)NULL);
JNI_TRACE("ECP_GROUP_cmp(%p, %p) => %d", group1, group2, ret);
return ret == 0;
}
static void NativeCrypto_EC_GROUP_set_generator(JNIEnv* env, jclass, jlong groupRef, jlong pointRef, jbyteArray njavaBytes, jbyteArray hjavaBytes)
{
EC_GROUP* group = reinterpret_cast<EC_GROUP*>(groupRef);
const EC_POINT* point = reinterpret_cast<const EC_POINT*>(pointRef);
JNI_TRACE("EC_GROUP_set_generator(%p, %p, %p, %p)", group, point, njavaBytes, hjavaBytes);
if (group == NULL || point == NULL) {
JNI_TRACE("EC_GROUP_set_generator(%p, %p, %p, %p) => group == null || point == null",
group, point, njavaBytes, hjavaBytes);
jniThrowNullPointerException(env, "group == null || point == null");
return;
}
BIGNUM* nRef;
if (!arrayToBignum(env, njavaBytes, &nRef)) {
return;
}
Unique_BIGNUM n(nRef);
BIGNUM* hRef;
if (!arrayToBignum(env, hjavaBytes, &hRef)) {
return;
}
Unique_BIGNUM h(hRef);
int ret = EC_GROUP_set_generator(group, point, n.get(), h.get());
if (ret == 0) {
throwExceptionIfNecessary(env, "EC_GROUP_set_generator");
}
JNI_TRACE("EC_GROUP_set_generator(%p, %p, %p, %p) => %d", group, point, njavaBytes, hjavaBytes, ret);
}
static jlong NativeCrypto_EC_GROUP_get_generator(JNIEnv* env, jclass, jlong groupRef)
{
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
JNI_TRACE("EC_GROUP_get_generator(%p)", group);
if (group == NULL) {
JNI_TRACE("EC_POINT_get_generator(%p) => group == null", group);
jniThrowNullPointerException(env, "group == null");
return 0;
}
const EC_POINT* generator = EC_GROUP_get0_generator(group);
Unique_EC_POINT dup(EC_POINT_dup(generator, group));
if (dup.get() == NULL) {
JNI_TRACE("EC_GROUP_get_generator(%p) => oom error", group);
jniThrowOutOfMemory(env, "unable to dupe generator");
return 0;
}
JNI_TRACE("EC_GROUP_get_generator(%p) => %p", group, dup.get());
return reinterpret_cast<uintptr_t>(dup.release());
}
static jlong NativeCrypto_EC_POINT_new(JNIEnv* env, jclass, jlong groupRef)
{
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
JNI_TRACE("EC_POINT_new(%p)", group);
if (group == NULL) {
JNI_TRACE("EC_POINT_new(%p) => group == null", group);
jniThrowNullPointerException(env, "group == null");
return 0;
}
EC_POINT* point = EC_POINT_new(group);
if (point == NULL) {
jniThrowOutOfMemory(env, "Unable create an EC_POINT");
return 0;
}
return reinterpret_cast<uintptr_t>(point);
}
static void NativeCrypto_EC_POINT_clear_free(JNIEnv* env, jclass, jlong groupRef) {
EC_POINT* group = reinterpret_cast<EC_POINT*>(groupRef);
JNI_TRACE("EC_POINT_clear_free(%p)", group);
if (group == NULL) {
JNI_TRACE("EC_POINT_clear_free => group == NULL");
jniThrowNullPointerException(env, "group == NULL");
return;
}
EC_POINT_clear_free(group);
JNI_TRACE("EC_POINT_clear_free(%p) => success", group);
}
static jboolean NativeCrypto_EC_POINT_cmp(JNIEnv* env, jclass, jlong groupRef, jlong point1Ref, jlong point2Ref)
{
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
const EC_POINT* point1 = reinterpret_cast<const EC_POINT*>(point1Ref);
const EC_POINT* point2 = reinterpret_cast<const EC_POINT*>(point2Ref);
JNI_TRACE("EC_POINT_cmp(%p, %p, %p)", group, point1, point2);
if (group == NULL || point1 == NULL || point2 == NULL) {
JNI_TRACE("EC_POINT_cmp(%p, %p, %p) => group == null || point1 == null || point2 == null",
group, point1, point2);
jniThrowNullPointerException(env, "group == null || point1 == null || point2 == null");
return false;
}
int ret = EC_POINT_cmp(group, point1, point2, (BN_CTX*)NULL);
JNI_TRACE("ECP_GROUP_cmp(%p, %p) => %d", point1, point2, ret);
return ret == 0;
}
static void NativeCrypto_EC_POINT_set_affine_coordinates(JNIEnv* env, jclass,
jlong groupRef, jlong pointRef, jbyteArray xjavaBytes, jbyteArray yjavaBytes)
{
const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
EC_POINT* point = reinterpret_cast<EC_POINT*>(pointRef);
JNI_TRACE("EC_POINT_set_affine_coordinates(%p, %p, %p, %p)", group, point, xjavaBytes,
yjavaBytes);
if (group == NULL || point == NULL) {
JNI_TRACE("EC_POINT_set_affine_coordinates(%p, %p, %p, %p) => group == null || point == null",
group, point, xjavaBytes, yjavaBytes);
jniThrowNullPointerException(env, "group == null || point == null");
return;
}
BIGNUM* xRef;
if (!arrayToBignum(env, xjavaBytes, &xRef)) {
return;
}
Unique_BIGNUM x(xRef);
BIGNUM* yRef;
if (!arrayToBignum(env, yjavaBytes, &yRef)) {
return;
}