| /* |
| * 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/curve25519.h> |
| #include <openssl/cmac.h> |
| #include <openssl/crypto.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_BIO(JNIEnv* env, jlong bio_address) { |
| BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bio_address)); |
| if (bio == nullptr) { |
| 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, const ASN1_BIT_STRING* bitStr) { |
| int size = ASN1_STRING_length(bitStr) * 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_id(pkey); |
| 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_get0_n(rsa.get()), "n"); |
| if (env->ExceptionCheck()) { |
| return nullptr; |
| } |
| env->SetObjectArrayElement(joa, 0, n); |
| |
| jbyteArray e = bignumToArray(env, RSA_get0_e(rsa.get()), "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_get0_n(rsa.get()), "n"); |
| if (env->ExceptionCheck()) { |
| return nullptr; |
| } |
| env->SetObjectArrayElement(joa, 0, n); |
| |
| if (RSA_get0_e(rsa.get()) != nullptr) { |
| jbyteArray e = bignumToArray(env, RSA_get0_e(rsa.get()), "e"); |
| if (env->ExceptionCheck()) { |
| return nullptr; |
| } |
| env->SetObjectArrayElement(joa, 1, e); |
| } |
| |
| if (RSA_get0_d(rsa.get()) != nullptr) { |
| jbyteArray d = bignumToArray(env, RSA_get0_d(rsa.get()), "d"); |
| if (env->ExceptionCheck()) { |
| return nullptr; |
| } |
| env->SetObjectArrayElement(joa, 2, d); |
| } |
| |
| if (RSA_get0_p(rsa.get()) != nullptr) { |
| jbyteArray p = bignumToArray(env, RSA_get0_p(rsa.get()), "p"); |
| if (env->ExceptionCheck()) { |
| return nullptr; |
| } |
| env->SetObjectArrayElement(joa, 3, p); |
| } |
| |
| if (RSA_get0_q(rsa.get()) != nullptr) { |
| jbyteArray q = bignumToArray(env, RSA_get0_q(rsa.get()), "q"); |
| if (env->ExceptionCheck()) { |
| return nullptr; |
| } |
| env->SetObjectArrayElement(joa, 4, q); |
| } |
| |
| if (RSA_get0_dmp1(rsa.get()) != nullptr) { |
| jbyteArray dmp1 = bignumToArray(env, RSA_get0_dmp1(rsa.get()), "dmp1"); |
| if (env->ExceptionCheck()) { |
| return nullptr; |
| } |
| env->SetObjectArrayElement(joa, 5, dmp1); |
| } |
| |
| if (RSA_get0_dmq1(rsa.get()) != nullptr) { |
| jbyteArray dmq1 = bignumToArray(env, RSA_get0_dmq1(rsa.get()), "dmq1"); |
| if (env->ExceptionCheck()) { |
| return nullptr; |
| } |
| env->SetObjectArrayElement(joa, 6, dmq1); |
| } |
| |
| if (RSA_get0_iqmp(rsa.get()) != nullptr) { |
| jbyteArray iqmp = bignumToArray(env, RSA_get0_iqmp(rsa.get()), "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_id(pkey) != 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_id(pkey)); |
| return 0; |
| } |
| |
| EC_GROUP* group = EC_GROUP_dup(EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey))); |
| 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 jboolean NativeCrypto_X25519(JNIEnv* env, jclass, jbyteArray outArray, |
| jbyteArray privkeyArray, jbyteArray pubkeyArray) { |
| CHECK_ERROR_QUEUE_ON_RETURN; |
| JNI_TRACE("X25519(%p, %p, %p)", outArray, privkeyArray, pubkeyArray); |
| |
| ScopedByteArrayRW out(env, outArray); |
| if (out.get() == nullptr) { |
| JNI_TRACE("X25519(%p, %p, %p) can't get output buffer", outArray, privkeyArray, pubkeyArray); |
| return JNI_FALSE; |
| } |
| |
| ScopedByteArrayRO privkey(env, privkeyArray); |
| if (privkey.get() == nullptr) { |
| JNI_TRACE("X25519(%p) => privkey == null", outArray); |
| return JNI_FALSE; |
| } |
| |
| ScopedByteArrayRO pubkey(env, pubkeyArray); |
| if (pubkey.get() == nullptr) { |
| JNI_TRACE("X25519(%p) => pubkey == null", outArray); |
| return JNI_FALSE; |
| } |
| |
| if (X25519(reinterpret_cast<uint8_t*>(out.get()), |
| reinterpret_cast<const uint8_t*>(privkey.get()), |
| reinterpret_cast<const uint8_t*>(pubkey.get())) != 1) { |
| JNI_TRACE("X25519(%p) => failure", outArray); |
| conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "X25519", |
| conscrypt::jniutil::throwInvalidKeyException); |
| return JNI_FALSE; |
| } |
| |
| JNI_TRACE("X25519(%p) => success", outArray); |
| return JNI_TRUE; |
| } |
| |
| static void NativeCrypto_X25519_keypair(JNIEnv* env, jclass, jbyteArray outPublicArray, jbyteArray outPrivateArray) { |
| CHECK_ERROR_QUEUE_ON_RETURN; |
| JNI_TRACE("X25519_keypair(%p, %p)", outPublicArray, outPrivateArray); |
| |
| ScopedByteArrayRW outPublic(env, outPublicArray); |
| if (outPublic.get() == nullptr) { |
| JNI_TRACE("X25519_keypair(%p, %p) can't get output public key buffer", outPublicArray, outPrivateArray); |
| return; |
| } |
| |
| ScopedByteArrayRW outPrivate(env, outPrivateArray); |
| if (outPrivate.get() == nullptr) { |
| JNI_TRACE("X25519_keypair(%p, %p) can't get output private key buffer", outPublicArray, outPrivateArray); |
| return; |
| } |
| |
| if (outPublic.size() != X25519_PUBLIC_VALUE_LEN || outPrivate.size() != X25519_PRIVATE_KEY_LEN) { |
| conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException", "Output key array length != 32"); |
| return; |
| } |
| |
| X25519_keypair(reinterpret_cast<uint8_t*>(outPublic.get()), reinterpret_cast<uint8_t*>(outPrivate.get())); |
| JNI_TRACE("X25519_keypair(%p, %p) => success", outPublicArray, outPrivateArray); |
| } |
| |
| 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"); |
| return -1; |
| } else if (hash == nullptr) { |
| conscrypt::jniutil::throwNullPointerException(env, "hash == null"); |
| return -1; |
| } |
| |
| ScopedByteArrayRW hashBytes(env, hash); |
| if (hashBytes.get() == nullptr) { |
| return -1; |
| } |
| unsigned int bytesWritten = static_cast<unsigned int>(-1); |
| int ok = EVP_DigestFinal_ex(ctx, reinterpret_cast<unsigned char*>(hashBytes.get() + offset), |
| &bytesWritten); |
| if (ok == 0) { |
| conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_DigestFinal_ex"); |
| return -1; |
| } |
| |
| JNI_TRACE_MD("EVP_DigestFinal_ex(%p, %p, %d) => %d (%d)", ctx, hash, offset, bytesWritten, ok); |
| return static_cast<jint>(bytesWritten); |
| } |
| |
| static jint NativeCrypto_EVP_DigestInit_ex(JNIEnv* env, jclass, jobject evpMdCtxRef, |
| jlong evpMdRef) { |
| CHECK_ERROR_QUEUE_ON_RETURN; |
| EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, evpMdCtxRef); |
| const EVP_MD* evp_md = reinterpret_cast<const EVP_MD*>(evpMdRef); |
| JNI_TRACE_MD("EVP_DigestInit_ex(%p, %p)", ctx, evp_md); |
| |
| if (ctx == nullptr) { |
| JNI_TRACE("EVP_DigestInit_ex(%p) => ctx == null", evp_md); |
| return 0; |
| } else if (evp_md == nullptr) { |
| conscrypt::jniutil::throwNullPointerException(env, "evp_md == null"); |
| return 0; |
| } |
| |
| int ok = EVP_DigestInit_ex(ctx, evp_md, nullptr); |
| if (ok == 0) { |
| conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_DigestInit_ex"); |
| JNI_TRACE("EVP_DigestInit_ex(%p) => threw exception", evp_md); |
| return 0; |
| } |
| JNI_TRACE_MD("EVP_DigestInit_ex(%p, %p) => %d", ctx, evp_md, ok); |
| return ok; |
| } |
| |
| /* |
| * public static native int EVP_get_digestbyname(java.lang.String) |
| */ |
| static jlong NativeCrypto_EVP_get_digestbyname(JNIEnv* env, jclass, jstring algorithm) { |
| CHECK_ERROR_QUEUE_ON_RETURN; |
| JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%p)", algorithm); |
| |
| if (algorithm == nullptr) { |
| conscrypt::jniutil::throwNullPointerException(env, nullptr); |
| return -1; |
| } |
| |
| ScopedUtfChars algorithmChars(env, algorithm); |
| if (algorithmChars.c_str() == nullptr) { |
| return 0; |
| } |
| JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%s)", algorithmChars.c_str()); |
| |
| const char* alg = algorithmChars.c_str(); |
| const EVP_MD* md; |
| |
| if (strcasecmp(alg, "md4") == 0) { |
| md = EVP_md4(); |
| } else if (strcasecmp(alg, "md5") == 0) { |
| md = EVP_md5(); |
| } else if (strcasecmp(alg, "sha1") == 0) { |
| md = EVP_sha1(); |
| } else if (strcasecmp(alg, "sha224") == 0) { |
| md = EVP_sha224(); |
| } else if (strcasecmp(alg, "sha256") == 0) { |
| md = EVP_sha256(); |
| } else if (strcasecmp(alg, "sha384") == 0) { |
| md = EVP_sha384(); |
| } else if (strcasecmp(alg, "sha512") == 0) { |
| md = EVP_sha512(); |
| } else { |
| JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%s) => error", alg); |
| conscrypt::jniutil::throwRuntimeException(env, "Hash algorithm not found"); |
| return 0; |
| } |
| |
| return reinterpret_cast<uintptr_t>(md); |
| } |
| |
| /* |
| * public static native int EVP_MD_size(long) |
| */ |
| static jint NativeCrypto_EVP_MD_size(JNIEnv* env, jclass, jlong evpMdRef) { |
| CHECK_ERROR_QUEUE_ON_RETURN; |
| EVP_MD* evp_md = reinterpret_cast<EVP_MD*>(evpMdRef); |
| JNI_TRACE("NativeCrypto_EVP_MD_size(%p)", evp_md); |
| |
| if (evp_md == nullptr) { |
| conscrypt::jniutil::throwNullPointerException(env, nullptr); |
| return -1; |
| } |
| |
| jint result = static_cast<jint>(EVP_MD_size(evp_md)); |
| JNI_TRACE("NativeCrypto_EVP_MD_size(%p) => %d", evp_md, result); |
| return result; |
| } |
| |
| static jlong evpDigestSignVerifyInit(JNIEnv* env, |
| int (*init_func)(EVP_MD_CTX*, EVP_PKEY_CTX**, const EVP_MD*, |
| ENGINE*, EVP_PKEY*), |
| const char* jniName, jobject evpMdCtxRef, jlong evpMdRef, |
| jobject pkeyRef) { |
| EVP_MD_CTX* mdCtx = fromContextObject<EVP_MD_CTX>(env, evpMdCtxRef); |
| if (mdCtx == nullptr) { |
| JNI_TRACE("%s => mdCtx == null", jniName); |
| return 0; |
| } |
| const EVP_MD* md = reinterpret_cast<const EVP_MD*>(evpMdRef); |
| EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef); |
| if (pkey == nullptr) { |
| JNI_TRACE("ctx=%p %s => pkey == null", mdCtx, jniName); |
| return 0; |
| } |
| JNI_TRACE("%s(%p, %p, %p) <- ptr", jniName, mdCtx, md, pkey); |
| |
| if (md == nullptr) { |
| JNI_TRACE("ctx=%p %s => md == null", mdCtx, jniName); |
| conscrypt::jniutil::throwNullPointerException(env, "md == null"); |
| return 0; |
| } |
| |
| EVP_PKEY_CTX* pctx = nullptr; |
| if (init_func(mdCtx, &pctx, md, nullptr, pkey) <= 0) { |
| JNI_TRACE("ctx=%p %s => threw exception", mdCtx, jniName); |
| conscrypt::jniutil::throwExceptionFromBoringSSLError(env, jniName); |
| return 0; |
| } |
| |
| JNI_TRACE("%s(%p, %p, %p) => success", jniName, mdCtx, md, pkey); |
| return reinterpret_cast<jlong>(pctx); |
| } |
| |
| static jlong NativeCrypto_EVP_DigestSignInit(JNIEnv* env, jclass, jobject evpMdCtxRef, |
| const jlong evpMdRef, jobject pkeyRef) { |
| CHECK_ERROR_QUEUE_ON_RETURN; |
| return evpDigestSignVerifyInit(env, EVP_DigestSignInit, "EVP_DigestSignInit", evpMdCtxRef, |
| evpMdRef, pkeyRef); |
| } |
| |
| static jlong NativeCrypto_EVP_DigestVerifyInit(JNIEnv* env, jclass, jobject evpMdCtxRef, |
| const jlong evpMdRef, jobject pkeyRef) { |
| CHECK_ERROR_QUEUE_ON_RETURN; |
| return evpDigestSignVerifyInit(env, EVP_DigestVerifyInit, "EVP_DigestVerifyInit", evpMdCtxRef, |
| evpMdRef, pkeyRef); |
| } |
| |
| static void evpUpdate(JNIEnv* env, jobject evpMdCtxRef, jlong inPtr, jint inLength, |
| const char* jniName, int (*update_func)(EVP_MD_CTX*, const void*, size_t)) { |
| EVP_MD_CTX* mdCtx = fromContextObject<EVP_MD_CTX>(env, evpMdCtxRef); |
| const void* p = reinterpret_cast<const void*>(inPtr); |
| JNI_TRACE_MD("%s(%p, %p, %d)", jniName, mdCtx, p, inLength); |
| |
| if (mdCtx == nullptr) { |
| return; |
| } |
| |
| if (p == nullptr) { |
| conscrypt::jniutil::throwNullPointerException(env, nullptr); |
| return; |
| } |
| |
| if (!update_func(mdCtx, p, static_cast<std::size_t>(inLength))) { |
| JNI_TRACE("ctx=%p %s => threw exception", mdCtx, jniName); |
| conscrypt::jniu
|