| /* |
| * Copyright (C) 2007-2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * Native glue for Java class org.conscrypt.NativeCrypto |
| */ |
| |
| #define TO_STRING1(x) #x |
| #define TO_STRING(x) TO_STRING1(x) |
| #ifndef JNI_JARJAR_PREFIX |
| #ifndef CONSCRYPT_NOT_UNBUNDLED |
| #define CONSCRYPT_UNBUNDLED |
| #endif |
| #define JNI_JARJAR_PREFIX |
| #endif |
| |
| #define LOG_TAG "NativeCrypto" |
| |
| #include <arpa/inet.h> |
| #include <fcntl.h> |
| #include <pthread.h> |
| #include <sys/socket.h> |
| #include <sys/syscall.h> |
| #include <unistd.h> |
| |
| #ifdef CONSCRYPT_UNBUNDLED |
| #include <dlfcn.h> |
| #endif |
| |
| #include <jni.h> |
| |
| #include <openssl/asn1t.h> |
| #include <openssl/engine.h> |
| #include <openssl/err.h> |
| #include <openssl/evp.h> |
| #include <openssl/rand.h> |
| #include <openssl/rsa.h> |
| #include <openssl/ssl.h> |
| #include <openssl/x509v3.h> |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| #include "crypto/ecdsa/ecs_locl.h" |
| #endif |
| |
| #ifndef CONSCRYPT_UNBUNDLED |
| /* If we're compiled unbundled from Android system image, we use the |
| * CompatibilityCloseMonitor |
| */ |
| #include "AsynchronousCloseMonitor.h" |
| #endif |
| |
| #ifndef CONSCRYPT_UNBUNDLED |
| #include "cutils/log.h" |
| #else |
| #include "log_compat.h" |
| #endif |
| |
| #ifndef CONSCRYPT_UNBUNDLED |
| #include "JNIHelp.h" |
| #include "JniConstants.h" |
| #include "JniException.h" |
| #else |
| #define NATIVE_METHOD(className, functionName, signature) \ |
| { (char*) #functionName, (char*) signature, reinterpret_cast<void*>(className ## _ ## functionName) } |
| #define REGISTER_NATIVE_METHODS(jni_class_name) \ |
| RegisterNativeMethods(env, jni_class_name, gMethods, arraysize(gMethods)) |
| #endif |
| |
| #include "ScopedLocalRef.h" |
| #include "ScopedPrimitiveArray.h" |
| #include "ScopedUtfChars.h" |
| #include "UniquePtr.h" |
| #include "NetFd.h" |
| |
| #include "macros.h" |
| |
| #undef WITH_JNI_TRACE |
| #undef WITH_JNI_TRACE_MD |
| #undef WITH_JNI_TRACE_DATA |
| |
| /* |
| * How to use this for debugging with Wireshark: |
| * |
| * 1. Pull lines from logcat to a file that looks like (without quotes): |
| * "RSA Session-ID:... Master-Key:..." <CR> |
| * "RSA Session-ID:... Master-Key:..." <CR> |
| * <etc> |
| * 2. Start Wireshark |
| * 3. Go to Edit -> Preferences -> SSL -> (Pre-)Master-Key log and fill in |
| * the file you put the lines in above. |
| * 4. Follow the stream that corresponds to the desired "Session-ID" in |
| * the Server Hello. |
| */ |
| #undef WITH_JNI_TRACE_KEYS |
| |
| #ifdef WITH_JNI_TRACE |
| #define JNI_TRACE(...) \ |
| ((void)ALOG(LOG_INFO, LOG_TAG "-jni", __VA_ARGS__)) |
| #else |
| #define JNI_TRACE(...) ((void)0) |
| #endif |
| #ifdef WITH_JNI_TRACE_MD |
| #define JNI_TRACE_MD(...) \ |
| ((void)ALOG(LOG_INFO, LOG_TAG "-jni", __VA_ARGS__)); |
| #else |
| #define JNI_TRACE_MD(...) ((void)0) |
| #endif |
| // don't overwhelm logcat |
| #define WITH_JNI_TRACE_DATA_CHUNK_SIZE 512 |
| |
| static JavaVM* gJavaVM; |
| static jclass cryptoUpcallsClass; |
| static jclass openSslInputStreamClass; |
| static jclass nativeRefClass; |
| |
| static jclass byteArrayClass; |
| static jclass calendarClass; |
| static jclass objectClass; |
| static jclass objectArrayClass; |
| static jclass integerClass; |
| static jclass inputStreamClass; |
| static jclass outputStreamClass; |
| static jclass stringClass; |
| |
| static jfieldID nativeRef_context; |
| |
| static jmethodID calendar_setMethod; |
| static jmethodID inputStream_readMethod; |
| static jmethodID integer_valueOfMethod; |
| static jmethodID openSslInputStream_readLineMethod; |
| static jmethodID outputStream_writeMethod; |
| static jmethodID outputStream_flushMethod; |
| |
| struct OPENSSL_Delete { |
| void operator()(void* p) const { |
| OPENSSL_free(p); |
| } |
| }; |
| typedef UniquePtr<unsigned char, OPENSSL_Delete> Unique_OPENSSL_str; |
| |
| struct BIO_Delete { |
| void operator()(BIO* p) const { |
| BIO_free_all(p); |
| } |
| }; |
| typedef UniquePtr<BIO, BIO_Delete> Unique_BIO; |
| |
| struct BIGNUM_Delete { |
| void operator()(BIGNUM* p) const { |
| BN_free(p); |
| } |
| }; |
| typedef UniquePtr<BIGNUM, BIGNUM_Delete> Unique_BIGNUM; |
| |
| struct ASN1_INTEGER_Delete { |
| void operator()(ASN1_INTEGER* p) const { |
| ASN1_INTEGER_free(p); |
| } |
| }; |
| typedef UniquePtr<ASN1_INTEGER, ASN1_INTEGER_Delete> Unique_ASN1_INTEGER; |
| |
| struct DH_Delete { |
| void operator()(DH* p) const { |
| DH_free(p); |
| } |
| }; |
| typedef UniquePtr<DH, DH_Delete> Unique_DH; |
| |
| struct DSA_Delete { |
| void operator()(DSA* p) const { |
| DSA_free(p); |
| } |
| }; |
| typedef UniquePtr<DSA, DSA_Delete> Unique_DSA; |
| |
| struct EC_GROUP_Delete { |
| void operator()(EC_GROUP* p) const { |
| EC_GROUP_free(p); |
| } |
| }; |
| typedef UniquePtr<EC_GROUP, EC_GROUP_Delete> Unique_EC_GROUP; |
| |
| struct EC_POINT_Delete { |
| void operator()(EC_POINT* p) const { |
| EC_POINT_clear_free(p); |
| } |
| }; |
| typedef UniquePtr<EC_POINT, EC_POINT_Delete> Unique_EC_POINT; |
| |
| struct EC_KEY_Delete { |
| void operator()(EC_KEY* p) const { |
| EC_KEY_free(p); |
| } |
| }; |
| typedef UniquePtr<EC_KEY, EC_KEY_Delete> Unique_EC_KEY; |
| |
| struct EVP_MD_CTX_Delete { |
| void operator()(EVP_MD_CTX* p) const { |
| EVP_MD_CTX_destroy(p); |
| } |
| }; |
| typedef UniquePtr<EVP_MD_CTX, EVP_MD_CTX_Delete> Unique_EVP_MD_CTX; |
| |
| struct EVP_CIPHER_CTX_Delete { |
| void operator()(EVP_CIPHER_CTX* p) const { |
| EVP_CIPHER_CTX_free(p); |
| } |
| }; |
| typedef UniquePtr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_Delete> Unique_EVP_CIPHER_CTX; |
| |
| struct EVP_PKEY_Delete { |
| void operator()(EVP_PKEY* p) const { |
| EVP_PKEY_free(p); |
| } |
| }; |
| typedef UniquePtr<EVP_PKEY, EVP_PKEY_Delete> Unique_EVP_PKEY; |
| |
| struct PKCS8_PRIV_KEY_INFO_Delete { |
| void operator()(PKCS8_PRIV_KEY_INFO* p) const { |
| PKCS8_PRIV_KEY_INFO_free(p); |
| } |
| }; |
| typedef UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> Unique_PKCS8_PRIV_KEY_INFO; |
| |
| struct RSA_Delete { |
| void operator()(RSA* p) const { |
| RSA_free(p); |
| } |
| }; |
| typedef UniquePtr<RSA, RSA_Delete> Unique_RSA; |
| |
| struct ASN1_BIT_STRING_Delete { |
| void operator()(ASN1_BIT_STRING* p) const { |
| ASN1_BIT_STRING_free(p); |
| } |
| }; |
| typedef UniquePtr<ASN1_BIT_STRING, ASN1_BIT_STRING_Delete> Unique_ASN1_BIT_STRING; |
| |
| struct ASN1_OBJECT_Delete { |
| void operator()(ASN1_OBJECT* p) const { |
| ASN1_OBJECT_free(p); |
| } |
| }; |
| typedef UniquePtr<ASN1_OBJECT, ASN1_OBJECT_Delete> Unique_ASN1_OBJECT; |
| |
| struct ASN1_GENERALIZEDTIME_Delete { |
| void operator()(ASN1_GENERALIZEDTIME* p) const { |
| ASN1_GENERALIZEDTIME_free(p); |
| } |
| }; |
| typedef UniquePtr<ASN1_GENERALIZEDTIME, ASN1_GENERALIZEDTIME_Delete> Unique_ASN1_GENERALIZEDTIME; |
| |
| struct SSL_Delete { |
| void operator()(SSL* p) const { |
| SSL_free(p); |
| } |
| }; |
| typedef UniquePtr<SSL, SSL_Delete> Unique_SSL; |
| |
| struct SSL_CTX_Delete { |
| void operator()(SSL_CTX* p) const { |
| SSL_CTX_free(p); |
| } |
| }; |
| typedef UniquePtr<SSL_CTX, SSL_CTX_Delete> Unique_SSL_CTX; |
| |
| struct X509_Delete { |
| void operator()(X509* p) const { |
| X509_free(p); |
| } |
| }; |
| typedef UniquePtr<X509, X509_Delete> Unique_X509; |
| |
| struct X509_NAME_Delete { |
| void operator()(X509_NAME* p) const { |
| X509_NAME_free(p); |
| } |
| }; |
| typedef UniquePtr<X509_NAME, X509_NAME_Delete> Unique_X509_NAME; |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| struct PKCS7_Delete { |
| void operator()(PKCS7* p) const { |
| PKCS7_free(p); |
| } |
| }; |
| typedef UniquePtr<PKCS7, PKCS7_Delete> Unique_PKCS7; |
| #endif |
| |
| struct sk_SSL_CIPHER_Delete { |
| void operator()(STACK_OF(SSL_CIPHER)* p) const { |
| // We don't own SSL_CIPHER references, so no need for pop_free |
| sk_SSL_CIPHER_free(p); |
| } |
| }; |
| typedef UniquePtr<STACK_OF(SSL_CIPHER), sk_SSL_CIPHER_Delete> Unique_sk_SSL_CIPHER; |
| |
| struct sk_X509_Delete { |
| void operator()(STACK_OF(X509)* p) const { |
| sk_X509_pop_free(p, X509_free); |
| } |
| }; |
| typedef UniquePtr<STACK_OF(X509), sk_X509_Delete> Unique_sk_X509; |
| |
| #if defined(OPENSSL_IS_BORINGSSL) |
| struct sk_X509_CRL_Delete { |
| void operator()(STACK_OF(X509_CRL)* p) const { |
| sk_X509_CRL_pop_free(p, X509_CRL_free); |
| } |
| }; |
| typedef UniquePtr<STACK_OF(X509_CRL), sk_X509_CRL_Delete> Unique_sk_X509_CRL; |
| #endif |
| |
| struct sk_X509_NAME_Delete { |
| void operator()(STACK_OF(X509_NAME)* p) const { |
| sk_X509_NAME_pop_free(p, X509_NAME_free); |
| } |
| }; |
| typedef UniquePtr<STACK_OF(X509_NAME), sk_X509_NAME_Delete> Unique_sk_X509_NAME; |
| |
| struct sk_ASN1_OBJECT_Delete { |
| void operator()(STACK_OF(ASN1_OBJECT)* p) const { |
| sk_ASN1_OBJECT_pop_free(p, ASN1_OBJECT_free); |
| } |
| }; |
| typedef UniquePtr<STACK_OF(ASN1_OBJECT), sk_ASN1_OBJECT_Delete> Unique_sk_ASN1_OBJECT; |
| |
| struct sk_GENERAL_NAME_Delete { |
| void operator()(STACK_OF(GENERAL_NAME)* p) const { |
| sk_GENERAL_NAME_pop_free(p, GENERAL_NAME_free); |
| } |
| }; |
| typedef UniquePtr<STACK_OF(GENERAL_NAME), sk_GENERAL_NAME_Delete> Unique_sk_GENERAL_NAME; |
| |
| /** |
| * Many OpenSSL APIs take ownership of an argument on success but don't free the argument |
| * on failure. This means we need to tell our scoped pointers when we've transferred ownership, |
| * without triggering a warning by not using the result of release(). |
| */ |
| #define OWNERSHIP_TRANSFERRED(obj) \ |
| do { typeof (obj.release()) _dummy __attribute__((unused)) = obj.release(); } while(0) |
| |
| /** |
| * UNUSED_ARGUMENT can be used to mark an, otherwise unused, argument as "used" |
| * for the purposes of -Werror=unused-parameter. This can be needed when an |
| * argument's use is based on an #ifdef. |
| */ |
| #define UNUSED_ARGUMENT(x) ((void)(x)); |
| |
| /** |
| * Frees the SSL error state. |
| * |
| * OpenSSL keeps an "error stack" per thread, and given that this code |
| * can be called from arbitrary threads that we don't keep track of, |
| * we err on the side of freeing the error state promptly (instead of, |
| * say, at thread death). |
| */ |
| static void freeOpenSslErrorState(void) { |
| ERR_clear_error(); |
| ERR_remove_thread_state(NULL); |
| } |
| |
| /** |
| * Manages the freeing of the OpenSSL error stack. This allows you to |
| * instantiate this object during an SSL call that may fail and not worry |
| * about manually calling freeOpenSslErrorState() later. |
| * |
| * As an optimization, you can also call .release() for passing as an |
| * argument to things that free the error stack state as a side-effect. |
| */ |
| class OpenSslError { |
| public: |
| OpenSslError() : sslError_(SSL_ERROR_NONE), released_(false) { |
| } |
| |
| OpenSslError(SSL* ssl, int returnCode) : sslError_(SSL_ERROR_NONE), released_(false) { |
| reset(ssl, returnCode); |
| } |
| |
| ~OpenSslError() { |
| if (!released_ && sslError_ != SSL_ERROR_NONE) { |
| freeOpenSslErrorState(); |
| } |
| } |
| |
| int get() const { |
| return sslError_; |
| } |
| |
| void reset(SSL* ssl, int returnCode) { |
| if (returnCode <= 0) { |
| sslError_ = SSL_get_error(ssl, returnCode); |
| } else { |
| sslError_ = SSL_ERROR_NONE; |
| } |
| } |
| |
| int release() { |
| released_ = true; |
| return sslError_; |
| } |
| |
| private: |
| int sslError_; |
| bool released_; |
| }; |
| |
| /** |
| * Throws a OutOfMemoryError with the given string as a message. |
| */ |
| static int jniThrowOutOfMemory(JNIEnv* env, const char* message) { |
| return jniThrowException(env, "java/lang/OutOfMemoryError", message); |
| } |
| |
| /** |
| * Throws a BadPaddingException with the given string as a message. |
| */ |
| static int throwBadPaddingException(JNIEnv* env, const char* message) { |
| JNI_TRACE("throwBadPaddingException %s", message); |
| return jniThrowException(env, "javax/crypto/BadPaddingException", message); |
| } |
| |
| /** |
| * Throws a SignatureException with the given string as a message. |
| */ |
| static int throwSignatureException(JNIEnv* env, const char* message) { |
| JNI_TRACE("throwSignatureException %s", message); |
| return jniThrowException(env, "java/security/SignatureException", message); |
| } |
| |
| /** |
| * Throws a InvalidKeyException with the given string as a message. |
| */ |
| static int throwInvalidKeyException(JNIEnv* env, const char* message) { |
| JNI_TRACE("throwInvalidKeyException %s", message); |
| return jniThrowException(env, "java/security/InvalidKeyException", message); |
| } |
| |
| /** |
| * Throws a SignatureException with the given string as a message. |
| */ |
| static int throwIllegalBlockSizeException(JNIEnv* env, const char* message) { |
| JNI_TRACE("throwIllegalBlockSizeException %s", message); |
| return jniThrowException(env, "javax/crypto/IllegalBlockSizeException", message); |
| } |
| |
| /** |
| * Throws a NoSuchAlgorithmException with the given string as a message. |
| */ |
| static int throwNoSuchAlgorithmException(JNIEnv* env, const char* message) { |
| JNI_TRACE("throwUnknownAlgorithmException %s", message); |
| return jniThrowException(env, "java/security/NoSuchAlgorithmException", message); |
| } |
| |
| static int throwForAsn1Error(JNIEnv* env, int reason, const char *message, |
| int (*defaultThrow)(JNIEnv*, const char*)) { |
| switch (reason) { |
| case ASN1_R_UNABLE_TO_DECODE_RSA_KEY: |
| case ASN1_R_UNABLE_TO_DECODE_RSA_PRIVATE_KEY: |
| case ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE: |
| case ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE: |
| // These #if sections can be removed once BoringSSL is landed. |
| #if defined(ASN1_R_WRONG_PUBLIC_KEY_TYPE) |
| case ASN1_R_WRONG_PUBLIC_KEY_TYPE: |
| #endif |
| return throwInvalidKeyException(env, message); |
| break; |
| #if defined(ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM) |
| case ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM: |
| return throwNoSuchAlgorithmException(env, message); |
| break; |
| #endif |
| } |
| return defaultThrow(env, message); |
| } |
| |
| #if defined(OPENSSL_IS_BORINGSSL) |
| static int throwForCipherError(JNIEnv* env, int reason, const char *message, |
| int (*defaultThrow)(JNIEnv*, const char*)) { |
| switch (reason) { |
| case CIPHER_R_BAD_DECRYPT: |
| return throwBadPaddingException(env, message); |
| break; |
| case CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: |
| case CIPHER_R_WRONG_FINAL_BLOCK_LENGTH: |
| return throwIllegalBlockSizeException(env, message); |
| break; |
| case CIPHER_R_AES_KEY_SETUP_FAILED: |
| case CIPHER_R_BAD_KEY_LENGTH: |
| case CIPHER_R_UNSUPPORTED_KEY_SIZE: |
| return throwInvalidKeyException(env, message); |
| break; |
| } |
| return defaultThrow(env, message); |
| } |
| |
| static int throwForEvpError(JNIEnv* env, int reason, const char *message, |
| int (*defaultThrow)(JNIEnv*, const char*)) { |
| switch (reason) { |
| case EVP_R_MISSING_PARAMETERS: |
| return throwInvalidKeyException(env, message); |
| break; |
| case EVP_R_UNSUPPORTED_ALGORITHM: |
| case EVP_R_X931_UNSUPPORTED: |
| return throwNoSuchAlgorithmException(env, message); |
| break; |
| // These #if sections can be make unconditional once BoringSSL is landed. |
| #if defined(EVP_R_WRONG_PUBLIC_KEY_TYPE) |
| case EVP_R_WRONG_PUBLIC_KEY_TYPE: |
| return throwInvalidKeyException(env, message); |
| break; |
| #endif |
| #if defined(EVP_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM) |
| case EVP_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM: |
| return throwNoSuchAlgorithmException(env, message); |
| break; |
| #endif |
| default: |
| return defaultThrow(env, message); |
| break; |
| } |
| } |
| #else |
| static int throwForEvpError(JNIEnv* env, int reason, const char *message, |
| int (*defaultThrow)(JNIEnv*, const char*)) { |
| switch (reason) { |
| case EVP_R_BAD_DECRYPT: |
| return throwBadPaddingException(env, message); |
| break; |
| case EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: |
| case EVP_R_WRONG_FINAL_BLOCK_LENGTH: |
| return throwIllegalBlockSizeException(env, message); |
| break; |
| case EVP_R_BAD_KEY_LENGTH: |
| case EVP_R_BN_DECODE_ERROR: |
| case EVP_R_BN_PUBKEY_ERROR: |
| case EVP_R_INVALID_KEY_LENGTH: |
| case EVP_R_MISSING_PARAMETERS: |
| case EVP_R_UNSUPPORTED_KEY_SIZE: |
| case EVP_R_UNSUPPORTED_KEYLENGTH: |
| return throwInvalidKeyException(env, message); |
| break; |
| case EVP_R_WRONG_PUBLIC_KEY_TYPE: |
| return throwSignatureException(env, message); |
| break; |
| case EVP_R_UNSUPPORTED_ALGORITHM: |
| return throwNoSuchAlgorithmException(env, message); |
| break; |
| default: |
| return defaultThrow(env, message); |
| break; |
| } |
| } |
| #endif |
| |
| static int throwForRsaError(JNIEnv* env, int reason, const char *message, |
| int (*defaultThrow)(JNIEnv*, const char*)) { |
| switch (reason) { |
| case RSA_R_BLOCK_TYPE_IS_NOT_01: |
| case RSA_R_BLOCK_TYPE_IS_NOT_02: |
| case RSA_R_PKCS_DECODING_ERROR: |
| return throwBadPaddingException(env, message); |
| break; |
| case RSA_R_BAD_SIGNATURE: |
| case RSA_R_DATA_TOO_LARGE_FOR_MODULUS: |
| case RSA_R_INVALID_MESSAGE_LENGTH: |
| case RSA_R_WRONG_SIGNATURE_LENGTH: |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| case RSA_R_ALGORITHM_MISMATCH: |
| case RSA_R_DATA_GREATER_THAN_MOD_LEN: |
| #endif |
| return throwSignatureException(env, message); |
| break; |
| case RSA_R_UNKNOWN_ALGORITHM_TYPE: |
| return throwNoSuchAlgorithmException(env, message); |
| break; |
| case RSA_R_MODULUS_TOO_LARGE: |
| case RSA_R_NO_PUBLIC_EXPONENT: |
| return throwInvalidKeyException(env, message); |
| break; |
| } |
| return defaultThrow(env, message); |
| } |
| |
| static int throwForX509Error(JNIEnv* env, int reason, const char *message, |
| int (*defaultThrow)(JNIEnv*, const char*)) { |
| switch (reason) { |
| case X509_R_UNSUPPORTED_ALGORITHM: |
| return throwNoSuchAlgorithmException(env, message); |
| break; |
| default: |
| return defaultThrow(env, message); |
| break; |
| } |
| } |
| |
| /* |
| * Checks this thread's OpenSSL error queue and throws a RuntimeException if |
| * necessary. |
| * |
| * @return true if an exception was thrown, false if not. |
| */ |
| static bool throwExceptionIfNecessary(JNIEnv* env, const char* location __attribute__ ((unused)), |
| int (*defaultThrow)(JNIEnv*, const char*) = jniThrowRuntimeException) { |
| const char* file; |
| int line; |
| const char* data; |
| int flags; |
| unsigned long error = ERR_get_error_line_data(&file, &line, &data, &flags); |
| int result = false; |
| |
| if (error != 0) { |
| char message[256]; |
| ERR_error_string_n(error, message, sizeof(message)); |
| int library = ERR_GET_LIB(error); |
| int reason = ERR_GET_REASON(error); |
| JNI_TRACE("OpenSSL error in %s error=%lx library=%x reason=%x (%s:%d): %s %s", |
| location, error, library, reason, file, line, message, |
| (flags & ERR_TXT_STRING) ? data : "(no data)"); |
| switch (library) { |
| case ERR_LIB_RSA: |
| throwForRsaError(env, reason, message, defaultThrow); |
| break; |
| case ERR_LIB_ASN1: |
| throwForAsn1Error(env, reason, message, defaultThrow); |
| break; |
| #if defined(OPENSSL_IS_BORINGSSL) |
| case ERR_LIB_CIPHER: |
| throwForCipherError(env, reason, message, defaultThrow); |
| break; |
| #endif |
| case ERR_LIB_EVP: |
| throwForEvpError(env, reason, message, defaultThrow); |
| break; |
| case ERR_LIB_X509: |
| throwForX509Error(env, reason, message, defaultThrow); |
| break; |
| case ERR_LIB_DSA: |
| throwInvalidKeyException(env, message); |
| break; |
| default: |
| defaultThrow(env, message); |
| break; |
| } |
| result = true; |
| } |
| |
| freeOpenSslErrorState(); |
| return result; |
| } |
| |
| /** |
| * Throws an SocketTimeoutException with the given string as a message. |
| */ |
| static int throwSocketTimeoutException(JNIEnv* env, const char* message) { |
| JNI_TRACE("throwSocketTimeoutException %s", message); |
| return jniThrowException(env, "java/net/SocketTimeoutException", message); |
| } |
| |
| /** |
| * Throws a javax.net.ssl.SSLException with the given string as a message. |
| */ |
| static int throwSSLHandshakeExceptionStr(JNIEnv* env, const char* message) { |
| JNI_TRACE("throwSSLExceptionStr %s", message); |
| return jniThrowException(env, "javax/net/ssl/SSLHandshakeException", message); |
| } |
| |
| /** |
| * Throws a javax.net.ssl.SSLException with the given string as a message. |
| */ |
| static int throwSSLExceptionStr(JNIEnv* env, const char* message) { |
| JNI_TRACE("throwSSLExceptionStr %s", message); |
| return jniThrowException(env, "javax/net/ssl/SSLException", message); |
| } |
| |
| /** |
| * Throws a javax.net.ssl.SSLProcotolException with the given string as a message. |
| */ |
| static int throwSSLProtocolExceptionStr(JNIEnv* env, const char* message) { |
| JNI_TRACE("throwSSLProtocolExceptionStr %s", message); |
| return jniThrowException(env, "javax/net/ssl/SSLProtocolException", message); |
| } |
| |
| /** |
| * Throws an SSLException with a message constructed from the current |
| * SSL errors. This will also log the errors. |
| * |
| * @param env the JNI environment |
| * @param ssl the possibly NULL SSL |
| * @param sslErrorCode error code returned from SSL_get_error() or |
| * SSL_ERROR_NONE to probe with ERR_get_error |
| * @param message null-ok; general error message |
| */ |
| static int throwSSLExceptionWithSslErrors(JNIEnv* env, SSL* ssl, int sslErrorCode, |
| const char* message, int (*actualThrow)(JNIEnv*, const char*) = throwSSLExceptionStr) { |
| |
| if (message == NULL) { |
| message = "SSL error"; |
| } |
| |
| // First consult the SSL error code for the general message. |
| const char* sslErrorStr = NULL; |
| switch (sslErrorCode) { |
| case SSL_ERROR_NONE: |
| if (ERR_peek_error() == 0) { |
| sslErrorStr = "OK"; |
| } else { |
| sslErrorStr = ""; |
| } |
| break; |
| case SSL_ERROR_SSL: |
| sslErrorStr = "Failure in SSL library, usually a protocol error"; |
| break; |
| case SSL_ERROR_WANT_READ: |
| sslErrorStr = "SSL_ERROR_WANT_READ occurred. You should never see this."; |
| break; |
| case SSL_ERROR_WANT_WRITE: |
| sslErrorStr = "SSL_ERROR_WANT_WRITE occurred. You should never see this."; |
| break; |
| case SSL_ERROR_WANT_X509_LOOKUP: |
| sslErrorStr = "SSL_ERROR_WANT_X509_LOOKUP occurred. You should never see this."; |
| break; |
| case SSL_ERROR_SYSCALL: |
| sslErrorStr = "I/O error during system call"; |
| break; |
| case SSL_ERROR_ZERO_RETURN: |
| sslErrorStr = "SSL_ERROR_ZERO_RETURN occurred. You should never see this."; |
| break; |
| case SSL_ERROR_WANT_CONNECT: |
| sslErrorStr = "SSL_ERROR_WANT_CONNECT occurred. You should never see this."; |
| break; |
| case SSL_ERROR_WANT_ACCEPT: |
| sslErrorStr = "SSL_ERROR_WANT_ACCEPT occurred. You should never see this."; |
| break; |
| default: |
| sslErrorStr = "Unknown SSL error"; |
| } |
| |
| // Prepend either our explicit message or a default one. |
| char* str; |
| if (asprintf(&str, "%s: ssl=%p: %s", message, ssl, sslErrorStr) <= 0) { |
| // problem with asprintf, just throw argument message, log everything |
| int ret = actualThrow(env, message); |
| ALOGV("%s: ssl=%p: %s", message, ssl, sslErrorStr); |
| freeOpenSslErrorState(); |
| return ret; |
| } |
| |
| char* allocStr = str; |
| |
| // For protocol errors, SSL might have more information. |
| if (sslErrorCode == SSL_ERROR_NONE || sslErrorCode == SSL_ERROR_SSL) { |
| // Append each error as an additional line to the message. |
| for (;;) { |
| char errStr[256]; |
| const char* file; |
| int line; |
| const char* data; |
| int flags; |
| unsigned long err = ERR_get_error_line_data(&file, &line, &data, &flags); |
| if (err == 0) { |
| break; |
| } |
| |
| ERR_error_string_n(err, errStr, sizeof(errStr)); |
| |
| int ret = asprintf(&str, "%s\n%s (%s:%d %p:0x%08x)", |
| (allocStr == NULL) ? "" : allocStr, |
| errStr, |
| file, |
| line, |
| (flags & ERR_TXT_STRING) ? data : "(no data)", |
| flags); |
| |
| if (ret < 0) { |
| break; |
| } |
| |
| free(allocStr); |
| allocStr = str; |
| } |
| // For errors during system calls, errno might be our friend. |
| } else if (sslErrorCode == SSL_ERROR_SYSCALL) { |
| if (asprintf(&str, "%s, %s", allocStr, strerror(errno)) >= 0) { |
| free(allocStr); |
| allocStr = str; |
| } |
| // If the error code is invalid, print it. |
| } else if (sslErrorCode > SSL_ERROR_WANT_ACCEPT) { |
| if (asprintf(&str, ", error code is %d", sslErrorCode) >= 0) { |
| free(allocStr); |
| allocStr = str; |
| } |
| } |
| |
| int ret; |
| if (sslErrorCode == SSL_ERROR_SSL) { |
| ret = throwSSLProtocolExceptionStr(env, allocStr); |
| } else { |
| ret = actualThrow(env, allocStr); |
| } |
| |
| ALOGV("%s", allocStr); |
| free(allocStr); |
| freeOpenSslErrorState(); |
| return ret; |
| } |
| |
| /** |
| * Helper function that grabs the casts an ssl pointer and then checks for nullness. |
| * If this function returns NULL and <code>throwIfNull</code> is |
| * passed as <code>true</code>, then this function will call |
| * <code>throwSSLExceptionStr</code> before returning, so in this case of |
| * NULL, a caller of this function should simply return and allow JNI |
| * to do its thing. |
| * |
| * @param env the JNI environment |
| * @param ssl_address; the ssl_address pointer as an integer |
| * @param throwIfNull whether to throw if the SSL pointer is NULL |
| * @returns the pointer, which may be NULL |
| */ |
| static SSL_CTX* to_SSL_CTX(JNIEnv* env, jlong ssl_ctx_address, bool throwIfNull) { |
| SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address)); |
| if ((ssl_ctx == NULL) && throwIfNull) { |
| JNI_TRACE("ssl_ctx == null"); |
| jniThrowNullPointerException(env, "ssl_ctx == null"); |
| } |
| return ssl_ctx; |
| } |
| |
| static SSL* to_SSL(JNIEnv* env, jlong ssl_address, bool throwIfNull) { |
| SSL* ssl = reinterpret_cast<SSL*>(static_cast<uintptr_t>(ssl_address)); |
| if ((ssl == NULL) && throwIfNull) { |
| JNI_TRACE("ssl == null"); |
| jniThrowNullPointerException(env, "ssl == null"); |
| } |
| return ssl; |
| } |
| |
| static SSL_SESSION* to_SSL_SESSION(JNIEnv* env, jlong ssl_session_address, bool throwIfNull) { |
| SSL_SESSION* ssl_session |
| = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address)); |
| if ((ssl_session == NULL) && throwIfNull) { |
| JNI_TRACE("ssl_session == null"); |
| jniThrowNullPointerException(env, "ssl_session == null"); |
| } |
| return ssl_session; |
| } |
| |
| static SSL_CIPHER* to_SSL_CIPHER(JNIEnv* env, jlong ssl_cipher_address, bool throwIfNull) { |
| SSL_CIPHER* ssl_cipher |
| = reinterpret_cast<SSL_CIPHER*>(static_cast<uintptr_t>(ssl_cipher_address)); |
| if ((ssl_cipher == NULL) && throwIfNull) { |
| JNI_TRACE("ssl_cipher == null"); |
| jniThrowNullPointerException(env, "ssl_cipher == null"); |
| } |
| return ssl_cipher; |
| } |
| |
| template<typename T> |
| static T* fromContextObject(JNIEnv* env, jobject contextObject) { |
| if (contextObject == NULL) { |
| JNI_TRACE("contextObject == null"); |
| jniThrowNullPointerException(env, "contextObject == null"); |
| return NULL; |
| } |
| T* ref = reinterpret_cast<T*>(env->GetLongField(contextObject, nativeRef_context)); |
| if (ref == NULL) { |
| JNI_TRACE("ref == null"); |
| jniThrowNullPointerException(env, "ref == null"); |
| return NULL; |
| } |
| return ref; |
| } |
| |
| /** |
| * Converts a Java byte[] two's complement to an OpenSSL BIGNUM. This will |
| * allocate the BIGNUM if *dest == NULL. Returns true on success. If the |
| * return value is false, there is a pending exception. |
| */ |
| static bool arrayToBignum(JNIEnv* env, jbyteArray source, BIGNUM** dest) { |
| JNI_TRACE("arrayToBignum(%p, %p)", source, dest); |
| if (dest == NULL) { |
| JNI_TRACE("arrayToBignum(%p, %p) => dest is null!", source, dest); |
| jniThrowNullPointerException(env, "dest == null"); |
| return false; |
| } |
| JNI_TRACE("arrayToBignum(%p, %p) *dest == %p", source, dest, *dest); |
| |
| ScopedByteArrayRO sourceBytes(env, source); |
| if (sourceBytes.get() == NULL) { |
| JNI_TRACE("arrayToBignum(%p, %p) => NULL", source, dest); |
| return false; |
| } |
| const unsigned char* tmp = reinterpret_cast<const unsigned char*>(sourceBytes.get()); |
| size_t tmpSize = sourceBytes.size(); |
| |
| /* if the array is empty, it is zero. */ |
| if (tmpSize == 0) { |
| if (*dest == NULL) { |
| *dest = BN_new(); |
| } |
| BN_zero(*dest); |
| return true; |
| } |
| |
| UniquePtr<unsigned char[]> twosComplement; |
| bool negative = (tmp[0] & 0x80) != 0; |
| if (negative) { |
| // Need to convert to two's complement. |
| twosComplement.reset(new unsigned char[tmpSize]); |
| unsigned char* twosBytes = reinterpret_cast<unsigned char*>(twosComplement.get()); |
| memcpy(twosBytes, tmp, tmpSize); |
| tmp = twosBytes; |
| |
| bool carry = true; |
| for (ssize_t i = tmpSize - 1; i >= 0; i--) { |
| twosBytes[i] ^= 0xFF; |
| if (carry) { |
| carry = (++twosBytes[i]) == 0; |
| } |
| } |
| } |
| BIGNUM *ret = BN_bin2bn(tmp, tmpSize, *dest); |
| if (ret == NULL) { |
| jniThrowRuntimeException(env, "Conversion to BIGNUM failed"); |
| JNI_TRACE("arrayToBignum(%p, %p) => threw exception", source, dest); |
| return false; |
| } |
| BN_set_negative(ret, negative ? 1 : 0); |
| |
| *dest = ret; |
| JNI_TRACE("arrayToBignum(%p, %p) => *dest = %p", source, dest, ret); |
| return true; |
| } |
| |
| #if defined(OPENSSL_IS_BORINGSSL) |
| /** |
| * arrayToBignumSize sets |*out_size| to the size of the big-endian number |
| * contained in |source|. It returns true on success and sets an exception and |
| * returns false otherwise. |
| */ |
| static bool arrayToBignumSize(JNIEnv* env, jbyteArray source, size_t* out_size) { |
| JNI_TRACE("arrayToBignumSize(%p, %p)", source, out_size); |
| |
| ScopedByteArrayRO sourceBytes(env, source); |
| if (sourceBytes.get() == NULL) { |
| JNI_TRACE("arrayToBignum(%p, %p) => NULL", source, out_size); |
| return false; |
| } |
| const uint8_t* tmp = reinterpret_cast<const uint8_t*>(sourceBytes.get()); |
| size_t tmpSize = sourceBytes.size(); |
| |
| if (tmpSize == 0) { |
| *out_size = 0; |
| return true; |
| } |
| |
| if ((tmp[0] & 0x80) != 0) { |
| // Negative numbers are invalid. |
| jniThrowRuntimeException(env, "Negative number"); |
| return false; |
| } |
| |
| while (tmpSize > 0 && tmp[0] == 0) { |
| tmp++; |
| tmpSize--; |
| } |
| |
| *out_size = tmpSize; |
| return true; |
| } |
| #endif |
| |
| /** |
| * Converts an OpenSSL BIGNUM to a Java byte[] array in two's complement. |
| */ |
| static jbyteArray bignumToArray(JNIEnv* env, const BIGNUM* source, const char* sourceName) { |
| JNI_TRACE("bignumToArray(%p, %s)", source, sourceName); |
| |
| if (source == NULL) { |
| jniThrowNullPointerException(env, sourceName); |
| return NULL; |
| } |
| |
| size_t numBytes = BN_num_bytes(source) + 1; |
| jbyteArray javaBytes = env->NewByteArray(numBytes); |
| ScopedByteArrayRW bytes(env, javaBytes); |
| if (bytes.get() == NULL) { |
| JNI_TRACE("bignumToArray(%p, %s) => NULL", source, sourceName); |
| return NULL; |
| } |
| |
| unsigned char* tmp = reinterpret_cast<unsigned char*>(bytes.get()); |
| if (BN_num_bytes(source) > 0 && BN_bn2bin(source, tmp + 1) <= 0) { |
| throwExceptionIfNecessary(env, "bignumToArray"); |
| return NULL; |
| } |
| |
| // Set the sign and convert to two's complement if necessary for the Java code. |
| if (BN_is_negative(source)) { |
| bool carry = true; |
| for (ssize_t i = numBytes - 1; i >= 0; i--) { |
| tmp[i] ^= 0xFF; |
| if (carry) { |
| carry = (++tmp[i]) == 0; |
| } |
| } |
| *tmp |= 0x80; |
| } else { |
| *tmp = 0x00; |
| } |
| |
| JNI_TRACE("bignumToArray(%p, %s) => %p", source, sourceName, javaBytes); |
| return javaBytes; |
| } |
| |
| /** |
| * Converts various OpenSSL ASN.1 types to a jbyteArray with DER-encoded data |
| * inside. The "i2d_func" function pointer is a function of the "i2d_<TYPE>" |
| * from the OpenSSL ASN.1 API. |
| */ |
| template<typename T> |
| jbyteArray ASN1ToByteArray(JNIEnv* env, T* obj, int (*i2d_func)(T*, unsigned char**)) { |
| if (obj == NULL) { |
| jniThrowNullPointerException(env, "ASN1 input == null"); |
| JNI_TRACE("ASN1ToByteArray(%p) => null input", obj); |
| return NULL; |
| } |
| |
| int derLen = i2d_func(obj, NULL); |
| if (derLen < 0) { |
| throwExceptionIfNecessary(env, "ASN1ToByteArray"); |
| JNI_TRACE("ASN1ToByteArray(%p) => measurement failed", obj); |
| return NULL; |
| } |
| |
| ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(derLen)); |
| if (byteArray.get() == NULL) { |
| JNI_TRACE("ASN1ToByteArray(%p) => creating byte array failed", obj); |
| return NULL; |
| } |
| |
| ScopedByteArrayRW bytes(env, byteArray.get()); |
| if (bytes.get() == NULL) { |
| JNI_TRACE("ASN1ToByteArray(%p) => using byte array failed", obj); |
| return NULL; |
| } |
| |
| unsigned char* p = reinterpret_cast<unsigned char*>(bytes.get()); |
| int ret = i2d_func(obj, &p); |
| if (ret < 0) { |
| throwExceptionIfNecessary(env, "ASN1ToByteArray"); |
| JNI_TRACE("ASN1ToByteArray(%p) => final conversion failed", obj); |
| return NULL; |
| } |
| |
| JNI_TRACE("ASN1ToByteArray(%p) => success (%d bytes written)", obj, ret); |
| return byteArray.release(); |
| } |
| |
| template<typename T, T* (*d2i_func)(T**, const unsigned char**, long)> |
| T* ByteArrayToASN1(JNIEnv* env, jbyteArray byteArray) { |
| ScopedByteArrayRO bytes(env, byteArray); |
| if (bytes.get() == NULL) { |
| JNI_TRACE("ByteArrayToASN1(%p) => using byte array failed", byteArray); |
| return 0; |
| } |
| |
| const unsigned char* tmp = reinterpret_cast<const unsigned char*>(bytes.get()); |
| return d2i_func(NULL, &tmp, bytes.size()); |
| } |
| |
| /** |
| * Converts ASN.1 BIT STRING to a jbooleanArray. |
| */ |
| jbooleanArray ASN1BitStringToBooleanArray(JNIEnv* env, ASN1_BIT_STRING* bitStr) { |
| int size = bitStr->length * 8; |
| if (bitStr->flags & ASN1_STRING_FLAG_BITS_LEFT) { |
| size -= bitStr->flags & 0x07; |
| } |
| |
| ScopedLocalRef<jbooleanArray> bitsRef(env, env->NewBooleanArray(size)); |
| if (bitsRef.get() == NULL) { |
| return NULL; |
| } |
| |
| ScopedBooleanArrayRW bitsArray(env, bitsRef.get()); |
| for (int i = 0; i < static_cast<int>(bitsArray.size()); i++) { |
| bitsArray[i] = ASN1_BIT_STRING_get_bit(bitStr, i); |
| } |
| |
| return bitsRef.release(); |
| } |
| |
| /** |
| * Safely clear SSL sessions and throw an error if there was something already |
| * in the error stack. |
| */ |
| static void safeSslClear(SSL* ssl) { |
| if (SSL_clear(ssl) != 1) { |
| freeOpenSslErrorState(); |
| } |
| } |
| |
| /** |
| * To avoid the round-trip to ASN.1 and back in X509_dup, we just up the reference count. |
| */ |
| static X509* X509_dup_nocopy(X509* x509) { |
| if (x509 == NULL) { |
| return NULL; |
| } |
| CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509); |
| return x509; |
| } |
| |
| /* |
| * Sets the read and write BIO for an SSL connection and removes it when it goes out of scope. |
| * We hang on to BIO with a JNI GlobalRef and we want to remove them as soon as possible. |
| */ |
| class ScopedSslBio { |
| public: |
| ScopedSslBio(SSL *ssl, BIO* rbio, BIO* wbio) : ssl_(ssl) { |
| SSL_set_bio(ssl_, rbio, wbio); |
| CRYPTO_add(&rbio->references,1,CRYPTO_LOCK_BIO); |
| CRYPTO_add(&wbio->references,1,CRYPTO_LOCK_BIO); |
| } |
| |
| ~ScopedSslBio() { |
| SSL_set_bio(ssl_, NULL, NULL); |
| } |
| |
| private: |
| SSL* const ssl_; |
| }; |
| |
| /** |
| * Obtains the current thread's JNIEnv |
| */ |
| static JNIEnv* getJNIEnv() { |
| JNIEnv* env; |
| #ifdef ANDROID |
| if (gJavaVM->AttachCurrentThread(&env, NULL) < 0) { |
| #else |
| if (gJavaVM->AttachCurrentThread(reinterpret_cast<void**>(&env), NULL) < 0) { |
| #endif |
| ALOGE("Could not attach JavaVM to find current JNIEnv"); |
| return NULL; |
| } |
| return env; |
| } |
| |
| /** |
| * BIO for InputStream |
| */ |
| class BIO_Stream { |
| public: |
| BIO_Stream(jobject stream) : |
| mEof(false) { |
| JNIEnv* env = getJNIEnv(); |
| mStream = env->NewGlobalRef(stream); |
| } |
| |
| ~BIO_Stream() { |
| JNIEnv* env = getJNIEnv(); |
| |
| env->DeleteGlobalRef(mStream); |
| } |
| |
| bool isEof() const { |
| JNI_TRACE("isEof? %s", mEof ? "yes" : "no"); |
| return mEof; |
| } |
| |
| int flush() { |
| JNIEnv* env = getJNIEnv(); |
| if (env == NULL) { |
| return -1; |
| } |
| |
| if (env->ExceptionCheck()) { |
| JNI_TRACE("BIO_Stream::flush called with pending exception"); |
| return -1; |
| } |
| |
| env->CallVoidMethod(mStream, outputStream_flushMethod); |
| if (env->ExceptionCheck()) { |
| return -1; |
| } |
| |
| return 1; |
| } |
| |
| protected: |
| jobject getStream() { |
| return mStream; |
| } |
| |
| void setEof(bool eof) { |
| mEof = eof; |
| } |
| |
| private: |
| jobject mStream; |
| bool mEof; |
| }; |
| |
| class BIO_InputStream : public BIO_Stream { |
| public: |
| BIO_InputStream(jobject stream) : |
| BIO_Stream(stream) { |
| } |
| |
| int read(char *buf, int len) { |
| return read_internal(buf, len, inputStream_readMethod); |
| } |
| |
| int gets(char *buf, int len) { |
| if (len > PEM_LINE_LENGTH) { |
| len = PEM_LINE_LENGTH; |
| } |
| |
| int read = read_internal(buf, len - 1, openSslInputStream_readLineMethod); |
| buf[read] = '\0'; |
| JNI_TRACE("BIO::gets \"%s\"", buf); |
| return read; |
| } |
| |
| private: |
| int read_internal(char *buf, int len, jmethodID method) { |
| JNIEnv* env = getJNIEnv(); |
| if (env == NULL) { |
| JNI_TRACE("BIO_InputStream::read could not get JNIEnv"); |
| return -1; |
| } |
| |
| if (env->ExceptionCheck()) { |
| JNI_TRACE("BIO_InputStream::read called with pending exception"); |
| return -1; |
| } |
| |
| ScopedLocalRef<jbyteArray> javaBytes(env, env->NewByteArray(len)); |
| if (javaBytes.get() == NULL) { |
| JNI_TRACE("BIO_InputStream::read failed call to NewByteArray"); |
| return -1; |
| } |
| |
| jint read = env->CallIntMethod(getStream(), method, javaBytes.get()); |
| if (env->ExceptionCheck()) { |
| JNI_TRACE("BIO_InputStream::read failed call to InputStream#read"); |
| return -1; |
| } |
| |
| /* Java uses -1 to indicate EOF condition. */ |
| if (read == -1) { |
| setEof(true); |
| read = 0; |
| } else if (read > 0) { |
| env->GetByteArrayRegion(javaBytes.get(), 0, read, reinterpret_cast<jbyte*>(buf)); |
| } |
| |
| return read; |
| } |
| |
| public: |
| /** Length of PEM-encoded line (64) plus CR plus NULL */ |
| static const int PEM_LINE_LENGTH = 66; |
| }; |
| |
| class BIO_OutputStream : public BIO_Stream { |
| public: |
| BIO_OutputStream(jobject stream) : |
| BIO_Stream(stream) { |
| } |
| |
| int write(const char *buf, int len) { |
| JNIEnv* env = getJNIEnv(); |
| if (env == NULL) { |
| JNI_TRACE("BIO_OutputStream::write => could not get JNIEnv"); |
| return -1; |
| } |
| |
| if (env->ExceptionCheck()) { |
| JNI_TRACE("BIO_OutputStream::write => called with pending exception"); |
| return -1; |
| } |
| |
| ScopedLocalRef<jbyteArray> javaBytes(env, env->NewByteArray(len)); |
| if (javaBytes.get() == NULL) { |
| JNI_TRACE("BIO_OutputStream::write => failed call to NewByteArray"); |
| return -1; |
| } |
| |
| env->SetByteArrayRegion(javaBytes.get(), 0, len, reinterpret_cast<const jbyte*>(buf)); |
| |
| env->CallVoidMethod(getStream(), outputStream_writeMethod, javaBytes.get()); |
| if (env->ExceptionCheck()) { |
| JNI_TRACE("BIO_OutputStream::write => failed call to OutputStream#write"); |
| return -1; |
| } |
| |
| return len; |
| } |
| }; |
| |
| static int bio_stream_create(BIO *b) { |
| b->init = 1; |
| b->num = 0; |
| b->ptr = NULL; |
| b->flags = 0; |
| return 1; |
| } |
| |
| static int bio_stream_destroy(BIO *b) { |
| if (b == NULL) { |
| return 0; |
| } |
| |
| if (b->ptr != NULL) { |
| delete static_cast<BIO_Stream*>(b->ptr); |
| b->ptr = NULL; |
| } |
| |
| b->init = 0; |
| b->flags = 0; |
| return 1; |
| } |
| |
| static int bio_stream_read(BIO *b, char *buf, int len) { |
| BIO_clear_retry_flags(b); |
| BIO_InputStream* stream = static_cast<BIO_InputStream*>(b->ptr); |
| int ret = stream->read(buf, len); |
| if (ret == 0) { |
| // EOF is indicated by -1 with a BIO flag. |
| BIO_set_retry_read(b); |
| return -1; |
| } |
| return ret; |
| } |
| |
| static int bio_stream_write(BIO *b, const char *buf, int len) { |
| BIO_clear_retry_flags(b); |
| BIO_OutputStream* stream = static_cast<BIO_OutputStream*>(b->ptr); |
| return stream->write(buf, len); |
| } |
| |
| static int bio_stream_puts(BIO *b, const char *buf) { |
| BIO_OutputStream* stream = static_cast<BIO_OutputStream*>(b->ptr); |
| return stream->write(buf, strlen(buf)); |
| } |
| |
| static int bio_stream_gets(BIO *b, char *buf, int len) { |
| BIO_InputStream* stream = static_cast<BIO_InputStream*>(b->ptr); |
| return stream->gets(buf, len); |
| } |
| |
| static void bio_stream_assign(BIO *b, BIO_Stream* stream) { |
| b->ptr = static_cast<void*>(stream); |
| } |
| |
| static long bio_stream_ctrl(BIO *b, int cmd, long, void *) { |
| BIO_Stream* stream = static_cast<BIO_Stream*>(b->ptr); |
| |
| switch (cmd) { |
| case BIO_CTRL_EOF: |
| return stream->isEof() ? 1 : 0; |
| case BIO_CTRL_FLUSH: |
| return stream->flush(); |
| default: |
| return 0; |
| } |
| } |
| |
| static BIO_METHOD stream_bio_method = { |
| ( 100 | 0x0400 ), /* source/sink BIO */ |
| "InputStream/OutputStream BIO", |
| bio_stream_write, /* bio_write */ |
| bio_stream_read, /* bio_read */ |
| bio_stream_puts, /* bio_puts */ |
| bio_stream_gets, /* bio_gets */ |
| bio_stream_ctrl, /* bio_ctrl */ |
| bio_stream_create, /* bio_create */ |
| bio_stream_destroy, /* bio_free */ |
| NULL, /* no bio_callback_ctrl */ |
| }; |
| |
| static jbyteArray rawSignDigestWithPrivateKey(JNIEnv* env, jobject privateKey, |
| const char* message, size_t message_len) { |
| ScopedLocalRef<jbyteArray> messageArray(env, env->NewByteArray(message_len)); |
| if (env->ExceptionCheck()) { |
| JNI_TRACE("rawSignDigestWithPrivateKey(%p) => threw exception", privateKey); |
| return NULL; |
| } |
| |
| { |
| ScopedByteArrayRW messageBytes(env, messageArray.get()); |
| if (messageBytes.get() == NULL) { |
| JNI_TRACE("rawSignDigestWithPrivateKey(%p) => using byte array failed", privateKey); |
| return NULL; |
| } |
| |
| memcpy(messageBytes.get(), message, message_len); |
| } |
| |
| jmethodID rawSignMethod = env->GetStaticMethodID(cryptoUpcallsClass, |
| "rawSignDigestWithPrivateKey", "(Ljava/security/PrivateKey;[B)[B"); |
| if (rawSignMethod == NULL) { |
| ALOGE("Could not find rawSignDigestWithPrivateKey"); |
| return NULL; |
| } |
| |
| return reinterpret_cast<jbyteArray>(env->CallStaticObjectMethod( |
| cryptoUpcallsClass, rawSignMethod, privateKey, messageArray.get())); |
| } |
| |
| static jbyteArray rawCipherWithPrivateKey(JNIEnv* env, jobject privateKey, jboolean encrypt, |
| const char* ciphertext, size_t ciphertext_len) { |
| ScopedLocalRef<jbyteArray> ciphertextArray(env, env->NewByteArray(ciphertext_len)); |
| if (env->ExceptionCheck()) { |
| JNI_TRACE("rawCipherWithPrivateKey(%p) => threw exception", privateKey); |
| return NULL; |
| } |
| |
| { |
| ScopedByteArrayRW ciphertextBytes(env, ciphertextArray.get()); |
| if (ciphertextBytes.get() == NULL) { |
| JNI_TRACE("rawCipherWithPrivateKey(%p) => using byte array failed", privateKey); |
| return NULL; |
| } |
| |
| memcpy(ciphertextBytes.get(), ciphertext, ciphertext_len); |
| } |
| |
| jmethodID rawCipherMethod = env->GetStaticMethodID(cryptoUpcallsClass, |
| "rawCipherWithPrivateKey", "(Ljava/security/PrivateKey;Z[B)[B"); |
| if (rawCipherMethod == NULL) { |
| ALOGE("Could not find rawCipherWithPrivateKey"); |
| return NULL; |
| } |
| |
| return reinterpret_cast<jbyteArray>(env->CallStaticObjectMethod( |
| cryptoUpcallsClass, rawCipherMethod, privateKey, encrypt, ciphertextArray.get())); |
| } |
| |
| // ********************************************* |
| // From keystore_openssl.cpp in Chromium source. |
| // ********************************************* |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| // Custom RSA_METHOD that uses the platform APIs. |
| // Note that for now, only signing through RSA_sign() is really supported. |
| // all other method pointers are either stubs returning errors, or no-ops. |
| // See <openssl/rsa.h> for exact declaration of RSA_METHOD. |
| |
| int RsaMethodPubEnc(int /* flen */, |
| const unsigned char* /* from */, |
| unsigned char* /* to */, |
| RSA* /* rsa */, |
| int /* padding */) { |
| RSAerr(RSA_F_RSA_PUBLIC_ENCRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED); |
| return -1; |
| } |
| |
| int RsaMethodPubDec(int /* flen */, |
| const unsigned char* /* from */, |
| unsigned char* /* to */, |
| RSA* /* rsa */, |
| int /* padding */) { |
| RSAerr(RSA_F_RSA_PUBLIC_DECRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED); |
| return -1; |
| } |
| |
| // See RSA_eay_private_encrypt in |
| // third_party/openssl/openssl/crypto/rsa/rsa_eay.c for the default |
| // implementation of this function. |
| int RsaMethodPrivEnc(int flen, |
| const unsigned char *from, |
| unsigned char *to, |
| RSA *rsa, |
| int padding) { |
| if (padding != RSA_PKCS1_PADDING) { |
| // TODO(davidben): If we need to, we can implement RSA_NO_PADDING |
| // by using javax.crypto.Cipher and picking either the |
| // "RSA/ECB/NoPadding" or "RSA/ECB/PKCS1Padding" transformation as |
| // appropriate. I believe support for both of these was added in |
| // the same Android version as the "NONEwithRSA" |
| // java.security.Signature algorithm, so the same version checks |
| // for GetRsaLegacyKey should work. |
| RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); |
| return -1; |
| } |
| |
| // Retrieve private key JNI reference. |
| jobject private_key = reinterpret_cast<jobject>(RSA_get_app_data(rsa)); |
| if (!private_key) { |
| ALOGE("Null JNI reference passed to RsaMethodPrivEnc!"); |
| RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR); |
| return -1; |
| } |
| |
| JNIEnv* env = getJNIEnv(); |
| if (env == NULL) { |
| return -1; |
| } |
| |
| // For RSA keys, this function behaves as RSA_private_encrypt with |
| // PKCS#1 padding. |
| ScopedLocalRef<jbyteArray> signature( |
| env, rawSignDigestWithPrivateKey(env, private_key, |
| reinterpret_cast<const char*>(from), flen)); |
| if (signature.get() == NULL) { |
| ALOGE("Could not sign message in RsaMethodPrivEnc!"); |
| RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR); |
| return -1; |
| } |
| |
| ScopedByteArrayRO signatureBytes(env, signature.get()); |
| size_t expected_size = static_cast<size_t>(RSA_size(rsa)); |
| if (signatureBytes.size() > expected_size) { |
| ALOGE("RSA Signature size mismatch, actual: %zd, expected <= %zd", signatureBytes.size(), |
| expected_size); |
| RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR); |
| return -1; |
| } |
| |
| // Copy result to OpenSSL-provided buffer. rawSignDigestWithPrivateKey |
| // should pad with leading 0s, but if it doesn't, pad the result. |
| size_t zero_pad = expected_size - signatureBytes.size(); |
| memset(to, 0, zero_pad); |
| memcpy(to + zero_pad, signatureBytes.get(), signatureBytes.size()); |
| |
| return expected_size; |
| } |
| |
| int RsaMethodPrivDec(int flen, |
| const unsigned char* from, |
| unsigned char* to, |
| RSA* rsa, |
| int padding) { |
| if (padding != RSA_PKCS1_PADDING) { |
| RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, RSA_R_UNKNOWN_PADDING_TYPE); |
| return -1; |
| } |
| |
| // Retrieve private key JNI reference. |
| jobject private_key = reinterpret_cast<jobject>(RSA_get_app_data(rsa)); |
| if (!private_key) { |
| ALOGE("Null JNI reference passed to RsaMethodPrivDec!"); |
| RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, ERR_R_INTERNAL_ERROR); |
| return -1; |
| } |
| |
| JNIEnv* env = getJNIEnv(); |
| if (env == NULL) { |
| return -1; |
| } |
| |
| // For RSA keys, this function behaves as RSA_private_decrypt with |
| // PKCS#1 padding. |
| ScopedLocalRef<jbyteArray> cleartext(env, rawCipherWithPrivateKey(env, private_key, false, |
| reinterpret_cast<const char*>(from), flen)); |
| if (cleartext.get() == NULL) { |
| ALOGE("Could not decrypt message in RsaMethodPrivDec!"); |
| RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, ERR_R_INTERNAL_ERROR); |
| return -1; |
| } |
| |
| ScopedByteArrayRO cleartextBytes(env, cleartext.get()); |
| size_t expected_size = static_cast<size_t>(RSA_size(rsa)); |
| if (cleartextBytes.size() > expected_size) { |
| ALOGE("RSA ciphertext size mismatch, actual: %zd, expected <= %zd", cleartextBytes.size(), |
| expected_size); |
| RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, ERR_R_INTERNAL_ERROR); |
| return -1; |
| } |
| |
| // Copy result to OpenSSL-provided buffer. |
| memcpy(to, cleartextBytes.get(), cleartextBytes.size()); |
| |
| return cleartextBytes.size(); |
| } |
| |
| int RsaMethodInit(RSA*) { |
| return 0; |
| } |
| |
| int RsaMethodFinish(RSA* rsa) { |
| // Ensure the global JNI reference created with this wrapper is |
| // properly destroyed with it. |
| jobject key = reinterpret_cast<jobject>(RSA_get_app_data(rsa)); |
| if (key != NULL) { |
| RSA_set_app_data(rsa, NULL); |
| JNIEnv* env = getJNIEnv(); |
| env->DeleteGlobalRef(key); |
| } |
| // Actual return value is ignored by OpenSSL. There are no docs |
| // explaining what this is supposed to be. |
| return 0; |
| } |
| |
| const RSA_METHOD android_rsa_method = { |
| /* .name = */ "Android signing-only RSA method", |
| /* .rsa_pub_enc = */ RsaMethodPubEnc, |
| /* .rsa_pub_dec = */ RsaMethodPubDec, |
| /* .rsa_priv_enc = */ RsaMethodPrivEnc, |
| /* .rsa_priv_dec = */ RsaMethodPrivDec, |
| /* .rsa_mod_exp = */ NULL, |
| /* .bn_mod_exp = */ NULL, |
| /* .init = */ RsaMethodInit, |
| /* .finish = */ RsaMethodFinish, |
| // This flag is necessary to tell OpenSSL to avoid checking the content |
| // (i.e. internal fields) of the private key. Otherwise, it will complain |
| // it's not valid for the certificate. |
| /* .flags = */ RSA_METHOD_FLAG_NO_CHECK, |
| /* .app_data = */ NULL, |
| /* .rsa_sign = */ NULL, |
| /* .rsa_verify = */ NULL, |
| /* .rsa_keygen = */ NULL, |
| }; |
| |
| // Used to ensure that the global JNI reference associated with a custom |
| // EC_KEY + ECDSA_METHOD wrapper is released when its EX_DATA is destroyed |
| // (this function is called when EVP_PKEY_free() is called on the wrapper). |
| void ExDataFree(void* /* parent */, |
| void* ptr, |
| CRYPTO_EX_DATA* ad, |
| int idx, |
| long /* argl */, |
| #if defined(OPENSSL_IS_BORINGSSL) |
| const void* /* argp */) { |
| #else /* defined(OPENSSL_IS_BORINGSSL) */ |
| void* /* argp */) { |
| #endif /* defined(OPENSSL_IS_BORINGSSL) */ |
| jobject private_key = reinterpret_cast<jobject>(ptr); |
| if (private_key == NULL) return; |
| |
| CRYPTO_set_ex_data(ad, idx, NULL); |
| JNIEnv* env = getJNIEnv(); |
| env->DeleteGlobalRef(private_key); |
| } |
| |
| int ExDataDup(CRYPTO_EX_DATA* /* to */, |
| CRYPTO_EX_DATA* /* from */, |
| void* /* from_d */, |
| int /* idx */, |
| long /* argl */, |
| #if defined(OPENSSL_IS_BORINGSSL) |
| const void* /* argp */) { |
| #else /* defined(OPENSSL_IS_BORINGSSL) */ |
| void* /* argp */) { |
| #endif /* defined(OPENSSL_IS_BORINGSSL) */ |
| // This callback shall never be called with the current OpenSSL |
| // implementation (the library only ever duplicates EX_DATA items |
| // for SSL and BIO objects). But provide this to catch regressions |
| // in the future. |
| // Return value is currently ignored by OpenSSL. |
| return 0; |
| } |
| |
| class EcdsaExDataIndex { |
| public: |
| int ex_data_index() { return ex_data_index_; } |
| |
| static EcdsaExDataIndex& Instance() { |
| static EcdsaExDataIndex singleton; |
| return singleton; |
| } |
| |
| private: |
| EcdsaExDataIndex() { |
| ex_data_index_ = ECDSA_get_ex_new_index(0, NULL, NULL, ExDataDup, ExDataFree); |
| } |
| EcdsaExDataIndex(EcdsaExDataIndex const&); |
| ~EcdsaExDataIndex() {} |
| EcdsaExDataIndex& operator=(EcdsaExDataIndex const&); |
| |
| int ex_data_index_; |
| }; |
| |
| // Returns the index of the custom EX_DATA used to store the JNI reference. |
| int EcdsaGetExDataIndex(void) { |
| EcdsaExDataIndex& exData = EcdsaExDataIndex::Instance(); |
| return exData.ex_data_index(); |
| } |
| |
| ECDSA_SIG* EcdsaMethodDoSign(const unsigned char* dgst, int dgst_len, const BIGNUM* /* inv */, |
| const BIGNUM* /* rp */, EC_KEY* eckey) { |
| // Retrieve private key JNI reference. |
| jobject private_key = |
| reinterpret_cast<jobject>(ECDSA_get_ex_data(eckey, EcdsaGetExDataIndex())); |
| if (!private_key) { |
| ALOGE("Null JNI reference passed to EcdsaMethodDoSign!"); |
| return NULL; |
| } |
| JNIEnv* env = getJNIEnv(); |
| if (env == NULL) { |
| return NULL; |
| } |
| |
| // Sign message with it through JNI. |
| ScopedLocalRef<jbyteArray> signature( |
| env, rawSignDigestWithPrivateKey(env, private_key, reinterpret_cast<const char*>(dgst), |
| dgst_len)); |
| if (signature.get() == NULL) { |
| ALOGE("Could not sign message in EcdsaMethodDoSign!"); |
| return NULL; |
| } |
| |
| ScopedByteArrayRO signatureBytes(env, signature.get()); |
| // Note: With ECDSA, the actual signature may be smaller than |
| // ECDSA_size(). |
| size_t max_expected_size = static_cast<size_t>(ECDSA_size(eckey)); |
| if (signatureBytes.size() > max_expected_size) { |
| ALOGE("ECDSA Signature size mismatch, actual: %zd, expected <= %zd", signatureBytes.size(), |
| max_expected_size); |
| return NULL; |
| } |
| |
| // Convert signature to ECDSA_SIG object |
| const unsigned char* sigbuf = reinterpret_cast<const unsigned char*>(signatureBytes.get()); |
| long siglen = static_cast<long>(signatureBytes.size()); |
| return d2i_ECDSA_SIG(NULL, &sigbuf, siglen); |
| } |
| |
| int EcdsaMethodSignSetup(EC_KEY* /* eckey */, |
| BN_CTX* /* ctx */, |
| BIGNUM** /* kinv */, |
| BIGNUM** /* r */, |
| const unsigned char*, |
| int) { |
| ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ECDSA_R_ERR_EC_LIB); |
| return -1; |
| } |
| |
| int EcdsaMethodDoVerify(const unsigned char* /* dgst */, |
| int /* dgst_len */, |
| const ECDSA_SIG* /* sig */, |
| EC_KEY* /* eckey */) { |
| ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ECDSA_R_ERR_EC_LIB); |
| return -1; |
| } |
| |
| const ECDSA_METHOD android_ecdsa_method = { |
| /* .name = */ "Android signing-only ECDSA method", |
| /* .ecdsa_do_sign = */ EcdsaMethodDoSign, |
| /* .ecdsa_sign_setup = */ EcdsaMethodSignSetup, |
| /* .ecdsa_do_verify = */ EcdsaMethodDoVerify, |
| /* .flags = */ 0, |
| /* .app_data = */ NULL, |
| }; |
| |
| #else /* OPENSSL_IS_BORINGSSL */ |
| |
| namespace { |
| |
| ENGINE *g_engine; |
| int g_rsa_exdata_index; |
| int g_ecdsa_exdata_index; |
| pthread_once_t g_engine_once = PTHREAD_ONCE_INIT; |
| |
| void init_engine_globals(); |
| |
| void ensure_engine_globals() { |
| pthread_once(&g_engine_once, init_engine_globals); |
| } |
| |
| // KeyExData contains the data that is contained in the EX_DATA of the RSA |
| // and ECDSA objects that are created to wrap Android system keys. |
| struct KeyExData { |
| // private_key contains a reference to a Java, private-key object. |
| jobject private_key; |
| // cached_size contains the "size" of the key. This is the size of the |
| // modulus (in bytes) for RSA, or the group order size for ECDSA. This |
| // avoids calling into Java to calculate the size. |
| size_t cached_size; |
| }; |
| |
| // ExDataDup is called when one of the RSA or EC_KEY objects is duplicated. We |
| // don't support this and it should never happen. |
| int ExDataDup(CRYPTO_EX_DATA* /* to */, |
| const CRYPTO_EX_DATA* /* from */, |
| void** /* from_d */, |
| int /* index */, |
| long /* argl */, |
| void* /* argp */) { |
| return 0; |
| } |
| |
| // ExDataFree is called when one of the RSA or EC_KEY objects is freed. |
| void ExDataFree(void* /* parent */, |
| void* ptr, |
| CRYPTO_EX_DATA* /* ad */, |
| int /* index */, |
| long /* argl */, |
| void* /* argp */) { |
| // Ensure the global JNI reference created with this wrapper is |
| // properly destroyed with it. |
| KeyExData *ex_data = reinterpret_cast<KeyExData*>(ptr); |
| if (ex_data != NULL) { |
| JNIEnv* env = getJNIEnv(); |
| env->DeleteGlobalRef(ex_data->private_key); |
| delete ex_data; |
| } |
| } |
| |
| KeyExData* RsaGetExData(const RSA* rsa) { |
| return reinterpret_cast<KeyExData*>(RSA_get_ex_data(rsa, g_rsa_exdata_index)); |
| } |
| |
| size_t RsaMethodSize(const RSA *rsa) { |
| const KeyExData *ex_data = RsaGetExData(rsa); |
| return ex_data->cached_size; |
| } |
| |
| int RsaMethodEncrypt(RSA* /* rsa */, |
| size_t* /* out_len */, |
| uint8_t* /* out */, |
| size_t /* max_out */, |
| const uint8_t* /* in */, |
| size_t /* in_len */, |
| int /* padding */) { |
| OPENSSL_PUT_ERROR(RSA, encrypt, RSA_R_UNKNOWN_ALGORITHM_TYPE); |
| return 0; |
| } |
| |
| int RsaMethodSignRaw(RSA* rsa, |
| size_t* out_len, |
| uint8_t* out, |
| size_t max_out, |
| const uint8_t* in, |
| size_t in_len, |
| int padding) { |
| if (padding != RSA_PKCS1_PADDING) { |
| // TODO(davidben): If we need to, we can implement RSA_NO_PADDING |
| // by using javax.crypto.Cipher and picking either the |
| // "RSA/ECB/NoPadding" or "RSA/ECB/PKCS1Padding" transformation as |
| // appropriate. I believe support for both of these was added in |
| // the same Android version as the "NONEwithRSA" |
| // java.security.Signature algorithm, so the same version checks |
| // for GetRsaLegacyKey should work. |
| OPENSSL_PUT_ERROR(RSA, sign_raw, RSA_R_UNKNOWN_PADDING_TYPE); |
| return 0; |
| } |
| |
| // Retrieve private key JNI reference. |
| const KeyExData *ex_data = RsaGetExData(rsa); |
| if (!ex_data || !ex_data->private_key) { |
| OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR); |
| return 0; |
| } |
| |
| JNIEnv* env = getJNIEnv(); |
| if (env == NULL) { |
| OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR); |
| return 0; |
| } |
| |
| // For RSA keys, this function behaves as RSA_private_decrypt with |
| // PKCS#1 v1.5 padding. |
| ScopedLocalRef<jbyteArray> cleartext( |
| env, rawCipherWithPrivateKey(env, ex_data->private_key, false, |
| reinterpret_cast<const char*>(in), in_len)); |
| |
| if (cleartext.get() == NULL) { |
| OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR); |
| return 0; |
| } |
| |
| ScopedByteArrayRO result(env, cleartext.get()); |
| |
| size_t expected_size = static_cast<size_t>(RSA_size(rsa)); |
| if (result.size() > expected_size) { |
| OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR); |
| return 0; |
| } |
| |
| if (max_out < expected_size) { |
| OPENSSL_PUT_ERROR(RSA, sign_raw, RSA_R_DATA_TOO_LARGE); |
| return 0; |
| } |
| |
| // Copy result to OpenSSL-provided buffer. RawSignDigestWithPrivateKey |
| // should pad with leading 0s, but if it doesn't, pad the result. |
| size_t zero_pad = expected_size - result.size(); |
| memset(out, 0, zero_pad); |
| memcpy(out + zero_pad, &result[0], result.size()); |
| *out_len = expected_size; |
| |
| return 1; |
| } |
| |
| int RsaMethodDecrypt(RSA* /* rsa */, |
| size_t* /* out_len */, |
| uint8_t* /* out */, |
| size_t /* max_out */, |
| const uint8_t* /* in */, |
| size_t /* in_len */, |
| int /* padding */) { |
| OPENSSL_PUT_ERROR(RSA, decrypt, RSA_R_UNKNOWN_ALGORITHM_TYPE); |
| return 0; |
| } |
| |
| int RsaMethodVerifyRaw(RSA* /* rsa */, |
| size_t* /* out_len */, |
| uint8_t* /* out */, |
| size_t /* max_out */, |
| const uint8_t* /* in */, |
| size_t /* in_len */, |
| int /* padding */) { |
| OPENSSL_PUT_ERROR(RSA, verify_raw, RSA_R_UNKNOWN_ALGORITHM_TYPE); |
| return 0; |
| } |
| |
| const RSA_METHOD android_rsa_method = { |
| { |
| 0 /* references */, |
| 1 /* is_static */ |
| } /* common */, |
| NULL /* app_data */, |
| |
| NULL /* init */, |
| NULL /* finish */, |
| RsaMethodSize, |
| NULL /* sign */, |
| NULL /* verify */, |
| RsaMethodEncrypt, |
| RsaMethodSignRaw, |
| RsaMethodDecrypt, |
| RsaMethodVerifyRaw, |
| NULL /* mod_exp */, |
| NULL /* bn_mod_exp */, |
| NULL /* private_transform */, |
| RSA_FLAG_OPAQUE, |
| NULL /* keygen */, |
| NULL /* supports_digest */, |
| }; |
| |
| // Custom ECDSA_METHOD that uses the platform APIs. |
| // Note that for now, only signing through ECDSA_sign() is really supported. |
| // all other method pointers are either stubs returning errors, or no-ops. |
| |
| jobject EcKeyGetKey(const EC_KEY* ec_key) { |
| KeyExData* ex_data = reinterpret_cast<KeyExData*>(EC_KEY_get_ex_data( |
| ec_key, g_ecdsa_exdata_index)); |
| return ex_data->private_key; |
| } |
| |
| size_t EcdsaMethodGroupOrderSize(const EC_KEY* ec_key) { |
| KeyExData* ex_data = reinterpret_cast<KeyExData*>(EC_KEY_get_ex_data( |
| ec_key, g_ecdsa_exdata_index)); |
| return ex_data->cached_size; |
| } |
| |
| int EcdsaMethodSign(const uint8_t* digest, |
| size_t digest_len, |
| uint8_t* sig, |
| unsigned int* sig_len, |
| EC_KEY* ec_key) { |
| // Retrieve private key JNI reference. |
| jobject private_key = EcKeyGetKey(ec_key); |
| if (!private_key) { |
| ALOGE("Null JNI reference passed to EcdsaMethodSign!"); |
| return 0; |
| } |
| |
| JNIEnv* env = getJNIEnv(); |
| if (env == NULL) { |
| return 0; |
| } |
| |
| // Sign message with it through JNI. |
| ScopedLocalRef<jbyteArray> signature( |
| env, rawSignDigestWithPrivateKey(env, private_key, |
| reinterpret_cast<const char*>(digest), |
| digest_len)); |
| if (signature.get() == NULL) { |
| ALOGE("Could not sign message in EcdsaMethodDoSign!"); |
| return 0; |
| } |
| |
| ScopedByteArrayRO signatureBytes(env, signature.get()); |
| // Note: With ECDSA, the actual signature may be smaller than |
| // ECDSA_size(). |
| size_t max_expected_size = ECDSA_size(ec_key); |
| if (signatureBytes.size() > max_expected_size) { |
| ALOGE("ECDSA Signature size mismatch, actual: %zd, expected <= %zd", |
| signatureBytes.size(), max_expected_size); |
| return 0; |
| } |
| |
| memcpy(sig, signatureBytes.get(), signatureBytes.size()); |
| *sig_len = signatureBytes.size(); |
| return 1; |
| } |
| |
| int EcdsaMethodVerify(const uint8_t* /* digest */, |
| size_t /* digest_len */, |
| const uint8_t* /* sig */, |
| size_t /* sig_len */, |
| EC_KEY* /* ec_key */) { |
| OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ECDSA_R_NOT_IMPLEMENTED); |
| return 0; |
| } |
| |
| const ECDSA_METHOD android_ecdsa_method = { |
| { |
| 0 /* references */, |
| 1 /* is_static */ |
| } /* common */, |
| NULL /* app_data */, |
| |
| NULL /* init */, |
| NULL /* finish */, |
| EcdsaMethodGroupOrderSize, |
| EcdsaMethodSign, |
| EcdsaMethodVerify, |
| ECDSA_FLAG_OPAQUE, |
| }; |
| |
| |
| void init_engine_globals() { |
| g_rsa_exdata_index = |
| RSA_get_ex_new_index(0 /* argl */, NULL /* argp */, NULL /* new_func */, |
| ExDataDup, ExDataFree); |
| g_ecdsa_exdata_index = |
| EC_KEY_get_ex_new_index(0 /* argl */, NULL /* argp */, |
| NULL /* new_func */, ExDataDup, ExDataFree); |
| |
| g_engine = ENGINE_new(); |
| ENGINE_set_RSA_method(g_engine, &android_rsa_method, |
| sizeof(android_rsa_method)); |
| ENGINE_set_ECDSA_method(g_engine, &android_ecdsa_method, |
| sizeof(android_ecdsa_method)); |
| } |
| |
| } // anonymous namespace |
| #endif |
| |
| #ifdef CONSCRYPT_UNBUNDLED |
| /* |
| * This is a big hack; don't learn from this. Basically what happened is we do |
| * not have an API way to insert ourselves into the AsynchronousCloseMonitor |
| * that's compiled into the native libraries for libcore when we're unbundled. |
| * So we try to look up the symbol from the main library to find it. |
| */ |
| typedef void (*acm_ctor_func)(void*, int); |
| typedef void (*acm_dtor_func)(void*); |
| static acm_ctor_func async_close_monitor_ctor = NULL; |
| static acm_dtor_func async_close_monitor_dtor = NULL; |
| |
| class CompatibilityCloseMonitor { |
| public: |
| CompatibilityCloseMonitor(int fd) { |
| if (async_close_monitor_ctor != NULL) { |
| async_close_monitor_ctor(objBuffer, fd); |
| } |
| } |
| |
| ~CompatibilityCloseMonitor() { |
| if (async_close_monitor_dtor != NULL) { |
| async_close_monitor_dtor(objBuffer); |
| } |
| } |
| private: |
| char objBuffer[256]; |
| #if 0 |
| static_assert(sizeof(objBuffer) > 2*sizeof(AsynchronousCloseMonitor), |
| "CompatibilityCloseMonitor must be larger than the actual object"); |
| #endif |
| }; |
| |
| static void findAsynchronousCloseMonitorFuncs() { |
| void *lib = dlopen("libjavacore.so", RTLD_NOW); |
| if (lib != NULL) { |
| async_close_monitor_ctor = (acm_ctor_func) dlsym(lib, "_ZN24AsynchronousCloseMonitorC1Ei"); |
| async_close_monitor_dtor = (acm_dtor_func) dlsym(lib, "_ZN24AsynchronousCloseMonitorD1Ev"); |
| } |
| } |
| #endif |
| |
| /** |
| * Copied from libnativehelper NetworkUtilites.cpp |
| */ |
| static bool setBlocking(int fd, bool blocking) { |
| int flags = fcntl(fd, F_GETFL); |
| if (flags == -1) { |
| return false; |
| } |
| |
| if (!blocking) { |
| flags |= O_NONBLOCK; |
| } else { |
| flags &= ~O_NONBLOCK; |
| } |
| |
| int rc = fcntl(fd, F_SETFL, flags); |
| return (rc != -1); |
| } |
| |
| /** |
| * OpenSSL locking support. Taken from the O'Reilly book by Viega et al., but I |
| * suppose there are not many other ways to do this on a Linux system (modulo |
| * isomorphism). |
| */ |
| #define MUTEX_TYPE pthread_mutex_t |
| #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) |
| #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) |
| #define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) |
| #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) |
| #define THREAD_ID pthread_self() |
| #define THROW_SSLEXCEPTION (-2) |
| #define THROW_SOCKETTIMEOUTEXCEPTION (-3) |
| #define THROWN_EXCEPTION (-4) |
| |
| static MUTEX_TYPE* mutex_buf = NULL; |
| |
| static void locking_function(int mode, int n, const char*, int) { |
| if (mode & CRYPTO_LOCK) { |
| MUTEX_LOCK(mutex_buf[n]); |
| } else { |
| MUTEX_UNLOCK(mutex_buf[n]); |
| } |
| } |
| |
| static void threadid_callback(CRYPTO_THREADID *threadid) { |
| #if defined(__APPLE__) |
| uint64_t owner; |
| int rc = pthread_threadid_np(NULL, &owner); // Requires Mac OS 10.6 |
| if (rc == 0) { |
| CRYPTO_THREADID_set_numeric(threadid, owner); |
| } else { |
| ALOGE("Error calling pthread_threadid_np"); |
| } |
| #else |
| // bionic exposes gettid(), but glibc doesn't |
| CRYPTO_THREADID_set_numeric(threadid, syscall(__NR_gettid)); |
| #endif |
| } |
| |
| int THREAD_setup(void) { |
| mutex_buf = new MUTEX_TYPE[CRYPTO_num_locks()]; |
| if (!mutex_buf) { |
| return 0; |
| } |
| |
| for (int i = 0; i < CRYPTO_num_locks(); ++i) { |
| MUTEX_SETUP(mutex_buf[i]); |
| } |
| |
| CRYPTO_THREADID_set_callback(threadid_callback); |
| CRYPTO_set_locking_callback(locking_function); |
| |
| return 1; |
| } |
| |
| int THREAD_cleanup(void) { |
| if (!mutex_buf) { |
| return 0; |
| } |
| |
| CRYPTO_THREADID_set_callback(NULL); |
| CRYPTO_set_locking_callback(NULL); |
| |
| for (int i = 0; i < CRYPTO_num_locks( ); i++) { |
| MUTEX_CLEANUP(mutex_buf[i]); |
| } |
| |
| free(mutex_buf); |
| mutex_buf = NULL; |
| |
| return 1; |
| } |
| |
| /** |
| * Initialization phase for every OpenSSL job: Loads the Error strings, the |
| * crypto algorithms and reset the OpenSSL library |
| */ |
| static jboolean NativeCrypto_clinit(JNIEnv*, jclass) |
| { |
| SSL_load_error_strings(); |
| ERR_load_crypto_strings(); |
| SSL_library_init(); |
| OpenSSL_add_all_algorithms(); |
| THREAD_setup(); |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| return JNI_FALSE; |
| #else |
| return JNI_TRUE; |
| #endif |
| } |
| |
| static void NativeCrypto_ENGINE_load_dynamic(JNIEnv*, jclass) { |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| JNI_TRACE("ENGINE_load_dynamic()"); |
| |
| ENGINE_load_dynamic(); |
| #endif |
| } |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| static jlong NativeCrypto_ENGINE_by_id(JNIEnv* env, jclass, jstring idJava) { |
| JNI_TRACE("ENGINE_by_id(%p)", idJava); |
| |
| ScopedUtfChars id(env, idJava); |
| if (id.c_str() == NULL) { |
| JNI_TRACE("ENGINE_by_id(%p) => id == null", idJava); |
| return 0; |
| } |
| JNI_TRACE("ENGINE_by_id(\"%s\")", id.c_str()); |
| |
| ENGINE* e = ENGINE_by_id(id.c_str()); |
| if (e == NULL) { |
| freeOpenSslErrorState(); |
| } |
| |
| JNI_TRACE("ENGINE_by_id(\"%s\") => %p", id.c_str(), e); |
| return reinterpret_cast<uintptr_t>(e); |
| } |
| #else |
| static jlong NativeCrypto_ENGINE_by_id(JNIEnv*, jclass, jstring) { |
| return 0; |
| } |
| #endif |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| static jint NativeCrypto_ENGINE_add(JNIEnv* env, jclass, jlong engineRef) { |
| ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef)); |
| JNI_TRACE("ENGINE_add(%p)", e); |
| |
| if (e == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", "engineRef == 0"); |
| return 0; |
| } |
| |
| int ret = ENGINE_add(e); |
| |
| /* |
| * We tolerate errors, because the most likely error is that |
| * the ENGINE is already in the list. |
| */ |
| freeOpenSslErrorState(); |
| |
| JNI_TRACE("ENGINE_add(%p) => %d", e, ret); |
| return ret; |
| } |
| #else |
| static jint NativeCrypto_ENGINE_add(JNIEnv*, jclass, jlong) { |
| return 0; |
| } |
| #endif |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| static jint NativeCrypto_ENGINE_init(JNIEnv* env, jclass, jlong engineRef) { |
| ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef)); |
| JNI_TRACE("ENGINE_init(%p)", e); |
| |
| if (e == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", "engineRef == 0"); |
| return 0; |
| } |
| |
| int ret = ENGINE_init(e); |
| JNI_TRACE("ENGINE_init(%p) => %d", e, ret); |
| return ret; |
| } |
| #else |
| static jint NativeCrypto_ENGINE_init(JNIEnv*, jclass, jlong) { |
| return 0; |
| } |
| #endif |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| static jint NativeCrypto_ENGINE_finish(JNIEnv* env, jclass, jlong engineRef) { |
| ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef)); |
| JNI_TRACE("ENGINE_finish(%p)", e); |
| |
| if (e == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", "engineRef == 0"); |
| return 0; |
| } |
| |
| int ret = ENGINE_finish(e); |
| JNI_TRACE("ENGINE_finish(%p) => %d", e, ret); |
| return ret; |
| } |
| #else |
| static jint NativeCrypto_ENGINE_finish(JNIEnv*, jclass, jlong) { |
| return 0; |
| } |
| #endif |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| static jint NativeCrypto_ENGINE_free(JNIEnv* env, jclass, jlong engineRef) { |
| ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef)); |
| JNI_TRACE("ENGINE_free(%p)", e); |
| |
| if (e == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", "engineRef == 0"); |
| return 0; |
| } |
| |
| int ret = ENGINE_free(e); |
| JNI_TRACE("ENGINE_free(%p) => %d", e, ret); |
| return ret; |
| } |
| #else |
| static jint NativeCrypto_ENGINE_free(JNIEnv*, jclass, jlong) { |
| return 0; |
| } |
| #endif |
| |
| #if defined(OPENSSL_IS_BORINGSSL) |
| extern "C" { |
| /* EVP_PKEY_from_keystore is from system/security/keystore-engine. */ |
| extern EVP_PKEY* EVP_PKEY_from_keystore(const char *key_id); |
| } |
| #endif |
| |
| static jlong NativeCrypto_ENGINE_load_private_key(JNIEnv* env, jclass, jlong engineRef, |
| jstring idJava) { |
| ScopedUtfChars id(env, idJava); |
| if (id.c_str() == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", "id == NULL"); |
| return 0; |
| } |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef)); |
| JNI_TRACE("ENGINE_load_private_key(%p, %p)", e, idJava); |
| |
| Unique_EVP_PKEY pkey(ENGINE_load_private_key(e, id.c_str(), NULL, NULL)); |
| if (pkey.get() == NULL) { |
| throwExceptionIfNecessary(env, "ENGINE_load_private_key", throwInvalidKeyException); |
| return 0; |
| } |
| |
| JNI_TRACE("ENGINE_load_private_key(%p, %p) => %p", e, idJava, pkey.get()); |
| return reinterpret_cast<uintptr_t>(pkey.release()); |
| #else |
| UNUSED_ARGUMENT(engineRef); |
| #if defined(NO_KEYSTORE_ENGINE) |
| jniThrowRuntimeException(env, "No keystore ENGINE support compiled in"); |
| return 0; |
| #else |
| Unique_EVP_PKEY pkey(EVP_PKEY_from_keystore(id.c_str())); |
| if (pkey.get() == NULL) { |
| throwExceptionIfNecessary(env, "ENGINE_load_private_key", throwInvalidKeyException); |
| return 0; |
| } |
| return reinterpret_cast<uintptr_t>(pkey.release()); |
| #endif |
| #endif |
| } |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| static jstring NativeCrypto_ENGINE_get_id(JNIEnv* env, jclass, jlong engineRef) |
| { |
| ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef)); |
| JNI_TRACE("ENGINE_get_id(%p)", e); |
| |
| if (e == NULL) { |
| jniThrowNullPointerException(env, "engine == null"); |
| JNI_TRACE("ENGINE_get_id(%p) => engine == null", e); |
| return NULL; |
| } |
| |
| const char *id = ENGINE_get_id(e); |
| ScopedLocalRef<jstring> idJava(env, env->NewStringUTF(id)); |
| |
| JNI_TRACE("ENGINE_get_id(%p) => \"%s\"", e, id); |
| return idJava.release(); |
| } |
| #else |
| static jstring NativeCrypto_ENGINE_get_id(JNIEnv* env, jclass, jlong) |
| { |
| ScopedLocalRef<jstring> idJava(env, env->NewStringUTF("keystore")); |
| return idJava.release(); |
| } |
| #endif |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| static jint NativeCrypto_ENGINE_ctrl_cmd_string(JNIEnv* env, jclass, jlong engineRef, |
| jstring cmdJava, jstring argJava, jint cmd_optional) |
| { |
| ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef)); |
| JNI_TRACE("ENGINE_ctrl_cmd_string(%p, %p, %p, %d)", e, cmdJava, argJava, cmd_optional); |
| |
| if (e == NULL) { |
| jniThrowNullPointerException(env, "engine == null"); |
| JNI_TRACE("ENGINE_ctrl_cmd_string(%p, %p, %p, %d) => engine == null", e, cmdJava, argJava, |
| cmd_optional); |
| return 0; |
| } |
| |
| ScopedUtfChars cmdChars(env, cmdJava); |
| if (cmdChars.c_str() == NULL) { |
| return 0; |
| } |
| |
| UniquePtr<ScopedUtfChars> arg; |
| const char* arg_c_str = NULL; |
| if (argJava != NULL) { |
| arg.reset(new ScopedUtfChars(env, argJava)); |
| arg_c_str = arg->c_str(); |
| if (arg_c_str == NULL) { |
| return 0; |
| } |
| } |
| JNI_TRACE("ENGINE_ctrl_cmd_string(%p, \"%s\", \"%s\", %d)", e, cmdChars.c_str(), arg_c_str, |
| cmd_optional); |
| |
| int ret = ENGINE_ctrl_cmd_string(e, cmdChars.c_str(), arg_c_str, cmd_optional); |
| if (ret != 1) { |
| throwExceptionIfNecessary(env, "ENGINE_ctrl_cmd_string"); |
| JNI_TRACE("ENGINE_ctrl_cmd_string(%p, \"%s\", \"%s\", %d) => threw error", e, |
| cmdChars.c_str(), arg_c_str, cmd_optional); |
| return 0; |
| } |
| |
| JNI_TRACE("ENGINE_ctrl_cmd_string(%p, \"%s\", \"%s\", %d) => %d", e, cmdChars.c_str(), |
| arg_c_str, cmd_optional, ret); |
| return ret; |
| } |
| #else |
| static jint NativeCrypto_ENGINE_ctrl_cmd_string(JNIEnv*, jclass, jlong, jstring, jstring, jint) |
| { |
| return 0; |
| } |
| #endif |
| |
| static jlong NativeCrypto_EVP_PKEY_new_DH(JNIEnv* env, jclass, |
| jbyteArray p, jbyteArray g, |
| jbyteArray pub_key, jbyteArray priv_key) { |
| JNI_TRACE("EVP_PKEY_new_DH(p=%p, g=%p, pub_key=%p, priv_key=%p)", |
| p, g, pub_key, priv_key); |
| |
| Unique_DH dh(DH_new()); |
| if (dh.get() == NULL) { |
| jniThrowRuntimeException(env, "DH_new failed"); |
| return 0; |
| } |
| |
| if (!arrayToBignum(env, p, &dh->p)) { |
| return 0; |
| } |
| |
| if (!arrayToBignum(env, g, &dh->g)) { |
| return 0; |
| } |
| |
| if (pub_key != NULL && !arrayToBignum(env, pub_key, &dh->pub_key)) { |
| return 0; |
| } |
| |
| if (priv_key != NULL && !arrayToBignum(env, priv_key, &dh->priv_key)) { |
| return 0; |
| } |
| |
| if (dh->p == NULL || dh->g == NULL |
| || (pub_key != NULL && dh->pub_key == NULL) |
| || (priv_key != NULL && dh->priv_key == NULL)) { |
| jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM"); |
| return 0; |
| } |
| |
| /* The public key can be recovered if the private key is available. */ |
| if (dh->pub_key == NULL && dh->priv_key != NULL) { |
| if (!DH_generate_key(dh.get())) { |
| jniThrowRuntimeException(env, "EVP_PKEY_new_DH failed during pub_key generation"); |
| return 0; |
| } |
| } |
| |
| Unique_EVP_PKEY pkey(EVP_PKEY_new()); |
| if (pkey.get() == NULL) { |
| jniThrowRuntimeException(env, "EVP_PKEY_new failed"); |
| return 0; |
| } |
| if (EVP_PKEY_assign_DH(pkey.get(), dh.get()) != 1) { |
| jniThrowRuntimeException(env, "EVP_PKEY_assign_DH failed"); |
| return 0; |
| } |
| OWNERSHIP_TRANSFERRED(dh); |
| JNI_TRACE("EVP_PKEY_new_DH(p=%p, g=%p, pub_key=%p, priv_key=%p) => %p", |
| p, g, pub_key, priv_key, pkey.get()); |
| return reinterpret_cast<jlong>(pkey.release()); |
| } |
| |
| /** |
| * private static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q); |
| */ |
| static jlong NativeCrypto_EVP_PKEY_new_RSA(JNIEnv* env, jclass, |
| jbyteArray n, jbyteArray e, jbyteArray d, |
| jbyteArray p, jbyteArray q, |
| jbyteArray dmp1, jbyteArray dmq1, |
| jbyteArray iqmp) { |
| JNI_TRACE("EVP_PKEY_new_RSA(n=%p, e=%p, d=%p, p=%p, q=%p, dmp1=%p, dmq1=%p, iqmp=%p)", |
| n, e, d, p, q, dmp1, dmq1, iqmp); |
| |
| Unique_RSA rsa(RSA_new()); |
| if (rsa.get() == NULL) { |
| jniThrowRuntimeException(env, "RSA_new failed"); |
| return 0; |
| } |
| |
| if (e == NULL && d == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", "e == NULL && d == NULL"); |
| JNI_TRACE("NativeCrypto_EVP_PKEY_new_RSA => e == NULL && d == NULL"); |
| return 0; |
| } |
| |
| if (!arrayToBignum(env, n, &rsa->n)) { |
| return 0; |
| } |
| |
| if (e != NULL && !arrayToBignum(env, e, &rsa->e)) { |
| return 0; |
| } |
| |
| if (d != NULL && !arrayToBignum(env, d, &rsa->d)) { |
| return 0; |
| } |
| |
| if (p != NULL && !arrayToBignum(env, p, &rsa->p)) { |
| return 0; |
| } |
| |
| if (q != NULL && !arrayToBignum(env, q, &rsa->q)) { |
| return 0; |
| } |
| |
| if (dmp1 != NULL && !arrayToBignum(env, dmp1, &rsa->dmp1)) { |
| return 0; |
| } |
| |
| if (dmq1 != NULL && !arrayToBignum(env, dmq1, &rsa->dmq1)) { |
| return 0; |
| } |
| |
| if (iqmp != NULL && !arrayToBignum(env, iqmp, &rsa->iqmp)) { |
| return 0; |
| } |
| |
| #ifdef WITH_JNI_TRACE |
| if (p != NULL && q != NULL) { |
| int check = RSA_check_key(rsa.get()); |
| JNI_TRACE("EVP_PKEY_new_RSA(...) RSA_check_key returns %d", check); |
| } |
| #endif |
| |
| if (rsa->n == NULL || (rsa->e == NULL && rsa->d == NULL)) { |
| jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM"); |
| return 0; |
| } |
| |
| /* |
| * If the private exponent is available, there is the potential to do signing |
| * operations. However, we can only do blinding if the public exponent is also |
| * available. Disable blinding if the public exponent isn't available. |
| * |
| * TODO[kroot]: We should try to recover the public exponent by trying |
| * some common ones such 3, 17, or 65537. |
| */ |
| if (rsa->d != NULL && rsa->e == NULL) { |
| JNI_TRACE("EVP_PKEY_new_RSA(...) disabling RSA blinding => %p", rsa.get()); |
| rsa->flags |= RSA_FLAG_NO_BLINDING; |
| } |
| |
| Unique_EVP_PKEY pkey(EVP_PKEY_new()); |
| if (pkey.get() == NULL) { |
| jniThrowRuntimeException(env, "EVP_PKEY_new failed"); |
| return 0; |
| } |
| if (EVP_PKEY_assign_RSA(pkey.get(), rsa.get()) != 1) { |
| jniThrowRuntimeException(env, "EVP_PKEY_new failed"); |
| return 0; |
| } |
| OWNERSHIP_TRANSFERRED(rsa); |
| JNI_TRACE("EVP_PKEY_new_RSA(n=%p, e=%p, d=%p, p=%p, q=%p dmp1=%p, dmq1=%p, iqmp=%p) => %p", |
| n, e, d, p, q, dmp1, dmq1, iqmp, pkey.get()); |
| return reinterpret_cast<uintptr_t>(pkey.release()); |
| } |
| |
| static jlong NativeCrypto_EVP_PKEY_new_EC_KEY(JNIEnv* env, jclass, jobject groupRef, |
| jobject pubkeyRef, jbyteArray keyJavaBytes) { |
| JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p)", groupRef, pubkeyRef, keyJavaBytes); |
| const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef); |
| if (group == NULL) { |
| return 0; |
| } |
| const EC_POINT* pubkey = pubkeyRef == NULL ? NULL : |
| fromContextObject<EC_POINT>(env, pubkeyRef); |
| JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) <- ptr", group, pubkey, keyJavaBytes); |
| |
| Unique_BIGNUM key(NULL); |
| if (keyJavaBytes != NULL) { |
| BIGNUM* keyRef = NULL; |
| if (!arrayToBignum(env, keyJavaBytes, &keyRef)) { |
| return 0; |
| } |
| key.reset(keyRef); |
| } |
| |
| Unique_EC_KEY eckey(EC_KEY_new()); |
| if (eckey.get() == NULL) { |
| jniThrowRuntimeException(env, "EC_KEY_new failed"); |
| return 0; |
| } |
| |
| if (EC_KEY_set_group(eckey.get(), group) != 1) { |
| JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) > EC_KEY_set_group failed", group, pubkey, |
| keyJavaBytes); |
| throwExceptionIfNecessary(env, "EC_KEY_set_group"); |
| return 0; |
| } |
| |
| if (pubkey != NULL) { |
| if (EC_KEY_set_public_key(eckey.get(), pubkey) != 1) { |
| JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) => EC_KEY_set_private_key failed", group, |
| pubkey, keyJavaBytes); |
| throwExceptionIfNecessary(env, "EC_KEY_set_public_key"); |
| return 0; |
| } |
| } |
| |
| if (key.get() != NULL) { |
| if (EC_KEY_set_private_key(eckey.get(), key.get()) != 1) { |
| JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) => EC_KEY_set_private_key failed", group, |
| pubkey, keyJavaBytes); |
| throwExceptionIfNecessary(env, "EC_KEY_set_private_key"); |
| return 0; |
| } |
| if (pubkey == NULL) { |
| Unique_EC_POINT calcPubkey(EC_POINT_new(group)); |
| if (!EC_POINT_mul(group, calcPubkey.get(), key.get(), NULL, NULL, NULL)) { |
| JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) => can't calulate public key", group, |
| pubkey, keyJavaBytes); |
| throwExceptionIfNecessary(env, "EC_KEY_set_private_key"); |
| return 0; |
| } |
| EC_KEY_set_public_key(eckey.get(), calcPubkey.get()); |
| } |
| } |
| |
| if (!EC_KEY_check_key(eckey.get())) { |
| JNI_TRACE("EVP_KEY_new_EC_KEY(%p, %p, %p) => invalid key created", group, pubkey, keyJavaBytes); |
| throwExceptionIfNecessary(env, "EC_KEY_check_key"); |
| return 0; |
| } |
| |
| Unique_EVP_PKEY pkey(EVP_PKEY_new()); |
| if (pkey.get() == NULL) { |
| JNI_TRACE("EVP_PKEY_new_EC(%p, %p, %p) => threw error", group, pubkey, keyJavaBytes); |
| throwExceptionIfNecessary(env, "EVP_PKEY_new failed"); |
| return 0; |
| } |
| if (EVP_PKEY_assign_EC_KEY(pkey.get(), eckey.get()) != 1) { |
| JNI_TRACE("EVP_PKEY_new_EC(%p, %p, %p) => threw error", group, pubkey, keyJavaBytes); |
| jniThrowRuntimeException(env, "EVP_PKEY_assign_EC_KEY failed"); |
| return 0; |
| } |
| OWNERSHIP_TRANSFERRED(eckey); |
| |
| JNI_TRACE("EVP_PKEY_new_EC_KEY(%p, %p, %p) => %p", group, pubkey, keyJavaBytes, pkey.get()); |
| return reinterpret_cast<uintptr_t>(pkey.release()); |
| } |
| |
| static jlong NativeCrypto_EVP_PKEY_new_mac_key(JNIEnv* env, jclass, jint pkeyType, |
| jbyteArray keyJavaBytes) |
| { |
| JNI_TRACE("EVP_PKEY_new_mac_key(%d, %p)", pkeyType, keyJavaBytes); |
| |
| ScopedByteArrayRO key(env, keyJavaBytes); |
| if (key.get() == NULL) { |
| return 0; |
| } |
| |
| const unsigned char* tmp = reinterpret_cast<const unsigned char*>(key.get()); |
| Unique_EVP_PKEY pkey(EVP_PKEY_new_mac_key(pkeyType, (ENGINE *) NULL, tmp, key.size())); |
| if (pkey.get() == NULL) { |
| JNI_TRACE("EVP_PKEY_new_mac_key(%d, %p) => threw error", pkeyType, keyJavaBytes); |
| throwExceptionIfNecessary(env, "ENGINE_load_private_key"); |
| return 0; |
| } |
| |
| JNI_TRACE("EVP_PKEY_new_mac_key(%d, %p) => %p", pkeyType, keyJavaBytes, pkey.get()); |
| return reinterpret_cast<uintptr_t>(pkey.release()); |
| } |
| |
| static int NativeCrypto_EVP_PKEY_type(JNIEnv* env, jclass, jobject pkeyRef) { |
| EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef); |
| JNI_TRACE("EVP_PKEY_type(%p)", pkey); |
| |
| if (pkey == NULL) { |
| return -1; |
| } |
| |
| int result = EVP_PKEY_type(pkey->type); |
| JNI_TRACE("EVP_PKEY_type(%p) => %d", pkey, result); |
| return result; |
| } |
| |
| /** |
| * private static native int EVP_PKEY_size(int pkey); |
| */ |
| static int NativeCrypto_EVP_PKEY_size(JNIEnv* env, jclass, jobject pkeyRef) |