blob: c739546ef7159751a686aa14d2ac4f93533b9b04 [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.
*/
#include <conscrypt/NetFd.h>
#include <conscrypt/app_data.h>
#include <conscrypt/bio_input_stream.h>
#include <conscrypt/bio_output_stream.h>
#include <conscrypt/bio_stream.h>
#include <conscrypt/compat.h>
#include <conscrypt/compatibility_close_monitor.h>
#include <conscrypt/jniutil.h>
#include <conscrypt/logging.h>
#include <conscrypt/macros.h>
#include <conscrypt/native_crypto.h>
#include <conscrypt/netutil.h>
#include <conscrypt/scoped_ssl_bio.h>
#include <conscrypt/ssl_error.h>
#include <nativehelper/scoped_primitive_array.h>
#include <nativehelper/scoped_utf_chars.h>
#include <limits.h>
#include <openssl/aead.h>
#include <openssl/asn1.h>
#include <openssl/chacha.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/pkcs7.h>
#include <openssl/pkcs8.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <limits>
#include <vector>
using conscrypt::AppData;
using conscrypt::BioInputStream;
using conscrypt::BioOutputStream;
using conscrypt::BioStream;
using conscrypt::CompatibilityCloseMonitor;
using conscrypt::NativeCrypto;
using conscrypt::SslError;
/**
* Helper function that grabs the casts an ssl pointer and then checks for nullness.
* If this function returns nullptr 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
* nullptr, 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 nullptr
* @returns the pointer, which may be nullptr
*/
static SSL_CTX* to_SSL_CTX(JNIEnv* env, jlong ssl_ctx_address, bool throwIfNull) {
SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
if ((ssl_ctx == nullptr) && throwIfNull) {
JNI_TRACE("ssl_ctx == null");
conscrypt::jniutil::throwNullPointerException(env, "ssl_ctx == null");
}
return ssl_ctx;
}
static SSL* to_SSL(JNIEnv* env, jlong ssl_address, bool throwIfNull) {
SSL* ssl = reinterpret_cast<SSL*>(static_cast<uintptr_t>(ssl_address));
if ((ssl == nullptr) && throwIfNull) {
JNI_TRACE("ssl == null");
conscrypt::jniutil::throwNullPointerException(env, "ssl == null");
}
return ssl;
}
static BIO* to_SSL_BIO(JNIEnv* env, jlong bio_address, bool throwIfNull) {
BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bio_address));
if ((bio == nullptr) && throwIfNull) {
JNI_TRACE("bio == null");
conscrypt::jniutil::throwNullPointerException(env, "bio == null");
}
return bio;
}
static SSL_SESSION* to_SSL_SESSION(JNIEnv* env, jlong ssl_session_address, bool throwIfNull) {
SSL_SESSION* ssl_session =
reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
if ((ssl_session == nullptr) && throwIfNull) {
JNI_TRACE("ssl_session == null");
conscrypt::jniutil::throwNullPointerException(env, "ssl_session == null");
}
return ssl_session;
}
static SSL_CIPHER* to_SSL_CIPHER(JNIEnv* env, jlong ssl_cipher_address, bool throwIfNull) {
SSL_CIPHER* ssl_cipher =
reinterpret_cast<SSL_CIPHER*>(static_cast<uintptr_t>(ssl_cipher_address));
if ((ssl_cipher == nullptr) && throwIfNull) {
JNI_TRACE("ssl_cipher == null");
conscrypt::jniutil::throwNullPointerException(env, "ssl_cipher == null");
}
return ssl_cipher;
}
template <typename T>
static T* fromContextObject(JNIEnv* env, jobject contextObject) {
if (contextObject == nullptr) {
JNI_TRACE("contextObject == null");
conscrypt::jniutil::throwNullPointerException(env, "contextObject == null");
return nullptr;
}
T* ref = reinterpret_cast<T*>(
env->GetLongField(contextObject, conscrypt::jniutil::nativeRef_address));
if (ref == nullptr) {
JNI_TRACE("ref == null");
conscrypt::jniutil::throwNullPointerException(env, "ref == null");
return nullptr;
}
return ref;
}
/**
* Converts a Java byte[] two's complement to an OpenSSL BIGNUM. This will
* allocate the BIGNUM if *dest == nullptr. Returns true on success. If the
* return value is false, there is a pending exception.
*/
static bool arrayToBignum(JNIEnv* env, jbyteArray source, BIGNUM** dest) {
JNI_TRACE("arrayToBignum(%p, %p)", source, dest);
if (dest == nullptr) {
JNI_TRACE("arrayToBignum(%p, %p) => dest is null!", source, dest);
conscrypt::jniutil::throwNullPointerException(env, "dest == null");
return false;
}
JNI_TRACE("arrayToBignum(%p, %p) *dest == %p", source, dest, *dest);
ScopedByteArrayRO sourceBytes(env, source);
if (sourceBytes.get() == nullptr) {
JNI_TRACE("arrayToBignum(%p, %p) => null", source, dest);
return false;
}
const unsigned char* tmp = reinterpret_cast<const unsigned char*>(sourceBytes.get());
size_t tmpSize = sourceBytes.size();
/* if the array is empty, it is zero. */
if (tmpSize == 0) {
if (*dest == nullptr) {
*dest = BN_new();
}
BN_zero(*dest);
return true;
}
std::unique_ptr<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 = static_cast<ssize_t>(tmpSize - 1); i >= 0; i--) {
twosBytes[i] ^= 0xFF;
if (carry) {
carry = (++twosBytes[i]) == 0;
}
}
}
BIGNUM* ret = BN_bin2bn(tmp, tmpSize, *dest);
if (ret == nullptr) {
conscrypt::jniutil::throwRuntimeException(env, "Conversion to BIGNUM failed");
ERR_clear_error();
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;
}
/**
* arrayToBignumSize sets |*out_size| to the size of the big-endian number
* contained in |source|. It returns true on success and sets an exception and
* returns false otherwise.
*/
static bool arrayToBignumSize(JNIEnv* env, jbyteArray source, size_t* out_size) {
JNI_TRACE("arrayToBignumSize(%p, %p)", source, out_size);
ScopedByteArrayRO sourceBytes(env, source);
if (sourceBytes.get() == nullptr) {
JNI_TRACE("arrayToBignum(%p, %p) => null", source, out_size);
return false;
}
const uint8_t* tmp = reinterpret_cast<const uint8_t*>(sourceBytes.get());
size_t tmpSize = sourceBytes.size();
if (tmpSize == 0) {
*out_size = 0;
return true;
}
if ((tmp[0] & 0x80) != 0) {
// Negative numbers are invalid.
conscrypt::jniutil::throwRuntimeException(env, "Negative number");
return false;
}
while (tmpSize > 0 && tmp[0] == 0) {
tmp++;
tmpSize--;
}
*out_size = tmpSize;
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 == nullptr) {
conscrypt::jniutil::throwNullPointerException(env, sourceName);
return nullptr;
}
size_t numBytes = BN_num_bytes(source) + 1;
jbyteArray javaBytes = env->NewByteArray(static_cast<jsize>(numBytes));
ScopedByteArrayRW bytes(env, javaBytes);
if (bytes.get() == nullptr) {
JNI_TRACE("bignumToArray(%p, %s) => null", source, sourceName);
return nullptr;
}
unsigned char* tmp = reinterpret_cast<unsigned char*>(bytes.get());
if (BN_num_bytes(source) > 0 && BN_bn2bin(source, tmp + 1) <= 0) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "bignumToArray");
return nullptr;
}
// Set the sign and convert to two's complement if necessary for the Java code.
if (BN_is_negative(source)) {
bool carry = true;
for (ssize_t i = static_cast<ssize_t>(numBytes - 1); i >= 0; i--) {
tmp[i] ^= 0xFF;
if (carry) {
carry = (++tmp[i]) == 0;
}
}
*tmp |= 0x80;
} else {
*tmp = 0x00;
}
JNI_TRACE("bignumToArray(%p, %s) => %p", source, sourceName, javaBytes);
return javaBytes;
}
/**
* Converts various OpenSSL ASN.1 types to a jbyteArray with DER-encoded data
* inside. The "i2d_func" function pointer is a function of the "i2d_<TYPE>"
* from the OpenSSL ASN.1 API.
*/
template <typename T>
jbyteArray ASN1ToByteArray(JNIEnv* env, T* obj, int (*i2d_func)(T*, unsigned char**)) {
if (obj == nullptr) {
conscrypt::jniutil::throwNullPointerException(env, "ASN1 input == null");
JNI_TRACE("ASN1ToByteArray(%p) => null input", obj);
return nullptr;
}
int derLen = i2d_func(obj, nullptr);
if (derLen < 0) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "ASN1ToByteArray");
JNI_TRACE("ASN1ToByteArray(%p) => measurement failed", obj);
return nullptr;
}
ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(derLen));
if (byteArray.get() == nullptr) {
JNI_TRACE("ASN1ToByteArray(%p) => creating byte array failed", obj);
return nullptr;
}
ScopedByteArrayRW bytes(env, byteArray.get());
if (bytes.get() == nullptr) {
JNI_TRACE("ASN1ToByteArray(%p) => using byte array failed", obj);
return nullptr;
}
unsigned char* p = reinterpret_cast<unsigned char*>(bytes.get());
int ret = i2d_func(obj, &p);
if (ret < 0) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "ASN1ToByteArray");
JNI_TRACE("ASN1ToByteArray(%p) => final conversion failed", obj);
return nullptr;
}
JNI_TRACE("ASN1ToByteArray(%p) => success (%d bytes written)", obj, ret);
return byteArray.release();
}
/**
* Finishes a pending CBB and returns a jbyteArray with the contents.
*/
jbyteArray CBBToByteArray(JNIEnv* env, CBB* cbb) {
uint8_t* data;
size_t len;
if (!CBB_finish(cbb, &data, &len)) {
conscrypt::jniutil::throwRuntimeException(env, "CBB_finish failed");
ERR_clear_error();
JNI_TRACE("creating byte array failed");
return nullptr;
}
bssl::UniquePtr<uint8_t> free_data(data);
ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(static_cast<jsize>(len)));
if (byteArray.get() == nullptr) {
JNI_TRACE("creating byte array failed");
return nullptr;
}
ScopedByteArrayRW bytes(env, byteArray.get());
if (bytes.get() == nullptr) {
JNI_TRACE("using byte array failed");
return nullptr;
}
memcpy(bytes.get(), data, len);
return byteArray.release();
}
jbyteArray CryptoBufferToByteArray(JNIEnv* env, const CRYPTO_BUFFER* buf) {
if (CRYPTO_BUFFER_len(buf) > INT_MAX) {
JNI_TRACE("buffer too large");
conscrypt::jniutil::throwRuntimeException(env, "buffer too large");
return nullptr;
}
int length = static_cast<int>(CRYPTO_BUFFER_len(buf));
jbyteArray ret = env->NewByteArray(length);
if (ret == nullptr) {
JNI_TRACE("allocating byte array failed");
return nullptr;
}
env->SetByteArrayRegion(ret, 0, length,
reinterpret_cast<const int8_t*>(CRYPTO_BUFFER_data(buf)));
return ret;
}
bssl::UniquePtr<CRYPTO_BUFFER> ByteArrayToCryptoBuffer(JNIEnv* env, const jbyteArray array,
CONSCRYPT_UNUSED CRYPTO_BUFFER_POOL* pool) {
if (array == nullptr) {
JNI_TRACE("array was null");
conscrypt::jniutil::throwNullPointerException(env, "array == null");
return nullptr;
}
ScopedByteArrayRO arrayRo(env, array);
if (arrayRo.get() == nullptr) {
JNI_TRACE("failed to get bytes");
return nullptr;
}
bssl::UniquePtr<CRYPTO_BUFFER> ret(CRYPTO_BUFFER_new(
reinterpret_cast<const uint8_t*>(arrayRo.get()), arrayRo.size(), nullptr));
if (!ret) {
JNI_TRACE("failed to allocate CRYPTO_BUFFER");
conscrypt::jniutil::throwOutOfMemory(env, "failed to allocate CRYPTO_BUFFER");
return nullptr;
}
return ret;
}
static jobjectArray CryptoBuffersToObjectArray(JNIEnv* env,
const STACK_OF(CRYPTO_BUFFER) * buffers) {
size_t numBuffers = sk_CRYPTO_BUFFER_num(buffers);
if (numBuffers > INT_MAX) {
JNI_TRACE("too many buffers");
conscrypt::jniutil::throwRuntimeException(env, "too many buffers");
return nullptr;
}
ScopedLocalRef<jobjectArray> array(
env, env->NewObjectArray(static_cast<int>(numBuffers),
conscrypt::jniutil::byteArrayClass, nullptr));
if (array.get() == nullptr) {
JNI_TRACE("failed to allocate array");
return nullptr;
}
for (size_t i = 0; i < numBuffers; ++i) {
CRYPTO_BUFFER* buffer = sk_CRYPTO_BUFFER_value(buffers, i);
ScopedLocalRef<jbyteArray> bArray(env, CryptoBufferToByteArray(env, buffer));
if (bArray.get() == nullptr) {
return nullptr;
}
env->SetObjectArrayElement(array.get(), i, bArray.get());
}
return array.release();
}
/**
* Converts ASN.1 BIT STRING to a jbooleanArray.
*/
jbooleanArray ASN1BitStringToBooleanArray(JNIEnv* env, ASN1_BIT_STRING* bitStr) {
int size = bitStr->length * 8;
if (bitStr->flags & ASN1_STRING_FLAG_BITS_LEFT) {
size -= bitStr->flags & 0x07;
}
ScopedLocalRef<jbooleanArray> bitsRef(env, env->NewBooleanArray(size));
if (bitsRef.get() == nullptr) {
return nullptr;
}
ScopedBooleanArrayRW bitsArray(env, bitsRef.get());
for (size_t i = 0; i < bitsArray.size(); i++) {
bitsArray[i] = static_cast<jboolean>(ASN1_BIT_STRING_get_bit(bitStr, static_cast<int>(i)));
}
return bitsRef.release();
}
static int bio_stream_create(BIO* b) {
b->init = 1;
b->num = 0;
b->ptr = nullptr;
b->flags = 0;
return 1;
}
static int bio_stream_destroy(BIO* b) {
if (b == nullptr) {
return 0;
}
if (b->ptr != nullptr) {
delete static_cast<BioStream*>(b->ptr);
b->ptr = nullptr;
}
b->init = 0;
b->flags = 0;
return 1;
}
static int bio_stream_read(BIO* b, char* buf, int len) {
BIO_clear_retry_flags(b);
BioInputStream* stream = static_cast<BioInputStream*>(b->ptr);
int ret = stream->read(buf, len);
if (ret == 0) {
if (stream->isFinite()) {
return 0;
}
// If the BioInputStream is not finite then EOF doesn't mean that
// there's nothing more coming.
BIO_set_retry_read(b);
return -1;
}
return ret;
}
static int bio_stream_write(BIO* b, const char* buf, int len) {
BIO_clear_retry_flags(b);
BioOutputStream* stream = static_cast<BioOutputStream*>(b->ptr);
return stream->write(buf, len);
}
static int bio_stream_puts(BIO* b, const char* buf) {
BioOutputStream* stream = static_cast<BioOutputStream*>(b->ptr);
return stream->write(buf, static_cast<int>(strlen(buf)));
}
static int bio_stream_gets(BIO* b, char* buf, int len) {
BioInputStream* stream = static_cast<BioInputStream*>(b->ptr);
return stream->gets(buf, len);
}
static void bio_stream_assign(BIO* b, BioStream* stream) {
b->ptr = static_cast<void*>(stream);
}
// NOLINTNEXTLINE(runtime/int)
static long bio_stream_ctrl(BIO* b, int cmd, long, void*) {
BioStream* stream = static_cast<BioStream*>(b->ptr);
switch (cmd) {
case BIO_CTRL_EOF:
return stream->isEof() ? 1 : 0;
case BIO_CTRL_FLUSH:
return stream->flush();
default:
return 0;
}
}
static BIO_METHOD stream_bio_method = {
(100 | 0x0400), /* source/sink BIO */
"InputStream/OutputStream BIO",
bio_stream_write, /* bio_write */
bio_stream_read, /* bio_read */
bio_stream_puts, /* bio_puts */
bio_stream_gets, /* bio_gets */
bio_stream_ctrl, /* bio_ctrl */
bio_stream_create, /* bio_create */
bio_stream_destroy, /* bio_free */
nullptr, /* no bio_callback_ctrl */
};
static jbyteArray ecSignDigestWithPrivateKey(JNIEnv* env, jobject privateKey, const char* message,
size_t message_len) {
JNI_TRACE("ecSignDigestWithPrivateKey(%p)", privateKey);
if (message_len > std::numeric_limits<jsize>::max()) {
JNI_TRACE("ecSignDigestWithPrivateKey(%p) => argument too large", privateKey);
return nullptr;
}
ScopedLocalRef<jbyteArray> messageArray(env,
env->NewByteArray(static_cast<jsize>(message_len)));
if (env->ExceptionCheck()) {
JNI_TRACE("ecSignDigestWithPrivateKey(%p) => threw exception", privateKey);
return nullptr;
}
{
ScopedByteArrayRW messageBytes(env, messageArray.get());
if (messageBytes.get() == nullptr) {
JNI_TRACE("ecSignDigestWithPrivateKey(%p) => using byte array failed", privateKey);
return nullptr;
}
memcpy(messageBytes.get(), message, message_len);
}
jmethodID rawSignMethod = env->GetStaticMethodID(conscrypt::jniutil::cryptoUpcallsClass,
"ecSignDigestWithPrivateKey",
"(Ljava/security/PrivateKey;[B)[B");
if (rawSignMethod == nullptr) {
CONSCRYPT_LOG_ERROR("Could not find ecSignDigestWithPrivateKey");
return nullptr;
}
return reinterpret_cast<jbyteArray>(env->CallStaticObjectMethod(
conscrypt::jniutil::cryptoUpcallsClass, rawSignMethod, privateKey, messageArray.get()));
}
static jbyteArray rsaSignDigestWithPrivateKey(JNIEnv* env, jobject privateKey, jint padding,
const char* message, size_t message_len) {
if (message_len > std::numeric_limits<jsize>::max()) {
JNI_TRACE("rsaSignDigestWithPrivateKey(%p) => argument too large", privateKey);
return nullptr;
}
ScopedLocalRef<jbyteArray> messageArray(env,
env->NewByteArray(static_cast<jsize>(message_len)));
if (env->ExceptionCheck()) {
JNI_TRACE("rsaSignDigestWithPrivateKey(%p) => threw exception", privateKey);
return nullptr;
}
{
ScopedByteArrayRW messageBytes(env, messageArray.get());
if (messageBytes.get() == nullptr) {
JNI_TRACE("rsaSignDigestWithPrivateKey(%p) => using byte array failed", privateKey);
return nullptr;
}
memcpy(messageBytes.get(), message, message_len);
}
jmethodID rsaSignMethod = env->GetStaticMethodID(conscrypt::jniutil::cryptoUpcallsClass,
"rsaSignDigestWithPrivateKey",
"(Ljava/security/PrivateKey;I[B)[B");
if (rsaSignMethod == nullptr) {
CONSCRYPT_LOG_ERROR("Could not find rsaSignDigestWithPrivateKey");
return nullptr;
}
return reinterpret_cast<jbyteArray>(
env->CallStaticObjectMethod(conscrypt::jniutil::cryptoUpcallsClass, rsaSignMethod,
privateKey, padding, messageArray.get()));
}
// rsaDecryptWithPrivateKey uses privateKey to decrypt |ciphertext_len| bytes
// from |ciphertext|. The ciphertext is expected to be padded using the scheme
// given in |padding|, which must be one of |RSA_*_PADDING| constants from
// OpenSSL.
static jbyteArray rsaDecryptWithPrivateKey(JNIEnv* env, jobject privateKey, jint padding,
const char* ciphertext, size_t ciphertext_len) {
if (ciphertext_len > std::numeric_limits<jsize>::max()) {
JNI_TRACE("rsaDecryptWithPrivateKey(%p) => argument too large", privateKey);
return nullptr;
}
ScopedLocalRef<jbyteArray> ciphertextArray(
env, env->NewByteArray(static_cast<jsize>(ciphertext_len)));
if (env->ExceptionCheck()) {
JNI_TRACE("rsaDecryptWithPrivateKey(%p) => threw exception", privateKey);
return nullptr;
}
{
ScopedByteArrayRW ciphertextBytes(env, ciphertextArray.get());
if (ciphertextBytes.get() == nullptr) {
JNI_TRACE("rsaDecryptWithPrivateKey(%p) => using byte array failed", privateKey);
return nullptr;
}
memcpy(ciphertextBytes.get(), ciphertext, ciphertext_len);
}
jmethodID rsaDecryptMethod =
env->GetStaticMethodID(conscrypt::jniutil::cryptoUpcallsClass,
"rsaDecryptWithPrivateKey", "(Ljava/security/PrivateKey;I[B)[B");
if (rsaDecryptMethod == nullptr) {
CONSCRYPT_LOG_ERROR("Could not find rsaDecryptWithPrivateKey");
return nullptr;
}
return reinterpret_cast<jbyteArray>(
env->CallStaticObjectMethod(conscrypt::jniutil::cryptoUpcallsClass, rsaDecryptMethod,
privateKey, padding, ciphertextArray.get()));
}
// *********************************************
// From keystore_openssl.cpp in Chromium source.
// *********************************************
namespace {
ENGINE* g_engine;
int g_rsa_exdata_index;
int g_ecdsa_exdata_index;
RSA_METHOD g_rsa_method;
ECDSA_METHOD g_ecdsa_method;
std::once_flag g_engine_once;
void init_engine_globals();
void ensure_engine_globals() {
std::call_once(g_engine_once, init_engine_globals);
}
// KeyExData contains the data that is contained in the EX_DATA of the RSA
// and ECDSA objects that are created to wrap Android system keys.
struct KeyExData {
// private_key contains a reference to a Java, private-key object.
jobject private_key;
// cached_size contains the "size" of the key. This is the size of the
// modulus (in bytes) for RSA, or the group order size for ECDSA. This
// avoids calling into Java to calculate the size.
size_t cached_size;
};
// ExDataDup is called when one of the RSA or EC_KEY objects is duplicated. We
// don't support this and it should never happen.
int ExDataDup(CRYPTO_EX_DATA* /* to */,
const CRYPTO_EX_DATA* /* from */,
void** /* from_d */,
int /* index */,
long /* argl */ /* NOLINT(runtime/int) */,
void* /* argp */) {
return 0;
}
// ExDataFree is called when one of the RSA or EC_KEY objects is freed.
void ExDataFree(void* /* parent */,
void* ptr,
CRYPTO_EX_DATA* /* ad */,
int /* index */,
long /* argl */ /* NOLINT(runtime/int) */,
void* /* argp */) {
// Ensure the global JNI reference created with this wrapper is
// properly destroyed with it.
KeyExData* ex_data = reinterpret_cast<KeyExData*>(ptr);
if (ex_data != nullptr) {
JNIEnv* env = conscrypt::jniutil::getJNIEnv();
env->DeleteGlobalRef(ex_data->private_key);
delete ex_data;
}
}
KeyExData* RsaGetExData(const RSA* rsa) {
return reinterpret_cast<KeyExData*>(RSA_get_ex_data(rsa, g_rsa_exdata_index));
}
size_t RsaMethodSize(const RSA* rsa) {
const KeyExData* ex_data = RsaGetExData(rsa);
return ex_data->cached_size;
}
int RsaMethodSignRaw(RSA* rsa, size_t* out_len, uint8_t* out, size_t max_out, const uint8_t* in,
size_t in_len, int padding) {
if (padding != RSA_PKCS1_PADDING && padding != RSA_NO_PADDING) {
OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_PADDING_TYPE);
return 0;
}
// Retrieve private key JNI reference.
const KeyExData* ex_data = RsaGetExData(rsa);
if (!ex_data || !ex_data->private_key) {
OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
return 0;
}
JNIEnv* env = conscrypt::jniutil::getJNIEnv();
if (env == nullptr) {
OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
return 0;
}
// For RSA keys, this function behaves as RSA_private_encrypt with
// the specified padding.
ScopedLocalRef<jbyteArray> signature(
env, rsaSignDigestWithPrivateKey(env, ex_data->private_key, padding,
reinterpret_cast<const char*>(in), in_len));
if (signature.get() == nullptr) {
OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
return 0;
}
ScopedByteArrayRO result(env, signature.get());
size_t expected_size = static_cast<size_t>(RSA_size(rsa));
if (result.size() > expected_size) {
OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
return 0;
}
if (max_out < expected_size) {
OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
return 0;
}
// Copy result to OpenSSL-provided buffer. rsaSignDigestWithPrivateKey
// should pad with leading 0s, but if it doesn't, pad the result.
size_t zero_pad = expected_size - result.size();
memset(out, 0, zero_pad);
memcpy(out + zero_pad, &result[0], result.size());
*out_len = expected_size;
return 1;
}
int RsaMethodDecrypt(RSA* rsa, size_t* out_len, uint8_t* out, size_t max_out, const uint8_t* in,
size_t in_len, int padding) {
// Retrieve private key JNI reference.
const KeyExData* ex_data = RsaGetExData(rsa);
if (!ex_data || !ex_data->private_key) {
OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
return 0;
}
JNIEnv* env = conscrypt::jniutil::getJNIEnv();
if (env == nullptr) {
OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
return 0;
}
// This function behaves as RSA_private_decrypt.
ScopedLocalRef<jbyteArray> cleartext(
env, rsaDecryptWithPrivateKey(env, ex_data->private_key, padding,
reinterpret_cast<const char*>(in), in_len));
if (cleartext.get() == nullptr) {
OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
return 0;
}
ScopedByteArrayRO cleartextBytes(env, cleartext.get());
if (max_out < cleartextBytes.size()) {
OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
return 0;
}
// Copy result to OpenSSL-provided buffer.
memcpy(out, cleartextBytes.get(), cleartextBytes.size());
*out_len = cleartextBytes.size();
return 1;
}
// Custom ECDSA_METHOD that uses the platform APIs.
// Note that for now, only signing through ECDSA_sign() is really supported.
// all other method pointers are either stubs returning errors, or no-ops.
jobject EcKeyGetKey(const EC_KEY* ec_key) {
KeyExData* ex_data =
reinterpret_cast<KeyExData*>(EC_KEY_get_ex_data(ec_key, g_ecdsa_exdata_index));
return ex_data->private_key;
}
int EcdsaMethodSign(const uint8_t* digest, size_t digest_len, uint8_t* sig, unsigned int* sig_len,
EC_KEY* ec_key) {
// Retrieve private key JNI reference.
jobject private_key = EcKeyGetKey(ec_key);
if (!private_key) {
CONSCRYPT_LOG_ERROR("Null JNI reference passed to EcdsaMethodSign!");
return 0;
}
JNIEnv* env = conscrypt::jniutil::getJNIEnv();
if (env == nullptr) {
return 0;
}
// Sign message with it through JNI.
ScopedLocalRef<jbyteArray> signature(
env, ecSignDigestWithPrivateKey(env, private_key,
reinterpret_cast<const char*>(digest), digest_len));
if (signature.get() == nullptr) {
CONSCRYPT_LOG_ERROR("Could not sign message in EcdsaMethodDoSign!");
return 0;
}
ScopedByteArrayRO signatureBytes(env, signature.get());
// Note: With ECDSA, the actual signature may be smaller than
// ECDSA_size().
size_t max_expected_size = ECDSA_size(ec_key);
if (signatureBytes.size() > max_expected_size) {
CONSCRYPT_LOG_ERROR("ECDSA Signature size mismatch, actual: %zd, expected <= %zd",
signatureBytes.size(), max_expected_size);
return 0;
}
memcpy(sig, signatureBytes.get(), signatureBytes.size());
*sig_len = static_cast<unsigned int>(signatureBytes.size());
return 1;
}
void init_engine_globals() {
g_rsa_exdata_index = RSA_get_ex_new_index(0 /* argl */, nullptr /* argp */,
nullptr /* new_func */, ExDataDup, ExDataFree);
g_ecdsa_exdata_index = EC_KEY_get_ex_new_index(0 /* argl */, nullptr /* argp */,
nullptr /* new_func */, ExDataDup, ExDataFree);
g_rsa_method.common.is_static = 1;
g_rsa_method.size = RsaMethodSize;
g_rsa_method.sign_raw = RsaMethodSignRaw;
g_rsa_method.decrypt = RsaMethodDecrypt;
g_rsa_method.flags = RSA_FLAG_OPAQUE;
g_ecdsa_method.common.is_static = 1;
g_ecdsa_method.sign = EcdsaMethodSign;
g_ecdsa_method.flags = ECDSA_FLAG_OPAQUE;
g_engine = ENGINE_new();
ENGINE_set_RSA_method(g_engine, &g_rsa_method, sizeof(g_rsa_method));
ENGINE_set_ECDSA_method(g_engine, &g_ecdsa_method, sizeof(g_ecdsa_method));
}
} // anonymous namespace
#define THROW_SSLEXCEPTION (-2)
#define THROW_SOCKETTIMEOUTEXCEPTION (-3)
#define THROWN_EXCEPTION (-4)
/**
* Initialization phase for every OpenSSL job: Loads the Error strings, the
* crypto algorithms and reset the OpenSSL library
*/
static void NativeCrypto_clinit(JNIEnv*, jclass) {
CRYPTO_library_init();
}
/**
* 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) {
CHECK_ERROR_QUEUE_ON_RETURN;
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);
bssl::UniquePtr<RSA> rsa(RSA_new());
if (rsa.get() == nullptr) {
conscrypt::jniutil::throwRuntimeException(env, "RSA_new failed");
return 0;
}
if (e == nullptr && d == nullptr) {
conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException",
"e == null && d == null");
JNI_TRACE("NativeCrypto_EVP_PKEY_new_RSA => e == null && d == null");
return 0;
}
if (!arrayToBignum(env, n, &rsa->n)) {
return 0;
}
if (e != nullptr && !arrayToBignum(env, e, &rsa->e)) {
return 0;
}
if (d != nullptr && !arrayToBignum(env, d, &rsa->d)) {
return 0;
}
if (p != nullptr && !arrayToBignum(env, p, &rsa->p)) {
return 0;
}
if (q != nullptr && !arrayToBignum(env, q, &rsa->q)) {
return 0;
}
if (dmp1 != nullptr && !arrayToBignum(env, dmp1, &rsa->dmp1)) {
return 0;
}
if (dmq1 != nullptr && !arrayToBignum(env, dmq1, &rsa->dmq1)) {
return 0;
}
if (iqmp != nullptr && !arrayToBignum(env, iqmp, &rsa->iqmp)) {
return 0;
}
if (conscrypt::trace::kWithJniTrace) {
if (p != nullptr && q != nullptr) {
int check = RSA_check_key(rsa.get());
JNI_TRACE("EVP_PKEY_new_RSA(...) RSA_check_key returns %d", check);
}
}
if (rsa->n == nullptr || (rsa->e == nullptr && rsa->d == nullptr)) {
conscrypt::jniutil::throwRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
return 0;
}
/*
* If the private exponent is available, there is the potential to do signing
* operations. However, we can only do blinding if the public exponent is also
* available. Disable blinding if the public exponent isn't available.
*
* TODO[kroot]: We should try to recover the public exponent by trying
* some common ones such 3, 17, or 65537.
*/
if (rsa->d != nullptr && rsa->e == nullptr) {
JNI_TRACE("EVP_PKEY_new_RSA(...) disabling RSA blinding => %p", rsa.get());
rsa->flags |= RSA_FLAG_NO_BLINDING;
}
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
if (pkey.get() == nullptr) {
conscrypt::jniutil::throwRuntimeException(env, "EVP_PKEY_new failed");
return 0;
}
if (EVP_PKEY_assign_RSA(pkey.get(), rsa.get()) != 1) {
conscrypt::jniutil::throwRuntimeException(env, "EVP_PKEY_new failed");
ERR_clear_error();
return 0;
}
OWNERSHIP_TRANSFERRED(rsa);
JNI_TRACE("EVP_PKEY_new_RSA(n=%p, e=%p, d=%p, p=%p, q=%p dmp1=%p, dmq1=%p, iqmp=%p) => %p", n,
e, d, p, q, dmp1, dmq1, iqmp, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
static jlong NativeCrypto_EVP_PKEY_new_EC_KEY(JNIEnv* env, jclass, jobject groupRef,
jobject pubkeyRef, jbyteArray keyJavaBytes) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p)", groupRef, pubkeyRef, keyJavaBytes);
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
if (group == nullptr) {
return 0;
}
const EC_POINT* pubkey =
pubkeyRef == nullptr ? nullptr : fromContextObject<EC_POINT>(env, pubkeyRef);
JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) <- ptr", group, pubkey, keyJavaBytes);
bssl::UniquePtr<BIGNUM> key(nullptr);
if (keyJavaBytes != nullptr) {
BIGNUM* keyRef = nullptr;
if (!arrayToBignum(env, keyJavaBytes, &keyRef)) {
return 0;
}
key.reset(keyRef);
}
bssl::UniquePtr<EC_KEY> eckey(EC_KEY_new());
if (eckey.get() == nullptr) {
conscrypt::jniutil::throwRuntimeException(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);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_KEY_set_group");
return 0;
}
if (pubkey != nullptr) {
if (EC_KEY_set_public_key(eckey.get(), pubkey) != 1) {
JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) => EC_KEY_set_private_key failed", group,
pubkey, keyJavaBytes);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_KEY_set_public_key");
return 0;
}
}
if (key.get() != nullptr) {
if (EC_KEY_set_private_key(eckey.get(), key.get()) != 1) {
JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) => EC_KEY_set_private_key failed", group,
pubkey, keyJavaBytes);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_KEY_set_private_key");
return 0;
}
if (pubkey == nullptr) {
bssl::UniquePtr<EC_POINT> calcPubkey(EC_POINT_new(group));
if (!EC_POINT_mul(group, calcPubkey.get(), key.get(), nullptr, nullptr, nullptr)) {
JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) => can't calculate public key", group,
pubkey, keyJavaBytes);
conscrypt::jniutil::throwExceptionFromBoringSSLError(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);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_KEY_check_key");
return 0;
}
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
if (pkey.get() == nullptr) {
JNI_TRACE("EVP_PKEY_new_EC(%p, %p, %p) => threw error", group, pubkey, keyJavaBytes);
conscrypt::jniutil::throwExceptionFromBoringSSLError(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);
conscrypt::jniutil::throwRuntimeException(env, "EVP_PKEY_assign_EC_KEY failed");
ERR_clear_error();
return 0;
}
OWNERSHIP_TRANSFERRED(eckey);
JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) => %p", group, pubkey, keyJavaBytes, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
static int NativeCrypto_EVP_PKEY_type(JNIEnv* env, jclass, jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("EVP_PKEY_type(%p)", pkey);
if (pkey == nullptr) {
return -1;
}
int result = EVP_PKEY_type(pkey->type);
JNI_TRACE("EVP_PKEY_type(%p) => %d", pkey, result);
return result;
}
typedef int print_func(BIO*, const EVP_PKEY*, int, ASN1_PCTX*);
static jstring evp_print_func(JNIEnv* env, jobject pkeyRef, print_func* func,
const char* debug_name) {
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("%s(%p)", debug_name, pkey);
if (pkey == nullptr) {
return nullptr;
}
bssl::UniquePtr<BIO> buffer(BIO_new(BIO_s_mem()));
if (buffer.get() == nullptr) {
conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate BIO");
return nullptr;
}
if (func(buffer.get(), pkey, 0, nullptr) != 1) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, debug_name);
return nullptr;
}
// Null terminate this
BIO_write(buffer.get(), "\0", 1);
char* tmp;
BIO_get_mem_data(buffer.get(), &tmp);
jstring description = env->NewStringUTF(tmp);
JNI_TRACE("%s(%p) => \"%s\"", debug_name, pkey, tmp);
return description;
}
static jstring NativeCrypto_EVP_PKEY_print_public(JNIEnv* env, jclass, jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
return evp_print_func(env, pkeyRef, EVP_PKEY_print_public, "EVP_PKEY_print_public");
}
static jstring NativeCrypto_EVP_PKEY_print_params(JNIEnv* env, jclass, jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
return evp_print_func(env, pkeyRef, EVP_PKEY_print_params, "EVP_PKEY_print_params");
}
static void NativeCrypto_EVP_PKEY_free(JNIEnv* env, jclass, jlong pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("EVP_PKEY_free(%p)", pkey);
if (pkey != nullptr) {
EVP_PKEY_free(pkey);
}
}
static jint NativeCrypto_EVP_PKEY_cmp(JNIEnv* env, jclass, jobject pkey1Ref, jobject pkey2Ref) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("EVP_PKEY_cmp(%p, %p)", pkey1Ref, pkey2Ref);
EVP_PKEY* pkey1 = fromContextObject<EVP_PKEY>(env, pkey1Ref);
if (pkey1 == nullptr) {
JNI_TRACE("EVP_PKEY_cmp => pkey1 == null");
return 0;
}
EVP_PKEY* pkey2 = fromContextObject<EVP_PKEY>(env, pkey2Ref);
if (pkey2 == nullptr) {
JNI_TRACE("EVP_PKEY_cmp => pkey2 == null");
return 0;
}
JNI_TRACE("EVP_PKEY_cmp(%p, %p) <- ptr", pkey1, pkey2);
int result = EVP_PKEY_cmp(pkey1, pkey2);
JNI_TRACE("EVP_PKEY_cmp(%p, %p) => %d", pkey1, pkey2, result);
return result;
}
/*
* static native byte[] EVP_marshal_private_key(long)
*/
static jbyteArray NativeCrypto_EVP_marshal_private_key(JNIEnv* env, jclass, jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("EVP_marshal_private_key(%p)", pkey);
if (pkey == nullptr) {
return nullptr;
}
bssl::ScopedCBB cbb;
if (!CBB_init(cbb.get(), 64)) {
conscrypt::jniutil::throwOutOfMemory(env, "CBB_init failed");
JNI_TRACE("CBB_init failed");
return nullptr;
}
if (!EVP_marshal_private_key(cbb.get(), pkey)) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_marshal_private_key");
JNI_TRACE("key=%p EVP_marshal_private_key => error", pkey);
return nullptr;
}
return CBBToByteArray(env, cbb.get());
}
/*
* static native long EVP_parse_private_key(byte[])
*/
static jlong NativeCrypto_EVP_parse_private_key(JNIEnv* env, jclass, jbyteArray keyJavaBytes) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("EVP_parse_private_key(%p)", keyJavaBytes);
ScopedByteArrayRO bytes(env, keyJavaBytes);
if (bytes.get() == nullptr) {
JNI_TRACE("bytes=%p EVP_parse_private_key => threw exception", keyJavaBytes);
return 0;
}
CBS cbs;
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(bytes.get()), bytes.size());
bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_private_key(&cbs));
// We intentionally do not check that cbs is exhausted, as JCA providers typically
// allow parsing keys from buffers that are larger than the contained key structure
// so we do the same for compatibility.
if (!pkey) {
conscrypt::jniutil::throwParsingException(env, "Error parsing private key");
ERR_clear_error();
JNI_TRACE("bytes=%p EVP_parse_private_key => threw exception", keyJavaBytes);
return 0;
}
JNI_TRACE("bytes=%p EVP_parse_private_key => %p", keyJavaBytes, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
/*
* static native byte[] EVP_marshal_public_key(long)
*/
static jbyteArray NativeCrypto_EVP_marshal_public_key(JNIEnv* env, jclass, jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("EVP_marshal_public_key(%p)", pkey);
if (pkey == nullptr) {
return nullptr;
}
bssl::ScopedCBB cbb;
if (!CBB_init(cbb.get(), 64)) {
conscrypt::jniutil::throwOutOfMemory(env, "CBB_init failed");
JNI_TRACE("CBB_init failed");
return nullptr;
}
if (!EVP_marshal_public_key(cbb.get(), pkey)) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_marshal_public_key");
JNI_TRACE("key=%p EVP_marshal_public_key => error", pkey);
return nullptr;
}
return CBBToByteArray(env, cbb.get());
}
/*
* static native long EVP_parse_public_key(byte[])
*/
static jlong NativeCrypto_EVP_parse_public_key(JNIEnv* env, jclass, jbyteArray keyJavaBytes) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("EVP_parse_public_key(%p)", keyJavaBytes);
ScopedByteArrayRO bytes(env, keyJavaBytes);
if (bytes.get() == nullptr) {
JNI_TRACE("bytes=%p EVP_parse_public_key => threw exception", keyJavaBytes);
return 0;
}
CBS cbs;
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(bytes.get()), bytes.size());
bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_public_key(&cbs));
// We intentionally do not check that cbs is exhausted, as JCA providers typically
// allow parsing keys from buffers that are larger than the contained key structure
// so we do the same for compatibility.
if (!pkey) {
conscrypt::jniutil::throwParsingException(env, "Error parsing public key");
ERR_clear_error();
JNI_TRACE("bytes=%p EVP_parse_public_key => threw exception", keyJavaBytes);
return 0;
}
JNI_TRACE("bytes=%p EVP_parse_public_key => %p", keyJavaBytes, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
static jlong NativeCrypto_getRSAPrivateKeyWrapper(JNIEnv* env, jclass, jobject javaKey,
jbyteArray modulusBytes) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("getRSAPrivateKeyWrapper(%p, %p)", javaKey, modulusBytes);
size_t cached_size;
if (!arrayToBignumSize(env, modulusBytes, &cached_size)) {
JNI_TRACE("getRSAPrivateKeyWrapper failed");
return 0;
}
ensure_engine_globals();
bssl::UniquePtr<RSA> rsa(RSA_new_method(g_engine));
if (rsa.get() == nullptr) {
conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate RSA key");
return 0;
}
// The PSS padding code needs access to the actual n, so set it even though we
// don't set any other parts of the key
if (!arrayToBignum(env, modulusBytes, &rsa->n)) {
return 0;
}
auto ex_data = new KeyExData;
ex_data->private_key = env->NewGlobalRef(javaKey);
ex_data->cached_size = cached_size;
RSA_set_ex_data(rsa.get(), g_rsa_exdata_index, ex_data);
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
if (pkey.get() == nullptr) {
JNI_TRACE("getRSAPrivateKeyWrapper failed");
conscrypt::jniutil::throwRuntimeException(env,
"NativeCrypto_getRSAPrivateKeyWrapper failed");
ERR_clear_error();
return 0;
}
if (EVP_PKEY_assign_RSA(pkey.get(), rsa.get()) != 1) {
conscrypt::jniutil::throwRuntimeException(env, "getRSAPrivateKeyWrapper failed");
ERR_clear_error();
return 0;
}
OWNERSHIP_TRANSFERRED(rsa);
JNI_TRACE("getRSAPrivateKeyWrapper(%p, %p) => %p", javaKey, modulusBytes, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
static jlong NativeCrypto_getECPrivateKeyWrapper(JNIEnv* env, jclass, jobject javaKey,
jobject groupRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
JNI_TRACE("getECPrivateKeyWrapper(%p, %p)", javaKey, group);
if (group == nullptr) {
return 0;
}
ensure_engine_globals();
bssl::UniquePtr<EC_KEY> ecKey(EC_KEY_new_method(g_engine));
if (ecKey.get() == nullptr) {
conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate EC key");
return 0;
}
if (EC_KEY_set_group(ecKey.get(), group) != 1) {
JNI_TRACE("getECPrivateKeyWrapper(%p, %p) => EC_KEY_set_group error", javaKey, group);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_KEY_set_group");
return 0;
}
auto ex_data = new KeyExData;
ex_data->private_key = env->NewGlobalRef(javaKey);
if (!EC_KEY_set_ex_data(ecKey.get(), g_ecdsa_exdata_index, ex_data)) {
env->DeleteGlobalRef(ex_data->private_key);
delete ex_data;
conscrypt::jniutil::throwRuntimeException(env, "EC_KEY_set_ex_data");
ERR_clear_error();
return 0;
}
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
if (pkey.get() == nullptr) {
JNI_TRACE("getECPrivateKeyWrapper failed");
conscrypt::jniutil::throwRuntimeException(env,
"NativeCrypto_getECPrivateKeyWrapper failed");
ERR_clear_error();
return 0;
}
if (EVP_PKEY_assign_EC_KEY(pkey.get(), ecKey.get()) != 1) {
conscrypt::jniutil::throwRuntimeException(env, "getECPrivateKeyWrapper failed");
ERR_clear_error();
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) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("RSA_generate_key_ex(%d, %p)", modulusBits, publicExponent);
BIGNUM* eRef = nullptr;
if (!arrayToBignum(env, publicExponent, &eRef)) {
return 0;
}
bssl::UniquePtr<BIGNUM> e(eRef);
bssl::UniquePtr<RSA> rsa(RSA_new());
if (rsa.get() == nullptr) {
conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate RSA key");
return 0;
}
if (RSA_generate_key_ex(rsa.get(), modulusBits, e.get(), nullptr) != 1) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "RSA_generate_key_ex failed");
return 0;
}
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
if (pkey.get() == nullptr) {
conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate RSA key");
return 0;
}
if (EVP_PKEY_assign_RSA(pkey.get(), rsa.get()) != 1) {
conscrypt::jniutil::throwRuntimeException(env, "RSA_generate_key_ex failed");
ERR_clear_error();
return 0;
}
OWNERSHIP_TRANSFERRED(rsa);
JNI_TRACE("RSA_generate_key_ex(n=%d, e=%p) => %p", modulusBits, publicExponent, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
static jint NativeCrypto_RSA_size(JNIEnv* env, jclass, jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("RSA_size(%p)", pkey);
if (pkey == nullptr) {
return 0;
}
bssl::UniquePtr<RSA> rsa(EVP_PKEY_get1_RSA(pkey));
if (rsa.get() == nullptr) {
conscrypt::jniutil::throwRuntimeException(env, "RSA_size failed");
ERR_clear_error();
return 0;
}
return static_cast<jint>(RSA_size(rsa.get()));
}
typedef int RSACryptOperation(size_t flen, const unsigned char* from, unsigned char* to, RSA* rsa,
int padding);
static jint RSA_crypt_operation(RSACryptOperation operation, const char* caller, JNIEnv* env,
jint flen, jbyteArray fromJavaBytes, jbyteArray toJavaBytes,
jobject pkeyRef, jint padding) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("%s(%d, %p, %p, %p)", caller, flen, fromJavaBytes, toJavaBytes, pkey);
if (pkey == nullptr) {
return -1;
}
bssl::UniquePtr<RSA> rsa(EVP_PKEY_get1_RSA(pkey));
if (rsa.get() == nullptr) {
return -1;
}
ScopedByteArrayRO from(env, fromJavaBytes);
if (from.get() == nullptr) {
return -1;
}
ScopedByteArrayRW to(env, toJavaBytes);
if (to.get() == nullptr) {
return -1;
}
int resultSize =
operation(static_cast<size_t>(flen), reinterpret_cast<const unsigned char*>(from.get()),
reinterpret_cast<unsigned char*>(to.get()), rsa.get(), padding);
if (resultSize == -1) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, caller,
conscrypt::jniutil::throwBadPaddingException);
JNI_TRACE("%s => threw error", caller);
return -1;
}
JNI_TRACE("%s(%d, %p, %p, %p) => %d", caller, flen, fromJavaBytes, toJavaBytes, pkey,
resultSize);
return static_cast<jint>(resultSize);
}
static jint NativeCrypto_RSA_private_encrypt(JNIEnv* env, jclass, jint flen,
jbyteArray fromJavaBytes, jbyteArray toJavaBytes,
jobject pkeyRef, jint padding) {
CHECK_ERROR_QUEUE_ON_RETURN;
return RSA_crypt_operation(RSA_private_encrypt, __FUNCTION__, env, flen, fromJavaBytes,
toJavaBytes, pkeyRef, padding);
}
static jint NativeCrypto_RSA_public_decrypt(JNIEnv* env, jclass, jint flen,
jbyteArray fromJavaBytes, jbyteArray toJavaBytes,
jobject pkeyRef, jint padding) {
CHECK_ERROR_QUEUE_ON_RETURN;
return RSA_crypt_operation(RSA_public_decrypt, __FUNCTION__, env, flen, fromJavaBytes,
toJavaBytes, pkeyRef, padding);
}
static jint NativeCrypto_RSA_public_encrypt(JNIEnv* env, jclass, jint flen,
jbyteArray fromJavaBytes, jbyteArray toJavaBytes,
jobject pkeyRef, jint padding) {
CHECK_ERROR_QUEUE_ON_RETURN;
return RSA_crypt_operation(RSA_public_encrypt, __FUNCTION__, env, flen, fromJavaBytes,
toJavaBytes, pkeyRef, padding);
}
static jint NativeCrypto_RSA_private_decrypt(JNIEnv* env, jclass, jint flen,
jbyteArray fromJavaBytes, jbyteArray toJavaBytes,
jobject pkeyRef, jint padding) {
CHECK_ERROR_QUEUE_ON_RETURN;
return RSA_crypt_operation(RSA_private_decrypt, __FUNCTION__, env, flen, fromJavaBytes,
toJavaBytes, pkeyRef, padding);
}
/*
* public static native byte[][] get_RSA_public_params(long);
*/
static jobjectArray NativeCrypto_get_RSA_public_params(JNIEnv* env, jclass, jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("get_RSA_public_params(%p)", pkey);
if (pkey == nullptr) {
return nullptr;
}
bssl::UniquePtr<RSA> rsa(EVP_PKEY_get1_RSA(pkey));
if (rsa.get() == nullptr) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "get_RSA_public_params failed");
return nullptr;
}
jobjectArray joa = env->NewObjectArray(2, conscrypt::jniutil::byteArrayClass, nullptr);
if (joa == nullptr) {
return nullptr;
}
jbyteArray n = bignumToArray(env, rsa->n, "n");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 0, n);
jbyteArray e = bignumToArray(env, rsa->e, "e");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 1, e);
return joa;
}
/*
* public static native byte[][] get_RSA_private_params(long);
*/
static jobjectArray NativeCrypto_get_RSA_private_params(JNIEnv* env, jclass, jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("get_RSA_public_params(%p)", pkey);
if (pkey == nullptr) {
return nullptr;
}
bssl::UniquePtr<RSA> rsa(EVP_PKEY_get1_RSA(pkey));
if (rsa.get() == nullptr) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "get_RSA_public_params failed");
return nullptr;
}
jobjectArray joa = env->NewObjectArray(8, conscrypt::jniutil::byteArrayClass, nullptr);
if (joa == nullptr) {
return nullptr;
}
jbyteArray n = bignumToArray(env, rsa->n, "n");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 0, n);
if (rsa->e != nullptr) {
jbyteArray e = bignumToArray(env, rsa->e, "e");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 1, e);
}
if (rsa->d != nullptr) {
jbyteArray d = bignumToArray(env, rsa->d, "d");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 2, d);
}
if (rsa->p != nullptr) {
jbyteArray p = bignumToArray(env, rsa->p, "p");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 3, p);
}
if (rsa->q != nullptr) {
jbyteArray q = bignumToArray(env, rsa->q, "q");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 4, q);
}
if (rsa->dmp1 != nullptr) {
jbyteArray dmp1 = bignumToArray(env, rsa->dmp1, "dmp1");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 5, dmp1);
}
if (rsa->dmq1 != nullptr) {
jbyteArray dmq1 = bignumToArray(env, rsa->dmq1, "dmq1");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 6, dmq1);
}
if (rsa->iqmp != nullptr) {
jbyteArray iqmp = bignumToArray(env, rsa->iqmp, "iqmp");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 7, iqmp);
}
return joa;
}
static void NativeCrypto_chacha20_encrypt_decrypt(JNIEnv* env, jclass, jbyteArray inBytes,
jint inOffset, jbyteArray outBytes, jint outOffset, jint length, jbyteArray keyBytes,
jbyteArray nonceBytes, jint blockCounter) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("chacha20_encrypt_decrypt");
ScopedByteArrayRO in(env, inBytes);
if (in.get() == nullptr) {
JNI_TRACE("chacha20_encrypt_decrypt => threw exception: could not read input bytes");
return;
}
ScopedByteArrayRW out(env, outBytes);
if (out.get() == nullptr) {
JNI_TRACE("chacha20_encrypt_decrypt => threw exception: could not read output bytes");
return;
}
ScopedByteArrayRO key(env, keyBytes);
if (key.get() == nullptr) {
JNI_TRACE("chacha20_encrypt_decrypt => threw exception: could not read key bytes");
return;
}
ScopedByteArrayRO nonce(env, nonceBytes);
if (nonce.get() == nullptr) {
JNI_TRACE("chacha20_encrypt_decrypt => threw exception: could not read nonce bytes");
return;
}
CRYPTO_chacha_20(
reinterpret_cast<unsigned char*>(out.get()) + outOffset,
reinterpret_cast<const unsigned char*>(in.get()) + inOffset,
length,
reinterpret_cast<const unsigned char*>(key.get()),
reinterpret_cast<const unsigned char*>(nonce.get()),
blockCounter);
}
static jlong NativeCrypto_EC_GROUP_new_by_curve_name(JNIEnv* env, jclass, jstring curveNameJava) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("EC_GROUP_new_by_curve_name(%p)", curveNameJava);
ScopedUtfChars curveName(env, curveNameJava);
if (curveName.c_str() == nullptr) {
return 0;
}
JNI_TRACE("EC_GROUP_new_by_curve_name(%s)", curveName.c_str());
int nid = OBJ_sn2nid(curveName.c_str());
if (nid == NID_undef) {
JNI_TRACE("EC_GROUP_new_by_curve_name(%s) => unknown NID name", curveName.c_str());
return 0;
}
EC_GROUP* group = EC_GROUP_new_by_curve_name(nid);
if (group == nullptr) {
JNI_TRACE("EC_GROUP_new_by_curve_name(%s) => unknown NID %d", curveName.c_str(), nid);
ERR_clear_error();
return 0;
}
JNI_TRACE("EC_GROUP_new_by_curve_name(%s) => %p", curveName.c_str(), group);
return reinterpret_cast<uintptr_t>(group);
}
static jlong NativeCrypto_EC_GROUP_new_arbitrary(JNIEnv* env, jclass, jbyteArray pBytes,
jbyteArray aBytes, jbyteArray bBytes,
jbyteArray xBytes, jbyteArray yBytes,
jbyteArray orderBytes, jint cofactorInt) {
CHECK_ERROR_QUEUE_ON_RETURN;
BIGNUM *p = nullptr, *a = nullptr, *b = nullptr, *x = nullptr, *y = nullptr;
BIGNUM *order = nullptr, *cofactor = nullptr;
JNI_TRACE("EC_GROUP_new_arbitrary");
if (cofactorInt < 1) {
conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException",
"cofactor < 1");
return 0;
}
cofactor = BN_new();
if (cofactor == nullptr) {
return 0;
}
int ok = 1;
if (!arrayToBignum(env, pBytes, &p) || !arrayToBignum(env, aBytes, &a) ||
!arrayToBignum(env, bBytes, &b) || !arrayToBignum(env, xBytes, &x) ||
!arrayToBignum(env, yBytes, &y) || !arrayToBignum(env, orderBytes, &order) ||
!BN_set_word(cofactor, static_cast<uint32_t>(cofactorInt))) {
ok = 0;
}
bssl::UniquePtr<BIGNUM> pStorage(p);
bssl::UniquePtr<BIGNUM> aStorage(a);
bssl::UniquePtr<BIGNUM> bStorage(b);
bssl::UniquePtr<BIGNUM> xStorage(x);
bssl::UniquePtr<BIGNUM> yStorage(y);
bssl::UniquePtr<BIGNUM> orderStorage(order);
bssl::UniquePtr<BIGNUM> cofactorStorage(cofactor);
if (!ok) {
return 0;
}
bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_curve_GFp(p, a, b, ctx.get()));
if (group.get() == nullptr) {
JNI_TRACE("EC_GROUP_new_curve_GFp => null");
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_GROUP_new_curve_GFp");
return 0;
}
bssl::UniquePtr<EC_POINT> generator(EC_POINT_new(group.get()));
if (generator.get() == nullptr) {
JNI_TRACE("EC_POINT_new => null");
ERR_clear_error();
return 0;
}
if (!EC_POINT_set_affine_coordinates_GFp(group.get(), generator.get(), x, y, ctx.get())) {
JNI_TRACE("EC_POINT_set_affine_coordinates_GFp => error");
conscrypt::jniutil::throwExceptionFromBoringSSLError(env,
"EC_POINT_set_affine_coordinates_GFp");
return 0;
}
if (!EC_GROUP_set_generator(group.get(), generator.get(), order, cofactor)) {
JNI_TRACE("EC_GROUP_set_generator => error");
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_GROUP_set_generator");
return 0;
}
JNI_TRACE("EC_GROUP_new_arbitrary => %p", group.get());
return reinterpret_cast<uintptr_t>(group.release());
}
static jstring NativeCrypto_EC_GROUP_get_curve_name(JNIEnv* env, jclass, jobject groupRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
JNI_TRACE("EC_GROUP_get_curve_name(%p)", group);
if (group == nullptr) {
JNI_TRACE("EC_GROUP_get_curve_name => group == null");
return nullptr;
}
int nid = EC_GROUP_get_curve_name(group);
if (nid == NID_undef) {
JNI_TRACE("EC_GROUP_get_curve_name(%p) => unnamed curve", group);
return nullptr;
}
const char* shortName = OBJ_nid2sn(nid);
JNI_TRACE("EC_GROUP_get_curve_name(%p) => \"%s\"", group, shortName);
return env->NewStringUTF(shortName);
}
static jobjectArray NativeCrypto_EC_GROUP_get_curve(JNIEnv* env, jclass, jobject groupRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
JNI_TRACE("EC_GROUP_get_curve(%p)", group);
if (group == nullptr) {
JNI_TRACE("EC_GROUP_get_curve => group == null");
return nullptr;
}
bssl::UniquePtr<BIGNUM> p(BN_new());
bssl::UniquePtr<BIGNUM> a(BN_new());
bssl::UniquePtr<BIGNUM> b(BN_new());
int ret = EC_GROUP_get_curve_GFp(group, p.get(), a.get(), b.get(), nullptr);
if (ret != 1) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_GROUP_get_curve");
return nullptr;
}
jobjectArray joa = env->NewObjectArray(3, conscrypt::jniutil::byteArrayClass, nullptr);
if (joa == nullptr) {
return nullptr;
}
jbyteArray pArray = bignumToArray(env, p.get(), "p");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 0, pArray);
jbyteArray aArray = bignumToArray(env, a.get(), "a");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 1, aArray);
jbyteArray bArray = bignumToArray(env, b.get(), "b");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 2, bArray);
JNI_TRACE("EC_GROUP_get_curve(%p) => %p", group, joa);
return joa;
}
static jbyteArray NativeCrypto_EC_GROUP_get_order(JNIEnv* env, jclass, jobject groupRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
JNI_TRACE("EC_GROUP_get_order(%p)", group);
if (group == nullptr) {
return nullptr;
}
bssl::UniquePtr<BIGNUM> order(BN_new());
if (order.get() == nullptr) {
JNI_TRACE("EC_GROUP_get_order(%p) => can't create BN", group);
conscrypt::jniutil::throwOutOfMemory(env, "BN_new");
return nullptr;
}
if (EC_GROUP_get_order(group, order.get(), nullptr) != 1) {
JNI_TRACE("EC_GROUP_get_order(%p) => threw error", group);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_GROUP_get_order");
return nullptr;
}
jbyteArray orderArray = bignumToArray(env, order.get(), "order");
if (env->ExceptionCheck()) {
return nullptr;
}
JNI_TRACE("EC_GROUP_get_order(%p) => %p", group, orderArray);
return orderArray;
}
static jint NativeCrypto_EC_GROUP_get_degree(JNIEnv* env, jclass, jobject groupRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
JNI_TRACE("EC_GROUP_get_degree(%p)", group);
if (group == nullptr) {
return 0;
}
jint degree = static_cast<jint>(EC_GROUP_get_degree(group));
if (degree == 0) {
JNI_TRACE("EC_GROUP_get_degree(%p) => unsupported", group);
conscrypt::jniutil::throwRuntimeException(env, "not supported");
ERR_clear_error();
return 0;
}
JNI_TRACE("EC_GROUP_get_degree(%p) => %d", group, degree);
return degree;
}
static jbyteArray NativeCrypto_EC_GROUP_get_cofactor(JNIEnv* env, jclass, jobject groupRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
JNI_TRACE("EC_GROUP_get_cofactor(%p)", group);
if (group == nullptr) {
return nullptr;
}
bssl::UniquePtr<BIGNUM> cofactor(BN_new());
if (cofactor.get() == nullptr) {
JNI_TRACE("EC_GROUP_get_cofactor(%p) => can't create BN", group);
conscrypt::jniutil::throwOutOfMemory(env, "BN_new");
return nullptr;
}
if (EC_GROUP_get_cofactor(group, cofactor.get(), nullptr) != 1) {
JNI_TRACE("EC_GROUP_get_cofactor(%p) => threw error", group);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_GROUP_get_cofactor");
return nullptr;
}
jbyteArray cofactorArray = bignumToArray(env, cofactor.get(), "cofactor");
if (env->ExceptionCheck()) {
return nullptr;
}
JNI_TRACE("EC_GROUP_get_cofactor(%p) => %p", group, cofactorArray);
return cofactorArray;
}
static void NativeCrypto_EC_GROUP_clear_free(JNIEnv* env, jclass, jlong groupRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EC_GROUP* group = reinterpret_cast<EC_GROUP*>(groupRef);
JNI_TRACE("EC_GROUP_clear_free(%p)", group);
if (group == nullptr) {
JNI_TRACE("EC_GROUP_clear_free => group == null");
conscrypt::jniutil::throwNullPointerException(env, "group == null");
return;
}
EC_GROUP_free(group);
JNI_TRACE("EC_GROUP_clear_free(%p) => success", group);
}
static jlong NativeCrypto_EC_GROUP_get_generator(JNIEnv* env, jclass, jobject groupRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
JNI_TRACE("EC_GROUP_get_generator(%p)", group);
if (group == nullptr) {
JNI_TRACE("EC_POINT_get_generator(%p) => group == null", group);
return 0;
}
const EC_POINT* generator = EC_GROUP_get0_generator(group);
bssl::UniquePtr<EC_POINT> dup(EC_POINT_dup(generator, group));
if (dup.get() == nullptr) {
JNI_TRACE("EC_GROUP_get_generator(%p) => oom error", group);
conscrypt::jniutil::throwOutOfMemory(env, "unable to dupe generator");
return 0;
}
JNI_TRACE("EC_GROUP_get_generator(%p) => %p", group, dup.get());
return reinterpret_cast<uintptr_t>(dup.release());
}
static jlong NativeCrypto_EC_POINT_new(JNIEnv* env, jclass, jobject groupRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
JNI_TRACE("EC_POINT_new(%p)", group);
if (group == nullptr) {
JNI_TRACE("EC_POINT_new(%p) => group == null", group);
return 0;
}
EC_POINT* point = EC_POINT_new(group);
if (point == nullptr) {
conscrypt::jniutil::throwOutOfMemory(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) {
CHECK_ERROR_QUEUE_ON_RETURN;
EC_POINT* group = reinterpret_cast<EC_POINT*>(groupRef);
JNI_TRACE("EC_POINT_clear_free(%p)", group);
if (group == nullptr) {
JNI_TRACE("EC_POINT_clear_free => group == null");
conscrypt::jniutil::throwNullPointerException(env, "group == null");
return;
}
EC_POINT_free(group);
JNI_TRACE("EC_POINT_clear_free(%p) => success", group);
}
static void NativeCrypto_EC_POINT_set_affine_coordinates(JNIEnv* env, jclass, jobject groupRef,
jobject pointRef, jbyteArray xjavaBytes,
jbyteArray yjavaBytes) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("EC_POINT_set_affine_coordinates(%p, %p, %p, %p)", groupRef, pointRef, xjavaBytes,
yjavaBytes);
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
if (group == nullptr) {
return;
}
EC_POINT* point = fromContextObject<EC_POINT>(env, pointRef);
if (point == nullptr) {
return;
}
JNI_TRACE("EC_POINT_set_affine_coordinates(%p, %p, %p, %p) <- ptr", group, point, xjavaBytes,
yjavaBytes);
BIGNUM* xRef = nullptr;
if (!arrayToBignum(env, xjavaBytes, &xRef)) {
return;
}
bssl::UniquePtr<BIGNUM> x(xRef);
BIGNUM* yRef = nullptr;
if (!arrayToBignum(env, yjavaBytes, &yRef)) {
return;
}
bssl::UniquePtr<BIGNUM> y(yRef);
int ret = EC_POINT_set_affine_coordinates_GFp(group, point, x.get(), y.get(), nullptr);
if (ret != 1) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env,
"EC_POINT_set_affine_coordinates");
return;
}
JNI_TRACE("EC_POINT_set_affine_coordinates(%p, %p, %p, %p) => %d", group, point, xjavaBytes,
yjavaBytes, ret);
}
static jobjectArray NativeCrypto_EC_POINT_get_affine_coordinates(JNIEnv* env, jclass,
jobject groupRef,
jobject pointRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("EC_POINT_get_affine_coordinates(%p, %p)", groupRef, pointRef);
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
if (group == nullptr) {
return nullptr;
}
const EC_POINT* point = fromContextObject<EC_POINT>(env, pointRef);
if (point == nullptr) {
return nullptr;
}
JNI_TRACE("EC_POINT_get_affine_coordinates(%p, %p) <- ptr", group, point);
bssl::UniquePtr<BIGNUM> x(BN_new());
bssl::UniquePtr<BIGNUM> y(BN_new());
int ret = EC_POINT_get_affine_coordinates_GFp(group, point, x.get(), y.get(), nullptr);
if (ret != 1) {
JNI_TRACE("EC_POINT_get_affine_coordinates(%p, %p)", group, point);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env,
"EC_POINT_get_affine_coordinates");
return nullptr;
}
jobjectArray joa = env->NewObjectArray(2, conscrypt::jniutil::byteArrayClass, nullptr);
if (joa == nullptr) {
return nullptr;
}
jbyteArray xBytes = bignumToArray(env, x.get(), "x");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 0, xBytes);
jbyteArray yBytes = bignumToArray(env, y.get(), "y");
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(joa, 1, yBytes);
JNI_TRACE("EC_POINT_get_affine_coordinates(%p, %p) => %p", group, point, joa);
return joa;
}
static jlong NativeCrypto_EC_KEY_generate_key(JNIEnv* env, jclass, jobject groupRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
JNI_TRACE("EC_KEY_generate_key(%p)", group);
if (group == nullptr) {
return 0;
}
bssl::UniquePtr<EC_KEY> eckey(EC_KEY_new());
if (eckey.get() == nullptr) {
JNI_TRACE("EC_KEY_generate_key(%p) => EC_KEY_new() oom", group);
conscrypt::jniutil::throwOutOfMemory(env, "Unable to create an EC_KEY");
return 0;
}
if (EC_KEY_set_group(eckey.get(), group) != 1) {
JNI_TRACE("EC_KEY_generate_key(%p) => EC_KEY_set_group error", group);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_KEY_set_group");
return 0;
}
if (EC_KEY_generate_key(eckey.get()) != 1) {
JNI_TRACE("EC_KEY_generate_key(%p) => EC_KEY_generate_key error", group);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_KEY_set_group");
return 0;
}
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
if (pkey.get() == nullptr) {
JNI_TRACE("EC_KEY_generate_key(%p) => threw error", group);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EC_KEY_generate_key");
return 0;
}
if (EVP_PKEY_assign_EC_KEY(pkey.get(), eckey.get()) != 1) {
conscrypt::jniutil::throwRuntimeException(env, "EVP_PKEY_assign_EC_KEY failed");
ERR_clear_error();
return 0;
}
OWNERSHIP_TRANSFERRED(eckey);
JNI_TRACE("EC_KEY_generate_key(%p) => %p", group, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
static jlong NativeCrypto_EC_KEY_get1_group(JNIEnv* env, jclass, jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("EC_KEY_get1_group(%p)", pkey);
if (pkey == nullptr) {
JNI_TRACE("EC_KEY_get1_group(%p) => pkey == null", pkey);
return 0;
}
if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
conscrypt::jniutil::throwRuntimeException(env, "not EC key");
JNI_TRACE("EC_KEY_get1_group(%p) => not EC key (type == %d)", pkey,
EVP_PKEY_type(pkey->type));
return 0;
}
EC_GROUP* group = EC_GROUP_dup(EC_KEY_get0_group(pkey->pkey.ec));
JNI_TRACE("EC_KEY_get1_group(%p) => %p", pkey, group);
return reinterpret_cast<uintptr_t>(group);
}
static jbyteArray NativeCrypto_EC_KEY_get_private_key(JNIEnv* env, jclass, jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("EC_KEY_get_private_key(%p)", pkey);
if (pkey == nullptr) {
JNI_TRACE("EC_KEY_get_private_key => pkey == null");
return nullptr;
}
bssl::UniquePtr<EC_KEY> eckey(EVP_PKEY_get1_EC_KEY(pkey));
if (eckey.get() == nullptr) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_PKEY_get1_EC_KEY");
return nullptr;
}
const BIGNUM* privkey = EC_KEY_get0_private_key(eckey.get());
jbyteArray privBytes = bignumToArray(env, privkey, "privkey");
if (env->ExceptionCheck()) {
JNI_TRACE("EC_KEY_get_private_key(%p) => threw error", pkey);
return nullptr;
}
JNI_TRACE("EC_KEY_get_private_key(%p) => %p", pkey, privBytes);
return privBytes;
}
static jlong NativeCrypto_EC_KEY_get_public_key(JNIEnv* env, jclass, jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("EC_KEY_get_public_key(%p)", pkey);
if (pkey == nullptr) {
JNI_TRACE("EC_KEY_get_public_key => pkey == null");
return 0;
}
bssl::UniquePtr<EC_KEY> eckey(EVP_PKEY_get1_EC_KEY(pkey));
if (eckey.get() == nullptr) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_PKEY_get1_EC_KEY");
return 0;
}
bssl::UniquePtr<EC_POINT> dup(
EC_POINT_dup(EC_KEY_get0_public_key(eckey.get()), EC_KEY_get0_group(eckey.get())));
if (dup.get() == nullptr) {
JNI_TRACE("EC_KEY_get_public_key(%p) => can't dup public key", pkey);
conscrypt::jniutil::throwRuntimeException(env, "EC_POINT_dup");
ERR_clear_error();
return 0;
}
JNI_TRACE("EC_KEY_get_public_key(%p) => %p", pkey, dup.get());
return reinterpret_cast<uintptr_t>(dup.release());
}
static jbyteArray NativeCrypto_EC_KEY_marshal_curve_name(JNIEnv* env, jclass, jobject groupRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
JNI_TRACE("EC_KEY_marshal_curve_name(%p)", group);
if (group == nullptr) {
conscrypt::jniutil::throwIOException(env, "Invalid group pointer");
return nullptr;
}
bssl::ScopedCBB cbb;
if (!CBB_init(cbb.get(), 64)) {
conscrypt::jniutil::throwOutOfMemory(env, "CBB_init failed");
JNI_TRACE("CBB_init failed");
return nullptr;
}
if (!EC_KEY_marshal_curve_name(cbb.get(), group)) {
conscrypt::jniutil::throwIOException(env, "Error writing ASN.1 encoding");
ERR_clear_error();
JNI_TRACE("group=%p EC_KEY_marshal_curve_name => error", group);
return nullptr;
}
return CBBToByteArray(env, cbb.get());
}
static jlong NativeCrypto_EC_KEY_parse_curve_name(JNIEnv* env, jclass, jbyteArray curveNameBytes) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("EC_KEY_parse_curve_name(%p)", curveNameBytes);
ScopedByteArrayRO bytes(env, curveNameBytes);
if (bytes.get() == nullptr) {
conscrypt::jniutil::throwIOException(env, "Error reading ASN.1 encoding");
JNI_TRACE("bytes=%p EC_KEY_parse_curve_name => threw exception", curveNameBytes);
return 0;
}
CBS cbs;
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(bytes.get()), bytes.size());
bssl::UniquePtr<EC_GROUP> group(EC_KEY_parse_curve_name(&cbs));
if (!group || CBS_len(&cbs) != 0) {
conscrypt::jniutil::throwIOException(env, "Error reading ASN.1 encoding");
ERR_clear_error();
JNI_TRACE("bytes=%p EC_KEY_parse_curve_name => threw exception", curveNameBytes);
return 0;
}
JNI_TRACE("bytes=%p EC_KEY_parse_curve_name => %p", curveNameBytes, group.get());
return reinterpret_cast<uintptr_t>(group.release());
}
static jint NativeCrypto_ECDH_compute_key(JNIEnv* env, jclass, jbyteArray outArray, jint outOffset,
jobject pubkeyRef, jobject privkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("ECDH_compute_key(%p, %d, %p, %p)", outArray, outOffset, pubkeyRef, privkeyRef);
EVP_PKEY* pubPkey = fromContextObject<EVP_PKEY>(env, pubkeyRef);
if (pubPkey == nullptr) {
JNI_TRACE("ECDH_compute_key => pubPkey == null");
return -1;
}
EVP_PKEY* privPkey = fromContextObject<EVP_PKEY>(env, privkeyRef);
if (privPkey == nullptr) {
JNI_TRACE("ECDH_compute_key => privPkey == null");
return -1;
}
JNI_TRACE("ECDH_compute_key(%p, %d, %p, %p) <- ptr", outArray, outOffset, pubPkey, privPkey);
ScopedByteArrayRW out(env, outArray);
if (out.get() == nullptr) {
JNI_TRACE("ECDH_compute_key(%p, %d, %p, %p) can't get output buffer", outArray, outOffset,
pubPkey, privPkey);
return -1;
}
if (ARRAY_OFFSET_INVALID(out, outOffset)) {
conscrypt::jniutil::throwException(env, "java/lang/ArrayIndexOutOfBoundsException",
nullptr);
return -1;
}
if (pubPkey == nullptr) {
JNI_TRACE("ECDH_compute_key(%p) => pubPkey == null", pubPkey);
conscrypt::jniutil::throwNullPointerException(env, "pubPkey == null");
return -1;
}
bssl::UniquePtr<EC_KEY> pubkey(EVP_PKEY_get1_EC_KEY(pubPkey));
if (pubkey.get() == nullptr) {
JNI_TRACE("ECDH_compute_key(%p) => can't get public key", pubPkey);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_PKEY_get1_EC_KEY public",
conscrypt::jniutil::throwInvalidKeyException);
return -1;
}
const EC_POINT* pubkeyPoint = EC_KEY_get0_public_key(pubkey.get());
if (pubkeyPoint == nullptr) {
JNI_TRACE("ECDH_compute_key(%p) => can't get public key point", pubPkey);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_PKEY_get1_EC_KEY public",
conscrypt::jniutil::throwInvalidKeyException);
return -1;
}
if (privPkey == nullptr) {
JNI_TRACE("ECDH_compute_key(%p) => privKey == null", pubPkey);
conscrypt::jniutil::throwNullPointerException(env, "privPkey == null");
return -1;
}
bssl::UniquePtr<EC_KEY> privkey(EVP_PKEY_get1_EC_KEY(privPkey));
if (privkey.get() == nullptr) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_PKEY_get1_EC_KEY private",
conscrypt::jniutil::throwInvalidKeyException);
return -1;
}
std::size_t stdOutOffset = static_cast<std::size_t>(outOffset);
int outputLength = ECDH_compute_key(&out[stdOutOffset], out.size() - stdOutOffset, pubkeyPoint,
privkey.get(), nullptr /* No KDF */);
if (outputLength == -1) {
JNI_TRACE("ECDH_compute_key(%p) => outputLength = -1", pubPkey);
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "ECDH_compute_key",
conscrypt::jniutil::throwInvalidKeyException);
return -1;
}
JNI_TRACE("ECDH_compute_key(%p) => outputLength=%d", pubPkey, outputLength);
return outputLength;
}
static jint NativeCrypto_ECDSA_size(JNIEnv* env, jclass, jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("ECDSA_size(%p)", pkey);
if (pkey == nullptr) {
return 0;
}
bssl::UniquePtr<EC_KEY> ec_key(EVP_PKEY_get1_EC_KEY(pkey));
if (ec_key.get() == nullptr) {
conscrypt::jniutil::throwRuntimeException(env, "ECDSA_size failed");
ERR_clear_error();
return 0;
}
size_t size = ECDSA_size(ec_key.get());
JNI_TRACE("ECDSA_size(%p) => %zu", pkey, size);
return static_cast<jint>(size);
}
static jint NativeCrypto_ECDSA_sign(JNIEnv* env, jclass, jbyteArray data, jbyteArray sig,
jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("ECDSA_sign(%p, %p, %p)", data, sig, pkey);
if (pkey == nullptr) {
return -1;
}
bssl::UniquePtr<EC_KEY> ec_key(EVP_PKEY_get1_EC_KEY(pkey));
if (ec_key.get() == nullptr) {
return -1;
}
ScopedByteArrayRO data_array(env, data);
if (data_array.get() == nullptr) {
return -1;
}
ScopedByteArrayRW sig_array(env, sig);
if (sig_array.get() == nullptr) {
return -1;
}
unsigned int sig_size;
int result = ECDSA_sign(0, reinterpret_cast<const unsigned char*>(data_array.get()),
data_array.size(), reinterpret_cast<unsigned char*>(sig_array.get()),
&sig_size, ec_key.get());
if (result == 0) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "ECDSA_sign");
JNI_TRACE("ECDSA_sign => threw error");
return -1;
}
JNI_TRACE("ECDSA_sign(%p, %p, %p) => %d", data, sig, pkey, sig_size);
return static_cast<jint>(sig_size);
}
static jint NativeCrypto_ECDSA_verify(JNIEnv* env, jclass, jbyteArray data, jbyteArray sig,
jobject pkeyRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
JNI_TRACE("ECDSA_verify(%p, %p, %p)", data, sig, pkey);
if (pkey == nullptr) {
return -1;
}
bssl::UniquePtr<EC_KEY> ec_key(EVP_PKEY_get1_EC_KEY(pkey));
if (ec_key.get() == nullptr) {
return -1;
}
ScopedByteArrayRO data_array(env, data);
if (data_array.get() == nullptr) {
return -1;
}
ScopedByteArrayRO sig_array(env, sig);
if (sig_array.get() == nullptr) {
return -1;
}
int result =
ECDSA_verify(0, reinterpret_cast<const unsigned char*>(data_array.get()),
data_array.size(), reinterpret_cast<const unsigned char*>(sig_array.get()),
sig_array.size(), ec_key.get());
if (result == 0) {
// NOLINTNEXTLINE(runtime/int)
unsigned long error = ERR_peek_last_error();
if ((ERR_GET_LIB(error) == ERR_LIB_ECDSA) &&
(ERR_GET_REASON(error) == ECDSA_R_BAD_SIGNATURE)) {
// This error just means the signature didn't verify, so clear the error and return
// a failed verification
ERR_clear_error();
JNI_TRACE("ECDSA_verify(%p, %p, %p) => %d", data, sig, pkey, result);
return 0;
}
if (error != 0) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "ECDSA_verify");
JNI_TRACE("ECDSA_verify => threw error");
return -1;
}
return 0;
}
JNI_TRACE("ECDSA_verify(%p, %p, %p) => %d", data, sig, pkey, result);
return static_cast<jint>(result);
}
static jlong NativeCrypto_EVP_MD_CTX_create(JNIEnv* env, jclass) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE_MD("EVP_MD_CTX_create()");
bssl::UniquePtr<EVP_MD_CTX> ctx(EVP_MD_CTX_create());
if (ctx.get() == nullptr) {
conscrypt::jniutil::throwOutOfMemory(env, "Unable create a EVP_MD_CTX");
return 0;
}
JNI_TRACE_MD("EVP_MD_CTX_create() => %p", ctx.get());
return reinterpret_cast<uintptr_t>(ctx.release());
}
static void NativeCrypto_EVP_MD_CTX_cleanup(JNIEnv* env, jclass, jobject ctxRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, ctxRef);
JNI_TRACE_MD("EVP_MD_CTX_cleanup(%p)", ctx);
if (ctx != nullptr) {
EVP_MD_CTX_cleanup(ctx);
}
}
static void NativeCrypto_EVP_MD_CTX_destroy(JNIEnv* env, jclass, jlong ctxRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
JNI_TRACE_MD("EVP_MD_CTX_destroy(%p)", ctx);
if (ctx != nullptr) {
EVP_MD_CTX_destroy(ctx);
}
}
static jint NativeCrypto_EVP_MD_CTX_copy_ex(JNIEnv* env, jclass, jobject dstCtxRef,
jobject srcCtxRef) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE_MD("EVP_MD_CTX_copy_ex(%p. %p)", dstCtxRef, srcCtxRef);
EVP_MD_CTX* dst_ctx = fromContextObject<EVP_MD_CTX>(env, dstCtxRef);
if (dst_ctx == nullptr) {
JNI_TRACE_MD("EVP_MD_CTX_copy_ex => dst_ctx == null");
return 0;
}
const EVP_MD_CTX* src_ctx = fromContextObject<EVP_MD_CTX>(env, srcCtxRef);
if (src_ctx == nullptr) {
JNI_TRACE_MD("EVP_MD_CTX_copy_ex => src_ctx == null");
return 0;
}
JNI_TRACE_MD("EVP_MD_CTX_copy_ex(%p. %p) <- ptr", dst_ctx, src_ctx);
int result = EVP_MD_CTX_copy_ex(dst_ctx, src_ctx);
if (result == 0) {
conscrypt::jniutil::throwRuntimeException(env, "Unable to copy EVP_MD_CTX");
ERR_clear_error();
}
JNI_TRACE_MD("EVP_MD_CTX_copy_ex(%p, %p) => %d", dst_ctx, src_ctx, result);
return result;
}
/*
* public static native int EVP_DigestFinal_ex(long, byte[], int)
*/
static jint NativeCrypto_EVP_DigestFinal_ex(JNIEnv* env, jclass, jobject ctxRef, jbyteArray hash,
jint offset) {
CHECK_ERROR_QUEUE_ON_RETURN;
EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, ctxRef);
JNI_TRACE_MD("EVP_DigestFinal_ex(%p, %p, %d)", ctx, hash, offset);
if (ctx == nullptr) {
JNI_TRACE("EVP_DigestFinal_ex => ctx == null");