Fix NativeCrypto.X509_verify() exceptions. am: 8b63789f4a am: 631f434be5
Original change: https://android-review.googlesource.com/c/platform/external/conscrypt/+/3296234
Change-Id: Id086e951d3f7b591423e529272c00670d6832c03
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index af41a1a..bc1be6a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -282,6 +282,7 @@
visibility: [
"//art/build",
"//external/robolectric-shadows",
+ "//frameworks/layoutlib",
],
static_libs: [
"conscrypt",
@@ -328,8 +329,8 @@
java_sdk_library {
name: "conscrypt.module.platform.api",
visibility: [
+ "//build/soong/java/core-libraries",
"//external/wycheproof",
- "//libcore:__subpackages__",
// Visibility for prebuilt conscrypt-module-sdk from the prebuilt of
// this module.
// TODO(b/155921753): Restrict this when prebuilts are in their proper
@@ -347,7 +348,6 @@
droiddoc_options: [
"--hide-annotation libcore.api.Hide",
"--show-single-annotation libcore.api.CorePlatformApi\\(status=libcore.api.CorePlatformApi.Status.STABLE\\)",
- "--skip-annotation-instance-methods=false",
],
hostdex: true,
@@ -364,10 +364,10 @@
java_sdk_library {
name: "conscrypt.module.public.api",
visibility: [
- "//packages/modules/common/sdk",
+ "//build/soong/java/core-libraries",
"//frameworks/base",
"//frameworks/base/api",
- "//libcore",
+ "//packages/modules/common/sdk",
// TODO(b/165823103): Remove visiblity for IPsec once CorePlatformApi is available
"//packages/modules/IPsec",
// Visibility for prebuilt art-module-host-exports from the prebuilt of
@@ -387,12 +387,21 @@
// version of the API.
dist_stem: "conscrypt",
+ public: {
+ enabled: true,
+ },
+ system: {
+ enabled: true,
+ },
+ module_lib: {
+ enabled: true,
+ },
+
api_dir: "api/public",
api_only: true,
- droiddoc_options: [
- // Emit nullability annotations from the source to the stub files.
- "--include-annotations",
- ],
+
+ // Emit nullability annotations from the source to the stub files.
+ annotations_enabled: true,
java_version: "1.9",
@@ -451,7 +460,6 @@
droiddoc_options: [
"--hide-annotation libcore.api.Hide",
"--show-single-annotation libcore.api.IntraCoreApi",
- "--skip-annotation-instance-methods=false",
],
sdk_version: "none",
@@ -525,7 +533,12 @@
":conscrypt-unbundled_generated_constants",
],
javacflags: ["-XDignore.symbol.file"],
- java_version: "1.7",
+ java_version: "1.8",
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
}
// Static unbundled Conscrypt crypto JNI library
@@ -636,7 +649,7 @@
enabled: false,
},
},
- java_version: "1.7",
+ java_version: "1.8",
}
// Device SDK exposed by the Conscrypt module.
diff --git a/OWNERS b/OWNERS
index 6a33b41..73617ea 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,7 +1,2 @@
# Bug component: 684135
-dauletz@google.com
-kroot@google.com
-mingaleev@google.com
-narayan@google.com
-ngeoffray@google.com
-prb@google.com
+include platform/libcore:/OWNERS
diff --git a/android-stub/build.gradle b/android-stub/build.gradle
index f1a911f..874f599 100644
--- a/android-stub/build.gradle
+++ b/android-stub/build.gradle
@@ -1,9 +1,5 @@
description = 'Conscrypt: Android-Stub'
-// Needs to be binary-compatible with Android minSdkVersion.
-sourceCompatibility = androidMinJavaVersion
-targetCompatibility = androidMinJavaVersion
-
dependencies {
compileOnly project(':conscrypt-libcore-stub')
}
diff --git a/apex/Android.bp b/apex/Android.bp
index 2a7d225..f20ea90 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -24,7 +24,6 @@
apex_defaults {
name: "com.android.conscrypt-defaults",
- updatable: true,
androidManifest: ":com.android.conscrypt-androidManifest",
compile_multilib: "both",
bootclasspath_fragments: ["com.android.conscrypt-bootclasspath-fragment"],
@@ -37,15 +36,17 @@
},
key: "apex.conscrypt.key",
certificate: ":com.android.conscrypt.certificate",
+ // Indicates that pre-installed version of this apex can be compressed.
+ // Whether it actually will be compressed is controlled on per-device basis.
+ compressible: true,
- // IMPORTANT: For the APEX to be installed on Android 10,
- // min_sdk_version should be 29. This enables the build system to make
+ // IMPORTANT: q-launched-apex-module enables the build system to make
// sure the package compatible to Android 10 in two ways:
// - build the APEX package compatible to Android 10
// so that the package can be installed.
// - build artifacts (lib/javalib/bin) against Android 10 SDK
// so that the artifacts can run.
- min_sdk_version: "29",
+ defaults: ["q-launched-apex-module"],
}
filegroup {
@@ -111,5 +112,19 @@
// from the api stub libraries.
hidden_api: {
max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+
+ // This module does not contain any split packages.
+ split_packages: [],
+
+ // The following packages and all their subpackages currently only
+ // contain classes from this bootclasspath_fragment. Listing a package
+ // here won't prevent other bootclasspath modules from adding classes in
+ // any of those packages but it will prevent them from adding those
+ // classes into an API surface, e.g. public, system, etc.. Doing so will
+ // result in a build failure due to inconsistent flags.
+ package_prefixes: [
+ "android.net.ssl",
+ "com.android.org.conscrypt",
+ ],
},
}
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json
index e59fbcf..9584d95 100644
--- a/apex/apex_manifest.json
+++ b/apex/apex_manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.conscrypt",
- "version": 319999900
+ "version": 339990000
}
diff --git a/api/public/module-lib-current.txt b/api/public/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/api/public/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/api/public/module-lib-removed.txt b/api/public/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/api/public/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/api/public/system-current.txt b/api/public/system-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/api/public/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/api/public/system-removed.txt b/api/public/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/api/public/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/build.gradle b/build.gradle
index 162c491..08b9ebd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -165,8 +165,8 @@
}
if (!androidProject) {
- sourceCompatibility = JavaVersion.VERSION_1_7
- targetCompatibility = JavaVersion.VERSION_1_7
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
[tasks.named("compileJava"), tasks.named("compileTestJava")].forEach { t ->
t.configure {
diff --git a/common/src/jni/main/cpp/conscrypt/jniutil.cc b/common/src/jni/main/cpp/conscrypt/jniutil.cc
index c30adf1..f5c6b92 100644
--- a/common/src/jni/main/cpp/conscrypt/jniutil.cc
+++ b/common/src/jni/main/cpp/conscrypt/jniutil.cc
@@ -38,9 +38,12 @@
jclass outputStreamClass;
jclass stringClass;
jclass byteBufferClass;
-jclass bufferClass;
+static jclass bufferClass;
+static jclass fileDescriptorClass;
+static jclass sslHandshakeCallbacksClass;
jfieldID nativeRef_address;
+static jfieldID fileDescriptor_fd;
jmethodID calendar_setMethod;
jmethodID inputStream_readMethod;
@@ -50,6 +53,19 @@
jmethodID outputStream_flushMethod;
jmethodID buffer_positionMethod;
jmethodID buffer_limitMethod;
+jmethodID buffer_isDirectMethod;
+jmethodID cryptoUpcallsClass_rawSignMethod;
+jmethodID cryptoUpcallsClass_rsaSignMethod;
+jmethodID cryptoUpcallsClass_rsaDecryptMethod;
+jmethodID sslHandshakeCallbacks_verifyCertificateChain;
+jmethodID sslHandshakeCallbacks_onSSLStateChange;
+jmethodID sslHandshakeCallbacks_clientCertificateRequested;
+jmethodID sslHandshakeCallbacks_serverCertificateRequested;
+jmethodID sslHandshakeCallbacks_clientPSKKeyRequested;
+jmethodID sslHandshakeCallbacks_serverPSKKeyRequested;
+jmethodID sslHandshakeCallbacks_onNewSessionEstablished;
+jmethodID sslHandshakeCallbacks_selectApplicationProtocol;
+jmethodID sslHandshakeCallbacks_serverSessionRequested;
void init(JavaVM* vm, JNIEnv* env) {
gJavaVM = vm;
@@ -64,6 +80,7 @@
stringClass = findClass(env, "java/lang/String");
byteBufferClass = findClass(env, "java/nio/ByteBuffer");
bufferClass = findClass(env, "java/nio/Buffer");
+ fileDescriptorClass = findClass(env, "java/io/FileDescriptor");
cryptoUpcallsClass = getGlobalRefToClass(
env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/CryptoUpcalls");
@@ -71,8 +88,15 @@
env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef");
openSslInputStreamClass = getGlobalRefToClass(
env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLBIOInputStream");
+ sslHandshakeCallbacksClass = getGlobalRefToClass(
+ env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeCrypto$SSLHandshakeCallbacks");
nativeRef_address = getFieldRef(env, nativeRefClass, "address", "J");
+#if defined(ANDROID) && !defined(CONSCRYPT_OPENJDK)
+ fileDescriptor_fd = getFieldRef(env, fileDescriptorClass, "descriptor", "I");
+#else /* !ANDROID || CONSCRYPT_OPENJDK */
+ fileDescriptor_fd = getFieldRef(env, fileDescriptorClass, "fd", "I");
+#endif
calendar_setMethod = getMethodRef(env, calendarClass, "set", "(IIIIII)V");
inputStream_readMethod = getMethodRef(env, inputStreamClass, "read", "([B)I");
@@ -84,6 +108,41 @@
outputStream_flushMethod = getMethodRef(env, outputStreamClass, "flush", "()V");
buffer_positionMethod = getMethodRef(env, bufferClass, "position", "()I");
buffer_limitMethod = getMethodRef(env, bufferClass, "limit", "()I");
+ buffer_isDirectMethod = getMethodRef(env, bufferClass, "isDirect", "()Z");
+ sslHandshakeCallbacks_verifyCertificateChain = getMethodRef(
+ env, sslHandshakeCallbacksClass, "verifyCertificateChain", "([[BLjava/lang/String;)V");
+ sslHandshakeCallbacks_onSSLStateChange =
+ getMethodRef(env, sslHandshakeCallbacksClass, "onSSLStateChange", "(II)V");
+ sslHandshakeCallbacks_clientCertificateRequested = getMethodRef(
+ env, sslHandshakeCallbacksClass, "clientCertificateRequested", "([B[I[[B)V");
+ sslHandshakeCallbacks_serverCertificateRequested =
+ getMethodRef(env, sslHandshakeCallbacksClass, "serverCertificateRequested", "()V");
+ sslHandshakeCallbacks_clientPSKKeyRequested = getMethodRef(
+ env, sslHandshakeCallbacksClass, "clientPSKKeyRequested", "(Ljava/lang/String;[B[B)I");
+ sslHandshakeCallbacks_serverPSKKeyRequested =
+ getMethodRef(env, sslHandshakeCallbacksClass, "serverPSKKeyRequested",
+ "(Ljava/lang/String;Ljava/lang/String;[B)I");
+ sslHandshakeCallbacks_onNewSessionEstablished =
+ getMethodRef(env, sslHandshakeCallbacksClass, "onNewSessionEstablished", "(J)V");
+ sslHandshakeCallbacks_serverSessionRequested =
+ getMethodRef(env, sslHandshakeCallbacksClass, "serverSessionRequested", "([B)J");
+ sslHandshakeCallbacks_selectApplicationProtocol =
+ getMethodRef(env, sslHandshakeCallbacksClass, "selectApplicationProtocol", "([B)I");
+ cryptoUpcallsClass_rawSignMethod = env->GetStaticMethodID(
+ cryptoUpcallsClass, "ecSignDigestWithPrivateKey", "(Ljava/security/PrivateKey;[B)[B");
+ if (cryptoUpcallsClass_rawSignMethod == nullptr) {
+ env->FatalError("Could not find ecSignDigestWithPrivateKey");
+ }
+ cryptoUpcallsClass_rsaSignMethod = env->GetStaticMethodID(
+ cryptoUpcallsClass, "rsaSignDigestWithPrivateKey", "(Ljava/security/PrivateKey;I[B)[B");
+ if (cryptoUpcallsClass_rsaSignMethod == nullptr) {
+ env->FatalError("Could not find rsaSignDigestWithPrivateKey");
+ }
+ cryptoUpcallsClass_rsaDecryptMethod = env->GetStaticMethodID(
+ cryptoUpcallsClass, "rsaDecryptWithPrivateKey", "(Ljava/security/PrivateKey;I[B)[B");
+ if (cryptoUpcallsClass_rsaDecryptMethod == nullptr) {
+ env->FatalError("Could not find rsaDecryptWithPrivateKey");
+ }
}
void jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods,
@@ -106,19 +165,25 @@
}
int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
- ScopedLocalRef<jclass> localClass(env, env->FindClass("java/io/FileDescriptor"));
-#if defined(ANDROID) && !defined(CONSCRYPT_OPENJDK)
- static jfieldID fid = env->GetFieldID(localClass.get(), "descriptor", "I");
-#else /* !ANDROID || CONSCRYPT_OPENJDK */
- static jfieldID fid = env->GetFieldID(localClass.get(), "fd", "I");
-#endif
if (fileDescriptor != nullptr) {
- return env->GetIntField(fileDescriptor, fid);
+ return env->GetIntField(fileDescriptor, fileDescriptor_fd);
} else {
return -1;
}
}
+extern bool isDirectByteBufferInstance(JNIEnv* env, jobject buffer) {
+ // Some versions of ART do not check the buffer validity when handling GetDirectBufferAddress()
+ // and GetDirectBufferCapacity().
+ if (buffer == nullptr) {
+ return false;
+ }
+ if (!env->IsInstanceOf(buffer, conscrypt::jniutil::byteBufferClass)) {
+ return false;
+ }
+ return env->CallBooleanMethod(buffer, conscrypt::jniutil::buffer_isDirectMethod) == JNI_TRUE;
+}
+
bool isGetByteArrayElementsLikelyToReturnACopy(size_t size) {
#if defined(ANDROID) && !defined(CONSCRYPT_OPENJDK)
// ART's GetByteArrayElements creates copies only for arrays smaller than 12 kB.
@@ -153,9 +218,11 @@
return conscrypt::jniutil::throwException(env, "java/lang/RuntimeException", msg);
}
+#ifdef CONSCRYPT_CHECK_ERROR_QUEUE
int throwAssertionError(JNIEnv* env, const char* msg) {
return conscrypt::jniutil::throwException(env, "java/lang/AssertionError", msg);
}
+#endif
int throwNullPointerException(JNIEnv* env, const char* msg) {
return conscrypt::jniutil::throwException(env, "java/lang/NullPointerException", msg);
diff --git a/common/src/jni/main/cpp/conscrypt/native_crypto.cc b/common/src/jni/main/cpp/conscrypt/native_crypto.cc
index 5b0acf0..94a61fd 100644
--- a/common/src/jni/main/cpp/conscrypt/native_crypto.cc
+++ b/common/src/jni/main/cpp/conscrypt/native_crypto.cc
@@ -52,6 +52,7 @@
#include <openssl/x509v3.h>
#include <limits>
+#include <type_traits>
#include <vector>
using conscrypt::AppData;
@@ -283,10 +284,19 @@
/**
* 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.
+ * from the OpenSSL ASN.1 API. Note i2d_func may take a const parameter, so we
+ * use a separate type parameter.
+ *
+ * TODO(https://crbug.com/boringssl/407): When all BoringSSL i2d functions are
+ * const, switch back to a single template parameter.
*/
-template <typename T>
-jbyteArray ASN1ToByteArray(JNIEnv* env, T* obj, int (*i2d_func)(T*, unsigned char**)) {
+template <typename T, typename U>
+jbyteArray ASN1ToByteArray(JNIEnv* env, T* obj, int (*i2d_func)(U*, unsigned char**)) {
+ // T and U should be the same type, but may differ in const.
+ static_assert(std::is_same<typename std::remove_const<T>::type,
+ typename std::remove_const<U>::type>::value,
+ "obj and i2d_func have incompatible types");
+
if (obj == nullptr) {
conscrypt::jniutil::throwNullPointerException(env, "ASN1 input == null");
JNI_TRACE("ASN1ToByteArray(%p) => null input", obj);
@@ -559,16 +569,9 @@
memcpy(messageBytes.get(), message, message_len);
}
- jmethodID rawSignMethod = env->GetStaticMethodID(conscrypt::jniutil::cryptoUpcallsClass,
- "ecSignDigestWithPrivateKey",
- "(Ljava/security/PrivateKey;[B)[B");
- if (rawSignMethod == nullptr) {
- CONSCRYPT_LOG_ERROR("Could not find ecSignDigestWithPrivateKey");
- return nullptr;
- }
-
return reinterpret_cast<jbyteArray>(env->CallStaticObjectMethod(
- conscrypt::jniutil::cryptoUpcallsClass, rawSignMethod, privateKey, messageArray.get()));
+ conscrypt::jniutil::cryptoUpcallsClass,
+ conscrypt::jniutil::cryptoUpcallsClass_rawSignMethod, privateKey, messageArray.get()));
}
static jbyteArray rsaSignDigestWithPrivateKey(JNIEnv* env, jobject privateKey, jint padding,
@@ -594,16 +597,9 @@
memcpy(messageBytes.get(), message, message_len);
}
- jmethodID rsaSignMethod = env->GetStaticMethodID(conscrypt::jniutil::cryptoUpcallsClass,
- "rsaSignDigestWithPrivateKey",
- "(Ljava/security/PrivateKey;I[B)[B");
- if (rsaSignMethod == nullptr) {
- CONSCRYPT_LOG_ERROR("Could not find rsaSignDigestWithPrivateKey");
- return nullptr;
- }
-
return reinterpret_cast<jbyteArray>(
- env->CallStaticObjectMethod(conscrypt::jniutil::cryptoUpcallsClass, rsaSignMethod,
+ env->CallStaticObjectMethod(conscrypt::jniutil::cryptoUpcallsClass,
+ conscrypt::jniutil::cryptoUpcallsClass_rsaSignMethod,
privateKey, padding, messageArray.get()));
}
@@ -634,16 +630,9 @@
memcpy(ciphertextBytes.get(), ciphertext, ciphertext_len);
}
- jmethodID rsaDecryptMethod =
- env->GetStaticMethodID(conscrypt::jniutil::cryptoUpcallsClass,
- "rsaDecryptWithPrivateKey", "(Ljava/security/PrivateKey;I[B)[B");
- if (rsaDecryptMethod == nullptr) {
- CONSCRYPT_LOG_ERROR("Could not find rsaDecryptWithPrivateKey");
- return nullptr;
- }
-
return reinterpret_cast<jbyteArray>(
- env->CallStaticObjectMethod(conscrypt::jniutil::cryptoUpcallsClass, rsaDecryptMethod,
+ env->CallStaticObjectMethod(conscrypt::jniutil::cryptoUpcallsClass,
+ conscrypt::jniutil::cryptoUpcallsClass_rsaDecryptMethod,
privateKey, padding, ciphertextArray.get()));
}
@@ -2204,7 +2193,9 @@
const EC_GROUP* group = fromContextObject<EC_GROUP>(env, groupRef);
JNI_TRACE("EC_KEY_marshal_curve_name(%p)", group);
if (group == nullptr) {
+ env->ExceptionClear();
conscrypt::jniutil::throwIOException(env, "Invalid group pointer");
+ JNI_TRACE("group=%p EC_KEY_marshal_curve_name => Invalid group pointer", group);
return nullptr;
}
@@ -2231,8 +2222,9 @@
ScopedByteArrayRO bytes(env, curveNameBytes);
if (bytes.get() == nullptr) {
- conscrypt::jniutil::throwIOException(env, "Error reading ASN.1 encoding");
- JNI_TRACE("bytes=%p EC_KEY_parse_curve_name => threw exception", curveNameBytes);
+ env->ExceptionClear();
+ conscrypt::jniutil::throwIOException(env, "Null EC curve name");
+ JNI_TRACE("bytes=%p EC_KEY_parse_curve_name => curveNameBytes == null ", curveNameBytes);
return 0;
}
@@ -3208,6 +3200,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("EVP_get_cipherbyname(%p)", algorithm);
+ if (algorithm == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "algorithm == null");
+ JNI_TRACE("EVP_get_cipherbyname(%p) => algorithm == null", algorithm);
+ return -1;
+ }
+
ScopedUtfChars scoped_alg(env, algorithm);
const char* alg = scoped_alg.c_str();
const EVP_CIPHER* cipher;
@@ -3245,7 +3243,7 @@
} else if (strcasecmp(alg, "aes-256-gcm") == 0) {
cipher = EVP_aes_256_gcm();
} else {
- JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%s) => error", alg);
+ JNI_TRACE("NativeCrypto_EVP_get_cipherbyname(%s) => error", alg);
return 0;
}
@@ -3684,20 +3682,22 @@
JNI_TRACE("evp_aead_ctx_op(%p, %p, %d, %p, %p, %p, %p)", evpAead, keyArray, tagLen,
outBuffer, nonceArray, inBuffer, aadArray);
- if (env->IsInstanceOf(inBuffer, conscrypt::jniutil::byteBufferClass) != JNI_TRUE ||
- env->IsInstanceOf(outBuffer, conscrypt::jniutil::byteBufferClass) != JNI_TRUE ) {
- conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException", "ByteBuffer Class Error");
+ if (!conscrypt::jniutil::isDirectByteBufferInstance(env, inBuffer)) {
+ conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException",
+ "inBuffer is not a direct ByteBuffer");
+ return 0;
+ }
+
+ if (!conscrypt::jniutil::isDirectByteBufferInstance(env, outBuffer)) {
+ conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException",
+ "outBuffer is not a direct ByteBuffer");
return 0;
}
uint8_t* inBuf;
jint in_limit;
jint in_position;
- jint inCapacity = env->GetDirectBufferCapacity(inBuffer);
- if (inCapacity == -1) {
- conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException", "Non Direct ByteBuffer Error");
- return 0;
- }
+
inBuf = (uint8_t*)(env->GetDirectBufferAddress(inBuffer));
// limit is the index of the first element that should not be read or written
in_limit = env->CallIntMethod(inBuffer,conscrypt::jniutil::buffer_limitMethod);
@@ -3707,11 +3707,6 @@
uint8_t* outBuf;
jint out_limit;
jint out_position;
- jint outCapacity = env->GetDirectBufferCapacity(outBuffer);
- if (outCapacity == -1) {
- conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException", "Non Direct ByteBuffer Error");
- return 0;
- }
outBuf = (uint8_t*)(env->GetDirectBufferAddress(outBuffer));
// limit is the index of the first element that should not be read or written
out_limit = env->CallIntMethod(outBuffer,conscrypt::jniutil::buffer_limitMethod);
@@ -4176,17 +4171,24 @@
case GEN_EMAIL:
case GEN_DNS:
case GEN_URI: {
- // This must not be a T61String and must not contain NULs.
- const char* data = reinterpret_cast<const char*>(ASN1_STRING_data(gen->d.ia5));
+ // This must be a valid IA5String and must not contain NULs.
+ // BoringSSL does not currently enforce the former (see
+ // https://crbug.com/boringssl/427). The latter was historically an
+ // issue for parsers that truncate at NUL.
+ const uint8_t* data = ASN1_STRING_get0_data(gen->d.ia5);
ssize_t len = ASN1_STRING_length(gen->d.ia5);
- if ((len == static_cast<ssize_t>(strlen(data))) &&
- (ASN1_PRINTABLE_type(ASN1_STRING_data(gen->d.ia5), len) != V_ASN1_T61STRING)) {
- JNI_TRACE("GENERAL_NAME_to_jobject(%p) => Email/DNS/URI \"%s\"", gen, data);
- return env->NewStringUTF(data);
- } else {
- JNI_TRACE("GENERAL_NAME_to_jobject(%p) => Email/DNS/URI invalid", gen);
- return nullptr;
+ std::vector<jchar> jchars;
+ jchars.reserve(len);
+ for (ssize_t i = 0; i < len; i++) {
+ if (data[i] == 0 || data[i] > 127) {
+ JNI_TRACE("GENERAL_NAME_to_jobject(%p) => Email/DNS/URI invalid", gen);
+ return nullptr;
+ }
+ // Converting ASCII to UTF-16 is the identity function.
+ jchars.push_back(data[i]);
}
+ JNI_TRACE("GENERAL_NAME_to_jobject(%p)=> Email/DNS/URI \"%.*s\"", gen, (int) len, data);
+ return env->NewString(jchars.data(), jchars.size());
}
case GEN_DIRNAME:
/* Write in RFC 2253 format */
@@ -4378,6 +4380,12 @@
X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref));
JNI_TRACE("X509_get_version(%p)", x509);
+ if (x509 == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "x509 == null");
+ JNI_TRACE("X509_get_version(%p) => x509 == null", x509);
+ return 0;
+ }
+
// NOLINTNEXTLINE(runtime/int)
long version = X509_get_version(x509);
JNI_TRACE("X509_get_version(%p) => %ld", x509, version);
@@ -4417,6 +4425,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref));
JNI_TRACE("X509_get_serialNumber(%p)", x509);
+
+ if (x509 == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "x509 == null");
+ JNI_TRACE("X509_get_serialNumber(%p) => x509 == null", x509);
+ return nullptr;
+ }
return get_X509Type_serialNumber<X509>(env, x509, X509_get0_serialNumber);
}
@@ -4425,6 +4439,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509_REVOKED* revoked = reinterpret_cast<X509_REVOKED*>(static_cast<uintptr_t>(x509RevokedRef));
JNI_TRACE("X509_REVOKED_get_serialNumber(%p)", revoked);
+
+ if (revoked == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "revoked == null");
+ JNI_TRACE("X509_REVOKED_get_serialNumber(%p) => revoked == null", revoked);
+ return 0;
+ }
return get_X509Type_serialNumber<X509_REVOKED>(env, revoked, X509_REVOKED_get0_serialNumber);
}
@@ -4546,6 +4566,16 @@
X509* x509_2 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref2));
JNI_TRACE("X509_check_issued(%p, %p)", x509_1, x509_2);
+ if (x509_1 == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "x509Ref1 == null");
+ JNI_TRACE("X509_check_issued(%p, %p) => x509_1 == null", x509_1, x509_2);
+ return 0;
+ }
+ if (x509_2 == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "x509Ref2 == null");
+ JNI_TRACE("X509_check_issued(%p, %p) => x509_2 == null", x509_1, x509_2);
+ return 0;
+ }
int ret = X509_check_issued(x509_1, x509_2);
JNI_TRACE("X509_check_issued(%p, %p) => %d", x509_1, x509_2, ret);
return ret;
@@ -4601,6 +4631,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref));
JNI_TRACE("get_X509_signature(%p)", x509);
+
+ if (x509 == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "x509 == null");
+ JNI_TRACE("get_X509_signature(%p) => x509 == null", x509);
+ return nullptr;
+ }
return get_X509Type_signature<X509>(env, x509, get_X509_signature);
}
@@ -4609,6 +4645,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef));
JNI_TRACE("get_X509_CRL_signature(%p)", crl);
+
+ if (crl == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "crl == null");
+ JNI_TRACE("X509_CRL_signature(%p) => crl == null", crl);
+ return nullptr;
+ }
return get_X509Type_signature<X509_CRL>(env, crl, get_X509_CRL_signature);
}
@@ -4694,6 +4736,7 @@
if (crl == nullptr) {
conscrypt::jniutil::throwNullPointerException(env, "crl == null");
+ JNI_TRACE("X509_CRL_get_REVOKED(%p) => crl == null", crl);
return nullptr;
}
@@ -4721,6 +4764,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef));
JNI_TRACE("i2d_X509_CRL(%p)", crl);
+
+ if (crl == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "crl == null");
+ JNI_TRACE("i2d_X509_CRL(%p) => crl == null", crl);
+ return nullptr;
+ }
return ASN1ToByteArray<X509_CRL>(env, crl, i2d_X509_CRL);
}
@@ -4827,6 +4876,12 @@
CONSCRYPT_UNUSED jobject holder) {
CHECK_ERROR_QUEUE_ON_RETURN;
X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef));
+
+ if (crl == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "crl == null");
+ JNI_TRACE("X509_CRL_get_issuer_name(%p) => crl == null", crl);
+ return nullptr;
+ }
JNI_TRACE("X509_CRL_get_issuer_name(%p)", crl);
return ASN1ToByteArray<X509_NAME>(env, X509_CRL_get_issuer(crl), i2d_X509_NAME);
}
@@ -4838,6 +4893,11 @@
X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef));
JNI_TRACE("X509_CRL_get_version(%p)", crl);
+ if (crl == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "crl == null");
+ JNI_TRACE("X509_CRL_get_version(%p) => crl == null", crl);
+ return 0;
+ }
// NOLINTNEXTLINE(runtime/int)
long version = X509_CRL_get_version(crl);
JNI_TRACE("X509_CRL_get_version(%p) => %ld", crl, version);
@@ -4898,6 +4958,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef));
JNI_TRACE("X509_CRL_get_ext(%p, %p)", crl, oid);
+
+ if (crl == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "crl == null");
+ JNI_TRACE("X509_CRL_get_ext(%p) => crl == null", crl);
+ return 0;
+ }
X509_EXTENSION* ext =
X509Type_get_ext<X509_CRL, X509_CRL_get_ext_by_OBJ, X509_CRL_get_ext>(env, crl, oid);
JNI_TRACE("X509_CRL_get_ext(%p, %p) => %p", crl, oid, ext);
@@ -4991,6 +5057,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef));
JNI_TRACE("get_X509_CRL_crl_enc(%p)", crl);
+
+ if (crl == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "crl == null");
+ JNI_TRACE("get_X509_CRL_crl_enc(%p) => crl == null", crl);
+ return nullptr;
+ }
return ASN1ToByteArray<X509_CRL>(env, crl, i2d_X509_CRL_tbs);
}
@@ -5528,6 +5600,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref));
JNI_TRACE("i2d_X509(%p)", x509);
+
+ if (x509 == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "x509 == null");
+ JNI_TRACE("i2d_X509(%p) => x509 == null", x509);
+ return nullptr;
+ }
return ASN1ToByteArray<X509>(env, x509, i2d_X509);
}
@@ -5536,6 +5614,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref));
JNI_TRACE("i2d_X509_PUBKEY(%p)", x509);
+
+ if (x509 == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "x509 == null");
+ JNI_TRACE("i2d_X509_PUBKEY(%p) => x509 == null", x509);
+ return nullptr;
+ }
return ASN1ToByteArray<X509_PUBKEY>(env, X509_get_X509_PUBKEY(x509), i2d_X509_PUBKEY);
}
@@ -5949,6 +6033,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref));
JNI_TRACE("X509_get_issuer_name(%p)", x509);
+
+ if (x509 == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "x509 == null");
+ JNI_TRACE("X509_get_issuer_name(%p) => x509 == null", x509);
+ return nullptr;
+ }
return ASN1ToByteArray<X509_NAME>(env, X509_get_issuer_name(x509), i2d_X509_NAME);
}
@@ -5957,6 +6047,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref));
JNI_TRACE("X509_get_subject_name(%p)", x509);
+
+ if (x509 == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "x509 == null");
+ JNI_TRACE("X509_get_subject_name(%p) => x509 == null", x509);
+ return nullptr;
+ }
return ASN1ToByteArray<X509_NAME>(env, X509_get_subject_name(x509), i2d_X509_NAME);
}
@@ -6199,6 +6295,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef));
JNI_TRACE("X509_CRL_get_ext_oid(%p, %p)", crl, oidString);
+
+ if (crl == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "crl == null");
+ JNI_TRACE("X509_CRL_get_ext_oid(%p) => crl == null", crl);
+ return nullptr;
+ }
return X509Type_get_ext_oid<X509_CRL, X509_CRL_get_ext_by_OBJ, X509_CRL_get_ext>(env, crl,
oidString);
}
@@ -6208,6 +6310,12 @@
CHECK_ERROR_QUEUE_ON_RETURN;
X509_REVOKED* revoked = reinterpret_cast<X509_REVOKED*>(static_cast<uintptr_t>(x509RevokedRef));
JNI_TRACE("X509_REVOKED_get_ext_oid(%p, %p)", revoked, oidString);
+
+ if (revoked == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "revoked == null");
+ JNI_TRACE("X509_REVOKED_get_ext_oid(%p) => revoked == null", revoked);
+ return nullptr;
+ }
return X509Type_get_ext_oid<X509_REVOKED, X509_REVOKED_get_ext_by_OBJ, X509_REVOKED_get_ext>(
env, revoked, oidString);
}
@@ -6553,9 +6661,7 @@
}
jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
- jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
- jmethodID methodID =
- env->GetMethodID(cls, "verifyCertificateChain", "([[BLjava/lang/String;)V");
+ jmethodID methodID = conscrypt::jniutil::sslHandshakeCallbacks_verifyCertificateChain;
const SSL_CIPHER* cipher = SSL_get_pending_cipher(ssl);
const char* authMethod = SSL_CIPHER_get_kx_name(cipher);
@@ -6599,11 +6705,9 @@
jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
- jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
- jmethodID methodID = env->GetMethodID(cls, "onSSLStateChange", "(II)V");
-
JNI_TRACE("ssl=%p info_callback calling onSSLStateChange", ssl);
- env->CallVoidMethod(sslHandshakeCallbacks, methodID, type, value);
+ env->CallVoidMethod(sslHandshakeCallbacks,
+ conscrypt::jniutil::sslHandshakeCallbacks_onSSLStateChange, type, value);
if (env->ExceptionCheck()) {
JNI_TRACE("ssl=%p info_callback exception", ssl);
@@ -6641,8 +6745,7 @@
}
jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
- jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
- jmethodID methodID = env->GetMethodID(cls, "clientCertificateRequested", "([B[I[[B)V");
+ jmethodID methodID = conscrypt::jniutil::sslHandshakeCallbacks_clientCertificateRequested;
// Call Java callback which can reconfigure the client certificate.
const uint8_t* ctype = nullptr;
@@ -6716,8 +6819,7 @@
}
jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
- jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
- jmethodID methodID = env->GetMethodID(cls, "serverCertificateRequested", "()V");
+ jmethodID methodID = conscrypt::jniutil::sslHandshakeCallbacks_serverCertificateRequested;
JNI_TRACE("ssl=%p select_certificate_cb calling serverCertificateRequested", ssl);
env->CallVoidMethod(sslHandshakeCallbacks, methodID);
@@ -6751,9 +6853,7 @@
}
jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
- jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
- jmethodID methodID =
- env->GetMethodID(cls, "clientPSKKeyRequested", "(Ljava/lang/String;[B[B)I");
+ jmethodID methodID = conscrypt::jniutil::sslHandshakeCallbacks_clientPSKKeyRequested;
JNI_TRACE("ssl=%p psk_client_callback calling clientPSKKeyRequested", ssl);
ScopedLocalRef<jstring> identityHintJava(env,
(hint != nullptr) ? env->NewStringUTF(hint) : nullptr);
@@ -6819,9 +6919,7 @@
}
jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
- jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
- jmethodID methodID = env->GetMethodID(cls, "serverPSKKeyRequested",
- "(Ljava/lang/String;Ljava/lang/String;[B)I");
+ jmethodID methodID = conscrypt::jniutil::sslHandshakeCallbacks_serverPSKKeyRequested;
JNI_TRACE("ssl=%p psk_server_callback calling serverPSKKeyRequested", ssl);
const char* identityHint = SSL_get_psk_identity_hint(ssl);
ScopedLocalRef<jstring> identityHintJava(
@@ -6873,8 +6971,7 @@
}
jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
- jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
- jmethodID methodID = env->GetMethodID(cls, "onNewSessionEstablished", "(J)V");
+ jmethodID methodID = conscrypt::jniutil::sslHandshakeCallbacks_onNewSessionEstablished;
JNI_TRACE("ssl=%p new_session_callback calling onNewSessionEstablished", ssl);
env->CallVoidMethod(sslHandshakeCallbacks, methodID, reinterpret_cast<jlong>(session));
if (env->ExceptionCheck()) {
@@ -6918,8 +7015,7 @@
reinterpret_cast<const jbyte*>(id));
jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
- jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
- jmethodID methodID = env->GetMethodID(cls, "serverSessionRequested", "([B)J");
+ jmethodID methodID = conscrypt::jniutil::sslHandshakeCallbacks_serverSessionRequested;
JNI_TRACE("ssl=%p server_session_requested_callback calling serverSessionRequested", ssl);
jlong ssl_session_address = env->CallLongMethod(sslHandshakeCallbacks, methodID, id_array);
if (env->ExceptionCheck()) {
@@ -6956,7 +7052,8 @@
// Packet preamble for text2pcap
CONSCRYPT_LOG(LOG_INFO, LOG_TAG "-jni", "ssl=%p SSL_DATA: %c %ld.%06ld", ssl, direction,
- tv.tv_sec, static_cast<long>(tv.tv_usec)); // NOLINT(runtime/int)
+ static_cast<long>(tv.tv_sec),
+ static_cast<long>(tv.tv_usec)); // NOLINT(runtime/int)
char out[kDataWidth * 3 + 1];
for (size_t i = 0; i < len; i += kDataWidth) {
@@ -7145,8 +7242,7 @@
}
static void NativeCrypto_SSL_enable_tls_channel_id(JNIEnv* env, jclass, jlong ssl_address,
- CONSCRYPT_UNUSED CONSCRYPT_UNUSED jobject
- ssl_holder) {
+ CONSCRYPT_UNUSED jobject ssl_holder) {
CHECK_ERROR_QUEUE_ON_RETURN;
SSL* ssl = to_SSL(env, ssl_address, true);
JNI_TRACE("ssl=%p NativeCrypto_SSL_enable_tls_channel_id", ssl);
@@ -8041,8 +8137,7 @@
reinterpret_cast<const jbyte*>(in));
// Invoke the selection method.
- jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
- jmethodID methodID = env->GetMethodID(cls, "selectApplicationProtocol", "([B)I");
+ jmethodID methodID = conscrypt::jniutil::sslHandshakeCallbacks_selectApplicationProtocol;
jint offset = env->CallIntMethod(sslHandshakeCallbacks, methodID, protocols.get());
if (offset < 0) {
@@ -9339,11 +9434,12 @@
static jstring NativeCrypto_SSL_CIPHER_get_kx_name(JNIEnv* env, jclass, jlong cipher_address) {
CHECK_ERROR_QUEUE_ON_RETURN;
- const SSL_CIPHER* cipher = to_SSL_CIPHER(env, cipher_address, true);
- const char* kx_name = nullptr;
+ const SSL_CIPHER* cipher = to_SSL_CIPHER(env, cipher_address, /*throwIfNull=*/true);
+ if (cipher == nullptr) {
+ return nullptr;
+ }
- kx_name = SSL_CIPHER_get_kx_name(cipher);
-
+ const char* kx_name = SSL_CIPHER_get_kx_name(cipher);
return env->NewStringUTF(kx_name);
}
@@ -9635,6 +9731,11 @@
}
static jlong NativeCrypto_getDirectBufferAddress(JNIEnv* env, jclass, jobject buffer) {
+ // The javadoc for NativeCrypto.getDirectBufferAddress(Buffer buf) defines the behaviour here,
+ // no throwing if the buffer is null or not a direct ByteBuffer.
+ if (!conscrypt::jniutil::isDirectByteBufferInstance(env, buffer)) {
+ return 0;
+ }
return reinterpret_cast<jlong>(env->GetDirectBufferAddress(buffer));
}
diff --git a/common/src/jni/main/include/conscrypt/jniutil.h b/common/src/jni/main/include/conscrypt/jniutil.h
index 6f55608..dcde21e 100644
--- a/common/src/jni/main/include/conscrypt/jniutil.h
+++ b/common/src/jni/main/include/conscrypt/jniutil.h
@@ -41,7 +41,6 @@
extern jclass outputStreamClass;
extern jclass stringClass;
extern jclass byteBufferClass;
-extern jclass bufferClass;
extern jfieldID nativeRef_address;
@@ -53,6 +52,19 @@
extern jmethodID outputStream_flushMethod;
extern jmethodID buffer_positionMethod;
extern jmethodID buffer_limitMethod;
+extern jmethodID buffer_isDirectMethod;
+extern jmethodID cryptoUpcallsClass_rawSignMethod;
+extern jmethodID cryptoUpcallsClass_rsaSignMethod;
+extern jmethodID cryptoUpcallsClass_rsaDecryptMethod;
+extern jmethodID sslHandshakeCallbacks_verifyCertificateChain;
+extern jmethodID sslHandshakeCallbacks_onSSLStateChange;
+extern jmethodID sslHandshakeCallbacks_clientCertificateRequested;
+extern jmethodID sslHandshakeCallbacks_serverCertificateRequested;
+extern jmethodID sslHandshakeCallbacks_clientPSKKeyRequested;
+extern jmethodID sslHandshakeCallbacks_serverPSKKeyRequested;
+extern jmethodID sslHandshakeCallbacks_onNewSessionEstablished;
+extern jmethodID sslHandshakeCallbacks_selectApplicationProtocol;
+extern jmethodID sslHandshakeCallbacks_serverSessionRequested;
/**
* Initializes the JNI constants from the environment.
@@ -135,6 +147,11 @@
extern int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor);
/**
+ * Returns true if buffer is a non-null direct ByteBuffer instance.
+ */
+extern bool isDirectByteBufferInstance(JNIEnv* env, jobject buffer);
+
+/**
* Returns true if the VM's JNI GetByteArrayElements method is likely to create a copy when
* invoked on an array of the provided size.
*/
@@ -159,10 +176,12 @@
*/
extern int throwRuntimeException(JNIEnv* env, const char* msg);
+#ifdef CONSCRYPT_CHECK_ERROR_QUEUE
/**
* Throw a java.lang.AssertionError, with an optional message.
*/
extern int throwAssertionError(JNIEnv* env, const char* msg);
+#endif
/*
* Throw a java.lang.NullPointerException, with an optional message.
diff --git a/common/src/test/java/org/conscrypt/ConscryptSuite.java b/common/src/test/java/org/conscrypt/ConscryptSuite.java
index 263afcd..cce41d5 100644
--- a/common/src/test/java/org/conscrypt/ConscryptSuite.java
+++ b/common/src/test/java/org/conscrypt/ConscryptSuite.java
@@ -35,6 +35,7 @@
import org.conscrypt.java.security.KeyFactoryTestDSA;
import org.conscrypt.java.security.KeyFactoryTestEC;
import org.conscrypt.java.security.KeyFactoryTestRSA;
+import org.conscrypt.java.security.KeyFactoryTestRSACrt;
import org.conscrypt.java.security.KeyPairGeneratorTest;
import org.conscrypt.java.security.KeyPairGeneratorTestDH;
import org.conscrypt.java.security.KeyPairGeneratorTestDSA;
@@ -80,8 +81,9 @@
// org.conscrypt tests
CertPinManagerTest.class,
ChainStrengthAnalyzerTest.class,
- TrustManagerImplTest.class,
HostnameVerifierTest.class,
+ NativeCryptoArgTest.class,
+ TrustManagerImplTest.class,
// org.conscrypt.ct tests
CTVerifierTest.class,
SerializationTest.class,
@@ -106,6 +108,7 @@
KeyFactoryTestDSA.class,
KeyFactoryTestEC.class,
KeyFactoryTestRSA.class,
+ KeyFactoryTestRSACrt.class,
KeyPairGeneratorTest.class,
KeyPairGeneratorTestDH.class,
KeyPairGeneratorTestDSA.class,
diff --git a/common/src/test/java/org/conscrypt/NativeCryptoArgTest.java b/common/src/test/java/org/conscrypt/NativeCryptoArgTest.java
new file mode 100644
index 0000000..76e8beb
--- /dev/null
+++ b/common/src/test/java/org/conscrypt/NativeCryptoArgTest.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2020 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class NativeCryptoArgTest {
+ // Null value passed in for a long which represents a native address
+ private static final long NULL = 0L;
+ /*
+ * Non-null value passed in for a long which represents a native address. Shouldn't
+ * ever get de-referenced but we make it a multiple of 4 to avoid any alignment errors.
+ * Used in the case where there are multiple checks we want to test in a native method,
+ * so we can get past the first check and test the second one.
+ */
+ private static final long NOT_NULL = 4L;
+ private static final String CONSCRYPT_PACKAGE = NativeCryptoArgTest.class.getCanonicalName()
+ .substring(0, NativeCryptoArgTest.class.getCanonicalName().lastIndexOf('.') + 1);
+ private static final Set<String> testedMethods = new HashSet<>();
+ private final Map<String, Class<?>> classCache = new HashMap<>();
+ private final Map<String, Method> methodMap = buildMethodMap();
+
+ @AfterClass
+ public static void after() {
+ // TODO(prb): Temporary hacky check - remove
+ assertTrue(testedMethods.size() >= 190);
+ }
+
+ @Test
+ public void ecMethods() throws Throwable {
+ String[] illegalArgMethods = new String[] {
+ "EC_GROUP_new_arbitrary"
+ };
+ String[] ioExMethods = new String[] {
+ "EC_KEY_parse_curve_name",
+ "EC_KEY_marshal_curve_name"
+ };
+
+ // All of the EC_* methods apart from the exceptions below throw NPE if their
+ // first argument is null.
+ MethodFilter filter = MethodFilter.newBuilder("EC_ methods")
+ .hasPrefix("EC_")
+ .except(illegalArgMethods)
+ .except(ioExMethods)
+ .expectSize(16)
+ .build();
+ testMethods(filter, NullPointerException.class);
+
+ filter = MethodFilter.nameFilter("EC_ methods (IllegalArgument)", illegalArgMethods);
+ testMethods(filter, IllegalArgumentException.class);
+
+ filter = MethodFilter.nameFilter("EC_ methods (IOException)", ioExMethods);
+ testMethods(filter, IOException.class);
+ }
+
+ @Test
+ public void macMethods() throws Throwable {
+ // All of the non-void HMAC and CMAC methods throw NPE when passed a null pointer
+ MethodFilter filter = MethodFilter.newBuilder("HMAC methods")
+ .hasPrefix("HMAC_")
+ .takesArguments()
+ .expectSize(5)
+ .build();
+ testMethods(filter, NullPointerException.class);
+
+ filter = MethodFilter.newBuilder("CMAC methods")
+ .hasPrefix("CMAC_")
+ .takesArguments()
+ .expectSize(5)
+ .build();
+ testMethods(filter, NullPointerException.class);
+ }
+
+ @Test
+ public void sslMethods() throws Throwable {
+ // These methods don't throw on a null first arg as they can get called before the
+ // connection is fully initialised. However if the first arg is non-NULL, any subsequent
+ // null args should throw NPE.
+ String[] nonThrowingMethods = new String[] {
+ "SSL_interrupt",
+ "SSL_shutdown",
+ "ENGINE_SSL_shutdown",
+ };
+
+ // Most of the NativeSsl methods take a long holding a pointer to the native
+ // object followed by a {@code NativeSsl} holder object. However the second arg
+ // is unused(!) so we don't need to test it.
+ MethodFilter filter = MethodFilter.newBuilder("NativeSsl methods")
+ .hasArg(0, long.class)
+ .hasArg(1, conscryptClass("NativeSsl"))
+ .except(nonThrowingMethods)
+ .expectSize(60)
+ .build();
+
+ testMethods(filter, NullPointerException.class);
+
+ // Many of the SSL_* methods take a single long which points
+ // to a native object.
+ filter = MethodFilter.newBuilder("1-arg SSL methods")
+ .hasPrefix("SSL_")
+ .hasArgLength(1)
+ .hasArg(0, long.class)
+ .expectSize(10)
+ .build();
+
+ testMethods(filter, NullPointerException.class);
+
+ filter = MethodFilter.nameFilter("Non throwing NativeSsl methods", nonThrowingMethods);
+ testMethods(filter, null);
+
+ expectVoid("SSL_shutdown", NOT_NULL, null, null, null);
+ expectNPE("SSL_shutdown", NOT_NULL, null, new FileDescriptor(), null);
+ expectNPE("ENGINE_SSL_shutdown", NOT_NULL, null, null);
+ expectVoid("SSL_set_session", NOT_NULL, null, NULL);
+ }
+
+ @Test
+ public void evpMethods() throws Throwable {
+ String[] illegalArgMethods = new String[] {
+ "EVP_AEAD_CTX_open_buf",
+ "EVP_AEAD_CTX_seal_buf",
+ "EVP_PKEY_new_RSA"
+ };
+ String[] nonThrowingMethods = new String[] {
+ "EVP_MD_CTX_destroy",
+ "EVP_PKEY_CTX_free",
+ "EVP_PKEY_free",
+ "EVP_CIPHER_CTX_free"
+ };
+
+ // All of the non-void EVP_ methods apart from the above should throw on a null
+ // first argument.
+ MethodFilter filter = MethodFilter.newBuilder("EVP methods")
+ .hasPrefix("EVP_")
+ .takesArguments()
+ .except(illegalArgMethods)
+ .except(nonThrowingMethods)
+ .expectSize(45)
+ .build();
+
+ testMethods(filter, NullPointerException.class);
+
+ filter = MethodFilter.nameFilter("EVP methods (IllegalArgument)", illegalArgMethods);
+ testMethods(filter, IllegalArgumentException.class);
+
+ filter = MethodFilter.nameFilter("EVP methods (non-throwing)", nonThrowingMethods);
+ testMethods(filter, null);
+ }
+
+ @Test
+ public void x509Methods() throws Throwable {
+ // A number of X509 methods have a native pointer as arg 0 and an
+ // OpenSSLX509Certificate or OpenSSLX509CRL as arg 1.
+ MethodFilter filter = MethodFilter.newBuilder("X509 methods")
+ .hasArgLength(2)
+ .hasArg(0, long.class)
+ .hasArg(1, conscryptClass("OpenSSLX509Certificate"),
+ conscryptClass("OpenSSLX509CRL"))
+ .expectSize(32)
+ .build();
+ // TODO(prb): test null second argument
+ testMethods(filter, NullPointerException.class);
+
+ // The rest of the X509 methods are somewhat ad hoc.
+ expectNPE("d2i_X509", (Object) null);
+
+ invokeAndExpect( conscryptThrowable("OpenSSLX509CertificateFactory$ParsingException"),
+ "d2i_X509", new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0});
+
+ expectNPE("d2i_X509_bio", NULL);
+ expectNPE("PEM_read_bio_X509", NULL);
+ expectNPE("ASN1_seq_pack_X509", (Object) null);
+
+ // TODO(prb): Check what this should really throw
+ // expectNPE("ASN1_seq_pack_X509", (Object) new long[] { NULL });
+
+ expectNPE("ASN1_seq_unpack_X509_bio", NULL);
+
+ //
+ expectNPE("X509_cmp", NULL, null, NULL, null);
+ expectNPE("X509_cmp", NOT_NULL, null, NULL, null);
+ expectNPE("X509_cmp", NULL, null, NOT_NULL, null);
+
+ expectNPE("X509_print_ex", NULL, NULL, null, NULL, NULL);
+ expectNPE("X509_print_ex", NOT_NULL, NULL, null, NULL, NULL);
+ expectNPE("X509_print_ex", NULL, NOT_NULL, null, NULL, NULL);
+ }
+
+ private void testMethods(MethodFilter filter, Class<? extends Throwable> exceptionClass)
+ throws Throwable {
+ List<Method> methods = filter.filter(methodMap.values());
+
+ for (Method method : methods) {
+ List<Object[]> argsLists = permuteArgs(method);
+ for (Object[] args : argsLists) {
+ invokeAndExpect(exceptionClass, method, args);
+ }
+ }
+ }
+
+ private List<Object[]> permuteArgs(Method method) {
+ // For now just supply 0 for integral types and null for everything else
+ // TODO: allow user defined strategy, e.g. if two longs passed as native refs,
+ // generate {NULL,NULL}, {NULL,NOT_NULL}, {NOT_NULL,NULL} to test both null checks
+ List<Object[]> result = new ArrayList<>(1);
+
+ Class<?>[] argTypes = method.getParameterTypes();
+
+ int argCount = argTypes.length;
+ assertTrue(argCount > 0);
+ Object[] args = new Object[argCount];
+
+ for (int arg = 0; arg < argCount; arg++) {
+ if (argTypes[arg] == int.class) {
+ args[arg] = 0;
+ } else if (argTypes[arg] == long.class) {
+ args[arg] = NULL;
+ } else if (argTypes[arg] == boolean.class) {
+ args[arg] = false;
+ } else {
+ args[arg] = null;
+ }
+ }
+ result.add(args);
+ return result;
+ }
+
+ private void expectVoid(String methodName, Object... args) throws Throwable {
+ invokeAndExpect(null, methodName, args);
+ }
+
+ private void expectNPE(String methodName, Object... args) throws Throwable {
+ invokeAndExpect(NullPointerException.class, methodName, args);
+ }
+
+ private void invokeAndExpect(Class<? extends Throwable> expectedThrowable, String methodName,
+ Object... args) throws Throwable {
+ Method method = methodMap.get(methodName);
+ assertNotNull(method);
+ assertEquals(methodName, method.getName());
+ invokeAndExpect(expectedThrowable, method, args);
+ }
+
+ private void invokeAndExpect(Class<? extends Throwable> expectedThrowable, Method method,
+ Object... args) throws Throwable {
+ try {
+ method.invoke(null, args);
+ if (expectedThrowable != null) {
+ fail("No exception thrown by method " + method.getName());
+ }
+ } catch (IllegalAccessException e) {
+ throw new AssertionError("Illegal access", e);
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (expectedThrowable != null) {
+ assertEquals("Method: " + method.getName(), expectedThrowable, cause.getClass());
+ } else {
+ throw cause;
+ }
+ }
+ testedMethods.add(method.getName());
+ }
+
+ @SuppressWarnings("unchecked")
+ private Class<? extends Throwable> conscryptThrowable(String name) {
+ Class<?> klass = conscryptClass(name);
+ assertNotNull(klass);
+ assertTrue(Throwable.class.isAssignableFrom(klass));
+ return (Class<? extends Throwable>) klass;
+ }
+
+ private Class<?> conscryptClass(String className) {
+ return classCache.computeIfAbsent(className, s -> {
+ try {
+ return Class.forName(CONSCRYPT_PACKAGE + className);
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ });
+ }
+
+ private Map<String, Method> buildMethodMap() {
+ Map<String, Method> classMap = new HashMap<>();
+ assertNotNull(classMap);
+ Class<?> nativeCryptoClass = conscryptClass("NativeCrypto");
+ assertNotNull(nativeCryptoClass);
+ for (Method method : nativeCryptoClass.getDeclaredMethods()) {
+ int modifiers = method.getModifiers();
+ if (!Modifier.isNative(modifiers)) {
+ continue;
+ }
+ method.setAccessible(true);
+ classMap.put(method.getName(), method);
+ }
+ return classMap;
+ }
+}
diff --git a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java
index ff9be81..571c1a7 100644
--- a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java
+++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java
@@ -42,7 +42,6 @@
import java.util.Arrays;
import java.util.List;
import org.junit.ClassRule;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
@@ -69,7 +68,6 @@
}
@Test
- @Ignore("b/209335673 Base image has fix for b/191150645 but release version of module does not")
public void getEncodedFailsWhenCrtValuesMissing() throws Exception {
PrivateKey privateKey = getPrivateKey();
try {
@@ -121,7 +119,6 @@
}
@Test
- @Ignore("b/209335673 Base image has fix for b/191150645 but release version of module does not")
public void javaSerialization() throws Exception{
PrivateKey privatekey = getPrivateKey();
@@ -136,13 +133,12 @@
assertEquals(privatekey, copy);
}
- // b/209335673 Base image has fix for b/191150645 but release version of module does not,
- // @Override
- // protected List<KeyPair> getKeys() throws NoSuchAlgorithmException, InvalidKeySpecException {
- // return Arrays.asList(
- // new KeyPair(DefaultKeys.getPublicKey(algorithmName), getPrivateKey())
- // );
- // }
+ @Override
+ protected List<KeyPair> getKeys() throws NoSuchAlgorithmException, InvalidKeySpecException {
+ return Arrays.asList(
+ new KeyPair(DefaultKeys.getPublicKey(algorithmName), getPrivateKey())
+ );
+ }
// The private RSA key returned by DefaultKeys.getPrivateKey() is built from a PKCS#8
// KeySpec and so will be an instance of RSAPrivateCrtKey, but we want to test RSAPrivateKey
diff --git a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSACrt.java b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSACrt.java
index 3afa73f..bddd898 100644
--- a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSACrt.java
+++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSACrt.java
@@ -30,7 +30,6 @@
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -60,8 +59,7 @@
@Test
public void testExtraBufferSpace_Private() throws Exception {
PrivateKey privateKey = DefaultKeys.getPrivateKey("RSA");
- // b/209335673 Base image has fix for b/191150645 but release version of module does not
- // assertTrue(privateKey instanceof RSAPrivateCrtKey);
+ assertTrue(privateKey instanceof RSAPrivateCrtKey);
byte[] encoded = privateKey.getEncoded();
byte[] longBuffer = new byte[encoded.length + 147];
@@ -72,7 +70,6 @@
}
@Test
- @Ignore("b/209335673 Base image has fix for b/191150645 but release version of module does not")
public void javaSerialization() throws Exception{
PrivateKey privateKey = DefaultKeys.getPrivateKey("RSA");
assertTrue(privateKey instanceof RSAPrivateCrtKey);
diff --git a/common/src/test/java/org/conscrypt/javax/crypto/CipherTest.java b/common/src/test/java/org/conscrypt/javax/crypto/CipherTest.java
index 8f4f896..e77f492 100644
--- a/common/src/test/java/org/conscrypt/javax/crypto/CipherTest.java
+++ b/common/src/test/java/org/conscrypt/javax/crypto/CipherTest.java
@@ -148,6 +148,33 @@
|| algorithm.equals("AES/OFB/PKCS7PADDING"))) {
return false;
}
+
+ if (provider.equals("BC")) {
+ return isSupportedByBC(algorithm);
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks for algorithms removed from BC in Android 12 and so not usable for these
+ * tests.
+ *
+ * TODO(prb): make this version aware, as this test runs against BC on older Android
+ * versions via MTS and should continue to test these algorithms there.
+ *
+ */
+ private static boolean isSupportedByBC(String algorithm) {
+ String[] removedBcPrefices = new String[]{
+ "AES/ECB",
+ "AES/CBC",
+ "AES/GCM"
+ };
+ for (String prefix : removedBcPrefices) {
+ if (algorithm.startsWith(prefix)) {
+ return false;
+ }
+ }
return true;
}
@@ -1080,7 +1107,9 @@
for (String padding : paddings) {
final String algorithmName = algorithm + "/" + mode + "/" + padding;
try {
- test_Cipher_Algorithm(provider, algorithmName);
+ if (isSupported(algorithmName, provider.getName())) {
+ test_Cipher_Algorithm(provider, algorithmName);
+ }
} catch (Throwable e) {
out.append("Error encountered checking " + algorithmName
+ " with provider " + provider.getName() + "\n");
@@ -3475,6 +3504,9 @@
if (provider.equals("SunJCE") && transformation.endsWith("/PKCS7PADDING")) {
return false;
}
+ if (provider.equals("BC")) {
+ return isSupportedByBC(transformation);
+ }
return true;
}
}
@@ -4102,6 +4134,9 @@
final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
PrintStream out = new PrintStream(errBuffer);
for (CipherTestParam p : CIPHER_TEST_PARAMS) {
+ if (!p.compatibleWith(provider)) {
+ continue;
+ }
try {
checkCipher_ShortBlock_Failure(p, provider);
} catch (Exception e) {
@@ -4301,32 +4336,6 @@
}
}
- // Test that when reading GCM parameters encoded using ASN1, a value for the tag size
- // not present indicates a value of 12.
- // https://b/29876633
- @Test
- public void test_DefaultGCMTagSizeAlgorithmParameterSpec() throws Exception {
- Assume.assumeNotNull(Security.getProvider("BC"));
- final String AES = "AES";
- final String AES_GCM = "AES/GCM/NoPadding";
- byte[] input = new byte[16];
- byte[] key = new byte[16];
- Cipher cipher = Cipher.getInstance(AES_GCM, "BC");
- AlgorithmParameters param = AlgorithmParameters.getInstance("GCM");
- param.init(new byte[] {
- (byte) 48, // DER encoding : tag_Sequence
- (byte) 14, // DER encoding : total length
- (byte) 4, // DER encoding : tag_OctetString
- (byte) 12, // DER encoding : counter length
- (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
- (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0 });
- cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, AES), param);
- byte[] ciphertext = cipher.update(input);
- assertEquals(16, ciphertext.length);
- byte[] tag = cipher.doFinal();
- assertEquals(12, tag.length);
- }
-
@Test
public void testAES_ECB_PKCS5Padding_ShortBuffer_Failure() throws Exception {
for (String provider : AES_PROVIDERS) {
@@ -4389,7 +4398,11 @@
}
private void testAES_ECB_NoPadding_IncrementalUpdate_Success(String provider) throws Exception {
- Cipher c = Cipher.getInstance("AES/ECB/NoPadding", provider);
+ String algorithm = "AES/ECB/NoPadding";
+ if (!isSupported(algorithm, provider)) {
+ return;
+ }
+ Cipher c = Cipher.getInstance(algorithm, provider);
assertEquals(provider, c.getProvider().getName());
c.init(Cipher.ENCRYPT_MODE, AES_128_KEY);
@@ -4423,7 +4436,11 @@
}
private void testAES_ECB_NoPadding_IvParameters_Failure(String provider) throws Exception {
- Cipher c = Cipher.getInstance("AES/ECB/NoPadding", provider);
+ String algorithm = "AES/ECB/NoPadding";
+ if (!isSupported(algorithm, provider)) {
+ return;
+ }
+ Cipher c = Cipher.getInstance(algorithm, provider);
AlgorithmParameterSpec spec = new IvParameterSpec(AES_IV_ZEROES);
try {
diff --git a/common/src/test/java/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java b/common/src/test/java/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java
index 4cd0843..9f64315 100644
--- a/common/src/test/java/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java
+++ b/common/src/test/java/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java
@@ -85,6 +85,10 @@
@Test
public void test_XDHKeyAgreement() throws Exception {
for (Provider p : Security.getProviders("KeyAgreement.XDH")) {
+ // Skip testing Android Keystore as it's covered by CTS tests.
+ if ("AndroidKeyStore".equals(p.getName())) {
+ continue;
+ }
setupKeys(p);
KeyAgreement ka = KeyAgreement.getInstance("XDH", p);
diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketTest.java
index 17c3e77..e5a18ce 100644
--- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketTest.java
+++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketTest.java
@@ -72,7 +72,6 @@
import org.conscrypt.tlswire.util.TlsProtocolVersion;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -422,7 +421,6 @@
* lower span of contiguous protocols is used in practice.
*/
@Test
- @Ignore("Needs TLS 1.0 or 1.1 which are scheduled for deprecation")
public void test_SSLSocket_noncontiguousProtocols_useLower() throws Exception {
TestSSLContext c = TestSSLContext.create();
SSLContext clientContext = c.clientContext;
@@ -456,7 +454,6 @@
* for both client and server isn't supported by the other.
*/
@Test
- @Ignore("Needs TLS 1.0 or 1.1 which are scheduled for deprecation")
public void test_SSLSocket_noncontiguousProtocols_canNegotiate() throws Exception {
TestSSLContext c = TestSSLContext.create();
SSLContext clientContext = c.clientContext;
@@ -1011,7 +1008,6 @@
// Confirms that communication without the TLS_FALLBACK_SCSV cipher works as it always did.
@Test
- @Ignore("Needs TLS 1.0 or 1.1 which are scheduled for deprecation")
public void test_SSLSocket_sendsNoTlsFallbackScsv_Fallback_Success() throws Exception {
TestSSLContext context = TestSSLContext.create();
// TLS_FALLBACK_SCSV is only applicable to TLS <= 1.2
@@ -1052,7 +1048,6 @@
}
@Test
- @Ignore("Needs TLS 1.0 or 1.1 which are scheduled for deprecation")
public void test_SSLSocket_sendsTlsFallbackScsv_InappropriateFallback_Failure()
throws Exception {
TestSSLContext context = TestSSLContext.create();
@@ -1106,7 +1101,6 @@
}
@Test
- @Ignore("Needs TLS 1.0 or 1.1 which are scheduled for deprecation")
public void test_SSLSocket_tlsFallback_byVersion() throws Exception {
String[] supportedProtocols =
SSLContext.getDefault().getDefaultSSLParameters().getProtocols();
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ClientSessionContext.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ClientSessionContext.java
index 41bc8d2..c4fa3a7 100644
--- a/repackaged/common/src/main/java/com/android/org/conscrypt/ClientSessionContext.java
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ClientSessionContext.java
@@ -52,10 +52,9 @@
* Applications should not use this method. Instead use {@link
* Conscrypt#setClientSessionCache(SSLContext, SSLClientSessionCache)}.
*/
- @android.compat.annotation
- .UnsupportedAppUsage
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- public void setPersistentCache(SSLClientSessionCache persistentCache) {
+ @android.compat.annotation.UnsupportedAppUsage
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ public void setPersistentCache(SSLClientSessionCache persistentCache) {
this.persistentCache = persistentCache;
}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/FileClientSessionCache.java b/repackaged/common/src/main/java/com/android/org/conscrypt/FileClientSessionCache.java
index cdcae47..248eee1 100644
--- a/repackaged/common/src/main/java/com/android/org/conscrypt/FileClientSessionCache.java
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/FileClientSessionCache.java
@@ -331,10 +331,9 @@
* @throws IOException if the file exists and is not a directory or if
* creating the directories fails
*/
- @android.compat.annotation
- .UnsupportedAppUsage
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- public static synchronized SSLClientSessionCache usingDirectory(File directory)
+ @android.compat.annotation.UnsupportedAppUsage
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ public static synchronized SSLClientSessionCache usingDirectory(File directory)
throws IOException {
FileClientSessionCache.Impl cache = caches.get(directory);
if (cache == null) {
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLKey.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLKey.java
index 61216ab..bc09c81 100644
--- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLKey.java
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLKey.java
@@ -65,9 +65,8 @@
/**
* Returns the EVP_PKEY context for use in JNI calls.
*/
- @android.compat.annotation
- .UnsupportedAppUsage
- NativeRef.EVP_PKEY getNativeRef() {
+ @android.compat.annotation.UnsupportedAppUsage
+ NativeRef.EVP_PKEY getNativeRef() {
return ctx;
}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLProvider.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLProvider.java
index 42a5d33..16241e3 100644
--- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLProvider.java
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLProvider.java
@@ -32,8 +32,7 @@
* </ul>
* @hide This class is not part of the Android public SDK API
*/
-@libcore.
-api.IntraCoreApi
+@libcore.api.IntraCoreApi
@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
@Internal
public final class OpenSSLProvider extends Provider {
@@ -50,12 +49,10 @@
private static final String STANDARD_RSA_PUBLIC_KEY_INTERFACE_CLASS_NAME =
"java.security.interfaces.RSAPublicKey";
- @android.compat.annotation
- .UnsupportedAppUsage
- @libcore.api
- .IntraCoreApi
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- public OpenSSLProvider() {
+ @android.compat.annotation.UnsupportedAppUsage
+ @libcore.api.IntraCoreApi
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ public OpenSSLProvider() {
this(Platform.getDefaultProviderName());
}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRandom.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRandom.java
index 57e2fe6..0c7b404 100644
--- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRandom.java
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRandom.java
@@ -29,10 +29,9 @@
public final class OpenSSLRandom extends SecureRandomSpi implements Serializable {
private static final long serialVersionUID = 8506210602917522861L;
- @android.compat.annotation
- .UnsupportedAppUsage
- @libcore.api.IntraCoreApi
- public OpenSSLRandom() {}
+ @android.compat.annotation.UnsupportedAppUsage
+ @libcore.api.IntraCoreApi
+ public OpenSSLRandom() {}
@Override
protected void engineSetSeed(byte[] seed) {
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSocketImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSocketImpl.java
index ca2542c..717f62d 100644
--- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSocketImpl.java
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSocketImpl.java
@@ -67,13 +67,13 @@
return super.getHostname();
}
- @android.compat.annotation
- .UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q,
- publicAlternatives = "Use {@code javax.net.ssl.SSLParameters#setServerNames}.")
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- @Override
- public void
- setHostname(String hostname) {
+ @android.compat.annotation.
+ UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q,
+ publicAlternatives = "Use {@code javax.net.ssl.SSLParameters#setServerNames}.")
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ @Override
+ public void
+ setHostname(String hostname) {
super.setHostname(hostname);
}
@@ -88,11 +88,10 @@
return super.getFileDescriptor$();
}
- @android.compat.annotation
- .UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- @Override
- public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
+ @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ @Override
+ public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
super.setSoWriteTimeout(writeTimeoutMilliseconds);
}
@@ -102,26 +101,23 @@
return super.getSoWriteTimeout();
}
- @android.compat.annotation
- .UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- @Override
- public void setHandshakeTimeout(int handshakeTimeoutMilliseconds)
- throws SocketException {
+ @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ @Override
+ public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
super.setHandshakeTimeout(handshakeTimeoutMilliseconds);
}
@Override
public abstract SSLSession getHandshakeSession();
- @android.compat.annotation
- .UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q,
- publicAlternatives =
- "Use {@link android.net.ssl.SSLSockets#setUseSessionTickets}.")
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- @Override
- public abstract void
- setUseSessionTickets(boolean useSessionTickets);
+ @android.compat.annotation.
+ UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q,
+ publicAlternatives = "Use {@link android.net.ssl.SSLSockets#setUseSessionTickets}.")
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ @Override
+ public abstract void
+ setUseSessionTickets(boolean useSessionTickets);
@android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
@Override
@@ -131,8 +127,7 @@
@Override
public abstract byte[] getChannelId() throws SSLException;
- @android.compat.
- annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+ @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
@Override
public abstract void setChannelIdPrivateKey(PrivateKey privateKey);
@@ -140,24 +135,22 @@
/**
* @deprecated NPN is not supported
*/
- @android.compat.annotation
- .UnsupportedAppUsage
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- @Override
- @Deprecated
- public final byte[] getNpnSelectedProtocol() {
+ @android.compat.annotation.UnsupportedAppUsage
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ @Override
+ @Deprecated
+ public final byte[] getNpnSelectedProtocol() {
return super.getNpnSelectedProtocol();
}
/**
* @deprecated NPN is not supported
*/
- @android.compat.annotation
- .UnsupportedAppUsage
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- @Override
- @Deprecated
- public final void setNpnProtocols(byte[] npnProtocols) {
+ @android.compat.annotation.UnsupportedAppUsage
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ @Override
+ @Deprecated
+ public final void setNpnProtocols(byte[] npnProtocols) {
super.setNpnProtocols(npnProtocols);
}
@@ -178,29 +171,28 @@
/**
* @deprecated use {@link #getApplicationProtocol()} instead.
*/
- @android.compat.annotation
- .UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q,
- publicAlternatives =
- "Use {@code javax.net.ssl.SSLSocket#getApplicationProtocol()}.")
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- @Override
- @Deprecated
- public final byte[] getAlpnSelectedProtocol() {
+ @android.compat.annotation.
+ UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q,
+ publicAlternatives = "Use {@code javax.net.ssl.SSLSocket#getApplicationProtocol()}.")
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ @Override
+ @Deprecated
+ public final byte[] getAlpnSelectedProtocol() {
return SSLUtils.toProtocolBytes(getApplicationProtocol());
}
/**
* @deprecated Use {@link #setAlpnProtocols(String[])} instead.
*/
- @android.compat.annotation
- .UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q,
- publicAlternatives =
- "Use {@code javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}.")
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- @Override
- @Deprecated
- public final void
- setAlpnProtocols(byte[] protocols) {
+ @android.compat.annotation.
+ UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q,
+ publicAlternatives =
+ "Use {@code javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}.")
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ @Override
+ @Deprecated
+ public final void
+ setAlpnProtocols(byte[] protocols) {
setApplicationProtocols(SSLUtils.decodeProtocols(protocols == null ? EmptyArray.BYTE : protocols));
}
}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/TEST_MAPPING b/repackaged/common/src/main/java/com/android/org/conscrypt/TEST_MAPPING
index b284ce2..317e347 100644
--- a/repackaged/common/src/main/java/com/android/org/conscrypt/TEST_MAPPING
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/TEST_MAPPING
@@ -5,8 +5,11 @@
"options": [
{
"include-filter": "com.android.org.conscrypt"
+ },
+ {
+ "include-filter": "libcore.java.security"
}
]
}
]
-}
\ No newline at end of file
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/TrustManagerImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/TrustManagerImpl.java
index 79945c4..8fc5c36 100644
--- a/repackaged/common/src/main/java/com/android/org/conscrypt/TrustManagerImpl.java
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/TrustManagerImpl.java
@@ -149,10 +149,9 @@
/**
* Creates X509TrustManager based on a keystore
*/
- @android.compat.annotation
- .UnsupportedAppUsage
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- public TrustManagerImpl(KeyStore keyStore) {
+ @android.compat.annotation.UnsupportedAppUsage
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ public TrustManagerImpl(KeyStore keyStore) {
this(keyStore, null);
}
@@ -326,11 +325,10 @@
/**
* For backward compatibility with older Android API that used String for the hostname only.
*/
- @android.compat.annotation
- .UnsupportedAppUsage
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- public List<X509Certificate> checkServerTrusted(X509Certificate[] chain,
- String authType, String hostname) throws CertificateException {
+ @android.compat.annotation.UnsupportedAppUsage
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ public List<X509Certificate> checkServerTrusted(
+ X509Certificate[] chain, String authType, String hostname) throws CertificateException {
return checkTrusted(chain, null /* ocspData */, null /* tlsSctData */, authType, hostname,
false);
}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/NativeCryptoArgTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/NativeCryptoArgTest.java
new file mode 100644
index 0000000..db096a0
--- /dev/null
+++ b/repackaged/common/src/test/java/com/android/org/conscrypt/NativeCryptoArgTest.java
@@ -0,0 +1,335 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2020 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 com.android.org.conscrypt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class NativeCryptoArgTest {
+ // Null value passed in for a long which represents a native address
+ private static final long NULL = 0L;
+ /*
+ * Non-null value passed in for a long which represents a native address. Shouldn't
+ * ever get de-referenced but we make it a multiple of 4 to avoid any alignment errors.
+ * Used in the case where there are multiple checks we want to test in a native method,
+ * so we can get past the first check and test the second one.
+ */
+ private static final long NOT_NULL = 4L;
+ private static final String CONSCRYPT_PACKAGE = NativeCryptoArgTest.class.getCanonicalName()
+ .substring(0, NativeCryptoArgTest.class.getCanonicalName().lastIndexOf('.') + 1);
+ private static final Set<String> testedMethods = new HashSet<>();
+ private final Map<String, Class<?>> classCache = new HashMap<>();
+ private final Map<String, Method> methodMap = buildMethodMap();
+
+ @AfterClass
+ public static void after() {
+ // TODO(prb): Temporary hacky check - remove
+ assertTrue(testedMethods.size() >= 190);
+ }
+
+ @Test
+ public void ecMethods() throws Throwable {
+ String[] illegalArgMethods = new String[] {
+ "EC_GROUP_new_arbitrary"
+ };
+ String[] ioExMethods = new String[] {
+ "EC_KEY_parse_curve_name",
+ "EC_KEY_marshal_curve_name"
+ };
+
+ // All of the EC_* methods apart from the exceptions below throw NPE if their
+ // first argument is null.
+ MethodFilter filter = MethodFilter.newBuilder("EC_ methods")
+ .hasPrefix("EC_")
+ .except(illegalArgMethods)
+ .except(ioExMethods)
+ .expectSize(16)
+ .build();
+ testMethods(filter, NullPointerException.class);
+
+ filter = MethodFilter.nameFilter("EC_ methods (IllegalArgument)", illegalArgMethods);
+ testMethods(filter, IllegalArgumentException.class);
+
+ filter = MethodFilter.nameFilter("EC_ methods (IOException)", ioExMethods);
+ testMethods(filter, IOException.class);
+ }
+
+ @Test
+ public void macMethods() throws Throwable {
+ // All of the non-void HMAC and CMAC methods throw NPE when passed a null pointer
+ MethodFilter filter = MethodFilter.newBuilder("HMAC methods")
+ .hasPrefix("HMAC_")
+ .takesArguments()
+ .expectSize(5)
+ .build();
+ testMethods(filter, NullPointerException.class);
+
+ filter = MethodFilter.newBuilder("CMAC methods")
+ .hasPrefix("CMAC_")
+ .takesArguments()
+ .expectSize(5)
+ .build();
+ testMethods(filter, NullPointerException.class);
+ }
+
+ @Test
+ public void sslMethods() throws Throwable {
+ // These methods don't throw on a null first arg as they can get called before the
+ // connection is fully initialised. However if the first arg is non-NULL, any subsequent
+ // null args should throw NPE.
+ String[] nonThrowingMethods = new String[] {
+ "SSL_interrupt",
+ "SSL_shutdown",
+ "ENGINE_SSL_shutdown",
+ };
+
+ // Most of the NativeSsl methods take a long holding a pointer to the native
+ // object followed by a {@code NativeSsl} holder object. However the second arg
+ // is unused(!) so we don't need to test it.
+ MethodFilter filter = MethodFilter.newBuilder("NativeSsl methods")
+ .hasArg(0, long.class)
+ .hasArg(1, conscryptClass("NativeSsl"))
+ .except(nonThrowingMethods)
+ .expectSize(60)
+ .build();
+
+ testMethods(filter, NullPointerException.class);
+
+ // Many of the SSL_* methods take a single long which points
+ // to a native object.
+ filter = MethodFilter.newBuilder("1-arg SSL methods")
+ .hasPrefix("SSL_")
+ .hasArgLength(1)
+ .hasArg(0, long.class)
+ .expectSize(10)
+ .build();
+
+ testMethods(filter, NullPointerException.class);
+
+ filter = MethodFilter.nameFilter("Non throwing NativeSsl methods", nonThrowingMethods);
+ testMethods(filter, null);
+
+ expectVoid("SSL_shutdown", NOT_NULL, null, null, null);
+ expectNPE("SSL_shutdown", NOT_NULL, null, new FileDescriptor(), null);
+ expectNPE("ENGINE_SSL_shutdown", NOT_NULL, null, null);
+ expectVoid("SSL_set_session", NOT_NULL, null, NULL);
+ }
+
+ @Test
+ public void evpMethods() throws Throwable {
+ String[] illegalArgMethods = new String[] {
+ "EVP_AEAD_CTX_open_buf",
+ "EVP_AEAD_CTX_seal_buf",
+ "EVP_PKEY_new_RSA"
+ };
+ String[] nonThrowingMethods = new String[] {
+ "EVP_MD_CTX_destroy",
+ "EVP_PKEY_CTX_free",
+ "EVP_PKEY_free",
+ "EVP_CIPHER_CTX_free"
+ };
+
+ // All of the non-void EVP_ methods apart from the above should throw on a null
+ // first argument.
+ MethodFilter filter = MethodFilter.newBuilder("EVP methods")
+ .hasPrefix("EVP_")
+ .takesArguments()
+ .except(illegalArgMethods)
+ .except(nonThrowingMethods)
+ .expectSize(45)
+ .build();
+
+ testMethods(filter, NullPointerException.class);
+
+ filter = MethodFilter.nameFilter("EVP methods (IllegalArgument)", illegalArgMethods);
+ testMethods(filter, IllegalArgumentException.class);
+
+ filter = MethodFilter.nameFilter("EVP methods (non-throwing)", nonThrowingMethods);
+ testMethods(filter, null);
+ }
+
+ @Test
+ public void x509Methods() throws Throwable {
+ // A number of X509 methods have a native pointer as arg 0 and an
+ // OpenSSLX509Certificate or OpenSSLX509CRL as arg 1.
+ MethodFilter filter = MethodFilter.newBuilder("X509 methods")
+ .hasArgLength(2)
+ .hasArg(0, long.class)
+ .hasArg(1, conscryptClass("OpenSSLX509Certificate"),
+ conscryptClass("OpenSSLX509CRL"))
+ .expectSize(32)
+ .build();
+ // TODO(prb): test null second argument
+ testMethods(filter, NullPointerException.class);
+
+ // The rest of the X509 methods are somewhat ad hoc.
+ expectNPE("d2i_X509", (Object) null);
+
+ invokeAndExpect( conscryptThrowable("OpenSSLX509CertificateFactory$ParsingException"),
+ "d2i_X509", new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0});
+
+ expectNPE("d2i_X509_bio", NULL);
+ expectNPE("PEM_read_bio_X509", NULL);
+ expectNPE("ASN1_seq_pack_X509", (Object) null);
+
+ // TODO(prb): Check what this should really throw
+ // expectNPE("ASN1_seq_pack_X509", (Object) new long[] { NULL });
+
+ expectNPE("ASN1_seq_unpack_X509_bio", NULL);
+
+ //
+ expectNPE("X509_cmp", NULL, null, NULL, null);
+ expectNPE("X509_cmp", NOT_NULL, null, NULL, null);
+ expectNPE("X509_cmp", NULL, null, NOT_NULL, null);
+
+ expectNPE("X509_print_ex", NULL, NULL, null, NULL, NULL);
+ expectNPE("X509_print_ex", NOT_NULL, NULL, null, NULL, NULL);
+ expectNPE("X509_print_ex", NULL, NOT_NULL, null, NULL, NULL);
+ }
+
+ private void testMethods(MethodFilter filter, Class<? extends Throwable> exceptionClass)
+ throws Throwable {
+ List<Method> methods = filter.filter(methodMap.values());
+
+ for (Method method : methods) {
+ List<Object[]> argsLists = permuteArgs(method);
+ for (Object[] args : argsLists) {
+ invokeAndExpect(exceptionClass, method, args);
+ }
+ }
+ }
+
+ private List<Object[]> permuteArgs(Method method) {
+ // For now just supply 0 for integral types and null for everything else
+ // TODO: allow user defined strategy, e.g. if two longs passed as native refs,
+ // generate {NULL,NULL}, {NULL,NOT_NULL}, {NOT_NULL,NULL} to test both null checks
+ List<Object[]> result = new ArrayList<>(1);
+
+ Class<?>[] argTypes = method.getParameterTypes();
+
+ int argCount = argTypes.length;
+ assertTrue(argCount > 0);
+ Object[] args = new Object[argCount];
+
+ for (int arg = 0; arg < argCount; arg++) {
+ if (argTypes[arg] == int.class) {
+ args[arg] = 0;
+ } else if (argTypes[arg] == long.class) {
+ args[arg] = NULL;
+ } else if (argTypes[arg] == boolean.class) {
+ args[arg] = false;
+ } else {
+ args[arg] = null;
+ }
+ }
+ result.add(args);
+ return result;
+ }
+
+ private void expectVoid(String methodName, Object... args) throws Throwable {
+ invokeAndExpect(null, methodName, args);
+ }
+
+ private void expectNPE(String methodName, Object... args) throws Throwable {
+ invokeAndExpect(NullPointerException.class, methodName, args);
+ }
+
+ private void invokeAndExpect(Class<? extends Throwable> expectedThrowable, String methodName,
+ Object... args) throws Throwable {
+ Method method = methodMap.get(methodName);
+ assertNotNull(method);
+ assertEquals(methodName, method.getName());
+ invokeAndExpect(expectedThrowable, method, args);
+ }
+
+ private void invokeAndExpect(Class<? extends Throwable> expectedThrowable, Method method,
+ Object... args) throws Throwable {
+ try {
+ method.invoke(null, args);
+ if (expectedThrowable != null) {
+ fail("No exception thrown by method " + method.getName());
+ }
+ } catch (IllegalAccessException e) {
+ throw new AssertionError("Illegal access", e);
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (expectedThrowable != null) {
+ assertEquals("Method: " + method.getName(), expectedThrowable, cause.getClass());
+ } else {
+ throw cause;
+ }
+ }
+ testedMethods.add(method.getName());
+ }
+
+ @SuppressWarnings("unchecked")
+ private Class<? extends Throwable> conscryptThrowable(String name) {
+ Class<?> klass = conscryptClass(name);
+ assertNotNull(klass);
+ assertTrue(Throwable.class.isAssignableFrom(klass));
+ return (Class<? extends Throwable>) klass;
+ }
+
+ private Class<?> conscryptClass(String className) {
+ return classCache.computeIfAbsent(className, s -> {
+ try {
+ return Class.forName(CONSCRYPT_PACKAGE + className);
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ });
+ }
+
+ private Map<String, Method> buildMethodMap() {
+ Map<String, Method> classMap = new HashMap<>();
+ assertNotNull(classMap);
+ Class<?> nativeCryptoClass = conscryptClass("NativeCrypto");
+ assertNotNull(nativeCryptoClass);
+ for (Method method : nativeCryptoClass.getDeclaredMethods()) {
+ int modifiers = method.getModifiers();
+ if (!Modifier.isNative(modifiers)) {
+ continue;
+ }
+ method.setAccessible(true);
+ classMap.put(method.getName(), method);
+ }
+ return classMap;
+ }
+}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSA.java b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSA.java
index 1c51b59..b4287f9 100644
--- a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSA.java
+++ b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSA.java
@@ -43,7 +43,6 @@
import java.util.List;
import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule;
import org.junit.ClassRule;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
@@ -72,7 +71,6 @@
}
@Test
- @Ignore("b/209335673 Base image has fix for b/191150645 but release version of module does not")
public void getEncodedFailsWhenCrtValuesMissing() throws Exception {
PrivateKey privateKey = getPrivateKey();
try {
@@ -124,7 +122,6 @@
}
@Test
- @Ignore("b/209335673 Base image has fix for b/191150645 but release version of module does not")
public void javaSerialization() throws Exception {
PrivateKey privatekey = getPrivateKey();
@@ -139,13 +136,10 @@
assertEquals(privatekey, copy);
}
- // b/209335673 Base image has fix for b/191150645 but release version of module does not,
- // @Override
- // protected List<KeyPair> getKeys() throws NoSuchAlgorithmException, InvalidKeySpecException {
- // return Arrays.asList(
- // new KeyPair(DefaultKeys.getPublicKey(algorithmName), getPrivateKey())
- // );
- // }
+ @Override
+ protected List<KeyPair> getKeys() throws NoSuchAlgorithmException, InvalidKeySpecException {
+ return Arrays.asList(new KeyPair(DefaultKeys.getPublicKey(algorithmName), getPrivateKey()));
+ }
// The private RSA key returned by DefaultKeys.getPrivateKey() is built from a PKCS#8
// KeySpec and so will be an instance of RSAPrivateCrtKey, but we want to test RSAPrivateKey
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSACrt.java b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSACrt.java
index d8d8ebe..5e84e7c 100644
--- a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSACrt.java
+++ b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSACrt.java
@@ -31,7 +31,6 @@
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -64,8 +63,7 @@
@Test
public void testExtraBufferSpace_Private() throws Exception {
PrivateKey privateKey = DefaultKeys.getPrivateKey("RSA");
- // b/209335673 Base image has fix for b/191150645 but release version of module does not
- // assertTrue(privateKey instanceof RSAPrivateCrtKey);
+ assertTrue(privateKey instanceof RSAPrivateCrtKey);
byte[] encoded = privateKey.getEncoded();
byte[] longBuffer = new byte[encoded.length + 147];
@@ -76,7 +74,6 @@
}
@Test
- @Ignore("b/209335673 Base image has fix for b/191150645 but release version of module does not")
public void javaSerialization() throws Exception {
PrivateKey privateKey = DefaultKeys.getPrivateKey("RSA");
assertTrue(privateKey instanceof RSAPrivateCrtKey);
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/CipherTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/CipherTest.java
index 0a762d2..3956c83 100644
--- a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/CipherTest.java
+++ b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/CipherTest.java
@@ -152,6 +152,29 @@
|| algorithm.equals("AES/OFB/PKCS7PADDING"))) {
return false;
}
+
+ if (provider.equals("BC")) {
+ return isSupportedByBC(algorithm);
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks for algorithms removed from BC in Android 12 and so not usable for these
+ * tests.
+ *
+ * TODO(prb): make this version aware, as this test runs against BC on older Android
+ * versions via MTS and should continue to test these algorithms there.
+ *
+ */
+ private static boolean isSupportedByBC(String algorithm) {
+ String[] removedBcPrefices = new String[] {"AES/ECB", "AES/CBC", "AES/GCM"};
+ for (String prefix : removedBcPrefices) {
+ if (algorithm.startsWith(prefix)) {
+ return false;
+ }
+ }
return true;
}
@@ -1084,7 +1107,9 @@
for (String padding : paddings) {
final String algorithmName = algorithm + "/" + mode + "/" + padding;
try {
- test_Cipher_Algorithm(provider, algorithmName);
+ if (isSupported(algorithmName, provider.getName())) {
+ test_Cipher_Algorithm(provider, algorithmName);
+ }
} catch (Throwable e) {
out.append("Error encountered checking " + algorithmName
+ " with provider " + provider.getName() + "\n");
@@ -3470,6 +3495,9 @@
if (provider.equals("SunJCE") && transformation.endsWith("/PKCS7PADDING")) {
return false;
}
+ if (provider.equals("BC")) {
+ return isSupportedByBC(transformation);
+ }
return true;
}
}
@@ -4098,6 +4126,9 @@
final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
PrintStream out = new PrintStream(errBuffer);
for (CipherTestParam p : CIPHER_TEST_PARAMS) {
+ if (!p.compatibleWith(provider)) {
+ continue;
+ }
try {
checkCipher_ShortBlock_Failure(p, provider);
} catch (Exception e) {
@@ -4297,32 +4328,6 @@
}
}
- // Test that when reading GCM parameters encoded using ASN1, a value for the tag size
- // not present indicates a value of 12.
- // https://b/29876633
- @Test
- public void test_DefaultGCMTagSizeAlgorithmParameterSpec() throws Exception {
- Assume.assumeNotNull(Security.getProvider("BC"));
- final String AES = "AES";
- final String AES_GCM = "AES/GCM/NoPadding";
- byte[] input = new byte[16];
- byte[] key = new byte[16];
- Cipher cipher = Cipher.getInstance(AES_GCM, "BC");
- AlgorithmParameters param = AlgorithmParameters.getInstance("GCM");
- param.init(new byte[] {
- (byte) 48, // DER encoding : tag_Sequence
- (byte) 14, // DER encoding : total length
- (byte) 4, // DER encoding : tag_OctetString
- (byte) 12, // DER encoding : counter length
- (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
- (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0 });
- cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, AES), param);
- byte[] ciphertext = cipher.update(input);
- assertEquals(16, ciphertext.length);
- byte[] tag = cipher.doFinal();
- assertEquals(12, tag.length);
- }
-
@Test
public void testAES_ECB_PKCS5Padding_ShortBuffer_Failure() throws Exception {
for (String provider : AES_PROVIDERS) {
@@ -4385,7 +4390,11 @@
}
private void testAES_ECB_NoPadding_IncrementalUpdate_Success(String provider) throws Exception {
- Cipher c = Cipher.getInstance("AES/ECB/NoPadding", provider);
+ String algorithm = "AES/ECB/NoPadding";
+ if (!isSupported(algorithm, provider)) {
+ return;
+ }
+ Cipher c = Cipher.getInstance(algorithm, provider);
assertEquals(provider, c.getProvider().getName());
c.init(Cipher.ENCRYPT_MODE, AES_128_KEY);
@@ -4419,7 +4428,11 @@
}
private void testAES_ECB_NoPadding_IvParameters_Failure(String provider) throws Exception {
- Cipher c = Cipher.getInstance("AES/ECB/NoPadding", provider);
+ String algorithm = "AES/ECB/NoPadding";
+ if (!isSupported(algorithm, provider)) {
+ return;
+ }
+ Cipher c = Cipher.getInstance(algorithm, provider);
AlgorithmParameterSpec spec = new IvParameterSpec(AES_IV_ZEROES);
try {
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java
index ade5447..7f44a34 100644
--- a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java
+++ b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java
@@ -87,6 +87,10 @@
@Test
public void test_XDHKeyAgreement() throws Exception {
for (Provider p : Security.getProviders("KeyAgreement.XDH")) {
+ // Skip testing Android Keystore as it's covered by CTS tests.
+ if ("AndroidKeyStore".equals(p.getName())) {
+ continue;
+ }
setupKeys(p);
KeyAgreement ka = KeyAgreement.getInstance("XDH", p);
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketTest.java
index 5a556fd..197674b 100644
--- a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketTest.java
+++ b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketTest.java
@@ -73,7 +73,6 @@
import javax.net.ssl.X509ExtendedTrustManager;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -426,7 +425,6 @@
* lower span of contiguous protocols is used in practice.
*/
@Test
- @Ignore("Needs TLS 1.0 or 1.1 which are scheduled for deprecation")
public void test_SSLSocket_noncontiguousProtocols_useLower() throws Exception {
TestSSLContext c = TestSSLContext.create();
SSLContext clientContext = c.clientContext;
@@ -460,7 +458,6 @@
* for both client and server isn't supported by the other.
*/
@Test
- @Ignore("Needs TLS 1.0 or 1.1 which are scheduled for deprecation")
public void test_SSLSocket_noncontiguousProtocols_canNegotiate() throws Exception {
TestSSLContext c = TestSSLContext.create();
SSLContext clientContext = c.clientContext;
@@ -1015,7 +1012,6 @@
// Confirms that communication without the TLS_FALLBACK_SCSV cipher works as it always did.
@Test
- @Ignore("Needs TLS 1.0 or 1.1 which are scheduled for deprecation")
public void test_SSLSocket_sendsNoTlsFallbackScsv_Fallback_Success() throws Exception {
TestSSLContext context = TestSSLContext.create();
// TLS_FALLBACK_SCSV is only applicable to TLS <= 1.2
@@ -1056,7 +1052,6 @@
}
@Test
- @Ignore("Needs TLS 1.0 or 1.1 which are scheduled for deprecation")
public void test_SSLSocket_sendsTlsFallbackScsv_InappropriateFallback_Failure()
throws Exception {
TestSSLContext context = TestSSLContext.create();
@@ -1110,7 +1105,6 @@
}
@Test
- @Ignore("Needs TLS 1.0 or 1.1 which are scheduled for deprecation")
public void test_SSLSocket_tlsFallback_byVersion() throws Exception {
String[] supportedProtocols =
SSLContext.getDefault().getDefaultSSLParameters().getProtocols();
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/TrustedCertificateStore.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/TrustedCertificateStore.java
index 16128b9..3d71e47 100644
--- a/repackaged/platform/src/main/java/com/android/org/conscrypt/TrustedCertificateStore.java
+++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/TrustedCertificateStore.java
@@ -127,10 +127,9 @@
private final File addedDir;
private final File deletedDir;
- @android.compat.annotation
- .UnsupportedAppUsage
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- public TrustedCertificateStore() {
+ @android.compat.annotation.UnsupportedAppUsage
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ public TrustedCertificateStore() {
this(PreloadHolder.defaultCaCertsSystemDir, PreloadHolder.defaultCaCertsAddedDir,
PreloadHolder.defaultCaCertsDeletedDir);
}
@@ -498,10 +497,9 @@
* @throws CertificateException if there was a problem parsing the
* certificates
*/
- @android.compat.annotation
- .UnsupportedAppUsage
- @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
- public List<X509Certificate> getCertificateChain(X509Certificate leaf)
+ @android.compat.annotation.UnsupportedAppUsage
+ @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+ public List<X509Certificate> getCertificateChain(X509Certificate leaf)
throws CertificateException {
final LinkedHashSet<OpenSSLX509Certificate> chain
= new LinkedHashSet<OpenSSLX509Certificate>();
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/MethodFilter.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/MethodFilter.java
new file mode 100644
index 0000000..680abd7
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/MethodFilter.java
@@ -0,0 +1,201 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+
+/**
+ * Test support class for filtering collections of {@link Method}. Each filter is a list of
+ * predicates which must all be true for a Method in order for it to be included it the output.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class MethodFilter {
+ private final String name;
+ private final CompoundMethodPredicate predicates = new CompoundMethodPredicate();
+ private int expectedSize = 0;
+
+ public MethodFilter(String name) {
+ this.name = name;
+ }
+
+ public List<Method> filter(Iterable<Method> input) {
+ List<Method> result = new ArrayList<>();
+ for (Method method : input) {
+ if (predicates.test(method)) {
+ result.add(method);
+ }
+ }
+ if (expectedSize != 0) {
+ assertTrue(String.format("Filter %s only returned %d methods, expected at least %d",
+ name, result.size(), expectedSize), result.size() >= expectedSize);
+ }
+ return result;
+ }
+
+ /** Returns a new {@link Builder} */
+ public static Builder newBuilder(String name) {
+ return new Builder(name);
+ }
+
+ /** Returns a filter which selects only methods named in {@code methodNames} */
+ public static MethodFilter nameFilter(String name, String... methodNames) {
+ return newBuilder(name)
+ .named(methodNames)
+ .expectSize(methodNames.length)
+ .build();
+ }
+
+ private void addPredicate(Predicate<Method> predicate) {
+ predicates.add(predicate);
+ }
+
+ /**
+ * @hide This class is not part of the Android public SDK API
+ */
+ public static class Builder {
+ private final MethodFilter filter;
+
+ private Builder(String name) {
+ filter = new MethodFilter(name);
+ }
+
+ /** Method's simple name must start with {@code prefix}. */
+ public Builder hasPrefix(String prefix) {
+ filter.addPredicate(new MethodNamePrefixPredicate(prefix));
+ return this;
+ }
+
+ /** Argument at {@code position} must be one of the supplied {@code classes}. */
+ public Builder hasArg(int position, Class<?>... classes) {
+ filter.addPredicate(new MethodArgPredicate(position, classes));
+ return this;
+ }
+
+ /** Method must take exactly {@code length} args. */
+ public Builder hasArgLength(int length) {
+ filter.addPredicate(new MethodArgLengthPredicate(length));
+ return this;
+ }
+
+ /* Method must take one or more arguments, i.e. not void. */
+ public Builder takesArguments() {
+ filter.addPredicate(new MethodArgLengthPredicate(0).negate());
+ return this;
+ }
+
+ /** Method's simple name is in the list of {@code names} provided. */
+ public Builder named(String... names) {
+ filter.addPredicate(new MethodNamePredicate(names));
+ return this;
+ }
+
+ /** Method's simple name is NOT in the list of {@code names} provided. */
+ public Builder except(String... names) {
+ filter.addPredicate(new MethodNamePredicate(names).negate());
+ return this;
+ }
+
+ /** Expect at least {@code size} matching methods when filtering, otherwise filter()
+ * will throw {@code AssertionError} */
+ public Builder expectSize(int size) {
+ filter.expectedSize = size;
+ return this;
+ }
+
+ public MethodFilter build() {
+ return filter;
+ }
+ }
+
+ // Implements Builder.hasPrefix()
+ private static class MethodNamePrefixPredicate implements Predicate<Method> {
+ private final String prefix;
+
+ public MethodNamePrefixPredicate(String prefix) {
+ this.prefix = prefix;
+ }
+
+ @Override
+ public boolean test(Method method) {
+ return method.getName().startsWith(prefix);
+ }
+ }
+
+ // Implements Builder.named()
+ private static class MethodNamePredicate implements Predicate<Method> {
+ private final List<String> names;
+
+ public MethodNamePredicate(String... names) {
+ this.names = Arrays.asList(names);
+ }
+
+ @Override
+ public boolean test(Method method) {
+ return names.contains(method.getName());
+ }
+ }
+
+ // Implements Builder.hasArg()
+ private static class MethodArgPredicate implements Predicate<Method> {
+ private final int position;
+ private final List<Class<?>> allowedClasses;
+
+ public MethodArgPredicate(int position, Class<?>... classes) {
+ this.position = position;
+ allowedClasses = Arrays.asList(classes);
+ }
+
+ @Override
+ public boolean test(Method method) {
+ Class<?>[] argTypes = method.getParameterTypes();
+ if (argTypes.length > position) {
+ for (Class<?> c : allowedClasses) {
+ if (argTypes[position] == c) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ // Implements Builder.hasArgLength()
+ private static class MethodArgLengthPredicate implements Predicate<Method> {
+ private final int length;
+
+ public MethodArgLengthPredicate(int length) {
+ this.length = length;
+ }
+
+ @Override
+ public boolean test(Method method) {
+ return method.getParameterCount() == length;
+ }
+ }
+
+ // A Predicate which contains a list of sub-Predicates, all of which must be true
+ // for this one to be true.
+ private static class CompoundMethodPredicate implements Predicate<Method> {
+ private final List<Predicate<Method>> predicates = new ArrayList<>();
+
+ @Override
+ public boolean test(Method method) {
+ for (Predicate<Method> p : predicates) {
+ if (!p.test(method)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void add(Predicate<Method> predicate) {
+ predicates.add(predicate);
+ }
+ }
+}
\ No newline at end of file
diff --git a/testing/src/main/java/org/conscrypt/MethodFilter.java b/testing/src/main/java/org/conscrypt/MethodFilter.java
new file mode 100644
index 0000000..0939d4b
--- /dev/null
+++ b/testing/src/main/java/org/conscrypt/MethodFilter.java
@@ -0,0 +1,196 @@
+package org.conscrypt;
+
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+
+/**
+ * Test support class for filtering collections of {@link Method}. Each filter is a list of
+ * predicates which must all be true for a Method in order for it to be included it the output.
+ */
+public class MethodFilter {
+ private final String name;
+ private final CompoundMethodPredicate predicates = new CompoundMethodPredicate();
+ private int expectedSize = 0;
+
+ public MethodFilter(String name) {
+ this.name = name;
+ }
+
+ public List<Method> filter(Iterable<Method> input) {
+ List<Method> result = new ArrayList<>();
+ for (Method method : input) {
+ if (predicates.test(method)) {
+ result.add(method);
+ }
+ }
+ if (expectedSize != 0) {
+ assertTrue(String.format("Filter %s only returned %d methods, expected at least %d",
+ name, result.size(), expectedSize), result.size() >= expectedSize);
+ }
+ return result;
+ }
+
+ /** Returns a new {@link Builder} */
+ public static Builder newBuilder(String name) {
+ return new Builder(name);
+ }
+
+ /** Returns a filter which selects only methods named in {@code methodNames} */
+ public static MethodFilter nameFilter(String name, String... methodNames) {
+ return newBuilder(name)
+ .named(methodNames)
+ .expectSize(methodNames.length)
+ .build();
+ }
+
+ private void addPredicate(Predicate<Method> predicate) {
+ predicates.add(predicate);
+ }
+
+ public static class Builder {
+ private final MethodFilter filter;
+
+ private Builder(String name) {
+ filter = new MethodFilter(name);
+ }
+
+ /** Method's simple name must start with {@code prefix}. */
+ public Builder hasPrefix(String prefix) {
+ filter.addPredicate(new MethodNamePrefixPredicate(prefix));
+ return this;
+ }
+
+ /** Argument at {@code position} must be one of the supplied {@code classes}. */
+ public Builder hasArg(int position, Class<?>... classes) {
+ filter.addPredicate(new MethodArgPredicate(position, classes));
+ return this;
+ }
+
+ /** Method must take exactly {@code length} args. */
+ public Builder hasArgLength(int length) {
+ filter.addPredicate(new MethodArgLengthPredicate(length));
+ return this;
+ }
+
+ /* Method must take one or more arguments, i.e. not void. */
+ public Builder takesArguments() {
+ filter.addPredicate(new MethodArgLengthPredicate(0).negate());
+ return this;
+ }
+
+ /** Method's simple name is in the list of {@code names} provided. */
+ public Builder named(String... names) {
+ filter.addPredicate(new MethodNamePredicate(names));
+ return this;
+ }
+
+ /** Method's simple name is NOT in the list of {@code names} provided. */
+ public Builder except(String... names) {
+ filter.addPredicate(new MethodNamePredicate(names).negate());
+ return this;
+ }
+
+ /** Expect at least {@code size} matching methods when filtering, otherwise filter()
+ * will throw {@code AssertionError} */
+ public Builder expectSize(int size) {
+ filter.expectedSize = size;
+ return this;
+ }
+
+ public MethodFilter build() {
+ return filter;
+ }
+ }
+
+ // Implements Builder.hasPrefix()
+ private static class MethodNamePrefixPredicate implements Predicate<Method> {
+ private final String prefix;
+
+ public MethodNamePrefixPredicate(String prefix) {
+ this.prefix = prefix;
+ }
+
+ @Override
+ public boolean test(Method method) {
+ return method.getName().startsWith(prefix);
+ }
+ }
+
+ // Implements Builder.named()
+ private static class MethodNamePredicate implements Predicate<Method> {
+ private final List<String> names;
+
+ public MethodNamePredicate(String... names) {
+ this.names = Arrays.asList(names);
+ }
+
+ @Override
+ public boolean test(Method method) {
+ return names.contains(method.getName());
+ }
+ }
+
+ // Implements Builder.hasArg()
+ private static class MethodArgPredicate implements Predicate<Method> {
+ private final int position;
+ private final List<Class<?>> allowedClasses;
+
+ public MethodArgPredicate(int position, Class<?>... classes) {
+ this.position = position;
+ allowedClasses = Arrays.asList(classes);
+ }
+
+ @Override
+ public boolean test(Method method) {
+ Class<?>[] argTypes = method.getParameterTypes();
+ if (argTypes.length > position) {
+ for (Class<?> c : allowedClasses) {
+ if (argTypes[position] == c) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ // Implements Builder.hasArgLength()
+ private static class MethodArgLengthPredicate implements Predicate<Method> {
+ private final int length;
+
+ public MethodArgLengthPredicate(int length) {
+ this.length = length;
+ }
+
+ @Override
+ public boolean test(Method method) {
+ return method.getParameterCount() == length;
+ }
+ }
+
+ // A Predicate which contains a list of sub-Predicates, all of which must be true
+ // for this one to be true.
+ private static class CompoundMethodPredicate implements Predicate<Method> {
+ private final List<Predicate<Method>> predicates = new ArrayList<>();
+
+ @Override
+ public boolean test(Method method) {
+ for (Predicate<Method> p : predicates) {
+ if (!p.test(method)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void add(Predicate<Method> predicate) {
+ predicates.add(predicate);
+ }
+ }
+}
\ No newline at end of file