diff --git a/common/src/jni/main/cpp/conscrypt/jniutil.cc b/common/src/jni/main/cpp/conscrypt/jniutil.cc index f6b726e15..9da4a39f5 100644 --- a/common/src/jni/main/cpp/conscrypt/jniutil.cc +++ b/common/src/jni/main/cpp/conscrypt/jniutil.cc @@ -192,8 +192,10 @@ extern bool isDirectByteBufferInstance(JNIEnv* env, jobject buffer) { } bool isGetByteArrayElementsLikelyToReturnACopy(size_t size) { -#if defined(ANDROID) && !defined(CONSCRYPT_OPENJDK) +#if (defined(ANDROID) && !defined(CONSCRYPT_OPENJDK)) || defined(CONSCRYPT_JNI_TESTING) // ART's GetByteArrayElements creates copies only for arrays smaller than 12 kB. + // CONSCRYPT_JNI_TESTING is also included so that C++ unit tests (jni_testing) on Linux can + // exercise both the true (<= 12 kB) and false (> 12 kB) code paths. return size <= 12 * 1024; #else (void)size; diff --git a/common/src/jni/main/cpp/conscrypt/native_crypto.cc b/common/src/jni/main/cpp/conscrypt/native_crypto.cc index e1960940a..60a3760cf 100644 --- a/common/src/jni/main/cpp/conscrypt/native_crypto.cc +++ b/common/src/jni/main/cpp/conscrypt/native_crypto.cc @@ -157,39 +157,36 @@ static T* fromContextObject(JNIEnv* env, jobject contextObject) { } /** - * 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. + * Converts a Java byte[] two's complement to an OpenSSL BIGNUM. Returns a + * non-null BIGNUM on success. If the return value is nullptr, 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); +static bssl::UniquePtr arrayToBignum(JNIEnv* env, jbyteArray source) { + JNI_TRACE("arrayToBignum(%p)", source); if (source == nullptr) { - JNI_TRACE("arrayToBignum(%p, %p) => source is null!", source, dest); + JNI_TRACE("arrayToBignum(%p) => source is null!", source); conscrypt::jniutil::throwNullPointerException(env, "source == null"); - return false; + return nullptr; } ScopedByteArrayRO sourceBytes(env, source); if (sourceBytes.get() == nullptr) { - JNI_TRACE("arrayToBignum(%p, %p) => null", source, dest); - return false; + JNI_TRACE("arrayToBignum(%p) => null", source); + return nullptr; } const unsigned char* tmp = reinterpret_cast(sourceBytes.get()); size_t tmpSize = sourceBytes.size(); /* if the array is empty, it is zero. */ if (tmpSize == 0) { - if (*dest == nullptr) { - *dest = BN_new(); + bssl::UniquePtr ret(BN_new()); + if (ret == nullptr) { + conscrypt::jniutil::throwOutOfMemory(env, "Failed to allocate BIGNUM"); + ERR_clear_error(); + return nullptr; } - BN_zero(*dest); - return true; + BN_zero(ret.get()); + return ret; } std::unique_ptr twosComplement; @@ -209,26 +206,17 @@ static bool arrayToBignum(JNIEnv* env, jbyteArray source, BIGNUM** dest) { } } } - BIGNUM* ret = BN_bin2bn(tmp, tmpSize, *dest); + bssl::UniquePtr ret(BN_bin2bn(tmp, tmpSize, nullptr)); 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; -} - -static bssl::UniquePtr arrayToBignum(JNIEnv* env, jbyteArray source) { - BIGNUM* bn = nullptr; - if (!arrayToBignum(env, source, &bn)) { + JNI_TRACE("arrayToBignum(%p) => threw exception", source); return nullptr; } - return bssl::UniquePtr(bn); + BN_set_negative(ret.get(), negative ? 1 : 0); + + JNI_TRACE("arrayToBignum(%p) => %p", source, ret.get()); + return ret; } /** @@ -244,6 +232,10 @@ static jbyteArray bignumToArray(JNIEnv* env, const BIGNUM* source, const char* s size_t numBytes = BN_num_bytes(source) + 1; jbyteArray javaBytes = env->NewByteArray(static_cast(numBytes)); + if (javaBytes == nullptr) { + JNI_TRACE("bignumToArray(%p, %s) => null", source, sourceName); + return nullptr; + } ScopedByteArrayRW bytes(env, javaBytes); if (bytes.get() == nullptr) { JNI_TRACE("bignumToArray(%p, %s) => null", source, sourceName); @@ -1644,8 +1636,6 @@ static jlong NativeCrypto_EVP_parse_private_key(JNIEnv* env, jclass, jbyteArray } ScopedByteArrayRO bytes(env, keyJavaBytes); if (bytes.get() == nullptr) { - JNI_TRACE("bytes=%p EVP_parse_private_key => threw exception", keyJavaBytes); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for bytes"); return 0; } @@ -1696,8 +1686,6 @@ static jlong NativeCrypto_EVP_PKEY_from_private_key_info(JNIEnv* env, jclass, ScopedByteArrayRO bytes(env, key_java_bytes); if (bytes.get() == nullptr) { - JNI_TRACE("EVP_PKEY_from_private_key_info => threw exception"); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for bytes"); return 0; } @@ -1708,6 +1696,9 @@ static jlong NativeCrypto_EVP_PKEY_from_private_key_info(JNIEnv* env, jclass, return 0; } ScopedIntArrayRO algs_ro(env, algs); + if (algs_ro.get() == nullptr) { + return 0; + } std::vector alg_pointers(num_algs); for (size_t i = 0; i < num_algs; ++i) { const EVP_PKEY_ALG* alg = GetAlg(algs_ro.get()[i]); @@ -1745,9 +1736,6 @@ static jlong NativeCrypto_EVP_PKEY_from_subject_public_key_info(JNIEnv* env, jcl ScopedByteArrayRO bytes(env, key_java_bytes); if (bytes.get() == nullptr) { - JNI_TRACE("bytes=%p EVP_PKEY_from_subject_public_key_info => threw exception", - key_java_bytes); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for bytes"); return 0; } @@ -1758,6 +1746,9 @@ static jlong NativeCrypto_EVP_PKEY_from_subject_public_key_info(JNIEnv* env, jcl return 0; } ScopedIntArrayRO algs_ro(env, algs); + if (algs_ro.get() == nullptr) { + return 0; + } std::vector alg_pointers(num_algs); for (size_t i = 0; i < num_algs; ++i) { const EVP_PKEY_ALG* alg = GetAlg(algs_ro.get()[i]); @@ -1796,9 +1787,6 @@ static jlong NativeCrypto_EVP_PKEY_from_raw_private_key(JNIEnv* env, jclass, jin ScopedByteArrayRO bytes(env, key_java_bytes); if (bytes.get() == nullptr) { - JNI_TRACE("key_java_bytes=%p EVP_PKEY_from_raw_private_key => threw exception", - key_java_bytes); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for bytes"); return 0; } @@ -1847,14 +1835,10 @@ static jbyteArray NativeCrypto_EVP_PKEY_get_raw_private_key(JNIEnv* env, jclass, env->NewByteArray(static_cast(key_length))); if (raw_key_array.get() == nullptr) { - JNI_TRACE("key=%p EVP_PKEY_get_raw_private_key: creating byte array failed", pkey); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for raw_key_array"); return nullptr; } ScopedByteArrayRW raw_key(env, raw_key_array.get()); if (raw_key.get() == nullptr) { - JNI_TRACE("EVP_PKEY_get_raw_private_key: using byte array failed"); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for raw_key"); return nullptr; } @@ -1879,8 +1863,6 @@ static jlong NativeCrypto_EVP_PKEY_from_raw_public_key(JNIEnv* env, jclass, jint ScopedByteArrayRO bytes(env, key_java_bytes); if (bytes.get() == nullptr) { - JNI_TRACE("bytes=%p EVP_PKEY_from_raw_public_key => threw exception", key_java_bytes); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for key_java_bytes"); return 0; } @@ -1922,14 +1904,10 @@ static jbyteArray NativeCrypto_EVP_PKEY_get_raw_public_key(JNIEnv* env, jclass, ScopedLocalRef raw_key_array(env, env->NewByteArray(static_cast(key_length))); if (raw_key_array.get() == nullptr) { - JNI_TRACE("EVP_PKEY_get_raw_public_key: creating byte array failed"); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for raw_key_array"); return nullptr; } ScopedByteArrayRW raw_key(env, raw_key_array.get()); if (raw_key.get() == nullptr) { - JNI_TRACE("EVP_PKEY_get_raw_public_key: using byte array failed"); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for raw_key"); return nullptr; } @@ -1954,8 +1932,6 @@ static jlong NativeCrypto_EVP_PKEY_from_private_seed(JNIEnv* env, jclass, jint p ScopedByteArrayRO seed(env, javaSeedBytes); if (seed.get() == nullptr) { - JNI_TRACE("bytes=%p EVP_PKEY_from_private_seed => threw exception", javaSeedBytes); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for seed"); return 0; } @@ -2002,14 +1978,10 @@ static jbyteArray NativeCrypto_EVP_PKEY_get_private_seed(JNIEnv* env, jclass, jo ScopedLocalRef seedArray(env, env->NewByteArray(static_cast(seed_length))); if (seedArray.get() == nullptr) { - JNI_TRACE("EVP_PKEY_get_raw_private_key: creating byte array failed"); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for seedArray"); return nullptr; } ScopedByteArrayRW seed(env, seedArray.get()); if (seed.get() == nullptr) { - JNI_TRACE("EVP_PKEY_get_raw_private_key: using byte array failed"); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for seed"); return nullptr; } @@ -2039,15 +2011,11 @@ static jbyteArray NativeCrypto_EVP_raw_X25519_private_key(JNIEnv* env, jclass cl size_t key_length = X25519_PRIVATE_KEY_LEN; ScopedLocalRef byteArray(env, env->NewByteArray(static_cast(key_length))); if (byteArray.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Allocating byte[]"); - JNI_TRACE("NativeCrypto_EVP_raw_X25519_private_key: byte array creation failed"); return nullptr; } ScopedByteArrayRW bytes(env, byteArray.get()); if (bytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Allocating scoped byte array"); - JNI_TRACE("NativeCrypto_EVP_raw_X25519_private_key: scoped byte array failed"); return nullptr; } @@ -2103,8 +2071,6 @@ static jlong NativeCrypto_EVP_parse_public_key(JNIEnv* env, jclass, jbyteArray k } ScopedByteArrayRO bytes(env, keyJavaBytes); if (bytes.get() == nullptr) { - JNI_TRACE("bytes=%p EVP_parse_public_key => threw exception", keyJavaBytes); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for bytes"); return 0; } @@ -2666,6 +2632,12 @@ static jobjectArray NativeCrypto_EC_GROUP_get_curve(JNIEnv* env, jclass, jobject bssl::UniquePtr p(BN_new()); bssl::UniquePtr a(BN_new()); bssl::UniquePtr b(BN_new()); + if (p.get() == nullptr || a.get() == nullptr || b.get() == nullptr) { + JNI_TRACE("EC_GROUP_get_curve(%p) => can't create BN", group); + conscrypt::jniutil::throwOutOfMemory(env, "BN_new"); + ERR_clear_error(); + return nullptr; + } int ret = EC_GROUP_get_curve_GFp(group, p.get(), a.get(), b.get(), nullptr); if (ret != 1) { @@ -3091,14 +3063,17 @@ static jlong NativeCrypto_EC_KEY_parse_curve_name(JNIEnv* env, jclass, jbyteArra CHECK_ERROR_QUEUE_ON_RETURN; JNI_TRACE("EC_KEY_parse_curve_name(%p)", curveNameBytes); - ScopedByteArrayRO bytes(env, curveNameBytes); - if (bytes.get() == nullptr) { - env->ExceptionClear(); + if (curveNameBytes == nullptr) { conscrypt::jniutil::throwIOException(env, "Null EC curve name"); JNI_TRACE("bytes=%p EC_KEY_parse_curve_name => curveNameBytes == null ", curveNameBytes); return 0; } + ScopedByteArrayRO bytes(env, curveNameBytes); + if (bytes.get() == nullptr) { + return 0; + } + CBS cbs; CBS_init(&cbs, reinterpret_cast(bytes.get()), bytes.size()); bssl::UniquePtr group(EC_KEY_parse_curve_name(&cbs)); @@ -3717,9 +3692,6 @@ static jboolean NativeCrypto_X25519(JNIEnv* env, jclass, jbyteArray outArray, } ScopedByteArrayRW out(env, outArray); if (out.get() == nullptr) { - JNI_TRACE("X25519(%p, %p, %p) can't get output buffer", outArray, privkeyArray, - pubkeyArray); - conscrypt::jniutil::throwOutOfMemory(env, "Can't get output buffer"); return JNI_FALSE; } if (out.size() != 32) { @@ -3734,8 +3706,6 @@ static jboolean NativeCrypto_X25519(JNIEnv* env, jclass, jbyteArray outArray, } ScopedByteArrayRO privkey(env, privkeyArray); if (privkey.get() == nullptr) { - JNI_TRACE("X25519(%p) => privkey == null", outArray); - conscrypt::jniutil::throwOutOfMemory(env, "Can't get private key buffer"); return JNI_FALSE; } if (privkey.size() != 32) { @@ -3750,8 +3720,6 @@ static jboolean NativeCrypto_X25519(JNIEnv* env, jclass, jbyteArray outArray, } ScopedByteArrayRO pubkey(env, pubkeyArray); if (pubkey.get() == nullptr) { - JNI_TRACE("X25519(%p) => pubkey == null", pubkeyArray); - conscrypt::jniutil::throwOutOfMemory(env, "Can't get public key buffer"); return JNI_FALSE; } if (pubkey.size() != 32) { @@ -4325,7 +4293,6 @@ static void evpUpdate(JNIEnv* env, jobject evpMdCtxRef, jbyteArray inJavaBytes, // another approach. jbyte* array_elements = env->GetByteArrayElements(inJavaBytes, nullptr); if (array_elements == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to obtain elements of inBytes"); return; } const unsigned char* buf = reinterpret_cast(array_elements); @@ -4421,7 +4388,6 @@ static jbyteArray NativeCrypto_EVP_DigestSignFinal(JNIEnv* env, jclass, jobject ScopedLocalRef sigJavaBytes(env, env->NewByteArray(static_cast(actualLen))); if (sigJavaBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Failed to allocate signature byte[]"); return nullptr; } env->SetByteArrayRegion(sigJavaBytes.get(), 0, static_cast(actualLen), @@ -4506,7 +4472,6 @@ static jbyteArray NativeCrypto_EVP_DigestSign(JNIEnv* env, jclass, jobject evpMd ScopedByteArrayRO array_elements(env, inJavaBytes); if (array_elements.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to obtain elements of inBytes"); return nullptr; } const unsigned char* buf = reinterpret_cast(array_elements.get()); @@ -4540,7 +4505,6 @@ static jbyteArray NativeCrypto_EVP_DigestSign(JNIEnv* env, jclass, jobject evpMd ScopedLocalRef sigJavaBytes(env, env->NewByteArray(static_cast(actualLen))); if (sigJavaBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Failed to allocate signature byte[]"); return nullptr; } env->SetByteArrayRegion(sigJavaBytes.get(), 0, static_cast(actualLen), @@ -5331,7 +5295,6 @@ static jint evp_aead_ctx_op_common(JNIEnv* env, jlong evpAeadRef, jbyteArray key } ScopedByteArrayRO keyBytes(env, keyArray); if (keyBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate key buffer"); return 0; } @@ -5353,7 +5316,6 @@ static jint evp_aead_ctx_op_common(JNIEnv* env, jlong evpAeadRef, jbyteArray key } ScopedByteArrayRO nonceBytes(env, nonceArray); if (nonceBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate nonce buffer"); return 0; } @@ -5396,7 +5358,6 @@ static jint evp_aead_ctx_op(JNIEnv* env, jlong evpAeadRef, jbyteArray keyArray, } ScopedByteArrayRW outBytes(env, outArray); if (outBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate out buffer"); return 0; } @@ -5416,7 +5377,6 @@ static jint evp_aead_ctx_op(JNIEnv* env, jlong evpAeadRef, jbyteArray keyArray, } ScopedByteArrayRO inBytes(env, inArray); if (inBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate in buffer"); return 0; } @@ -6248,7 +6208,6 @@ static void NativeCrypto_HMAC_Init_ex(JNIEnv* env, jclass, jobject hmacCtxRef, j } ScopedByteArrayRO keyBytes(env, keyArray); if (keyBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Failed to allocate memory for keyBytes"); return; } @@ -6299,7 +6258,6 @@ static void NativeCrypto_HMAC_Update(JNIEnv* env, jclass, jobject hmacCtxRef, jb } ScopedByteArrayRO inBytes(env, inArray); if (inBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Failed to allocate memory for inBytes"); return; } @@ -8030,7 +7988,6 @@ static jlong NativeCrypto_d2i_X509(JNIEnv* env, jclass, jbyteArray certBytes) { ScopedByteArrayRO bytes(env, certBytes); if (bytes.get() == nullptr) { JNI_TRACE("NativeCrypto_d2i_X509(%p) => using byte array failed", certBytes); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate byte array for cert"); return 0; } @@ -8168,6 +8125,10 @@ static jbyteArray NativeCrypto_i2d_PKCS7(JNIEnv* env, jclass, jlongArray certsAr STACK_OF(X509)* stack = sk_X509_new_null(); ScopedLongArrayRO certs(env, certsArray); + if (certs.get() == nullptr) { + sk_X509_free(stack); + return nullptr; + } for (size_t i = 0; i < certs.size(); i++) { X509* item = reinterpret_cast(certs[i]); if (sk_X509_push(stack, item) == 0) { @@ -8350,7 +8311,6 @@ static jbyteArray NativeCrypto_ASN1_seq_pack_X509(JNIEnv* env, jclass, jlongArra ScopedLongArrayRO certsArray(env, certs); if (certsArray.get() == nullptr) { JNI_TRACE("ASN1_seq_pack_X509(%p) => failed to get certs array", certs); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to get certs array"); return nullptr; } @@ -8723,7 +8683,7 @@ static jint NativeCrypto_get_X509_ex_pathlen(JNIEnv* env, jclass, jlong x509Ref, return -1; } - if (basic_constraints->pathlen->type == V_ASN1_NEG_INTEGER) { + if (ASN1_STRING_type(basic_constraints->pathlen) == V_ASN1_NEG_INTEGER) { // Path length constraints may not be negative. // TODO(https://github.com/google/conscrypt/issues/916): Treat this as an // error condition. @@ -9494,8 +9454,6 @@ static void NativeCrypto_SSL_CTX_set_session_id_context(JNIEnv* env, jclass, jlo "ssl_ctx=%p NativeCrypto_SSL_CTX_set_session_id_context => threw " "exception", ssl_ctx); - conscrypt::jniutil::throwOutOfMemory( - env, "NativeCrypto_SSL_CTX_set_session_id_context buffer allocation failed"); return; } @@ -9824,7 +9782,6 @@ static void NativeCrypto_SSL_set_signed_cert_timestamp_list(JNIEnv* env, jclass, } ScopedByteArrayRO listBytes(env, list); if (listBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate list bytes"); return; } @@ -9905,7 +9862,6 @@ static void NativeCrypto_SSL_set_ocsp_response(JNIEnv* env, jclass, jlong ssl_ad } ScopedByteArrayRO responseBytes(env, response); if (responseBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate response"); return; } @@ -9975,7 +9931,6 @@ static jbyteArray NativeCrypto_SSL_export_keying_material(JNIEnv* env, jclass, j } ScopedByteArrayRO labelBytes(env, label); if (labelBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate label bytes"); return nullptr; } std::unique_ptr out(new uint8_t[num_bytes]); @@ -9987,7 +9942,6 @@ static jbyteArray NativeCrypto_SSL_export_keying_material(JNIEnv* env, jclass, j } else { ScopedByteArrayRO contextBytes(env, context); if (contextBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate context bytes"); return nullptr; } ret = SSL_export_keying_material( @@ -10003,7 +9957,6 @@ static jbyteArray NativeCrypto_SSL_export_keying_material(JNIEnv* env, jclass, j } jbyteArray result = env->NewByteArray(static_cast(num_bytes)); if (result == nullptr) { - conscrypt::jniutil::throwSSLExceptionStr(env, "Could not create result array"); JNI_TRACE( "ssl=%p NativeCrypto_SSL_export_keying_material => could not create " "array", @@ -10501,8 +10454,6 @@ static void NativeCrypto_setApplicationProtocols(JNIEnv* env, jclass, jlong ssl_ if (client_mode) { ScopedByteArrayRO protosBytes(env, protocols); if (protosBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, - "Unable to allocate buffer for protocols"); return; } @@ -10585,7 +10536,6 @@ static void NativeCrypto_SSL_set1_groups(JNIEnv* env, jclass, jlong sslAddress, ScopedIntArrayRO groups_ro(env, groups); if (groups_ro.get() == nullptr) { JNI_TRACE("ssl=%p NativeCrypto_SSL_set1_groups => threw exception", ssl); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for groups"); return; } std::vector groups_vector; @@ -10985,12 +10935,12 @@ static jlong NativeCrypto_d2i_SSL_SESSION(JNIEnv* env, jclass, jbyteArray javaBy ScopedByteArrayRO bytes(env, javaBytes); if (bytes.get() == nullptr) { JNI_TRACE("NativeCrypto_d2i_SSL_SESSION => threw exception"); - conscrypt::jniutil::throwOutOfMemory(env, "Unable to get byte array"); return 0; } const unsigned char* ucp = reinterpret_cast(bytes.get()); // NOLINTNEXTLINE(runtime/int) - SSL_SESSION* ssl_session = d2i_SSL_SESSION(nullptr, &ucp, static_cast(bytes.size())); + bssl::UniquePtr ssl_session( + d2i_SSL_SESSION(nullptr, &ucp, static_cast(bytes.size()))); if (ssl_session == nullptr || ucp != (reinterpret_cast(bytes.get()) + bytes.size())) { @@ -11000,8 +10950,8 @@ static jlong NativeCrypto_d2i_SSL_SESSION(JNIEnv* env, jclass, jbyteArray javaBy return 0L; } - JNI_TRACE("NativeCrypto_d2i_SSL_SESSION => %p", ssl_session); - return reinterpret_cast(ssl_session); + JNI_TRACE("NativeCrypto_d2i_SSL_SESSION => %p", ssl_session.get()); + return reinterpret_cast(ssl_session.release()); } static jstring NativeCrypto_SSL_CIPHER_get_kx_name(JNIEnv* env, jclass, jlong cipher_address) { @@ -11267,7 +11217,6 @@ static jbyteArray NativeCrypto_get_ocsp_single_extension( } ScopedByteArrayRO ocspData(env, ocspDataBytes); if (ocspData.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to create ocspData"); return nullptr; } @@ -11921,24 +11870,20 @@ static jbyteArray NativeCrypto_Scrypt_generate_key(JNIEnv* env, jclass, jbyteArr jbyteArray key_bytes = env->NewByteArray(static_cast(key_len)); if (key_bytes == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for key_bytes"); return nullptr; } ScopedByteArrayRW out_key(env, key_bytes); if (out_key.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for key"); return nullptr; } size_t memory_limit = 1u << 29; ScopedByteArrayRO password_bytes(env, password); if (password_bytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for password"); return nullptr; } ScopedByteArrayRO salt_bytes(env, salt); if (salt_bytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate buffer for salt"); return nullptr; } @@ -11969,7 +11914,7 @@ static void NativeCrypto_SSL_CTX_set_spake_credential( SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true); if (ssl_ctx == nullptr) { - return; + return; } JNI_TRACE("SSL_CTX_set_spake_credential(%p, %p, %p, %p, %d, %d, %p)", context, pw_array, @@ -12256,7 +12201,6 @@ static jboolean NativeCrypto_SSL_set1_ech_config_list(JNIEnv* env, jclass, jlong } ScopedByteArrayRO configBytes(env, configJavaBytes); if (configBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate config bytes"); JNI_TRACE("NativeCrypto_SSL_set1_ech_config_list => could not read config bytes"); return JNI_FALSE; } @@ -12395,7 +12339,6 @@ static jboolean NativeCrypto_SSL_CTX_ech_enable_server(JNIEnv* env, jclass, jlon } ScopedByteArrayRO keyBytes(env, keyJavaBytes); if (keyBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate key bytes"); JNI_TRACE( "NativeCrypto_SSL_CTX_ech_enable_server => threw exception: " "could not read key bytes"); @@ -12403,7 +12346,6 @@ static jboolean NativeCrypto_SSL_CTX_ech_enable_server(JNIEnv* env, jclass, jlon } ScopedByteArrayRO configBytes(env, configJavaBytes); if (configBytes.get() == nullptr) { - conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate config bytes"); JNI_TRACE( "NativeCrypto_SSL_CTX_ech_enable_server => threw exception: " "could not read config bytes"); diff --git a/common/src/main/java/org/conscrypt/OpenSSLECGroupContext.java b/common/src/main/java/org/conscrypt/OpenSSLECGroupContext.java index eed2b4f64..3cc8299d3 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLECGroupContext.java +++ b/common/src/main/java/org/conscrypt/OpenSSLECGroupContext.java @@ -44,10 +44,21 @@ final class OpenSSLECGroupContext { ALIASES.put("1.2.840.10045.3.1.7", "prime256v1"); } + static final OpenSSLECGroupContext SECP224R1 = new OpenSSLECGroupContext( + new NativeRef.EC_GROUP(NativeCrypto.EC_GROUP_new_by_curve_name("secp224r1"))); + static final OpenSSLECGroupContext PRIME256V1 = new OpenSSLECGroupContext( + new NativeRef.EC_GROUP(NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1"))); + static final OpenSSLECGroupContext SECP384R1 = new OpenSSLECGroupContext( + new NativeRef.EC_GROUP(NativeCrypto.EC_GROUP_new_by_curve_name("secp384r1"))); + static final OpenSSLECGroupContext SECP521R1 = new OpenSSLECGroupContext( + new NativeRef.EC_GROUP(NativeCrypto.EC_GROUP_new_by_curve_name("secp521r1"))); + private final NativeRef.EC_GROUP groupCtx; + private final ECParameterSpec ecParameterSpec; OpenSSLECGroupContext(NativeRef.EC_GROUP groupCtx) { this.groupCtx = groupCtx; + this.ecParameterSpec = createECParameterSpec(groupCtx); } static OpenSSLECGroupContext getCurveByName(String curveName) { @@ -55,13 +66,18 @@ static OpenSSLECGroupContext getCurveByName(String curveName) { curveName = ALIASES.get(curveName); } - final long ctx = NativeCrypto.EC_GROUP_new_by_curve_name(curveName); - if (ctx == 0) { - return null; + switch (curveName) { + case "secp224r1": + return SECP224R1; + case "prime256v1": + return PRIME256V1; + case "secp384r1": + return SECP384R1; + case "secp521r1": + return SECP521R1; + default: + return null; } - NativeRef.EC_GROUP groupRef = new NativeRef.EC_GROUP(ctx); - - return new OpenSSLECGroupContext(groupRef); } @Override @@ -115,7 +131,7 @@ static OpenSSLECGroupContext getInstance(ECParameterSpec params) "b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21") && y.toString(16).equals( "bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34")) { - curveName = "secp224r1"; + return SECP224R1; } break; case 256: @@ -127,7 +143,7 @@ static OpenSSLECGroupContext getInstance(ECParameterSpec params) "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296") && y.toString(16).equals( "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5")) { - curveName = "prime256v1"; + return PRIME256V1; } break; case 384: @@ -139,7 +155,7 @@ static OpenSSLECGroupContext getInstance(ECParameterSpec params) + "1e082542a385502f25dbf55296c3a545e3872760ab7") && y.toString(16).equals("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3" + "113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f")) { - curveName = "secp384r1"; + return SECP384R1; } break; case 521: @@ -155,13 +171,11 @@ static OpenSSLECGroupContext getInstance(ECParameterSpec params) && y.toString(16).equals( "11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97e" + "e72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650")) { - curveName = "secp521r1"; + return SECP521R1; } break; - } - - if (curveName != null) { - return OpenSSLECGroupContext.getCurveByName(curveName); + default: + break; } final BigInteger a = curve.getA(); @@ -191,7 +205,7 @@ String getCurveName() { return NativeCrypto.EC_GROUP_get_curve_name(groupCtx); } - ECParameterSpec getECParameterSpec() { + ECParameterSpec createECParameterSpec(NativeRef.EC_GROUP groupCtx) { final String curveName = NativeCrypto.EC_GROUP_get_curve_name(groupCtx); final byte[][] curveParams = NativeCrypto.EC_GROUP_get_curve(groupCtx); @@ -214,4 +228,9 @@ ECParameterSpec getECParameterSpec() { Platform.setCurveName(spec, curveName); return spec; } + + ECParameterSpec getECParameterSpec() { + return ecParameterSpec; + } + } diff --git a/common/src/test/java/org/conscrypt/OpenSSLECGroupContextTest.java b/common/src/test/java/org/conscrypt/OpenSSLECGroupContextTest.java new file mode 100644 index 000000000..6d939c1e0 --- /dev/null +++ b/common/src/test/java/org/conscrypt/OpenSSLECGroupContextTest.java @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2026 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. + */ + +package org.conscrypt; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertThrows; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.spec.ECFieldF2m; +import java.security.spec.ECFieldFp; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.EllipticCurve; + +/** Unit tests for {@link OpenSSLECGroupContext}. */ +@RunWith(JUnit4.class) +public class OpenSSLECGroupContextTest { + // NIST P-224 (secp224r1) parameters + private static final BigInteger P224_P = + new BigInteger("ffffffffffffffffffffffffffffffff000000000000000000000001", 16); + private static final BigInteger P224_A = P224_P.subtract(BigInteger.valueOf(3)); + private static final BigInteger P224_B = + new BigInteger("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", 16); + private static final BigInteger P224_X = + new BigInteger("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", 16); + private static final BigInteger P224_Y = + new BigInteger("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", 16); + private static final BigInteger P224_ORDER = + new BigInteger("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d", 16); + private static final int P224_COFACTOR = 1; + + // NIST P-256 (prime256v1 / secp256r1) parameters + private static final BigInteger P256_P = + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16); + private static final BigInteger P256_A = P256_P.subtract(BigInteger.valueOf(3)); + private static final BigInteger P256_B = + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16); + private static final BigInteger P256_X = + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16); + private static final BigInteger P256_Y = + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16); + private static final BigInteger P256_ORDER = + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16); + private static final int P256_COFACTOR = 1; + + // NIST P-384 (secp384r1) parameters + private static final BigInteger P384_P = + new BigInteger("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffff" + + "f0000000000000000ffffffff", + 16); + private static final BigInteger P384_A = P384_P.subtract(BigInteger.valueOf(3)); + private static final BigInteger P384_B = + new BigInteger("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398" + + "d8a2ed19d2a85c8edd3ec2aef", + 16); + private static final BigInteger P384_X = + new BigInteger("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25" + + "dbf55296c3a545e3872760ab7", + 16); + private static final BigInteger P384_Y = + new BigInteger("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1c" + + "e1d7e819d7a431d7c90ea0e5f", + 16); + private static final BigInteger P384_ORDER = + new BigInteger("ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db" + + "248b0a77aecec196accc52973", + 16); + private static final int P384_COFACTOR = 1; + + // NIST P-521 (secp521r1) parameters + private static final BigInteger P521_P = + new BigInteger("1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + 16); + private static final BigInteger P521_A = P521_P.subtract(BigInteger.valueOf(3)); + private static final BigInteger P521_B = + new BigInteger("51953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193" + + "951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00", + 16); + private static final BigInteger P521_X = + new BigInteger("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5" + + "e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", + 16); + private static final BigInteger P521_Y = + new BigInteger("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee" + + "72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", + 16); + private static final BigInteger P521_ORDER = + new BigInteger("01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa518" + + "68783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409", + 16); + private static final int P521_COFACTOR = 1; + + // NIST P-192 (secp192r1) parameters (192-bit arbitrary curve) + private static final BigInteger P192_P = + new BigInteger("fffffffffffffffffffffffffffffffeffffffffffffffff", 16); + private static final BigInteger P192_A = + new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16); + private static final BigInteger P192_B = + new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16); + private static final BigInteger P192_X = + new BigInteger("188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012", 16); + private static final BigInteger P192_Y = + new BigInteger("07192b95ffc8da78631011ed6b24cdd573f977a11e794811", 16); + private static final BigInteger P192_ORDER = + new BigInteger("ffffffffffffffffffffffff99def836146bc9b1b4d22831", 16); + private static final int P192_COFACTOR = 1; + + @BeforeClass + public static void setUp() { + TestUtils.assumeAllowsUnsignedCrypto(); + } + + // --- getCurveByName Tests --- + + @Test + public void getCurveByName_standardNistCurves_returnsStaticInstances() { + // Arrange & Act + OpenSSLECGroupContext p224 = OpenSSLECGroupContext.getCurveByName("secp224r1"); + OpenSSLECGroupContext p256 = OpenSSLECGroupContext.getCurveByName("prime256v1"); + OpenSSLECGroupContext p384 = OpenSSLECGroupContext.getCurveByName("secp384r1"); + OpenSSLECGroupContext p521 = OpenSSLECGroupContext.getCurveByName("secp521r1"); + + // Assert: returns static final instances + assertThat(p224).isSameInstanceAs(OpenSSLECGroupContext.SECP224R1); + assertThat(p224.getCurveName()).isEqualTo("secp224r1"); + assertThat(p224.getNativeRef()).isNotNull(); + + assertThat(p256).isSameInstanceAs(OpenSSLECGroupContext.PRIME256V1); + assertThat(p256.getCurveName()).isEqualTo("prime256v1"); + assertThat(p256.getNativeRef()).isNotNull(); + + assertThat(p384).isSameInstanceAs(OpenSSLECGroupContext.SECP384R1); + assertThat(p384.getCurveName()).isEqualTo("secp384r1"); + assertThat(p384.getNativeRef()).isNotNull(); + + assertThat(p521).isSameInstanceAs(OpenSSLECGroupContext.SECP521R1); + assertThat(p521.getCurveName()).isEqualTo("secp521r1"); + assertThat(p521.getNativeRef()).isNotNull(); + } + + @Test + public void getCurveByName_aliasSecp256r1_resolvesToPrime256v1() { + // Act + OpenSSLECGroupContext group = OpenSSLECGroupContext.getCurveByName("secp256r1"); + + // Assert + assertThat(group).isSameInstanceAs(OpenSSLECGroupContext.PRIME256V1); + assertThat(group.getCurveName()).isEqualTo("prime256v1"); + } + + @Test + public void getCurveByName_oidAliases_resolveToStandardCurves() { + // Act & Assert + // 1.3.132.0.33 -> secp224r1 + OpenSSLECGroupContext group224 = OpenSSLECGroupContext.getCurveByName("1.3.132.0.33"); + assertThat(group224).isSameInstanceAs(OpenSSLECGroupContext.SECP224R1); + assertThat(group224.getCurveName()).isEqualTo("secp224r1"); + + // 1.3.132.0.34 -> secp384r1 + OpenSSLECGroupContext group384 = OpenSSLECGroupContext.getCurveByName("1.3.132.0.34"); + assertThat(group384).isSameInstanceAs(OpenSSLECGroupContext.SECP384R1); + assertThat(group384.getCurveName()).isEqualTo("secp384r1"); + + // 1.3.132.0.35 -> secp521r1 + OpenSSLECGroupContext group521 = OpenSSLECGroupContext.getCurveByName("1.3.132.0.35"); + assertThat(group521).isSameInstanceAs(OpenSSLECGroupContext.SECP521R1); + assertThat(group521.getCurveName()).isEqualTo("secp521r1"); + + // 1.2.840.10045.3.1.7 -> prime256v1 + OpenSSLECGroupContext group256 = + OpenSSLECGroupContext.getCurveByName("1.2.840.10045.3.1.7"); + assertThat(group256).isSameInstanceAs(OpenSSLECGroupContext.PRIME256V1); + assertThat(group256.getCurveName()).isEqualTo("prime256v1"); + } + + @Test + public void getCurveByName_unknownCurveName_returnsNull() { + // Act & Assert + assertThat(OpenSSLECGroupContext.getCurveByName("non_existent_curve")).isNull(); + assertThat(OpenSSLECGroupContext.getCurveByName("")).isNull(); + assertThat(OpenSSLECGroupContext.getCurveByName("invalid_12345")).isNull(); + } + + @Test + public void getCurveByName_nullCurveName_throwsNullPointerException() { + // Act & Assert + assertThrows(NullPointerException.class, () -> OpenSSLECGroupContext.getCurveByName(null)); + } + + // --- Constructor & getNativeRef Tests --- + + @Test + public void constructor_storesAndReturnsNativeRef() { + // Arrange + OpenSSLECGroupContext original = OpenSSLECGroupContext.getCurveByName("prime256v1"); + NativeRef.EC_GROUP nativeRef = original.getNativeRef(); + + // Act + OpenSSLECGroupContext context = new OpenSSLECGroupContext(nativeRef); + + // Assert + assertThat(context.getNativeRef()).isSameInstanceAs(nativeRef); + assertThat(context.getCurveName()).isEqualTo("prime256v1"); + } + + // --- equals & hashCode Contract Tests --- + + @Test + public void equals_alwaysThrowsIllegalArgumentException() { + // Arrange + OpenSSLECGroupContext group1 = OpenSSLECGroupContext.getCurveByName("prime256v1"); + OpenSSLECGroupContext group2 = OpenSSLECGroupContext.getCurveByName("prime256v1"); + Object sameGroup = group1; + Object otherType = "different_type"; + + // Act & Assert + assertThrows(IllegalArgumentException.class, () -> group1.equals(sameGroup)); + assertThrows(IllegalArgumentException.class, () -> group1.equals(group2)); + assertThrows(IllegalArgumentException.class, () -> group1.equals(null)); + assertThrows(IllegalArgumentException.class, () -> group1.equals(otherType)); + } + + @Test + public void hashCode_returnsConsistentValue() { + // Arrange + OpenSSLECGroupContext group = OpenSSLECGroupContext.getCurveByName("prime256v1"); + + // Act + int hash1 = group.hashCode(); + int hash2 = group.hashCode(); + + // Assert + assertThat(hash1).isEqualTo(hash2); + } + + // --- getECParameterSpec Tests --- + + @Test + public void getECParameterSpec_secp224r1_returnsValidParameters() { + // Arrange + OpenSSLECGroupContext group = OpenSSLECGroupContext.SECP224R1; + + // Act + ECParameterSpec spec = group.getECParameterSpec(); + + // Assert + assertThat(spec).isNotNull(); + assertThat(spec.getCofactor()).isEqualTo(P224_COFACTOR); + assertThat(spec.getOrder()).isEqualTo(P224_ORDER); + + EllipticCurve curve = spec.getCurve(); + assertThat(curve.getField()).isInstanceOf(ECFieldFp.class); + ECFieldFp field = (ECFieldFp) curve.getField(); + assertThat(field.getP()).isEqualTo(P224_P); + assertThat(curve.getA()).isEqualTo(P224_A); + assertThat(curve.getB()).isEqualTo(P224_B); + + ECPoint generator = spec.getGenerator(); + assertThat(generator.getAffineX()).isEqualTo(P224_X); + assertThat(generator.getAffineY()).isEqualTo(P224_Y); + } + + @Test + public void getECParameterSpec_prime256v1_returnsValidParameters() { + // Arrange + OpenSSLECGroupContext group = OpenSSLECGroupContext.PRIME256V1; + + // Act + ECParameterSpec spec = group.getECParameterSpec(); + + // Assert + assertThat(spec).isNotNull(); + assertThat(spec.getCofactor()).isEqualTo(P256_COFACTOR); + assertThat(spec.getOrder()).isEqualTo(P256_ORDER); + + EllipticCurve curve = spec.getCurve(); + assertThat(curve.getField()).isInstanceOf(ECFieldFp.class); + ECFieldFp field = (ECFieldFp) curve.getField(); + assertThat(field.getP()).isEqualTo(P256_P); + assertThat(curve.getA()).isEqualTo(P256_A); + assertThat(curve.getB()).isEqualTo(P256_B); + + ECPoint generator = spec.getGenerator(); + assertThat(generator.getAffineX()).isEqualTo(P256_X); + assertThat(generator.getAffineY()).isEqualTo(P256_Y); + } + + @Test + public void getECParameterSpec_secp384r1_returnsValidParameters() { + // Arrange + OpenSSLECGroupContext group = OpenSSLECGroupContext.SECP384R1; + + // Act + ECParameterSpec spec = group.getECParameterSpec(); + + // Assert + assertThat(spec).isNotNull(); + assertThat(spec.getCofactor()).isEqualTo(P384_COFACTOR); + assertThat(spec.getOrder()).isEqualTo(P384_ORDER); + + EllipticCurve curve = spec.getCurve(); + assertThat(curve.getField()).isInstanceOf(ECFieldFp.class); + ECFieldFp field = (ECFieldFp) curve.getField(); + assertThat(field.getP()).isEqualTo(P384_P); + assertThat(curve.getA()).isEqualTo(P384_A); + assertThat(curve.getB()).isEqualTo(P384_B); + + ECPoint generator = spec.getGenerator(); + assertThat(generator.getAffineX()).isEqualTo(P384_X); + assertThat(generator.getAffineY()).isEqualTo(P384_Y); + } + + @Test + public void getECParameterSpec_secp521r1_returnsValidParameters() { + // Arrange + OpenSSLECGroupContext group = OpenSSLECGroupContext.SECP521R1; + + // Act + ECParameterSpec spec = group.getECParameterSpec(); + + // Assert + assertThat(spec).isNotNull(); + assertThat(spec.getCofactor()).isEqualTo(P521_COFACTOR); + assertThat(spec.getOrder()).isEqualTo(P521_ORDER); + + EllipticCurve curve = spec.getCurve(); + assertThat(curve.getField()).isInstanceOf(ECFieldFp.class); + ECFieldFp field = (ECFieldFp) curve.getField(); + assertThat(field.getP()).isEqualTo(P521_P); + assertThat(curve.getA()).isEqualTo(P521_A); + assertThat(curve.getB()).isEqualTo(P521_B); + + ECPoint generator = spec.getGenerator(); + assertThat(generator.getAffineX()).isEqualTo(P521_X); + assertThat(generator.getAffineY()).isEqualTo(P521_Y); + } + + // --- getInstance(ECParameterSpec) Named Curve Recognition Tests --- + + @Test + public void getInstance_fromECParameterSpec_roundTripMatchesOriginalCurve() + throws InvalidAlgorithmParameterException { + String[] curveNames = new String[] {"secp224r1", "prime256v1", "secp384r1", "secp521r1"}; + + for (String name : curveNames) { + // Arrange + OpenSSLECGroupContext group = OpenSSLECGroupContext.getCurveByName(name); + ECParameterSpec spec = group.getECParameterSpec(); + + // Act + OpenSSLECGroupContext reconstructed = OpenSSLECGroupContext.getInstance(spec); + + // Assert + assertThat(reconstructed).isNotNull(); + assertThat(reconstructed.getCurveName()).isEqualTo(name); + } + } + + @Test + public void getInstance_rawP224Parameters_recognizesSecp224r1() + throws InvalidAlgorithmParameterException { + // Arrange + EllipticCurve curve = new EllipticCurve(new ECFieldFp(P224_P), P224_A, P224_B); + ECPoint generator = new ECPoint(P224_X, P224_Y); + ECParameterSpec spec = new ECParameterSpec(curve, generator, P224_ORDER, P224_COFACTOR); + + // Act + OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(spec); + + // Assert + assertThat(group).isSameInstanceAs(OpenSSLECGroupContext.SECP224R1); + assertThat(group.getCurveName()).isEqualTo("secp224r1"); + } + + @Test + public void getInstance_rawP256Parameters_recognizesPrime256v1() + throws InvalidAlgorithmParameterException { + // Arrange + EllipticCurve curve = new EllipticCurve(new ECFieldFp(P256_P), P256_A, P256_B); + ECPoint generator = new ECPoint(P256_X, P256_Y); + ECParameterSpec spec = new ECParameterSpec(curve, generator, P256_ORDER, P256_COFACTOR); + + // Act + OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(spec); + + // Assert + assertThat(group).isSameInstanceAs(OpenSSLECGroupContext.PRIME256V1); + assertThat(group.getCurveName()).isEqualTo("prime256v1"); + } + + @Test + public void getInstance_rawP384Parameters_recognizesSecp384r1() + throws InvalidAlgorithmParameterException { + // Arrange + EllipticCurve curve = new EllipticCurve(new ECFieldFp(P384_P), P384_A, P384_B); + ECPoint generator = new ECPoint(P384_X, P384_Y); + ECParameterSpec spec = new ECParameterSpec(curve, generator, P384_ORDER, P384_COFACTOR); + + // Act + OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(spec); + + // Assert + assertThat(group).isSameInstanceAs(OpenSSLECGroupContext.SECP384R1); + assertThat(group.getCurveName()).isEqualTo("secp384r1"); + } + + @Test + public void getInstance_rawP521Parameters_recognizesSecp521r1() + throws InvalidAlgorithmParameterException { + // Arrange + EllipticCurve curve = new EllipticCurve(new ECFieldFp(P521_P), P521_A, P521_B); + ECPoint generator = new ECPoint(P521_X, P521_Y); + ECParameterSpec spec = new ECParameterSpec(curve, generator, P521_ORDER, P521_COFACTOR); + + // Act + OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(spec); + + // Assert + assertThat(group).isSameInstanceAs(OpenSSLECGroupContext.SECP521R1); + assertThat(group.getCurveName()).isEqualTo("secp521r1"); + } + + // --- getInstance Field Type Tests --- + + @Test + public void getInstance_binaryFieldF2m_throwsInvalidParameterException() { + // Arrange + EllipticCurve curve = + new EllipticCurve(new ECFieldF2m(163), BigInteger.ONE, BigInteger.ONE); + ECPoint generator = new ECPoint(BigInteger.ONE, BigInteger.ONE); + ECParameterSpec spec = new ECParameterSpec(curve, generator, BigInteger.ONE, 1); + + // Act & Assert + InvalidParameterException e = assertThrows(InvalidParameterException.class, + () -> OpenSSLECGroupContext.getInstance(spec)); + assertThat(e).hasMessageThat().contains("unhandled field class"); + assertThat(e).hasMessageThat().contains("ECFieldF2m"); + } + + // --- getInstance Mismatched Bit Length Parameters (Fall through to Arbitrary) --- + + @Test + public void getInstance_p256BitLengthWithMismatchedB_fallsThroughToArbitraryAndFails() { + // Arrange: 256-bit prime, but 'b' altered so it does not match prime256v1 + EllipticCurve curve = + new EllipticCurve(new ECFieldFp(P256_P), P256_A, P256_B.add(BigInteger.ONE)); + ECPoint generator = new ECPoint(P256_X, P256_Y); + ECParameterSpec spec = new ECParameterSpec(curve, generator, P256_ORDER, P256_COFACTOR); + + // Act & Assert: generator is not on modified curve -> arbitrary curve creation fails + assertThrows(InvalidAlgorithmParameterException.class, + () -> OpenSSLECGroupContext.getInstance(spec)); + } + + // --- getInstance Arbitrary Curve Creation Tests --- + + @Test + public void getInstance_validArbitraryCurveSecp192r1_succeeds() + throws InvalidAlgorithmParameterException { + // Arrange: 192-bit NIST curve is valid over ECFieldFp, but not in the 224/256/384/521 + // switch + EllipticCurve curve = new EllipticCurve(new ECFieldFp(P192_P), P192_A, P192_B); + ECPoint generator = new ECPoint(P192_X, P192_Y); + ECParameterSpec spec = new ECParameterSpec(curve, generator, P192_ORDER, P192_COFACTOR); + + // Act + OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(spec); + + // Assert + assertThat(group).isNotNull(); + // Arbitrary curves do not have a registered curve name in BoringSSL EC_GROUP + assertThat(group.getCurveName()).isNull(); + + // Parameter spec round-trip + ECParameterSpec reconstructedSpec = group.getECParameterSpec(); + assertThat(reconstructedSpec).isNotNull(); + assertThat(reconstructedSpec.getCofactor()).isEqualTo(P192_COFACTOR); + assertThat(reconstructedSpec.getOrder()).isEqualTo(P192_ORDER); + assertThat(((ECFieldFp) reconstructedSpec.getCurve().getField()).getP()).isEqualTo(P192_P); + assertThat(reconstructedSpec.getCurve().getA()).isEqualTo(P192_A); + assertThat(reconstructedSpec.getCurve().getB()).isEqualTo(P192_B); + assertThat(reconstructedSpec.getGenerator().getAffineX()).isEqualTo(P192_X); + assertThat(reconstructedSpec.getGenerator().getAffineY()).isEqualTo(P192_Y); + + // Can round-trip back into getInstance + OpenSSLECGroupContext group2 = OpenSSLECGroupContext.getInstance(reconstructedSpec); + assertThat(group2).isNotNull(); + assertThat(group2.getCurveName()).isNull(); + } + + @Test + public void + getInstance_arbitraryCurveWithInvalidGenerator_throwsInvalidAlgorithmParameterException() { + // Arrange: generator point not on curve + EllipticCurve curve = new EllipticCurve(new ECFieldFp(P192_P), P192_A, P192_B); + ECPoint invalidGenerator = new ECPoint(P192_X, P192_Y.add(BigInteger.ONE)); + ECParameterSpec spec = + new ECParameterSpec(curve, invalidGenerator, P192_ORDER, P192_COFACTOR); + + // Act & Assert + assertThrows(InvalidAlgorithmParameterException.class, + () -> OpenSSLECGroupContext.getInstance(spec)); + } + + @Test + public void + getInstance_arbitraryCurveWithInvalidPrime_throwsInvalidAlgorithmParameterException() { + // Arrange: even prime is invalid for EC_GROUP_new_curve_GFp + BigInteger evenP = new BigInteger("1000", 16); + EllipticCurve curve = + new EllipticCurve(new ECFieldFp(evenP), BigInteger.ONE, BigInteger.ONE); + ECPoint generator = new ECPoint(BigInteger.ONE, BigInteger.ONE); + ECParameterSpec spec = new ECParameterSpec(curve, generator, BigInteger.ONE, 1); + + // Act & Assert + assertThrows(InvalidAlgorithmParameterException.class, + () -> OpenSSLECGroupContext.getInstance(spec)); + } + + @Test + public void getInstance_nullParameterSpec_throwsNullPointerException() { + // Act & Assert + assertThrows(NullPointerException.class, () -> OpenSSLECGroupContext.getInstance(null)); + } +} diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptAndroidSuite.java b/openjdk/src/test/java/org/conscrypt/ConscryptAndroidSuite.java index 0c93741b7..ebe411a22 100644 --- a/openjdk/src/test/java/org/conscrypt/ConscryptAndroidSuite.java +++ b/openjdk/src/test/java/org/conscrypt/ConscryptAndroidSuite.java @@ -105,6 +105,7 @@ NativeSslTest.class, NativeRefTest.class, NativeSslSessionTest.class, + OpenSSLECGroupContextTest.class, OpenSSLKeyTest.class, OpenSSLX509CertificateTest.class, OpenSSLX509CRLTest.class, diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptAndroidWithoutTlsSuite.java b/openjdk/src/test/java/org/conscrypt/ConscryptAndroidWithoutTlsSuite.java index ffb52b96d..950600859 100644 --- a/openjdk/src/test/java/org/conscrypt/ConscryptAndroidWithoutTlsSuite.java +++ b/openjdk/src/test/java/org/conscrypt/ConscryptAndroidWithoutTlsSuite.java @@ -97,6 +97,7 @@ NativeSslTest.class, NativeRefTest.class, NativeSslSessionTest.class, + OpenSSLECGroupContextTest.class, OpenSSLKeyTest.class, OpenSSLX509CertificateTest.class, OpenSSLX509CRLTest.class, diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptOpenJdkSuite.java b/openjdk/src/test/java/org/conscrypt/ConscryptOpenJdkSuite.java index db7a6eb2d..b670a0a00 100644 --- a/openjdk/src/test/java/org/conscrypt/ConscryptOpenJdkSuite.java +++ b/openjdk/src/test/java/org/conscrypt/ConscryptOpenJdkSuite.java @@ -112,6 +112,7 @@ NativeSslTest.class, NativeRefTest.class, NativeSslSessionTest.class, + OpenSSLECGroupContextTest.class, OpenSSLKeyTest.class, OpenSSLX509CertificateTest.class, OpenSSLX509CRLTest.class, diff --git a/scripts/export_to_ag.py b/scripts/export_to_ag.py index ff0eae95f..5328d2c2e 100755 --- a/scripts/export_to_ag.py +++ b/scripts/export_to_ag.py @@ -131,7 +131,13 @@ def setup_kokoro_git_env() -> None: ) if res_name.returncode != 0 or not res_name.stdout.strip(): subprocess.run( - [real_git, "config", "--global", "user.name", "Conscrypt Team"], + [ + real_git, + "config", + "--global", + "user.name", + "Conscrypt Kokoro Export Bot", + ], capture_output=True, check=False, ) @@ -143,7 +149,13 @@ def setup_kokoro_git_env() -> None: ) if res_email.returncode != 0 or not res_email.stdout.strip(): subprocess.run( - [real_git, "config", "--global", "user.email", "no-reply@google.com"], + [ + real_git, + "config", + "--global", + "user.email", + "kokoro-instances@conscrypt-kokoro.iam.gserviceaccount.com", + ], capture_output=True, check=False, ) @@ -379,7 +391,7 @@ def resolve_android_and_build_top( return p, build_top, None # Check explicit environment variables - for env_var in ["CONSCYPT_ANDROID_DIR", "ANDROID_BUILD_TOP"]: + for env_var in ["CONSCRYPT_ANDROID_DIR", "ANDROID_BUILD_TOP"]: val = os.environ.get(env_var) if val: p = ( @@ -1165,16 +1177,41 @@ def step_format_and_commit_android( print("Amending Copybara commit with repackaged and formatted changes...") # Ensure git user identity is configured in repository (needed in Kokoro VMs) - subprocess.run( - ["git", "config", "user.name", "Conscrypt Team"], + res_name = subprocess.run( + ["git", "config", "--get", "user.name"], cwd=android_dir, + capture_output=True, + text=True, check=False, ) - subprocess.run( - ["git", "config", "user.email", "no-reply@google.com"], + if res_name.returncode != 0 or not res_name.stdout.strip(): + subprocess.run( + ["git", "config", "user.name", "Conscrypt Kokoro Export Bot"], + cwd=android_dir, + check=False, + ) + res_email = subprocess.run( + ["git", "config", "--get", "user.email"], cwd=android_dir, + capture_output=True, + text=True, check=False, ) + if ( + res_email.returncode != 0 + or not res_email.stdout.strip() + or res_email.stdout.strip() == "no-reply@google.com" + ): + subprocess.run( + [ + "git", + "config", + "user.email", + "kokoro-instances@conscrypt-kokoro.iam.gserviceaccount.com", + ], + cwd=android_dir, + check=False, + ) msg = subprocess.check_output( ["git", "log", "-1", "--format=%B"], cwd=android_dir, text=True @@ -1189,10 +1226,19 @@ def step_format_and_commit_android( ["git", "rev-parse", "HEAD"], cwd=android_dir, text=True ).strip() change_id = "I" + hashlib.sha1(seed.encode("utf-8")).hexdigest() - msg = f"{msg}\n\nChange-Id: {change_id}\n" + sep = "\n" if "PiperOrigin-RevId:" in msg.splitlines()[-1] else "\n\n" + msg = f"{msg}{sep}Change-Id: {change_id}\n" run_cmd( - ["git", "commit", "--amend", "--allow-empty", "-m", msg], + [ + "git", + "commit", + "--amend", + "--reset-author", + "--allow-empty", + "-m", + msg, + ], cwd=android_dir, check=False, )