[automerger skipped] Remove CT tests am: de68ba35b9 -s ours am skip reason: Merged-In I24e87ebcd5033c0a7d78ee203a318c398c4d85c5 with SHA-1 d29e52b96c is already in history Original change: https://android-review.googlesource.com/c/platform/external/conscrypt/+/3283516 Change-Id: I83032ad1bb3263a77a99b99afc2d81b2bb969a98 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.gitignore b/.gitignore index e065940..00eefa9 100644 --- a/.gitignore +++ b/.gitignore
@@ -1,6 +1,5 @@ # Gradle build -gradle.properties .gradle local.properties
diff --git a/.travis.yml b/.travis.yml index 20ee519..1910355 100644 --- a/.travis.yml +++ b/.travis.yml
@@ -23,7 +23,6 @@ - ANDROID_TOOLS_URL="https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip" - ANDROID_HOME="$HOME/android-sdk-linux" - JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 - - JAVA7_HOME=/usr/lib/jvm/java-7-openjdk-amd64 - JAVA11_HOME=/usr/lib/jvm/java-11-openjdk-amd64 - CC=clang-5.0 - CXX=clang++-5.0 @@ -42,7 +41,7 @@ - $ANDROID_HOME/tools/bin/sdkmanager 'build-tools;28.0.3' | tr '\r' '\n' | uniq - $ANDROID_HOME/tools/bin/sdkmanager 'platforms;android-26' | tr '\r' '\n' | uniq - $ANDROID_HOME/tools/bin/sdkmanager 'extras;android;m2repository' | tr '\r' '\n' | uniq - - $ANDROID_HOME/tools/bin/sdkmanager ndk-bundle | tr '\r' '\n' | uniq + - $ANDROID_HOME/tools/bin/sdkmanager 'ndk;21.3.6528147' | tr '\r' '\n' | uniq - $ANDROID_HOME/tools/bin/sdkmanager 'cmake;3.10.2.4988404' | tr '\r' '\n' | uniq - gimme 1.13 # Needed for BoringSSL build - source ~/.gimme/envs/go1.13.env @@ -62,7 +61,6 @@ - libc6-dev:i386 - linux-libc-dev - ninja-build - - openjdk-7-jre # for running tests with Java 7 - openjdk-8-jdk # for building - openjdk-11-jre # for running tests with Java 11 @@ -109,15 +107,7 @@ - ./gradlew build -PcheckErrorQueue - # Also test with Java 7 and 11 on linux - - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" != "false" ]]; - then - ${JAVA7_HOME}/bin/java -version; - fi - - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" != "false" ]]; - then - ./gradlew check -DjavaExecutable64=${JAVA7_HOME}/bin/java -PcheckErrorQueue; - fi + # Also test with Java 11 on linux - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" != "false" ]]; then ./gradlew check -DjavaExecutable64=${JAVA11_HOME}/bin/java -PcheckErrorQueue;
diff --git a/Android.bp b/Android.bp index 6d4472f..af41a1a 100644 --- a/Android.bp +++ b/Android.bp
@@ -18,6 +18,40 @@ default_visibility: [ ":__subpackages__", ], + default_applicable_licenses: ["external_conscrypt_license"], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// +// large-scale-change included anything that looked like it might be a license +// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc. +// +// Please consider removing redundant or irrelevant files from 'license_text:'. +// See: http://go/android-license-faq +license { + name: "external_conscrypt_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "legacy_unencumbered", + ], + license_text: [ + "LICENSE", + "NOTICE", + "licenses/**/*", + ], } // @@ -69,6 +103,8 @@ "common/src/jni/main/cpp/conscrypt/trace.cc", ], + header_libs: ["jni_headers"], + local_include_dirs: [ "common/src/jni/main/include", ], @@ -84,6 +120,8 @@ "common/src/jni/unbundled/include", ], + header_libs: ["jni_headers"], + shared_libs: [ "liblog", ], @@ -106,6 +144,11 @@ cc_library_host_shared { name: "libconscrypt_openjdk_jni", + visibility: [ + "//build/make/tools/signapk", + "//tools/apksig", + "//vendor:__subpackages__", + ], defaults: ["conscrypt_global"], cflags: [ @@ -189,9 +232,7 @@ java_library { name: "conscrypt", visibility: [ - "//art/build", "//device:__subpackages__", - "//external/robolectric-shadows", "//system/apex/tests", ":__subpackages__", ], @@ -203,7 +244,9 @@ min_sdk_version: "29", installable: true, - hostdex: true, + // Hostdex is only for ART testing on host: ART build file has its + // own hostdex support for conscrypt. + hostdex: false, srcs: [ ":conscrypt_java_files", @@ -218,18 +261,11 @@ system_modules: "art-module-intra-core-api-stubs-system-modules", patch_module: "java.base", - // Workaround for b/124476339: libjavacrypto is required for both APEX and - // hostdex builds, but adding a top-level required property results in - // it being installed to /system on Android. - // TODO(b/124476339): move required back to a top level property target: { // boringssl_self_test needed in both /system/bin and /apex/com.android.conscrypt/bin android: { required: ["boringssl_self_test"], }, - hostdex: { - required: ["libjavacrypto"], - }, }, permitted_packages: [ @@ -240,6 +276,20 @@ plugins: ["java_api_finder"], } +// Java library for use on host, e.g. by robolectric. +java_library { + name: "conscrypt-for-host", + visibility: [ + "//art/build", + "//external/robolectric-shadows", + ], + static_libs: [ + "conscrypt", + ], + sdk_version: "none", + system_modules: "none", +} + // Referenced implicitly from conscrypt.module.platform.api. filegroup { name: "conscrypt.module.platform.api.api.public.latest", @@ -256,6 +306,14 @@ ], } +// Referenced implicitly from conscrypt.module.platform.api. +filegroup { + name: "conscrypt.module.platform.api-incompatibilities.api.public.latest", + srcs: [ + "api/platform/last-incompatibilities.txt", + ], +} + // A library containing the core platform API stubs of the Conscrypt module. // // Core platform APIs are only intended for use of other parts of the platform, not the @@ -264,71 +322,71 @@ // The API specification .txt files managed by this only contain the additional // classes/members that are in the platform API but which are not in the public // API. +// +// Note that this entire API surface is considered stable in the sense described in +// libcore/mmodules/core_platform_api/Android.bp. java_sdk_library { name: "conscrypt.module.platform.api", visibility: [ "//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 + // locations. + "//prebuilts:__subpackages__", ], srcs: [ ":conscrypt_java_files", ], api_dir: "api/platform", api_only: true, + api_lint: { + enabled: true, + }, droiddoc_options: [ "--hide-annotation libcore.api.Hide", - "--show-single-annotation libcore.api.CorePlatformApi", + "--show-single-annotation libcore.api.CorePlatformApi\\(status=libcore.api.CorePlatformApi.Status.STABLE\\)", "--skip-annotation-instance-methods=false", ], hostdex: true, sdk_version: "none", - system_modules: "art-module-platform-api-stubs-system-modules", -} + system_modules: "art-module-lib-api-stubs-system-modules", -// A guaranteed unstripped version of conscrypt. -// The build system may or may not strip the conscrypt jar, but this one will -// not be stripped. See b/24535627. -java_library { - name: "conscrypt-testdex", - installable: true, - - static_libs: ["conscrypt"], - dex_preopt: { - enabled: false, - }, - - sdk_version: "core_platform", - - required: ["libjavacrypto"], -} - -// Referenced implicitly from conscrypt.module.public.api. -filegroup { - name: "conscrypt.module.public.api.api.public.latest", - srcs: [ - "api/public/last-api.txt", - ], -} - -// Referenced implicitly from conscrypt.module.public.api. -filegroup { - name: "conscrypt.module.public.api-removed.api.public.latest", - srcs: [ - "api/public/last-removed.txt", - ], + dist_group: "android", + dist_stem: "conscrypt-coreplatform", + // TODO: remove this when Conscrypt's @CorePlatformApi has been migrated to @SystemApi + unsafe_ignore_missing_latest_api: true, } // A library containing the public API stubs of the Conscrypt module. java_sdk_library { name: "conscrypt.module.public.api", visibility: [ + "//packages/modules/common/sdk", "//frameworks/base", + "//frameworks/base/api", "//libcore", + // 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 + // this module. + // TODO(b/155921753): Restrict this when prebuilts are in their proper + // locations. + "//prebuilts:__subpackages__", ], srcs: [ ":conscrypt_public_api_files", ], + + // The base name for the artifacts that are automatically published to the + // dist and which end up in one of the sub-directories of prebuilts/sdk. + // As long as this matches the name of the artifacts in prebuilts/sdk then + // the API will be checked for compatibility against the latest released + // version of the API. + dist_stem: "conscrypt", + api_dir: "api/public", api_only: true, droiddoc_options: [ @@ -340,6 +398,7 @@ sdk_version: "none", system_modules: "art-module-public-api-stubs-system-modules", + dist_group: "android", } // Referenced implicitly from conscrypt.module.intra.core.api. @@ -358,6 +417,14 @@ ], } +// Referenced implicitly from conscrypt.module.intra.core.api. +filegroup { + name: "conscrypt.module.intra.core.api-incompatibilities.api.public.latest", + srcs: [ + "api/intra/last-incompatibilities.txt", + ], +} + // A library containing the intra-core API stubs of the Conscrypt module. // // Intra-core APIs are only intended for the use of other core library modules. @@ -369,6 +436,11 @@ visibility: [ "//external/okhttp", "//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 + // locations. + "//prebuilts:__subpackages__", ], srcs: [ ":conscrypt_java_files", @@ -384,6 +456,9 @@ sdk_version: "none", system_modules: "art-module-intra-core-api-stubs-system-modules", + + // Don't copy any output files to the dist. + no_dist: true, } // Platform conscrypt crypto JNI library @@ -399,6 +474,7 @@ ], srcs: ["common/src/jni/main/cpp/**/*.cc"], + header_libs: ["jni_headers"], local_include_dirs: ["common/src/jni/main/include"], } @@ -500,6 +576,7 @@ "core-test-rules", "junit", "mockito-target-minus-junit4", + "framework-statsd.stubs.module_lib", ], static_libs: [ @@ -513,8 +590,16 @@ //"-Xlint:-serial,-deprecation,-unchecked", ], - required: ["libjavacrypto"], - java_version: "1.7", + target: { + host: { + required: ["libjavacrypto"], + }, + darwin: { + // required module "libjavacrypto" is disabled on darwin + enabled: false, + }, + }, + java_version: "1.8", } // Make the conscrypt-benchmarks library. @@ -542,13 +627,24 @@ //"-Xlint:-serial,-deprecation,-unchecked", ], - required: ["libjavacrypto"], + target: { + host: { + required: ["libjavacrypto"], + }, + darwin: { + // required module "libjavacrypto" is disabled on darwin + enabled: false, + }, + }, java_version: "1.7", } // Device SDK exposed by the Conscrypt module. sdk { name: "conscrypt-module-sdk", + bootclasspath_fragments: [ + "com.android.conscrypt-bootclasspath-fragment", + ], java_sdk_libs: [ "conscrypt.module.public.api", "conscrypt.module.intra.core.api", @@ -567,19 +663,38 @@ java_libs: [ "conscrypt-unbundled", ], + native_shared_libs: [ + "libconscrypt_openjdk_jni", + ], } // Test libraries exposed by the Conscrypt module. module_exports { name: "conscrypt-module-test-exports", - java_libs: [ - // For use by robolectric. - "conscrypt", - // For use by art tests - "conscrypt-testdex", - ], - java_tests: [ - // For use by CTS - "conscrypt-tests", - ], + host_supported: true, + target: { + android: { + java_libs: [ + // For use by robolectric and ART tests. + "conscrypt-for-host", + ], + java_tests: [ + // For use by CTS + "conscrypt-tests", + ], + // TODO: Remove this when we resolve b/151303681. + native_shared_libs: [ + "libjavacrypto", + ], + }, + darwin: { + enabled: false, + }, + // For use by ART tests on host. + not_windows: { + native_shared_libs: [ + "libjavacrypto", + ], + }, + }, }
diff --git a/CAPABILITIES.md b/CAPABILITIES.md index 0be1621..b043f63 100644 --- a/CAPABILITIES.md +++ b/CAPABILITIES.md
@@ -89,6 +89,7 @@ * `AES/CTR/NoPadding` * `AES/ECB/NoPadding` * `AES/ECB/PKCS5Padding` +* `AES/GCM-SIV/NoPadding` AES with 128, 192, or 256-bit keys. @@ -101,11 +102,13 @@ * `AES_128/ECB/NoPadding` * `AES_128/ECB/PKCS5Padding` * `AES_128/GCM/NoPadding` +* `AES_128/GCM-SIV/NoPadding` * `AES_256/CBC/NoPadding` * `AES_256/CBC/PKCS5Padding` * `AES_256/ECB/NoPadding` * `AES_256/ECB/PKCS5Padding` * `AES_256/GCM/NoPadding` +* `AES_256/GCM-SIV/NoPadding` Key-restricted versions of the AES ciphers.
diff --git a/IMPLEMENTATION_NOTES.md b/IMPLEMENTATION_NOTES.md index 3c40dea..87c89aa 100644 --- a/IMPLEMENTATION_NOTES.md +++ b/IMPLEMENTATION_NOTES.md
@@ -13,14 +13,20 @@ ## Hostname Verification -Conscrypt's hostname verification (enabled by +Prior to version 2.5.0 Conscrypt's hostname verification (enabled by [`setEndpointIdentificationAlgorithm("HTTPS")`](https://docs.oracle.com/javase/9/docs/api/javax/net/ssl/SSLParameters.html#setEndpointIdentificationAlgorithm-java.lang.String-)) -defers entirely to the hostname verifier. The default `HostnameVerifier` on -OpenJDK always fails, so a `HostnameVerifier` or `ConscryptHostnameVerifier` -must be set to use hostname verification on OpenJDK. On Android, the default +defers entirely to the underlying platform's `HttpsURLConnection` hostname verifier. + +The default `HostnameVerifier` on OpenJDK rejects all hostnames, and +so a `HostnameVerifier` or `ConscryptHostnameVerifier` +must be set in order to use hostname verification on OpenJDK. On Android, the default `HostnameVerifier` performs [RFC 2818](https://tools.ietf.org/html/rfc2818) hostname validation, so it will work out of the box. +As of version 2.5.0, Conscrypt ships with its own default `ConscryptHostnameVerifier` +and this is used on both Android and OpenJDK. It performs RFC 2818 verification +and is equivalent to the system `HostnameVerifier` on Android 10 and 11. + ## AEAD Ciphers Conscrypt's AEAD ciphers do not support incremental processing (i.e. they will
diff --git a/METADATA b/METADATA new file mode 100644 index 0000000..d97975c --- /dev/null +++ b/METADATA
@@ -0,0 +1,3 @@ +third_party { + license_type: NOTICE +}
diff --git a/OWNERS b/OWNERS index b2ed0be..6a33b41 100644 --- a/OWNERS +++ b/OWNERS
@@ -1,7 +1,7 @@ # Bug component: 684135 dauletz@google.com kroot@google.com +mingaleev@google.com narayan@google.com -nfuller@google.com +ngeoffray@google.com prb@google.com -tobiast@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 84d310f..7d08c6f 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg
@@ -2,3 +2,7 @@ commit_msg_test_field = true clang_format = true bpfmt = true + +[Hook Scripts] + +hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
diff --git a/README.md b/README.md index dbc2808..c63c5ed 100644 --- a/README.md +++ b/README.md
@@ -68,7 +68,7 @@ <dependency> <groupId>org.conscrypt</groupId> <artifactId>conscrypt-openjdk</artifactId> - <version>2.4.0</version> + <version>2.5.2</version> <classifier>${os.detected.classifier}</classifier> </dependency> ``` @@ -91,7 +91,7 @@ apply plugin: "com.google.osdetector" dependencies { - compile 'org.conscrypt:conscrypt-openjdk:2.4.0:' + osdetector.classifier + compile 'org.conscrypt:conscrypt-openjdk:2.5.2:' + osdetector.classifier } ``` @@ -109,14 +109,14 @@ <dependency> <groupId>org.conscrypt</groupId> <artifactId>conscrypt-openjdk-uber</artifactId> - <version>2.4.0</version> + <version>2.5.2</version> </dependency> ``` ##### Gradle ```gradle dependencies { - compile 'org.conscrypt:conscrypt-openjdk-uber:2.4.0' + compile 'org.conscrypt:conscrypt-openjdk-uber:2.5.2' } ``` @@ -129,7 +129,7 @@ ```gradle dependencies { - implementation 'org.conscrypt:conscrypt-android:2.4.0' + implementation 'org.conscrypt:conscrypt-android:2.5.2' } ```
diff --git a/android-stub/build.gradle b/android-stub/build.gradle index 8355c94..f1a911f 100644 --- a/android-stub/build.gradle +++ b/android-stub/build.gradle
@@ -9,4 +9,4 @@ } // Disable the javadoc task. -tasks.withType(Javadoc).all { enabled = false } +tasks.withType(Javadoc).configureEach { enabled = false }
diff --git a/android-stub/src/main/java/android/util/StatsEvent.java b/android-stub/src/main/java/android/util/StatsEvent.java new file mode 100644 index 0000000..c24c4d9 --- /dev/null +++ b/android-stub/src/main/java/android/util/StatsEvent.java
@@ -0,0 +1,124 @@ +/* + * 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 android.util; + +@SuppressWarnings("unused") +public final class StatsEvent { + private StatsEvent(int atomId, StatsEvent.Buffer buffer, byte[] payload, int numBytes) { + throw new RuntimeException("Stub!"); + } + + public static StatsEvent.Builder newBuilder() { + throw new RuntimeException("Stub!"); + } + + public int getAtomId() { + throw new RuntimeException("Stub!"); + } + + public byte[] getBytes() { + throw new RuntimeException("Stub!"); + } + + public int getNumBytes() { + throw new RuntimeException("Stub!"); + } + + public void release() { + throw new RuntimeException("Stub!"); + } + + private static final class Buffer { + private static StatsEvent.Buffer obtain() { + throw new RuntimeException("Stub!"); + } + + private Buffer() { + throw new RuntimeException("Stub!"); + } + } + + public static final class Builder { + private Builder(StatsEvent.Buffer buffer) { + throw new RuntimeException("Stub!"); + } + + public StatsEvent.Builder setAtomId(int atomId) { + throw new RuntimeException("Stub!"); + } + + public StatsEvent.Builder writeBoolean(boolean value) { + throw new RuntimeException("Stub!"); + } + + public StatsEvent.Builder writeInt(int value) { + throw new RuntimeException("Stub!"); + } + + public StatsEvent.Builder writeLong(long value) { + throw new RuntimeException("Stub!"); + } + + public StatsEvent.Builder writeFloat(float value) { + throw new RuntimeException("Stub!"); + } + + public StatsEvent.Builder writeString(String value) { + throw new RuntimeException("Stub!"); + } + + public StatsEvent.Builder writeByteArray(byte[] value) { + throw new RuntimeException("Stub!"); + } + + private void writeByteArray(byte[] value, byte typeId) { + throw new RuntimeException("Stub!"); + } + + public StatsEvent.Builder writeAttributionChain(int[] uids, String[] tags) { + throw new RuntimeException("Stub!"); + } + + public StatsEvent.Builder addBooleanAnnotation(byte annotationId, boolean value) { + throw new RuntimeException("Stub!"); + } + + public StatsEvent.Builder addIntAnnotation(byte annotationId, int value) { + throw new RuntimeException("Stub!"); + } + + public StatsEvent.Builder usePooledBuffer() { + throw new RuntimeException("Stub!"); + } + + public StatsEvent build() { + throw new RuntimeException("Stub!"); + } + + private void writeTypeId(byte typeId) { + throw new RuntimeException("Stub!"); + } + + private void writeAnnotationCount() { + throw new RuntimeException("Stub!"); + } + + private static byte[] stringToBytes(String value) { + throw new RuntimeException("Stub!"); + } + } +}
diff --git a/android-stub/src/main/java/javax/net/ssl/ExtendedSSLSession.java b/android-stub/src/main/java/javax/net/ssl/ExtendedSSLSession.java index aa49734..c899170 100644 --- a/android-stub/src/main/java/javax/net/ssl/ExtendedSSLSession.java +++ b/android-stub/src/main/java/javax/net/ssl/ExtendedSSLSession.java
@@ -23,7 +23,7 @@ */ public abstract class ExtendedSSLSession implements SSLSession { - public ExtendedSSLSession() { + protected ExtendedSSLSession() { } public abstract String[] getLocalSupportedSignatureAlgorithms();
diff --git a/android/build.gradle b/android/build.gradle index 38ea56d..732e52c 100644 --- a/android/build.gradle +++ b/android/build.gradle
@@ -21,6 +21,7 @@ androidVersionName = "$version" androidMinSdkVersion = 9 androidTargetSdkVersion = 26 + androidNdkVersion = "21.3.6528147" } if (androidSdkInstalled) { @@ -32,6 +33,7 @@ android { compileSdkVersion androidTargetSdkVersion + ndkVersion androidNdkVersion compileOptions { sourceCompatibility androidMinJavaVersion @@ -119,14 +121,15 @@ compileOnly project(':conscrypt-constants') } - task configureJavadocs { + def configureJavaDocs = tasks.register("configureJavadocs") { dependsOn configurations.publicApiDocs doLast { javadocs.options.docletpath = configurations.publicApiDocs.files as List } } - task javadocs(type: Javadoc, dependsOn: [configureJavadocs]) { + def javadocs = tasks.register("javadocs", Javadoc) { + dependsOn configureJavadocs source = android.sourceSets.main.java.srcDirs classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + project(':conscrypt-android-stub').sourceSets.main.output // TODO(nmittler): Fix the javadoc errors. @@ -142,12 +145,15 @@ } } - task javadocsJar(type: Jar, dependsOn: javadocs) { + def javadocsJar = tasks.register("javadocsJar", Jar) { + dependsOn javadocs classifier = 'javadoc' - from javadocs.destinationDir + from { + javadocs.get().destinationDir + } } - task sourcesJar(type: Jar) { + def sourcesJar = tasks.register("sourcesJar", Jar) { classifier = 'sources' from android.sourceSets.main.java.srcDirs } @@ -155,14 +161,14 @@ apply from: "$rootDir/gradle/publishing.gradle" publishing.publications.maven { from components.android - artifact sourcesJar - artifact javadocsJar + artifact sourcesJar.get() + artifact javadocsJar.get() } } else { logger.warn('Android SDK has not been detected. The Android module will not be built.') // Disable all tasks - tasks.collect { + tasks.configureEach { it.enabled = false } }
diff --git a/android/proguard-rules.pro b/android/proguard-rules.pro index 3bc75b2..e966c43 100644 --- a/android/proguard-rules.pro +++ b/android/proguard-rules.pro
@@ -17,6 +17,7 @@ -dontnote org.apache.harmony.security.utils.AlgNameMapper -dontnote sun.security.x509.AlgorithmId +-dontwarn android.util.StatsEvent -dontwarn dalvik.system.BlockGuard -dontwarn dalvik.system.BlockGuard$Policy -dontwarn dalvik.system.CloseGuard
diff --git a/android/src/main/java/org/conscrypt/Platform.java b/android/src/main/java/org/conscrypt/Platform.java index a739a78..68a7409 100644 --- a/android/src/main/java/org/conscrypt/Platform.java +++ b/android/src/main/java/org/conscrypt/Platform.java
@@ -19,6 +19,7 @@ import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.os.Build; +import android.os.SystemClock; import android.util.Log; import dalvik.system.BlockGuard; import dalvik.system.CloseGuard; @@ -59,6 +60,9 @@ import javax.net.ssl.X509TrustManager; import org.conscrypt.ct.CTLogStore; import org.conscrypt.ct.CTPolicy; +import org.conscrypt.metrics.CipherSuite; +import org.conscrypt.metrics.ConscryptStatsLog; +import org.conscrypt.metrics.Protocol; /** * Platform-specific methods for unbundled Android. @@ -72,6 +76,7 @@ m_getCurveName = ECParameterSpec.class.getDeclaredMethod("getCurveName"); m_getCurveName.setAccessible(true); } catch (Exception ignored) { + //Ignored } } @@ -124,6 +129,7 @@ Method setCurveName = spec.getClass().getDeclaredMethod("setCurveName", String.class); setCurveName.invoke(spec, curveName); } catch (Exception ignored) { + //Ignored } } @@ -244,7 +250,9 @@ } } } catch (NoSuchMethodException ignored) { + //Ignored } catch (IllegalAccessException ignored) { + //Ignored } catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } @@ -262,7 +270,9 @@ } } } catch (NoSuchMethodException ignored) { + //Ignored } catch (IllegalAccessException ignored) { + //Ignored } catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } @@ -306,7 +316,9 @@ setParametersSniHostname(params, impl, socket); } } catch (NoSuchMethodException ignored) { + //Ignored } catch (IllegalAccessException ignored) { + //Ignored } catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } @@ -333,7 +345,9 @@ setParametersSniHostname(params, impl, engine); } } catch (NoSuchMethodException ignored) { + //Ignored } catch (IllegalAccessException ignored) { + //Ignored } catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } @@ -359,6 +373,7 @@ try { return Class.forName(klass); } catch (Exception ignored) { + //Ignored } } return null; @@ -388,7 +403,9 @@ method.invoke(tm, chain, authType, argumentInstance); return true; } catch (NoSuchMethodException ignored) { + //Ignored } catch (IllegalAccessException ignored) { + //Ignored } catch (InvocationTargetException e) { if (e.getCause() instanceof CertificateException) { throw(CertificateException) e.getCause(); @@ -826,6 +843,7 @@ } throw new RuntimeException(e); } catch (Exception ignored) { + //Ignored } // Newer OpenJDK style @@ -847,6 +865,7 @@ } throw new RuntimeException(e); } catch (Exception ignored) { + //Ignored } return oid; @@ -884,7 +903,9 @@ } catch (ClassNotFoundException ignore) { // passthrough and return addr.getHostAddress() } catch (IllegalAccessException ignore) { + //Ignored } catch (NoSuchMethodException ignore) { + //Ignored } } return addr.getHostAddress(); @@ -902,6 +923,7 @@ } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (Exception ignored) { + //Ignored } } return null; @@ -984,7 +1006,7 @@ return null; } - static CertBlacklist newDefaultBlacklist() { + static CertBlocklist newDefaultBlocklist() { return null; } @@ -1019,4 +1041,39 @@ } return false; } + + public static ConscryptHostnameVerifier getDefaultHostnameVerifier() { + return OkHostnameVerifier.strictInstance(); + } + + /** + * Returns milliseconds elapsed since boot, including time spent in sleep. + * @return long number of milliseconds elapsed since boot + */ + static long getMillisSinceBoot() { + return SystemClock.elapsedRealtime(); + } + + static void countTlsHandshake( + boolean success, String protocol, String cipherSuite, long duration) { + // Statsd classes appeared in SDK 30 and aren't available in earlier versions + + if (Build.VERSION.SDK_INT >= 30) { + Protocol proto = Protocol.forName(protocol); + CipherSuite suite = CipherSuite.forName(cipherSuite); + int dur = (int) duration; + + writeStats(success, proto.getId(), suite.getId(), dur); + } + } + + @TargetApi(30) + private static void writeStats(boolean success, int protocol, int cipherSuite, int duration) { + ConscryptStatsLog.write( + ConscryptStatsLog.TLS_HANDSHAKE_REPORTED, success, protocol, cipherSuite, duration); + } + + public static boolean isJavaxCertificateSupported() { + return true; + } }
diff --git a/apex/Android.bp b/apex/Android.bp index 7310f7a..2a7d225 100644 --- a/apex/Android.bp +++ b/apex/Android.bp
@@ -13,13 +13,23 @@ // limitations under the License. // Defaults shared between real and test versions of the APEX. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "external_conscrypt_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["external_conscrypt_license"], +} + apex_defaults { name: "com.android.conscrypt-defaults", updatable: true, androidManifest: ":com.android.conscrypt-androidManifest", compile_multilib: "both", - java_libs: ["conscrypt"], - native_shared_libs: ["libjavacrypto"], + bootclasspath_fragments: ["com.android.conscrypt-bootclasspath-fragment"], + jni_libs: ["libjavacrypto"], + prebuilts: ["current_sdkinfo"], multilib: { both: { binaries: ["boringssl_self_test"], @@ -72,3 +82,34 @@ binaries: ["boringssl_self_test"], prebuilts: ["com.android.conscrypt.ld.config.txt"], } + +// Encapsulate the contributions made by the com.android.conscrypt to the bootclasspath. +bootclasspath_fragment { + name: "com.android.conscrypt-bootclasspath-fragment", + contents: ["conscrypt"], + apex_available: ["com.android.conscrypt"], + // The bootclasspath_fragments that provide APIs on which this depends. + fragments: [ + { + apex: "com.android.art", + module: "art-bootclasspath-fragment", + }, + ], + // The APIs provided by this fragment. + api: { + stub_libs: [ + "conscrypt.module.public.api", + ], + }, + // The core platform APIs provided by this fragment. + core_platform_api: { + stub_libs: [ + "conscrypt.module.platform.api", + ], + }, + // Additional hidden API flags that override the default flags derived + // from the api stub libraries. + hidden_api: { + max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"], + }, +}
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json index 14d81ea..e59fbcf 100644 --- a/apex/apex_manifest.json +++ b/apex/apex_manifest.json
@@ -1,4 +1,4 @@ { "name": "com.android.conscrypt", - "version": 300000000 + "version": 319999900 }
diff --git a/apex/hiddenapi/OWNERS b/apex/hiddenapi/OWNERS new file mode 100644 index 0000000..ac8a2b6 --- /dev/null +++ b/apex/hiddenapi/OWNERS
@@ -0,0 +1,5 @@ +# soong-team@ as the hiddenapi files are tightly coupled with Soong +file:platform/build/soong:/OWNERS + +# compat-team@ for changes to hiddenapi files +file:tools/platform-compat:/OWNERS
diff --git a/apex/hiddenapi/hiddenapi-max-target-o-low-priority.txt b/apex/hiddenapi/hiddenapi-max-target-o-low-priority.txt new file mode 100644 index 0000000..bd3d12b --- /dev/null +++ b/apex/hiddenapi/hiddenapi-max-target-o-low-priority.txt
@@ -0,0 +1,277 @@ +Lcom/android/org/conscrypt/AbstractConscryptSocket;-><init>()V +Lcom/android/org/conscrypt/AbstractConscryptSocket;-><init>(Ljava/lang/String;I)V +Lcom/android/org/conscrypt/AbstractConscryptSocket;-><init>(Ljava/lang/String;ILjava/net/InetAddress;I)V +Lcom/android/org/conscrypt/AbstractConscryptSocket;-><init>(Ljava/net/InetAddress;I)V +Lcom/android/org/conscrypt/AbstractConscryptSocket;-><init>(Ljava/net/InetAddress;ILjava/net/InetAddress;I)V +Lcom/android/org/conscrypt/AbstractConscryptSocket;->getFileDescriptor$()Ljava/io/FileDescriptor; +Lcom/android/org/conscrypt/AbstractConscryptSocket;->getTlsUnique()[B +Lcom/android/org/conscrypt/AbstractConscryptSocket;->peerInfoProvider()Lcom/android/org/conscrypt/PeerInfoProvider; +Lcom/android/org/conscrypt/AbstractConscryptSocket;->setApplicationProtocolSelector(Lcom/android/org/conscrypt/ApplicationProtocolSelector;)V +Lcom/android/org/conscrypt/ApplicationProtocolSelector;-><init>()V +Lcom/android/org/conscrypt/ApplicationProtocolSelector;->selectApplicationProtocol(Ljavax/net/ssl/SSLEngine;Ljava/util/List;)Ljava/lang/String; +Lcom/android/org/conscrypt/ApplicationProtocolSelector;->selectApplicationProtocol(Ljavax/net/ssl/SSLSocket;Ljava/util/List;)Ljava/lang/String; +Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;-><init>(Ljavax/net/ssl/SSLEngine;Lcom/android/org/conscrypt/ApplicationProtocolSelector;)V +Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;-><init>(Ljavax/net/ssl/SSLSocket;Lcom/android/org/conscrypt/ApplicationProtocolSelector;)V +Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;->engine:Ljavax/net/ssl/SSLEngine; +Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;->NO_PROTOCOL_SELECTED:I +Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;->selectApplicationProtocol([B)I +Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;->selector:Lcom/android/org/conscrypt/ApplicationProtocolSelector; +Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;->socket:Ljavax/net/ssl/SSLSocket; +Lcom/android/org/conscrypt/CertBlacklist;-><init>(Ljava/util/Set;Ljava/util/Set;)V +Lcom/android/org/conscrypt/CertBlacklist;->closeQuietly(Ljava/io/Closeable;)V +Lcom/android/org/conscrypt/CertBlacklist;->getDefault()Lcom/android/org/conscrypt/CertBlacklist; +Lcom/android/org/conscrypt/CertBlacklist;->HEX_TABLE:[B +Lcom/android/org/conscrypt/CertBlacklist;->isHex(Ljava/lang/String;)Z +Lcom/android/org/conscrypt/CertBlacklist;->isPubkeyHash(Ljava/lang/String;)Z +Lcom/android/org/conscrypt/CertBlacklist;->isPublicKeyBlackListed(Ljava/security/PublicKey;)Z +Lcom/android/org/conscrypt/CertBlacklist;->isSerialNumberBlackListed(Ljava/math/BigInteger;)Z +Lcom/android/org/conscrypt/CertBlacklist;->logger:Ljava/util/logging/Logger; +Lcom/android/org/conscrypt/CertBlacklist;->pubkeyBlacklist:Ljava/util/Set; +Lcom/android/org/conscrypt/CertBlacklist;->readBlacklist(Ljava/lang/String;)Ljava/lang/String; +Lcom/android/org/conscrypt/CertBlacklist;->readFileAsBytes(Ljava/lang/String;)Ljava/io/ByteArrayOutputStream; +Lcom/android/org/conscrypt/CertBlacklist;->readFileAsString(Ljava/lang/String;)Ljava/lang/String; +Lcom/android/org/conscrypt/CertBlacklist;->readPublicKeyBlackList(Ljava/lang/String;)Ljava/util/Set; +Lcom/android/org/conscrypt/CertBlacklist;->readSerialBlackList(Ljava/lang/String;)Ljava/util/Set; +Lcom/android/org/conscrypt/CertBlacklist;->serialBlacklist:Ljava/util/Set; +Lcom/android/org/conscrypt/CertBlacklist;->toHex([B)[B +Lcom/android/org/conscrypt/CertificatePriorityComparator;-><init>()V +Lcom/android/org/conscrypt/CertificatePriorityComparator;->ALGORITHM_OID_PRIORITY_MAP:Ljava/util/Map; +Lcom/android/org/conscrypt/CertificatePriorityComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I +Lcom/android/org/conscrypt/CertificatePriorityComparator;->compare(Ljava/security/cert/X509Certificate;Ljava/security/cert/X509Certificate;)I +Lcom/android/org/conscrypt/CertificatePriorityComparator;->compareKeyAlgorithm(Ljava/security/PublicKey;Ljava/security/PublicKey;)I +Lcom/android/org/conscrypt/CertificatePriorityComparator;->compareKeySize(Ljava/security/PublicKey;Ljava/security/PublicKey;)I +Lcom/android/org/conscrypt/CertificatePriorityComparator;->compareSignatureAlgorithm(Ljava/security/cert/X509Certificate;Ljava/security/cert/X509Certificate;)I +Lcom/android/org/conscrypt/CertificatePriorityComparator;->compareStrength(Ljava/security/cert/X509Certificate;Ljava/security/cert/X509Certificate;)I +Lcom/android/org/conscrypt/CertificatePriorityComparator;->getKeySize(Ljava/security/PublicKey;)I +Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_MD5:Ljava/lang/Integer; +Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_SHA1:Ljava/lang/Integer; +Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_SHA224:Ljava/lang/Integer; +Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_SHA256:Ljava/lang/Integer; +Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_SHA384:Ljava/lang/Integer; +Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_SHA512:Ljava/lang/Integer; +Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_UNKNOWN:Ljava/lang/Integer; +Lcom/android/org/conscrypt/CertPinManager;->checkChainPinning(Ljava/lang/String;Ljava/util/List;)V +Lcom/android/org/conscrypt/ConscryptSocketBase;-><init>()V +Lcom/android/org/conscrypt/ConscryptSocketBase;-><init>(Ljava/lang/String;I)V +Lcom/android/org/conscrypt/ConscryptSocketBase;-><init>(Ljava/lang/String;ILjava/net/InetAddress;I)V +Lcom/android/org/conscrypt/ConscryptSocketBase;-><init>(Ljava/net/InetAddress;I)V +Lcom/android/org/conscrypt/ConscryptSocketBase;-><init>(Ljava/net/InetAddress;ILjava/net/InetAddress;I)V +Lcom/android/org/conscrypt/ConscryptSocketBase;-><init>(Ljava/net/Socket;Ljava/lang/String;IZ)V +Lcom/android/org/conscrypt/ConscryptSocketBase;->autoClose:Z +Lcom/android/org/conscrypt/ConscryptSocketBase;->checkOpen()V +Lcom/android/org/conscrypt/ConscryptSocketBase;->getActiveSession()Ljavax/net/ssl/SSLSession; +Lcom/android/org/conscrypt/ConscryptSocketBase;->getFileDescriptor$()Ljava/io/FileDescriptor; +Lcom/android/org/conscrypt/ConscryptSocketBase;->isDelegating()Z +Lcom/android/org/conscrypt/ConscryptSocketBase;->listeners:Ljava/util/List; +Lcom/android/org/conscrypt/ConscryptSocketBase;->notifyHandshakeCompletedListeners()V +Lcom/android/org/conscrypt/ConscryptSocketBase;->peerHostname:Ljava/lang/String; +Lcom/android/org/conscrypt/ConscryptSocketBase;->peerInfoProvider()Lcom/android/org/conscrypt/PeerInfoProvider; +Lcom/android/org/conscrypt/ConscryptSocketBase;->peerInfoProvider:Lcom/android/org/conscrypt/PeerInfoProvider; +Lcom/android/org/conscrypt/ConscryptSocketBase;->peerPort:I +Lcom/android/org/conscrypt/ConscryptSocketBase;->readTimeoutMilliseconds:I +Lcom/android/org/conscrypt/ConscryptSocketBase;->setApplicationProtocolSelector(Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;)V +Lcom/android/org/conscrypt/NativeRef$EC_GROUP;-><init>(J)V +Lcom/android/org/conscrypt/NativeRef$EC_GROUP;->doFree(J)V +Lcom/android/org/conscrypt/NativeRef$EC_POINT;-><init>(J)V +Lcom/android/org/conscrypt/NativeRef$EC_POINT;->doFree(J)V +Lcom/android/org/conscrypt/NativeRef$EVP_CIPHER_CTX;-><init>(J)V +Lcom/android/org/conscrypt/NativeRef$EVP_CIPHER_CTX;->doFree(J)V +Lcom/android/org/conscrypt/NativeRef$EVP_MD_CTX;-><init>(J)V +Lcom/android/org/conscrypt/NativeRef$EVP_MD_CTX;->doFree(J)V +Lcom/android/org/conscrypt/NativeRef$EVP_PKEY;-><init>(J)V +Lcom/android/org/conscrypt/NativeRef$EVP_PKEY;->doFree(J)V +Lcom/android/org/conscrypt/NativeRef$EVP_PKEY_CTX;-><init>(J)V +Lcom/android/org/conscrypt/NativeRef$EVP_PKEY_CTX;->doFree(J)V +Lcom/android/org/conscrypt/NativeRef$HMAC_CTX;-><init>(J)V +Lcom/android/org/conscrypt/NativeRef$HMAC_CTX;->doFree(J)V +Lcom/android/org/conscrypt/NativeRef$SSL_SESSION;-><init>(J)V +Lcom/android/org/conscrypt/NativeRef$SSL_SESSION;->doFree(J)V +Lcom/android/org/conscrypt/NativeRef;-><init>(J)V +Lcom/android/org/conscrypt/NativeRef;->context:J +Lcom/android/org/conscrypt/NativeRef;->doFree(J)V +Lcom/android/org/conscrypt/OpenSSLKey;-><init>(JZ)V +Lcom/android/org/conscrypt/OpenSSLKey;->ctx:Lcom/android/org/conscrypt/NativeRef$EVP_PKEY; +Lcom/android/org/conscrypt/OpenSSLKey;->fromECPrivateKeyForTLSStackOnly(Ljava/security/PrivateKey;Ljava/security/spec/ECParameterSpec;)Lcom/android/org/conscrypt/OpenSSLKey; +Lcom/android/org/conscrypt/OpenSSLKey;->fromKeyMaterial(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey; +Lcom/android/org/conscrypt/OpenSSLKey;->fromPrivateKeyForTLSStackOnly(Ljava/security/PrivateKey;Ljava/security/PublicKey;)Lcom/android/org/conscrypt/OpenSSLKey; +Lcom/android/org/conscrypt/OpenSSLKey;->fromPrivateKeyPemInputStream(Ljava/io/InputStream;)Lcom/android/org/conscrypt/OpenSSLKey; +Lcom/android/org/conscrypt/OpenSSLKey;->fromPublicKey(Ljava/security/PublicKey;)Lcom/android/org/conscrypt/OpenSSLKey; +Lcom/android/org/conscrypt/OpenSSLKey;->fromPublicKeyPemInputStream(Ljava/io/InputStream;)Lcom/android/org/conscrypt/OpenSSLKey; +Lcom/android/org/conscrypt/OpenSSLKey;->getOpenSSLKey(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey; +Lcom/android/org/conscrypt/OpenSSLKey;->getPrivateKey()Ljava/security/PrivateKey; +Lcom/android/org/conscrypt/OpenSSLKey;->getPrivateKey(Ljava/security/spec/PKCS8EncodedKeySpec;I)Ljava/security/PrivateKey; +Lcom/android/org/conscrypt/OpenSSLKey;->getPublicKey(Ljava/security/spec/X509EncodedKeySpec;I)Ljava/security/PublicKey; +Lcom/android/org/conscrypt/OpenSSLKey;->isWrapped()Z +Lcom/android/org/conscrypt/OpenSSLKey;->wrapJCAPrivateKeyForTLSStackOnly(Ljava/security/PrivateKey;Ljava/security/PublicKey;)Lcom/android/org/conscrypt/OpenSSLKey; +Lcom/android/org/conscrypt/OpenSSLKey;->wrapped:Z +Lcom/android/org/conscrypt/OpenSSLKey;->wrapPrivateKey(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey; +Lcom/android/org/conscrypt/OpenSSLSocketImpl;-><init>()V +Lcom/android/org/conscrypt/OpenSSLSocketImpl;-><init>(Ljava/lang/String;I)V +Lcom/android/org/conscrypt/OpenSSLSocketImpl;-><init>(Ljava/lang/String;ILjava/net/InetAddress;I)V +Lcom/android/org/conscrypt/OpenSSLSocketImpl;-><init>(Ljava/net/InetAddress;I)V +Lcom/android/org/conscrypt/OpenSSLSocketImpl;-><init>(Ljava/net/InetAddress;ILjava/net/InetAddress;I)V +Lcom/android/org/conscrypt/OpenSSLSocketImpl;-><init>(Ljava/net/Socket;Ljava/lang/String;IZ)V +Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getFileDescriptor$()Ljava/io/FileDescriptor; +Lcom/android/org/conscrypt/OpenSSLX509Certificate;-><init>(J)V +Lcom/android/org/conscrypt/OpenSSLX509Certificate;-><init>(JLjava/util/Date;Ljava/util/Date;)V +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->alternativeNameArrayToList([[Ljava/lang/Object;)Ljava/util/Collection; +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->fromCertificate(Ljava/security/cert/Certificate;)Lcom/android/org/conscrypt/OpenSSLX509Certificate; +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->fromPkcs7DerInputStream(Ljava/io/InputStream;)Ljava/util/List; +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->fromPkcs7PemInputStream(Ljava/io/InputStream;)Ljava/util/List; +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->fromX509Der([B)Lcom/android/org/conscrypt/OpenSSLX509Certificate; +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->fromX509DerInputStream(Ljava/io/InputStream;)Lcom/android/org/conscrypt/OpenSSLX509Certificate; +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->getContext()J +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->mHashCode:Ljava/lang/Integer; +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->notAfter:Ljava/util/Date; +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->notBefore:Ljava/util/Date; +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->toDate(J)Ljava/util/Date; +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->verifyInternal(Ljava/security/PublicKey;Ljava/lang/String;)V +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->verifyOpenSSL(Lcom/android/org/conscrypt/OpenSSLKey;)V +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->withDeletedExtension(Ljava/lang/String;)Lcom/android/org/conscrypt/OpenSSLX509Certificate; +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;-><init>()V +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;->fromPkcs7DerInputStream(Ljava/io/InputStream;)Ljava/util/List; +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;->fromPkcs7PemInputStream(Ljava/io/InputStream;)Ljava/util/List; +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;->fromX509DerInputStream(Ljava/io/InputStream;)Ljava/lang/Object; +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;->fromX509PemInputStream(Ljava/io/InputStream;)Ljava/lang/Object; +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;->generateItem(Ljava/io/InputStream;)Ljava/lang/Object; +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;->generateItems(Ljava/io/InputStream;)Ljava/util/Collection; +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$ParsingException;-><init>(Ljava/lang/Exception;)V +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$ParsingException;-><init>(Ljava/lang/String;)V +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$ParsingException;-><init>(Ljava/lang/String;Ljava/lang/Exception;)V +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory;-><init>()V +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory;->certificateParser:Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser; +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory;->crlParser:Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser; +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory;->PKCS7_MARKER:[B +Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory;->PUSHBACK_SIZE:I +Lcom/android/org/conscrypt/OpenSSLX509CRL;-><init>(J)V +Lcom/android/org/conscrypt/OpenSSLX509CRL;->fromPkcs7DerInputStream(Ljava/io/InputStream;)Ljava/util/List; +Lcom/android/org/conscrypt/OpenSSLX509CRL;->fromPkcs7PemInputStream(Ljava/io/InputStream;)Ljava/util/List; +Lcom/android/org/conscrypt/OpenSSLX509CRL;->fromX509DerInputStream(Ljava/io/InputStream;)Lcom/android/org/conscrypt/OpenSSLX509CRL; +Lcom/android/org/conscrypt/OpenSSLX509CRL;->fromX509PemInputStream(Ljava/io/InputStream;)Lcom/android/org/conscrypt/OpenSSLX509CRL; +Lcom/android/org/conscrypt/OpenSSLX509CRL;->mContext:J +Lcom/android/org/conscrypt/OpenSSLX509CRL;->nextUpdate:Ljava/util/Date; +Lcom/android/org/conscrypt/OpenSSLX509CRL;->thisUpdate:Ljava/util/Date; +Lcom/android/org/conscrypt/OpenSSLX509CRL;->toDate(J)Ljava/util/Date; +Lcom/android/org/conscrypt/OpenSSLX509CRL;->verifyInternal(Ljava/security/PublicKey;Ljava/lang/String;)V +Lcom/android/org/conscrypt/OpenSSLX509CRL;->verifyOpenSSL(Lcom/android/org/conscrypt/OpenSSLKey;)V +Lcom/android/org/conscrypt/PeerInfoProvider;-><init>()V +Lcom/android/org/conscrypt/PeerInfoProvider;->forHostAndPort(Ljava/lang/String;I)Lcom/android/org/conscrypt/PeerInfoProvider; +Lcom/android/org/conscrypt/PeerInfoProvider;->getHostname()Ljava/lang/String; +Lcom/android/org/conscrypt/PeerInfoProvider;->getHostnameOrIP()Ljava/lang/String; +Lcom/android/org/conscrypt/PeerInfoProvider;->getPort()I +Lcom/android/org/conscrypt/PeerInfoProvider;->nullProvider()Lcom/android/org/conscrypt/PeerInfoProvider; +Lcom/android/org/conscrypt/PeerInfoProvider;->NULL_PEER_INFO_PROVIDER:Lcom/android/org/conscrypt/PeerInfoProvider; +Lcom/android/org/conscrypt/SSLClientSessionCache;->getSessionData(Ljava/lang/String;I)[B +Lcom/android/org/conscrypt/SSLClientSessionCache;->putSessionData(Ljavax/net/ssl/SSLSession;[B)V +Lcom/android/org/conscrypt/TrustedCertificateIndex;-><init>()V +Lcom/android/org/conscrypt/TrustedCertificateIndex;-><init>(Ljava/util/Set;)V +Lcom/android/org/conscrypt/TrustedCertificateIndex;->findAllByIssuerAndSignature(Ljava/security/cert/X509Certificate;)Ljava/util/Set; +Lcom/android/org/conscrypt/TrustedCertificateIndex;->findByIssuerAndSignature(Ljava/security/cert/X509Certificate;)Ljava/security/cert/TrustAnchor; +Lcom/android/org/conscrypt/TrustedCertificateIndex;->findBySubjectAndPublicKey(Ljava/security/cert/X509Certificate;)Ljava/security/cert/TrustAnchor; +Lcom/android/org/conscrypt/TrustedCertificateIndex;->findBySubjectAndPublicKey(Ljava/security/cert/X509Certificate;Ljava/util/Collection;)Ljava/security/cert/TrustAnchor; +Lcom/android/org/conscrypt/TrustedCertificateIndex;->index(Ljava/security/cert/TrustAnchor;)V +Lcom/android/org/conscrypt/TrustedCertificateIndex;->index(Ljava/security/cert/X509Certificate;)Ljava/security/cert/TrustAnchor; +Lcom/android/org/conscrypt/TrustedCertificateIndex;->index(Ljava/util/Set;)V +Lcom/android/org/conscrypt/TrustedCertificateIndex;->reset()V +Lcom/android/org/conscrypt/TrustedCertificateIndex;->reset(Ljava/util/Set;)V +Lcom/android/org/conscrypt/TrustedCertificateIndex;->subjectToTrustAnchors:Ljava/util/Map; +Lcom/android/org/conscrypt/TrustedCertificateStore$CertSelector;->match(Ljava/security/cert/X509Certificate;)Z +Lcom/android/org/conscrypt/TrustedCertificateStore$PreloadHolder;-><init>()V +Lcom/android/org/conscrypt/TrustedCertificateStore$PreloadHolder;->defaultCaCertsAddedDir:Ljava/io/File; +Lcom/android/org/conscrypt/TrustedCertificateStore$PreloadHolder;->defaultCaCertsDeletedDir:Ljava/io/File; +Lcom/android/org/conscrypt/TrustedCertificateStore$PreloadHolder;->defaultCaCertsSystemDir:Ljava/io/File; +Lcom/android/org/conscrypt/TrustedCertificateStore;-><init>(Ljava/io/File;Ljava/io/File;Ljava/io/File;)V +Lcom/android/org/conscrypt/TrustedCertificateStore;->addAliases(Ljava/util/Set;Ljava/lang/String;Ljava/io/File;)V +Lcom/android/org/conscrypt/TrustedCertificateStore;->addedDir:Ljava/io/File; +Lcom/android/org/conscrypt/TrustedCertificateStore;->aliases()Ljava/util/Set; +Lcom/android/org/conscrypt/TrustedCertificateStore;->allSystemAliases()Ljava/util/Set; +Lcom/android/org/conscrypt/TrustedCertificateStore;->CERT_FACTORY:Ljava/security/cert/CertificateFactory; +Lcom/android/org/conscrypt/TrustedCertificateStore;->containsAlias(Ljava/lang/String;)Z +Lcom/android/org/conscrypt/TrustedCertificateStore;->containsAlias(Ljava/lang/String;Z)Z +Lcom/android/org/conscrypt/TrustedCertificateStore;->convertToOpenSSLIfNeeded(Ljava/security/cert/X509Certificate;)Lcom/android/org/conscrypt/OpenSSLX509Certificate; +Lcom/android/org/conscrypt/TrustedCertificateStore;->deleteCertificateEntry(Ljava/lang/String;)V +Lcom/android/org/conscrypt/TrustedCertificateStore;->deletedDir:Ljava/io/File; +Lcom/android/org/conscrypt/TrustedCertificateStore;->file(Ljava/io/File;Ljava/lang/String;I)Ljava/io/File; +Lcom/android/org/conscrypt/TrustedCertificateStore;->fileForAlias(Ljava/lang/String;)Ljava/io/File; +Lcom/android/org/conscrypt/TrustedCertificateStore;->findAllIssuers(Ljava/security/cert/X509Certificate;)Ljava/util/Set; +Lcom/android/org/conscrypt/TrustedCertificateStore;->findCert(Ljava/io/File;Ljavax/security/auth/x500/X500Principal;Lcom/android/org/conscrypt/TrustedCertificateStore$CertSelector;Ljava/lang/Class;)Ljava/lang/Object; +Lcom/android/org/conscrypt/TrustedCertificateStore;->findIssuer(Ljava/security/cert/X509Certificate;)Ljava/security/cert/X509Certificate; +Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificate(Ljava/lang/String;)Ljava/security/cert/Certificate; +Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificate(Ljava/lang/String;Z)Ljava/security/cert/Certificate; +Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateAlias(Ljava/security/cert/Certificate;)Ljava/lang/String; +Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateAlias(Ljava/security/cert/Certificate;Z)Ljava/lang/String; +Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateFile(Ljava/io/File;Ljava/security/cert/X509Certificate;)Ljava/io/File; +Lcom/android/org/conscrypt/TrustedCertificateStore;->getCreationDate(Ljava/lang/String;)Ljava/util/Date; +Lcom/android/org/conscrypt/TrustedCertificateStore;->getTrustAnchor(Ljava/security/cert/X509Certificate;)Ljava/security/cert/X509Certificate; +Lcom/android/org/conscrypt/TrustedCertificateStore;->hash(Ljavax/security/auth/x500/X500Principal;)Ljava/lang/String; +Lcom/android/org/conscrypt/TrustedCertificateStore;->installCertificate(Ljava/security/cert/X509Certificate;)V +Lcom/android/org/conscrypt/TrustedCertificateStore;->isDeletedSystemCertificate(Ljava/security/cert/X509Certificate;)Z +Lcom/android/org/conscrypt/TrustedCertificateStore;->isSelfIssuedCertificate(Lcom/android/org/conscrypt/OpenSSLX509Certificate;)Z +Lcom/android/org/conscrypt/TrustedCertificateStore;->isSystem(Ljava/lang/String;)Z +Lcom/android/org/conscrypt/TrustedCertificateStore;->isTombstone(Ljava/io/File;)Z +Lcom/android/org/conscrypt/TrustedCertificateStore;->isUser(Ljava/lang/String;)Z +Lcom/android/org/conscrypt/TrustedCertificateStore;->isUserAddedCertificate(Ljava/security/cert/X509Certificate;)Z +Lcom/android/org/conscrypt/TrustedCertificateStore;->PREFIX_SYSTEM:Ljava/lang/String; +Lcom/android/org/conscrypt/TrustedCertificateStore;->PREFIX_USER:Ljava/lang/String; +Lcom/android/org/conscrypt/TrustedCertificateStore;->readCertificate(Ljava/io/File;)Ljava/security/cert/X509Certificate; +Lcom/android/org/conscrypt/TrustedCertificateStore;->removeUnnecessaryTombstones(Ljava/lang/String;)V +Lcom/android/org/conscrypt/TrustedCertificateStore;->setDefaultUserDirectory(Ljava/io/File;)V +Lcom/android/org/conscrypt/TrustedCertificateStore;->systemDir:Ljava/io/File; +Lcom/android/org/conscrypt/TrustedCertificateStore;->userAliases()Ljava/util/Set; +Lcom/android/org/conscrypt/TrustedCertificateStore;->writeCertificate(Ljava/io/File;Ljava/security/cert/X509Certificate;)V +Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;-><init>(ZLjava/security/cert/X509Certificate;)V +Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->clientAuth:Z +Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->EKU_anyExtendedKeyUsage:Ljava/lang/String; +Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->EKU_clientAuth:Ljava/lang/String; +Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->EKU_msSGC:Ljava/lang/String; +Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->EKU_nsSGC:Ljava/lang/String; +Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->EKU_OID:Ljava/lang/String; +Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->EKU_serverAuth:Ljava/lang/String; +Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->leaf:Ljava/security/cert/X509Certificate; +Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->SUPPORTED_EXTENSIONS:Ljava/util/Set; +Lcom/android/org/conscrypt/TrustManagerImpl$TrustAnchorComparator;-><init>()V +Lcom/android/org/conscrypt/TrustManagerImpl$TrustAnchorComparator;->CERT_COMPARATOR:Lcom/android/org/conscrypt/CertificatePriorityComparator; +Lcom/android/org/conscrypt/TrustManagerImpl$TrustAnchorComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I +Lcom/android/org/conscrypt/TrustManagerImpl$TrustAnchorComparator;->compare(Ljava/security/cert/TrustAnchor;Ljava/security/cert/TrustAnchor;)I +Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;Lcom/android/org/conscrypt/CertPinManager;)V +Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;Lcom/android/org/conscrypt/CertPinManager;Lcom/android/org/conscrypt/TrustedCertificateStore;)V +Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;Lcom/android/org/conscrypt/CertPinManager;Lcom/android/org/conscrypt/TrustedCertificateStore;Lcom/android/org/conscrypt/CertBlacklist;)V +Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;Lcom/android/org/conscrypt/CertPinManager;Lcom/android/org/conscrypt/TrustedCertificateStore;Lcom/android/org/conscrypt/CertBlacklist;Lcom/android/org/conscrypt/ct/CTLogStore;Lcom/android/org/conscrypt/ct/CTVerifier;Lcom/android/org/conscrypt/ct/CTPolicy;)V +Lcom/android/org/conscrypt/TrustManagerImpl;->acceptedIssuers(Ljava/security/KeyStore;)[Ljava/security/cert/X509Certificate; +Lcom/android/org/conscrypt/TrustManagerImpl;->acceptedIssuers:[Ljava/security/cert/X509Certificate; +Lcom/android/org/conscrypt/TrustManagerImpl;->blacklist:Lcom/android/org/conscrypt/CertBlacklist; +Lcom/android/org/conscrypt/TrustManagerImpl;->checkBlacklist(Ljava/security/cert/X509Certificate;)V +Lcom/android/org/conscrypt/TrustManagerImpl;->checkClientTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List; +Lcom/android/org/conscrypt/TrustManagerImpl;->checkCT(Ljava/lang/String;Ljava/util/List;[B[B)V +Lcom/android/org/conscrypt/TrustManagerImpl;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljavax/net/ssl/SSLSession;)Ljava/util/List; +Lcom/android/org/conscrypt/TrustManagerImpl;->checkTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljavax/net/ssl/SSLSession;Ljavax/net/ssl/SSLParameters;Z)Ljava/util/List; +Lcom/android/org/conscrypt/TrustManagerImpl;->checkTrusted([Ljava/security/cert/X509Certificate;[B[BLjava/lang/String;Ljava/lang/String;Z)Ljava/util/List; +Lcom/android/org/conscrypt/TrustManagerImpl;->checkTrustedRecursive([Ljava/security/cert/X509Certificate;[B[BLjava/lang/String;ZLjava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/Set;)Ljava/util/List; +Lcom/android/org/conscrypt/TrustManagerImpl;->ctEnabledOverride:Z +Lcom/android/org/conscrypt/TrustManagerImpl;->ctPolicy:Lcom/android/org/conscrypt/ct/CTPolicy; +Lcom/android/org/conscrypt/TrustManagerImpl;->ctVerifier:Lcom/android/org/conscrypt/ct/CTVerifier; +Lcom/android/org/conscrypt/TrustManagerImpl;->err:Ljava/lang/Exception; +Lcom/android/org/conscrypt/TrustManagerImpl;->factory:Ljava/security/cert/CertificateFactory; +Lcom/android/org/conscrypt/TrustManagerImpl;->findAllTrustAnchorsByIssuerAndSignature(Ljava/security/cert/X509Certificate;)Ljava/util/Set; +Lcom/android/org/conscrypt/TrustManagerImpl;->findTrustAnchorBySubjectAndPublicKey(Ljava/security/cert/X509Certificate;)Ljava/security/cert/TrustAnchor; +Lcom/android/org/conscrypt/TrustManagerImpl;->getHandshakeSessionOrThrow(Ljavax/net/ssl/SSLSocket;)Ljavax/net/ssl/SSLSession; +Lcom/android/org/conscrypt/TrustManagerImpl;->getOcspDataFromSession(Ljavax/net/ssl/SSLSession;)[B +Lcom/android/org/conscrypt/TrustManagerImpl;->getTlsSctDataFromSession(Ljavax/net/ssl/SSLSession;)[B +Lcom/android/org/conscrypt/TrustManagerImpl;->getTrustedChainForServer([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/net/Socket;)Ljava/util/List; +Lcom/android/org/conscrypt/TrustManagerImpl;->getTrustedChainForServer([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljavax/net/ssl/SSLEngine;)Ljava/util/List; +Lcom/android/org/conscrypt/TrustManagerImpl;->handleTrustStorageUpdate()V +Lcom/android/org/conscrypt/TrustManagerImpl;->intermediateIndex:Lcom/android/org/conscrypt/TrustedCertificateIndex; +Lcom/android/org/conscrypt/TrustManagerImpl;->isUserAddedCertificate(Ljava/security/cert/X509Certificate;)Z +Lcom/android/org/conscrypt/TrustManagerImpl;->pinManager:Lcom/android/org/conscrypt/CertPinManager; +Lcom/android/org/conscrypt/TrustManagerImpl;->rootKeyStore:Ljava/security/KeyStore; +Lcom/android/org/conscrypt/TrustManagerImpl;->setCTEnabledOverride(Z)V +Lcom/android/org/conscrypt/TrustManagerImpl;->setCTPolicy(Lcom/android/org/conscrypt/ct/CTPolicy;)V +Lcom/android/org/conscrypt/TrustManagerImpl;->setCTVerifier(Lcom/android/org/conscrypt/ct/CTVerifier;)V +Lcom/android/org/conscrypt/TrustManagerImpl;->setOcspResponses(Ljava/security/cert/PKIXParameters;Ljava/security/cert/X509Certificate;[B)V +Lcom/android/org/conscrypt/TrustManagerImpl;->sortPotentialAnchors(Ljava/util/Set;)Ljava/util/Collection; +Lcom/android/org/conscrypt/TrustManagerImpl;->trustAnchors([Ljava/security/cert/X509Certificate;)Ljava/util/Set; +Lcom/android/org/conscrypt/TrustManagerImpl;->trustedCertificateIndex:Lcom/android/org/conscrypt/TrustedCertificateIndex; +Lcom/android/org/conscrypt/TrustManagerImpl;->trustedCertificateStore:Lcom/android/org/conscrypt/TrustedCertificateStore; +Lcom/android/org/conscrypt/TrustManagerImpl;->TRUST_ANCHOR_COMPARATOR:Lcom/android/org/conscrypt/TrustManagerImpl$TrustAnchorComparator; +Lcom/android/org/conscrypt/TrustManagerImpl;->validator:Ljava/security/cert/CertPathValidator; +Lcom/android/org/conscrypt/TrustManagerImpl;->verifyChain(Ljava/util/List;Ljava/util/List;Ljava/lang/String;Z[B[B)Ljava/util/List;
diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp index 6f99a0f..8a1c102 100644 --- a/apex/testing/Android.bp +++ b/apex/testing/Android.bp
@@ -12,10 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "external_conscrypt_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["external_conscrypt_license"], +} + apex_test { name: "test_com.android.conscrypt", visibility: [ "//system/apex/tests", + "//vendor:__subpackages__", ], defaults: ["com.android.conscrypt-defaults"], manifest: "test_apex_manifest.json",
diff --git a/apex/tests/Android.bp b/apex/tests/Android.bp index 408d95f..e8c85cb 100644 --- a/apex/tests/Android.bp +++ b/apex/tests/Android.bp
@@ -12,10 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "external_conscrypt_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["external_conscrypt_license"], +} + android_test { name: "MtsConscryptTestCases", platform_apis: true, - defaults: ["cts_defaults"], static_libs: [ "conscrypt-support", "conscrypt-tests", @@ -33,6 +41,32 @@ // Tag this module as an mts test artifact test_suites: [ "general-tests", + "mts-conscrypt", + ], + +} + +android_test { + name: "MtsConscryptFdSocketTestCases", + platform_apis: true, + static_libs: [ + "conscrypt-support", + "conscrypt-tests", + "core-test-rules", + "ctstestrunner-axt", + "libcore-crypto-tests", + "junit", + ], + + test_config: "FdSocket.xml", + min_sdk_version: "29", + libs: [ + "android.test.base.stubs", + ], + + // Tag this module as an mts test artifact + test_suites: [ + "general-tests", "mts", ],
diff --git a/apex/tests/AndroidTest.xml b/apex/tests/AndroidTest.xml index 9e38f83..7d03650 100644 --- a/apex/tests/AndroidTest.xml +++ b/apex/tests/AndroidTest.xml
@@ -19,6 +19,7 @@ <option name="config-descriptor:metadata" key="component" value="libcore" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.conscrypt.apex" /> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="MtsConscryptTestCases.apk" /> @@ -33,26 +34,6 @@ <option name="device-listeners" value="org.conscrypt.ConscryptInstrumentationListener" /> <option name="instrumentation-arg" key="conscrypt_sslsocket_implementation" value="engine" /> </test> - <!-- Re-run a subset of tests using Conscrypt's file-descriptor based implementation to ensure - there are no regressions in this implementation before it is fully deprecated. - - Apart from the include filters and SSLSocket implementation this test suite is - idential to the one above. - --> - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="com.android.conscrypt.mts" /> - <option name="include-filter" value="libcore.javax.net.ssl" /> - <option name="include-filter" value="com.android.org.conscrypt.javax.net.ssl" /> - <option name="include-filter" value="org.apache.harmony.tests.javax.net.ssl" /> - <!-- module-libs APIs are not accessible to app code by default intentionally. --> - <option name="hidden-api-checks" value="false" /> - <option name="runtime-hint" value="1m" /> - <!-- test-timeout unit is ms, value = 20 min --> - <option name="test-timeout" value="1200000" /> - <option name="device-listeners" value="org.conscrypt.ConscryptInstrumentationListener" /> - <option name="instrumentation-arg" key="conscrypt_sslsocket_implementation" value="fd" /> - </test> - <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> <option name="mainline-module-package-name" value="com.google.android.conscrypt" /> </object>
diff --git a/apex/tests/FdSocket.xml b/apex/tests/FdSocket.xml new file mode 100644 index 0000000..58b080d --- /dev/null +++ b/apex/tests/FdSocket.xml
@@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 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. + ~ + ~ Re-runs a subset of MtsConscryptTestCases using Conscrypt's file-descriptor based + ~ implementation to ensure there are no regressions in this implementation before + ~ it is fully deprecated. + ~ + ~ Apart from the include filters and SSLSocket implementation this test suite is + ~ identical to MtsConscryptTestCases. + --> +<configuration description="Config for MTS tests for the Conscrypt mainline module"> + <option name="test-suite-tag" value="mts" /> + <option name="config-descriptor:metadata" key="component" value="libcore" /> + <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.conscrypt.apex" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="MtsConscryptFdSocketTestCases.apk" /> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.conscrypt.mts" /> + <option name="include-filter" value="libcore.javax.net.ssl" /> + <option name="include-filter" value="com.android.org.conscrypt.javax.net.ssl" /> + <option name="include-filter" value="org.apache.harmony.tests.javax.net.ssl" /> + <!-- module-libs APIs are not accessible to app code by default intentionally. --> + <option name="hidden-api-checks" value="false" /> + <option name="runtime-hint" value="1m" /> + <!-- test-timeout unit is ms, value = 20 min --> + <option name="test-timeout" value="1200000" /> + <option name="device-listeners" value="org.conscrypt.ConscryptInstrumentationListener" /> + <option name="instrumentation-arg" key="conscrypt_sslsocket_implementation" value="fd" /> + </test> + + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> + <option name="mainline-module-package-name" value="com.google.android.conscrypt" /> + </object> +</configuration>
diff --git a/apex/tests/TEST_MAPPING b/apex/tests/TEST_MAPPING new file mode 100644 index 0000000..8b9c9fa --- /dev/null +++ b/apex/tests/TEST_MAPPING
@@ -0,0 +1,41 @@ +{ + "mainline-presubmit": [ + { + "name": "MtsConscryptTestCases[com.google.android.conscrypt.apex]", + "options": [ + { + "exclude-filter": "org.apache.harmony.crypto.tests.javax.crypto.func.KeyAgreementFunctionalTest#test_KeyAgreement" + }, + { + "exclude-filter": "com.android.org.conscrypt.java.security.AlgorithmParameterGeneratorTestDH#testAlgorithmParameterGenerator" + }, + { + "exclude-filter": "org.apache.harmony.crypto.tests.javax.crypto.KeyAgreementTest#test_generateSecretLjava_lang_String" + }, + { + "exclude-filter": "org.apache.harmony.tests.javax.net.ssl.TrustManagerFactory1Test#test_initLjavax_net_ssl_ManagerFactoryParameters" + }, + { + "exclude-filter": "com.android.org.conscrypt.java.security.SignatureTest#test_getInstance" + } + ] + }, + { + "name": "MtsConscryptFdSocketTestCases[com.google.android.conscrypt.apex]", + "options": [ + { + "exclude-filter": "org.apache.harmony.crypto.tests.javax.crypto.func.KeyAgreementFunctionalTest#test_KeyAgreement" + }, + { + "exclude-filter": "com.android.org.conscrypt.java.security.AlgorithmParameterGeneratorTestDH#testAlgorithmParameterGenerator" + }, + { + "exclude-filter": "org.apache.harmony.crypto.tests.javax.crypto.KeyAgreementTest#test_generateSecretLjava_lang_String" + }, + { + "exclude-filter": "org.apache.harmony.tests.javax.net.ssl.TrustManagerFactory1Test#test_initLjavax_net_ssl_ManagerFactoryParameters" + } + ] + } + ] +}
diff --git a/api-doclet/build.gradle b/api-doclet/build.gradle index 65ad223..229e9d1 100644 --- a/api-doclet/build.gradle +++ b/api-doclet/build.gradle
@@ -1,6 +1,8 @@ description = 'Conscrypt: API Doclet' -dependencies { - // This should be removed when upgrading to Gradle 5.x - compile files(org.gradle.internal.jvm.Jvm.current().toolsJar) +def toolsJar = org.gradle.internal.jvm.Jvm.current().toolsJar +if (toolsJar != null) { + dependencies { + compile files(toolsJar) + } }
diff --git a/api/public/last-removed.txt b/api/intra/last-incompatibilities.txt similarity index 100% rename from api/public/last-removed.txt rename to api/intra/last-incompatibilities.txt
diff --git a/api/public/last-removed.txt b/api/platform/last-incompatibilities.txt similarity index 100% copy from api/public/last-removed.txt copy to api/platform/last-incompatibilities.txt
diff --git a/api/public/current.txt b/api/public/current.txt index fb4a405..809495c 100644 --- a/api/public/current.txt +++ b/api/public/current.txt
@@ -2,11 +2,13 @@ package android.net.ssl { public class SSLEngines { + method @Nullable public static byte[] exportKeyingMaterial(@NonNull javax.net.ssl.SSLEngine, @NonNull String, @Nullable byte[], int) throws javax.net.ssl.SSLException; method public static boolean isSupportedEngine(@NonNull javax.net.ssl.SSLEngine); method public static void setUseSessionTickets(@NonNull javax.net.ssl.SSLEngine, boolean); } public class SSLSockets { + method @Nullable public static byte[] exportKeyingMaterial(@NonNull javax.net.ssl.SSLSocket, @NonNull String, @Nullable byte[], int) throws javax.net.ssl.SSLException; method public static boolean isSupportedSocket(@NonNull javax.net.ssl.SSLSocket); method public static void setUseSessionTickets(@NonNull javax.net.ssl.SSLSocket, boolean); }
diff --git a/api/public/last-api.txt b/api/public/last-api.txt deleted file mode 100644 index fb4a405..0000000 --- a/api/public/last-api.txt +++ /dev/null
@@ -1,15 +0,0 @@ -// Signature format: 2.0 -package android.net.ssl { - - public class SSLEngines { - method public static boolean isSupportedEngine(@NonNull javax.net.ssl.SSLEngine); - method public static void setUseSessionTickets(@NonNull javax.net.ssl.SSLEngine, boolean); - } - - public class SSLSockets { - method public static boolean isSupportedSocket(@NonNull javax.net.ssl.SSLSocket); - method public static void setUseSessionTickets(@NonNull javax.net.ssl.SSLSocket, boolean); - } - -} -
diff --git a/benchmark-android/build.gradle b/benchmark-android/build.gradle index 6be26d0..9673586 100644 --- a/benchmark-android/build.gradle +++ b/benchmark-android/build.gradle
@@ -69,7 +69,7 @@ libraries.bouncycastle_apis depsJarImplementation project(':conscrypt-benchmark-base'), - project(':conscrypt-testing'), + project(path: ":conscrypt-testing", configuration: "runtime"), project(':conscrypt-libcore-stub') implementation 'com.google.caliper:caliper:1.0-beta-2' @@ -83,7 +83,7 @@ // the .aar and .jar contents before the actual archives are built. To do this we create a // configure task where the "from" contents is set inside a doLast stanza to ensure it is run // after the execution phase of the "assemble" task. - task configureDepsJar { + def configureDepsJar = tasks.register("configureDepsJar") { dependsOn assemble, \ configurations.depsJarApi.artifacts, \ configurations.depsJarImplementation.artifacts @@ -124,11 +124,12 @@ } } - task depsJar(type: Jar, dependsOn: configureDepsJar) { + def depsJar = tasks.register("depsJar", Jar) { + dependsOn configureDepsJar archiveName = 'bundled-deps.jar' } - task getAndroidDeviceAbi { + def getAndroidDeviceAbi = tasks.register("getAndroidDeviceAbi") { doLast { new ByteArrayOutputStream().withStream { os -> def result = exec { @@ -142,7 +143,7 @@ } } - task configureExtractNativeLib { + def configureExtractNativeLib = tasks.register("configureExtractNativeLib") { dependsOn getAndroidDeviceAbi, depsJar doLast { extractNativeLib.from { @@ -156,11 +157,12 @@ } } - task extractNativeLib(type: Copy, dependsOn: configureExtractNativeLib) { + def extractNativeLib = tasks.register("extractNativeLib", Copy) { + dependsOn configureExtractNativeLib into "$buildDir/extracted-native-libs" } - task configurePushNativeLibrary { + def configurePushNativeLibrary = tasks.register("configurePushNativeLibrary") { dependsOn extractNativeLib doLast { project.ext.nativeLibPath = "/system/lib${androidDevice64Bit ? '64' : ''}/libconscrypt_jni.so" @@ -168,11 +170,13 @@ } } - task pushNativeLibrary(type: Exec, dependsOn: configurePushNativeLibrary) { + def pushNativeLibrary = tasks.register("pushNativeLibrary", Exec) { + dependsOn configurePushNativeLibrary pushNativeLibrary.executable android.adbExecutable } - task runBenchmarks(dependsOn: [depsJar, pushNativeLibrary]) { + def runBenchmarks = tasks.register("runBenchmarks") { + dependsOn depsJar, pushNativeLibrary doLast { // Execute the benchmarks exec { @@ -202,7 +206,7 @@ logger.warn('Android SDK has not been detected. The Android Benchmark module will not be built.') // Disable all tasks - tasks.collect { + tasks.configureEach { it.enabled = false } }
diff --git a/benchmark-base/build.gradle b/benchmark-base/build.gradle index a47943a..a69fee7 100644 --- a/benchmark-base/build.gradle +++ b/benchmark-base/build.gradle
@@ -5,6 +5,6 @@ targetCompatibility = androidMinJavaVersion dependencies { - compile project(':conscrypt-testing'), + compile project(path: ":conscrypt-testing", configuration: "runtime"), libraries.junit }
diff --git a/benchmark-jmh/build.gradle b/benchmark-jmh/build.gradle index 2d4ebce..35611fe 100644 --- a/benchmark-jmh/build.gradle +++ b/benchmark-jmh/build.gradle
@@ -69,7 +69,7 @@ } dependencies { - compile project(':conscrypt-openjdk'), + compile project(path: ":conscrypt-openjdk", configuration: "runtime"), project(':conscrypt-benchmark-base'), // Add the preferred native openjdk configuration for this platform. project(':conscrypt-openjdk').sourceSets["$preferredSourceSet"].output,
diff --git a/build.gradle b/build.gradle index 858425d..162c491 100644 --- a/build.gradle +++ b/build.gradle
@@ -1,8 +1,8 @@ import org.gradle.util.VersionNumber buildscript { - ext.android_tools = 'com.android.tools.build:gradle:3.5.0' - ext.errorproneVersion = '2.3.3' + ext.android_tools = 'com.android.tools.build:gradle:4.1.0' + ext.errorproneVersion = '2.4.0' ext.errorproneJavacVersion = '9+181-r4173-1' repositories { google() @@ -20,7 +20,7 @@ // Add dependency for build script so we can access Git from our // build script. id 'org.ajoberstar.grgit' version '3.1.1' - id 'net.ltgt.errorprone' version '1.1.1' + id 'net.ltgt.errorprone' version '1.3.0' } subprojects { @@ -51,7 +51,7 @@ group = "org.conscrypt" description = 'Conscrypt is an alternate Java Security Provider that uses BoringSSL' - version = "2.5.0-SNAPSHOT" + version = "2.6-SNAPSHOT" ext { os = org.gradle.internal.os.OperatingSystem.current(); @@ -153,7 +153,7 @@ errorproneJavac("com.google.errorprone:javac:$errorproneJavacVersion") } - task generateProperties(type: WriteProperties) { + tasks.register("generateProperties", WriteProperties) { ext { parsedVersion = VersionNumber.parse(version) } @@ -168,26 +168,30 @@ sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 - [compileJava, compileTestJava].each() { - it.options.compilerArgs += ["-Xlint:all", "-Xlint:-options", '-Xmaxwarns', '9999999'] - it.options.encoding = "UTF-8" - if (rootProject.hasProperty('failOnWarnings') && rootProject.failOnWarnings.toBoolean()) { - it.options.compilerArgs += ["-Werror"] + [tasks.named("compileJava"), tasks.named("compileTestJava")].forEach { t -> + t.configure { + options.compilerArgs += ["-Xlint:all", "-Xlint:-options", '-Xmaxwarns', '9999999'] + options.encoding = "UTF-8" + if (rootProject.hasProperty('failOnWarnings') && rootProject.failOnWarnings.toBoolean()) { + options.compilerArgs += ["-Werror"] + } } } - compileTestJava { + tasks.named("compileTestJava").configure { // serialVersionUID is basically guaranteed to be useless in our tests options.compilerArgs += ["-Xlint:-serial"] } - jar.manifest { - attributes('Implementation-Title': name, - 'Implementation-Version': version, - 'Built-By': System.getProperty('user.name'), - 'Built-JDK': System.getProperty('java.version'), - 'Source-Compatibility': sourceCompatibility, - 'Target-Compatibility': targetCompatibility) + tasks.named("jar").configure { + manifest { + attributes('Implementation-Title': name, + 'Implementation-Version': version, + 'Built-By': System.getProperty('user.name'), + 'Built-JDK': System.getProperty('java.version'), + 'Source-Compatibility': sourceCompatibility, + 'Target-Compatibility': targetCompatibility) + } } javadoc.options { @@ -204,12 +208,12 @@ } } - task javadocJar(type: Jar) { + tasks.register("javadocJar", Jar) { classifier = 'javadoc' from javadoc } - task sourcesJar(type: Jar) { + tasks.register("sourcesJar", Jar) { classifier = 'sources' from sourceSets.main.allSource }
diff --git a/common/src/jni/main/cpp/conscrypt/jniutil.cc b/common/src/jni/main/cpp/conscrypt/jniutil.cc index 384bcb8..c30adf1 100644 --- a/common/src/jni/main/cpp/conscrypt/jniutil.cc +++ b/common/src/jni/main/cpp/conscrypt/jniutil.cc
@@ -37,6 +37,8 @@ jclass inputStreamClass; jclass outputStreamClass; jclass stringClass; +jclass byteBufferClass; +jclass bufferClass; jfieldID nativeRef_address; @@ -46,6 +48,8 @@ jmethodID openSslInputStream_readLineMethod; jmethodID outputStream_writeMethod; jmethodID outputStream_flushMethod; +jmethodID buffer_positionMethod; +jmethodID buffer_limitMethod; void init(JavaVM* vm, JNIEnv* env) { gJavaVM = vm; @@ -58,6 +62,8 @@ objectArrayClass = findClass(env, "[Ljava/lang/Object;"); outputStreamClass = findClass(env, "java/io/OutputStream"); stringClass = findClass(env, "java/lang/String"); + byteBufferClass = findClass(env, "java/nio/ByteBuffer"); + bufferClass = findClass(env, "java/nio/Buffer"); cryptoUpcallsClass = getGlobalRefToClass( env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/CryptoUpcalls"); @@ -76,6 +82,8 @@ getMethodRef(env, openSslInputStreamClass, "gets", "([B)I"); outputStream_writeMethod = getMethodRef(env, outputStreamClass, "write", "([B)V"); outputStream_flushMethod = getMethodRef(env, outputStreamClass, "flush", "()V"); + buffer_positionMethod = getMethodRef(env, bufferClass, "position", "()I"); + buffer_limitMethod = getMethodRef(env, bufferClass, "limit", "()I"); } void jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods,
diff --git a/common/src/jni/main/cpp/conscrypt/native_crypto.cc b/common/src/jni/main/cpp/conscrypt/native_crypto.cc index c739546..5b0acf0 100644 --- a/common/src/jni/main/cpp/conscrypt/native_crypto.cc +++ b/common/src/jni/main/cpp/conscrypt/native_crypto.cc
@@ -37,6 +37,9 @@ #include <openssl/aead.h> #include <openssl/asn1.h> #include <openssl/chacha.h> +#include <openssl/curve25519.h> +#include <openssl/cmac.h> +#include <openssl/crypto.h> #include <openssl/engine.h> #include <openssl/err.h> #include <openssl/evp.h> @@ -90,9 +93,9 @@ return ssl; } -static BIO* to_SSL_BIO(JNIEnv* env, jlong bio_address, bool throwIfNull) { +static BIO* to_BIO(JNIEnv* env, jlong bio_address) { BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bio_address)); - if ((bio == nullptr) && throwIfNull) { + if (bio == nullptr) { JNI_TRACE("bio == null"); conscrypt::jniutil::throwNullPointerException(env, "bio == null"); } @@ -427,8 +430,8 @@ /** * Converts ASN.1 BIT STRING to a jbooleanArray. */ -jbooleanArray ASN1BitStringToBooleanArray(JNIEnv* env, ASN1_BIT_STRING* bitStr) { - int size = bitStr->length * 8; +jbooleanArray ASN1BitStringToBooleanArray(JNIEnv* env, const ASN1_BIT_STRING* bitStr) { + int size = ASN1_STRING_length(bitStr) * 8; if (bitStr->flags & ASN1_STRING_FLAG_BITS_LEFT) { size -= bitStr->flags & 0x07; } @@ -1078,7 +1081,7 @@ return -1; } - int result = EVP_PKEY_type(pkey->type); + int result = EVP_PKEY_id(pkey); JNI_TRACE("EVP_PKEY_type(%p) => %d", pkey, result); return result; } @@ -1529,13 +1532,13 @@ return nullptr; } - jbyteArray n = bignumToArray(env, rsa->n, "n"); + jbyteArray n = bignumToArray(env, RSA_get0_n(rsa.get()), "n"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 0, n); - jbyteArray e = bignumToArray(env, rsa->e, "e"); + jbyteArray e = bignumToArray(env, RSA_get0_e(rsa.get()), "e"); if (env->ExceptionCheck()) { return nullptr; } @@ -1567,62 +1570,62 @@ return nullptr; } - jbyteArray n = bignumToArray(env, rsa->n, "n"); + jbyteArray n = bignumToArray(env, RSA_get0_n(rsa.get()), "n"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 0, n); - if (rsa->e != nullptr) { - jbyteArray e = bignumToArray(env, rsa->e, "e"); + if (RSA_get0_e(rsa.get()) != nullptr) { + jbyteArray e = bignumToArray(env, RSA_get0_e(rsa.get()), "e"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 1, e); } - if (rsa->d != nullptr) { - jbyteArray d = bignumToArray(env, rsa->d, "d"); + if (RSA_get0_d(rsa.get()) != nullptr) { + jbyteArray d = bignumToArray(env, RSA_get0_d(rsa.get()), "d"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 2, d); } - if (rsa->p != nullptr) { - jbyteArray p = bignumToArray(env, rsa->p, "p"); + if (RSA_get0_p(rsa.get()) != nullptr) { + jbyteArray p = bignumToArray(env, RSA_get0_p(rsa.get()), "p"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 3, p); } - if (rsa->q != nullptr) { - jbyteArray q = bignumToArray(env, rsa->q, "q"); + if (RSA_get0_q(rsa.get()) != nullptr) { + jbyteArray q = bignumToArray(env, RSA_get0_q(rsa.get()), "q"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 4, q); } - if (rsa->dmp1 != nullptr) { - jbyteArray dmp1 = bignumToArray(env, rsa->dmp1, "dmp1"); + if (RSA_get0_dmp1(rsa.get()) != nullptr) { + jbyteArray dmp1 = bignumToArray(env, RSA_get0_dmp1(rsa.get()), "dmp1"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 5, dmp1); } - if (rsa->dmq1 != nullptr) { - jbyteArray dmq1 = bignumToArray(env, rsa->dmq1, "dmq1"); + if (RSA_get0_dmq1(rsa.get()) != nullptr) { + jbyteArray dmq1 = bignumToArray(env, RSA_get0_dmq1(rsa.get()), "dmq1"); if (env->ExceptionCheck()) { return nullptr; } env->SetObjectArrayElement(joa, 6, dmq1); } - if (rsa->iqmp != nullptr) { - jbyteArray iqmp = bignumToArray(env, rsa->iqmp, "iqmp"); + if (RSA_get0_iqmp(rsa.get()) != nullptr) { + jbyteArray iqmp = bignumToArray(env, RSA_get0_iqmp(rsa.get()), "iqmp"); if (env->ExceptionCheck()) { return nullptr; } @@ -2128,14 +2131,13 @@ return 0; } - if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) { + if (EVP_PKEY_id(pkey) != EVP_PKEY_EC) { conscrypt::jniutil::throwRuntimeException(env, "not EC key"); - JNI_TRACE("EC_KEY_get1_group(%p) => not EC key (type == %d)", pkey, - EVP_PKEY_type(pkey->type)); + JNI_TRACE("EC_KEY_get1_group(%p) => not EC key (type == %d)", pkey, EVP_PKEY_id(pkey)); return 0; } - EC_GROUP* group = EC_GROUP_dup(EC_KEY_get0_group(pkey->pkey.ec)); + EC_GROUP* group = EC_GROUP_dup(EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey))); JNI_TRACE("EC_KEY_get1_group(%p) => %p", pkey, group); return reinterpret_cast<uintptr_t>(group); } @@ -2440,6 +2442,67 @@ return static_cast<jint>(result); } +static jboolean NativeCrypto_X25519(JNIEnv* env, jclass, jbyteArray outArray, + jbyteArray privkeyArray, jbyteArray pubkeyArray) { + CHECK_ERROR_QUEUE_ON_RETURN; + JNI_TRACE("X25519(%p, %p, %p)", outArray, privkeyArray, pubkeyArray); + + ScopedByteArrayRW out(env, outArray); + if (out.get() == nullptr) { + JNI_TRACE("X25519(%p, %p, %p) can't get output buffer", outArray, privkeyArray, pubkeyArray); + return JNI_FALSE; + } + + ScopedByteArrayRO privkey(env, privkeyArray); + if (privkey.get() == nullptr) { + JNI_TRACE("X25519(%p) => privkey == null", outArray); + return JNI_FALSE; + } + + ScopedByteArrayRO pubkey(env, pubkeyArray); + if (pubkey.get() == nullptr) { + JNI_TRACE("X25519(%p) => pubkey == null", outArray); + return JNI_FALSE; + } + + if (X25519(reinterpret_cast<uint8_t*>(out.get()), + reinterpret_cast<const uint8_t*>(privkey.get()), + reinterpret_cast<const uint8_t*>(pubkey.get())) != 1) { + JNI_TRACE("X25519(%p) => failure", outArray); + conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "X25519", + conscrypt::jniutil::throwInvalidKeyException); + return JNI_FALSE; + } + + JNI_TRACE("X25519(%p) => success", outArray); + return JNI_TRUE; +} + +static void NativeCrypto_X25519_keypair(JNIEnv* env, jclass, jbyteArray outPublicArray, jbyteArray outPrivateArray) { + CHECK_ERROR_QUEUE_ON_RETURN; + JNI_TRACE("X25519_keypair(%p, %p)", outPublicArray, outPrivateArray); + + ScopedByteArrayRW outPublic(env, outPublicArray); + if (outPublic.get() == nullptr) { + JNI_TRACE("X25519_keypair(%p, %p) can't get output public key buffer", outPublicArray, outPrivateArray); + return; + } + + ScopedByteArrayRW outPrivate(env, outPrivateArray); + if (outPrivate.get() == nullptr) { + JNI_TRACE("X25519_keypair(%p, %p) can't get output private key buffer", outPublicArray, outPrivateArray); + return; + } + + if (outPublic.size() != X25519_PUBLIC_VALUE_LEN || outPrivate.size() != X25519_PRIVATE_KEY_LEN) { + conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException", "Output key array length != 32"); + return; + } + + X25519_keypair(reinterpret_cast<uint8_t*>(outPublic.get()), reinterpret_cast<uint8_t*>(outPrivate.get())); + JNI_TRACE("X25519_keypair(%p, %p) => success", outPublicArray, outPrivateArray); +} + static jlong NativeCrypto_EVP_MD_CTX_create(JNIEnv* env, jclass) { CHECK_ERROR_QUEUE_ON_RETURN; JNI_TRACE_MD("EVP_MD_CTX_create()"); @@ -3511,6 +3574,63 @@ const uint8_t* in, size_t in_len, const uint8_t* ad, size_t ad_len); +static jint evp_aead_ctx_op_common(JNIEnv* env, jlong evpAeadRef, jbyteArray keyArray, jint tagLen, + uint8_t* outBuf, jbyteArray nonceArray, + const uint8_t* inBuf, jbyteArray aadArray, + evp_aead_ctx_op_func realFunc, jobject inBuffer, jobject outBuffer, jint outRange, jint inRange) { + const EVP_AEAD* evpAead = reinterpret_cast<const EVP_AEAD*>(evpAeadRef); + + ScopedByteArrayRO keyBytes(env, keyArray); + if (keyBytes.get() == nullptr) { + return 0; + } + + std::unique_ptr<ScopedByteArrayRO> aad; + const uint8_t* aad_chars = nullptr; + size_t aad_chars_size = 0; + if (aadArray != nullptr) { + aad.reset(new ScopedByteArrayRO(env, aadArray)); + aad_chars = reinterpret_cast<const uint8_t*>(aad->get()); + if (aad_chars == nullptr) { + return 0; + } + aad_chars_size = aad->size(); + } + + ScopedByteArrayRO nonceBytes(env, nonceArray); + if (nonceBytes.get() == nullptr) { + return 0; + } + + bssl::ScopedEVP_AEAD_CTX aeadCtx; + const uint8_t* keyTmp = reinterpret_cast<const uint8_t*>(keyBytes.get()); + if (!EVP_AEAD_CTX_init(aeadCtx.get(), evpAead, keyTmp, keyBytes.size(), + static_cast<size_t>(tagLen), nullptr)) { + conscrypt::jniutil::throwExceptionFromBoringSSLError(env, + "failure initializing AEAD context"); + JNI_TRACE( + "evp_aead_ctx_op(%p, %p, %d, %p, %p, %p, %p) => fail EVP_AEAD_CTX_init", + evpAead, keyArray, tagLen, outBuffer, nonceArray, inBuffer, + aadArray); + return 0; + } + + const uint8_t* nonceTmp = reinterpret_cast<const uint8_t*>(nonceBytes.get()); + size_t actualOutLength; + + if (!realFunc(aeadCtx.get(), outBuf, &actualOutLength, outRange, + nonceTmp, nonceBytes.size(), inBuf, static_cast<size_t>(inRange), + aad_chars, aad_chars_size)) { + conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "evp_aead_ctx_op"); + return 0; + } + + JNI_TRACE("evp_aead_ctx_op(%p, %p, %d, %p, %p, %p, %p) => success outlength=%zd", + evpAead, keyArray, tagLen, outBuffer, nonceArray, inBuffer, + aadArray, actualOutLength); + return static_cast<jint>(actualOutLength); +} + static jint evp_aead_ctx_op(JNIEnv* env, jlong evpAeadRef, jbyteArray keyArray, jint tagLen, jbyteArray outArray, jint outOffset, jbyteArray nonceArray, jbyteArray inArray, jint inOffset, jint inLength, jbyteArray aadArray, @@ -3519,10 +3639,6 @@ JNI_TRACE("evp_aead_ctx_op(%p, %p, %d, %p, %d, %p, %p, %d, %d, %p)", evpAead, keyArray, tagLen, outArray, outOffset, nonceArray, inArray, inOffset, inLength, aadArray); - ScopedByteArrayRO keyBytes(env, keyArray); - if (keyBytes.get() == nullptr) { - return 0; - } ScopedByteArrayRW outBytes(env, outArray); if (outBytes.get() == nullptr) { @@ -3552,51 +3668,76 @@ return 0; } - std::unique_ptr<ScopedByteArrayRO> aad; - const uint8_t* aad_chars = nullptr; - size_t aad_chars_size = 0; - if (aadArray != nullptr) { - aad.reset(new ScopedByteArrayRO(env, aadArray)); - aad_chars = reinterpret_cast<const uint8_t*>(aad->get()); - if (aad_chars == nullptr) { - return 0; - } - aad_chars_size = aad->size(); - } - - ScopedByteArrayRO nonceBytes(env, nonceArray); - if (nonceBytes.get() == nullptr) { - return 0; - } - - bssl::ScopedEVP_AEAD_CTX aeadCtx; - const uint8_t* keyTmp = reinterpret_cast<const uint8_t*>(keyBytes.get()); - if (!EVP_AEAD_CTX_init(aeadCtx.get(), evpAead, keyTmp, keyBytes.size(), - static_cast<size_t>(tagLen), nullptr)) { - conscrypt::jniutil::throwExceptionFromBoringSSLError(env, - "failure initializing AEAD context"); - JNI_TRACE( - "evp_aead_ctx_op(%p, %p, %d, %p, %d, %p, %p, %d, %d, %p) => fail EVP_AEAD_CTX_init", - evpAead, keyArray, tagLen, outArray, outOffset, nonceArray, inArray, inOffset, - inLength, aadArray); - return 0; - } - uint8_t* outTmp = reinterpret_cast<uint8_t*>(outBytes.get()); const uint8_t* inTmp = reinterpret_cast<const uint8_t*>(inBytes.get()); - const uint8_t* nonceTmp = reinterpret_cast<const uint8_t*>(nonceBytes.get()); - size_t actualOutLength; - if (!realFunc(aeadCtx.get(), outTmp + outOffset, &actualOutLength, outBytes.size() - outOffset, - nonceTmp, nonceBytes.size(), inTmp + inOffset, static_cast<size_t>(inLength), - aad_chars, aad_chars_size)) { - conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "evp_aead_ctx_op"); + + return evp_aead_ctx_op_common(env, evpAeadRef, keyArray, tagLen, outTmp + outOffset, nonceArray, inTmp + inOffset, + aadArray, realFunc, inArray, outArray, outBytes.size() - outOffset, inLength); +} + +static jint evp_aead_ctx_op_buf(JNIEnv* env, jlong evpAeadRef, jbyteArray keyArray, jint tagLen, + jobject outBuffer, jbyteArray nonceArray, + jobject inBuffer, jbyteArray aadArray, + evp_aead_ctx_op_func realFunc) { + + const EVP_AEAD* evpAead = reinterpret_cast<const EVP_AEAD*>(evpAeadRef); + 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"); return 0; } - JNI_TRACE("evp_aead_ctx_op(%p, %p, %d, %p, %d, %p, %p, %d, %d, %p) => success outlength=%zd", - evpAead, keyArray, tagLen, outArray, outOffset, nonceArray, inArray, inOffset, - inLength, aadArray, actualOutLength); - return static_cast<jint>(actualOutLength); + 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); + // position is the index of the next element to be read or written + in_position = env->CallIntMethod(inBuffer,conscrypt::jniutil::buffer_positionMethod); + + 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); + // position is the index of the next element to be read or written + out_position = env->CallIntMethod(outBuffer,conscrypt::jniutil::buffer_positionMethod); + + // Shifting over of ByteBuffer address to start at true position + inBuf += in_position; + outBuf += out_position; + + size_t inSize = in_limit - in_position; + uint8_t* outBufEnd = outBuf + out_limit - out_position; + uint8_t* inBufEnd = inBuf + inSize; + std::unique_ptr<uint8_t[]> inCopy; + if (outBufEnd >= inBuf && inBufEnd >= outBuf) { // We have an overlap + inCopy.reset((new(std::nothrow) uint8_t[inSize])); + if (inCopy.get() == nullptr) { + conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate new buffer for overlap"); + return 0; + } + memcpy(inCopy.get(), inBuf, inSize); + inBuf = inCopy.get(); + } + + return evp_aead_ctx_op_common(env, evpAeadRef, keyArray, tagLen, outBuf, nonceArray, inBuf, aadArray, realFunc, + inBuffer, outBuffer, out_limit-out_position, in_limit-in_position); } static jint NativeCrypto_EVP_AEAD_CTX_seal(JNIEnv* env, jclass, jlong evpAeadRef, @@ -3619,6 +3760,166 @@ inArray, inOffset, inLength, aadArray, EVP_AEAD_CTX_open); } +static jint NativeCrypto_EVP_AEAD_CTX_seal_buf(JNIEnv* env, jclass, jlong evpAeadRef, + jbyteArray keyArray, jint tagLen, jobject outBuffer, + jbyteArray nonceArray, jobject inBuffer, jbyteArray aadArray) { + CHECK_ERROR_QUEUE_ON_RETURN; + return evp_aead_ctx_op_buf(env, evpAeadRef, keyArray, tagLen, outBuffer, nonceArray, + inBuffer, aadArray, EVP_AEAD_CTX_seal); +} + +static jint NativeCrypto_EVP_AEAD_CTX_open_buf(JNIEnv* env, jclass, jlong evpAeadRef, + jbyteArray keyArray, jint tagLen, jobject outBuffer, + jbyteArray nonceArray, jobject inBuffer, jbyteArray aadArray) { + CHECK_ERROR_QUEUE_ON_RETURN; + return evp_aead_ctx_op_buf(env, evpAeadRef, keyArray, tagLen, outBuffer, nonceArray, + inBuffer, aadArray, EVP_AEAD_CTX_open); +} + +static jlong NativeCrypto_CMAC_CTX_new(JNIEnv* env, jclass) { + CHECK_ERROR_QUEUE_ON_RETURN; + JNI_TRACE("CMAC_CTX_new"); + auto cmacCtx = CMAC_CTX_new(); + if (cmacCtx == nullptr) { + conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate CMAC_CTX"); + return 0; + } + + return reinterpret_cast<jlong>(cmacCtx); +} + +static void NativeCrypto_CMAC_CTX_free(JNIEnv* env, jclass, jlong cmacCtxRef) { + CHECK_ERROR_QUEUE_ON_RETURN; + CMAC_CTX* cmacCtx = reinterpret_cast<CMAC_CTX*>(cmacCtxRef); + JNI_TRACE("CMAC_CTX_free(%p)", cmacCtx); + if (cmacCtx == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "cmacCtx == null"); + return; + } + CMAC_CTX_free(cmacCtx); +} + +static void NativeCrypto_CMAC_Init(JNIEnv* env, jclass, jobject cmacCtxRef, jbyteArray keyArray) { + CHECK_ERROR_QUEUE_ON_RETURN; + CMAC_CTX* cmacCtx = fromContextObject<CMAC_CTX>(env, cmacCtxRef); + JNI_TRACE("CMAC_Init(%p, %p)", cmacCtx, keyArray); + if (cmacCtx == nullptr) { + return; + } + ScopedByteArrayRO keyBytes(env, keyArray); + if (keyBytes.get() == nullptr) { + return; + } + + const uint8_t* keyPtr = reinterpret_cast<const uint8_t*>(keyBytes.get()); + + const EVP_CIPHER *cipher; + switch(keyBytes.size()) { + case 16: + cipher = EVP_aes_128_cbc(); + break; + case 24: + cipher = EVP_aes_192_cbc(); + break; + case 32: + cipher = EVP_aes_256_cbc(); + break; + default: + conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException", + "CMAC_Init: Unsupported key length"); + return; + } + + if (!CMAC_Init(cmacCtx, keyPtr, keyBytes.size(), cipher, nullptr)) { + conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "CMAC_Init"); + JNI_TRACE("CMAC_Init(%p, %p) => fail CMAC_Init_ex", cmacCtx, keyArray); + return; + } +} + +static void NativeCrypto_CMAC_UpdateDirect(JNIEnv* env, jclass, jobject cmacCtxRef, jlong inPtr, + int inLength) { + CHECK_ERROR_QUEUE_ON_RETURN; + CMAC_CTX* cmacCtx = fromContextObject<CMAC_CTX>(env, cmacCtxRef); + const uint8_t* p = reinterpret_cast<const uint8_t*>(inPtr); + JNI_TRACE("CMAC_UpdateDirect(%p, %p, %d)", cmacCtx, p, inLength); + + if (cmacCtx == nullptr) { + return; + } + + if (p == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, nullptr); + return; + } + + if (!CMAC_Update(cmacCtx, p, static_cast<size_t>(inLength))) { + JNI_TRACE("CMAC_UpdateDirect(%p, %p, %d) => threw exception", cmacCtx, p, inLength); + conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "CMAC_UpdateDirect"); + return; + } +} + +static void NativeCrypto_CMAC_Update(JNIEnv* env, jclass, jobject cmacCtxRef, jbyteArray inArray, + jint inOffset, jint inLength) { + CHECK_ERROR_QUEUE_ON_RETURN; + CMAC_CTX* cmacCtx = fromContextObject<CMAC_CTX>(env, cmacCtxRef); + JNI_TRACE("CMAC_Update(%p, %p, %d, %d)", cmacCtx, inArray, inOffset, inLength); + + if (cmacCtx == nullptr) { + return; + } + + ScopedByteArrayRO inBytes(env, inArray); + if (inBytes.get() == nullptr) { + return; + } + + if (ARRAY_OFFSET_LENGTH_INVALID(inBytes, inOffset, inLength)) { + conscrypt::jniutil::throwException(env, "java/lang/ArrayIndexOutOfBoundsException", + "inBytes"); + return; + } + + const uint8_t* inPtr = reinterpret_cast<const uint8_t*>(inBytes.get()); + + if (!CMAC_Update(cmacCtx, inPtr + inOffset, static_cast<size_t>(inLength))) { + JNI_TRACE("CMAC_Update(%p, %p, %d, %d) => threw exception", cmacCtx, inArray, inOffset, + inLength); + conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "CMAC_Update"); + return; + } +} + +static jbyteArray NativeCrypto_CMAC_Final(JNIEnv* env, jclass, jobject cmacCtxRef) { + CHECK_ERROR_QUEUE_ON_RETURN; + CMAC_CTX* cmacCtx = fromContextObject<CMAC_CTX>(env, cmacCtxRef); + JNI_TRACE("CMAC_Final(%p)", cmacCtx); + + if (cmacCtx == nullptr) { + return nullptr; + } + + uint8_t result[EVP_MAX_MD_SIZE]; + size_t len; + if (!CMAC_Final(cmacCtx, result, &len)) { + JNI_TRACE("CMAC_Final(%p) => threw exception", cmacCtx); + conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "CMAC_Final"); + return nullptr; + } + + ScopedLocalRef<jbyteArray> resultArray(env, env->NewByteArray(static_cast<jsize>(len))); + if (resultArray.get() == nullptr) { + return nullptr; + } + ScopedByteArrayRW resultBytes(env, resultArray.get()); + if (resultBytes.get() == nullptr) { + return nullptr; + } + memcpy(resultBytes.get(), result, len); + return resultArray.release(); +} + static jlong NativeCrypto_HMAC_CTX_new(JNIEnv* env, jclass) { CHECK_ERROR_QUEUE_ON_RETURN; JNI_TRACE("HMAC_CTX_new"); @@ -3637,6 +3938,7 @@ HMAC_CTX* hmacCtx = reinterpret_cast<HMAC_CTX*>(hmacCtxRef); JNI_TRACE("HMAC_CTX_free(%p)", hmacCtx); if (hmacCtx == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "hmacCtx == null"); return; } HMAC_CTX_cleanup(hmacCtx); @@ -3689,7 +3991,7 @@ } static void NativeCrypto_HMAC_Update(JNIEnv* env, jclass, jobject hmacCtxRef, jbyteArray inArray, - jint inOffset, int inLength) { + jint inOffset, jint inLength) { CHECK_ERROR_QUEUE_ON_RETURN; HMAC_CTX* hmacCtx = fromContextObject<HMAC_CTX>(env, hmacCtxRef); JNI_TRACE("HMAC_Update(%p, %p, %d, %d)", hmacCtx, inArray, inOffset, inLength); @@ -3830,11 +4132,10 @@ static void NativeCrypto_BIO_free_all(JNIEnv* env, jclass, jlong bioRef) { CHECK_ERROR_QUEUE_ON_RETURN; - BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); + BIO* bio = to_BIO(env, bioRef); JNI_TRACE("BIO_free_all(%p)", bio); if (bio == nullptr) { - conscrypt::jniutil::throwNullPointerException(env, "bio == null"); return; } @@ -3948,22 +4249,26 @@ return nullptr; } - X509_check_ca(x509); - - STACK_OF(GENERAL_NAME) * gn_stack; - bssl::UniquePtr<STACK_OF(GENERAL_NAME)> stackHolder; + bssl::UniquePtr<STACK_OF(GENERAL_NAME)> gn_stack; if (type == GN_STACK_SUBJECT_ALT_NAME) { - gn_stack = x509->altname; + gn_stack.reset(static_cast<STACK_OF(GENERAL_NAME)*>( + X509_get_ext_d2i(x509, NID_subject_alt_name, nullptr, nullptr))); } else if (type == GN_STACK_ISSUER_ALT_NAME) { - stackHolder.reset(static_cast<STACK_OF(GENERAL_NAME)*>( + gn_stack.reset(static_cast<STACK_OF(GENERAL_NAME)*>( X509_get_ext_d2i(x509, NID_issuer_alt_name, nullptr, nullptr))); - gn_stack = stackHolder.get(); } else { JNI_TRACE("get_X509_GENERAL_NAME_stack(%p, %d) => unknown type", x509, type); return nullptr; } + // TODO(https://github.com/google/conscrypt/issues/916): Handle errors, remove + // |ERR_clear_error|, and throw CertificateParsingException. + if (gn_stack == nullptr) { + JNI_TRACE("get_X509_GENERAL_NAME_stack(%p, %d) => null (no extension or error)", x509, type); + ERR_clear_error(); + return nullptr; + } - int count = static_cast<int>(sk_GENERAL_NAME_num(gn_stack)); + int count = static_cast<int>(sk_GENERAL_NAME_num(gn_stack.get())); if (count <= 0) { JNI_TRACE("get_X509_GENERAL_NAME_stack(%p, %d) => null (no entries)", x509, type); return nullptr; @@ -3978,7 +4283,7 @@ ScopedLocalRef<jobjectArray> joa( env, env->NewObjectArray(count, conscrypt::jniutil::objectArrayClass, nullptr)); for (int i = 0, j = 0; i < origCount; i++, j++) { - GENERAL_NAME* gen = sk_GENERAL_NAME_value(gn_stack, static_cast<size_t>(i)); + GENERAL_NAME* gen = sk_GENERAL_NAME_value(gn_stack.get(), static_cast<size_t>(i)); ScopedLocalRef<jobject> val(env, GENERAL_NAME_to_jobject(env, gen)); if (env->ExceptionCheck()) { JNI_TRACE("get_X509_GENERAL_NAME_stack(%p, %d) => threw exception parsing gen name", @@ -4080,8 +4385,8 @@ } template <typename T> -static jbyteArray get_X509Type_serialNumber(JNIEnv* env, T* x509Type, - ASN1_INTEGER* (*get_serial_func)(T*)) { +static jbyteArray get_X509Type_serialNumber(JNIEnv* env, const T* x509Type, + const ASN1_INTEGER* (*get_serial_func)(const T*)) { JNI_TRACE("get_X509Type_serialNumber(%p)", x509Type); if (x509Type == nullptr) { @@ -4090,7 +4395,7 @@ return nullptr; } - ASN1_INTEGER* serialNumber = get_serial_func(x509Type); + const ASN1_INTEGER* serialNumber = get_serial_func(x509Type); bssl::UniquePtr<BIGNUM> serialBn(ASN1_INTEGER_to_BN(serialNumber, nullptr)); if (serialBn.get() == nullptr) { JNI_TRACE("X509_get_serialNumber(%p) => threw exception", x509Type); @@ -4107,19 +4412,12 @@ return serialArray.release(); } -/* OpenSSL includes set_serialNumber but not get. */ -#if !defined(X509_REVOKED_get_serialNumber) -static ASN1_INTEGER* X509_REVOKED_get_serialNumber(X509_REVOKED* x) { - return x->serialNumber; -} -#endif - static jbyteArray NativeCrypto_X509_get_serialNumber(JNIEnv* env, jclass, jlong x509Ref, CONSCRYPT_UNUSED jobject holder) { CHECK_ERROR_QUEUE_ON_RETURN; X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("X509_get_serialNumber(%p)", x509); - return get_X509Type_serialNumber<X509>(env, x509, X509_get_serialNumber); + return get_X509Type_serialNumber<X509>(env, x509, X509_get0_serialNumber); } static jbyteArray NativeCrypto_X509_REVOKED_get_serialNumber(JNIEnv* env, jclass, @@ -4127,7 +4425,7 @@ 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); - return get_X509Type_serialNumber<X509_REVOKED>(env, revoked, X509_REVOKED_get_serialNumber); + return get_X509Type_serialNumber<X509_REVOKED>(env, revoked, X509_REVOKED_get0_serialNumber); } static void NativeCrypto_X509_verify(JNIEnv* env, jclass, jlong x509Ref, @@ -4148,13 +4446,6 @@ return; } - if (X509_ALGOR_cmp(x509->sig_alg, X509_CINF_get_signature(X509_get_cert_info(x509)))) { - conscrypt::jniutil::throwCertificateException(env, - "Certificate signature algorithms do not match"); - JNI_TRACE("X509_verify(%p, %p) => signature alg mismatch", x509, pkey); - return; - } - if (X509_verify(x509, pkey) != 1) { conscrypt::jniutil::throwExceptionFromBoringSSLError( env, "X509_verify", conscrypt::jniutil::throwCertificateException); @@ -4164,12 +4455,63 @@ JNI_TRACE("X509_verify(%p, %p) => verify success", x509, pkey); } -static jbyteArray NativeCrypto_get_X509_cert_info_enc(JNIEnv* env, jclass, jlong x509Ref, - CONSCRYPT_UNUSED jobject holder) { +static jbyteArray NativeCrypto_get_X509_tbs_cert(JNIEnv* env, jclass, jlong x509Ref, + CONSCRYPT_UNUSED jobject holder) { CHECK_ERROR_QUEUE_ON_RETURN; X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); - JNI_TRACE("get_X509_cert_info_enc(%p)", x509); - return ASN1ToByteArray<X509_CINF>(env, x509->cert_info, i2d_X509_CINF); + JNI_TRACE("get_X509_tbs_cert(%p)", x509); + // Note |i2d_X509_tbs| preserves the original encoding of the TBSCertificate. + return ASN1ToByteArray<X509>(env, x509, i2d_X509_tbs); +} + +static jbyteArray NativeCrypto_get_X509_tbs_cert_without_ext(JNIEnv* env, jclass, jlong x509Ref, + CONSCRYPT_UNUSED jobject holder, + jstring oidString) { + CHECK_ERROR_QUEUE_ON_RETURN; + X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); + JNI_TRACE("get_X509_tbs_cert_without_ext(%p, %p)", x509, oidString); + + if (x509 == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "x509 == null"); + JNI_TRACE("get_X509_tbs_cert_without_ext(%p, %p) => x509 == null", x509, oidString); + return nullptr; + } + + bssl::UniquePtr<X509> copy(X509_dup(x509)); + if (copy == nullptr) { + conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "X509_dup"); + JNI_TRACE("get_X509_tbs_cert_without_ext(%p, %p) => threw error", x509, oidString); + return nullptr; + } + + ScopedUtfChars oid(env, oidString); + if (oid.c_str() == nullptr) { + JNI_TRACE("get_X509_tbs_cert_without_ext(%p, %p) => oidString == null", x509, oidString); + return nullptr; + } + + bssl::UniquePtr<ASN1_OBJECT> obj(OBJ_txt2obj(oid.c_str(), 1 /* allow numerical form only */)); + if (obj.get() == nullptr) { + JNI_TRACE("get_X509_tbs_cert_without_ext(%p, %s) => oid conversion failed", x509, + oid.c_str()); + conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException", + "Invalid OID."); + ERR_clear_error(); + return nullptr; + } + + int extIndex = X509_get_ext_by_OBJ(copy.get(), obj.get(), -1); + if (extIndex == -1) { + JNI_TRACE("get_X509_tbs_cert_without_ext(%p, %s) => ext not found", x509, oid.c_str()); + conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException", + "Extension not found."); + return nullptr; + } + + // Remove the extension and re-encode the TBSCertificate. Note |i2d_re_X509_tbs| ignores the + // cached encoding. + X509_EXTENSION_free(X509_delete_ext(copy.get(), extIndex)); + return ASN1ToByteArray<X509>(env, copy.get(), i2d_re_X509_tbs); } static jint NativeCrypto_get_X509_ex_flags(JNIEnv* env, jclass, jlong x509Ref, @@ -4184,14 +4526,21 @@ return 0; } - X509_check_ca(x509); - - return static_cast<jint>(x509->ex_flags); + uint32_t flags = X509_get_extension_flags(x509); + // X509_get_extension_flags sometimes leaves values in the error queue. See + // https://crbug.com/boringssl/382. + // + // TODO(https://github.com/google/conscrypt/issues/916): This function is used to check + // EXFLAG_CA, but does not check EXFLAG_INVALID. Fold the two JNI calls in getBasicConstraints() + // together and handle errors. (See also NativeCrypto_get_X509_ex_pathlen.) From there, limit + // this JNI call to EXFLAG_CRITICAL. + ERR_clear_error(); + return flags; } -static jboolean NativeCrypto_X509_check_issued(JNIEnv* env, jclass, jlong x509Ref1, - CONSCRYPT_UNUSED jobject holder, jlong x509Ref2, - CONSCRYPT_UNUSED jobject holder2) { +static jint NativeCrypto_X509_check_issued(JNIEnv* env, jclass, jlong x509Ref1, + CONSCRYPT_UNUSED jobject holder, jlong x509Ref2, + CONSCRYPT_UNUSED jobject holder2) { CHECK_ERROR_QUEUE_ON_RETURN; X509* x509_1 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref1)); X509* x509_2 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref2)); @@ -4199,20 +4548,24 @@ int ret = X509_check_issued(x509_1, x509_2); JNI_TRACE("X509_check_issued(%p, %p) => %d", x509_1, x509_2, ret); - return static_cast<jboolean>(ret); + return ret; } -static void get_X509_signature(X509* x509, ASN1_BIT_STRING** signature) { - *signature = x509->signature; +static const ASN1_BIT_STRING* get_X509_signature(X509* x509) { + const ASN1_BIT_STRING* signature; + X509_get0_signature(&signature, nullptr, x509); + return signature; } -static void get_X509_CRL_signature(X509_CRL* crl, ASN1_BIT_STRING** signature) { - *signature = crl->signature; +static const ASN1_BIT_STRING* get_X509_CRL_signature(X509_CRL* crl) { + const ASN1_BIT_STRING* signature; + X509_CRL_get0_signature(crl, &signature, nullptr); + return signature; } template <typename T> static jbyteArray get_X509Type_signature(JNIEnv* env, T* x509Type, - void (*get_signature_func)(T*, ASN1_BIT_STRING**)) { + const ASN1_BIT_STRING* (*get_signature_func)(T*)) { JNI_TRACE("get_X509Type_signature(%p)", x509Type); if (x509Type == nullptr) { @@ -4221,10 +4574,10 @@ return nullptr; } - ASN1_BIT_STRING* signature; - get_signature_func(x509Type, &signature); + const ASN1_BIT_STRING* signature = get_signature_func(x509Type); - ScopedLocalRef<jbyteArray> signatureArray(env, env->NewByteArray(signature->length)); + ScopedLocalRef<jbyteArray> signatureArray(env, + env->NewByteArray(ASN1_STRING_length(signature))); if (env->ExceptionCheck()) { JNI_TRACE("get_X509Type_signature(%p) => threw exception", x509Type); return nullptr; @@ -4236,10 +4589,10 @@ return nullptr; } - memcpy(signatureBytes.get(), signature->data, signature->length); + memcpy(signatureBytes.get(), ASN1_STRING_get0_data(signature), ASN1_STRING_length(signature)); JNI_TRACE("get_X509Type_signature(%p) => %p (%d bytes)", x509Type, signatureArray.get(), - signature->length); + ASN1_STRING_length(signature)); return signatureArray.release(); } @@ -4419,13 +4772,38 @@ X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef)); JNI_TRACE("get_X509_CRL_sig_alg_oid(%p)", crl); - if (crl == nullptr || crl->sig_alg == nullptr) { - conscrypt::jniutil::throwNullPointerException(env, "crl == null || crl->sig_alg == null"); + if (crl == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "crl == null"); JNI_TRACE("get_X509_CRL_sig_alg_oid(%p) => crl == null", crl); return nullptr; } - return ASN1_OBJECT_to_OID_string(env, crl->sig_alg->algorithm); + const X509_ALGOR *sig_alg; + X509_CRL_get0_signature(crl, nullptr, &sig_alg); + const ASN1_OBJECT *oid; + X509_ALGOR_get0(&oid, nullptr, nullptr, sig_alg); + return ASN1_OBJECT_to_OID_string(env, oid); +} + +static jbyteArray get_X509_ALGOR_parameter(JNIEnv* env, const X509_ALGOR *algor) { + int param_type; + const void* param_value; + X509_ALGOR_get0(nullptr, ¶m_type, ¶m_value, algor); + + if (param_type == V_ASN1_UNDEF) { + JNI_TRACE("get_X509_ALGOR_parameter(%p) => no parameters", algor); + return nullptr; + } + + // The OpenSSL 1.1.x API lacks a function to get the ASN1_TYPE out of X509_ALGOR directly, so + // recreate it from the returned components. + bssl::UniquePtr<ASN1_TYPE> param(ASN1_TYPE_new()); + if (!param || !ASN1_TYPE_set1(param.get(), param_type, param_value)) { + conscrypt::jniutil::throwOutOfMemory(env, "Unable to serialize parameter"); + return nullptr; + } + + return ASN1ToByteArray<ASN1_TYPE>(env, param.get(), i2d_ASN1_TYPE); } static jbyteArray NativeCrypto_get_X509_CRL_sig_alg_parameter(JNIEnv* env, jclass, jlong x509CrlRef, @@ -4440,12 +4818,9 @@ return nullptr; } - if (crl->sig_alg->parameter == nullptr) { - JNI_TRACE("get_X509_CRL_sig_alg_parameter(%p) => null", crl); - return nullptr; - } - - return ASN1ToByteArray<ASN1_TYPE>(env, crl->sig_alg->parameter, i2d_ASN1_TYPE); + const X509_ALGOR *sig_alg; + X509_CRL_get0_signature(crl, nullptr, &sig_alg); + return get_X509_ALGOR_parameter(env, sig_alg); } static jbyteArray NativeCrypto_X509_CRL_get_issuer_name(JNIEnv* env, jclass, jlong x509CrlRef, @@ -4469,9 +4844,9 @@ return version; } -template <typename T, int (*get_ext_by_OBJ_func)(T*, ASN1_OBJECT*, int), - X509_EXTENSION* (*get_ext_func)(T*, int)> -static X509_EXTENSION* X509Type_get_ext(JNIEnv* env, T* x509Type, jstring oidString) { +template <typename T, int (*get_ext_by_OBJ_func)(const T*, const ASN1_OBJECT*, int), + X509_EXTENSION* (*get_ext_func)(const T*, int)> +static X509_EXTENSION* X509Type_get_ext(JNIEnv* env, const T* x509Type, jstring oidString) { JNI_TRACE("X509Type_get_ext(%p)", x509Type); if (x509Type == nullptr) { @@ -4502,9 +4877,9 @@ return ext; } -template <typename T, int (*get_ext_by_OBJ_func)(T*, ASN1_OBJECT*, int), - X509_EXTENSION* (*get_ext_func)(T*, int)> -static jbyteArray X509Type_get_ext_oid(JNIEnv* env, T* x509Type, jstring oidString) { +template <typename T, int (*get_ext_by_OBJ_func)(const T*, const ASN1_OBJECT*, int), + X509_EXTENSION* (*get_ext_func)(const T*, int)> +static jbyteArray X509Type_get_ext_oid(JNIEnv* env, const T* x509Type, jstring oidString) { X509_EXTENSION* ext = X509Type_get_ext<T, get_ext_by_OBJ_func, get_ext_func>(env, x509Type, oidString); if (ext == nullptr) { @@ -4512,8 +4887,10 @@ return nullptr; } - JNI_TRACE("X509Type_get_ext_oid(%p, %p) => %p", x509Type, oidString, ext->value); - return ASN1ToByteArray<ASN1_OCTET_STRING>(env, ext->value, i2d_ASN1_OCTET_STRING); + JNI_TRACE("X509Type_get_ext_oid(%p, %p) => %p", x509Type, oidString, + X509_EXTENSION_get_data(ext)); + return ASN1ToByteArray<ASN1_OCTET_STRING>(env, X509_EXTENSION_get_data(ext), + i2d_ASN1_OCTET_STRING); } static jlong NativeCrypto_X509_CRL_get_ext(JNIEnv* env, jclass, jlong x509CrlRef, @@ -4567,8 +4944,9 @@ return 0; } - JNI_TRACE("get_X509_REVOKED_revocationDate(%p) => %p", revoked, revoked->revocationDate); - return reinterpret_cast<uintptr_t>(revoked->revocationDate); + JNI_TRACE("get_X509_REVOKED_revocationDate(%p) => %p", revoked, + X509_REVOKED_get0_revocationDate(revoked)); + return reinterpret_cast<uintptr_t>(X509_REVOKED_get0_revocationDate(revoked)); } #ifdef __GNUC__ @@ -4595,11 +4973,14 @@ } BIO_printf(bio, "Serial Number: "); - i2a_ASN1_INTEGER(bio, revoked->serialNumber); + i2a_ASN1_INTEGER(bio, X509_REVOKED_get0_serialNumber(revoked)); BIO_printf(bio, "\nRevocation Date: "); - ASN1_TIME_print(bio, revoked->revocationDate); + ASN1_TIME_print(bio, X509_REVOKED_get0_revocationDate(revoked)); BIO_printf(bio, "\n"); - X509V3_extensions_print(bio, "CRL entry extensions", revoked->extensions, 0, 0); + // TODO(davidben): Should the flags parameter be |X509V3_EXT_DUMP_UNKNOWN| so we don't error on + // unknown extensions. Alternatively, maybe we can use a simpler toString() implementation. + X509V3_extensions_print(bio, "CRL entry extensions", X509_REVOKED_get0_extensions(revoked), 0, + 0); } #ifndef _WIN32 #pragma GCC diagnostic pop @@ -4610,7 +4991,7 @@ 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); - return ASN1ToByteArray<X509_CRL_INFO>(env, crl->crl, i2d_X509_CRL_INFO); + return ASN1ToByteArray<X509_CRL>(env, crl, i2d_X509_CRL_tbs); } static void NativeCrypto_X509_CRL_verify(JNIEnv* env, jclass, jlong x509CrlRef, @@ -4694,12 +5075,17 @@ return X509_supported_extension(ext); } -static inline void get_ASN1_TIME_data(char** data, int* output, size_t len) { - char c = **data; - **data = '\0'; - *data -= len; - *output = atoi(*data); - *(*data + len) = c; +static inline bool decimal_to_integer(const char* data, size_t len, int* out) { + int ret = 0; + for (size_t i = 0; i < len; i++) { + ret *= 10; + if (data[i] < '0' || data[i] > '9') { + return false; + } + ret += data[i] - '0'; + } + *out = ret; + return true; } static void NativeCrypto_ASN1_TIME_to_Calendar(JNIEnv* env, jclass, jlong asn1TimeRef, @@ -4725,21 +5111,22 @@ return; } - if (gen->length < 14 || gen->data == nullptr) { + if (ASN1_STRING_length(gen.get()) < 14 || ASN1_STRING_get0_data(gen.get()) == nullptr) { conscrypt::jniutil::throwNullPointerException(env, "gen->length < 14 || gen->data == null"); return; } - int sec, min, hour, mday, mon, year; - - char* p = reinterpret_cast<char*>(&gen->data[14]); - - get_ASN1_TIME_data(&p, &sec, 2); - get_ASN1_TIME_data(&p, &min, 2); - get_ASN1_TIME_data(&p, &hour, 2); - get_ASN1_TIME_data(&p, &mday, 2); - get_ASN1_TIME_data(&p, &mon, 2); - get_ASN1_TIME_data(&p, &year, 4); + int year, mon, mday, hour, min, sec; + const char* data = reinterpret_cast<const char*>(ASN1_STRING_get0_data(gen.get())); + if (!decimal_to_integer(data, 4, &year) || + !decimal_to_integer(data + 4, 2, &mon) || + !decimal_to_integer(data + 6, 2, &mday) || + !decimal_to_integer(data + 8, 2, &hour) || + !decimal_to_integer(data + 10, 2, &min) || + !decimal_to_integer(data + 12, 2, &sec)) { + conscrypt::jniutil::throwParsingException(env, "Invalid date format"); + return; + } env->CallVoidMethod(calendar, conscrypt::jniutil::calendar_setMethod, year, mon - 1, mday, hour, min, sec); @@ -5091,11 +5478,10 @@ template <typename T, T* (*d2i_func)(BIO*, T**)> static jlong d2i_ASN1Object_to_jlong(JNIEnv* env, jlong bioRef) { - BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); + BIO* bio = to_BIO(env, bioRef); JNI_TRACE("d2i_ASN1Object_to_jlong(%p)", bio); if (bio == nullptr) { - conscrypt::jniutil::throwNullPointerException(env, "bio == null"); return 0; } @@ -5155,11 +5541,10 @@ template <typename T, T* (*PEM_read_func)(BIO*, T**, pem_password_cb*, void*)> static jlong PEM_to_jlong(JNIEnv* env, jlong bioRef) { - BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); + BIO* bio = to_BIO(env, bioRef); JNI_TRACE("PEM_to_jlong(%p)", bio); if (bio == nullptr) { - conscrypt::jniutil::throwNullPointerException(env, "bio == null"); JNI_TRACE("PEM_to_jlong(%p) => bio == null", bio); return 0; } @@ -5203,22 +5588,41 @@ return PEM_to_jlong<EVP_PKEY, PEM_read_bio_PrivateKey>(env, bioRef); } -template <typename T, typename T_stack> -static jlongArray PKCS7_to_ItemArray(JNIEnv* env, T_stack* stack, T* (*dup_func)(T*)) { - if (stack == nullptr) { +static jlongArray X509s_to_ItemArray(JNIEnv* env, STACK_OF(X509) *certs) { + if (certs == nullptr) { return nullptr; } ScopedLocalRef<jlongArray> ref_array(env, nullptr); - size_t size = sk_num(reinterpret_cast<_STACK*>(stack)); + size_t size = sk_X509_num(certs); ref_array.reset(env->NewLongArray(size)); ScopedLongArrayRW items(env, ref_array.get()); for (size_t i = 0; i < size; i++) { - T* item = reinterpret_cast<T*>(sk_value(reinterpret_cast<_STACK*>(stack), i)); - items[i] = reinterpret_cast<uintptr_t>(dup_func(item)); + X509* cert = sk_X509_value(certs, i); + X509_up_ref(cert); + items[i] = reinterpret_cast<uintptr_t>(cert); } - JNI_TRACE("PKCS7_to_ItemArray(%p) => %p [size=%zd]", stack, ref_array.get(), size); + JNI_TRACE("X509s_to_ItemArray(%p) => %p [size=%zd]", certs, ref_array.get(), size); + return ref_array.release(); +} + +static jlongArray X509_CRLs_to_ItemArray(JNIEnv* env, STACK_OF(X509_CRL) *crls) { + if (crls == nullptr) { + return nullptr; + } + + ScopedLocalRef<jlongArray> ref_array(env, nullptr); + size_t size = sk_X509_CRL_num(crls); + ref_array.reset(env->NewLongArray(size)); + ScopedLongArrayRW items(env, ref_array.get()); + for (size_t i = 0; i < size; i++) { + X509_CRL* crl = sk_X509_CRL_value(crls, i); + X509_CRL_up_ref(crl); + items[i] = reinterpret_cast<uintptr_t>(crl); + } + + JNI_TRACE("X509_CRLs_to_ItemArray(%p) => %p [size=%zd]", crls, ref_array.get(), size); return ref_array.release(); } @@ -5254,11 +5658,10 @@ static jlongArray NativeCrypto_PEM_read_bio_PKCS7(JNIEnv* env, jclass, jlong bioRef, jint which) { CHECK_ERROR_QUEUE_ON_RETURN; - BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); + BIO* bio = to_BIO(env, bioRef); JNI_TRACE("PEM_read_bio_PKCS7_CRLs(%p)", bio); if (bio == nullptr) { - conscrypt::jniutil::throwNullPointerException(env, "bio == null"); JNI_TRACE("PEM_read_bio_PKCS7_CRLs(%p) => bio == null", bio); return nullptr; } @@ -5269,14 +5672,14 @@ conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "PKCS7_get_PEM_certificates"); return nullptr; } - return PKCS7_to_ItemArray<X509, STACK_OF(X509)>(env, outCerts.get(), X509_dup); + return X509s_to_ItemArray(env, outCerts.get()); } else if (which == PKCS7_CRLS) { bssl::UniquePtr<STACK_OF(X509_CRL)> outCRLs(sk_X509_CRL_new_null()); if (!PKCS7_get_PEM_CRLs(outCRLs.get(), bio)) { conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "PKCS7_get_PEM_CRLs"); return nullptr; } - return PKCS7_to_ItemArray<X509_CRL, STACK_OF(X509_CRL)>(env, outCRLs.get(), X509_CRL_dup); + return X509_CRLs_to_ItemArray(env, outCRLs.get()); } else { conscrypt::jniutil::throwRuntimeException(env, "unknown PKCS7 field"); return nullptr; @@ -5285,11 +5688,10 @@ static jlongArray NativeCrypto_d2i_PKCS7_bio(JNIEnv* env, jclass, jlong bioRef, jint which) { CHECK_ERROR_QUEUE_ON_RETURN; - BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); + BIO* bio = to_BIO(env, bioRef); JNI_TRACE("d2i_PKCS7_bio(%p, %d)", bio, which); if (bio == nullptr) { - conscrypt::jniutil::throwNullPointerException(env, "bio == null"); JNI_TRACE("d2i_PKCS7_bio(%p, %d) => bio == null", bio, which); return nullptr; } @@ -5316,7 +5718,7 @@ return nullptr; } JNI_TRACE("d2i_PKCS7_bio(%p, %d) => success certs", bio, which); - return PKCS7_to_ItemArray<X509, STACK_OF(X509)>(env, outCerts.get(), X509_dup); + return X509s_to_ItemArray(env, outCerts.get()); } else if (which == PKCS7_CRLS) { bssl::UniquePtr<STACK_OF(X509_CRL)> outCRLs(sk_X509_CRL_new_null()); if (!PKCS7_get_CRLs(outCRLs.get(), &cbs)) { @@ -5326,7 +5728,7 @@ return nullptr; } JNI_TRACE("d2i_PKCS7_bio(%p, %d) => success CRLs", bio, which); - return PKCS7_to_ItemArray<X509_CRL, STACK_OF(X509_CRL)>(env, outCRLs.get(), X509_CRL_dup); + return X509_CRLs_to_ItemArray(env, outCRLs.get()); } else { conscrypt::jniutil::throwRuntimeException(env, "unknown PKCS7 field"); return nullptr; @@ -5335,9 +5737,14 @@ static jlongArray NativeCrypto_ASN1_seq_unpack_X509_bio(JNIEnv* env, jclass, jlong bioRef) { CHECK_ERROR_QUEUE_ON_RETURN; - BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); + BIO* bio = to_BIO(env, bioRef); JNI_TRACE("ASN1_seq_unpack_X509_bio(%p)", bio); + if (bio == nullptr) { + JNI_TRACE("ASN1_seq_unpack_X509_bio(%p) => bio == null", bio); + return nullptr; + } + uint8_t* data; size_t len; if (!BIO_read_asn1(bio, &data, &len, 256 * 1024 * 1024 /* max length, 256MB for sanity */)) { @@ -5447,21 +5854,6 @@ X509_free(x509); } -static jlong NativeCrypto_X509_dup(JNIEnv* env, jclass, jlong x509Ref, - CONSCRYPT_UNUSED jobject holder) { - CHECK_ERROR_QUEUE_ON_RETURN; - X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); - JNI_TRACE("X509_dup(%p)", x509); - - if (x509 == nullptr) { - conscrypt::jniutil::throwNullPointerException(env, "x509 == null"); - JNI_TRACE("X509_dup(%p) => x509 == null", x509); - return 0; - } - - return reinterpret_cast<uintptr_t>(X509_dup(x509)); -} - static jint NativeCrypto_X509_cmp(JNIEnv* env, jclass, jlong x509Ref1, CONSCRYPT_UNUSED jobject holder, jlong x509Ref2, CONSCRYPT_UNUSED jobject holder2) { @@ -5487,53 +5879,11 @@ return ret; } -static void NativeCrypto_X509_delete_ext(JNIEnv* env, jclass, jlong x509Ref, - CONSCRYPT_UNUSED jobject holder, jstring oidString) { - CHECK_ERROR_QUEUE_ON_RETURN; - X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); - JNI_TRACE("X509_delete_ext(%p, %p)", x509, oidString); - - if (x509 == nullptr) { - conscrypt::jniutil::throwNullPointerException(env, "x509 == null"); - JNI_TRACE("X509_delete_ext(%p, %p) => x509 == null", x509, oidString); - return; - } - - ScopedUtfChars oid(env, oidString); - if (oid.c_str() == nullptr) { - JNI_TRACE("X509_delete_ext(%p, %p) => oidString == null", x509, oidString); - return; - } - - bssl::UniquePtr<ASN1_OBJECT> obj(OBJ_txt2obj(oid.c_str(), 1 /* allow numerical form only */)); - if (obj.get() == nullptr) { - JNI_TRACE("X509_delete_ext(%p, %s) => oid conversion failed", x509, oid.c_str()); - conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException", - "Invalid OID."); - ERR_clear_error(); - return; - } - - int extIndex = X509_get_ext_by_OBJ(x509, obj.get(), -1); - if (extIndex == -1) { - JNI_TRACE("X509_delete_ext(%p, %s) => ext not found", x509, oid.c_str()); - return; - } - - X509_EXTENSION* ext = X509_delete_ext(x509, extIndex); - if (ext != nullptr) { - X509_EXTENSION_free(ext); - - // Invalidate the cached encoding - X509_CINF_set_modified(X509_get_cert_info(x509)); - } -} - static void NativeCrypto_X509_print_ex(JNIEnv* env, jclass, jlong bioRef, jlong x509Ref, CONSCRYPT_UNUSED jobject holder, jlong nmflagJava, jlong certflagJava) { CHECK_ERROR_QUEUE_ON_RETURN; - BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); + BIO* bio = to_BIO(env, bioRef); X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); // NOLINTNEXTLINE(runtime/int) unsigned long nmflag = static_cast<unsigned long>(nmflagJava); @@ -5542,7 +5892,6 @@ JNI_TRACE("X509_print_ex(%p, %p, %ld, %ld)", bio, x509, nmflag, certflag); if (bio == nullptr) { - conscrypt::jniutil::throwNullPointerException(env, "bio == null"); JNI_TRACE("X509_print_ex(%p, %p, %ld, %ld) => bio == null", bio, x509, nmflag, certflag); return; } @@ -5624,7 +5973,9 @@ } X509_PUBKEY* pubkey = X509_get_X509_PUBKEY(x509); - return ASN1_OBJECT_to_OID_string(env, pubkey->algor->algorithm); + ASN1_OBJECT* algorithm; + X509_PUBKEY_get0_param(&algorithm, nullptr, nullptr, nullptr, pubkey); + return ASN1_OBJECT_to_OID_string(env, algorithm); } static jstring NativeCrypto_get_X509_sig_alg_oid(JNIEnv* env, jclass, jlong x509Ref, @@ -5633,13 +5984,17 @@ X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509_sig_alg_oid(%p)", x509); - if (x509 == nullptr || x509->sig_alg == nullptr) { + if (x509 == nullptr) { conscrypt::jniutil::throwNullPointerException(env, "x509 == null || x509->sig_alg == null"); JNI_TRACE("get_X509_sig_alg_oid(%p) => x509 == null", x509); return nullptr; } - return ASN1_OBJECT_to_OID_string(env, x509->sig_alg->algorithm); + const X509_ALGOR *sig_alg; + X509_get0_signature(nullptr, &sig_alg, x509); + const ASN1_OBJECT *oid; + X509_ALGOR_get0(&oid, nullptr, nullptr, sig_alg); + return ASN1_OBJECT_to_OID_string(env, oid); } static jbyteArray NativeCrypto_get_X509_sig_alg_parameter(JNIEnv* env, jclass, jlong x509Ref, @@ -5654,12 +6009,9 @@ return nullptr; } - if (x509->sig_alg->parameter == nullptr) { - JNI_TRACE("get_X509_sig_alg_parameter(%p) => null", x509); - return nullptr; - } - - return ASN1ToByteArray<ASN1_TYPE>(env, x509->sig_alg->parameter, i2d_ASN1_TYPE); + const X509_ALGOR *sig_alg; + X509_get0_signature(nullptr, &sig_alg, x509); + return get_X509_ALGOR_parameter(env, sig_alg); } static jbooleanArray NativeCrypto_get_X509_issuerUID(JNIEnv* env, jclass, jlong x509Ref, @@ -5674,12 +6026,14 @@ return nullptr; } - if (x509->cert_info->issuerUID == nullptr) { + const ASN1_BIT_STRING *issuer_uid; + X509_get0_uids(x509, &issuer_uid, /*out_subject_uid=*/nullptr); + if (issuer_uid == nullptr) { JNI_TRACE("get_X509_issuerUID(%p) => null", x509); return nullptr; } - return ASN1BitStringToBooleanArray(env, x509->cert_info->issuerUID); + return ASN1BitStringToBooleanArray(env, issuer_uid); } static jbooleanArray NativeCrypto_get_X509_subjectUID(JNIEnv* env, jclass, jlong x509Ref, @@ -5694,12 +6048,14 @@ return nullptr; } - if (x509->cert_info->subjectUID == nullptr) { + const ASN1_BIT_STRING *subject_uid; + X509_get0_uids(x509, /*out_issuer_uid=*/nullptr, &subject_uid); + if (subject_uid == nullptr) { JNI_TRACE("get_X509_subjectUID(%p) => null", x509); return nullptr; } - return ASN1BitStringToBooleanArray(env, x509->cert_info->subjectUID); + return ASN1BitStringToBooleanArray(env, subject_uid); } static jbooleanArray NativeCrypto_get_X509_ex_kusage(JNIEnv* env, jclass, jlong x509Ref, @@ -5714,10 +6070,14 @@ return nullptr; } + // TODO(https://github.com/google/conscrypt/issues/916): Handle errors and remove + // |ERR_clear_error|. Note X509Certificate.getKeyUsage() cannot throw + // CertificateParsingException, so this needs to be checked earlier, e.g. in the constructor. bssl::UniquePtr<ASN1_BIT_STRING> bitStr( static_cast<ASN1_BIT_STRING*>(X509_get_ext_d2i(x509, NID_key_usage, nullptr, nullptr))); if (bitStr.get() == nullptr) { JNI_TRACE("get_X509_ex_kusage(%p) => null", x509); + ERR_clear_error(); return nullptr; } @@ -5736,10 +6096,13 @@ return nullptr; } + // TODO(https://github.com/google/conscrypt/issues/916): Handle errors, remove + // |ERR_clear_error|, and throw CertificateParsingException. bssl::UniquePtr<STACK_OF(ASN1_OBJECT)> objArray(static_cast<STACK_OF(ASN1_OBJECT)*>( X509_get_ext_d2i(x509, NID_ext_key_usage, nullptr, nullptr))); if (objArray.get() == nullptr) { JNI_TRACE("get_X509_ex_xkusage(%p) => null", x509); + ERR_clear_error(); return nullptr; } @@ -5773,11 +6136,52 @@ return 0; } - /* Just need to do this to cache the ex_* values. */ - X509_check_ca(x509); + // Use |X509_get_ext_d2i| instead of |X509_get_pathlen| because the latter treats + // |EXFLAG_INVALID| as the error case. |EXFLAG_INVALID| is set if any built-in extension is + // invalid. For now, we preserve Conscrypt's historical behavior in accepting certificates in + // the constructor even if |EXFLAG_INVALID| is set. + // + // TODO(https://github.com/google/conscrypt/issues/916): Handle errors and remove + // |ERR_clear_error|. Note X509Certificate.getBasicConstraints() cannot throw + // CertificateParsingException, so this needs to be checked earlier, e.g. in the constructor. + bssl::UniquePtr<BASIC_CONSTRAINTS> basic_constraints(static_cast<BASIC_CONSTRAINTS*>( + X509_get_ext_d2i(x509, NID_basic_constraints, nullptr, nullptr))); + if (basic_constraints == nullptr) { + JNI_TRACE("get_X509_ex_path(%p) => -1 (no extension or error)", x509); + ERR_clear_error(); + return -1; + } - JNI_TRACE("get_X509_ex_pathlen(%p) => %ld", x509, x509->ex_pathlen); - return x509->ex_pathlen; + if (basic_constraints->pathlen == nullptr) { + JNI_TRACE("get_X509_ex_path(%p) => -1 (no pathLenConstraint)", x509); + return -1; + } + + if (!basic_constraints->ca) { + // Path length constraints are only valid for CA certificates. + // TODO(https://github.com/google/conscrypt/issues/916): Treat this as an error condition. + JNI_TRACE("get_X509_ex_path(%p) => -1 (not a CA)", x509); + return -1; + } + + if (basic_constraints->pathlen->type == V_ASN1_NEG_INTEGER) { + // Path length constraints may not be negative. + // TODO(https://github.com/google/conscrypt/issues/916): Treat this as an error condition. + JNI_TRACE("get_X509_ex_path(%p) => -1 (negative)", x509); + return -1; + } + + long pathlen = ASN1_INTEGER_get(basic_constraints->pathlen); + if (pathlen == -1 || pathlen > INT_MAX) { + // TODO(https://github.com/google/conscrypt/issues/916): Treat this as an error condition. + // If the integer overflows, the certificate is effectively unconstrained. Reporting no + // constraint is plausible, but Chromium rejects all values above 255. + JNI_TRACE("get_X509_ex_path(%p) => -1 (overflow)", x509); + return -1; + } + + JNI_TRACE("get_X509_ex_path(%p) => %ld", x509, pathlen); + return pathlen; } static jbyteArray NativeCrypto_X509_get_ext_oid(JNIEnv* env, jclass, jlong x509Ref, @@ -5808,8 +6212,8 @@ env, revoked, oidString); } -template <typename T, typename C, C T::*member, int (*get_ext_by_critical_func)(T*, int, int), - X509_EXTENSION* (*get_ext_func)(T*, int)> +template <typename T, int (*get_ext_by_critical_func)(const T*, int, int), + X509_EXTENSION* (*get_ext_func)(const T*, int)> static jobjectArray get_X509Type_ext_oids(JNIEnv* env, jlong x509Ref, jint critical) { T* x509 = reinterpret_cast<T*>(static_cast<uintptr_t>(x509Ref)); JNI_TRACE("get_X509Type_ext_oids(%p, %d)", x509, critical); @@ -5819,11 +6223,6 @@ JNI_TRACE("get_X509Type_ext_oids(%p, %d) => x509 == null", x509, critical); return nullptr; } - if (member != nullptr && x509->*member == nullptr) { - conscrypt::jniutil::throwNullPointerException(env, "x509->*member == null"); - JNI_TRACE("get_X509Type_ext_oids(%p, %d) => x509->*member == null", x509, critical); - return nullptr; - } int lastPos = -1; int count = 0; @@ -5845,7 +6244,8 @@ while ((lastPos = get_ext_by_critical_func(x509, critical, lastPos)) != -1) { X509_EXTENSION* ext = get_ext_func(x509, lastPos); - ScopedLocalRef<jstring> extOid(env, ASN1_OBJECT_to_OID_string(env, ext->object)); + ScopedLocalRef<jstring> extOid( + env, ASN1_OBJECT_to_OID_string(env, X509_EXTENSION_get_object(ext))); if (extOid.get() == nullptr) { JNI_TRACE("get_X509Type_ext_oids(%p) => couldn't get OID", x509); return nullptr; @@ -5863,8 +6263,8 @@ CHECK_ERROR_QUEUE_ON_RETURN; // NOLINTNEXTLINE(runtime/int) JNI_TRACE("get_X509_ext_oids(0x%llx, %d)", (long long)x509Ref, critical); - return get_X509Type_ext_oids<X509, decltype(X509::cert_info), &X509::cert_info, - X509_get_ext_by_critical, X509_get_ext>(env, x509Ref, critical); + return get_X509Type_ext_oids<X509, X509_get_ext_by_critical, X509_get_ext>(env, x509Ref, + critical); } static jobjectArray NativeCrypto_get_X509_CRL_ext_oids(JNIEnv* env, jclass, jlong x509CrlRef, @@ -5873,8 +6273,8 @@ CHECK_ERROR_QUEUE_ON_RETURN; // NOLINTNEXTLINE(runtime/int) JNI_TRACE("get_X509_CRL_ext_oids(0x%llx, %d)", (long long)x509CrlRef, critical); - return get_X509Type_ext_oids<X509_CRL, decltype(X509_CRL::crl), &X509_CRL::crl, - X509_CRL_get_ext_by_critical, X509_CRL_get_ext>(env, x509CrlRef, critical); + return get_X509Type_ext_oids<X509_CRL, X509_CRL_get_ext_by_critical, X509_CRL_get_ext>( + env, x509CrlRef, critical); } static jobjectArray NativeCrypto_get_X509_REVOKED_ext_oids(JNIEnv* env, jclass, @@ -5882,8 +6282,8 @@ CHECK_ERROR_QUEUE_ON_RETURN; // NOLINTNEXTLINE(runtime/int) JNI_TRACE("get_X509_CRL_ext_oids(0x%llx, %d)", (long long)x509RevokedRef, critical); - return get_X509Type_ext_oids<X509_REVOKED, decltype(X509_REVOKED::extensions), nullptr, - X509_REVOKED_get_ext_by_critical, X509_REVOKED_get_ext>(env, x509RevokedRef, critical); + return get_X509Type_ext_oids<X509_REVOKED, X509_REVOKED_get_ext_by_critical, + X509_REVOKED_get_ext>(env, x509RevokedRef, critical); } /** @@ -9029,7 +9429,7 @@ bssl::UniquePtr<ASN1_INTEGER> serial_number( c2i_ASN1_INTEGER(nullptr, &p, static_cast<long>(CBS_len(&serial)))); // NOLINT(runtime/int) - ASN1_INTEGER* expected_serial_number = X509_get_serialNumber(x509); + const ASN1_INTEGER* expected_serial_number = X509_get_serialNumber(x509); if (serial_number.get() == nullptr || ASN1_INTEGER_cmp(expected_serial_number, serial_number.get()) != 0) { return false; @@ -9043,15 +9443,16 @@ // Hash the issuer's name and compare the hash with the one from the Cert ID uint8_t md[EVP_MAX_MD_SIZE]; - X509_NAME* issuer_name = X509_get_subject_name(issuerX509); + const X509_NAME* issuer_name = X509_get_subject_name(issuerX509); if (!X509_NAME_digest(issuer_name, digest, md, nullptr) || !CBS_mem_equal(&issuer_name_hash, md, EVP_MD_size(digest))) { return false; } // Same thing with the issuer's key - ASN1_BIT_STRING* issuer_key = X509_get0_pubkey_bitstr(issuerX509); - if (!EVP_Digest(issuer_key->data, static_cast<size_t>(issuer_key->length), md, nullptr, digest, + const ASN1_BIT_STRING* issuer_key = X509_get0_pubkey_bitstr(issuerX509); + if (!EVP_Digest(ASN1_STRING_get0_data(issuer_key), + static_cast<size_t>(ASN1_STRING_length(issuer_key)), md, nullptr, digest, nullptr) || !CBS_mem_equal(&issuer_key_hash, md, EVP_MD_size(digest))) { return false; @@ -9174,20 +9575,6 @@ } /* - * X509v3_get_ext_by_OBJ and X509v3_get_ext take const arguments, unlike the other *_get_ext - * functions. - * This means they cannot be used with X509Type_get_ext_oid, so these wrapper functions are used - * instead. - */ -static int _X509v3_get_ext_by_OBJ(X509_EXTENSIONS* exts, ASN1_OBJECT* obj, int lastpos) { - return X509v3_get_ext_by_OBJ(exts, obj, lastpos); -} - -static X509_EXTENSION* _X509v3_get_ext(X509_EXTENSIONS* exts, int loc) { - return X509v3_get_ext(exts, loc); -} - -/* public static native byte[] get_ocsp_single_extension(byte[] ocspData, String oid, long x509Ref, long issuerX509Ref); */ @@ -9243,7 +9630,7 @@ return nullptr; } - return X509Type_get_ext_oid<X509_EXTENSIONS, _X509v3_get_ext_by_OBJ, _X509v3_get_ext>( + return X509Type_get_ext_oid<X509_EXTENSIONS, X509v3_get_ext_by_OBJ, X509v3_get_ext>( env, x509_exts.get(), oid); } @@ -9277,7 +9664,7 @@ static jint NativeCrypto_SSL_pending_written_bytes_in_BIO(JNIEnv* env, jclass, jlong bio_address) { CHECK_ERROR_QUEUE_ON_RETURN; - BIO* bio = to_SSL_BIO(env, bio_address, true); + BIO* bio = to_BIO(env, bio_address); if (bio == nullptr) { return 0; } @@ -9581,7 +9968,7 @@ ssl); return -1; } - BIO* bio = to_SSL_BIO(env, bioRef, true); + BIO* bio = to_BIO(env, bioRef); if (bio == nullptr) { return -1; } @@ -9619,73 +10006,6 @@ return result; } -static int NativeCrypto_ENGINE_SSL_write_BIO_heap(JNIEnv* env, jclass, jlong ssl_address, - CONSCRYPT_UNUSED jobject ssl_holder, jlong bioRef, - jbyteArray sourceJava, jint sourceOffset, - jint sourceLength, jobject shc) { - CHECK_ERROR_QUEUE_ON_RETURN; - SSL* ssl = to_SSL(env, ssl_address, true); - if (ssl == nullptr) { - return -1; - } - if (shc == nullptr) { - conscrypt::jniutil::throwNullPointerException(env, "sslHandshakeCallbacks == null"); - JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_write_BIO_heap => sslHandshakeCallbacks == null", - ssl); - return -1; - } - BIO* bio = to_SSL_BIO(env, bioRef, true); - if (bio == nullptr) { - return -1; - } - if (sourceLength < 0 || BIO_ctrl_get_write_guarantee(bio) < static_cast<size_t>(sourceLength)) { - // The network BIO couldn't handle the entire write. Don't write anything, so that we - // only process one packet at a time. - return 0; - } - ScopedByteArrayRO source(env, sourceJava); - if (source.get() == nullptr) { - JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_write_BIO_heap => threw exception", ssl); - return -1; - } - if (ARRAY_OFFSET_LENGTH_INVALID(source, sourceOffset, sourceLength)) { - JNI_TRACE( - "ssl=%p NativeCrypto_ENGINE_SSL_write_BIO_heap => sourceOffset=%d, " - "sourceLength=%d, size=%zd", - ssl, sourceOffset, sourceLength, source.size()); - conscrypt::jniutil::throwException(env, "java/lang/ArrayIndexOutOfBoundsException", - nullptr); - return -1; - } - - AppData* appData = toAppData(ssl); - if (appData == nullptr) { - conscrypt::jniutil::throwSSLExceptionStr(env, "Unable to retrieve application data"); - ERR_clear_error(); - JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_write_BIO_heap appData => null", ssl); - return -1; - } - if (!appData->setCallbackState(env, shc, nullptr)) { - conscrypt::jniutil::throwSSLExceptionStr(env, "Unable to set appdata callback"); - ERR_clear_error(); - JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_write_BIO_heap => exception", ssl); - return -1; - } - - errno = 0; - - int result = BIO_write(bio, reinterpret_cast<const char*>(source.get()) + sourceOffset, - sourceLength); - appData->clearCallbackState(); - JNI_TRACE( - "ssl=%p NativeCrypto_ENGINE_SSL_write_BIO_heap bio=%p source=%p sourceOffset=%d " - "sourceLength=%d shc=%p => ret=%d", - ssl, bio, source.get(), sourceOffset, sourceLength, shc, result); - JNI_TRACE_PACKET_DATA(ssl, 'O', reinterpret_cast<const char*>(source.get()) + sourceOffset, - static_cast<size_t>(result)); - return result; -} - static int NativeCrypto_ENGINE_SSL_read_BIO_direct(JNIEnv* env, jclass, jlong ssl_address, CONSCRYPT_UNUSED jobject ssl_holder, jlong bioRef, jlong address, jint outputSize, @@ -9701,7 +10021,7 @@ ssl); return -1; } - BIO* bio = to_SSL_BIO(env, bioRef, true); + BIO* bio = to_BIO(env, bioRef); if (bio == nullptr) { return -1; } @@ -9737,67 +10057,6 @@ return result; } -static int NativeCrypto_ENGINE_SSL_read_BIO_heap(JNIEnv* env, jclass, jlong ssl_address, - CONSCRYPT_UNUSED jobject ssl_holder, jlong bioRef, - jbyteArray destJava, jint destOffset, - jint destLength, jobject shc) { - CHECK_ERROR_QUEUE_ON_RETURN; - SSL* ssl = to_SSL(env, ssl_address, true); - if (ssl == nullptr) { - return -1; - } - if (shc == nullptr) { - conscrypt::jniutil::throwNullPointerException(env, "sslHandshakeCallbacks == null"); - JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_read_BIO_heap => sslHandshakeCallbacks == null", - ssl); - return -1; - } - BIO* bio = to_SSL_BIO(env, bioRef, true); - if (bio == nullptr) { - return -1; - } - ScopedByteArrayRW dest(env, destJava); - if (dest.get() == nullptr) { - JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_read_BIO_heap => threw exception", ssl); - return -1; - } - if (ARRAY_OFFSET_LENGTH_INVALID(dest, destOffset, destLength)) { - JNI_TRACE( - "ssl=%p NativeCrypto_ENGINE_SSL_read_BIO_heap => destOffset=%d, destLength=%d, " - "size=%zd", - ssl, destOffset, destLength, dest.size()); - conscrypt::jniutil::throwException(env, "java/lang/ArrayIndexOutOfBoundsException", - nullptr); - return -1; - } - - AppData* appData = toAppData(ssl); - if (appData == nullptr) { - conscrypt::jniutil::throwSSLExceptionStr(env, "Unable to retrieve application data"); - ERR_clear_error(); - JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_read_BIO_heap appData => null", ssl); - return -1; - } - if (!appData->setCallbackState(env, shc, nullptr)) { - conscrypt::jniutil::throwSSLExceptionStr(env, "Unable to set appdata callback"); - ERR_clear_error(); - JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_read_BIO_heap => exception", ssl); - return -1; - } - - errno = 0; - - int result = BIO_read(bio, reinterpret_cast<char*>(dest.get()) + destOffset, destLength); - appData->clearCallbackState(); - JNI_TRACE( - "ssl=%p NativeCrypto_ENGINE_SSL_read_BIO_heap bio=%p dest=%p destOffset=%d " - "destLength=%d shc=%p => ret=%d", - ssl, bio, dest.get(), destOffset, destLength, shc, result); - JNI_TRACE_PACKET_DATA(ssl, 'I', reinterpret_cast<char*>(dest.get()) + destOffset, - static_cast<size_t>(result)); - return result; -} - static void NativeCrypto_ENGINE_SSL_force_read(JNIEnv* env, jclass, jlong ssl_address, CONSCRYPT_UNUSED jobject ssl_holder, jobject shc) { CHECK_ERROR_QUEUE_ON_RETURN; @@ -9921,13 +10180,25 @@ return result; } +/** + * public static native bool BoringSSL_FIPS_mode(); + */ +static jboolean NativeCrypto_usesBoringSSL_FIPS_mode() { + return FIPS_mode(); +} + // TESTING METHODS BEGIN static int NativeCrypto_BIO_read(JNIEnv* env, jclass, jlong bioRef, jbyteArray outputJavaBytes) { CHECK_ERROR_QUEUE_ON_RETURN; - BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); + BIO* bio = to_BIO(env, bioRef); JNI_TRACE("BIO_read(%p, %p)", bio, outputJavaBytes); + if (bio == nullptr) { + JNI_TRACE("BIO_read(%p, %p) => bio == null", bio, outputJavaBytes); + return 0; + } + if (outputJavaBytes == nullptr) { conscrypt::jniutil::throwNullPointerException(env, "output == null"); JNI_TRACE("BIO_read(%p, %p) => output == null", bio, outputJavaBytes); @@ -9958,9 +10229,13 @@ static void NativeCrypto_BIO_write(JNIEnv* env, jclass, jlong bioRef, jbyteArray inputJavaBytes, jint offset, jint length) { CHECK_ERROR_QUEUE_ON_RETURN; - BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef)); + BIO* bio = to_BIO(env, bioRef); JNI_TRACE("BIO_write(%p, %p, %d, %d)", bio, inputJavaBytes, offset, length); + if (bio == nullptr) { + return; + } + if (inputJavaBytes == nullptr) { conscrypt::jniutil::throwNullPointerException(env, "input == null"); return; @@ -10073,6 +10348,7 @@ #define REF_EVP_PKEY "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_PKEY;" #define REF_EVP_PKEY_CTX "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$EVP_PKEY_CTX;" #define REF_HMAC_CTX "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$HMAC_CTX;" +#define REF_CMAC_CTX "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef$CMAC_CTX;" #define REF_BIO_IN_STREAM "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLBIOInputStream;" #define REF_X509 "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLX509Certificate;" #define REF_X509_CRL "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLX509CRL;" @@ -10080,6 +10356,12 @@ #define REF_SSL_CTX "L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/AbstractSessionContext;" static JNINativeMethod sNativeCryptoMethods[] = { CONSCRYPT_NATIVE_METHOD(clinit, "()V"), + CONSCRYPT_NATIVE_METHOD(CMAC_CTX_new, "()J"), + CONSCRYPT_NATIVE_METHOD(CMAC_CTX_free, "(J)V"), + CONSCRYPT_NATIVE_METHOD(CMAC_Init, "(" REF_CMAC_CTX "[B)V"), + CONSCRYPT_NATIVE_METHOD(CMAC_Update, "(" REF_CMAC_CTX "[BII)V"), + CONSCRYPT_NATIVE_METHOD(CMAC_UpdateDirect, "(" REF_CMAC_CTX "JI)V"), + CONSCRYPT_NATIVE_METHOD(CMAC_Final, "(" REF_CMAC_CTX ")[B"), CONSCRYPT_NATIVE_METHOD(EVP_PKEY_new_RSA, "([B[B[B[B[B[B[B[B)J"), CONSCRYPT_NATIVE_METHOD(EVP_PKEY_new_EC_KEY, "(" REF_EC_GROUP REF_EC_POINT "[B)J"), CONSCRYPT_NATIVE_METHOD(EVP_PKEY_type, "(" REF_EVP_PKEY ")I"), @@ -10130,6 +10412,8 @@ CONSCRYPT_NATIVE_METHOD(ECDSA_size, "(" REF_EVP_PKEY ")I"), CONSCRYPT_NATIVE_METHOD(ECDSA_sign, "([B[B" REF_EVP_PKEY ")I"), CONSCRYPT_NATIVE_METHOD(ECDSA_verify, "([B[B" REF_EVP_PKEY ")I"), + CONSCRYPT_NATIVE_METHOD(X25519, "([B[B[B)Z"), + CONSCRYPT_NATIVE_METHOD(X25519_keypair, "([B[B)V"), CONSCRYPT_NATIVE_METHOD(EVP_MD_CTX_create, "()J"), CONSCRYPT_NATIVE_METHOD(EVP_MD_CTX_cleanup, "(" REF_EVP_MD_CTX ")V"), CONSCRYPT_NATIVE_METHOD(EVP_MD_CTX_destroy, "(J)V"), @@ -10179,6 +10463,8 @@ CONSCRYPT_NATIVE_METHOD(EVP_AEAD_nonce_length, "(J)I"), CONSCRYPT_NATIVE_METHOD(EVP_AEAD_CTX_seal, "(J[BI[BI[B[BII[B)I"), CONSCRYPT_NATIVE_METHOD(EVP_AEAD_CTX_open, "(J[BI[BI[B[BII[B)I"), + CONSCRYPT_NATIVE_METHOD(EVP_AEAD_CTX_seal_buf, "(J[BILjava/nio/ByteBuffer;[BLjava/nio/ByteBuffer;[B)I"), + CONSCRYPT_NATIVE_METHOD(EVP_AEAD_CTX_open_buf, "(J[BILjava/nio/ByteBuffer;[BLjava/nio/ByteBuffer;[B)I"), CONSCRYPT_NATIVE_METHOD(HMAC_CTX_new, "()J"), CONSCRYPT_NATIVE_METHOD(HMAC_CTX_free, "(J)V"), CONSCRYPT_NATIVE_METHOD(HMAC_Init_ex, "(" REF_HMAC_CTX "[BJ)V"), @@ -10200,7 +10486,6 @@ CONSCRYPT_NATIVE_METHOD(ASN1_seq_unpack_X509_bio, "(J)[J"), CONSCRYPT_NATIVE_METHOD(ASN1_seq_pack_X509, "([J)[B"), CONSCRYPT_NATIVE_METHOD(X509_free, "(J" REF_X509 ")V"), - CONSCRYPT_NATIVE_METHOD(X509_dup, "(J" REF_X509 ")J"), CONSCRYPT_NATIVE_METHOD(X509_cmp, "(J" REF_X509 "J" REF_X509 ")I"), CONSCRYPT_NATIVE_METHOD(X509_print_ex, "(JJ" REF_X509 "JJ)V"), CONSCRYPT_NATIVE_METHOD(X509_get_pubkey, "(J" REF_X509 ")J"), @@ -10216,7 +10501,6 @@ CONSCRYPT_NATIVE_METHOD(get_X509_ex_pathlen, "(J" REF_X509 ")I"), CONSCRYPT_NATIVE_METHOD(X509_get_ext_oid, "(J" REF_X509 "Ljava/lang/String;)[B"), CONSCRYPT_NATIVE_METHOD(X509_CRL_get_ext_oid, "(J" REF_X509_CRL "Ljava/lang/String;)[B"), - CONSCRYPT_NATIVE_METHOD(X509_delete_ext, "(J" REF_X509 "Ljava/lang/String;)V"), CONSCRYPT_NATIVE_METHOD(get_X509_CRL_crl_enc, "(J" REF_X509_CRL ")[B"), CONSCRYPT_NATIVE_METHOD(X509_CRL_verify, "(J" REF_X509_CRL REF_EVP_PKEY ")V"), CONSCRYPT_NATIVE_METHOD(X509_CRL_get_lastUpdate, "(J" REF_X509_CRL ")J"), @@ -10235,7 +10519,8 @@ CONSCRYPT_NATIVE_METHOD(X509_get_version, "(J" REF_X509 ")J"), CONSCRYPT_NATIVE_METHOD(X509_get_serialNumber, "(J" REF_X509 ")[B"), CONSCRYPT_NATIVE_METHOD(X509_verify, "(J" REF_X509 REF_EVP_PKEY ")V"), - CONSCRYPT_NATIVE_METHOD(get_X509_cert_info_enc, "(J" REF_X509 ")[B"), + CONSCRYPT_NATIVE_METHOD(get_X509_tbs_cert, "(J" REF_X509 ")[B"), + CONSCRYPT_NATIVE_METHOD(get_X509_tbs_cert_without_ext, "(J" REF_X509 "Ljava/lang/String;)[B"), CONSCRYPT_NATIVE_METHOD(get_X509_signature, "(J" REF_X509 ")[B"), CONSCRYPT_NATIVE_METHOD(get_X509_CRL_signature, "(J" REF_X509_CRL ")[B"), CONSCRYPT_NATIVE_METHOD(get_X509_ex_flags, "(J" REF_X509 ")I"), @@ -10360,10 +10645,9 @@ CONSCRYPT_NATIVE_METHOD(ENGINE_SSL_write_direct, "(J" REF_SSL "JI" SSL_CALLBACKS ")I"), CONSCRYPT_NATIVE_METHOD(ENGINE_SSL_write_BIO_direct, "(J" REF_SSL "JJI" SSL_CALLBACKS ")I"), CONSCRYPT_NATIVE_METHOD(ENGINE_SSL_read_BIO_direct, "(J" REF_SSL "JJI" SSL_CALLBACKS ")I"), - CONSCRYPT_NATIVE_METHOD(ENGINE_SSL_write_BIO_heap, "(J" REF_SSL "J[BII" SSL_CALLBACKS ")I"), - CONSCRYPT_NATIVE_METHOD(ENGINE_SSL_read_BIO_heap, "(J" REF_SSL "J[BII" SSL_CALLBACKS ")I"), CONSCRYPT_NATIVE_METHOD(ENGINE_SSL_force_read, "(J" REF_SSL SSL_CALLBACKS ")V"), CONSCRYPT_NATIVE_METHOD(ENGINE_SSL_shutdown, "(J" REF_SSL SSL_CALLBACKS ")V"), + CONSCRYPT_NATIVE_METHOD(usesBoringSSL_FIPS_mode, "()Z"), // Used for testing only. CONSCRYPT_NATIVE_METHOD(BIO_read, "(J[B)I"),
diff --git a/common/src/jni/main/include/conscrypt/jniutil.h b/common/src/jni/main/include/conscrypt/jniutil.h index 8465c05..6f55608 100644 --- a/common/src/jni/main/include/conscrypt/jniutil.h +++ b/common/src/jni/main/include/conscrypt/jniutil.h
@@ -40,6 +40,8 @@ extern jclass inputStreamClass; extern jclass outputStreamClass; extern jclass stringClass; +extern jclass byteBufferClass; +extern jclass bufferClass; extern jfieldID nativeRef_address; @@ -49,6 +51,8 @@ extern jmethodID openSslInputStream_readLineMethod; extern jmethodID outputStream_writeMethod; extern jmethodID outputStream_flushMethod; +extern jmethodID buffer_positionMethod; +extern jmethodID buffer_limitMethod; /** * Initializes the JNI constants from the environment.
diff --git a/common/src/main/java/org/conscrypt/AbstractSessionContext.java b/common/src/main/java/org/conscrypt/AbstractSessionContext.java index 70df88f..c527b58 100644 --- a/common/src/main/java/org/conscrypt/AbstractSessionContext.java +++ b/common/src/main/java/org/conscrypt/AbstractSessionContext.java
@@ -41,7 +41,6 @@ final long sslCtxNativePointer = NativeCrypto.SSL_CTX_new(); - @SuppressWarnings("serial") private final Map<ByteArray, NativeSslSession> sessions = new LinkedHashMap<ByteArray, NativeSslSession>() { @Override @@ -76,7 +75,7 @@ // Make a copy of the IDs. final Iterator<NativeSslSession> iter; synchronized (sessions) { - iter = Arrays.asList(sessions.values().toArray(new NativeSslSession[sessions.size()])) + iter = Arrays.asList(sessions.values().toArray(new NativeSslSession[0])) .iterator(); } return new Enumeration<byte[]>() { @@ -188,6 +187,7 @@ } @Override + @SuppressWarnings("deprecation") protected void finalize() throws Throwable { try { NativeCrypto.SSL_CTX_free(sslCtxNativePointer, this);
diff --git a/common/src/main/java/org/conscrypt/ActiveSession.java b/common/src/main/java/org/conscrypt/ActiveSession.java index b00a3fa..4e3a4e8 100644 --- a/common/src/main/java/org/conscrypt/ActiveSession.java +++ b/common/src/main/java/org/conscrypt/ActiveSession.java
@@ -33,7 +33,7 @@ */ final class ActiveSession implements ConscryptSession { private final NativeSsl ssl; - private AbstractSessionContext sessionContext; + private final AbstractSessionContext sessionContext; private byte[] id; private long creationTime; private String protocol; @@ -41,6 +41,7 @@ private String peerHost; private int peerPort = -1; private long lastAccessedTime = 0; + @SuppressWarnings("deprecation") private volatile javax.security.cert.X509Certificate[] peerCertificateChain; private X509Certificate[] localCertificates; private X509Certificate[] peerCertificates; @@ -108,7 +109,7 @@ @Override public List<byte[]> getStatusResponses() { if (peerCertificateOcspData == null) { - return Collections.<byte[]>emptyList(); + return Collections.emptyList(); } return Collections.singletonList(peerCertificateOcspData.clone()); @@ -205,8 +206,13 @@ * be verified. */ @Override + @SuppressWarnings("deprecation") // Public API public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { + if (!Platform.isJavaxCertificateSupported()) { + throw new UnsupportedOperationException("Use getPeerCertificates() instead"); + } + checkPeerCertificatesPresent(); // TODO(nathanmittler): Should we clone? javax.security.cert.X509Certificate[] result = peerCertificateChain;
diff --git a/common/src/main/java/org/conscrypt/BufferUtils.java b/common/src/main/java/org/conscrypt/BufferUtils.java new file mode 100644 index 0000000..e42506d --- /dev/null +++ b/common/src/main/java/org/conscrypt/BufferUtils.java
@@ -0,0 +1,137 @@ +/* + * Copyright 2021 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 java.lang.Math.min; +import static org.conscrypt.Preconditions.checkArgument; + +import java.nio.ByteBuffer; + +/** + * Utility methods for dealing with arrays of ByteBuffers. + * + * @hide This class is not part of the Android public SDK API + */ +public final class BufferUtils { + private BufferUtils() {} + + /** + * Throws {@link IllegalArgumentException} if any of the buffers in the array are null. + */ + public static void checkNotNull(ByteBuffer[] buffers) { + for (ByteBuffer buffer : buffers) { + if (buffer == null) { + throw new IllegalArgumentException("Null buffer in array"); + } + } + } + + /** + * Returns the total number of bytes remaining in the buffer array. + */ + public static long remaining(ByteBuffer[] buffers) { + long size = 0; + for (ByteBuffer buffer : buffers) { + size += buffer.remaining(); + } + return size; + } + + /** + * Marks {@code toConsume} bytes of data as consumed from the buffer array. + * + * @throws IllegalArgumentException if there are fewer than {@code toConsume} bytes remaining + */ + public static void consume(ByteBuffer[] sourceBuffers, int toConsume) { + for (ByteBuffer sourceBuffer : sourceBuffers) { + int amount = min(sourceBuffer.remaining(), toConsume); + if (amount > 0) { + sourceBuffer.position(sourceBuffer.position() + amount); + toConsume -= amount; + if (toConsume == 0) { + break; + } + } + } + if (toConsume > 0) { + throw new IllegalArgumentException("toConsume > data size"); + } + } + + /** + * Looks for a buffer in the buffer array which EITHER is larger than {@code minSize} AND + * has no preceding non-empty buffers OR is the only non-empty buffer in the array. + */ + public static ByteBuffer getBufferLargerThan(ByteBuffer[] buffers, int minSize) { + int length = buffers.length; + for (int i = 0; i < length; i++) { + ByteBuffer buffer = buffers[i]; + int remaining = buffer.remaining(); + if (remaining > 0) { + if (remaining >= minSize) { + return buffer; + } + for (int j = i + 1; j < length; j++) { + if (buffers[j].remaining() > 0) { + return null; + } + } + return buffer; + } + } + return null; + } + + /** + * Copies up to {@code maxAmount} bytes from a buffer array to {@code destination}. + * The copied data is <b>not</b> marked as consumed from the source buffers, on the + * assumption the copy will be passed to some method which will consume between 0 and + * {@code maxAmount} bytes which can then be reflected in the source array using the + * {@code consume()} method. + * + */ + public static ByteBuffer copyNoConsume(ByteBuffer[] buffers, ByteBuffer destination, int maxAmount) { + checkArgument(destination.remaining() >= maxAmount, "Destination buffer too small"); + int needed = maxAmount; + for (ByteBuffer buffer : buffers) { + int remaining = buffer.remaining(); + if (remaining > 0) { + // If this buffer can fit completely then copy it all, otherwise temporarily + // adjust its limit to fill so as to the output buffer completely + int oldPosition = buffer.position(); + if (remaining <= needed) { + destination.put(buffer); + needed -= remaining; + } else { + int oldLimit = buffer.limit(); + buffer.limit(buffer.position() + needed); + destination.put(buffer); + buffer.limit(oldLimit); + needed = 0; + } + // Restore the buffer's position, the data won't get marked as consumed until + // outputBuffer has been successfully consumed. + buffer.position(oldPosition); + if (needed == 0) { + break; + } + } + } + destination.flip(); + return destination; + } +}
diff --git a/common/src/main/java/org/conscrypt/ByteArray.java b/common/src/main/java/org/conscrypt/ByteArray.java index afdab14..bfc544f 100644 --- a/common/src/main/java/org/conscrypt/ByteArray.java +++ b/common/src/main/java/org/conscrypt/ByteArray.java
@@ -41,6 +41,9 @@ return false; } ByteArray lhs = (ByteArray) o; + if (hashCode != lhs.hashCode) { + return false; + } return Arrays.equals(bytes, lhs.bytes); } }
diff --git a/common/src/main/java/org/conscrypt/CertBlacklist.java b/common/src/main/java/org/conscrypt/CertBlocklist.java similarity index 86% rename from common/src/main/java/org/conscrypt/CertBlacklist.java rename to common/src/main/java/org/conscrypt/CertBlocklist.java index 6ef7e81..024947b 100644 --- a/common/src/main/java/org/conscrypt/CertBlacklist.java +++ b/common/src/main/java/org/conscrypt/CertBlocklist.java
@@ -22,15 +22,15 @@ /** * A set of certificates that are blacklisted from trust. */ -public interface CertBlacklist { +public interface CertBlocklist { /** * Returns whether the given public key is in the blacklist. */ - boolean isPublicKeyBlackListed(PublicKey publicKey); + boolean isPublicKeyBlockListed(PublicKey publicKey); /** * Returns whether the given serial number is in the blacklist. */ - boolean isSerialNumberBlackListed(BigInteger serial); + boolean isSerialNumberBlockListed(BigInteger serial); }
diff --git a/common/src/main/java/org/conscrypt/CertificatePriorityComparator.java b/common/src/main/java/org/conscrypt/CertificatePriorityComparator.java index c364da5..eb62fca 100644 --- a/common/src/main/java/org/conscrypt/CertificatePriorityComparator.java +++ b/common/src/main/java/org/conscrypt/CertificatePriorityComparator.java
@@ -75,6 +75,7 @@ } @Override + @SuppressWarnings("JdkObsolete") // Certificate uses Date public int compare(X509Certificate lhs, X509Certificate rhs) { int result; boolean lhsSelfSigned = lhs.getSubjectDN().equals(lhs.getIssuerDN());
diff --git a/common/src/main/java/org/conscrypt/Conscrypt.java b/common/src/main/java/org/conscrypt/Conscrypt.java index 42440fd..8b2f362 100644 --- a/common/src/main/java/org/conscrypt/Conscrypt.java +++ b/common/src/main/java/org/conscrypt/Conscrypt.java
@@ -21,7 +21,9 @@ import java.security.KeyManagementException; import java.security.PrivateKey; import java.security.Provider; +import java.security.cert.X509Certificate; import java.util.Properties; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContextSpi; @@ -29,6 +31,7 @@ import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLException; import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSessionContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; @@ -55,6 +58,17 @@ } } + /** + * Return {@code true} if BoringSSL has been built in FIPS mode. + */ + public static boolean isBoringSSLFIPSBuild() { + try { + return NativeCrypto.usesBoringSSL_FIPS_mode(); + } catch (Throwable e) { + return false; + } + } + public static class Version { private final int major; private final int minor; @@ -88,6 +102,7 @@ patch = Integer.parseInt(props.getProperty("org.conscrypt.version.patch", "-1")); } } catch (IOException e) { + // TODO(prb): This should probably be fatal or have some fallback behaviour } finally { IoUtils.closeQuietly(stream); } @@ -781,5 +796,15 @@ return toConscrypt(trustManager).getHostnameVerifier(); } - + /** + * Wraps the HttpsURLConnection.HostnameVerifier into a ConscryptHostnameVerifier + */ + public static ConscryptHostnameVerifier wrapHostnameVerifier(final HostnameVerifier verifier) { + return new ConscryptHostnameVerifier() { + @Override + public boolean verify(X509Certificate[] certificates, String hostname, SSLSession session) { + return verifier.verify(hostname, session); + } + }; + } }
diff --git a/common/src/main/java/org/conscrypt/ConscryptEngine.java b/common/src/main/java/org/conscrypt/ConscryptEngine.java index 5de5396..2296b90 100644 --- a/common/src/main/java/org/conscrypt/ConscryptEngine.java +++ b/common/src/main/java/org/conscrypt/ConscryptEngine.java
@@ -76,6 +76,7 @@ import java.security.cert.X509Certificate; import java.security.interfaces.ECKey; import java.security.spec.ECParameterSpec; +import java.util.Arrays; import javax.crypto.SecretKey; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; @@ -89,7 +90,6 @@ import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; -import org.conscrypt.ExternalSession.Provider; import org.conscrypt.NativeRef.SSL_SESSION; import org.conscrypt.NativeSsl.BioWrapper; import org.conscrypt.SSLParametersImpl.AliasChooser; @@ -100,6 +100,7 @@ final class ConscryptEngine extends AbstractConscryptEngine implements NativeCrypto.SSLHandshakeCallbacks, SSLParametersImpl.AliasChooser, SSLParametersImpl.PSKCallbacks { + private static final SSLEngineResult NEED_UNWRAP_OK = new SSLEngineResult(OK, NEED_UNWRAP, 0, 0); private static final SSLEngineResult NEED_UNWRAP_CLOSED = @@ -156,7 +157,7 @@ * The session object exposed externally from this class. */ private final SSLSession externalSession = - Platform.wrapSSLSession(new ExternalSession(new Provider() { + Platform.wrapSSLSession(new ExternalSession(new ExternalSession.Provider() { @Override public ConscryptSession provideSession() { return ConscryptEngine.this.provideSession(); @@ -586,7 +587,7 @@ SSLSession handshakeSession() { synchronized (ssl) { if (state == STATE_HANDSHAKE_STARTED) { - return Platform.wrapSSLSession(new ExternalSession(new Provider() { + return Platform.wrapSSLSession(new ExternalSession(new ExternalSession.Provider() { @Override public ConscryptSession provideSession() { return ConscryptEngine.this.provideHandshakeSession(); @@ -1391,6 +1392,11 @@ throw new ReadOnlyBufferException(); } + if ((srcsOffset != 0) || (srcsLength != srcs.length)) { + srcs = Arrays.copyOfRange(srcs, srcsOffset, srcsOffset + srcsLength); + } + BufferUtils.checkNotNull(srcs); + synchronized (ssl) { switch (state) { case STATE_MODE_SET: @@ -1430,118 +1436,111 @@ // NEED_WRAP - just fall through to perform the wrap. } - int srcsLen = 0; - final int endOffset = srcsOffset + srcsLength; - for (int i = srcsOffset; i < endOffset; ++i) { - final ByteBuffer src = srcs[i]; - if (src == null) { - throw new IllegalArgumentException("srcs[" + i + "] is null"); - } - if (srcsLen == SSL3_RT_MAX_PLAIN_LENGTH) { - continue; - } - - srcsLen += src.remaining(); - if (srcsLen > SSL3_RT_MAX_PLAIN_LENGTH || srcsLen < 0) { - // If srcLen > MAX_PLAINTEXT_LENGTH or secLen < 0 just set it to - // MAX_PLAINTEXT_LENGTH. - // This also help us to guard against overflow. - // We not break out here as we still need to check for null entries in srcs[]. - srcsLen = SSL3_RT_MAX_PLAIN_LENGTH; - } - } - - if (dst.remaining() < calculateOutNetBufSize(srcsLen)) { + int dataLength = (int) min(BufferUtils.remaining(srcs), SSL3_RT_MAX_PLAIN_LENGTH); + if (dst.remaining() < calculateOutNetBufSize(dataLength)) { return new SSLEngineResult( Status.BUFFER_OVERFLOW, getHandshakeStatusInternal(), 0, 0); } int bytesProduced = 0; int bytesConsumed = 0; - loop: - for (int i = srcsOffset; i < endOffset; ++i) { - final ByteBuffer src = srcs[i]; - checkArgument(src != null, "srcs[%d] is null", i); - while (src.hasRemaining()) { - final SSLEngineResult pendingNetResult; - // Write plaintext application data to the SSL engine - int result = writePlaintextData( - src, min(src.remaining(), SSL3_RT_MAX_PLAIN_LENGTH - bytesConsumed)); - if (result > 0) { - bytesConsumed += result; + if (dataLength > 0) { + // Try and find a single buffer to send, e.g. the first non-empty buffer has + // more than enough data remaining to fill a TLS record. Otherwise copy as much + // data as possible from the source buffers to fill a record. Note the we can't + // mark the data as consumed until we see how much the TLS layer actually consumes. + boolean isCopy = false; + ByteBuffer outputBuffer + = BufferUtils.getBufferLargerThan(srcs, SSL3_RT_MAX_PLAIN_LENGTH); + if (outputBuffer == null) { + // The buffer by getOrCreateLazyDirectBuffer() is also used by + // writePlainTextDataHeap(), but by filling it here the write path will go via + // writePlainTextDataDirect() and the cost will be approximately the same, + // especially if compacting multiple non-direct buffers into a single + // direct one. + // TODO(): use bufferAllocator if set. + // https://github.com/google/conscrypt/issues/974 + outputBuffer = BufferUtils.copyNoConsume( + srcs, getOrCreateLazyDirectBuffer(), SSL3_RT_MAX_PLAIN_LENGTH); + isCopy = true; + } + final SSLEngineResult pendingNetResult; + // Write plaintext application data to the SSL engine + int result = writePlaintextData(outputBuffer, + min(SSL3_RT_MAX_PLAIN_LENGTH, outputBuffer.remaining())); + if (result > 0) { + bytesConsumed = result; + if (isCopy) { + // Data was a copy, so mark it as consumed in the original buffers. + BufferUtils.consume(srcs, bytesConsumed); + } - pendingNetResult = readPendingBytesFromBIO( + pendingNetResult = readPendingBytesFromBIO( dst, bytesConsumed, bytesProduced, handshakeStatus); - if (pendingNetResult != null) { - if (pendingNetResult.getStatus() != OK) { - return pendingNetResult; - } - bytesProduced = pendingNetResult.bytesProduced(); + if (pendingNetResult != null) { + if (pendingNetResult.getStatus() != OK) { + return pendingNetResult; } - if (bytesConsumed == SSL3_RT_MAX_PLAIN_LENGTH) { - // If we consumed the maximum amount of bytes for the plaintext length - // break out of the loop and start to fill the dst buffer. - break loop; - } - } else { - int sslError = ssl.getError(result); - switch (sslError) { - case SSL_ERROR_ZERO_RETURN: - // This means the connection was shutdown correctly, close inbound - // and outbound - closeAll(); - pendingNetResult = readPendingBytesFromBIO( - dst, bytesConsumed, bytesProduced, handshakeStatus); - return pendingNetResult != null ? pendingNetResult - : CLOSED_NOT_HANDSHAKING; - case SSL_ERROR_WANT_READ: - // If there is no pending data to read from BIO we should go back to - // event loop and try - // to read more data [1]. It is also possible that event loop will - // detect the socket - // has been closed. [1] - // https://www.openssl.org/docs/manmaster/man3/SSL_write.html - pendingNetResult = readPendingBytesFromBIO( - dst, bytesConsumed, bytesProduced, handshakeStatus); - return pendingNetResult != null - ? pendingNetResult - : new SSLEngineResult(getEngineStatus(), NEED_UNWRAP, - bytesConsumed, bytesProduced); - case SSL_ERROR_WANT_WRITE: - // SSL_ERROR_WANT_WRITE typically means that the underlying - // transport is not writable - // and we should set the "want write" flag on the selector and try - // again when the - // underlying transport is writable [1]. However we are not directly - // writing to the - // underlying transport and instead writing to a BIO buffer. The - // OpenSsl documentation - // says we should do the following [1]: - // - // "When using a buffering BIO, like a BIO pair, data must be - // written into or retrieved - // out of the BIO before being able to continue." - // - // So we attempt to drain the BIO buffer below, but if there is no - // data this condition - // is undefined and we assume their is a fatal error with the - // openssl engine and close. - // [1] https://www.openssl.org/docs/manmaster/man3/SSL_write.html - pendingNetResult = readPendingBytesFromBIO( - dst, bytesConsumed, bytesProduced, handshakeStatus); - return pendingNetResult != null ? pendingNetResult - : NEED_WRAP_CLOSED; - default: - // Everything else is considered as error - closeAll(); - throw newSslExceptionWithMessage("SSL_write"); - } + bytesProduced = pendingNetResult.bytesProduced(); + } + } else { + int sslError = ssl.getError(result); + switch (sslError) { + case SSL_ERROR_ZERO_RETURN: + // This means the connection was shutdown correctly, close inbound + // and outbound + closeAll(); + pendingNetResult = readPendingBytesFromBIO( + dst, bytesConsumed, bytesProduced, handshakeStatus); + return pendingNetResult != null ? pendingNetResult + : CLOSED_NOT_HANDSHAKING; + case SSL_ERROR_WANT_READ: + // If there is no pending data to read from BIO we should go back to + // event loop and try + // to read more data [1]. It is also possible that event loop will + // detect the socket + // has been closed. [1] + // https://www.openssl.org/docs/manmaster/man3/SSL_write.html + pendingNetResult = readPendingBytesFromBIO( + dst, bytesConsumed, bytesProduced, handshakeStatus); + return pendingNetResult != null + ? pendingNetResult + : new SSLEngineResult(getEngineStatus(), NEED_UNWRAP, + bytesConsumed, bytesProduced); + case SSL_ERROR_WANT_WRITE: + // SSL_ERROR_WANT_WRITE typically means that the underlying + // transport is not writable + // and we should set the "want write" flag on the selector and try + // again when the + // underlying transport is writable [1]. However we are not directly + // writing to the + // underlying transport and instead writing to a BIO buffer. The + // OpenSsl documentation + // says we should do the following [1]: + // + // "When using a buffering BIO, like a BIO pair, data must be + // written into or retrieved + // out of the BIO before being able to continue." + // + // So we attempt to drain the BIO buffer below, but if there is no + // data this condition + // is undefined and we assume their is a fatal error with the + // openssl engine and close. + // [1] https://www.openssl.org/docs/manmaster/man3/SSL_write.html + pendingNetResult = readPendingBytesFromBIO( + dst, bytesConsumed, bytesProduced, handshakeStatus); + return pendingNetResult != null ? pendingNetResult + : NEED_WRAP_CLOSED; + default: + // Everything else is considered as error + closeAll(); + throw newSslExceptionWithMessage("SSL_write: error " + sslError); } } } - // We need to check if pendingWrittenBytesInBIO was checked yet, as we may not checked - // if the srcs was - // empty, or only contained empty buffers. + + // We need to check if pendingWrittenBytesInBIO was checked yet, as we may not have + // checked if the srcs was empty, or only contained empty buffers. if (bytesConsumed == 0) { SSLEngineResult pendingNetResult = readPendingBytesFromBIO(dst, 0, bytesProduced, handshakeStatus); @@ -1549,9 +1548,6 @@ return pendingNetResult; } } - - // return new SSLEngineResult(OK, getHandshakeStatusInternal(), bytesConsumed, - // bytesProduced); return newResult(bytesConsumed, bytesProduced, handshakeStatus); } } @@ -1673,16 +1669,19 @@ private void closeAndFreeResources() { transitionTo(STATE_CLOSED); - if (!ssl.isClosed()) { + if (ssl != null) { ssl.close(); + } + if (networkBio != null) { networkBio.close(); } } @Override + @SuppressWarnings("deprecation") protected void finalize() throws Throwable { try { - transitionTo(STATE_CLOSED); + closeAndFreeResources(); } finally { super.finalize(); }
diff --git a/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java b/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java index 11e7cbb..48c6b3d 100644 --- a/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java +++ b/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java
@@ -60,6 +60,8 @@ private SSLOutputStream out; private SSLInputStream in; + private long handshakeStartedMillis; + private BufferAllocator bufferAllocator = ConscryptEngine.getDefaultBufferAllocator(); // @GuardedBy("stateLock"); @@ -193,6 +195,7 @@ // Initialize the handshake if we haven't already. if (state == STATE_NEW) { state = STATE_HANDSHAKE_STARTED; + handshakeStartedMillis = Platform.getMillisSinceBoot(); engine.beginHandshake(); in = new SSLInputStream(); out = new SSLOutputStream(); @@ -247,6 +250,9 @@ case FINISHED: { // Handshake is complete. finished = true; + Platform.countTlsHandshake(true, engine.getSession().getProtocol(), + engine.getSession().getCipherSuite(), + Platform.getMillisSinceBoot() - handshakeStartedMillis); break; } default: { @@ -257,6 +263,9 @@ } } catch (SSLException e) { drainOutgoingQueue(); + Platform.countTlsHandshake(false, engine.getSession().getProtocol(), + engine.getSession().getCipherSuite(), + Platform.getMillisSinceBoot() - handshakeStartedMillis); close(); throw e; } catch (IOException e) {
diff --git a/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java b/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java index 003037b..cf4c83d 100644 --- a/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java +++ b/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java
@@ -108,7 +108,7 @@ * The session object exposed externally from this class. */ private final SSLSession externalSession = - Platform.wrapSSLSession(new ExternalSession(new Provider() { + Platform.wrapSSLSession(new ExternalSession(new ExternalSession.Provider() { @Override public ConscryptSession provideSession() { return ConscryptFileDescriptorSocket.this.provideSession(); @@ -118,6 +118,8 @@ private int writeTimeoutMilliseconds = 0; private int handshakeTimeoutMilliseconds = -1; // -1 = same as timeout; 0 = infinite + private long handshakeStartedMillis; + // The constructors should not be called except from the Platform class, because we may // want to construct a subclass instead. ConscryptFileDescriptorSocket(SSLParametersImpl sslParameters) throws IOException { @@ -183,6 +185,7 @@ checkOpen(); synchronized (ssl) { if (state == STATE_NEW) { + handshakeStartedMillis = Platform.getMillisSinceBoot(); transitionTo(STATE_HANDSHAKE_STARTED); } else { // We've either started the handshake already or have been closed. @@ -228,6 +231,9 @@ // Update the session from the current state of the SSL object. activeSession.onPeerCertificateAvailable(getHostnameOrIP(), getPort()); } catch (CertificateException e) { + Platform.countTlsHandshake(false, activeSession.getProtocol(), + activeSession.getCipherSuite(), + Platform.getMillisSinceBoot() - handshakeStartedMillis); SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage()); wrapper.initCause(e); throw wrapper; @@ -285,6 +291,10 @@ } } } catch (SSLProtocolException e) { + Platform.countTlsHandshake(false, activeSession.getProtocol(), + activeSession.getCipherSuite(), + Platform.getMillisSinceBoot() - handshakeStartedMillis); + throw(SSLHandshakeException) new SSLHandshakeException("Handshake failed").initCause(e); } finally { // on exceptional exit, treat the socket as closed @@ -338,6 +348,10 @@ // The handshake has completed successfully ... + Platform.countTlsHandshake(true, activeSession.getProtocol(), + activeSession.getCipherSuite(), + Platform.getMillisSinceBoot() - handshakeStartedMillis); + // First, update the state. synchronized (ssl) { if (state == STATE_CLOSED) { @@ -716,7 +730,7 @@ public final SSLSession getHandshakeSession() { synchronized (ssl) { if (state >= STATE_HANDSHAKE_STARTED && state < STATE_READY) { - return Platform.wrapSSLSession(new ExternalSession(new Provider() { + return Platform.wrapSSLSession(new ExternalSession(new ExternalSession.Provider() { @Override public ConscryptSession provideSession() { return ConscryptFileDescriptorSocket.this.provideHandshakeSession(); @@ -952,7 +966,7 @@ * Note write timeouts are not part of the javax.net.ssl.SSLSocket API */ @Override - public final int getSoWriteTimeout() throws SocketException { + public final int getSoWriteTimeout() { return writeTimeoutMilliseconds; } @@ -1064,6 +1078,7 @@ } @Override + @SuppressWarnings("deprecation") protected final void finalize() throws Throwable { try { /*
diff --git a/common/src/main/java/org/conscrypt/ConscryptHostnameVerifier.java b/common/src/main/java/org/conscrypt/ConscryptHostnameVerifier.java index e09ac1e..2772269 100644 --- a/common/src/main/java/org/conscrypt/ConscryptHostnameVerifier.java +++ b/common/src/main/java/org/conscrypt/ConscryptHostnameVerifier.java
@@ -16,6 +16,7 @@ package org.conscrypt; +import java.security.cert.X509Certificate; import javax.net.ssl.SSLSession; /** @@ -29,6 +30,6 @@ * Returns whether the given hostname is allowable given the peer's authentication information * from the given session. */ - boolean verify(String hostname, SSLSession session); + boolean verify(X509Certificate[] certs, String hostname, SSLSession session); }
diff --git a/common/src/main/java/org/conscrypt/ExternalSession.java b/common/src/main/java/org/conscrypt/ExternalSession.java index 18c67a3..123d895 100644 --- a/common/src/main/java/org/conscrypt/ExternalSession.java +++ b/common/src/main/java/org/conscrypt/ExternalSession.java
@@ -48,7 +48,7 @@ final class ExternalSession implements ConscryptSession { // Use an initial capacity of 2 to keep it small in the average case. - private final HashMap<String, Object> values = new HashMap<String, Object>(2); + private final HashMap<String, Object> values = new HashMap<>(2); private final Provider provider; public ExternalSession(Provider provider) { @@ -112,6 +112,7 @@ } @Override + @SuppressWarnings("deprecation") // Public API public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { return provider.provideSession().getPeerCertificateChain(); } @@ -171,7 +172,7 @@ @Override public String[] getValueNames() { - return values.keySet().toArray(new String[values.size()]); + return values.keySet().toArray(new String[0]); } @Override
diff --git a/common/src/main/java/org/conscrypt/Java7ExtendedSSLSession.java b/common/src/main/java/org/conscrypt/Java7ExtendedSSLSession.java index 10b1104..2cc25ac 100644 --- a/common/src/main/java/org/conscrypt/Java7ExtendedSSLSession.java +++ b/common/src/main/java/org/conscrypt/Java7ExtendedSSLSession.java
@@ -134,6 +134,7 @@ } @Override + @SuppressWarnings("deprecation") // Public API public final X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { return delegate.getPeerCertificateChain(); }
diff --git a/common/src/main/java/org/conscrypt/Java8ExtendedSSLSession.java b/common/src/main/java/org/conscrypt/Java8ExtendedSSLSession.java index def85d2..6330d3b 100644 --- a/common/src/main/java/org/conscrypt/Java8ExtendedSSLSession.java +++ b/common/src/main/java/org/conscrypt/Java8ExtendedSSLSession.java
@@ -36,7 +36,7 @@ public final List<SNIServerName> getRequestedServerNames() { String requestedServerName = delegate.getRequestedServerName(); if (requestedServerName == null) { - return null; + return Collections.emptyList(); } return Collections.singletonList((SNIServerName) new SNIHostName(requestedServerName));
diff --git a/common/src/main/java/org/conscrypt/KeyManagerImpl.java b/common/src/main/java/org/conscrypt/KeyManagerImpl.java index c23d20a..afd957d 100644 --- a/common/src/main/java/org/conscrypt/KeyManagerImpl.java +++ b/common/src/main/java/org/conscrypt/KeyManagerImpl.java
@@ -55,36 +55,33 @@ /** * Creates Key manager */ + @SuppressWarnings("JdkObsolete") // KeyStore#aliases is the only way of enumerating all entries KeyManagerImpl(KeyStore keyStore, char[] pwd) { - this.hash = new HashMap<String, PrivateKeyEntry>(); + this.hash = new HashMap<>(); final Enumeration<String> aliases; try { aliases = keyStore.aliases(); } catch (KeyStoreException e) { return; } - for (; aliases.hasMoreElements();) { + while (aliases.hasMoreElements()) { final String alias = aliases.nextElement(); try { - if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) { - KeyStore.PrivateKeyEntry entry; + if (keyStore.entryInstanceOf(alias, PrivateKeyEntry.class)) { + PrivateKeyEntry entry; try { - entry = (KeyStore.PrivateKeyEntry) keyStore + entry = (PrivateKeyEntry) keyStore .getEntry(alias, new KeyStore.PasswordProtection(pwd)); } catch (UnsupportedOperationException e) { // If the KeyStore doesn't support getEntry(), as Android Keystore // doesn't, fall back to reading the two values separately. PrivateKey key = (PrivateKey) keyStore.getKey(alias, pwd); Certificate[] certs = keyStore.getCertificateChain(alias); - entry = new KeyStore.PrivateKeyEntry(key, certs); + entry = new PrivateKeyEntry(key, certs); } hash.put(alias, entry); } - } catch (KeyStoreException ignored) { - // Ignored. - } catch (UnrecoverableEntryException ignored) { - // Ignored. - } catch (NoSuchAlgorithmException ignored) { + } catch (KeyStoreException | UnrecoverableEntryException | NoSuchAlgorithmException ignored) { // Ignored. } } @@ -159,7 +156,7 @@ return null; } List<Principal> issuersList = (issuers == null) ? null : Arrays.asList(issuers); - ArrayList<String> found = new ArrayList<String>(); + ArrayList<String> found = new ArrayList<>(); for (final Map.Entry<String, PrivateKeyEntry> entry : hash.entrySet()) { final String alias = entry.getKey(); final Certificate[] chain = entry.getValue().getCertificateChain(); @@ -224,7 +221,7 @@ } } if (!found.isEmpty()) { - return found.toArray(new String[found.size()]); + return found.toArray(new String[0]); } return null; }
diff --git a/common/src/main/java/org/conscrypt/NativeCrypto.java b/common/src/main/java/org/conscrypt/NativeCrypto.java index ba55df3..b5f80fb 100644 --- a/common/src/main/java/org/conscrypt/NativeCrypto.java +++ b/common/src/main/java/org/conscrypt/NativeCrypto.java
@@ -21,6 +21,7 @@ import java.io.OutputStream; import java.net.SocketTimeoutException; import java.nio.Buffer; +import java.nio.ByteBuffer; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.MessageDigest; @@ -202,6 +203,12 @@ static native int ECDSA_verify(byte[] data, byte[] sig, NativeRef.EVP_PKEY pkey); + // --- Curve25519 -------------- + + static native boolean X25519(byte[] out, byte[] privateKey, byte[] publicKey) throws InvalidKeyException; + + static native void X25519_keypair(byte[] outPublicKey, byte[] outPrivateKey); + // --- Message digest functions -------------- // These return const references @@ -331,11 +338,33 @@ static native int EVP_AEAD_CTX_seal(long evpAead, byte[] key, int tagLengthInBytes, byte[] out, int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength, byte[] ad) - throws ShortBufferException, BadPaddingException, IndexOutOfBoundsException; + throws ShortBufferException, BadPaddingException; + + static native int EVP_AEAD_CTX_seal_buf(long evpAead, byte[] key, int tagLengthInBytes, ByteBuffer out, + byte[] nonce, ByteBuffer input, byte[] ad) + throws ShortBufferException, BadPaddingException; static native int EVP_AEAD_CTX_open(long evpAead, byte[] key, int tagLengthInBytes, byte[] out, int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength, byte[] ad) - throws ShortBufferException, BadPaddingException, IndexOutOfBoundsException; + throws ShortBufferException, BadPaddingException; + + static native int EVP_AEAD_CTX_open_buf(long evpAead, byte[] key, int tagLengthInBytes, ByteBuffer out, + byte[] nonce, ByteBuffer input, byte[] ad) + throws ShortBufferException, BadPaddingException; + + // --- CMAC functions ------------------------------------------------------ + + static native long CMAC_CTX_new(); + + static native void CMAC_CTX_free(long ctx); + + static native void CMAC_Init(NativeRef.CMAC_CTX ctx, byte[] key); + + static native void CMAC_Update(NativeRef.CMAC_CTX ctx, byte[] in, int inOffset, int inLength); + + static native void CMAC_UpdateDirect(NativeRef.CMAC_CTX ctx, long inPtr, int inLength); + + static native byte[] CMAC_Final(NativeRef.CMAC_CTX ctx); // --- HMAC functions ------------------------------------------------------ @@ -413,8 +442,6 @@ static native void X509_free(long x509ctx, OpenSSLX509Certificate holder); - static native long X509_dup(long x509ctx, OpenSSLX509Certificate holder); - static native int X509_cmp(long x509ctx1, OpenSSLX509Certificate holder, long x509ctx2, OpenSSLX509Certificate holder2); static native void X509_print_ex(long bioCtx, long x509ctx, OpenSSLX509Certificate holder, long nmflag, long certflag); @@ -460,7 +487,9 @@ static native void X509_verify(long x509ctx, OpenSSLX509Certificate holder, NativeRef.EVP_PKEY pkeyCtx) throws BadPaddingException; - static native byte[] get_X509_cert_info_enc(long x509ctx, OpenSSLX509Certificate holder); + static native byte[] get_X509_tbs_cert(long x509ctx, OpenSSLX509Certificate holder); + + static native byte[] get_X509_tbs_cert_without_ext(long x509ctx, OpenSSLX509Certificate holder, String oid); static native byte[] get_X509_signature(long x509ctx, OpenSSLX509Certificate holder); @@ -518,8 +547,6 @@ static native byte[] X509_CRL_get_ext_oid(long x509CrlCtx, OpenSSLX509CRL holder, String oid); - static native void X509_delete_ext(long x509, OpenSSLX509Certificate holder, String oid); - static native long X509_CRL_get_version(long x509CrlCtx, OpenSSLX509CRL holder); static native long X509_CRL_get_ext(long x509CrlCtx, OpenSSLX509CRL holder, String oid); @@ -1398,26 +1425,12 @@ SSLHandshakeCallbacks shc) throws IOException; /** - * Writes data from the given array to the BIO. - */ - static native int ENGINE_SSL_write_BIO_heap(long ssl, NativeSsl ssl_holder, long bioRef, byte[] sourceJava, - int sourceOffset, int sourceLength, SSLHandshakeCallbacks shc) - throws IOException, IndexOutOfBoundsException; - - /** * Reads data from the given BIO into a direct {@link java.nio.ByteBuffer}. */ static native int ENGINE_SSL_read_BIO_direct(long ssl, NativeSsl ssl_holder, long bioRef, long address, int len, SSLHandshakeCallbacks shc) throws IOException; /** - * Reads data from the given BIO into an array. - */ - static native int ENGINE_SSL_read_BIO_heap(long ssl, NativeSsl ssl_holder, long bioRef, byte[] destJava, - int destOffset, int destLength, SSLHandshakeCallbacks shc) - throws IOException, IndexOutOfBoundsException; - - /** * Forces the SSL object to process any data pending in the BIO. */ static native void ENGINE_SSL_force_read(long ssl, NativeSsl ssl_holder, @@ -1431,6 +1444,11 @@ throws IOException; /** + * Return {@code true} if BoringSSL has been built in FIPS mode. + */ + static native boolean usesBoringSSL_FIPS_mode(); + + /** * Used for testing only. */ static native int BIO_read(long bioRef, byte[] buffer) throws IOException;
diff --git a/common/src/main/java/org/conscrypt/NativeRef.java b/common/src/main/java/org/conscrypt/NativeRef.java index db14f62..9d7de4b 100644 --- a/common/src/main/java/org/conscrypt/NativeRef.java +++ b/common/src/main/java/org/conscrypt/NativeRef.java
@@ -46,6 +46,7 @@ } @Override + @SuppressWarnings("deprecation") protected void finalize() throws Throwable { try { if (address != 0) { @@ -58,6 +59,17 @@ abstract void doFree(long context); + static final class CMAC_CTX extends NativeRef { + CMAC_CTX(long nativePointer) { + super(nativePointer); + } + + @Override + void doFree(long context) { + NativeCrypto.CMAC_CTX_free(context); + } + } + static final class EC_GROUP extends NativeRef { EC_GROUP(long ctx) { super(ctx);
diff --git a/common/src/main/java/org/conscrypt/NativeSsl.java b/common/src/main/java/org/conscrypt/NativeSsl.java index 6a70bf0..b7b0263 100644 --- a/common/src/main/java/org/conscrypt/NativeSsl.java +++ b/common/src/main/java/org/conscrypt/NativeSsl.java
@@ -30,6 +30,7 @@ import java.io.UnsupportedEncodingException; import java.net.SocketException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.PrivateKey; import java.security.PublicKey; @@ -211,7 +212,7 @@ byte[][] asn1DerEncodedPrincipals) throws SSLException, CertificateEncodingException { Set<String> keyTypesSet = SSLUtils.getSupportedClientKeyTypes(keyTypeBytes, signatureAlgs); - String[] keyTypes = keyTypesSet.toArray(new String[keyTypesSet.size()]); + String[] keyTypes = keyTypesSet.toArray(new String[0]); X500Principal[] issuers; if (asn1DerEncodedPrincipals == null) { @@ -636,6 +637,7 @@ } @Override + @SuppressWarnings("deprecation") protected final void finalize() throws Throwable { try { close(); @@ -655,27 +657,51 @@ } int getPendingWrittenBytes() { - if (bio != 0) { - return NativeCrypto.SSL_pending_written_bytes_in_BIO(bio); - } else { - return 0; + lock.readLock().lock(); + try { + return (bio == 0L) ? 0 : NativeCrypto.SSL_pending_written_bytes_in_BIO(bio); + } finally { + lock.readLock().unlock(); } } int writeDirectByteBuffer(long address, int length) throws IOException { - return NativeCrypto.ENGINE_SSL_write_BIO_direct( - ssl, NativeSsl.this, bio, address, length, handshakeCallbacks); + lock.readLock().lock(); + try { + if (isClosed()) { + throw new SSLException("Connection closed"); + } + return NativeCrypto.ENGINE_SSL_write_BIO_direct( + ssl, NativeSsl.this, bio, address, length, handshakeCallbacks); + } finally { + lock.readLock().unlock(); + } } int readDirectByteBuffer(long destAddress, int destLength) throws IOException { - return NativeCrypto.ENGINE_SSL_read_BIO_direct( - ssl, NativeSsl.this, bio, destAddress, destLength, handshakeCallbacks); + lock.readLock().lock(); + try { + if (isClosed()) { + throw new SSLException("Connection closed"); + } + return NativeCrypto.ENGINE_SSL_read_BIO_direct( + ssl, NativeSsl.this, bio, destAddress, destLength, handshakeCallbacks); + } finally { + lock.readLock().unlock(); + } } void close() { - long toFree = bio; - bio = 0L; - NativeCrypto.BIO_free_all(toFree); + lock.writeLock().lock(); + try { + long toFree = bio; + bio = 0L; + if (toFree != 0L) { + NativeCrypto.BIO_free_all(toFree); + } + } finally { + lock.writeLock().unlock(); + } } } }
diff --git a/common/src/main/java/org/conscrypt/NativeSslSession.java b/common/src/main/java/org/conscrypt/NativeSslSession.java index 983c2ef..7c15c44 100644 --- a/common/src/main/java/org/conscrypt/NativeSslSession.java +++ b/common/src/main/java/org/conscrypt/NativeSslSession.java
@@ -428,7 +428,7 @@ } @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { + public Certificate[] getPeerCertificates() { throw new UnsupportedOperationException(); } @@ -438,13 +438,13 @@ } @Override - public X509Certificate[] getPeerCertificateChain() - throws SSLPeerUnverifiedException { + @SuppressWarnings("deprecation") + public X509Certificate[] getPeerCertificateChain() { throw new UnsupportedOperationException(); } @Override - public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { + public Principal getPeerPrincipal() { throw new UnsupportedOperationException(); }
diff --git a/common/src/main/java/org/conscrypt/OkHostnameVerifier.java b/common/src/main/java/org/conscrypt/OkHostnameVerifier.java new file mode 100644 index 0000000..c14e371 --- /dev/null +++ b/common/src/main/java/org/conscrypt/OkHostnameVerifier.java
@@ -0,0 +1,286 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.security.cert.Certificate; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; + +/** + * A HostnameVerifier consistent with <a + * href="http://www.ietf.org/rfc/rfc2818.txt">RFC 2818</a>. + */ +public final class OkHostnameVerifier implements ConscryptHostnameVerifier { + // Android-changed: Add a mode which disallows top-level domain wildcards. b/144694112 + // public static final OkHostnameVerifier INSTANCE = new OkHostnameVerifier(); + public static final OkHostnameVerifier INSTANCE = new OkHostnameVerifier(false); + + /** + * Quick and dirty pattern to differentiate IP addresses from hostnames. This + * is an approximation of Android's private InetAddress#isNumeric API. + * + * <p>This matches IPv6 addresses as a hex string containing at least one + * colon, and possibly including dots after the first colon. It matches IPv4 + * addresses as strings containing only decimal digits and dots. This pattern + * matches strings like "a:.23" and "54" that are neither IP addresses nor + * hostnames; they will be verified as IP addresses (which is a more strict + * verification). + */ + private static final Pattern VERIFY_AS_IP_ADDRESS = Pattern.compile( + "([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)"); + + private static final int ALT_DNS_NAME = 2; + private static final int ALT_IPA_NAME = 7; + + // BEGIN Android-changed: Add a mode which disallows top-level domain wildcards. b/144694112 + // private OkHostnameVerifier() { + // } + private final boolean strictWildcardMode; + + private OkHostnameVerifier(boolean strictWildcardMode) { + this.strictWildcardMode = strictWildcardMode; + } + + public static OkHostnameVerifier strictInstance() { + return new OkHostnameVerifier(true); + } + // END Android-changed: Add a mode which disallows top-level domain wildcards. b/144694112 + + @Override + public boolean verify(X509Certificate[] certs, String host, SSLSession session) { + if (certs.length > 0) { + return verify(host, certs[0]); + } else { + try { + Certificate[] certificates = session.getPeerCertificates(); + return verify(host, (X509Certificate) certificates[0]); + } catch (SSLException e) { + return false; + } + } + } + + public boolean verify(String host, X509Certificate certificate) { + return verifyAsIpAddress(host) + ? verifyIpAddress(host, certificate) + : verifyHostName(host, certificate); + } + + static boolean verifyAsIpAddress(String host) { + return VERIFY_AS_IP_ADDRESS.matcher(host).matches(); + } + + /** + * Returns true if {@code certificate} matches {@code ipAddress}. + */ + private boolean verifyIpAddress(String ipAddress, X509Certificate certificate) { + List<String> altNames = getSubjectAltNames(certificate, ALT_IPA_NAME); + for (int i = 0, size = altNames.size(); i < size; i++) { + if (ipAddress.equalsIgnoreCase(altNames.get(i))) { + return true; + } + } + return false; + } + + /** + * Returns true if {@code certificate} matches {@code hostName}. + */ + @SuppressWarnings("UnusedVariable") + private boolean verifyHostName(String hostName, X509Certificate certificate) { + hostName = hostName.toLowerCase(Locale.US); + boolean hasDns = false; + List<String> altNames = getSubjectAltNames(certificate, ALT_DNS_NAME); + for (int i = 0, size = altNames.size(); i < size; i++) { + hasDns = true; + if (verifyHostName(hostName, altNames.get(i))) { + return true; + } + } + return false; + } + + // BEGIN Android-removed: Ignore common name in hostname verification. http://b/70278814 + /* + if (!hasDns) { + X500Principal principal = certificate.getSubjectX500Principal(); + // RFC 2818 advises using the most specific name for matching. + String cn = new DistinguishedNameParser(principal).findMostSpecific("cn"); + if (cn != null) { + return verifyHostName(hostName, cn); + } + } + */ + // END Android-removed: Ignore common name in hostname verification. http://b/70278814 + + public static List<String> allSubjectAltNames(X509Certificate certificate) { + List<String> altIpaNames = getSubjectAltNames(certificate, ALT_IPA_NAME); + List<String> altDnsNames = getSubjectAltNames(certificate, ALT_DNS_NAME); + List<String> result = new ArrayList<>(altIpaNames.size() + altDnsNames.size()); + result.addAll(altIpaNames); + result.addAll(altDnsNames); + return result; + } + + @SuppressWarnings("MixedMutabilityReturnType") + private static List<String> getSubjectAltNames(X509Certificate certificate, int type) { + List<String> result = new ArrayList<>(); + try { + Collection<?> subjectAltNames = certificate.getSubjectAlternativeNames(); + if (subjectAltNames == null) { + return Collections.emptyList(); + } + for (Object subjectAltName : subjectAltNames) { + List<?> entry = (List<?>) subjectAltName; + if (entry == null || entry.size() < 2) { + continue; + } + Integer altNameType = (Integer) entry.get(0); + if (altNameType == null) { + continue; + } + if (altNameType == type) { + String altName = (String) entry.get(1); + if (altName != null) { + result.add(altName); + } + } + } + return result; + } catch (CertificateParsingException e) { + return Collections.emptyList(); + } + } + + /** + * Returns {@code true} iff {@code hostName} matches the domain name {@code pattern}. + * + * @param hostName lower-case host name. + * @param pattern domain name pattern from certificate. May be a wildcard pattern such as + * {@code *.android.com}. + */ + private boolean verifyHostName(String hostName, String pattern) { + // Basic sanity checks + // Check length == 0 instead of .isEmpty() to support Java 5. + if (hostName == null || hostName.length() == 0 || hostName.startsWith(".") + || hostName.endsWith("..")) { + // Invalid domain name + return false; + } + if (pattern == null || pattern.length() == 0 || pattern.startsWith(".") + || pattern.endsWith("..")) { + // Invalid pattern/domain name + return false; + } + + // Normalize hostName and pattern by turning them into absolute domain names if they are not + // yet absolute. This is needed because server certificates do not normally contain absolute + // names or patterns, but they should be treated as absolute. At the same time, any hostName + // presented to this method should also be treated as absolute for the purposes of matching + // to the server certificate. + // www.android.com matches www.android.com + // www.android.com matches www.android.com. + // www.android.com. matches www.android.com. + // www.android.com. matches www.android.com + if (!hostName.endsWith(".")) { + hostName += '.'; + } + if (!pattern.endsWith(".")) { + pattern += '.'; + } + // hostName and pattern are now absolute domain names. + + pattern = pattern.toLowerCase(Locale.US); + // hostName and pattern are now in lower case -- domain names are case-insensitive. + + if (!pattern.contains("*")) { + // Not a wildcard pattern -- hostName and pattern must match exactly. + return hostName.equals(pattern); + } + // Wildcard pattern + + // WILDCARD PATTERN RULES: + // 1. Asterisk (*) is only permitted in the left-most domain name label and must be the + // only character in that label (i.e., must match the whole left-most label). + // For example, *.example.com is permitted, while *a.example.com, a*.example.com, + // a*b.example.com, a.*.example.com are not permitted. + // 2. Asterisk (*) cannot match across domain name labels. + // For example, *.example.com matches test.example.com but does not match + // sub.test.example.com. + // 3. Wildcard patterns for single-label domain names are not permitted. + // 4. Android-added: if strictWildcardMode is true then wildcards matching top-level domains, + // e.g. *.com, are not permitted. + + if (!pattern.startsWith("*.") || pattern.indexOf('*', 1) != -1) { + // Asterisk (*) is only permitted in the left-most domain name label and must be the only + // character in that label + return false; + } + + // Optimization: check whether hostName is too short to match the pattern. hostName must be at + // least as long as the pattern because asterisk must match the whole left-most label and + // hostName starts with a non-empty label. Thus, asterisk has to match one or more characters. + if (hostName.length() < pattern.length()) { + // hostName too short to match the pattern. + return false; + } + + if ("*.".equals(pattern)) { + // Wildcard pattern for single-label domain name -- not permitted. + return false; + } + + // BEGIN Android-added: Disallow top-level wildcards in strict mode. http://b/144694112 + if (strictWildcardMode) { + // By this point we know the pattern has been normalised and starts with a wildcard, + // i.e. "*.domainpart." + String domainPart = pattern.substring(2, pattern.length() - 1); + // If the domain part contains no dots then this pattern will match top level domains. + if (domainPart.indexOf('.') < 0) { + return false; + } + } + // END Android-added: Disallow top-level wildcards in strict mode. http://b/144694112 + + // hostName must end with the region of pattern following the asterisk. + String suffix = pattern.substring(1); + if (!hostName.endsWith(suffix)) { + // hostName does not end with the suffix + return false; + } + + // Check that asterisk did not match across domain name labels. + int suffixStartIndexInHostName = hostName.length() - suffix.length(); + if ((suffixStartIndexInHostName > 0) + && (hostName.lastIndexOf('.', suffixStartIndexInHostName - 1) != -1)) { + // Asterisk is matching across domain name labels -- not permitted. + return false; + } + + // hostName matches pattern + return true; + } +} \ No newline at end of file
diff --git a/common/src/main/java/org/conscrypt/OpenSSLAeadCipher.java b/common/src/main/java/org/conscrypt/OpenSSLAeadCipher.java index 0d48ad7..1b201cc 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLAeadCipher.java +++ b/common/src/main/java/org/conscrypt/OpenSSLAeadCipher.java
@@ -33,6 +33,11 @@ @Internal public abstract class OpenSSLAeadCipher extends OpenSSLCipher { /** + * Controls whether no-copy optimizations for direct ByteBuffers are enabled. + */ + private static final boolean ENABLE_BYTEBUFFER_OPTIMIZATIONS = true; + + /** * The default tag size when one is not specified. Default to * full-length tags (128-bits or 16 octets). */ @@ -84,7 +89,7 @@ */ int tagLengthInBytes; - public OpenSSLAeadCipher(Mode mode) { + protected OpenSSLAeadCipher(Mode mode) { super(mode, Padding.NOPADDING); } @@ -221,6 +226,49 @@ } @Override + protected int engineDoFinal(ByteBuffer input, ByteBuffer output) throws ShortBufferException, + IllegalBlockSizeException, BadPaddingException { + if (!ENABLE_BYTEBUFFER_OPTIMIZATIONS) { + return super.engineDoFinal(input, output); + } + if (input == null || output == null) { + throw new NullPointerException("Null ByteBuffer Error"); + } + if (getOutputSizeForFinal(input.remaining()) > output.remaining()) { + throw new ShortBufferWithoutStackTraceException("Insufficient Bytes for Output Buffer"); + } + if (output.isReadOnly()) { + throw new IllegalArgumentException("Cannot write to Read Only ByteBuffer"); + } + if (bufCount != 0) { + return super.engineDoFinal(input, output);// traditional case + } + int bytesWritten; + if (!input.isDirect()) { + int incap = input.remaining(); + ByteBuffer inputClone = ByteBuffer.allocateDirect(incap); + inputClone.mark(); + inputClone.put(input); + inputClone.reset(); + input = inputClone; + } + if (!output.isDirect()) { + ByteBuffer outputClone = ByteBuffer.allocateDirect( + getOutputSizeForFinal(input.remaining())); + bytesWritten = doFinalInternal(input, outputClone); + output.put(outputClone); + input.position(input.limit()); // API reasons + } + else { + bytesWritten = doFinalInternal(input, output); + output.position(output.position() + bytesWritten); + input.position(input.limit()); // API reasons + } + + return bytesWritten; + } + + @Override protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { @@ -281,6 +329,28 @@ } } + int doFinalInternal(ByteBuffer input, ByteBuffer output) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + checkInitialization(); + final int bytesWritten; + try { + if (isEncrypting()) { + bytesWritten = NativeCrypto.EVP_AEAD_CTX_seal_buf( + evpAead, encodedKey, tagLengthInBytes, output, iv, input, aad); + } else { + bytesWritten = NativeCrypto.EVP_AEAD_CTX_open_buf( + evpAead, encodedKey, tagLengthInBytes, output, iv, input, aad); + } + } catch (BadPaddingException e) { + throwAEADBadTagExceptionIfAvailable(e.getMessage(), e.getCause()); + throw e; + } + if (isEncrypting()) { + mustInitialize = true; + } + return bytesWritten; + } + @Override int doFinalInternal(byte[] output, int outputOffset, int maximumLen) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
diff --git a/common/src/main/java/org/conscrypt/OpenSSLBIOSink.java b/common/src/main/java/org/conscrypt/OpenSSLBIOSink.java deleted file mode 100644 index b385eb9..0000000 --- a/common/src/main/java/org/conscrypt/OpenSSLBIOSink.java +++ /dev/null
@@ -1,77 +0,0 @@ -/* - * Copyright (C) 2014 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 java.io.ByteArrayOutputStream; - -/** - * Wraps a BoringSSL BIO to act as a place to write out data. - */ -final class OpenSSLBIOSink { - private final long ctx; - private final ByteArrayOutputStream buffer; - private int position; - - static OpenSSLBIOSink create() { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - return new OpenSSLBIOSink(buffer); - } - - private OpenSSLBIOSink(ByteArrayOutputStream buffer) { - ctx = NativeCrypto.create_BIO_OutputStream(buffer); - this.buffer = buffer; - } - - int available() { - return buffer.size() - position; - } - - void reset() { - buffer.reset(); - position = 0; - } - - long skip(long byteCount) { - int maxLength = Math.min(available(), (int) byteCount); - position += maxLength; - if (position == buffer.size()) { - reset(); - } - return maxLength; - } - - long getContext() { - return ctx; - } - - byte[] toByteArray() { - return buffer.toByteArray(); - } - - int position() { - return position; - } - - @Override - protected void finalize() throws Throwable { - try { - NativeCrypto.BIO_free_all(ctx); - } finally { - super.finalize(); - } - } -}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLBIOSource.java b/common/src/main/java/org/conscrypt/OpenSSLBIOSource.java deleted file mode 100644 index fd3437d..0000000 --- a/common/src/main/java/org/conscrypt/OpenSSLBIOSource.java +++ /dev/null
@@ -1,106 +0,0 @@ -/* - * Copyright (C) 2014 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 java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; - -/** - * Wrapped by a BoringSSL BIO to act as a source of bytes. - */ -final class OpenSSLBIOSource { - private OpenSSLBIOInputStream source; - - static OpenSSLBIOSource wrap(ByteBuffer buffer) { - return new OpenSSLBIOSource( - new OpenSSLBIOInputStream(new ByteBufferInputStream(buffer), false)); - } - - private OpenSSLBIOSource(OpenSSLBIOInputStream source) { - this.source = source; - } - - long getContext() { - return source.getBioContext(); - } - - private synchronized void release() { - if (source != null) { - NativeCrypto.BIO_free_all(source.getBioContext()); - source = null; - } - } - - @Override - protected void finalize() throws Throwable { - try { - release(); - } finally { - super.finalize(); - } - } - - private static class ByteBufferInputStream extends InputStream { - private final ByteBuffer source; - - ByteBufferInputStream(ByteBuffer source) { - this.source = source; - } - - @Override - public int read() throws IOException { - if (source.remaining() > 0) { - return source.get(); - } else { - return -1; - } - } - - @Override - public int available() throws IOException { - return source.limit() - source.position(); - } - - @Override - public int read(byte[] buffer) throws IOException { - int originalPosition = source.position(); - source.get(buffer); - return source.position() - originalPosition; - } - - @Override - public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { - int toRead = Math.min(source.remaining(), byteCount); - int originalPosition = source.position(); - source.get(buffer, byteOffset, toRead); - return source.position() - originalPosition; - } - - @Override - public void reset() throws IOException { - source.reset(); - } - - @Override - public long skip(long byteCount) throws IOException { - long originalPosition = source.position(); - source.position((int) (originalPosition + byteCount)); - return source.position() - originalPosition; - } - } -}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLBaseDHKeyAgreement.java b/common/src/main/java/org/conscrypt/OpenSSLBaseDHKeyAgreement.java new file mode 100644 index 0000000..2aa2643 --- /dev/null +++ b/common/src/main/java/org/conscrypt/OpenSSLBaseDHKeyAgreement.java
@@ -0,0 +1,158 @@ +/* + * Copyright (C) 2013 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 java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.KeyAgreementSpi; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; + +/** + * Generic base classe for Diffie-Hellman style key agreement backed by the OpenSSL engine. + */ +@Internal +public abstract class OpenSSLBaseDHKeyAgreement<T> extends KeyAgreementSpi { + + /** OpenSSL handle of the private key. Only available after the engine has been initialized. */ + private T mPrivateKey; + + /** + * Expected length (in bytes) of the agreed key ({@link #mResult}). Only available after the + * engine has been initialized. + */ + private int mExpectedResultLength; + + /** Agreed key. Only available after {@link #engineDoPhase(Key, boolean)} completes. */ + private byte[] mResult; + + protected OpenSSLBaseDHKeyAgreement() {} + + @Override + public Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException { + if (mPrivateKey == null) { + throw new IllegalStateException("Not initialized"); + } + if (!lastPhase) { + throw new IllegalStateException("DH only has one phase"); + } + + if (key == null) { + throw new InvalidKeyException("key == null"); + } + if (!(key instanceof PublicKey)) { + throw new InvalidKeyException("Not a public key: " + key.getClass()); + } + T openSslPublicKey = convertPublicKey((PublicKey) key); + + byte[] buffer = new byte[mExpectedResultLength]; + int actualResultLength = computeKey(buffer, openSslPublicKey, mPrivateKey); + byte[] result; + if (actualResultLength == -1) { + throw new RuntimeException("Engine returned -1"); + } else if (actualResultLength == mExpectedResultLength) { + // The output is as long as expected -- use the whole buffer + result = buffer; + } else if (actualResultLength < mExpectedResultLength) { + // The output is shorter than expected -- use only what's produced by the engine + result = new byte[actualResultLength]; + System.arraycopy(buffer, 0, mResult, 0, mResult.length); + } else { + // The output is longer than expected + throw new RuntimeException("Engine produced a longer than expected result. Expected: " + + mExpectedResultLength + ", actual: " + actualResultLength); + } + mResult = result; + + return null; // No intermediate key + } + + protected abstract T convertPublicKey(PublicKey key) throws InvalidKeyException; + + protected abstract T convertPrivateKey(PrivateKey key) throws InvalidKeyException; + + /** + * Given the public key {@code theirPublicKey} and the private key {@code ourPrivateKey} write the shared secret + * to {@code buffer} and return the actual output size. + */ + protected abstract int computeKey(byte[] buffer, T theirPublicKey, T ourPrivateKey) throws InvalidKeyException; + + @Override + protected int engineGenerateSecret(byte[] sharedSecret, int offset) + throws ShortBufferException { + checkCompleted(); + int available = sharedSecret.length - offset; + if (mResult.length > available) { + throw new ShortBufferWithoutStackTraceException( + "Needed: " + mResult.length + ", available: " + available); + } + + System.arraycopy(mResult, 0, sharedSecret, offset, mResult.length); + return mResult.length; + } + + @Override + protected byte[] engineGenerateSecret() { + checkCompleted(); + return mResult; + } + + @Override + protected SecretKey engineGenerateSecret(String algorithm) { + checkCompleted(); + return new SecretKeySpec(engineGenerateSecret(), algorithm); + } + + @Override + protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { + if (key == null) { + throw new InvalidKeyException("key == null"); + } + if (!(key instanceof PrivateKey)) { + throw new InvalidKeyException("Not a private key: " + key.getClass()); + } + + T privateKey = convertPrivateKey((PrivateKey) key); + mExpectedResultLength = getOutputSize(privateKey); + mPrivateKey = privateKey; + } + + /** Returns the expected result length for the given {@code key}. */ + protected abstract int getOutputSize(T key); + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params, + SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + // ECDH doesn't need an AlgorithmParameterSpec + if (params != null) { + throw new InvalidAlgorithmParameterException("No algorithm parameters supported"); + } + engineInit(key, random); + } + + private void checkCompleted() { + if (mResult == null) { + throw new IllegalStateException("Key agreement not completed"); + } + } +}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLCipherRSA.java b/common/src/main/java/org/conscrypt/OpenSSLCipherRSA.java index 678b3bd..b9771cc 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLCipherRSA.java +++ b/common/src/main/java/org/conscrypt/OpenSSLCipherRSA.java
@@ -375,7 +375,7 @@ } public abstract static class DirectRSA extends OpenSSLCipherRSA { - public DirectRSA(int padding) { + protected DirectRSA(int padding) { super(padding); }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLECDHKeyAgreement.java b/common/src/main/java/org/conscrypt/OpenSSLECDHKeyAgreement.java index 708fd95..614f20e 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLECDHKeyAgreement.java +++ b/common/src/main/java/org/conscrypt/OpenSSLECDHKeyAgreement.java
@@ -16,136 +16,41 @@ package org.conscrypt; -import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; -import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.spec.AlgorithmParameterSpec; -import javax.crypto.KeyAgreementSpi; -import javax.crypto.SecretKey; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.SecretKeySpec; /** * Elliptic Curve Diffie-Hellman key agreement backed by the OpenSSL engine. */ @Internal -public final class OpenSSLECDHKeyAgreement extends KeyAgreementSpi { - - /** OpenSSL handle of the private key. Only available after the engine has been initialized. */ - private OpenSSLKey mOpenSslPrivateKey; - - /** - * Expected length (in bytes) of the agreed key ({@link #mResult}). Only available after the - * engine has been initialized. - */ - private int mExpectedResultLength; - - /** Agreed key. Only available after {@link #engineDoPhase(Key, boolean)} completes. */ - private byte[] mResult; - - public OpenSSLECDHKeyAgreement() {} +public final class OpenSSLECDHKeyAgreement extends OpenSSLBaseDHKeyAgreement<OpenSSLKey> { + public OpenSSLECDHKeyAgreement() { + } @Override - public Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException { - if (mOpenSslPrivateKey == null) { - throw new IllegalStateException("Not initialized"); - } - if (!lastPhase) { - throw new IllegalStateException("ECDH only has one phase"); - } + protected OpenSSLKey convertPublicKey(PublicKey key) throws InvalidKeyException { + return OpenSSLKey.fromPublicKey(key); + } - if (key == null) { - throw new InvalidKeyException("key == null"); - } - if (!(key instanceof PublicKey)) { - throw new InvalidKeyException("Not a public key: " + key.getClass()); - } - OpenSSLKey openSslPublicKey = OpenSSLKey.fromPublicKey((PublicKey) key); + @Override + protected OpenSSLKey convertPrivateKey(PrivateKey key) throws InvalidKeyException { + return OpenSSLKey.fromPrivateKey(key); + } - byte[] buffer = new byte[mExpectedResultLength]; - int actualResultLength = NativeCrypto.ECDH_compute_key( + @Override + protected int computeKey(byte[] buffer, OpenSSLKey theirPublicKey, OpenSSLKey ourPrivateKey) throws InvalidKeyException { + return NativeCrypto.ECDH_compute_key( buffer, 0, - openSslPublicKey.getNativeRef(), - mOpenSslPrivateKey.getNativeRef()); - byte[] result; - if (actualResultLength == -1) { - throw new RuntimeException("Engine returned " + actualResultLength); - } else if (actualResultLength == mExpectedResultLength) { - // The output is as long as expected -- use the whole buffer - result = buffer; - } else if (actualResultLength < mExpectedResultLength) { - // The output is shorter than expected -- use only what's produced by the engine - result = new byte[actualResultLength]; - System.arraycopy(buffer, 0, mResult, 0, mResult.length); - } else { - // The output is longer than expected - throw new RuntimeException("Engine produced a longer than expected result. Expected: " - + mExpectedResultLength + ", actual: " + actualResultLength); - } - mResult = result; - - return null; // No intermediate key + theirPublicKey.getNativeRef(), + ourPrivateKey.getNativeRef()); } @Override - protected int engineGenerateSecret(byte[] sharedSecret, int offset) - throws ShortBufferException { - checkCompleted(); - int available = sharedSecret.length - offset; - if (mResult.length > available) { - throw new ShortBufferWithoutStackTraceException( - "Needed: " + mResult.length + ", available: " + available); - } - - System.arraycopy(mResult, 0, sharedSecret, offset, mResult.length); - return mResult.length; - } - - @Override - protected byte[] engineGenerateSecret() { - checkCompleted(); - return mResult; - } - - @Override - protected SecretKey engineGenerateSecret(String algorithm) { - checkCompleted(); - return new SecretKeySpec(engineGenerateSecret(), algorithm); - } - - @Override - protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { - if (key == null) { - throw new InvalidKeyException("key == null"); - } - if (!(key instanceof PrivateKey)) { - throw new InvalidKeyException("Not a private key: " + key.getClass()); - } - - OpenSSLKey openSslKey = OpenSSLKey.fromPrivateKey((PrivateKey) key); + protected int getOutputSize(OpenSSLKey openSslKey) { int fieldSizeBits = NativeCrypto.EC_GROUP_get_degree(new NativeRef.EC_GROUP( NativeCrypto.EC_KEY_get1_group(openSslKey.getNativeRef()))); - mExpectedResultLength = (fieldSizeBits + 7) / 8; - mOpenSslPrivateKey = openSslKey; - } - - @Override - protected void engineInit(Key key, AlgorithmParameterSpec params, - SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - // ECDH doesn't need an AlgorithmParameterSpec - if (params != null) { - throw new InvalidAlgorithmParameterException("No algorithm parameters supported"); - } - engineInit(key, random); - } - - private void checkCompleted() { - if (mResult == null) { - throw new IllegalStateException("Key agreement not completed"); - } + return (fieldSizeBits + 7) / 8; } }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java b/common/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java index 63495f1..9af2b13 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java +++ b/common/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java
@@ -41,9 +41,9 @@ private static final String ALGORITHM = "EC"; - protected transient OpenSSLKey key; + private transient OpenSSLKey key; - protected transient OpenSSLECGroupContext group; + private transient OpenSSLECGroupContext group; OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key) { this.group = group;
diff --git a/common/src/main/java/org/conscrypt/OpenSSLECPublicKey.java b/common/src/main/java/org/conscrypt/OpenSSLECPublicKey.java index 1891e24..d68c465 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLECPublicKey.java +++ b/common/src/main/java/org/conscrypt/OpenSSLECPublicKey.java
@@ -37,9 +37,9 @@ private static final String ALGORITHM = "EC"; - protected transient OpenSSLKey key; + private transient OpenSSLKey key; - protected transient OpenSSLECGroupContext group; + private transient OpenSSLECGroupContext group; OpenSSLECPublicKey(OpenSSLECGroupContext group, OpenSSLKey key) { this.group = group;
diff --git a/common/src/main/java/org/conscrypt/OpenSSLEvpCipher.java b/common/src/main/java/org/conscrypt/OpenSSLEvpCipher.java index 7631c78..f527127 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLEvpCipher.java +++ b/common/src/main/java/org/conscrypt/OpenSSLEvpCipher.java
@@ -46,7 +46,7 @@ */ private int modeBlockSize; - public OpenSSLEvpCipher(Mode mode, Padding padding) { + protected OpenSSLEvpCipher(Mode mode, Padding padding) { super(mode, padding); }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLEvpCipherAES.java b/common/src/main/java/org/conscrypt/OpenSSLEvpCipherAES.java index b094410..b49468e 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLEvpCipherAES.java +++ b/common/src/main/java/org/conscrypt/OpenSSLEvpCipherAES.java
@@ -78,13 +78,13 @@ super(Mode.CBC, padding); } - public static class NoPadding extends CBC { + public static class NoPadding extends AES.CBC { public NoPadding() { super(Padding.NOPADDING); } } - public static class PKCS5Padding extends CBC { + public static class PKCS5Padding extends AES.CBC { public PKCS5Padding() { super(Padding.PKCS5PADDING); } @@ -102,13 +102,13 @@ super(Mode.ECB, padding); } - public static class NoPadding extends ECB { + public static class NoPadding extends AES.ECB { public NoPadding() { super(Padding.NOPADDING); } } - public static class PKCS5Padding extends ECB { + public static class PKCS5Padding extends AES.ECB { public PKCS5Padding() { super(Padding.PKCS5PADDING); } @@ -139,13 +139,13 @@ super(Mode.CBC, padding); } - public static class NoPadding extends CBC { + public static class NoPadding extends AES_128.CBC { public NoPadding() { super(Padding.NOPADDING); } } - public static class PKCS5Padding extends CBC { + public static class PKCS5Padding extends AES_128.CBC { public PKCS5Padding() { super(Padding.PKCS5PADDING); } @@ -163,13 +163,13 @@ super(Mode.ECB, padding); } - public static class NoPadding extends ECB { + public static class NoPadding extends AES_128.ECB { public NoPadding() { super(Padding.NOPADDING); } } - public static class PKCS5Padding extends ECB { + public static class PKCS5Padding extends AES_128.ECB { public PKCS5Padding() { super(Padding.PKCS5PADDING); } @@ -194,13 +194,13 @@ super(Mode.CBC, padding); } - public static class NoPadding extends CBC { + public static class NoPadding extends AES_256.CBC { public NoPadding() { super(Padding.NOPADDING); } } - public static class PKCS5Padding extends CBC { + public static class PKCS5Padding extends AES_256.CBC { public PKCS5Padding() { super(Padding.PKCS5PADDING); } @@ -218,13 +218,13 @@ super(Mode.ECB, padding); } - public static class NoPadding extends ECB { + public static class NoPadding extends AES_256.ECB { public NoPadding() { super(Padding.NOPADDING); } } - public static class PKCS5Padding extends ECB { + public static class PKCS5Padding extends AES_256.ECB { public PKCS5Padding() { super(Padding.PKCS5PADDING); }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLMac.java b/common/src/main/java/org/conscrypt/OpenSSLMac.java index 13f1d75..5a5a068 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLMac.java +++ b/common/src/main/java/org/conscrypt/OpenSSLMac.java
@@ -30,18 +30,10 @@ */ @Internal public abstract class OpenSSLMac extends MacSpi { - private NativeRef.HMAC_CTX ctx; - - /** - * Holds the EVP_MD for the hashing algorithm, e.g. - * EVP_get_digestbyname("sha1"); - */ - private final long evp_md; - /** * The secret key used in this keyed MAC. */ - private byte[] keyBytes; + protected byte[] keyBytes; /** * Holds the output size of the message digest. @@ -53,11 +45,20 @@ */ private final byte[] singleByte = new byte[1]; - private OpenSSLMac(long evp_md, int size) { - this.evp_md = evp_md; + private OpenSSLMac(int size) { this.size = size; } + /** + * Creates and initializes the relevant BoringSSL *MAC context. + */ + protected abstract void resetContext(); + + /** + * Passes the contents of a direct ByteBuffer to the relevant BoringSSL *MAC function. + */ + protected abstract void updateDirect(long ptr, int len); + @Override protected int engineGetMacLength() { return size; @@ -82,15 +83,6 @@ resetContext(); } - private final void resetContext() { - NativeRef.HMAC_CTX ctxLocal = new NativeRef.HMAC_CTX(NativeCrypto.HMAC_CTX_new()); - if (keyBytes != null) { - NativeCrypto.HMAC_Init_ex(ctxLocal, keyBytes, evp_md); - } - - this.ctx = ctxLocal; - } - @Override protected void engineUpdate(byte input) { singleByte[0] = input; @@ -98,17 +90,10 @@ } @Override - protected void engineUpdate(byte[] input, int offset, int len) { - final NativeRef.HMAC_CTX ctxLocal = ctx; - NativeCrypto.HMAC_Update(ctxLocal, input, offset, len); - } - - @Override protected void engineUpdate(ByteBuffer input) { // Optimization: Avoid copying/allocation for direct buffers because their contents are // stored as a contiguous region in memory and thus can be efficiently accessed from native // code. - if (!input.hasRemaining()) { return; } @@ -120,7 +105,7 @@ long baseAddress = NativeCrypto.getDirectBufferAddress(input); if (baseAddress == 0) { - // Direct buffer's contents can't be accessed from JNI -- superclass's implementation + // Direct buffers' contents can't be accessed from JNI -- superclass's implementation // is good enough to handle this. super.engineUpdate(input); return; @@ -137,57 +122,135 @@ throw new RuntimeException("Negative remaining amount"); } - final NativeRef.HMAC_CTX ctxLocal = ctx; - NativeCrypto.HMAC_UpdateDirect(ctxLocal, ptr, len); + updateDirect(ptr, len); input.position(position + len); } @Override protected byte[] engineDoFinal() { - final NativeRef.HMAC_CTX ctxLocal = ctx; - final byte[] output = NativeCrypto.HMAC_Final(ctxLocal); + final byte[] output = doFinal(); resetContext(); return output; } + protected abstract byte[] doFinal(); + @Override protected void engineReset() { resetContext(); } - public static final class HmacMD5 extends OpenSSLMac { + public static class Hmac extends OpenSSLMac { + private NativeRef.HMAC_CTX ctx; + + /** + * Holds the EVP_MD for the hashing algorithm, e.g. + * EVP_get_digestbyname("sha1"); + */ + private final long evp_md; + + public Hmac(long evp_md, int size) { + super(size); + this.evp_md = evp_md; + } + + @Override + protected void resetContext() { + NativeRef.HMAC_CTX ctxLocal = new NativeRef.HMAC_CTX(NativeCrypto.HMAC_CTX_new()); + if (keyBytes != null) { + NativeCrypto.HMAC_Init_ex(ctxLocal, keyBytes, evp_md); + } + this.ctx = ctxLocal; + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + final NativeRef.HMAC_CTX ctxLocal = ctx; + NativeCrypto.HMAC_Update(ctxLocal, input, offset, len); + } + + @Override + protected void updateDirect(long ptr, int len) { + final NativeRef.HMAC_CTX ctxLocal = ctx; + NativeCrypto.HMAC_UpdateDirect(ctxLocal, ptr, len); + } + + @Override + protected byte[] doFinal() { + final NativeRef.HMAC_CTX ctxLocal = ctx; + return NativeCrypto.HMAC_Final(ctxLocal); + } + + } + + public static final class HmacMD5 extends Hmac { public HmacMD5() { super(EvpMdRef.MD5.EVP_MD, EvpMdRef.MD5.SIZE_BYTES); } } - public static final class HmacSHA1 extends OpenSSLMac { + public static final class HmacSHA1 extends Hmac { public HmacSHA1() { super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.SIZE_BYTES); } } - public static final class HmacSHA224 extends OpenSSLMac { + public static final class HmacSHA224 extends Hmac { public HmacSHA224() throws NoSuchAlgorithmException { super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.SIZE_BYTES); } } - public static final class HmacSHA256 extends OpenSSLMac { + public static final class HmacSHA256 extends Hmac { public HmacSHA256() throws NoSuchAlgorithmException { super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.SIZE_BYTES); } } - public static final class HmacSHA384 extends OpenSSLMac { + public static final class HmacSHA384 extends Hmac { public HmacSHA384() throws NoSuchAlgorithmException { super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.SIZE_BYTES); } } - public static final class HmacSHA512 extends OpenSSLMac { + public static final class HmacSHA512 extends Hmac { public HmacSHA512() { super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.SIZE_BYTES); } } + + public static final class AesCmac extends OpenSSLMac { + private NativeRef.CMAC_CTX ctx; + + public AesCmac() { + super(16); + } + + @Override + protected void resetContext() { + NativeRef.CMAC_CTX ctxLocal = new NativeRef.CMAC_CTX(NativeCrypto.CMAC_CTX_new()); + if (keyBytes != null) { + NativeCrypto.CMAC_Init(ctxLocal, keyBytes); + } + this.ctx = ctxLocal; + } + + @Override + protected void updateDirect(long ptr, int len) { + final NativeRef.CMAC_CTX ctxLocal = ctx; + NativeCrypto.CMAC_UpdateDirect(ctxLocal, ptr, len); + } + + @Override + protected byte[] doFinal() { + final NativeRef.CMAC_CTX ctxLocal = ctx; + return NativeCrypto.CMAC_Final(ctxLocal); + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + final NativeRef.CMAC_CTX ctxLocal = ctx; + NativeCrypto.CMAC_Update(ctxLocal, input, offset, len); + } + } }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLProvider.java b/common/src/main/java/org/conscrypt/OpenSSLProvider.java index 1e703ce..607ae1e 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLProvider.java +++ b/common/src/main/java/org/conscrypt/OpenSSLProvider.java
@@ -38,6 +38,8 @@ private static final String STANDARD_EC_PRIVATE_KEY_INTERFACE_CLASS_NAME = "java.security.interfaces.ECPrivateKey"; + private static final String STANDARD_XEC_PRIVATE_KEY_INTERFACE_CLASS_NAME = + "java.security.interfaces.XECPrivateKey"; private static final String STANDARD_RSA_PRIVATE_KEY_INTERFACE_CLASS_NAME = "java.security.interfaces.RSAPrivateKey"; private static final String STANDARD_RSA_PUBLIC_KEY_INTERFACE_CLASS_NAME = @@ -190,6 +192,9 @@ put("Alg.Alias.KeyPairGenerator.1.2.840.10045.2.1", "EC"); put("Alg.Alias.KeyPairGenerator.1.3.133.16.840.63.0.2", "EC"); + put("KeyPairGenerator.XDH", PREFIX + "OpenSSLXDHKeyPairGenerator"); + put("Alg.Alias.KeyPairGenerator.1.3.101.110", "XDH"); + /* == KeyFactory == */ put("KeyFactory.RSA", PREFIX + "OpenSSLRSAKeyFactory"); put("Alg.Alias.KeyFactory.1.2.840.113549.1.1.1", "RSA"); @@ -200,12 +205,16 @@ put("Alg.Alias.KeyFactory.1.2.840.10045.2.1", "EC"); put("Alg.Alias.KeyFactory.1.3.133.16.840.63.0.2", "EC"); + put("KeyFactory.XDH", PREFIX + "OpenSSLXDHKeyFactory"); + put("Alg.Alias.KeyFactory.1.3.101.110", "XDH"); + /* == SecretKeyFactory == */ put("SecretKeyFactory.DESEDE", PREFIX + "DESEDESecretKeyFactory"); put("Alg.Alias.SecretKeyFactory.TDEA", "DESEDE"); /* == KeyAgreement == */ putECDHKeyAgreementImplClass("OpenSSLECDHKeyAgreement"); + putXDHKeyAgreementImplClass("OpenSSLXDHKeyAgreement"); /* == Signatures == */ putSignatureImplClass("MD5withRSA", "OpenSSLSignature$MD5RSA"); @@ -494,6 +503,8 @@ put("Alg.Alias.Mac.HMAC/SHA512", "HmacSHA512"); put("Alg.Alias.Mac.PBEWITHHMACSHA512", "HmacSHA512"); + putMacImplClass("AESCMAC", "OpenSSLMac$AesCmac"); + /* === Certificate === */ put("CertificateFactory.X509", PREFIX + "OpenSSLX509CertificateFactory"); @@ -592,6 +603,22 @@ supportedKeyFormats); } + private void putXDHKeyAgreementImplClass(String className) { + // Accept only keys for which any of the following is true: + // * the key is from this provider (subclass of OpenSSLKeyHolder), + // * the key provides its key material in "PKCS#8" encoding via Key.getEncoded. + // * the key is a transparent XEC private key (subclass of XECPrivateKey). + String supportedKeyClasses = PREFIX + "OpenSSLKeyHolder" + + "|" + STANDARD_XEC_PRIVATE_KEY_INTERFACE_CLASS_NAME + + "|" + PREFIX + "OpenSSLX25519PrivateKey"; + String supportedKeyFormats = "PKCS#8"; + putImplClassWithKeyConstraints( + "KeyAgreement.XDH", + PREFIX + className, + supportedKeyClasses, + supportedKeyFormats); + } + private void putImplClassWithKeyConstraints(String typeAndAlgName, String fullyQualifiedClassName, String supportedKeyClasses,
diff --git a/common/src/main/java/org/conscrypt/OpenSSLX25519Key.java b/common/src/main/java/org/conscrypt/OpenSSLX25519Key.java new file mode 100644 index 0000000..8845999 --- /dev/null +++ b/common/src/main/java/org/conscrypt/OpenSSLX25519Key.java
@@ -0,0 +1,7 @@ +package org.conscrypt; + +public interface OpenSSLX25519Key { + int X25519_KEY_SIZE_BYTES = 32; + + byte[] getU(); +}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLX25519PrivateKey.java b/common/src/main/java/org/conscrypt/OpenSSLX25519PrivateKey.java new file mode 100644 index 0000000..a3c7e88 --- /dev/null +++ b/common/src/main/java/org/conscrypt/OpenSSLX25519PrivateKey.java
@@ -0,0 +1,106 @@ +package org.conscrypt; + +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Arrays; + +public class OpenSSLX25519PrivateKey implements OpenSSLX25519Key, PrivateKey { + private static final byte[] PKCS8_PREAMBLE = new byte[]{ + 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x04, 0x22, 0x04, 0x20, + }; + + private static final byte[] PKCS8_PREAMBLE_WITH_NULL = new byte[] { + 0x30, 0x30, 0x02, 0x01, 0x00, 0x30, 0x07, 0x06, 0x03, 0x2B, 0x65, 0x6E, 0x05, 0x00, 0x04, 0x22, 0x04, 0x20, + }; + + private byte[] uCoordinate; + + public OpenSSLX25519PrivateKey(PKCS8EncodedKeySpec keySpec) throws InvalidKeySpecException { + byte[] encoded = keySpec.getEncoded(); + if (encoded == null || !"PKCS#8".equals(keySpec.getFormat())) { + throw new InvalidKeySpecException("Key must be encoded in PKCS#8 format"); + } + + int preambleLength = matchesPreamble(PKCS8_PREAMBLE, encoded) | matchesPreamble(PKCS8_PREAMBLE_WITH_NULL, encoded); + if (preambleLength == 0) { + throw new InvalidKeySpecException("Key size is not correct size"); + } + + uCoordinate = Arrays.copyOfRange(encoded, PKCS8_PREAMBLE.length, encoded.length); + } + + private static int matchesPreamble(byte[] preamble, byte[] encoded) { + if (encoded.length != (preamble.length + X25519_KEY_SIZE_BYTES)) { + return 0; + } + int cmp = 0; + for (int i = 0; i < preamble.length; i++) { + cmp |= encoded[i] ^ preamble[i]; + } + if (cmp != 0) { + return 0; + } + return preamble.length; + } + + public OpenSSLX25519PrivateKey(byte[] coordinateBytes) { + uCoordinate = coordinateBytes.clone(); + } + + @Override + public String getAlgorithm() { + return "XDH"; + } + + @Override + public String getFormat() { + return "PKCS#8"; + } + + @Override + public byte[] getEncoded() { + if (uCoordinate == null) { + throw new IllegalStateException("key is destroyed"); + } + + byte[] encoded = Arrays.copyOf(PKCS8_PREAMBLE, PKCS8_PREAMBLE.length + uCoordinate.length); + System.arraycopy(uCoordinate, 0, encoded, PKCS8_PREAMBLE.length, uCoordinate.length); + return encoded; + } + + @Override + public byte[] getU() { + if (uCoordinate == null) { + throw new IllegalStateException("key is destroyed"); + } + + return uCoordinate.clone(); + } + + @Override + public void destroy() { + if (uCoordinate != null) { + Arrays.fill(uCoordinate, (byte) 0); + uCoordinate = null; + } + } + + @Override + public boolean isDestroyed() { + return uCoordinate == null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof OpenSSLX25519PrivateKey)) return false; + OpenSSLX25519PrivateKey that = (OpenSSLX25519PrivateKey) o; + return Arrays.equals(uCoordinate, that.uCoordinate); + } + + @Override + public int hashCode() { + return Arrays.hashCode(uCoordinate); + } +} \ No newline at end of file
diff --git a/common/src/main/java/org/conscrypt/OpenSSLX25519PublicKey.java b/common/src/main/java/org/conscrypt/OpenSSLX25519PublicKey.java new file mode 100644 index 0000000..abe8dc0 --- /dev/null +++ b/common/src/main/java/org/conscrypt/OpenSSLX25519PublicKey.java
@@ -0,0 +1,101 @@ +package org.conscrypt; + +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; + +public class OpenSSLX25519PublicKey implements OpenSSLX25519Key, PublicKey { + private static final byte[] X509_PREAMBLE = new byte[] { + 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x03, 0x21, 0x00, + }; + + private static final byte[] X509_PREAMBLE_WITH_NULL = new byte[] { + 0x30, 0x2C, 0x30, 0x07, 0x06, 0x03, 0x2B, 0x65, 0x6E, 0x05, 0x00, 0x03, 0x21, 0x00, + }; + + private byte[] uCoordinate; + + public OpenSSLX25519PublicKey(X509EncodedKeySpec keySpec) throws InvalidKeySpecException { + byte[] encoded = keySpec.getEncoded(); + if (encoded == null || !"X.509".equals(keySpec.getFormat())) { + throw new InvalidKeySpecException("Encoding must be in X.509 format"); + } + + int preambleLength = matchesPreamble(X509_PREAMBLE, encoded) | matchesPreamble(X509_PREAMBLE_WITH_NULL, encoded); + if (preambleLength == 0) { + throw new InvalidKeySpecException("Key size is not correct size"); + } + + uCoordinate = Arrays.copyOfRange(encoded, preambleLength, encoded.length); + } + + private static int matchesPreamble(byte[] preamble, byte[] encoded) { + if (encoded.length != (preamble.length + X25519_KEY_SIZE_BYTES)) { + return 0; + } + int cmp = 0; + for (int i = 0; i < preamble.length; i++) { + cmp |= encoded[i] ^ preamble[i]; + } + if (cmp != 0) { + return 0; + } + return preamble.length; + } + + public OpenSSLX25519PublicKey(byte[] coordinateBytes) { + uCoordinate = coordinateBytes.clone(); + } + + @Override + public String getAlgorithm() { + return "XDH"; + } + + @Override + public String getFormat() { + return "X.509"; + } + + @Override + public byte[] getEncoded() { + if (uCoordinate == null) { + throw new IllegalStateException("key is destroyed"); + } + + byte[] encoded = Arrays.copyOf(X509_PREAMBLE, X509_PREAMBLE.length + X25519_KEY_SIZE_BYTES); + System.arraycopy(uCoordinate, 0, encoded, X509_PREAMBLE.length, uCoordinate.length); + return encoded; + } + + @Override + public byte[] getU() { + if (uCoordinate == null) { + throw new IllegalStateException("key is destroyed"); + } + + return uCoordinate.clone(); + } + + @Override + public boolean equals(Object o) { + if (uCoordinate == null) { + throw new IllegalStateException("key is destroyed"); + } + + if (this == o) return true; + if (!(o instanceof OpenSSLX25519PublicKey)) return false; + OpenSSLX25519PublicKey that = (OpenSSLX25519PublicKey) o; + return Arrays.equals(uCoordinate, that.uCoordinate); + } + + @Override + public int hashCode() { + if (uCoordinate == null) { + throw new IllegalStateException("key is destroyed"); + } + + return Arrays.hashCode(uCoordinate); + } +}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLX509CRL.java b/common/src/main/java/org/conscrypt/OpenSSLX509CRL.java index abe5932..13a944b 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLX509CRL.java +++ b/common/src/main/java/org/conscrypt/OpenSSLX509CRL.java
@@ -98,12 +98,12 @@ bis.release(); } - final List<OpenSSLX509CRL> certs = new ArrayList<OpenSSLX509CRL>(certRefs.length); - for (int i = 0; i < certRefs.length; i++) { - if (certRefs[i] == 0) { + final List<OpenSSLX509CRL> certs = new ArrayList<>(certRefs.length); + for (long certRef : certRefs) { + if (certRef == 0) { continue; } - certs.add(new OpenSSLX509CRL(certRefs[i])); + certs.add(new OpenSSLX509CRL(certRef)); } return certs; } @@ -138,12 +138,12 @@ bis.release(); } - final List<OpenSSLX509CRL> certs = new ArrayList<OpenSSLX509CRL>(certRefs.length); - for (int i = 0; i < certRefs.length; i++) { - if (certRefs[i] == 0) { + final List<OpenSSLX509CRL> certs = new ArrayList<>(certRefs.length); + for (long certRef : certRefs) { + if (certRef == 0) { continue; } - certs.add(new OpenSSLX509CRL(certRefs[i])); + certs.add(new OpenSSLX509CRL(certRef)); } return certs; } @@ -164,7 +164,7 @@ return null; } - return new HashSet<String>(Arrays.asList(critOids)); + return new HashSet<>(Arrays.asList(critOids)); } @Override @@ -189,7 +189,7 @@ return null; } - return new HashSet<String>(Arrays.asList(nonCritOids)); + return new HashSet<>(Arrays.asList(nonCritOids)); } @Override @@ -220,9 +220,8 @@ } } - private void verifyInternal(PublicKey key, String sigProvider) throws CRLException, - NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, - SignatureException { + private void verifyInternal(PublicKey key, String sigProvider) throws NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { String sigAlg = getSigAlgName(); if (sigAlg == null) { sigAlg = getSigAlgOID(); @@ -329,7 +328,7 @@ return null; } - final Set<OpenSSLX509CRLEntry> crlSet = new HashSet<OpenSSLX509CRLEntry>(); + final Set<OpenSSLX509CRLEntry> crlSet = new HashSet<>(); for (long entryRef : entryRefs) { try { crlSet.add(new OpenSSLX509CRLEntry(entryRef)); @@ -342,7 +341,7 @@ } @Override - public byte[] getTBSCertList() throws CRLException { + public byte[] getTBSCertList() { return NativeCrypto.get_X509_CRL_crl_enc(mContext, this); } @@ -412,6 +411,7 @@ } @Override + @SuppressWarnings("deprecation") protected void finalize() throws Throwable { try { if (mContext != 0) {
diff --git a/common/src/main/java/org/conscrypt/OpenSSLX509CertPath.java b/common/src/main/java/org/conscrypt/OpenSSLX509CertPath.java index 50c2c30..3a5cb04 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLX509CertPath.java +++ b/common/src/main/java/org/conscrypt/OpenSSLX509CertPath.java
@@ -157,6 +157,7 @@ try { inStream.reset(); } catch (IOException ignored) { + // Ignored } } throw new CertificateException(e); @@ -220,6 +221,7 @@ try { inStream.reset(); } catch (IOException ignored) { + // Ignored } } throw new CertificateException(e);
diff --git a/common/src/main/java/org/conscrypt/OpenSSLX509Certificate.java b/common/src/main/java/org/conscrypt/OpenSSLX509Certificate.java index 2c86579..8245956 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLX509Certificate.java +++ b/common/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
@@ -130,16 +130,16 @@ if (certRefs == null) { // To avoid returning a immutable list in only one path, we create an // empty list here instead of using Collections.emptyList() - return new ArrayList<OpenSSLX509Certificate>(); + return new ArrayList<>(); } - final List<OpenSSLX509Certificate> certs = new ArrayList<OpenSSLX509Certificate>( + final List<OpenSSLX509Certificate> certs = new ArrayList<>( certRefs.length); - for (int i = 0; i < certRefs.length; i++) { - if (certRefs[i] == 0) { + for (long certRef : certRefs) { + if (certRef == 0) { continue; } - certs.add(new OpenSSLX509Certificate(certRefs[i])); + certs.add(new OpenSSLX509Certificate(certRef)); } return certs; } @@ -177,13 +177,13 @@ bis.release(); } - final List<OpenSSLX509Certificate> certs = new ArrayList<OpenSSLX509Certificate>( + final List<OpenSSLX509Certificate> certs = new ArrayList<>( certRefs.length); - for (int i = 0; i < certRefs.length; i++) { - if (certRefs[i] == 0) { + for (long certRef : certRefs) { + if (certRef == 0) { continue; } - certs.add(new OpenSSLX509Certificate(certRefs[i])); + certs.add(new OpenSSLX509Certificate(certRef)); } return certs; } @@ -215,7 +215,7 @@ return null; } - return new HashSet<String>(Arrays.asList(critOids)); + return new HashSet<>(Arrays.asList(critOids)); } @Override @@ -239,7 +239,7 @@ return null; } - return new HashSet<String>(Arrays.asList(nonCritOids)); + return new HashSet<>(Arrays.asList(nonCritOids)); } @Override @@ -248,12 +248,14 @@ } @Override + @SuppressWarnings("JdkObsolete") // Needed for API compatibility public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { checkValidity(new Date()); } @Override + @SuppressWarnings("JdkObsolete") // Needed for API compatibility public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException { if (getNotBefore().compareTo(date) > 0) { @@ -299,7 +301,7 @@ @Override public byte[] getTBSCertificate() throws CertificateEncodingException { - return NativeCrypto.get_X509_cert_info_enc(mContext, this); + return NativeCrypto.get_X509_tbs_cert(mContext, this); } @Override @@ -376,9 +378,7 @@ return NativeCrypto.i2d_X509(mContext, this); } - private void verifyOpenSSL(OpenSSLKey pkey) throws CertificateException, - NoSuchAlgorithmException, - InvalidKeyException, SignatureException { + private void verifyOpenSSL(OpenSSLKey pkey) throws CertificateException, SignatureException { try { NativeCrypto.X509_verify(mContext, this, pkey.getNativeRef()); } catch (RuntimeException e) { @@ -388,9 +388,9 @@ } } - private void verifyInternal(PublicKey key, String sigProvider) throws CertificateException, + private void verifyInternal(PublicKey key, String sigProvider) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, - SignatureException { + SignatureException, CertificateEncodingException { final Signature sig; if (sigProvider == null) { sig = Signature.getInstance(getSigAlgName()); @@ -414,7 +414,7 @@ return; } - verifyInternal(key, (String) null); + verifyInternal(key, null); } @Override @@ -468,8 +468,8 @@ try { OpenSSLKey pkey = new OpenSSLKey(NativeCrypto.X509_get_pubkey(mContext, this)); return pkey.getPublicKey(); - } catch (NoSuchAlgorithmException ignored) { - } catch (InvalidKeyException ignored) { + } catch (NoSuchAlgorithmException | InvalidKeyException ignored) { + // Ignored } /* Try generating the key using other Java providers. */ @@ -478,8 +478,8 @@ try { KeyFactory kf = KeyFactory.getInstance(oid); return kf.generatePublic(new X509EncodedKeySpec(encoded)); - } catch (NoSuchAlgorithmException ignored) { - } catch (InvalidKeySpecException ignored) { + } catch (NoSuchAlgorithmException | InvalidKeySpecException ignored) { + // Ignored } /* @@ -502,7 +502,7 @@ } @Override - public List<String> getExtendedKeyUsage() throws CertificateParsingException { + public List<String> getExtendedKeyUsage() { String[] extUsage = NativeCrypto.get_X509_ex_xkusage(mContext, this); if (extUsage == null) { return null; @@ -516,9 +516,9 @@ return null; } - Collection<List<?>> coll = new ArrayList<List<?>>(altNameArray.length); - for (int i = 0; i < altNameArray.length; i++) { - coll.add(Collections.unmodifiableList(Arrays.asList(altNameArray[i]))); + Collection<List<?>> coll = new ArrayList<>(altNameArray.length); + for (Object[] objects : altNameArray) { + coll.add(Collections.unmodifiableList(Arrays.asList(objects))); } return Collections.unmodifiableCollection(coll); @@ -567,19 +567,14 @@ } /** - * Delete an extension. - * - * A modified copy of the certificate is returned. The original object - * is unchanged. - * If the extension is not present, an unmodified copy is returned. + * Returns a re-encoded TBSCertificate with the extension identified by oid removed. */ - public OpenSSLX509Certificate withDeletedExtension(String oid) { - OpenSSLX509Certificate copy = new OpenSSLX509Certificate(NativeCrypto.X509_dup(mContext, this), notBefore, notAfter); - NativeCrypto.X509_delete_ext(copy.getContext(), copy, oid); - return copy; + public byte[] getTBSCertificateWithoutExtension(String oid) { + return NativeCrypto.get_X509_tbs_cert_without_ext(mContext, this, oid); } @Override + @SuppressWarnings("deprecation") protected void finalize() throws Throwable { try { if (mContext != 0) {
diff --git a/common/src/main/java/org/conscrypt/OpenSSLX509CertificateFactory.java b/common/src/main/java/org/conscrypt/OpenSSLX509CertificateFactory.java index c2c49e8..02d9f27 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLX509CertificateFactory.java +++ b/common/src/main/java/org/conscrypt/OpenSSLX509CertificateFactory.java
@@ -60,6 +60,40 @@ } } + private static boolean isMaybePkcs7(byte[] header) { + // The outer tag must be SEQUENCE. + if (header.length < 2 || header[0] != 0x30) { + return false; + } + + // Bytes are signed in Java. + int lengthByte = header[1] & 0xff; + + // Skip the length prefix to find the tag of the first child of SEQUENCE. This function is + // intentionally lax and does not attempt to parse the length itself. It is only necessary + // to return true on PKCS#7 inputs and false on X.509 inputs. Other structures can go either + // way. + int idx = 2; + if (lengthByte <= 0x80) { + // Short-form or indefinite length. + } else if (lengthByte == 0x81) { + idx += 1; + } else if (lengthByte == 0x82) { + idx += 2; + } else if (lengthByte == 0x83) { + idx += 3; + } else if (lengthByte == 0x84) { + idx += 4; + } else { + // BoringSSL stops at 4-byte lengths. A 5-byte length would require a 4GiB input. + return false; + } + + // The first element of a PKCS#7 structure is OBJECT IDENTIFIER, which has tag 6. The first + // element of an X.509 structure is never OBJECT IDENTIFIER. + return idx < header.length && header[idx] == 0x06; + } + /** * The code for X509 Certificates and CRL is pretty much the same. We use * this abstract class to share the code between them. This makes it ugly, @@ -88,19 +122,10 @@ pbis.unread(buffer, 0, len); if (buffer[0] == '-') { - if (len == PKCS7_MARKER.length && Arrays.equals(PKCS7_MARKER, buffer)) { - List<? extends T> items = fromPkcs7PemInputStream(pbis); - if (items.size() == 0) { - return null; - } - items.get(0); - } else { - return fromX509PemInputStream(pbis); - } + return fromX509PemInputStream(pbis); } - /* PKCS#7 bags have a byte 0x06 at position 4 in the stream. */ - if (buffer[4] == 0x06) { + if (isMaybePkcs7(buffer)) { List<? extends T> certs = fromPkcs7DerInputStream(pbis); if (certs.size() == 0) { return null; @@ -114,6 +139,7 @@ try { inStream.reset(); } catch (IOException ignored) { + // If resetting the stream fails, there's not much we can do } } throw new ParsingException(e); @@ -156,8 +182,7 @@ return fromPkcs7PemInputStream(pbis); } - /* PKCS#7 bags have a byte 0x06 at position 4 in the stream. */ - if (buffer[4] == 0x06) { + if (isMaybePkcs7(buffer)) { return fromPkcs7DerInputStream(pbis); } } catch (Exception e) { @@ -165,6 +190,7 @@ try { inStream.reset(); } catch (IOException ignored) { + // If resetting the stream fails, there's not much we can do } } throw new ParsingException(e); @@ -197,6 +223,7 @@ try { inStream.reset(); } catch (IOException ignored) { + // If resetting the stream fails, there's not much we can do } }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLXDHKeyAgreement.java b/common/src/main/java/org/conscrypt/OpenSSLXDHKeyAgreement.java new file mode 100644 index 0000000..660c5f1 --- /dev/null +++ b/common/src/main/java/org/conscrypt/OpenSSLXDHKeyAgreement.java
@@ -0,0 +1,66 @@ +/* + * Copyright (C) 2013 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 java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; + +/** + * Elliptic Curve Diffie-Hellman key agreement backed by the OpenSSL engine. + */ +@Internal +public final class OpenSSLXDHKeyAgreement extends OpenSSLBaseDHKeyAgreement<byte[]> { + public OpenSSLXDHKeyAgreement() { + } + + @Override + protected byte[] convertPublicKey(PublicKey key) throws InvalidKeyException { + if (!(key instanceof OpenSSLX25519PublicKey)) { + throw new InvalidKeyException("Only OpenSSLX25519PublicKey accepted"); + } + + return ((OpenSSLX25519PublicKey) key).getU(); + } + + @Override + protected byte[] convertPrivateKey(PrivateKey key) throws InvalidKeyException { + if (!(key instanceof OpenSSLX25519PrivateKey)) { + throw new InvalidKeyException("Only OpenSSLX25519PublicKey accepted"); + } + + return ((OpenSSLX25519PrivateKey) key).getU(); + } + + @Override + protected int computeKey(byte[] buffer, byte[] theirPublicKey, byte[] ourPrivateKey) throws InvalidKeyException { + if (!NativeCrypto.X25519( + buffer, + ourPrivateKey, + theirPublicKey)) { + throw new InvalidKeyException("Error running X25519"); + } + + return OpenSSLX25519Key.X25519_KEY_SIZE_BYTES; + } + + @Override + protected int getOutputSize(byte[] key) { + // We only support X25519 which is 32-byte (256-bit) + return OpenSSLX25519Key.X25519_KEY_SIZE_BYTES; + } +}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLXDHKeyFactory.java b/common/src/main/java/org/conscrypt/OpenSSLXDHKeyFactory.java new file mode 100644 index 0000000..e4af802 --- /dev/null +++ b/common/src/main/java/org/conscrypt/OpenSSLXDHKeyFactory.java
@@ -0,0 +1,215 @@ +/* + * Copyright (C) 2013 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 java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactorySpi; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * An implementation of a {@link KeyFactorySpi} for EC keys based on BoringSSL. + */ +@Internal +public final class OpenSSLXDHKeyFactory extends KeyFactorySpi { + + public OpenSSLXDHKeyFactory() {} + + @Override + protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException { + if (keySpec == null) { + throw new InvalidKeySpecException("keySpec == null"); + } + + if (keySpec instanceof X509EncodedKeySpec) { + return new OpenSSLX25519PublicKey((X509EncodedKeySpec) keySpec); + } + throw new InvalidKeySpecException("Must use ECPublicKeySpec or X509EncodedKeySpec; was " + + keySpec.getClass().getName()); + } + + @Override + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException { + if (keySpec == null) { + throw new InvalidKeySpecException("keySpec == null"); + } + + if (keySpec instanceof PKCS8EncodedKeySpec) { + return new OpenSSLX25519PrivateKey((PKCS8EncodedKeySpec) keySpec); + } + throw new InvalidKeySpecException("Must use ECPrivateKeySpec or PKCS8EncodedKeySpec; was " + + keySpec.getClass().getName()); + } + + @Override + protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec) + throws InvalidKeySpecException { + if (key == null) { + throw new InvalidKeySpecException("key == null"); + } + + if (keySpec == null) { + throw new InvalidKeySpecException("keySpec == null"); + } + + if (!"XDH".equals(key.getAlgorithm())) { + throw new InvalidKeySpecException("Key must be an XDH key"); + } + + Class<?> publicKeySpec = getJavaPublicKeySpec(); + Class<?> privateKeySpec = getJavaPrivateKeySpec(); + + if (publicKeySpec != null && key instanceof PublicKey && publicKeySpec.isAssignableFrom(keySpec)) { + final byte[] encoded = key.getEncoded(); + if (!"X.509".equals(key.getFormat()) || encoded == null) { + throw new InvalidKeySpecException("Not a valid X.509 encoding"); + } + OpenSSLX25519PublicKey publicKey = (OpenSSLX25519PublicKey) engineGeneratePublic(new X509EncodedKeySpec(encoded)); + @SuppressWarnings("unchecked") + T result = (T) constructJavaPublicKeySpec(publicKeySpec, publicKey); + return result; + } else if (privateKeySpec != null && key instanceof PrivateKey && privateKeySpec.isAssignableFrom(keySpec)) { + final byte[] encoded = key.getEncoded(); + if (!"PKCS#8".equals(key.getFormat()) || encoded == null) { + throw new InvalidKeySpecException("Not a valid PKCS#8 encoding"); + } + OpenSSLX25519PrivateKey privateKey = (OpenSSLX25519PrivateKey) engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded)); + @SuppressWarnings("unchecked") + T result = (T) constructJavaPrivateKeySpec(privateKeySpec, privateKey); + return result; + } else if (key instanceof PrivateKey && PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) { + final byte[] encoded = key.getEncoded(); + if (!"PKCS#8".equals(key.getFormat())) { + throw new InvalidKeySpecException("Encoding type must be PKCS#8; was " + + key.getFormat()); + } else if (encoded == null) { + throw new InvalidKeySpecException("Key is not encodable"); + } + @SuppressWarnings("unchecked") T result = (T) new PKCS8EncodedKeySpec(encoded); + return result; + } else if (key instanceof PublicKey && X509EncodedKeySpec.class.isAssignableFrom(keySpec)) { + final byte[] encoded = key.getEncoded(); + if (!"X.509".equals(key.getFormat())) { + throw new InvalidKeySpecException("Encoding type must be X.509; was " + + key.getFormat()); + } else if (encoded == null) { + throw new InvalidKeySpecException("Key is not encodable"); + } + @SuppressWarnings("unchecked") T result = (T) new X509EncodedKeySpec(encoded); + return result; + } + + throw new InvalidKeySpecException("Unsupported key type and key spec combination; key=" + + key.getClass().getName() + ", keySpec=" + keySpec.getName()); + } + + @Override + protected Key engineTranslateKey(Key key) throws InvalidKeyException { + if (key == null) { + throw new InvalidKeyException("key == null"); + } + if ((key instanceof OpenSSLX25519PublicKey) || (key instanceof OpenSSLX25519PrivateKey)) { + return key; + } else if ((key instanceof PrivateKey) && "PKCS#8".equals(key.getFormat())) { + byte[] encoded = key.getEncoded(); + if (encoded == null) { + throw new InvalidKeyException("Key does not support encoding"); + } + try { + return engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded)); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } + } else if ((key instanceof PublicKey) && "X.509".equals(key.getFormat())) { + byte[] encoded = key.getEncoded(); + if (encoded == null) { + throw new InvalidKeyException("Key does not support encoding"); + } + try { + return engineGeneratePublic(new X509EncodedKeySpec(encoded)); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } + } else { + throw new InvalidKeyException("Key must be EC public or private key; was " + + key.getClass().getName()); + } + } + + private static Class<?> getJavaPrivateKeySpec() { + try { + return Class.forName("java.security.spec.XECPrivateKeySpec"); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + private static Class<?> getJavaPublicKeySpec() { + try { + return Class.forName("java.security.spec.XECPublicKeySpec"); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + private KeySpec constructJavaPrivateKeySpec(Class<?> privateKeySpec, OpenSSLX25519PrivateKey privateKey) throws InvalidKeySpecException { + if (privateKeySpec == null) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPrivateKeySpec"); + } + + try { + Constructor<?> c = privateKeySpec.getConstructor(AlgorithmParameterSpec.class, byte[].class); + @SuppressWarnings("unchecked") + KeySpec result = (KeySpec) c.newInstance(new OpenSSLXECParameterSpec(OpenSSLXECParameterSpec.X25519), privateKey.getU()); + return result; + } catch (NoSuchMethodException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPrivateKeySpec", e); + } catch (InstantiationException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPrivateKeySpec", e); + } catch (IllegalAccessException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPrivateKeySpec", e); + } catch (InvocationTargetException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPrivateKeySpec", e); + } + } + + private KeySpec constructJavaPublicKeySpec(Class<?> publicKeySpec, OpenSSLX25519PublicKey publicKey) throws InvalidKeySpecException { + try { + Constructor<?> c = publicKeySpec.getConstructor(AlgorithmParameterSpec.class, BigInteger.class); + @SuppressWarnings("unchecked") + KeySpec result = (KeySpec) c.newInstance(new OpenSSLXECParameterSpec(OpenSSLXECParameterSpec.X25519), new BigInteger(1, publicKey.getU())); + return result; + } catch (NoSuchMethodException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPublicKeySpec", e); + } catch (InstantiationException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPublicKeySpec", e); + } catch (IllegalAccessException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPublicKeySpec", e); + } catch (InvocationTargetException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPublicKeySpec", e); + } + } +}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLXDHKeyPairGenerator.java b/common/src/main/java/org/conscrypt/OpenSSLXDHKeyPairGenerator.java new file mode 100644 index 0000000..361dd23 --- /dev/null +++ b/common/src/main/java/org/conscrypt/OpenSSLXDHKeyPairGenerator.java
@@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 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 java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +/** + * An implementation of {@link KeyPairGenerator} for XDH keys which uses BoringSSL to perform all the + * operations. This only supports X25519 keys. + */ +@Internal +public final class OpenSSLXDHKeyPairGenerator extends KeyPairGenerator { + private static final String ALGORITHM = "XDH"; + + public OpenSSLXDHKeyPairGenerator() { + super(ALGORITHM); + } + + @Override + public KeyPair generateKeyPair() { + byte[] publicKeyBytes = new byte[OpenSSLX25519Key.X25519_KEY_SIZE_BYTES]; + byte[] privateKeyBytes = new byte[OpenSSLX25519Key.X25519_KEY_SIZE_BYTES]; + + NativeCrypto.X25519_keypair(publicKeyBytes, privateKeyBytes); + + return new KeyPair(new OpenSSLX25519PublicKey(publicKeyBytes), new OpenSSLX25519PrivateKey(privateKeyBytes)); + } + + @Override + public void initialize(int keysize, SecureRandom random) { + } + + @Override + public void initialize(AlgorithmParameterSpec param, SecureRandom random) + throws InvalidAlgorithmParameterException { + throw new InvalidAlgorithmParameterException( + "No AlgorithmParameterSpec classes are supported"); + } +}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLXECParameterSpec.java b/common/src/main/java/org/conscrypt/OpenSSLXECParameterSpec.java new file mode 100644 index 0000000..9350f29 --- /dev/null +++ b/common/src/main/java/org/conscrypt/OpenSSLXECParameterSpec.java
@@ -0,0 +1,21 @@ +package org.conscrypt; + +import java.security.spec.AlgorithmParameterSpec; + +/** + * Parameter markers to assist in future compatibility should other XEC curves be supported. + */ +@Internal +class OpenSSLXECParameterSpec implements AlgorithmParameterSpec { + public static final String X25519 = "1.3.101.110"; + + private final String oid; + + public OpenSSLXECParameterSpec(String oid) { + this.oid = oid; + } + + public String getOid() { + return oid; + } +}
diff --git a/common/src/main/java/org/conscrypt/PSKKeyManager.java b/common/src/main/java/org/conscrypt/PSKKeyManager.java index 5f6222d..b998189 100644 --- a/common/src/main/java/org/conscrypt/PSKKeyManager.java +++ b/common/src/main/java/org/conscrypt/PSKKeyManager.java
@@ -81,7 +81,7 @@ * The following example illustrates how to create an {@code SSLContext} which enables the use of * TLS-PSK in {@code SSLSocket}, {@code SSLServerSocket} and {@code SSLEngine} instances obtained * from it. - * <pre> {@code + * <pre> * PSKKeyManager myPskKeyManager = ...; * * SSLContext sslContext = SSLContext.getInstance("TLS"); @@ -92,7 +92,7 @@ * ); * * SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(...); - * }</pre> + * </pre> * * @deprecated This abstraction is deprecated because it does not work with TLS 1.3. */
diff --git a/common/src/main/java/org/conscrypt/SSLNullSession.java b/common/src/main/java/org/conscrypt/SSLNullSession.java index b266843..69d6337 100644 --- a/common/src/main/java/org/conscrypt/SSLNullSession.java +++ b/common/src/main/java/org/conscrypt/SSLNullSession.java
@@ -116,6 +116,7 @@ } @Override + @SuppressWarnings("deprecation") // Public API public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { throw new SSLPeerUnverifiedException("No peer certificate");
diff --git a/common/src/main/java/org/conscrypt/SSLParametersImpl.java b/common/src/main/java/org/conscrypt/SSLParametersImpl.java index 44aa635..1c7cf98 100644 --- a/common/src/main/java/org/conscrypt/SSLParametersImpl.java +++ b/common/src/main/java/org/conscrypt/SSLParametersImpl.java
@@ -577,7 +577,9 @@ } else if (km != null) { try { return DuckTypedPSKKeyManager.getInstance(km); - } catch (NoSuchMethodException ignored) {} + } catch (NoSuchMethodException ignored) { + // This PSKKeyManager doesn't support the required methods, go to the next + } } } return null;
diff --git a/common/src/main/java/org/conscrypt/SSLUtils.java b/common/src/main/java/org/conscrypt/SSLUtils.java index 5610150..e1881d1 100644 --- a/common/src/main/java/org/conscrypt/SSLUtils.java +++ b/common/src/main/java/org/conscrypt/SSLUtils.java
@@ -275,7 +275,7 @@ */ static Set<String> getSupportedClientKeyTypes(byte[] clientCertificateTypes, int[] signatureAlgs) { - Set<String> fromClientCerts = new HashSet<String>(clientCertificateTypes.length); + Set<String> fromClientCerts = new HashSet<>(clientCertificateTypes.length); for (byte keyTypeCode : clientCertificateTypes) { String keyType = SSLUtils.getClientKeyType(keyTypeCode); if (keyType == null) { @@ -285,7 +285,7 @@ fromClientCerts.add(keyType); } // Signature algorithms are listed in preference order - Set<String> fromSigAlgs = new LinkedHashSet<String>(signatureAlgs.length); + Set<String> fromSigAlgs = new LinkedHashSet<>(signatureAlgs.length); for (int signatureAlg : signatureAlgs) { String keyType = SSLUtils.getClientKeyTypeFromSignatureAlg(signatureAlg); if (keyType == null) { @@ -319,6 +319,7 @@ /** * Converts the peer certificates into a cert chain. */ + @SuppressWarnings("deprecation") // Used in public Conscrypt APIs static javax.security.cert.X509Certificate[] toCertificateChain(X509Certificate[] certificates) throws SSLPeerUnverifiedException { try { @@ -332,11 +333,11 @@ return chain; } catch (CertificateEncodingException e) { SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage()); - exception.initCause(exception); + exception.initCause(e); throw exception; } catch (CertificateException e) { SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage()); - exception.initCause(exception); + exception.initCause(e); throw exception; } }
diff --git a/common/src/main/java/org/conscrypt/SessionSnapshot.java b/common/src/main/java/org/conscrypt/SessionSnapshot.java index 28e63b9..1fc708c 100644 --- a/common/src/main/java/org/conscrypt/SessionSnapshot.java +++ b/common/src/main/java/org/conscrypt/SessionSnapshot.java
@@ -64,7 +64,7 @@ @Override public List<byte[]> getStatusResponses() { - List<byte[]> ret = new ArrayList<byte[]>(statusResponses.size()); + List<byte[]> ret = new ArrayList<>(statusResponses.size()); for (byte[] resp : statusResponses) { ret.add(resp.clone()); } @@ -141,8 +141,13 @@ } @Override + @SuppressWarnings("deprecation") // Public API public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { + if (!Platform.isJavaxCertificateSupported()) { + throw new UnsupportedOperationException("Use getPeerCertificates() instead"); + } + throw new SSLPeerUnverifiedException("No peer certificates"); }
diff --git a/common/src/main/java/org/conscrypt/ShortBufferWithoutStackTraceException.java b/common/src/main/java/org/conscrypt/ShortBufferWithoutStackTraceException.java index 4bbfbb9..13c38d4 100644 --- a/common/src/main/java/org/conscrypt/ShortBufferWithoutStackTraceException.java +++ b/common/src/main/java/org/conscrypt/ShortBufferWithoutStackTraceException.java
@@ -35,7 +35,7 @@ super(msg); } - @Override public Throwable fillInStackTrace() { + @Override public synchronized Throwable fillInStackTrace() { return this; } }
diff --git a/common/src/main/java/org/conscrypt/TrustManagerImpl.java b/common/src/main/java/org/conscrypt/TrustManagerImpl.java index 3515fd5..ccec8e9 100644 --- a/common/src/main/java/org/conscrypt/TrustManagerImpl.java +++ b/common/src/main/java/org/conscrypt/TrustManagerImpl.java
@@ -135,7 +135,7 @@ private final Exception err; private final CertificateFactory factory; - private final CertBlacklist blacklist; + private final CertBlocklist blocklist; private CTVerifier ctVerifier; private CTPolicy ctPolicy; @@ -146,8 +146,6 @@ /** * Creates X509TrustManager based on a keystore - * - * @param keyStore */ public TrustManagerImpl(KeyStore keyStore) { this(keyStore, null); @@ -164,16 +162,16 @@ public TrustManagerImpl(KeyStore keyStore, CertPinManager manager, ConscryptCertStore certStore, - CertBlacklist blacklist) { - this(keyStore, manager, certStore, blacklist, null, null, null); + CertBlocklist blocklist) { + this(keyStore, manager, certStore, blocklist, null, null, null); } /** * For testing only. */ public TrustManagerImpl(KeyStore keyStore, CertPinManager manager, - ConscryptCertStore certStore, CertBlacklist blacklist, CTLogStore ctLogStore, - CTVerifier ctVerifier, CTPolicy ctPolicy) { + ConscryptCertStore certStore, CertBlocklist blocklist, CTLogStore ctLogStore, + CTVerifier ctVerifier, CTPolicy ctPolicy) { CertPathValidator validatorLocal = null; CertificateFactory factoryLocal = null; KeyStore rootKeyStoreLocal = null; @@ -205,8 +203,8 @@ errLocal = e; } - if (blacklist == null) { - blacklist = Platform.newDefaultBlacklist(); + if (blocklist == null) { + blocklist = Platform.newDefaultBlocklist(); } if (ctLogStore == null) { ctLogStore = Platform.newDefaultLogStore(); @@ -225,11 +223,12 @@ this.intermediateIndex = new TrustedCertificateIndex(); this.acceptedIssuers = acceptedIssuersLocal; this.err = errLocal; - this.blacklist = blacklist; + this.blocklist = blocklist; this.ctVerifier = new CTVerifier(ctLogStore); this.ctPolicy = ctPolicy; } + @SuppressWarnings("JdkObsolete") // KeyStore#aliases is the only API available private static X509Certificate[] acceptedIssuers(KeyStore ks) { try { // Note that unlike the PKIXParameters code to create a Set of @@ -404,7 +403,7 @@ String identificationAlgorithm = parameters.getEndpointIdentificationAlgorithm(); if ("HTTPS".equalsIgnoreCase(identificationAlgorithm)) { ConscryptHostnameVerifier verifier = getHttpsVerifier(); - if (!verifier.verify(hostname, session)) { + if (!verifier.verify(certs, hostname, session)) { throw new CertificateException("No subjectAltNames on the certificate match"); } } @@ -413,7 +412,7 @@ } @SuppressWarnings("unchecked") - private byte[] getOcspDataFromSession(SSLSession session) { + private static byte[] getOcspDataFromSession(SSLSession session) { List<byte[]> ocspResponses = null; if (session instanceof ConscryptSession) { ConscryptSession opensslSession = (ConscryptSession) session; @@ -427,10 +426,9 @@ if (rawResponses instanceof List) { ocspResponses = (List<byte[]>) rawResponses; } - } catch (NoSuchMethodException ignored) { - } catch (SecurityException ignored) { - } catch (IllegalAccessException ignored) { - } catch (IllegalArgumentException ignored) { + } catch (NoSuchMethodException | SecurityException + | IllegalAccessException | IllegalArgumentException ignored) { + // Method not available, fall through and return null } catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } @@ -457,10 +455,9 @@ if (rawData instanceof byte[]) { data = (byte[]) rawData; } - } catch (NoSuchMethodException ignored) { - } catch (SecurityException ignored) { - } catch (IllegalAccessException ignored) { - } catch (IllegalArgumentException ignored) { + } catch (NoSuchMethodException | SecurityException + | IllegalAccessException | IllegalArgumentException ignored) { + // Method not available, fall through and return null } catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } @@ -530,8 +527,8 @@ current = trustAnchorChain.get(trustAnchorChain.size() - 1).getTrustedCert(); } - // Check that the certificate isn't blacklisted. - checkBlacklist(current); + // Check that the certificate isn't blocklisted. + checkBlocklist(current); // 1. If the current certificate in the chain is self-signed verify the chain as is. if (current.getIssuerDN().equals(current.getSubjectDN())) { @@ -671,9 +668,9 @@ if (pinManager != null) { pinManager.checkChainPinning(host, wholeChain); } - // Check whole chain against the blacklist + // Check whole chain against the blocklist for (X509Certificate cert : wholeChain) { - checkBlacklist(cert); + checkBlocklist(cert); } // Check CT (if required). @@ -720,9 +717,9 @@ } } - private void checkBlacklist(X509Certificate cert) throws CertificateException { - if (blacklist != null && blacklist.isPublicKeyBlackListed(cert.getPublicKey())) { - throw new CertificateException("Certificate blacklisted by public key: " + cert); + private void checkBlocklist(X509Certificate cert) throws CertificateException { + if (blocklist != null && blocklist.isPublicKeyBlockListed(cert.getPublicKey())) { + throw new CertificateException("Certificate blocklisted by public key: " + cert); } } @@ -942,7 +939,7 @@ return trustAnchor; } if (trustedCertificateStore == null) { - // not trusted and no TrustedCertificateStore to check + // not trusted and no TrustedCertificateStore to check. return null; } // probe KeyStore for a cert. AndroidCAStore stores its @@ -1002,24 +999,11 @@ return hostnameVerifier; } - private enum GlobalHostnameVerifierAdapter implements ConscryptHostnameVerifier { - INSTANCE; - - @Override - public boolean verify(String hostname, SSLSession session) { - return HttpsURLConnection.getDefaultHostnameVerifier().verify(hostname, session); - } - } - private ConscryptHostnameVerifier getHttpsVerifier() { if (hostnameVerifier != null) { return hostnameVerifier; } - ConscryptHostnameVerifier defaultVerifier = getDefaultHostnameVerifier(); - if (defaultVerifier != null) { - return defaultVerifier; - } - return GlobalHostnameVerifierAdapter.INSTANCE; + return Platform.getDefaultHostnameVerifier(); } public void setCTEnabledOverride(boolean enabled) {
diff --git a/common/src/main/java/org/conscrypt/TrustedCertificateIndex.java b/common/src/main/java/org/conscrypt/TrustedCertificateIndex.java index 07d1e00..7e2e69a 100644 --- a/common/src/main/java/org/conscrypt/TrustedCertificateIndex.java +++ b/common/src/main/java/org/conscrypt/TrustedCertificateIndex.java
@@ -119,6 +119,7 @@ cert.verify(publicKey); return anchor; } catch (Exception ignored) { + // Ignored } } } @@ -195,6 +196,7 @@ cert.verify(publicKey); result.add(anchor); } catch (Exception ignored) { + // Ignored } } return result;
diff --git a/common/src/main/java/org/conscrypt/ct/CTVerifier.java b/common/src/main/java/org/conscrypt/ct/CTVerifier.java index e6136f2..2f1f79b 100644 --- a/common/src/main/java/org/conscrypt/ct/CTVerifier.java +++ b/common/src/main/java/org/conscrypt/ct/CTVerifier.java
@@ -163,7 +163,7 @@ * @param origin used to create the SignedCertificateTimestamp instances. */ @SuppressWarnings("MixedMutabilityReturnType") - private List<SignedCertificateTimestamp> getSCTsFromSCTList(byte[] data, + private static List<SignedCertificateTimestamp> getSCTsFromSCTList(byte[] data, SignedCertificateTimestamp.Origin origin) { if (data == null) { return Collections.emptyList();
diff --git a/common/src/main/java/org/conscrypt/ct/CertificateEntry.java b/common/src/main/java/org/conscrypt/ct/CertificateEntry.java index 48f9bcd..72ed530 100644 --- a/common/src/main/java/org/conscrypt/ct/CertificateEntry.java +++ b/common/src/main/java/org/conscrypt/ct/CertificateEntry.java
@@ -72,6 +72,8 @@ } /** + * Creates a CertificateEntry with type PRECERT_ENTRY + * * @throws IllegalArgumentException if issuerKeyHash isn't 32 bytes */ public static CertificateEntry createForPrecertificate(byte[] tbsCertificate, byte[] issuerKeyHash) { @@ -85,8 +87,7 @@ throw new CertificateException("Certificate does not contain embedded signed timestamps"); } - OpenSSLX509Certificate preCert = leaf.withDeletedExtension(CTConstants.X509_SCT_LIST_OID); - byte[] tbs = preCert.getTBSCertificate(); + byte[] tbs = leaf.getTBSCertificateWithoutExtension(CTConstants.X509_SCT_LIST_OID); byte[] issuerKey = issuer.getPublicKey().getEncoded(); MessageDigest md = MessageDigest.getInstance("SHA-256");
diff --git a/common/src/main/java/org/conscrypt/io/IoUtils.java b/common/src/main/java/org/conscrypt/io/IoUtils.java index aae3e3b..4f531fd 100644 --- a/common/src/main/java/org/conscrypt/io/IoUtils.java +++ b/common/src/main/java/org/conscrypt/io/IoUtils.java
@@ -35,6 +35,7 @@ } catch (RuntimeException rethrown) { throw rethrown; } catch (Exception ignored) { + // Ignored } } } @@ -47,6 +48,7 @@ try { socket.close(); } catch (Exception ignored) { + // Ignored } } }
diff --git a/common/src/main/java/org/conscrypt/metrics/CipherSuite.java b/common/src/main/java/org/conscrypt/metrics/CipherSuite.java new file mode 100644 index 0000000..ebdf6d2 --- /dev/null +++ b/common/src/main/java/org/conscrypt/metrics/CipherSuite.java
@@ -0,0 +1,81 @@ +/* + * 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.metrics; + +import org.conscrypt.Internal; + +/** + * Cipher suites to metric mapping for metrics instrumentation. + * + * Must be in sync with frameworks/base/cmds/statsd/src/atoms.proto + * + * Ids are based on IANA's database of SSL/TLS cipher suites + * @see https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4 + */ +@Internal +public enum CipherSuite { + UNKNOWN_CIPHER_SUITE(0x0000), + + // Supported but not enabled + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(0xC00A), + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(0xC014), + TLS_RSA_WITH_AES_256_CBC_SHA(0x0035), + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(0xC009), + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(0xC013), + TLS_RSA_WITH_AES_128_CBC_SHA(0x002F), + TLS_RSA_WITH_3DES_EDE_CBC_SHA(0x000A), + + // TLSv1.2 cipher suites + TLS_RSA_WITH_AES_128_GCM_SHA256(0x009C), + TLS_RSA_WITH_AES_256_GCM_SHA384(0x009D), + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F), + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030), + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(0xC02B), + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(0xC02C), + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(0xCCA9), + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256(0xCCA8), + + // Pre-Shared Key (PSK) cipher suites + TLS_PSK_WITH_AES_128_CBC_SHA(0x008C), + TLS_PSK_WITH_AES_256_CBC_SHA(0x008D), + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA(0xC035), + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA(0xC036), + TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256(0xCCAC), + + // TLS 1.3 cipher suites + TLS_AES_128_GCM_SHA256(0x1301), + TLS_AES_256_GCM_SHA384(0x1302), + TLS_CHACHA20_POLY1305_SHA256(0x1303), + ; + + final short id; + + public int getId() { + return this.id; + } + + public static CipherSuite forName(String name) { + try { + return CipherSuite.valueOf(name); + } catch (IllegalArgumentException e) { + return CipherSuite.UNKNOWN_CIPHER_SUITE; + } + } + + private CipherSuite(int id) { + this.id = (short) id; + } +} \ No newline at end of file
diff --git a/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java b/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java new file mode 100644 index 0000000..40b45a9 --- /dev/null +++ b/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java
@@ -0,0 +1,45 @@ +/* + * 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.metrics; + +import org.conscrypt.Internal; + +/** + * Reimplement with reflection calls the logging class, + * generated by frameworks/statsd. + * + * In case atom is changed, generate new wrapper with stats-log-api-gen + * tool as shown below and add corresponding methods to ReflexiveStatsEvent's + * newEvent() method. + * + * $ stats-log-api-gen \ + * --java "common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java" \ + * --module conscrypt \ + * --javaPackage org.conscrypt.metrics \ + * --javaClass ConscryptStatsLog + **/ +@Internal +public final class ConscryptStatsLog { + public static final int TLS_HANDSHAKE_REPORTED = 317; + + public static void write( + int code, boolean success, int protocol, int cipherSuite, int duration) { + ReflexiveStatsEvent event = + ReflexiveStatsEvent.buildEvent(code, success, protocol, cipherSuite, duration); + + ReflexiveStatsLog.write(event); + } +}
diff --git a/common/src/main/java/org/conscrypt/metrics/OptionalMethod.java b/common/src/main/java/org/conscrypt/metrics/OptionalMethod.java new file mode 100644 index 0000000..13c8366 --- /dev/null +++ b/common/src/main/java/org/conscrypt/metrics/OptionalMethod.java
@@ -0,0 +1,85 @@ +/* + * 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.metrics; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.conscrypt.Internal; + +/** + * Helper class to handle reflexive loading and invocation of methods which may be absent. + * + * @hide This class is not part of the Android public SDK API + */ +@Internal +public final class OptionalMethod { + private final Method cachedMethod; + + /** + * Instantiates a new OptionalMethod. + * <p>Does not throw any exceptions if the class or method can't be loaded, or if any parameter + * classes are {@code null} and instead behaves as a no-op, always returning {@code null}. + * + * @param clazz the Class to search for methods on + * @param methodName the name of the {@code Method} on {@code clazz} + * @param methodParams list of {@code Classes} of the {@code Method's} parameters + * + * @throws NullPointerException if the method name is {@code null} + */ + public OptionalMethod(Class<?> clazz, String methodName, Class<?>... methodParams) { + this.cachedMethod = initializeMethod(clazz, methodName, methodParams); + } + + private static Method initializeMethod( + Class<?> clazz, String methodName, Class<?>... methodParams) { + try { + for (Class<?> paramClass : methodParams) { + if (paramClass == null) { + return null; + } + } + if (clazz != null) { + return clazz.getMethod(checkNotNull(methodName), methodParams); + } + } catch (NoSuchMethodException ignored) { + // Ignored + } + return null; + } + + public Object invoke(Object target, Object... args) { + // no-op if failed to load method in constructor + if (cachedMethod == null) { + return null; + } + try { + return cachedMethod.invoke(target, args); + } catch (IllegalAccessException ignored) { + // Ignored + } catch (InvocationTargetException ignored) { + // Ignored + } + return null; + } + + private static <T> T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } + return reference; + } +}
diff --git a/common/src/main/java/org/conscrypt/metrics/Protocol.java b/common/src/main/java/org/conscrypt/metrics/Protocol.java new file mode 100644 index 0000000..0571260 --- /dev/null +++ b/common/src/main/java/org/conscrypt/metrics/Protocol.java
@@ -0,0 +1,61 @@ +/* + * 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.metrics; + +import org.conscrypt.Internal; + +/** + * Protocols to metric mapping for metrics instrumentation. + * + * Must be in sync with frameworks/base/cmds/statsd/src/atoms.proto + */ +@Internal +public enum Protocol { + UNKNOWN_PROTO(0), + SSLv3(1), + TLSv1(2), + TLSv1_1(3), + TLSv1_2(4), + TLSv1_3(5), + ; + + final byte id; + + public int getId() { + return this.id; + } + + public static Protocol forName(String name) { + switch (name) { + case "SSLv3": + return SSLv3; + case "TLSv1": + return TLSv1; + case "TLSv1.1": + return TLSv1_1; + case "TLSv1.2": + return TLSv1_2; + case "TLSv1.3": + return TLSv1_3; + default: + return UNKNOWN_PROTO; + } + } + + private Protocol(int id) { + this.id = (byte) id; + } +} \ No newline at end of file
diff --git a/common/src/main/java/org/conscrypt/metrics/ReflexiveStatsEvent.java b/common/src/main/java/org/conscrypt/metrics/ReflexiveStatsEvent.java new file mode 100644 index 0000000..f144867 --- /dev/null +++ b/common/src/main/java/org/conscrypt/metrics/ReflexiveStatsEvent.java
@@ -0,0 +1,122 @@ +/* + * 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.metrics; + +import org.conscrypt.Internal; + +/** + * Reflection wrapper around android.util.StatsEvent. + */ +@Internal +public class ReflexiveStatsEvent { + private static final OptionalMethod newBuilder; + private static final Class<?> c_statsEvent; + + static { + c_statsEvent = initStatsEventClass(); + newBuilder = new OptionalMethod(c_statsEvent, "newBuilder"); + } + + private static Class<?> initStatsEventClass() { + try { + return Class.forName("android.util.StatsEvent"); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + private Object statsEvent; + + private ReflexiveStatsEvent(Object statsEvent) { + this.statsEvent = statsEvent; + } + + public Object getStatsEvent() { + return statsEvent; + } + + public static ReflexiveStatsEvent.Builder newBuilder() { + return new ReflexiveStatsEvent.Builder(); + } + + public static ReflexiveStatsEvent buildEvent( + int atomId, boolean success, int protocol, int cipherSuite, int duration) { + ReflexiveStatsEvent.Builder builder = ReflexiveStatsEvent.newBuilder(); + builder.setAtomId(atomId); + builder.writeBoolean(success); + builder.writeInt(protocol); + builder.writeInt(cipherSuite); + builder.writeInt(duration); + builder.usePooledBuffer(); + return builder.build(); + } + + public static final class Builder { + private static final Class<?> c_statsEvent_Builder; + private static final OptionalMethod setAtomId; + private static final OptionalMethod writeBoolean; + private static final OptionalMethod writeInt; + private static final OptionalMethod build; + private static final OptionalMethod usePooledBuffer; + + static { + c_statsEvent_Builder = initStatsEventBuilderClass(); + setAtomId = new OptionalMethod(c_statsEvent_Builder, "setAtomId", int.class); + writeBoolean = new OptionalMethod(c_statsEvent_Builder, "writeBoolean", boolean.class); + writeInt = new OptionalMethod(c_statsEvent_Builder, "writeInt", int.class); + build = new OptionalMethod(c_statsEvent_Builder, "build"); + usePooledBuffer = new OptionalMethod(c_statsEvent_Builder, "usePooledBuffer"); + } + + private static Class<?> initStatsEventBuilderClass() { + try { + return Class.forName("android.util.StatsEvent$Builder"); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + private Object builder; + + private Builder() { + this.builder = newBuilder.invoke(null); + } + + public Builder setAtomId(final int atomId) { + setAtomId.invoke(this.builder, atomId); + return this; + } + + public Builder writeBoolean(final boolean value) { + writeBoolean.invoke(this.builder, value); + return this; + } + + public Builder writeInt(final int value) { + writeInt.invoke(this.builder, value); + return this; + } + + public void usePooledBuffer() { + usePooledBuffer.invoke(this.builder); + } + + public ReflexiveStatsEvent build() { + Object statsEvent = build.invoke(this.builder); + return new ReflexiveStatsEvent(statsEvent); + } + } +} \ No newline at end of file
diff --git a/common/src/main/java/org/conscrypt/metrics/ReflexiveStatsLog.java b/common/src/main/java/org/conscrypt/metrics/ReflexiveStatsLog.java new file mode 100644 index 0000000..aed898d --- /dev/null +++ b/common/src/main/java/org/conscrypt/metrics/ReflexiveStatsLog.java
@@ -0,0 +1,56 @@ +/* + * 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.metrics; + +import org.conscrypt.Internal; + +/** + * Reflection wrapper around android.util.StatsLog. + */ +@Internal +public class ReflexiveStatsLog { + private static final Class<?> c_statsLog; + private static final Class<?> c_statsEvent; + private static final OptionalMethod write; + + static { + c_statsLog = initStatsLogClass(); + c_statsEvent = initStatsEventClass(); + write = new OptionalMethod(c_statsLog, "write", c_statsEvent); + } + + private static Class<?> initStatsLogClass() { + try { + return Class.forName("android.util.StatsLog"); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + private static Class<?> initStatsEventClass() { + try { + return Class.forName("android.util.StatsEvent"); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + private ReflexiveStatsLog() {} + + public static void write(ReflexiveStatsEvent event) { + write.invoke(null, event.getStatsEvent()); + } +}
diff --git a/common/src/test/java/org/conscrypt/BufferUtilsTest.java b/common/src/test/java/org/conscrypt/BufferUtilsTest.java new file mode 100644 index 0000000..1cce07b --- /dev/null +++ b/common/src/test/java/org/conscrypt/BufferUtilsTest.java
@@ -0,0 +1,208 @@ +/* + * Copyright 2021 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.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +import java.nio.ByteBuffer; +import org.conscrypt.TestUtils.BufferType; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + + +@RunWith(Parameterized.class) +public class BufferUtilsTest { + private static final int K64 = 64 * 1024; + private static final int K16 = 16 * 1024; + + private static final int[][] TEST_SIZES = { + // All even numbers as several tests use size/2 + { 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 2 }, + { 2, 0, 0, 0 }, + { 100, 200, 300 }, + { 1000, 2000, 3000 }, + { K16 }, + { 0, 0, K16 }, + { K16, 0, 0 }, + { K64 }, + { 0, 0, K64 }, + { K64, 0, 0 }, + { 100, 100, K64 }, + { K64, 100, 100 }, + { K64, K64, K64 }, + }; + + + @Parameters(name = "{0}") + public static BufferType[] data() { + return new BufferType[] { BufferType.HEAP, BufferType.DIRECT }; + } + + @Parameter + public BufferType bufferType; + + @Test + public void checkNotNull() { + for (int[] sizes : TEST_SIZES) { + BufferUtils.checkNotNull(bufferType.newRandomBuffers(sizes)); + } + + ByteBuffer[] buffers = bufferType.newRandomBuffers(10, 10, 10, 10, 10); + buffers[2] = null; + try { + BufferUtils.checkNotNull(buffers); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + } + + @Test + public void remaining() { + for (int[] sizes : TEST_SIZES) { + assertEquals(arraySum(sizes), + BufferUtils.remaining(bufferType.newRandomBuffers(sizes))); + } + } + + @Test + public void consume() { + for (int[] sizes : TEST_SIZES) { + ByteBuffer[] buffers = bufferType.newRandomBuffers(sizes); + int totalSize = arraySum(sizes); + + BufferUtils.consume(buffers, 0); + assertEquals(totalSize, BufferUtils.remaining(buffers)); + + BufferUtils.consume(buffers,totalSize / 2); + assertEquals(totalSize / 2, BufferUtils.remaining(buffers)); + + BufferUtils.consume(buffers,totalSize / 2); + assertEquals(0, BufferUtils.remaining(buffers)); + + if (totalSize > 0) { + try { + BufferUtils.consume(buffers, totalSize / 2); + fail("Managed to consume past end of buffer array"); + } catch (IllegalArgumentException e) { + // Expected + } + } + } + } + + @Test + public void copyNoConsume() { + for (BufferType destinationType : BufferType.values()) { + for (int[] sizes : TEST_SIZES) { + ByteBuffer[] buffers = bufferType.newRandomBuffers(sizes); + int totalSize = arraySum(sizes); + + ByteBuffer destination = destinationType.newBuffer(totalSize); + BufferUtils.copyNoConsume(buffers, destination, totalSize); + assertEquals(totalSize, BufferUtils.remaining(buffers)); + + assertArrayEquals(toArray(buffers), toArray(destination)); + } + } + } + + private static byte[] toArray(ByteBuffer... buffers) { + byte[] bytes = new byte[(int) BufferUtils.remaining(buffers)]; + int offset = 0; + for (ByteBuffer buffer : buffers) { + int length = buffer.remaining(); + if (length > 0) { + buffer.get(bytes, offset, length); + offset += length; + } + } + return bytes; + } + + @Test + public void getBufferLargerThan_allSmall() { + ByteBuffer[] buffers = bufferType.newRandomBuffers(100, 200, 300, 400); + + assertNull(BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 300); + assertNull(BufferUtils.getBufferLargerThan(buffers, K16)); + assertSame(buffers[2], BufferUtils.getBufferLargerThan(buffers, 100)); + + BufferUtils.consume(buffers, 300); + assertSame(buffers[3], BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 200); + assertSame(buffers[3], BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 200); + ByteBuffer buffer = BufferUtils.getBufferLargerThan(buffers, K16); + assertNull(buffer); + } + + @Test + public void getBufferLargerThan_oneLarge() { + ByteBuffer[] buffers = bufferType.newRandomBuffers(100, K64, 300, 400); + + assertNull(BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 100); + assertSame(buffers[1], BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 1024); // 63K remaining in buffers[1] + assertSame(buffers[1], BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 60 * 1024); // 3K remaining in buffers[1] + assertNull(BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 3 * 1024); + assertEquals(0, buffers[1].remaining()); + assertNull(BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 300); + assertSame(buffers[3], BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 400); + ByteBuffer buffer = BufferUtils.getBufferLargerThan(buffers, K16); + assertNull(buffer); + } + + @Test + public void getBufferLargerThan_onlyOneBuffer() { + ByteBuffer[] buffers = bufferType.newRandomBuffers(0, 0, 100, 0, 0); + + assertSame(buffers[2], BufferUtils.getBufferLargerThan(buffers, K16)); + } + + private int arraySum(int[] sizes) { + int sum = 0; + for (int i : sizes) { + sum += i; + } + return sum; + } +}
diff --git a/common/src/test/java/org/conscrypt/ConscryptSuite.java b/common/src/test/java/org/conscrypt/ConscryptSuite.java index 8591a68..263afcd 100644 --- a/common/src/test/java/org/conscrypt/ConscryptSuite.java +++ b/common/src/test/java/org/conscrypt/ConscryptSuite.java
@@ -39,6 +39,7 @@ import org.conscrypt.java.security.KeyPairGeneratorTestDH; import org.conscrypt.java.security.KeyPairGeneratorTestDSA; import org.conscrypt.java.security.KeyPairGeneratorTestRSA; +import org.conscrypt.java.security.KeyPairGeneratorTestXDH; import org.conscrypt.java.security.MessageDigestTest; import org.conscrypt.java.security.SignatureTest; import org.conscrypt.java.security.cert.CertificateFactoryTest; @@ -49,6 +50,7 @@ import org.conscrypt.javax.crypto.CipherTest; import org.conscrypt.javax.crypto.ECDHKeyAgreementTest; import org.conscrypt.javax.crypto.KeyGeneratorTest; +import org.conscrypt.javax.crypto.XDHKeyAgreementTest; import org.conscrypt.javax.net.ssl.HttpsURLConnectionTest; import org.conscrypt.javax.net.ssl.KeyManagerFactoryTest; import org.conscrypt.javax.net.ssl.KeyStoreBuilderParametersTest; @@ -66,6 +68,9 @@ import org.conscrypt.javax.net.ssl.SSLSocketVersionCompatibilityTest; import org.conscrypt.javax.net.ssl.TrustManagerFactoryTest; import org.conscrypt.javax.net.ssl.X509KeyManagerTest; +import org.conscrypt.metrics.CipherSuiteTest; +import org.conscrypt.metrics.OptionalMethodTest; +import org.conscrypt.metrics.ProtocolTest; import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -76,6 +81,7 @@ CertPinManagerTest.class, ChainStrengthAnalyzerTest.class, TrustManagerImplTest.class, + HostnameVerifierTest.class, // org.conscrypt.ct tests CTVerifierTest.class, SerializationTest.class, @@ -94,6 +100,8 @@ AlgorithmParametersTestEC.class, AlgorithmParametersTestGCM.class, AlgorithmParametersTestOAEP.class, + BufferUtilsTest.class, + CipherSuiteTest.class, KeyFactoryTestDH.class, KeyFactoryTestDSA.class, KeyFactoryTestEC.class, @@ -102,18 +110,23 @@ KeyPairGeneratorTestDH.class, KeyPairGeneratorTestDSA.class, KeyPairGeneratorTestRSA.class, + KeyPairGeneratorTestXDH.class, MessageDigestTest.class, SignatureTest.class, // javax.crypto tests AeadCipherTest.class, CipherBasicsTest.class, CipherTest.class, + MacTest.class, ECDHKeyAgreementTest.class, KeyGeneratorTest.class, + XDHKeyAgreementTest.class, // javax.net.ssl tests HttpsURLConnectionTest.class, KeyManagerFactoryTest.class, KeyStoreBuilderParametersTest.class, + OptionalMethodTest.class, + ProtocolTest.class, SNIHostNameTest.class, SSLContextTest.class, SSLEngineTest.class,
diff --git a/common/src/test/java/org/conscrypt/HostnameVerifierTest.java b/common/src/test/java/org/conscrypt/HostnameVerifierTest.java new file mode 100644 index 0000000..253ca34 --- /dev/null +++ b/common/src/test/java/org/conscrypt/HostnameVerifierTest.java
@@ -0,0 +1,655 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You 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.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.nio.charset.Charset; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collection; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.security.auth.x500.X500Principal; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +/** + * Tests for our hostname verifier. Most of these tests are from AOSP, which + * itself includes tests from the Apache HTTP Client test suite. + */ +@RunWith(Parameterized.class) +public final class HostnameVerifierTest { + public static final class FakeSSLSession extends org.conscrypt.javax.net.ssl.FakeSSLSession { + + private final Certificate[] certificates; + + public FakeSSLSession(Certificate... certificates) throws Exception { + super("FakeHost"); + this.certificates = certificates; + } + + @Override + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { + if (certificates.length == 0) { + throw new SSLPeerUnverifiedException("peer not authenticated"); + } + return certificates; + } + } + + private static final Charset UTF_8 = Charset.forName("UTF-8"); + // BEGIN Android-changed: Run tests for both default and strict verifiers. http://b/144694112 + // private HostnameVerifier verifier = OkHostnameVerifier.INSTANCE; + @Parameters() + public static Collection<Object[]> data() { + // Both verifiers should behave the same in all tests except for + // subjectAltNameWithToplevelWildcard(), and that test is not parameterized for clarity. + return Arrays.asList(new Object[][] { + { OkHostnameVerifier.INSTANCE }, + { OkHostnameVerifier.strictInstance() } + }); + } + + @Parameter + public OkHostnameVerifier verifier; + // END Android-changed: Run tests for both default and strict verifiers. http://b/144694112 + + @Test public void verify() throws Exception { + FakeSSLSession session = new FakeSSLSession(); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs,"localhost", session)); + } + + @Test public void verifyCn() throws Exception { + // CN=foo.com + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIERjCCAy6gAwIBAgIJAIz+EYMBU6aQMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzE0MVoXDTI4MTEwNTE1MzE0MVowgaQx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n" + + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n" + + "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n" + + "FJ8Ud78/OrbKOIJCSBYs2tDLXofYMB8GA1UdIwQYMBaAFHua2o+QmU5S0qzbswNS\n" + + "yoemDT4NMA0GCSqGSIb3DQEBBQUAA4IBAQC3jRmEya6sQCkmieULcvx8zz1euCk9\n" + + "fSez7BEtki8+dmfMXe3K7sH0lI8f4jJR0rbSCjpmCQLYmzC3NxBKeJOW0RcjNBpO\n" + + "c2JlGO9auXv2GDP4IYiXElLJ6VSqc8WvDikv0JmCCWm0Zga+bZbR/EWN5DeEtFdF\n" + + "815CLpJZNcYwiYwGy/CVQ7w2TnXlG+mraZOz+owr+cL6J/ZesbdEWfjoS1+cUEhE\n" + + "HwlNrAu8jlZ2UqSgskSWlhYdMTAP9CPHiUv9N7FcT58Itv/I4fKREINQYjDpvQcx\n" + + "SaTYb9dr5sB4WLNglk7zxDtM80H518VvihTcP7FHL+Gn6g4j5fkI98+S\n" + + "-----END CERTIFICATE-----\n"); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("foo.com", session)); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "foo.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + assertFalse(verifier.verify(certs, "bar.com", session)); + } + + @Test public void verifyNonAsciiCn() throws Exception { + // CN=花子.co.jp + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIESzCCAzOgAwIBAgIJAIz+EYMBU6aTMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1NDIxNVoXDTI4MTEwNTE1NDIxNVowgakx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n" + + "cnRpZmljYXRlczEVMBMGA1UEAwwM6Iqx5a2QLmNvLmpwMSUwIwYJKoZIhvcNAQkB\n" + + "FhZqdWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" + + "MIIBCgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjU\n" + + "g4pNjYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQc\n" + + "wHf0ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t\n" + + "7iu1JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAn\n" + + "AxK6q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArD\n" + + "qUYxqJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG\n" + + "CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV\n" + + "HQ4EFgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLS\n" + + "rNuzA1LKh6YNPg0wDQYJKoZIhvcNAQEFBQADggEBALJ27i3okV/KvlDp6KMID3gd\n" + + "ITl68PyItzzx+SquF8gahMh016NX73z/oVZoVUNdftla8wPUB1GwIkAnGkhQ9LHK\n" + + "spBdbRiCj0gMmLCsX8SrjFvr7cYb2cK6J/fJe92l1tg/7Y4o7V/s4JBe/cy9U9w8\n" + + "a0ctuDmEBCgC784JMDtT67klRfr/2LlqWhlOEq7pUFxRLbhpquaAHSOjmIcWnVpw\n" + + "9BsO7qe46hidgn39hKh1WjKK2VcL/3YRsC4wUi0PBtFW6ScMCuMhgIRXSPU55Rae\n" + + "UIlOdPjjr1SUNWGId1rD7W16Scpwnknn310FNxFMHVI0GTGFkNdkilNCFJcIoRA=\n" + + "-----END CERTIFICATE-----\n"); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session)); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify(certs, "a.\u82b1\u5b50.co.jp", session)); + } + + @Test public void verifySubjectAlt() throws Exception { + // CN=foo.com, subjectAlt=bar.com + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIEXDCCA0SgAwIBAgIJAIz+EYMBU6aRMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzYyOVoXDTI4MTEwNTE1MzYyOVowgaQx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n" + + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaOBkDCBjTAJBgNVHRMEAjAAMCwGCWCG\n" + + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n" + + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n" + + "A1LKh6YNPg0wEgYDVR0RBAswCYIHYmFyLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEA\n" + + "dQyprNZBmVnvuVWjV42sey/PTfkYShJwy1j0/jcFZR/ypZUovpiHGDO1DgL3Y3IP\n" + + "zVQ26uhUsSw6G0gGRiaBDe/0LUclXZoJzXX1qpS55OadxW73brziS0sxRgGrZE/d\n" + + "3g5kkio6IED47OP6wYnlmZ7EKP9cqjWwlnvHnnUcZ2SscoLNYs9rN9ccp8tuq2by\n" + + "88OyhKwGjJfhOudqfTNZcDzRHx4Fzm7UsVaycVw4uDmhEHJrAsmMPpj/+XRK9/42\n" + + "2xq+8bc6HojdtbCyug/fvBZvZqQXSmU8m8IVcMmWMz0ZQO8ee3QkBHMZfCy7P/kr\n" + + "VbWx/uETImUu+NZg22ewEw==\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "foo.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + assertTrue(verifier.verify(certs, "bar.com", session)); + assertFalse(verifier.verify(certs, "a.bar.com", session)); + } + + /** + * Ignored due to incompatibilities between Android and Java on how non-ASCII + * subject alt names are parsed. Android fails to parse these, which means we + * fall back to the CN. The RI does parse them, so the CN is unused. + */ + @Test @Ignore public void verifyNonAsciiSubjectAlt() throws Exception { + // CN=foo.com, subjectAlt=bar.com, subjectAlt=花子.co.jp + // (hanako.co.jp in kanji) + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIEajCCA1KgAwIBAgIJAIz+EYMBU6aSMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzgxM1oXDTI4MTEwNTE1MzgxM1owgaQx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n" + + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaOBnjCBmzAJBgNVHRMEAjAAMCwGCWCG\n" + + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n" + + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n" + + "A1LKh6YNPg0wIAYDVR0RBBkwF4IHYmFyLmNvbYIM6Iqx5a2QLmNvLmpwMA0GCSqG\n" + + "SIb3DQEBBQUAA4IBAQBeZs7ZIYyKtdnVxVvdLgwySEPOE4pBSXii7XYv0Q9QUvG/\n" + + "++gFGQh89HhABzA1mVUjH5dJTQqSLFvRfqTHqLpxSxSWqMHnvRM4cPBkIRp/XlMK\n" + + "PlXadYtJLPTgpbgvulA1ickC9EwlNYWnowZ4uxnfsMghW4HskBqaV+PnQ8Zvy3L0\n" + + "12c7Cg4mKKS5pb1HdRuiD2opZ+Hc77gRQLvtWNS8jQvd/iTbh6fuvTKfAOFoXw22\n" + + "sWIKHYrmhCIRshUNohGXv50m2o+1w9oWmQ6Dkq7lCjfXfUB4wIbggJjpyEtbNqBt\n" + + "j4MC2x5rfsLKKqToKmNE7pFEgqwe8//Aar1b+Qj+\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + assertTrue(verifier.verify(certs, "foo.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + // these checks test alternative subjects. The test data contains an + // alternative subject starting with a japanese kanji character. This is + // not supported by Android because the underlying implementation from + // harmony follows the definition from rfc 1034 page 10 for alternative + // subject names. This causes the code to drop all alternative subjects. + // assertTrue(verifier.verify("bar.com", session)); + // assertFalse(verifier.verify("a.bar.com", session)); + // assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session)); + } + + @Test public void verifySubjectAltOnly() throws Exception { + // subjectAlt=foo.com + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIESjCCAzKgAwIBAgIJAIz+EYMBU6aYMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MjYxMFoXDTI4MTEwNTE2MjYxMFowgZIx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n" + + "cnRpZmljYXRlczElMCMGCSqGSIb3DQEJARYWanVsaXVzZGF2aWVzQGdtYWlsLmNv\n" + + "bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMhjr5aCPoyp0R1iroWA\n" + + "fnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2BlYho4O84X244QrZTRl8kQbYt\n" + + "xnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRyzerA/ZtrlUqf+lKo0uWcocxe\n" + + "Rc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY07hNKXAb2odnVqgzcYiDkLV8\n" + + "ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8BqnGd87xQU3FVZI4tbtkB+Kz\n" + + "jD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiVJTxpTKqym93whYk93l3ocEe5\n" + + "5c0CAwEAAaOBkDCBjTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM\n" + + "IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUnxR3vz86tso4gkJIFiza\n" + + "0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuzA1LKh6YNPg0wEgYDVR0RBAsw\n" + + "CYIHZm9vLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAjl78oMjzFdsMy6F1sGg/IkO8\n" + + "tF5yUgPgFYrs41yzAca7IQu6G9qtFDJz/7ehh/9HoG+oqCCIHPuIOmS7Sd0wnkyJ\n" + + "Y7Y04jVXIb3a6f6AgBkEFP1nOT0z6kjT7vkA5LJ2y3MiDcXuRNMSta5PYVnrX8aZ\n" + + "yiqVUNi40peuZ2R8mAUSBvWgD7z2qWhF8YgDb7wWaFjg53I36vWKn90ZEti3wNCw\n" + + "qAVqixM+J0qJmQStgAc53i2aTMvAQu3A3snvH/PHTBo+5UL72n9S1kZyNCsVf1Qo\n" + + "n8jKTiRriEM+fMFlcgQP284EBFzYHyCXFb9O/hMjK2+6mY9euMB1U1aFFzM/Bg==\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + assertTrue(verifier.verify(certs, "foo.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + assertTrue(verifier.verify(certs, "foo.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + } + + @Test public void verifyMultipleCn() throws Exception { + // CN=foo.com, CN=bar.com, CN=花子.co.jp + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIEbzCCA1egAwIBAgIJAIz+EYMBU6aXMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTk0NVoXDTI4MTEwNTE2MTk0NVowgc0x\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n" + + "cnRpZmljYXRlczEQMA4GA1UEAwwHZm9vLmNvbTEQMA4GA1UEAwwHYmFyLmNvbTEV\n" + + "MBMGA1UEAwwM6Iqx5a2QLmNvLmpwMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyGOv\n" + + "loI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pNjYGViGjg7zhf\n" + + "bjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0ZHLN6sD9m2uV\n" + + "Sp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1JVjTuE0pcBva\n" + + "h2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6q/wGqcZ3zvFB\n" + + "TcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYxqJUlPGlMqrKb\n" + + "3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQf\n" + + "Fh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUnxR3vz86\n" + + "tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuzA1LKh6YNPg0w\n" + + "DQYJKoZIhvcNAQEFBQADggEBAGuZb8ai1NO2j4v3y9TLZvd5s0vh5/TE7n7RX+8U\n" + + "y37OL5k7x9nt0mM1TyAKxlCcY+9h6frue8MemZIILSIvMrtzccqNz0V1WKgA+Orf\n" + + "uUrabmn+CxHF5gpy6g1Qs2IjVYWA5f7FROn/J+Ad8gJYc1azOWCLQqSyfpNRLSvY\n" + + "EriQFEV63XvkJ8JrG62b+2OT2lqT4OO07gSPetppdlSa8NBSKP6Aro9RIX1ZjUZQ\n" + + "SpQFCfo02NO0uNRDPUdJx2huycdNb+AXHaO7eXevDLJ+QnqImIzxWiY6zLOdzjjI\n" + + "VBMkLHmnP7SjGSQ3XA4ByrQOxfOUTyLyE7NuemhHppuQPxE=\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "foo.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + assertFalse(verifier.verify(certs, "bar.com", session)); + assertFalse(verifier.verify(certs, "a.bar.com", session)); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify(certs, "\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify(certs, "a.\u82b1\u5b50.co.jp", session)); + } + + @Test public void verifyWilcardCn() throws Exception { + // CN=*.foo.com + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIESDCCAzCgAwIBAgIJAIz+EYMBU6aUMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTU1NVoXDTI4MTEwNTE2MTU1NVowgaYx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + + "cnRpZmljYXRlczESMBAGA1UEAxQJKi5mb28uY29tMSUwIwYJKoZIhvcNAQkBFhZq\n" + + "dWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" + + "CgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pN\n" + + "jYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0\n" + + "ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1\n" + + "JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6\n" + + "q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYx\n" + + "qJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCG\n" + + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n" + + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n" + + "A1LKh6YNPg0wDQYJKoZIhvcNAQEFBQADggEBAH0ipG6J561UKUfgkeW7GvYwW98B\n" + + "N1ZooWX+JEEZK7+Pf/96d3Ij0rw9ACfN4bpfnCq0VUNZVSYB+GthQ2zYuz7tf/UY\n" + + "A6nxVgR/IjG69BmsBl92uFO7JTNtHztuiPqBn59pt+vNx4yPvno7zmxsfI7jv0ww\n" + + "yfs+0FNm7FwdsC1k47GBSOaGw38kuIVWqXSAbL4EX9GkryGGOKGNh0qvAENCdRSB\n" + + "G9Z6tyMbmfRY+dLSh3a9JwoEcBUso6EWYBakLbq4nG/nvYdYvG9ehrnLVwZFL82e\n" + + "l3Q/RK95bnA6cuRClGusLad0e6bjkBzx/VQ3VarDEpAkTLUGVAa0CLXtnyc=\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "foo.com", session)); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("www.foo.com", session)); + assertFalse(verifier.verify(certs, "www.foo.com", session)); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("\u82b1\u5b50.foo.com", session)); + assertFalse(verifier.verify(certs, "\u82b1\u5b50.foo.com", session)); + assertFalse(verifier.verify(certs, "a.b.foo.com", session)); + } + + @Test public void verifyWilcardCnOnTld() throws Exception { + // It's the CA's responsibility to not issue broad-matching certificates! + // CN=*.co.jp + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIERjCCAy6gAwIBAgIJAIz+EYMBU6aVMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTYzMFoXDTI4MTEwNTE2MTYzMFowgaQx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + + "cnRpZmljYXRlczEQMA4GA1UEAxQHKi5jby5qcDElMCMGCSqGSIb3DQEJARYWanVs\n" + + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n" + + "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n" + + "FJ8Ud78/OrbKOIJCSBYs2tDLXofYMB8GA1UdIwQYMBaAFHua2o+QmU5S0qzbswNS\n" + + "yoemDT4NMA0GCSqGSIb3DQEBBQUAA4IBAQA0sWglVlMx2zNGvUqFC73XtREwii53\n" + + "CfMM6mtf2+f3k/d8KXhLNySrg8RRlN11zgmpPaLtbdTLrmG4UdAHHYr8O4y2BBmE\n" + + "1cxNfGxxechgF8HX10QV4dkyzp6Z1cfwvCeMrT5G/V1pejago0ayXx+GPLbWlNeZ\n" + + "S+Kl0m3p+QplXujtwG5fYcIpaGpiYraBLx3Tadih39QN65CnAh/zRDhLCUzKyt9l\n" + + "UGPLEUDzRHMPHLnSqT1n5UU5UDRytbjJPXzF+l/+WZIsanefWLsxnkgAuZe/oMMF\n" + + "EJMryEzOjg4Tfuc5qM0EXoPcQ/JlheaxZ40p2IyHqbsWV4MRYuFH4bkM\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("foo.co.jp", session)); + assertFalse(verifier.verify(certs, "foo.co.jp", session)); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify(certs, "\u82b1\u5b50.co.jp", session)); + } + + /** + * Ignored due to incompatibilities between Android and Java on how non-ASCII + * subject alt names are parsed. Android fails to parse these, which means we + * fall back to the CN. The RI does parse them, so the CN is unused. + */ + @Test @Ignore public void testWilcardNonAsciiSubjectAlt() throws Exception { + // CN=*.foo.com, subjectAlt=*.bar.com, subjectAlt=*.花子.co.jp + // (*.hanako.co.jp in kanji) + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIEcDCCA1igAwIBAgIJAIz+EYMBU6aWMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTczMVoXDTI4MTEwNTE2MTczMVowgaYx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + + "cnRpZmljYXRlczESMBAGA1UEAxQJKi5mb28uY29tMSUwIwYJKoZIhvcNAQkBFhZq\n" + + "dWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" + + "CgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pN\n" + + "jYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0\n" + + "ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1\n" + + "JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6\n" + + "q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYx\n" + + "qJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo4GiMIGfMAkGA1UdEwQCMAAwLAYJ\n" + + "YIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1Ud\n" + + "DgQWBBSfFHe/Pzq2yjiCQkgWLNrQy16H2DAfBgNVHSMEGDAWgBR7mtqPkJlOUtKs\n" + + "27MDUsqHpg0+DTAkBgNVHREEHTAbggkqLmJhci5jb22CDiou6Iqx5a2QLmNvLmpw\n" + + "MA0GCSqGSIb3DQEBBQUAA4IBAQBobWC+D5/lx6YhX64CwZ26XLjxaE0S415ajbBq\n" + + "DK7lz+Rg7zOE3GsTAMi+ldUYnhyz0wDiXB8UwKXl0SDToB2Z4GOgqQjAqoMmrP0u\n" + + "WB6Y6dpkfd1qDRUzI120zPYgSdsXjHW9q2H77iV238hqIU7qCvEz+lfqqWEY504z\n" + + "hYNlknbUnR525ItosEVwXFBJTkZ3Yw8gg02c19yi8TAh5Li3Ad8XQmmSJMWBV4XK\n" + + "qFr0AIZKBlg6NZZFf/0dP9zcKhzSriW27bY0XfzA6GSiRDXrDjgXq6baRT6YwgIg\n" + + "pgJsDbJtZfHnV1nd3M6zOtQPm1TIQpNmMMMd/DPrGcUQerD3\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + // try the foo.com variations + assertTrue(verifier.verify(certs, "foo.com", session)); + assertTrue(verifier.verify(certs, "www.foo.com", session)); + assertTrue(verifier.verify(certs, "\u82b1\u5b50.foo.com", session)); + assertFalse(verifier.verify(certs, "a.b.foo.com", session)); + // these checks test alternative subjects. The test data contains an + // alternative subject starting with a japanese kanji character. This is + // not supported by Android because the underlying implementation from + // harmony follows the definition from rfc 1034 page 10 for alternative + // subject names. This causes the code to drop all alternative subjects. + // assertFalse(verifier.verify("bar.com", session)); + // assertTrue(verifier.verify("www.bar.com", session)); + // assertTrue(verifier.verify("\u82b1\u5b50.bar.com", session)); + // assertTrue(verifier.verify("a.b.bar.com", session)); + } + + @Test public void subjectAltUsesLocalDomainAndIp() throws Exception { + // cat cert.cnf + // [req] + // distinguished_name=distinguished_name + // req_extensions=req_extensions + // x509_extensions=x509_extensions + // [distinguished_name] + // [req_extensions] + // [x509_extensions] + // subjectAltName=DNS:localhost.localdomain,DNS:localhost,IP:127.0.0.1 + // + // $ openssl req -x509 -nodes -days 36500 -subj '/CN=localhost' -config ./cert.cnf \ + // -newkey rsa:512 -out cert.pem + X509Certificate certificate = certificate("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIBWDCCAQKgAwIBAgIJANS1EtICX2AZMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV\n" + + "BAMTCWxvY2FsaG9zdDAgFw0xMjAxMDIxOTA4NThaGA8yMTExMTIwOTE5MDg1OFow\n" + + "FDESMBAGA1UEAxMJbG9jYWxob3N0MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPpt\n" + + "atK8r4/hf4hSIs0os/BSlQLbRBaK9AfBReM4QdAklcQqe6CHsStKfI8pp0zs7Ptg\n" + + "PmMdpbttL0O7mUboBC8CAwEAAaM1MDMwMQYDVR0RBCowKIIVbG9jYWxob3N0Lmxv\n" + + "Y2FsZG9tYWlugglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQEFBQADQQD0ntfL\n" + + "DCzOCv9Ma6Lv5o5jcYWVxvBSTsnt22hsJpWD1K7iY9lbkLwl0ivn73pG2evsAn9G\n" + + "X8YKH52fnHsCrhSD\n" + + "-----END CERTIFICATE-----"); + + assertEquals(new X500Principal("CN=localhost"), certificate.getSubjectX500Principal()); + FakeSSLSession session = new FakeSSLSession(certificate); + + X509Certificate[] certs = {}; + + assertTrue(verifier.verify(certs, "localhost", session)); + assertTrue(verifier.verify(certs, "localhost.localdomain", session)); + assertFalse(verifier.verify(certs, "local.host", session)); + + assertTrue(verifier.verify(certs, "127.0.0.1", session)); + assertFalse(verifier.verify(certs, "127.0.0.2", session)); + } + + @Test public void wildcardsCannotMatchIpAddresses() throws Exception { + // openssl req -x509 -nodes -days 36500 -subj '/CN=*.0.0.1' -newkey rsa:512 -out cert.pem + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIBkjCCATygAwIBAgIJAMdemqOwd/BEMA0GCSqGSIb3DQEBBQUAMBIxEDAOBgNV\n" + + "BAMUByouMC4wLjEwIBcNMTAxMjIwMTY0NDI1WhgPMjExMDExMjYxNjQ0MjVaMBIx\n" + + "EDAOBgNVBAMUByouMC4wLjEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAqY8c9Qrt\n" + + "YPWCvb7lclI+aDHM6fgbJcHsS9Zg8nUOh5dWrS7AgeA25wyaokFl4plBbbHQe2j+\n" + + "cCjsRiJIcQo9HwIDAQABo3MwcTAdBgNVHQ4EFgQUJ436TZPJvwCBKklZZqIvt1Yt\n" + + "JjEwQgYDVR0jBDswOYAUJ436TZPJvwCBKklZZqIvt1YtJjGhFqQUMBIxEDAOBgNV\n" + + "BAMUByouMC4wLjGCCQDHXpqjsHfwRDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB\n" + + "BQUAA0EAk9i88xdjWoewqvE+iMC9tD2obMchgFDaHH0ogxxiRaIKeEly3g0uGxIt\n" + + "fl2WRY8hb4x+zRrwsFaLEpdEvqcjOQ==\n" + + "-----END CERTIFICATE-----"); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "127.0.0.1", session)); + } + + /** + * Earlier implementations of Android's hostname verifier required that + * wildcard names wouldn't match "*.com" or similar. This was a nonstandard + * check that we've since dropped. It is the CA's responsibility to not hand + * out certificates that match so broadly. + */ + @Test public void wildcardsDoesNotNeedTwoDots() throws Exception { + // openssl req -x509 -nodes -days 36500 -subj '/CN=*.com' -newkey rsa:512 -out cert.pem + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIBjDCCATagAwIBAgIJAOVulXCSu6HuMA0GCSqGSIb3DQEBBQUAMBAxDjAMBgNV\n" + + "BAMUBSouY29tMCAXDTEwMTIyMDE2NDkzOFoYDzIxMTAxMTI2MTY0OTM4WjAQMQ4w\n" + + "DAYDVQQDFAUqLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDJd8xqni+h7Iaz\n" + + "ypItivs9kPuiJUqVz+SuJ1C05SFc3PmlRCvwSIfhyD67fHcbMdl+A/LrIjhhKZJe\n" + + "1joO0+pFAgMBAAGjcTBvMB0GA1UdDgQWBBS4Iuzf5w8JdCp+EtBfdFNudf6+YzBA\n" + + "BgNVHSMEOTA3gBS4Iuzf5w8JdCp+EtBfdFNudf6+Y6EUpBIwEDEOMAwGA1UEAxQF\n" + + "Ki5jb22CCQDlbpVwkruh7jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA0EA\n" + + "U6LFxmZr31lFyis2/T68PpjAppc0DpNQuA2m/Y7oTHBDi55Fw6HVHCw3lucuWZ5d\n" + + "qUYo4ES548JdpQtcLrW2sA==\n" + + "-----END CERTIFICATE-----"); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("google.com", session)); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "google.com", session)); + } + + @Test public void subjectAltName() throws Exception { + // $ cat ./cert.cnf + // [req] + // distinguished_name=distinguished_name + // req_extensions=req_extensions + // x509_extensions=x509_extensions + // [distinguished_name] + // [req_extensions] + // [x509_extensions] + // subjectAltName=DNS:bar.com,DNS:baz.com + // + // $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \ + // -newkey rsa:512 -out cert.pem + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIBPTCB6KADAgECAgkA7zoHaaqNGHQwDQYJKoZIhvcNAQEFBQAwEjEQMA4GA1UE\n" + + "AxMHZm9vLmNvbTAgFw0xMDEyMjAxODM5MzZaGA8yMTEwMTEyNjE4MzkzNlowEjEQ\n" + + "MA4GA1UEAxMHZm9vLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC+gmoSxF+8\n" + + "hbV+rgRQqHIJd50216OWQJbU3BvdlPbca779NYO4+UZWTFdBM8BdQqs3H4B5Agvp\n" + + "y7HeSff1F7XRAgMBAAGjHzAdMBsGA1UdEQQUMBKCB2Jhci5jb22CB2Jhei5jb20w\n" + + "DQYJKoZIhvcNAQEFBQADQQBXpZZPOY2Dy1lGG81JTr8L4or9jpKacD7n51eS8iqI\n" + + "oTznPNuXHU5bFN0AAGX2ij47f/EahqTpo5RdS95P4sVm\n" + + "-----END CERTIFICATE-----"); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "foo.com", session)); + assertTrue(verifier.verify(certs, "bar.com", session)); + assertTrue(verifier.verify(certs, "baz.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + assertFalse(verifier.verify(certs, "quux.com", session)); + } + + @Test public void subjectAltNameWithWildcard() throws Exception { + // $ cat ./cert.cnf + // [req] + // distinguished_name=distinguished_name + // req_extensions=req_extensions + // x509_extensions=x509_extensions + // [distinguished_name] + // [req_extensions] + // [x509_extensions] + // subjectAltName=DNS:bar.com,DNS:*.baz.com + // + // $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \ + // -newkey rsa:512 -out cert.pem + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIBPzCB6qADAgECAgkAnv/7Jv5r7pMwDQYJKoZIhvcNAQEFBQAwEjEQMA4GA1UE\n" + + "AxMHZm9vLmNvbTAgFw0xMDEyMjAxODQ2MDFaGA8yMTEwMTEyNjE4NDYwMVowEjEQ\n" + + "MA4GA1UEAxMHZm9vLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDAz2YXnyog\n" + + "YdYLSFr/OEgSumtwqtZKJTB4wqTW/eKbBCEzxnyUMxWZIqUGu353PzwfOuWp2re3\n" + + "nvVV+QDYQlh9AgMBAAGjITAfMB0GA1UdEQQWMBSCB2Jhci5jb22CCSouYmF6LmNv\n" + + "bTANBgkqhkiG9w0BAQUFAANBAB8yrSl8zqy07i0SNYx2B/FnvQY734pxioaqFWfO\n" + + "Bqo1ZZl/9aPHEWIwBrxYNVB0SGu/kkbt/vxqOjzzrkXukmI=\n" + + "-----END CERTIFICATE-----"); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "foo.com", session)); + assertTrue(verifier.verify(certs, "bar.com", session)); + assertTrue(verifier.verify(certs, "a.baz.com", session)); + assertFalse(verifier.verify(certs, "baz.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + assertFalse(verifier.verify(certs, "a.bar.com", session)); + assertFalse(verifier.verify(certs, "quux.com", session)); + } + + // BEGIN Android-added: Verify behaviour with top level wildcard SAN. http://b/144694112 + @Test + public void subjectAltNameWithToplevelWildcard() throws Exception { + // Default OkHostnameVerifier instance should allow SANs which + // have wildcards for top-level domains. The strict instance should not. + // + // Certificate generated using:- + // openssl req -x509 -nodes -days 36500 -subj "/CN=Google Inc" \ + // -addext "subjectAltName=DNS:*.com" -newkey rsa:512 + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIBlTCCAT+gAwIBAgIUe1RB6C61ZW/SEQpKiywSEJOEOUMwDQYJKoZIhvcNAQEL\n" + + "BQAwFTETMBEGA1UEAwwKR29vZ2xlIEluYzAgFw0xOTExMjExMjE1NTBaGA8yMTE5\n" + + "MTAyODEyMTU1MFowFTETMBEGA1UEAwwKR29vZ2xlIEluYzBcMA0GCSqGSIb3DQEB\n" + + "AQUAA0sAMEgCQQCu24jT8hktpvnmcde4dqC6e7G5F4cNNLUFnTi3Ay9BzPH1r7sN\n" + + "v2lHTIQLKSlvjxa48mpeRBlOjDQigv7c+rfRAgMBAAGjZTBjMB0GA1UdDgQWBBQd\n" + + "myvYKfluxb0+kNEJoh1ZER2wUTAfBgNVHSMEGDAWgBQdmyvYKfluxb0+kNEJoh1Z\n" + + "ER2wUTAPBgNVHRMBAf8EBTADAQH/MBAGA1UdEQQJMAeCBSouY29tMA0GCSqGSIb3\n" + + "DQEBCwUAA0EAK710g2hQpXSmpbOQH4dHG61fkVDtM/kR/4/R61vDDqVkgOuyHqXl\n" + + "GUZFKHMeOZ8peQLT8b+5ik6pIO7Vu2pF6w==\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + assertTrue(OkHostnameVerifier.INSTANCE.verify(certs, "google.com", session)); + assertFalse(OkHostnameVerifier.strictInstance().verify(certs, "google.com", session)); + } + // END Android-added: Verify behaviour with top level wildcard SAN. http://b/144694112 + + // Android-changed: OkHostnameVerifier.verifyAsIpAddress not accessible on platform builds + @Test @Ignore public void verifyAsIpAddress() { + // IPv4 + assertTrue(OkHostnameVerifier.verifyAsIpAddress("127.0.0.1")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("1.2.3.4")); + + // IPv6 + assertTrue(OkHostnameVerifier.verifyAsIpAddress("::1")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("2001:db8::1")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("::192.168.0.1")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("::ffff:192.168.0.1")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("FEDC:BA98:7654:3210:FEDC:BA98:7654:3210")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("1080:0:0:0:8:800:200C:417A")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("1080::8:800:200C:417A")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("FF01::101")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("0:0:0:0:0:0:13.1.68.3")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("0:0:0:0:0:FFFF:129.144.52.38")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("::13.1.68.3")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("::FFFF:129.144.52.38")); + + // Hostnames + assertFalse(OkHostnameVerifier.verifyAsIpAddress("go")); + assertFalse(OkHostnameVerifier.verifyAsIpAddress("localhost")); + assertFalse(OkHostnameVerifier.verifyAsIpAddress("squareup.com")); + assertFalse(OkHostnameVerifier.verifyAsIpAddress("www.nintendo.co.jp")); + } + + private X509Certificate certificate(String certificate) throws Exception { + return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate( + new ByteArrayInputStream(certificate.getBytes(UTF_8))); + } + + private SSLSession session(String certificate) throws Exception { + return new FakeSSLSession(certificate(certificate)); + } +}
diff --git a/common/src/test/java/org/conscrypt/MacTest.java b/common/src/test/java/org/conscrypt/MacTest.java new file mode 100644 index 0000000..a80065a --- /dev/null +++ b/common/src/test/java/org/conscrypt/MacTest.java
@@ -0,0 +1,394 @@ +/* + * Copyright (C) 2021 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.conscrypt.TestUtils.decodeHex; +import static org.conscrypt.TestUtils.encodeHex; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.spec.AlgorithmParameterSpec; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import javax.crypto.Mac; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import tests.util.ServiceTester; + + +@RunWith(JUnit4.class) +public class MacTest { + private final List<String[]> testVectors = readTestVectors(); + + // Column indices in test vector CSV file + private static final int ALGORITHM_INDEX = 0; + private static final int KEY_INDEX = 1; + private static final int MESSAGE_INDEX = 2; + private static final int MAC_INDEX = 3; + + // Number of splits to use when testing multiple buffers + private static final int NUM_SPLITS = 4; + + private final Random random = new Random(System.currentTimeMillis()); + + private final Provider conscryptProvider = TestUtils.getConscryptProvider(); + + @BeforeClass + public static void setUp() { + TestUtils.assumeAllowsUnsignedCrypto(); + } + + @Test + public void knownAnswerTest() throws Exception { + for (String[] entry : testVectors) { + String algorithm = entry[ALGORITHM_INDEX]; + String key = entry[KEY_INDEX]; + String msg = entry[MESSAGE_INDEX]; + String expected = entry[MAC_INDEX]; + + byte[] keyBytes = decodeHex(key); + byte[] msgBytes = decodeHex(msg); + byte[] expectedBytes = decodeHex(expected); + SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "RawBytes"); + + String baseFailMsg = String.format("Mac=%s\nKey=%s\nMsg=%s\nExpected=%s", + algorithm, key, msg, expected); + + // Calculate using Mac.update(byte[]) + byte[] macBytes = generateMacUsingUpdate(algorithm, secretKey, msgBytes); + assertArrayEquals(failMessage("Using update()", baseFailMsg, macBytes), + expectedBytes, macBytes); + + // Calculate using Mac.final(byte[]) + macBytes = generateMacUsingFinal(algorithm, secretKey, msgBytes); + assertArrayEquals(failMessage("Using final()", baseFailMsg, macBytes), + expectedBytes, macBytes); + + // Calculate using Mac.update(ByteBuffer) with a single non-direct ByteBuffer + ByteBuffer nondirectBuffer = ByteBuffer.wrap(msgBytes); + macBytes = generateMac(algorithm, secretKey, nondirectBuffer); + assertArrayEquals(failMessage("Non-direct ByteBuffer", baseFailMsg, macBytes), + expectedBytes, macBytes); + + // Calculate using Mac.update(ByteBuffer) with a single direct ByteBuffer + ByteBuffer directBuffer = ByteBuffer.allocateDirect(msgBytes.length); + directBuffer.put(msgBytes); + directBuffer.flip(); + macBytes = generateMac(algorithm, secretKey, directBuffer); + assertArrayEquals(failMessage("Direct ByteBuffer", baseFailMsg, macBytes), + expectedBytes, macBytes); + + // Calculate using Mac.update(ByteBuffer) with a multiple non-direct ByteBuffers + nondirectBuffer.flip(); + macBytes = generateMac(algorithm, secretKey, split(nondirectBuffer)); + assertArrayEquals(failMessage("Multiple non-direct ByteBuffers", baseFailMsg, macBytes), + expectedBytes, macBytes); + + // Calculate using Mac.update(ByteBuffer) with a multiple direct ByteBuffers + directBuffer.flip(); + macBytes = generateMac(algorithm, secretKey, split(directBuffer)); + assertArrayEquals(failMessage("Multiple direct ByteBuffers", baseFailMsg, macBytes), + expectedBytes, macBytes); + + // Calculated using a pre-loved Mac + macBytes = generateReusingMac(algorithm, keyBytes, msgBytes); + assertArrayEquals(failMessage("Re-use Mac", baseFailMsg, macBytes), + expectedBytes, macBytes); + } + } + + @Test + public void serviceCreation() { + newMacServiceTester() + // Android KeyStore can only be initialised with its own private keys - tested elsewhere. + .skipProvider("AndroidKeyStore") + .skipProvider("AndroidKeyStoreBCWorkaround") + .run(new ServiceTester.Test() { + @Override + public void test(final Provider provider, final String algorithm) throws Exception { + SecretKeySpec key = findAnyKey(algorithm); + + Mac mac = Mac.getInstance(algorithm); + assertEquals(algorithm, mac.getAlgorithm()); + + mac = Mac.getInstance(algorithm, provider); + assertEquals(algorithm, mac.getAlgorithm()); + assertEquals(provider, mac.getProvider()); + if (key != null) { + // TODO(prb) Ensure we have at least one test vector for every + // MAC in Conscrypt and Android. + mac.init(key); + assertEquals(provider, mac.getProvider()); + } + + mac = Mac.getInstance(algorithm, provider.getName()); + assertEquals(algorithm, mac.getAlgorithm()); + assertEquals(provider, mac.getProvider()); + if (key != null) { + mac.init(key); + assertEquals(provider, mac.getProvider()); + } + } + }); + } + + @Test + public void invalidKeyThrows() { + newMacServiceTester() + // BC actually accepts RSA public keys for these algorithms for some reason. + .skipCombination("BC", "PBEWITHHMACSHA") + .skipCombination("BC", "PBEWITHHMACSHA1") + .run(new ServiceTester.Test() { + @Override + public void test(final Provider provider, final String algorithm) throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair keyPair = generator.generateKeyPair(); + + try { + Mac mac = Mac.getInstance(algorithm, provider); + mac.init(keyPair.getPublic(), null); + fail(); + } catch (InvalidKeyException e) { + // Expected + } + } + }); + } + + @Test + public void uninitializedMacThrows() { + newMacServiceTester() + .run(new ServiceTester.Test() { + @Override + public void test(final Provider provider, final String algorithm) throws Exception { + byte[] message = "Message".getBytes(StandardCharsets.UTF_8); + + try { + Mac mac = Mac.getInstance(algorithm, provider); + mac.update(message); + fail(); + } catch (IllegalStateException e) { + // Expected + } + try { + Mac mac = Mac.getInstance(algorithm, provider); + mac.doFinal(message); + fail(); + } catch (IllegalStateException e) { + // Expected + } + try { + Mac mac = Mac.getInstance(algorithm, provider); + mac.doFinal(); + fail(); + } catch (IllegalStateException e) { + // Expected + } + } + }); + + } + + private ServiceTester newMacServiceTester() { + return ServiceTester.test("Mac") + // On Android 10 and 11 BC advertises these Macs but they are deprecated so throw + // on initialization. + .skipCombination("BC", "HMACMD5") + .skipCombination("BC", "HMACSHA1") + .skipCombination("BC", "HMACSHA224") + .skipCombination("BC", "HMACSHA256") + .skipCombination("BC", "HMACSHA384") + .skipCombination("BC", "HMACSHA512") + .skipCombination("BC", "PBEWITHHMACSHA224") + .skipCombination("BC", "PBEWITHHMACSHA256") + .skipCombination("BC", "PBEWITHHMACSHA384") + .skipCombination("BC", "PBEWITHHMACSHA512"); + } + + private static class DummyParameterSpec implements AlgorithmParameterSpec { } + + @Test + public void algorithmParameters() { + ServiceTester.test("Mac") + // Android KeyStore can only be initialised with its own private keys - tested elsewhere. + .skipProvider("AndroidKeyStore") + .skipProvider("AndroidKeyStoreBCWorkaround") + .run(new ServiceTester.Test() { + @Override + public void test(final Provider provider, final String algorithm) throws Exception { + SecretKeySpec key = findAnyKey(algorithm); + if (key != null) { + Mac mac = Mac.getInstance(algorithm, provider); + // Equivalent to mac.init(key) - allowed + mac.init(key, null); + + try { + mac = Mac.getInstance(algorithm, provider); + mac.init(key, new DummyParameterSpec()); + fail(); + } catch (InvalidAlgorithmParameterException exception) { + // Expected + } + } + } + }); + } + + private SecretKeySpec findAnyKey(String algorithm) { + for (String[] entry : testVectors) { + if (entry[ALGORITHM_INDEX].equals(algorithm)) { + return new SecretKeySpec(decodeHex(entry[KEY_INDEX]), "RawBytes"); + } + } + return null; + } + + @Test + public void anyAlgorithmParametersThrows() throws Exception { + Set<String> seen = new HashSet<>(); + for (String[] entry : testVectors) { + String algorithm = entry[ALGORITHM_INDEX]; + if (!seen.contains(algorithm)) { + seen.add(algorithm); + byte[] keyBytes = decodeHex(entry[KEY_INDEX]); + SecretKeySpec key = new SecretKeySpec(keyBytes, "RawBytes"); + Mac mac = Mac.getInstance(algorithm); + try { + mac.init(key, new IvParameterSpec(keyBytes)); + fail(algorithm); + } catch (InvalidAlgorithmParameterException exception) { + // Expected + } + } + } + } + + private String failMessage(String test, String base, byte[] mac) { + return String.format("Test %s\n%s\nActual= %s", test, base, encodeHex(mac)); + } + + // Splits a ByteBuffer into an array of NUM_SPLITS ByteBuffers containing the same data. + // If input.remaining < NUM_SPLITS then some buffers will be empty, which is fine. + private ByteBuffer[] split(ByteBuffer input) { + ByteBuffer[] buffers = new ByteBuffer[NUM_SPLITS]; + int targetSize = (input.remaining() / NUM_SPLITS) + 1; + ByteBuffer buffer; + for (int i = 0; i < NUM_SPLITS; i++) { + int size = Math.min(targetSize, input.remaining()); + buffer = input.isDirect() ? ByteBuffer.allocateDirect(size) : ByteBuffer.allocate(size); + buffers[i] = buffer; + + int savedLimit = input.limit(); + input.limit(input.position() + size); + buffer.put(input); + buffer.flip(); + input.limit(savedLimit); + } + assertEquals(0, input.remaining()); + return buffers; + } + + private byte[] generateMacUsingUpdate(String algorithm, SecretKeySpec key, byte[] message) + throws Exception { + Mac mac = getConscryptMac(algorithm, key); + mac.update(message); + return mac.doFinal(); + } + + private byte[] generateMacUsingFinal(String algorithm, SecretKeySpec key, byte[] message) + throws Exception { + Mac mac = getConscryptMac(algorithm, key); + return mac.doFinal(message); + } + + private byte[] generateMac(String algorithm, SecretKeySpec key, ByteBuffer buffer) + throws Exception { + return generateMac(algorithm, key, new ByteBuffer[] { buffer }); + } + + private byte[] generateMac(String algorithm, SecretKeySpec key, ByteBuffer[] buffers) + throws Exception { + Mac mac = getConscryptMac(algorithm, key); + for (ByteBuffer buffer : buffers) { + mac.update(buffer); + } + return mac.doFinal(); + } + + private byte[] generateReusingMac(String algorithm, byte[] keyBytes, byte[] message) + throws Exception { + Mac mac = getConscryptMac(algorithm); + + // Mutate the original message and key and calculate a MAC from them + byte[] otherKeyBytes = new byte[keyBytes.length]; + random.nextBytes(otherKeyBytes); + SecretKeySpec otherKey = new SecretKeySpec(otherKeyBytes, "RawBytes"); + byte[] otherMessage = new byte[message.length]; + random.nextBytes(otherMessage); + mac.init(otherKey); + mac.doFinal(otherMessage); + + // Then re-use the same Mac with the original key and message + SecretKeySpec key = new SecretKeySpec(keyBytes, "RawBytes"); + mac.reset(); + mac.init(key); + mac.update(message); + return mac.doFinal(); + } + + private Mac getConscryptMac(String algorithm) throws Exception { + return getConscryptMac(algorithm, null); + } + + private Mac getConscryptMac(String algorithm, SecretKeySpec key) throws Exception { + Mac mac = Mac.getInstance(algorithm, conscryptProvider); + assertNotNull(mac); + if (key != null) { + // Provider is not actually chosen until init + mac.init(key); + assertSame(conscryptProvider, mac.getProvider()); + } + return mac; + } + + private List<String[]> readTestVectors() { + try { + return TestUtils.readCsvResource("crypto/macs.csv"); + + } catch (IOException e) { + throw new AssertionError("Unable to load MAC test vectors", e); + } + } +}
diff --git a/common/src/test/java/org/conscrypt/TrustManagerImplTest.java b/common/src/test/java/org/conscrypt/TrustManagerImplTest.java index e1cce50..4d6a273 100644 --- a/common/src/test/java/org/conscrypt/TrustManagerImplTest.java +++ b/common/src/test/java/org/conscrypt/TrustManagerImplTest.java
@@ -21,7 +21,6 @@ import static org.junit.Assert.fail; import java.io.IOException; -import java.lang.reflect.Method; import java.security.KeyStore; import java.security.Principal; import java.security.cert.Certificate; @@ -30,8 +29,6 @@ import java.util.Arrays; import java.util.List; import javax.net.ssl.HandshakeCompletedListener; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; @@ -39,6 +36,7 @@ import javax.net.ssl.SSLSocket; import javax.net.ssl.X509TrustManager; import org.conscrypt.java.security.TestKeyStore; +import org.conscrypt.javax.net.ssl.TestHostnameVerifier; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -137,18 +135,14 @@ String goodHostname = TestKeyStore.CERT_HOSTNAME; String badHostname = "definitelywrong.nopenopenope"; - // The default hostname verifier on OpenJDK rejects all hostnames, so use our own - javax.net.ssl.HostnameVerifier oldDefault = HttpsURLConnection.getDefaultHostnameVerifier(); try { - HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier()); - SSLParameters params = new SSLParameters(); // Without endpoint identification this should pass despite the mismatched hostname params.setEndpointIdentificationAlgorithm(null); List<X509Certificate> certs = tmi.getTrustedChainForServer(chain, "RSA", - new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); + new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); assertEquals(Arrays.asList(chain), certs); // Turn on endpoint identification @@ -156,87 +150,65 @@ try { tmi.getTrustedChainForServer(chain, "RSA", - new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); + new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); fail(); } catch (CertificateException expected) { } certs = tmi.getTrustedChainForServer(chain, "RSA", - new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); + new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); assertEquals(Arrays.asList(chain), certs); // Override the global default hostname verifier with a Conscrypt-specific one that // always passes. Both scenarios should pass. - HostnameVerifier alwaysTrue = new HostnameVerifier() { + Conscrypt.setHostnameVerifier(tmi, new ConscryptHostnameVerifier() { @Override - public boolean verify(String hostname, SSLSession session) { + public boolean verify(X509Certificate[] certificates, String s, SSLSession sslSession) { return true; } - }; - HttpsURLConnection.setDefaultHostnameVerifier(alwaysTrue); + }); certs = tmi.getTrustedChainForServer(chain, "RSA", - new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); + new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); assertEquals(Arrays.asList(chain), certs); certs = tmi.getTrustedChainForServer(chain, "RSA", - new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); + new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); assertEquals(Arrays.asList(chain), certs); // Now set an instance-specific verifier on the trust manager. The bad hostname should // fail again. - Conscrypt.setHostnameVerifier(tmi, wrapVerifier(new TestHostnameVerifier())); + Conscrypt.setHostnameVerifier(tmi, Conscrypt.wrapHostnameVerifier(new TestHostnameVerifier())); try { tmi.getTrustedChainForServer(chain, "RSA", - new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); + new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); fail(); } catch (CertificateException expected) { } certs = tmi.getTrustedChainForServer(chain, "RSA", - new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); + new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); assertEquals(Arrays.asList(chain), certs); // Remove the instance-specific verifier, and both should pass again. Conscrypt.setHostnameVerifier(tmi, null); - certs = tmi.getTrustedChainForServer(chain, "RSA", - new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); - assertEquals(Arrays.asList(chain), certs); + try { + tmi.getTrustedChainForServer(chain, "RSA", + new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); + fail(); + } catch (CertificateException expected) { + } certs = tmi.getTrustedChainForServer(chain, "RSA", - new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); + new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); assertEquals(Arrays.asList(chain), certs); } finally { Conscrypt.setDefaultHostnameVerifier(null); - HttpsURLConnection.setDefaultHostnameVerifier(oldDefault); } } - /* - * Wrap a HostnameVerifier in a ConscryptHostnameVerifier. - * In the Android platform ConscryptHostnameVerifier is a private API and the interface - * definition changed between Android 11 and Android 12. - * If an Android 12 Conscrypt module is present then there will also be a (non-public) - * method to wrap it with the correct interface. - * If an earlier module is present then the interface is the same as in the CTS 11 codebase - * and so we can just wrap it directly with an anonymous class. - * See also b/195615915 - */ - private ConscryptHostnameVerifier wrapVerifier(final HostnameVerifier verifier) throws Exception { - Method wrapMethod = TestUtils.findWrapVerifierMethod(); - if (wrapMethod != null) { - return (ConscryptHostnameVerifier) wrapMethod.invoke(null, verifier); - } - return new ConscryptHostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return verifier.verify(hostname, session); - } - }; - } - private X509TrustManager trustManager(X509Certificate ca) throws Exception { KeyStore keyStore = TestKeyStore.createKeyStore(); keyStore.setCertificateEntry("alias", ca); @@ -504,8 +476,4 @@ throw new UnsupportedOperationException(); } } - - private static class TestHostnameVerifier - extends org.conscrypt.javax.net.ssl.TestHostnameVerifier - implements ConscryptHostnameVerifier {} }
diff --git a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java index 4f813a9..938629e 100644 --- a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java +++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java
@@ -27,21 +27,20 @@ import java.util.List; import org.junit.ClassRule; import org.junit.rules.TestRule; +import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import tests.util.ServiceTester; @RunWith(JUnit4.class) -public class KeyFactoryTestEC extends - AbstractKeyFactoryTest<ECPublicKeySpec, ECPrivateKeySpec> { - - // BEGIN Android-Added: Allow access to deprecated BC algorithms. - // Allow access to deprecated BC algorithms in this test, so we can ensure they - // continue to work - @ClassRule - public static TestRule enableDeprecatedBCAlgorithmsRule = - EnableDeprecatedBouncyCastleAlgorithmsRule.getInstance(); - // END Android-Added: Allow access to deprecated BC algorithms. +public class KeyFactoryTestEC extends AbstractKeyFactoryTest<ECPublicKeySpec, ECPrivateKeySpec> { + // BEGIN Android-Added: Allow access to deprecated BC algorithms. + // Allow access to deprecated BC algorithms in this test, so we can ensure they + // continue to work + @ClassRule + public static TestRule enableDeprecatedBCAlgorithmsRule = + EnableDeprecatedBouncyCastleAlgorithmsRule.getInstance(); + // END Android-Added: Allow access to deprecated BC algorithms. public KeyFactoryTestEC() { super("EC", ECPublicKeySpec.class, ECPrivateKeySpec.class);
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 1d69ae6..6724535 100644 --- a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java +++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java
@@ -19,7 +19,6 @@ import java.security.KeyFactory; import java.security.KeyPair; -import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; @@ -42,7 +41,6 @@ @RunWith(JUnit4.class) public class KeyFactoryTestRSA extends AbstractKeyFactoryTest<RSAPublicKeySpec, RSAPrivateKeySpec> { - // BEGIN Android-Added: Allow access to deprecated BC algorithms. // Allow access to deprecated BC algorithms in this test, so we can ensure they // continue to work
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 c09193b..957be7a 100644 --- a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSACrt.java +++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSACrt.java
@@ -18,9 +18,6 @@ import java.security.KeyPair; import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPublicKeySpec; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import tests.util.ServiceTester;
diff --git a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSACustom.java b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSACustom.java index 2e30029..847b886 100644 --- a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSACustom.java +++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSACustom.java
@@ -17,16 +17,11 @@ import java.security.KeyPair; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.Arrays; import java.util.List; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import tests.util.ServiceTester;
diff --git a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestXDH.java b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestXDH.java new file mode 100644 index 0000000..1f6b5fd --- /dev/null +++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestXDH.java
@@ -0,0 +1,61 @@ +/* + * Copyright (C) 2019 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.java.security; + +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; +import java.util.List; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import tests.util.ServiceTester; + +@RunWith(JUnit4.class) +public class KeyFactoryTestXDH extends + AbstractKeyFactoryTest<X509EncodedKeySpec, PKCS8EncodedKeySpec> { + + public KeyFactoryTestXDH() { + super("XDH", X509EncodedKeySpec.class, PKCS8EncodedKeySpec.class); + } + + @Override + protected void check(KeyPair keyPair) throws Exception { + new KeyAgreementHelper("XDH").test(keyPair); + } + + @Override + protected ServiceTester customizeTester(ServiceTester tester) { + // TODO: fix this test when Conscrypt's XDH keys can inherit from XECPublicKey and XECPrivateKey + return tester.skipProvider("SunEC"); + } + + @Override + protected List<KeyPair> getKeys() throws NoSuchAlgorithmException, InvalidKeySpecException { + return Arrays.asList( + new KeyPair( + DefaultKeys.getPublicKey("XDH"), + DefaultKeys.getPrivateKey("XDH") + ), + new KeyPair( + new TestPublicKey(DefaultKeys.getPublicKey("XDH")), + new TestPrivateKey(DefaultKeys.getPrivateKey("XDH")) + ) + ); + } +}
diff --git a/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java b/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java index 62bcc58..b7b41ef 100644 --- a/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java +++ b/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java
@@ -315,6 +315,20 @@ // so ignore it. continue; } + if ("XDH".equals(k.getAlgorithm()) + && "SunEC".equalsIgnoreCase(p.getName()) + && "11".equals(System.getProperty("java.specification.version"))) { + // SunEC in OpenJDK 11 has a bug where the format specified in RFC 8410 + // Section 7. It uses a single OCTET STRING to represent the key instead + // of an OCTET STRING inside of an OCTET STRING as defined in the RFC: + // ("For the keys defined in this document, the private key is always an + // opaque byte sequence. The ASN.1 type CurvePrivateKey is defined in + // this document to hold the byte sequence. Thus, when encoding a + // OneAsymmetricKey object, the private key is wrapped in a + // CurvePrivateKey object and wrapped by the OCTET STRING of the + // "privateKey" field.") + continue; + } if ("PKCS#8".equals(k.getFormat())) { PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encoded);
diff --git a/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTestRSA.java b/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTestRSA.java index 6898433..035000f 100644 --- a/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTestRSA.java +++ b/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTestRSA.java
@@ -21,10 +21,7 @@ @RunWith(JUnit4.class) public class KeyPairGeneratorTestRSA extends AbstractKeyPairGeneratorTest { - @SuppressWarnings("unchecked") public KeyPairGeneratorTestRSA() { super("RSA", new CipherAsymmetricCryptHelper("RSA")); } - } -
diff --git a/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTestXDH.java b/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTestXDH.java new file mode 100644 index 0000000..020be58 --- /dev/null +++ b/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTestXDH.java
@@ -0,0 +1,32 @@ +/* + * Copyright (C) 2009 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.java.security; + +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class KeyPairGeneratorTestXDH extends AbstractKeyPairGeneratorTest { + + public KeyPairGeneratorTestXDH() { + super("XDH", new KeyAgreementHelper("XDH")); + } + + @Override + protected int getKeySize() { + return 255; + } +}
diff --git a/common/src/test/java/org/conscrypt/java/security/SignatureTest.java b/common/src/test/java/org/conscrypt/java/security/SignatureTest.java index 177864d..bba0863 100644 --- a/common/src/test/java/org/conscrypt/java/security/SignatureTest.java +++ b/common/src/test/java/org/conscrypt/java/security/SignatureTest.java
@@ -111,6 +111,10 @@ // https://bugs.openjdk.java.net/browse/JDK-8044554), but skip verifying it all // the same. .skipProvider("SunPKCS11-NSS") + // We don't have code to generate key pairs for these yet. + .skipAlgorithm("Ed448") + .skipAlgorithm("Ed25519") + .skipAlgorithm("EdDSA") .run(new ServiceTester.Test() { @Override public void test(Provider provider, String algorithm) throws Exception { @@ -2934,7 +2938,7 @@ * randomized, so this won't be the exact signature you'll get out of * another signing operation unless you use a fixed RNG. */ - public static final byte[] SHA1withDSA_Vector2Signature = new byte[] { + private static final byte[] SHA1withDSA_Vector2Signature = new byte[] { (byte) 0x30, (byte) 0x2d, (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0x88, (byte) 0xef, (byte) 0xac, (byte) 0x2b, (byte) 0x8b, (byte) 0xe2, (byte) 0x61, (byte) 0xc6, (byte) 0x2b, (byte) 0xea, (byte) 0xd5, (byte) 0x96, (byte) 0xbc, (byte) 0xb0, (byte) 0xa1, (byte) 0x30, (byte) 0x0c, (byte) 0x1f, (byte) 0xed, @@ -2948,7 +2952,7 @@ * randomized, so this won't be the exact signature you'll get out of * another signing operation unless you use a fixed RNG. */ - public static final byte[] SHA224withDSA_Vector2Signature = new byte[] { + private static final byte[] SHA224withDSA_Vector2Signature = new byte[] { (byte) 0x30, (byte) 0x2D, (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0xAD, (byte) 0xE5, (byte) 0x6D, (byte) 0xF5, (byte) 0x11, (byte) 0x8D, (byte) 0x2E, (byte) 0x62, (byte) 0x5D, (byte) 0x98, (byte) 0x8A, (byte) 0xC4, (byte) 0x88, (byte) 0x7E, (byte) 0xE6, (byte) 0xA3, (byte) 0x44, (byte) 0x99, (byte) 0xEF, @@ -2962,7 +2966,7 @@ * randomized, so this won't be the exact signature you'll get out of * another signing operation unless you use a fixed RNG. */ - public static final byte[] SHA256withDSA_Vector2Signature = new byte[] { + private static final byte[] SHA256withDSA_Vector2Signature = new byte[] { (byte) 0x30, (byte) 0x2D, (byte) 0x02, (byte) 0x14, (byte) 0x0A, (byte) 0xB1, (byte) 0x74, (byte) 0x45, (byte) 0xE1, (byte) 0x63, (byte) 0x43, (byte) 0x68, (byte) 0x65, (byte) 0xBC, (byte) 0xCA, (byte) 0x45, (byte) 0x27, (byte) 0x11, (byte) 0x4D, (byte) 0x52, (byte) 0xFB, (byte) 0x22, (byte) 0x93, (byte) 0xDD,
diff --git a/common/src/test/java/org/conscrypt/java/security/cert/CertificateFactoryTest.java b/common/src/test/java/org/conscrypt/java/security/cert/CertificateFactoryTest.java index 94a7114..b5da74a 100644 --- a/common/src/test/java/org/conscrypt/java/security/cert/CertificateFactoryTest.java +++ b/common/src/test/java/org/conscrypt/java/security/cert/CertificateFactoryTest.java
@@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -49,6 +50,7 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Date; import java.util.GregorianCalendar; import java.util.Iterator; @@ -205,6 +207,48 @@ + "zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqA" + "ibAxWEEHXw=="; + // Generated with openssl crl2pkcs7 -nocrl -certfile cert.pem + private static final String VALID_CERTIFICATE_PKCS7_PEM = "-----BEGIN PKCS7-----\n" + + "MIIDUgYJKoZIhvcNAQcCoIIDQzCCAz8CAQExADALBgkqhkiG9w0BBwGgggMlMIID\n" + + "ITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBMMQsw\n" + + "CQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkgTHRk\n" + + "LjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0xMTEy\n" + + "MTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw\n" + + "FAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcwFQYD\n" + + "VQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" + + "6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jNgtXj\n" + + "9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L05vu\n" + + "uWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAMBgNV\n" + + "HRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3RlLmNv\n" + + "bS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUFBwMC\n" + + "BglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRwOi8v\n" + + "b2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0ZS5j\n" + + "b20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUFAAOB\n" + + "gQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5u2ON\n" + + "gJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6z5nR\n" + + "UP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHX6EAMQA=\n" + + "-----END PKCS7-----\n"; + + private static final String VALID_CERTIFICATE_PKCS7_DER_BASE64 = + "MIIDUgYJKoZIhvcNAQcCoIIDQzCCAz8CAQExADALBgkqhkiG9w0BBwGgggMlMIID" + + "ITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBMMQsw" + + "CQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkgTHRk" + + "LjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0xMTEy" + + "MTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw" + + "FAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcwFQYD" + + "VQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA" + + "6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jNgtXj" + + "9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L05vu" + + "uWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAMBgNV" + + "HRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3RlLmNv" + + "bS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUFBwMC" + + "BglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRwOi8v" + + "b2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0ZS5j" + + "b20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUFAAOB" + + "gQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5u2ON" + + "gJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6z5nR" + + "UP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHX6EAMQA="; + private static final String VALID_CRL_PEM = "-----BEGIN X509 CRL-----\n" + "MIIBUTCBuwIBATANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJHQjEkMCIGA1UE\n" @@ -239,6 +283,30 @@ + "CCHWAw8WN9XSJ4nGxdRiacG/5vEIx00ICUGCeGcnqWsSnFtagDtvry2c4MMexbSP" + "nDN0LLg="; + // Generated with openssl crl2pkcs7 -in crl.pem + private static final String VALID_CRL_PKCS7_PEM = "-----BEGIN PKCS7-----\n" + + "MIIBggYJKoZIhvcNAQcCoIIBczCCAW8CAQExADALBgkqhkiG9w0BBwGgAKGCAVUw\n" + + "ggFRMIG7AgEBMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQK\n" + + "ExtDZXJ0aWZpY2F0ZSBUcmFuc3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAw\n" + + "DgYDVQQHEwdFcncgV2VuFw0xOTA4MDcxMDI3MTBaFw0xOTA5MDYxMDI3MTBaMCIw\n" + + "IAIBBxcNMTkwODA3MTAyNjU0WjAMMAoGA1UdFQQDCgEBoA4wDDAKBgNVHRQEAwIB\n" + + "AjANBgkqhkiG9w0BAQsFAAOBgQDMX8MuIi9kNfgWlKM0KfApFuWeEktnU00EAfFx\n" + + "Ft8Vjemyhu9sYY6PHMJBb/TeCSeAblWtJ91U4syZAOsDGkqp5ioUOPQcB6da6BsI\n" + + "IdYDDxY31dInicbF1GJpwb/m8QjHTQgJQYJ4ZyepaxKcW1qAO2+vLZzgwx7FtI+c\n" + + "M3QsuDEA\n" + + "-----END PKCS7-----\n"; + + private static final String VALID_CRL_PKCS7_DER_BASE64 = + "MIIBggYJKoZIhvcNAQcCoIIBczCCAW8CAQExADALBgkqhkiG9w0BBwGgAKGCAVUw" + + "ggFRMIG7AgEBMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQK" + + "ExtDZXJ0aWZpY2F0ZSBUcmFuc3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAw" + + "DgYDVQQHEwdFcncgV2VuFw0xOTA4MDcxMDI3MTBaFw0xOTA5MDYxMDI3MTBaMCIw" + + "IAIBBxcNMTkwODA3MTAyNjU0WjAMMAoGA1UdFQQDCgEBoA4wDDAKBgNVHRQEAwIB" + + "AjANBgkqhkiG9w0BAQsFAAOBgQDMX8MuIi9kNfgWlKM0KfApFuWeEktnU00EAfFx" + + "Ft8Vjemyhu9sYY6PHMJBb/TeCSeAblWtJ91U4syZAOsDGkqp5ioUOPQcB6da6BsI" + + "IdYDDxY31dInicbF1GJpwb/m8QjHTQgJQYJ4ZyepaxKcW1qAO2+vLZzgwx7FtI+c" + + "M3QsuDEA"; + private static final String INVALID_CRL_PEM = "-----BEGIN X509 CRL-----\n" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" @@ -277,6 +345,15 @@ + "AAAAAAAA\n" + "-----END X509 CRL-----\n"; + // PKCS#7 file containing a small certificate. This input is small enough to use a two-byte + // length prefix. + private static final String SMALL_PKCS7_DER_BASE64 = + "MIHrBgkqhkiG9w0BBwKggd0wgdoCAQExADALBgkqhkiG9w0BBwGggcEwgb4wcgIB" + + "ADAFBgMrZXAwDDEKMAgGA1UEAxMBQTAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMy" + + "MzIxNTdaMAwxCjAIBgNVBAMTAUEwKjAFBgMrZXADIQDXWpgBgrEKt9VL/tPJZAc6" + + "DuFy89qmIyWvAhpo9wdRGjAFBgMrZXADQQBuCzqji8VP9xU8mHEMjXGChX7YP5J6" + + "64UyVKHKH9Z1u4wEbB8dJ3ScaWSLr+VHVKUhsrvcdCelnXRrrSD7xWALoQAxAA=="; + @Test public void test_generateCertificate() throws Exception { ServiceTester.test("CertificateFactory") @@ -298,22 +375,56 @@ } private void test_generateCertificate(CertificateFactory cf) throws Exception { + Certificate cert; { byte[] valid = VALID_CERTIFICATE_PEM.getBytes(Charset.defaultCharset()); Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid)); assertNotNull(c); + cert = c; } { byte[] valid = VALID_CERTIFICATE_PEM_CRLF.getBytes(Charset.defaultCharset()); Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid)); assertNotNull(c); + assertEquals(c, cert); } { byte[] valid = TestUtils.decodeBase64(VALID_CERTIFICATE_DER_BASE64); Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid)); assertNotNull(c); + assertEquals(c, cert); + } + + // The RI only supports PKCS#7 blobs with generateCertificates, not + // generateCertificate. See https://github.com/google/conscrypt/issues/987 + if (!StandardNames.IS_RI) { + byte[] valid = TestUtils.decodeBase64(VALID_CERTIFICATE_PKCS7_DER_BASE64); + Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid)); + assertNotNull(c); + assertEquals(c, cert); + } + + { + byte[] valid = VALID_CERTIFICATE_PKCS7_PEM.getBytes(Charset.defaultCharset()); + Collection<? extends Certificate> cs = cf.generateCertificates(new ByteArrayInputStream(valid)); + assertEquals(1, cs.size()); + assertEquals(cs.iterator().next(), cert); + } + + { + byte[] valid = TestUtils.decodeBase64(VALID_CERTIFICATE_PKCS7_DER_BASE64); + Collection<? extends Certificate> cs = cf.generateCertificates(new ByteArrayInputStream(valid)); + assertEquals(1, cs.size()); + assertEquals(cs.iterator().next(), cert); + } + + { + byte[] valid = TestUtils.decodeBase64(SMALL_PKCS7_DER_BASE64); + Collection<? extends Certificate> cs = cf.generateCertificates(new ByteArrayInputStream(valid)); + assertEquals(1, cs.size()); + assertNotNull(cs.iterator().next()); } try { @@ -647,7 +758,7 @@ assertEquals(providerName, Arrays.toString(pathFromList.getEncoded(encoding)), Arrays.toString(encoded)); } - assertFalse(providerName, Arrays.toString(encoded).equals(Arrays.toString(encodedCopy))); + assertNotEquals(providerName, Arrays.toString(encoded), Arrays.toString(encodedCopy)); encodedCopy[0] ^= (byte) 0xFF; assertEquals(providerName, Arrays.toString(encoded), Arrays.toString(encodedCopy)); @@ -685,7 +796,7 @@ Object output = ois.readObject(); assertTrue(providerName, output instanceof CertPath); - assertEquals(providerName, actualPath, (CertPath) output); + assertEquals(providerName, actualPath, output); } public static class KeyHolder { @@ -783,12 +894,33 @@ assertNotNull(c); valid = VALID_CRL_PEM_CRLF.getBytes(Charset.defaultCharset()); - c = cf.generateCRL(new ByteArrayInputStream(valid)); - assertNotNull(c); + CRL c2 = cf.generateCRL(new ByteArrayInputStream(valid)); + assertNotNull(c2); + assertEquals(c, c2); valid = TestUtils.decodeBase64(VALID_CRL_DER_BASE64); - c = cf.generateCRL(new ByteArrayInputStream(valid)); + c2 = cf.generateCRL(new ByteArrayInputStream(valid)); assertNotNull(c); + assertEquals(c, c2); + + // The RI only supports PKCS#7 with generateCRLs, not generateCRL. + // See https://github.com/google/conscrypt/issues/987 + if (!StandardNames.IS_RI) { + valid = TestUtils.decodeBase64(VALID_CRL_PKCS7_DER_BASE64); + c2 = cf.generateCRL(new ByteArrayInputStream(valid)); + assertNotNull(c); + assertEquals(c, c2); + } + + valid = TestUtils.decodeBase64(VALID_CRL_PKCS7_DER_BASE64); + Collection<? extends CRL> crls = cf.generateCRLs(new ByteArrayInputStream(valid)); + assertEquals(1, crls.size()); + assertEquals(c, crls.iterator().next()); + + valid = VALID_CRL_PKCS7_PEM.getBytes(Charset.defaultCharset()); + crls = cf.generateCRLs(new ByteArrayInputStream(valid)); + assertEquals(1, crls.size()); + assertEquals(c, crls.iterator().next()); try { byte[] invalid = INVALID_CRL_PEM.getBytes(Charset.defaultCharset());
diff --git a/common/src/test/java/org/conscrypt/java/security/cert/X509CRLTest.java b/common/src/test/java/org/conscrypt/java/security/cert/X509CRLTest.java index 6559477..50b1688 100644 --- a/common/src/test/java/org/conscrypt/java/security/cert/X509CRLTest.java +++ b/common/src/test/java/org/conscrypt/java/security/cert/X509CRLTest.java
@@ -16,6 +16,7 @@ package org.conscrypt.java.security.cert; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; @@ -33,6 +34,7 @@ import java.security.cert.X509Certificate; import java.util.Collections; import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; +import org.conscrypt.TestUtils; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TestRule; @@ -102,6 +104,24 @@ + "nDN0LLg=\n" + "-----END X509 CRL-----\n"; + private static final String CRL_TBS_BASE64 = + "MIG7AgEBMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQKExtD" + + "ZXJ0aWZpY2F0ZSBUcmFuc3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAwDgYD" + + "VQQHEwdFcncgV2VuFw0xOTA4MDcxMDI3MTBaFw0xOTA5MDYxMDI3MTBaMCIwIAIB" + + "BxcNMTkwODA3MTAyNjU0WjAMMAoGA1UdFQQDCgEBoA4wDDAKBgNVHRQEAwIBAg=="; + + private static final String UNKNOWN_SIGNATURE_OID = + "-----BEGIN X509 CRL-----\n" + + "MIIBVzCBvgIBATAQBgwqhkiG9xIEAYS3CQIFADBVMQswCQYDVQQGEwJHQjEkMCIG\n" + + "A1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVXYWxl\n" + + "czEQMA4GA1UEBxMHRXJ3IFdlbhcNMTkwODA3MTAyNzEwWhcNMTkwOTA2MTAyNzEw\n" + + "WjAiMCACAQcXDTE5MDgwNzEwMjY1NFowDDAKBgNVHRUEAwoBAaAOMAwwCgYDVR0U\n" + + "BAMCAQIwEAYMKoZIhvcSBAGEtwkCBQADgYEAzF/DLiIvZDX4FpSjNCnwKRblnhJL\n" + + "Z1NNBAHxcRbfFY3psobvbGGOjxzCQW/03gkngG5VrSfdVOLMmQDrAxpKqeYqFDj0\n" + + "HAenWugbCCHWAw8WN9XSJ4nGxdRiacG/5vEIx00ICUGCeGcnqWsSnFtagDtvry2c\n" + + "4MMexbSPnDN0LLg=\n" + + "-----END X509 CRL-----\n"; + @Test public void testCrl() throws Exception { ServiceTester.test("CertificateFactory") @@ -126,6 +146,9 @@ } catch (SignatureException expected) { } + byte[] expectedTBSCertList = TestUtils.decodeBase64(CRL_TBS_BASE64); + assertArrayEquals(expectedTBSCertList, crl.getTBSCertList()); + assertTrue(crl.isRevoked(revoked)); X509CRLEntry entry = crl.getRevokedCertificate(revoked); assertEquals(CRLReason.KEY_COMPROMISE, entry.getRevocationReason()); @@ -140,4 +163,19 @@ } }); } + + @Test + public void testUnknownSigAlgOID() throws Exception { + ServiceTester.test("CertificateFactory") + .withAlgorithm("X509") + .run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + CertificateFactory cf = CertificateFactory.getInstance("X509", p); + X509CRL crl = (X509CRL) cf.generateCRL(new ByteArrayInputStream( + UNKNOWN_SIGNATURE_OID.getBytes(StandardCharsets.US_ASCII))); + assertEquals("1.2.840.113554.4.1.72585.2", crl.getSigAlgOID()); + } + }); + } }
diff --git a/common/src/test/java/org/conscrypt/java/security/cert/X509CertificateTest.java b/common/src/test/java/org/conscrypt/java/security/cert/X509CertificateTest.java index 9257315..beddeb3 100644 --- a/common/src/test/java/org/conscrypt/java/security/cert/X509CertificateTest.java +++ b/common/src/test/java/org/conscrypt/java/security/cert/X509CertificateTest.java
@@ -16,24 +16,38 @@ package org.conscrypt.java.security.cert; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; +import java.math.BigInteger; import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.Provider; -import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.CertificateParsingException; import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; +import javax.security.auth.x500.X500Principal; +import org.conscrypt.TestUtils; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import tests.util.Pair; import tests.util.ServiceTester; @RunWith(JUnit4.class) @@ -135,6 +149,248 @@ + "Qhy0YgIgYWr0qSCLqxUQv3oQHMUpSmfHtP0Pwvb3DbbH6lY7TkI=\n" + "-----END CERTIFICATE-----\n"; + /** + * This cert is signed with OID 1.2.840.113554.4.1.72585.2 instead of a + * standard one. + */ + private static final String UNKNOWN_SIGNATURE_OID = + "-----BEGIN CERTIFICATE-----\n" + + "MIIB2TCCAXugAwIBAgIJANlMBNpJfb/rMA4GDCqGSIb3EgQBhLcJAjBFMQswCQYD\n" + + "VQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQg\n" + + "V2lkZ2l0cyBQdHkgTHRkMB4XDTE0MDQyMzIzMjE1N1oXDTE0MDUyMzIzMjE1N1ow\n" + + "RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu\n" + + "dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA\n" + + "BOYraeK/ZZ+Xvi8eDZSKTNWXa7epHg1G+92pqR6d3LpaAefWl6gKGPnDxKMeVuJ8\n" + + "g0jbFhoc9R1+8ZQtS89yIsGjUDBOMB0GA1UdDgQWBBSrhNKsq5Xwgk4WeAdVV1/k\n" + + "Jo2C0TAfBgNVHSMEGDAWgBSrhNKsq5Xwgk4WeAdVV1/kJo2C0TAMBgNVHRMEBTAD\n" + + "AQH/MA4GDCqGSIb3EgQBhLcJAgNIADBFAiEA8qA1XlE6NsOCeZvuJ1CFjnAGdJVX\n" + + "0il0APS+FYddxAcCIHweeRRqIYPwenRoeV8UmZpotPHLnhVe5h8yUmFedckU\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is an X.509v1 certificatea, so most fields are missing. It exists to test accessors + * correctly handle the lack of fields. It was constructed by hand, so the signature itself is + * invalid. + */ + private static final String X509V1_CERT = "-----BEGIN CERTIFICATE-----\n" + + "MIIBGjCBwgIJANlMBNpJfb/rMAkGByqGSM49BAEwFjEUMBIGA1UEAwwLVGVzdCBJ\n" + + "c3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjAXMRUwEwYDVQQD\n" + + "DAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2niv2Wf\n" + + "l74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYaHPUd\n" + + "fvGULUvPciLBMAkGByqGSM49BAEDSAAwRQIhAPKgNV5ROjbDgnmb7idQhY5wBnSV\n" + + "V9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTxy54VXuYfMlJhXnXJFA==\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is a certificate with many extensions filled it. It exists to test accessors correctly + * report fields. It was constructed by hand, so the signature itself is invalid. Add more + * fields as necessary with https://github.com/google/der-ascii. + */ + private static final String MANY_EXTENSIONS = "-----BEGIN CERTIFICATE-----\n" + + "MIIEADCCAuigAwIBAgIJALW2IrlaBKUhMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV\n" + + "BAMMC1Rlc3QgSXNzdWVyMB4XDTE2MDcwOTA0MzgwOVoXDTE2MDgwODA0MzgwOVow\n" + + "FzEVMBMGA1UEAwwMVGVzdCBTdWJqZWN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" + + "MIIBCgKCAQEAugvahBkSAUF1fC49vb1bvlPrcl80kop1iLpiuYoz4Qptwy57+EWs\n" + + "sZBcHprZ5BkWf6PeGZ7F5AX1PyJbGHZLqvMCvViP6pd4MFox/igESISEHEixoiXC\n" + + "zepBrhtp5UQSjHD4D4hKtgdMgVxX+LRtwgW3mnu/vBu7rzpr/DS8io99p3lqZ1Ak\n" + + "y+aNlcMj6MYy8U+YFEevb/V0lRY9oqwmW7BHnXikm/vi6sjIS350U8zb/mRzYeIs\n" + + "2R65LUduTL50+UMgat9ocewI2dv8aO9Dph+8NdGtg8LFYyTTHcUxJoMr1PTOgnmE\n" + + "T19WJH4PrFwk7ZE1QJQQ1L4iKmPeQistuQIDAQABgQIEoIICA1CjggFGMIIBQjAP\n" + + "BgNVHRMECDAGAQH/AgEKMCEGA1UdJQQaMBgGCCsGAQUFBwMBBgwqhkiG9xIEAYS3\n" + + "CQIwfwYDVR0RBHgwdoETc3ViamVjdEBleGFtcGxlLmNvbYITc3ViamVjdC5leGFt\n" + + "cGxlLmNvbaQZMBcxFTATBgNVBAMMDFRlc3QgU3ViamVjdIYbaHR0cHM6Ly9leGFt\n" + + "cGxlLmNvbS9zdWJqZWN0hwR/AAABiAwqhkiG9xIEAYS3CQIwewYDVR0SBHQwcoES\n" + + "aXNzdWVyQGV4YW1wbGUuY29tghJpc3N1ZXIuZXhhbXBsZS5jb22kGDAWMRQwEgYD\n" + + "VQQDDAtUZXN0IElzc3VlcoYaaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXKHBH8A\n" + + "AAGIDCqGSIb3EgQBhLcJAjAOBgNVHQ8BAf8EBAMCBaAwDQYJKoZIhvcNAQELBQAD\n" + + "ggEBAD7Jg68SArYWlcoHfZAB90Pmyrt5H6D8LRi+W2Ri1fBNxREELnezWJ2scjl4\n" + + "UMcsKYp4Pi950gVN+62IgrImcCNvtb5I1Cfy/MNNur9ffas6X334D0hYVIQTePyF\n" + + "k3umI+2mJQrtZZyMPIKSY/sYGQHhGGX6wGK+GO/og0PQk/Vu6D+GU2XRnDV0YZg1\n" + + "lsAsHd21XryK6fDmNkEMwbIWrts4xc7scRrGHWy+iMf6/7p/Ak/SIicM4XSwmlQ8\n" + + "pPxAZPr+E2LoVd9pMpWUwpW2UbtO5wsGTrY5sO45tFNN/y+jtUheB1C2ijObG/tX\n" + + "ELaiyCdM+S/waeuv0MXtI4xnn1A=\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is a certificate whose basicConstraints extension marks it as a CA, with no pathlen + * constraint. + */ + private static final String BASIC_CONSTRAINTS_NO_PATHLEN = "-----BEGIN CERTIFICATE-----\n" + + "MIIBMzCB2qADAgECAgkA2UwE2kl9v+swCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwL\n" + + "VGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjAXMRUw\n" + + "EwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATm\n" + + "K2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI\n" + + "2xYaHPUdfvGULUvPciLBoxAwDjAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0gA\n" + + "MEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13EBwIgfB55FGohg/B6\n" + + "dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is a certificate whose basicConstraints extension marks it as a CA with a pathlen + * constraint of zero. + */ + private static final String BASIC_CONSTRAINTS_PATHLEN_0 = "-----BEGIN CERTIFICATE-----\n" + + "MIIBNjCB3aADAgECAgkA2UwE2kl9v+swCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwL\n" + + "VGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjAXMRUw\n" + + "EwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATm\n" + + "K2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI\n" + + "2xYaHPUdfvGULUvPciLBoxMwETAPBgNVHRMECDAGAQH/AgEAMAoGCCqGSM49BAMC\n" + + "A0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13EBwIgfB55FGoh\n" + + "g/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is a certificate whose basicConstraints extension marks it as a leaf certificate. + */ + private static final String BASIC_CONSTRAINTS_LEAF = "-----BEGIN CERTIFICATE-----\n" + + "MIIBMDCB16ADAgECAgkA2UwE2kl9v+swCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwL\n" + + "VGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjAXMRUw\n" + + "EwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATm\n" + + "K2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI\n" + + "2xYaHPUdfvGULUvPciLBow0wCzAJBgNVHRMEAjAAMAoGCCqGSM49BAMCA0gAMEUC\n" + + "IQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13EBwIgfB55FGohg/B6dGh5\n" + + "XxSZmmi08cueFV7mHzJSYV51yRQ=\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is a certificate with a pathlen constraint of 10, but there is an unrelated invalid + * subjectAltNames extension. + */ + private static final String BASIC_CONSTRAINTS_PATHLEN_10_BAD_SAN = + "-----BEGIN CERTIFICATE-----\n" + + "MIIBRjCB7aADAgECAgkA2UwE2kl9v+swCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwL\n" + + "VGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjAXMRUw\n" + + "EwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATm\n" + + "K2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI\n" + + "2xYaHPUdfvGULUvPciLBoyMwITAPBgNVHRMECDAGAQH/AgEKMA4GA1UdEQQHSU5W\n" + + "QUxJRDAKBggqhkjOPQQDAgNIADBFAiEA8qA1XlE6NsOCeZvuJ1CFjnAGdJVX0il0\n" + + "APS+FYddxAcCIHweeRRqIYPwenRoeV8UmZpotPHLnhVe5h8yUmFedckU\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is a certificate whose keyUsage extension has more than nine bits. The getKeyUsage() + * method internally rounds up to nine bits, so this tests what happens when it does not need to + * round. + */ + private static final String LARGE_KEY_USAGE = "-----BEGIN CERTIFICATE-----\n" + + "MIIBNjCB3aADAgECAgkA2UwE2kl9v+swCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwL\n" + + "VGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjAXMRUw\n" + + "EwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATm\n" + + "K2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI\n" + + "2xYaHPUdfvGULUvPciLBoxMwETAPBgNVHQ8BAf8EBQMDBaAAMAoGCCqGSM49BAMC\n" + + "A0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13EBwIgfB55FGoh\n" + + "g/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=\n" + + "-----END CERTIFICATE-----\n"; + + /* + * OpenSSLX509Certificate needs to compensate for OpenSSL's AlgorithmIdentifier representation + * by re-encoding the parameter field. Test this behaves correctly against a variety of + * different parameter types. + */ + private static final String SIGALG_NO_PARAMETER = "-----BEGIN CERTIFICATE-----\n" + + "MIIBKTCBzKADAgECAgkA2UwE2kl9v+swDgYMKoZIhvcSBAGEtwkCMBYxFDASBgNV\n" + + "BAMMC1Rlc3QgSXNzdWVyMB4XDTE0MDQyMzIzMjE1N1oXDTE0MDUyMzIzMjE1N1ow\n" + + "FzEVMBMGA1UEAwwMVGVzdCBTdWJqZWN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\n" + + "QgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPEox5W\n" + + "4nyDSNsWGhz1HX7xlC1Lz3IiwTAOBgwqhkiG9xIEAYS3CQIDSAAwRQIhAPKgNV5R\n" + + "OjbDgnmb7idQhY5wBnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTx\n" + + "y54VXuYfMlJhXnXJFA==\n" + + "-----END CERTIFICATE-----\n"; + private static final String SIGALG_NULL_PARAMETER = "-----BEGIN CERTIFICATE-----\n" + + "MIIBLTCBzqADAgECAgkA2UwE2kl9v+swEAYMKoZIhvcSBAGEtwkCBQAwFjEUMBIG\n" + + "A1UEAwwLVGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3\n" + + "WjAXMRUwEwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMB\n" + + "BwNCAATmK2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8Sj\n" + + "HlbifINI2xYaHPUdfvGULUvPciLBMBAGDCqGSIb3EgQBhLcJAgUAA0gAMEUCIQDy\n" + + "oDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13EBwIgfB55FGohg/B6dGh5XxSZ\n" + + "mmi08cueFV7mHzJSYV51yRQ=\n" + + "-----END CERTIFICATE-----\n"; + private static final String SIGALG_STRING_PARAMETER = "-----BEGIN CERTIFICATE-----\n" + + "MIIBNzCB06ADAgECAgkA2UwE2kl9v+swFQYMKoZIhvcSBAGEtwkCDAVwYXJhbTAW\n" + + "MRQwEgYDVQQDDAtUZXN0IElzc3VlcjAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMy\n" + + "MzIxNTdaMBcxFTATBgNVBAMMDFRlc3QgU3ViamVjdDBZMBMGByqGSM49AgEGCCqG\n" + + "SM49AwEHA0IABOYraeK/ZZ+Xvi8eDZSKTNWXa7epHg1G+92pqR6d3LpaAefWl6gK\n" + + "GPnDxKMeVuJ8g0jbFhoc9R1+8ZQtS89yIsEwFQYMKoZIhvcSBAGEtwkCDAVwYXJh\n" + + "bQNIADBFAiEA8qA1XlE6NsOCeZvuJ1CFjnAGdJVX0il0APS+FYddxAcCIHweeRRq\n" + + "IYPwenRoeV8UmZpotPHLnhVe5h8yUmFedckU\n" + + "-----END CERTIFICATE-----\n"; + private static final String SIGALG_BOOLEAN_PARAMETER = "-----BEGIN CERTIFICATE-----\n" + + "MIIBLzCBz6ADAgECAgkA2UwE2kl9v+swEQYMKoZIhvcSBAGEtwkCAQH/MBYxFDAS\n" + + "BgNVBAMMC1Rlc3QgSXNzdWVyMB4XDTE0MDQyMzIzMjE1N1oXDTE0MDUyMzIzMjE1\n" + + "N1owFzEVMBMGA1UEAwwMVGVzdCBTdWJqZWN0MFkwEwYHKoZIzj0CAQYIKoZIzj0D\n" + + "AQcDQgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPE\n" + + "ox5W4nyDSNsWGhz1HX7xlC1Lz3IiwTARBgwqhkiG9xIEAYS3CQIBAf8DSAAwRQIh\n" + + "APKgNV5ROjbDgnmb7idQhY5wBnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlf\n" + + "FJmaaLTxy54VXuYfMlJhXnXJFA==\n" + + "-----END CERTIFICATE-----\n"; + private static final String SIGALG_SEQUENCE_PARAMETER = "-----BEGIN CERTIFICATE-----\n" + + "MIIBLTCBzqADAgECAgkA2UwE2kl9v+swEAYMKoZIhvcSBAGEtwkCMAAwFjEUMBIG\n" + + "A1UEAwwLVGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3\n" + + "WjAXMRUwEwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMB\n" + + "BwNCAATmK2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8Sj\n" + + "HlbifINI2xYaHPUdfvGULUvPciLBMBAGDCqGSIb3EgQBhLcJAjAAA0gAMEUCIQDy\n" + + "oDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13EBwIgfB55FGohg/B6dGh5XxSZ\n" + + "mmi08cueFV7mHzJSYV51yRQ=\n" + + "-----END CERTIFICATE-----\n"; + + private static Date dateFromUTC(int year, int month, int day, int hour, int minute, int second) + throws Exception { + Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + c.set(year, month, day, hour, minute, second); + c.set(Calendar.MILLISECOND, 0); + return c.getTime(); + } + + private static X509Certificate certificateFromPEM(Provider p, String pem) + throws CertificateException { + CertificateFactory cf = CertificateFactory.getInstance("X509", p); + return (X509Certificate) cf.generateCertificate( + new ByteArrayInputStream(pem.getBytes(Charset.forName("US-ASCII")))); + } + + private static List<Pair<Integer, String>> normalizeGeneralNames(Collection<List<?>> names) { + // Extract a more convenient type than Java's Collection<List<?>>. + List<Pair<Integer, String>> result = new ArrayList<Pair<Integer, String>>(); + for (List<?> tuple : names) { + assertEquals(2, tuple.size()); + int type = ((Integer) tuple.get(0)).intValue(); + // TODO(davidben): Most name types are expected to have a String value, but some use + // byte[]. Update this logic when testing those name types. See + // X509Certificate.getSubjectAlternativeNames(). + String value = (String) tuple.get(1); + result.add(Pair.of(type, value)); + } + // Although there is a natural order (the order in the certificate), Java's API returns a + // Collection, so there is no guarantee of the provider using a particular order. Normalize + // the order before comparing. + Collections.sort(result, new Comparator<Pair<Integer, String>>() { + @Override + public int compare(Pair<Integer, String> a, Pair<Integer, String> b) { + int cmp = a.getFirst().compareTo(b.getFirst()); + if (cmp != 0) { + return cmp; + } + return a.getSecond().compareTo(b.getSecond()); + } + }); + return result; + } + + private static void assertGeneralNamesEqual( + Collection<List<?>> expected, Collection<List<?>> actual) throws Exception { + assertEquals(normalizeGeneralNames(expected), normalizeGeneralNames(actual)); + } + + // Error Prone flags Date.equals(), but Instant and LocalDateTime are not available in Java 7. + // We could compare Date.getTime(), but this trips another warning in Error Prone. We do not use + // Date subclasses, so stick with Date.equals for now. + // + // https://errorprone.info/bugpattern/UndefinedEquals + @SuppressWarnings("UndefinedEquals") + private static void assertDatesEqual(Date expected, Date actual) throws Exception { + assertEquals(expected, actual); + } + // See issue #539. @Test public void testMismatchedAlgorithm() throws Exception { @@ -143,10 +399,8 @@ .run(new ServiceTester.Test() { @Override public void test(Provider p, String algorithm) throws Exception { - CertificateFactory cf = CertificateFactory.getInstance("X509", p); try { - Certificate c = cf.generateCertificate(new ByteArrayInputStream( - MISMATCHED_ALGORITHM_CERT.getBytes(Charset.forName("US-ASCII")))); + X509Certificate c = certificateFromPEM(p, MISMATCHED_ALGORITHM_CERT); c.verify(c.getPublicKey()); fail(); } catch (CertificateException expected) { @@ -169,9 +423,7 @@ @Override public void test(Provider p, String algorithm) throws Exception { try { - CertificateFactory cf = CertificateFactory.getInstance("X509", p); - Certificate c = cf.generateCertificate(new ByteArrayInputStream( - EC_EXPLICIT_KEY_CERT.getBytes(Charset.forName("US-ASCII")))); + X509Certificate c = certificateFromPEM(p, EC_EXPLICIT_KEY_CERT); c.verify(c.getPublicKey()); fail(); } catch (InvalidKeyException expected) { @@ -190,12 +442,224 @@ .run(new ServiceTester.Test() { @Override public void test(Provider p, String algorithm) throws Exception { - CertificateFactory cf = CertificateFactory.getInstance("X509", p); - Certificate c = cf.generateCertificate(new ByteArrayInputStream( - VALID_CERT.getBytes(Charset.forName("US-ASCII")))); - assertEquals("SHA256WITHRSA", - ((X509Certificate) c).getSigAlgName().toUpperCase()); + X509Certificate c = certificateFromPEM(p, VALID_CERT); + assertEquals("SHA256WITHRSA", c.getSigAlgName().toUpperCase()); } }); } + + @Test + public void testUnknownSigAlgOID() throws Exception { + ServiceTester.test("CertificateFactory") + .withAlgorithm("X509") + .run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + X509Certificate c = certificateFromPEM(p, UNKNOWN_SIGNATURE_OID); + assertEquals("1.2.840.113554.4.1.72585.2", c.getSigAlgOID()); + } + }); + } + + @Test + public void testV1Cert() throws Exception { + ServiceTester tester = ServiceTester.test("CertificateFactory").withAlgorithm("X509"); + tester.run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + X509Certificate c = certificateFromPEM(p, X509V1_CERT); + + // Check basic certificate properties. + assertEquals(1, c.getVersion()); + assertEquals(new BigInteger("d94c04da497dbfeb", 16), c.getSerialNumber()); + assertDatesEqual( + dateFromUTC(2014, Calendar.APRIL, 23, 23, 21, 57), c.getNotBefore()); + assertDatesEqual(dateFromUTC(2014, Calendar.MAY, 23, 23, 21, 57), c.getNotAfter()); + assertEquals(new X500Principal("CN=Test Issuer"), c.getIssuerX500Principal()); + assertEquals(new X500Principal("CN=Test Subject"), c.getSubjectX500Principal()); + assertEquals("1.2.840.10045.4.1", c.getSigAlgOID()); + String signatureHex = "3045022100f2a0355e513a36c382799bee27" + + "50858e7006749557d2297400f4be15875dc4" + + "0702207c1e79146a2183f07a7468795f1499" + + "9a68b4f1cb9e155ee61f3252615e75c914"; + assertArrayEquals(TestUtils.decodeHex(signatureHex), c.getSignature()); + + // ECDSA signature AlgorithmIdentifiers omit parameters. + assertNull(c.getSigAlgParams()); + + // The certificate does not have UIDs. + assertNull(c.getIssuerUniqueID()); + assertNull(c.getSubjectUniqueID()); + + // The certificate does not have any extensions. + assertEquals(-1, c.getBasicConstraints()); + assertNull(c.getExtendedKeyUsage()); + assertNull(c.getIssuerAlternativeNames()); + assertNull(c.getKeyUsage()); + assertNull(c.getSubjectAlternativeNames()); + } + }); + } + + @Test + public void testManyExtensions() throws Exception { + ServiceTester tester = ServiceTester.test("CertificateFactory").withAlgorithm("X509"); + tester.run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + X509Certificate c = certificateFromPEM(p, MANY_EXTENSIONS); + + assertEquals(3, c.getVersion()); + assertEquals(new BigInteger("b5b622b95a04a521", 16), c.getSerialNumber()); + assertDatesEqual(dateFromUTC(2016, Calendar.JULY, 9, 4, 38, 9), c.getNotBefore()); + assertDatesEqual(dateFromUTC(2016, Calendar.AUGUST, 8, 4, 38, 9), c.getNotAfter()); + assertEquals(new X500Principal("CN=Test Issuer"), c.getIssuerX500Principal()); + assertEquals(new X500Principal("CN=Test Subject"), c.getSubjectX500Principal()); + assertEquals("1.2.840.113549.1.1.11", c.getSigAlgOID()); + String signatureHex = "3ec983af1202b61695ca077d9001f743e6ca" + + "bb791fa0fc2d18be5b6462d5f04dc511042e" + + "77b3589dac72397850c72c298a783e2f79d2" + + "054dfbad8882b22670236fb5be48d427f2fc" + + "c34dbabf5f7dab3a5f7df80f485854841378" + + "fc85937ba623eda6250aed659c8c3c829263" + + "fb181901e11865fac062be18efe88343d093" + + "f56ee83f865365d19c357461983596c02c1d" + + "ddb55ebc8ae9f0e636410cc1b216aedb38c5" + + "ceec711ac61d6cbe88c7faffba7f024fd222" + + "270ce174b09a543ca4fc4064fafe1362e855" + + "df69329594c295b651bb4ee70b064eb639b0" + + "ee39b4534dff2fa3b5485e0750b68a339b1b" + + "fb5710b6a2c8274cf92ff069ebafd0c5ed23" + + "8c679f50"; + assertArrayEquals(TestUtils.decodeHex(signatureHex), c.getSignature()); + + // Although documented to only return null when there are no parameters, the SUN + // provider also returns null when the algorithm uses an explicit parameter with a + // value of ASN.1 NULL. + if (c.getSigAlgParams() != null) { + assertArrayEquals(TestUtils.decodeHex("0500"), c.getSigAlgParams()); + } + + assertArrayEquals(new boolean[] {true, false, true, false}, c.getIssuerUniqueID()); + assertArrayEquals( + new boolean[] {false, true, false, true, false}, c.getSubjectUniqueID()); + assertEquals(10, c.getBasicConstraints()); + assertEquals(Arrays.asList("1.3.6.1.5.5.7.3.1", "1.2.840.113554.4.1.72585.2"), + c.getExtendedKeyUsage()); + + // TODO(davidben): Test the other name types. + assertGeneralNamesEqual( + Arrays.<List<?>>asList(Arrays.asList(1, "issuer@example.com"), + Arrays.asList(2, "issuer.example.com"), + Arrays.asList(4, "CN=Test Issuer"), + Arrays.asList(6, "https://example.com/issuer"), + // TODO(https://github.com/google/conscrypt/issues/938): Fix IPv6 + // handling and include it in this test. + Arrays.asList(7, "127.0.0.1"), + Arrays.asList(8, "1.2.840.113554.4.1.72585.2")), + c.getIssuerAlternativeNames()); + assertGeneralNamesEqual( + Arrays.<List<?>>asList(Arrays.asList(1, "subject@example.com"), + Arrays.asList(2, "subject.example.com"), + Arrays.asList(4, "CN=Test Subject"), + Arrays.asList(6, "https://example.com/subject"), + // TODO(https://github.com/google/conscrypt/issues/938): Fix IPv6 + // handling and include it in this test. + Arrays.asList(7, "127.0.0.1"), + Arrays.asList(8, "1.2.840.113554.4.1.72585.2")), + c.getSubjectAlternativeNames()); + + // Although the BIT STRING in the certificate only has three bits, getKeyUsage() + // rounds up to at least 9 bits. + assertArrayEquals( + new boolean[] {true, false, true, false, false, false, false, false, false}, + c.getKeyUsage()); + } + }); + } + + @Test + public void testBasicConstraints() throws Exception { + ServiceTester tester = ServiceTester.test("CertificateFactory").withAlgorithm("X509"); + tester.run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + // Test some additional edge cases in getBasicConstraints() beyond that + // testManyExtensions() and testV1Cert() covered. + + // If there is no pathLen constraint but the certificate is a CA, + // getBasicConstraints() returns Integer.MAX_VALUE. + X509Certificate c = certificateFromPEM(p, BASIC_CONSTRAINTS_NO_PATHLEN); + assertEquals(Integer.MAX_VALUE, c.getBasicConstraints()); + + // If there is a pathLen constraint of zero, getBasicConstraints() returns it. + c = certificateFromPEM(p, BASIC_CONSTRAINTS_PATHLEN_0); + assertEquals(0, c.getBasicConstraints()); + + // If there is basicConstraints extension indicating a leaf certficate, + // getBasicConstraints() returns -1. The accessor does not distinguish between no + // basicConstraints extension and a leaf one. + c = certificateFromPEM(p, BASIC_CONSTRAINTS_LEAF); + assertEquals(-1, c.getBasicConstraints()); + + // If some unrelated extension has a syntax error, and that syntax error does not + // fail when constructing the certificate, it should not interfere with + // getBasicConstraints(). + try { + c = certificateFromPEM(p, BASIC_CONSTRAINTS_PATHLEN_10_BAD_SAN); + } catch (CertificateParsingException e) { + // The certificate has a syntax error, so it would also be valid for the + // provider to reject the certificate at construction. X.509 is an extensible + // format, so different implementations may notice errors at different points. + c = null; + } + if (c != null) { + assertEquals(10, c.getBasicConstraints()); + } + } + }); + } + + @Test + public void testLargeKeyUsage() throws Exception { + ServiceTester tester = ServiceTester.test("CertificateFactory").withAlgorithm("X509"); + tester.run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + X509Certificate c = certificateFromPEM(p, LARGE_KEY_USAGE); + assertArrayEquals(new boolean[] {true, false, true, false, false, false, false, + false, false, false, false}, + c.getKeyUsage()); + } + }); + } + + @Test + public void testSigAlgParams() throws Exception { + ServiceTester tester = ServiceTester.test("CertificateFactory").withAlgorithm("X509"); + tester.run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + X509Certificate c = certificateFromPEM(p, SIGALG_NO_PARAMETER); + assertNull(c.getSigAlgParams()); + + c = certificateFromPEM(p, SIGALG_NULL_PARAMETER); + // Although documented to only return null when there are no parameters, the SUN + // provider also returns null when the algorithm uses an explicit parameter with a + // value of ASN.1 NULL. + if (c.getSigAlgParams() != null) { + assertArrayEquals(TestUtils.decodeHex("0500"), c.getSigAlgParams()); + } + + c = certificateFromPEM(p, SIGALG_STRING_PARAMETER); + assertArrayEquals(TestUtils.decodeHex("0c05706172616d"), c.getSigAlgParams()); + + c = certificateFromPEM(p, SIGALG_BOOLEAN_PARAMETER); + assertArrayEquals(TestUtils.decodeHex("0101ff"), c.getSigAlgParams()); + + c = certificateFromPEM(p, SIGALG_SEQUENCE_PARAMETER); + assertArrayEquals(TestUtils.decodeHex("3000"), c.getSigAlgParams()); + } + }); + } }
diff --git a/common/src/test/java/org/conscrypt/javax/crypto/CipherBasicsTest.java b/common/src/test/java/org/conscrypt/javax/crypto/CipherBasicsTest.java index 725f7a1..4aef5bb 100644 --- a/common/src/test/java/org/conscrypt/javax/crypto/CipherBasicsTest.java +++ b/common/src/test/java/org/conscrypt/javax/crypto/CipherBasicsTest.java
@@ -16,13 +16,11 @@ package org.conscrypt.javax.crypto; +import static org.conscrypt.TestUtils.decodeHex; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.nio.ByteBuffer; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -31,7 +29,6 @@ import java.security.Provider; import java.security.Security; import java.security.spec.AlgorithmParameterSpec; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -54,26 +51,26 @@ @RunWith(JUnit4.class) public final class CipherBasicsTest { - private static final Map<String, String> BASIC_CIPHER_TO_TEST_DATA = new HashMap<String, String>(); + private static final Map<String, String> BASIC_CIPHER_TO_TEST_DATA = new HashMap<>(); static { - BASIC_CIPHER_TO_TEST_DATA.put("AES/ECB/NoPadding", "/crypto/aes-ecb.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("AES/CBC/NoPadding", "/crypto/aes-cbc.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("AES/CFB8/NoPadding", "/crypto/aes-cfb8.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("AES/CFB128/NoPadding", "/crypto/aes-cfb128.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("AES/OFB/NoPadding", "/crypto/aes-ofb.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/ECB/NoPadding", "/crypto/desede-ecb.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CBC/NoPadding", "/crypto/desede-cbc.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CFB8/NoPadding", "/crypto/desede-cfb8.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CFB64/NoPadding", "/crypto/desede-cfb64.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/OFB/NoPadding", "/crypto/desede-ofb.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("ChaCha20", "/crypto/chacha20.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("AES/ECB/NoPadding", "crypto/aes-ecb.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("AES/CBC/NoPadding", "crypto/aes-cbc.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("AES/CFB8/NoPadding", "crypto/aes-cfb8.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("AES/CFB128/NoPadding", "crypto/aes-cfb128.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("AES/OFB/NoPadding", "crypto/aes-ofb.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/ECB/NoPadding", "crypto/desede-ecb.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CBC/NoPadding", "crypto/desede-cbc.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CFB8/NoPadding", "crypto/desede-cfb8.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CFB64/NoPadding", "crypto/desede-cfb64.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/OFB/NoPadding", "crypto/desede-ofb.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("ChaCha20", "crypto/chacha20.csv"); } - private static final Map<String, String> AEAD_CIPHER_TO_TEST_DATA = new HashMap<String, String>(); + private static final Map<String, String> AEAD_CIPHER_TO_TEST_DATA = new HashMap<>(); static { - AEAD_CIPHER_TO_TEST_DATA.put("AES/GCM/NoPadding", "/crypto/aes-gcm.csv"); - AEAD_CIPHER_TO_TEST_DATA.put("AES/GCM-SIV/NoPadding", "/crypto/aes-gcm-siv.csv"); - AEAD_CIPHER_TO_TEST_DATA.put("ChaCha20/Poly1305/NoPadding", "/crypto/chacha20-poly1305.csv"); + AEAD_CIPHER_TO_TEST_DATA.put("AES/GCM/NoPadding", "crypto/aes-gcm.csv"); + AEAD_CIPHER_TO_TEST_DATA.put("AES/GCM-SIV/NoPadding", "crypto/aes-gcm-siv.csv"); + AEAD_CIPHER_TO_TEST_DATA.put("ChaCha20/Poly1305/NoPadding", "crypto/chacha20-poly1305.csv"); } private static final int KEY_INDEX = 0; @@ -117,13 +114,13 @@ continue; } - List<String[]> data = readCsvResource(entry.getValue()); + List<String[]> data = TestUtils.readCsvResource(entry.getValue()); for (String[] line : data) { - Key key = new SecretKeySpec(toBytes(line[KEY_INDEX]), + Key key = new SecretKeySpec(decodeHex(line[KEY_INDEX]), getBaseAlgorithm(transformation)); - byte[] iv = toBytes(line[IV_INDEX]); - byte[] plaintext = toBytes(line[PLAINTEXT_INDEX]); - byte[] ciphertext = toBytes(line[CIPHERTEXT_INDEX]); + byte[] iv = decodeHex(line[IV_INDEX]); + byte[] plaintext = decodeHex(line[PLAINTEXT_INDEX]); + byte[] ciphertext = decodeHex(line[CIPHERTEXT_INDEX]); // Initialize the IV, if there is one AlgorithmParameters params; @@ -140,20 +137,20 @@ + ", algorithm " + transformation + " reported the wrong output size", ciphertext.length, cipher.getOutputSize(plaintext.length)); - assertTrue("Provider " + p.getName() - + ", algorithm " + transformation - + " failed on encryption, data is " + Arrays.toString(line), - Arrays.equals(ciphertext, cipher.doFinal(plaintext))); + assertArrayEquals("Provider " + p.getName() + + ", algorithm " + transformation + + " failed on encryption, data is " + Arrays.toString(line), + ciphertext, cipher.doFinal(plaintext)); cipher.init(Cipher.DECRYPT_MODE, key, params); assertEquals("Provider " + p.getName() + ", algorithm " + transformation + " reported the wrong output size", plaintext.length, cipher.getOutputSize(ciphertext.length)); - assertTrue("Provider " + p.getName() - + ", algorithm " + transformation - + " failed on decryption, data is " + Arrays.toString(line), - Arrays.equals(plaintext, cipher.doFinal(ciphertext))); + assertArrayEquals("Provider " + p.getName() + + ", algorithm " + transformation + + " failed on decryption, data is " + Arrays.toString(line), + plaintext, cipher.doFinal(ciphertext)); } catch (InvalidKeyException e) { // Some providers may not support raw SecretKeySpec keys, that's allowed } @@ -162,6 +159,39 @@ } } + public void arrayBasedAssessment(Cipher cipher, byte[] aad, byte[] tag, byte[] plaintext, + byte[] ciphertext, Key key, AlgorithmParameterSpec params, + String transformation, Provider p, String[] line) throws Exception { + cipher.init(Cipher.ENCRYPT_MODE, key, params); + if (aad.length > 0) { + cipher.updateAAD(aad); + } + byte[] combinedOutput = new byte[ciphertext.length + tag.length]; + assertEquals("Provider " + p.getName() + + ", algorithm " + transformation + + " reported the wrong output size", + combinedOutput.length, cipher.getOutputSize(plaintext.length)); + System.arraycopy(ciphertext, 0, combinedOutput, 0, ciphertext.length); + System.arraycopy(tag, 0, combinedOutput, ciphertext.length, tag.length); + assertArrayEquals("Provider " + p.getName() + + ", algorithm " + transformation + + " failed on encryption, data is " + Arrays.toString(line), + combinedOutput, cipher.doFinal(plaintext)); + + cipher.init(Cipher.DECRYPT_MODE, key, params); + if (aad.length > 0) { + cipher.updateAAD(aad); + } + assertEquals("Provider " + p.getName() + + ", algorithm " + transformation + + " reported the wrong output size", + plaintext.length, cipher.getOutputSize(combinedOutput.length)); + assertArrayEquals("Provider " + p.getName() + + ", algorithm " + transformation + + " failed on decryption, data is " + Arrays.toString(line), + plaintext, cipher.doFinal(combinedOutput)); + } + @Test public void testAeadEncryption() throws Exception { TestUtils.assumeAEADAvailable(); @@ -169,6 +199,13 @@ for (Map.Entry<String, String> entry : AEAD_CIPHER_TO_TEST_DATA.entrySet()) { String transformation = entry.getKey(); + // On Android 10 and below, BC can return AES/GCM/NoPadding when asked for + // AES/GCM-SIV/NoPadding. Android will never actually ship AES/GCM-SIV/NoPadding + // in BC, so skip that combination. + if (p.getName().equals("BC") && transformation.equals("AES/GCM-SIV/NoPadding")) { + continue; + } + Cipher cipher; try { cipher = Cipher.getInstance(transformation, p); @@ -177,15 +214,15 @@ continue; } - List<String[]> data = readCsvResource(entry.getValue()); + List<String[]> data = TestUtils.readCsvResource(entry.getValue()); for (String[] line : data) { - Key key = new SecretKeySpec(toBytes(line[KEY_INDEX]), + Key key = new SecretKeySpec(decodeHex(line[KEY_INDEX]), getBaseAlgorithm(transformation)); - byte[] iv = toBytes(line[IV_INDEX]); - byte[] plaintext = toBytes(line[PLAINTEXT_INDEX]); - byte[] ciphertext = toBytes(line[CIPHERTEXT_INDEX]); - byte[] tag = toBytes(line[TAG_INDEX]); - byte[] aad = toBytes(line[AAD_INDEX]); + byte[] iv = decodeHex(line[IV_INDEX]); + byte[] plaintext = decodeHex(line[PLAINTEXT_INDEX]); + byte[] ciphertext = decodeHex(line[CIPHERTEXT_INDEX]); + byte[] tag = decodeHex(line[TAG_INDEX]); + byte[] aad = decodeHex(line[AAD_INDEX]); // Some ChaCha20 tests include truncated tags, which the Java API doesn't // support. Skip those tests. @@ -201,34 +238,18 @@ } try { - cipher.init(Cipher.ENCRYPT_MODE, key, params); - if (aad.length > 0) { - cipher.updateAAD(aad); - } - byte[] combinedOutput = new byte[ciphertext.length + tag.length]; - assertEquals("Provider " + p.getName() - + ", algorithm " + transformation - + " reported the wrong output size", - combinedOutput.length, cipher.getOutputSize(plaintext.length)); - System.arraycopy(ciphertext, 0, combinedOutput, 0, ciphertext.length); - System.arraycopy(tag, 0, combinedOutput, ciphertext.length, tag.length); - assertTrue("Provider " + p.getName() - + ", algorithm " + transformation - + " failed on encryption, data is " + Arrays.toString(line), - Arrays.equals(combinedOutput, cipher.doFinal(plaintext))); - - cipher.init(Cipher.DECRYPT_MODE, key, params); - if (aad.length > 0) { - cipher.updateAAD(aad); - } - assertEquals("Provider " + p.getName() - + ", algorithm " + transformation - + " reported the wrong output size", - plaintext.length, cipher.getOutputSize(combinedOutput.length)); - assertTrue("Provider " + p.getName() - + ", algorithm " + transformation - + " failed on decryption, data is " + Arrays.toString(line), - Arrays.equals(plaintext, cipher.doFinal(combinedOutput))); + arrayBasedAssessment(cipher, aad, tag, plaintext, ciphertext, key, params, transformation, p, + line); + bufferBasedAssessment(cipher, aad, tag, plaintext, ciphertext, key, params, transformation, p, + false, false); + bufferBasedAssessment(cipher, aad, tag, plaintext, ciphertext, key, params, transformation, p, + true, true); + bufferBasedAssessment(cipher, aad, tag, plaintext, ciphertext, key, params, transformation, p, + true, false); + bufferBasedAssessment(cipher, aad, tag, plaintext, ciphertext, key, params, transformation, p, + false, true); + sharedBufferBasedAssessment(cipher, aad, tag, plaintext, ciphertext, key, params, + transformation, p); } catch (InvalidKeyException e) { // Some providers may not support raw SecretKeySpec keys, that's allowed } catch (InvalidAlgorithmParameterException e) { @@ -240,27 +261,131 @@ } } - private static List<String[]> readCsvResource(String resourceName) throws IOException { - InputStream stream = CipherBasicsTest.class.getResourceAsStream(resourceName); - List<String[]> lines = new ArrayList<String[]>(); - BufferedReader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(stream, "UTF-8")); - String line; - while ((line = reader.readLine()) != null) { - if (line.isEmpty() || line.startsWith("#")) { - continue; - } - lines.add(line.split(",", -1)); - } - } finally { - if (reader != null) { - reader.close(); - } + public void sharedBufferBasedAssessment(Cipher cipher, byte[] aad, byte[] tag, byte[] _plaintext, + byte[] _ciphertext, Key key, AlgorithmParameterSpec params, + String transformation, Provider p) throws Exception { + cipher.init(Cipher.ENCRYPT_MODE, key, params); + if (aad.length > 0) { + cipher.updateAAD(aad); } - return lines; + byte[] _combinedOutput = new byte[_ciphertext.length + tag.length]; + byte[] _commonBacking = new byte[_plaintext.length + _combinedOutput.length]; + + assertEquals("Provider " + p.getName() + + ", algorithm " + transformation + + " reported the wrong output size", + _combinedOutput.length, cipher.getOutputSize(_plaintext.length)); + System.arraycopy(_ciphertext, 0, _combinedOutput, 0, _ciphertext.length); + System.arraycopy(tag, 0, _combinedOutput, _ciphertext.length, tag.length); + System.arraycopy(_plaintext, 0, _commonBacking, 0, _plaintext.length); + System.arraycopy(_combinedOutput, 0, _commonBacking, _plaintext.length, _combinedOutput.length); + ByteBuffer combinedOutput = ByteBuffer.wrap(_commonBacking); + ByteBuffer plaintext = combinedOutput.slice(); + plaintext.limit(_plaintext.length); + combinedOutput.position(_plaintext.length); + // both byte buffers have been created from common backed array and have correct respecting positions and limits + + combinedOutput.position(combinedOutput.limit()); + ByteBuffer outputbuffer = ByteBuffer.allocate(cipher.getOutputSize(plaintext.remaining())); + + cipher.doFinal(plaintext, outputbuffer); + assertEquals("Cipher doFinal did not encrypt correctly", combinedOutput, outputbuffer); + assertEquals(" input was not shifted", plaintext.position(), plaintext.limit()); + + cipher.init(Cipher.DECRYPT_MODE, key, params); + if (aad.length > 0) { + cipher.updateAAD(aad); + } + assertEquals("Provider " + p.getName() + + ", algorithm " + transformation + + " reported the wrong output size", + _plaintext.length, cipher.getOutputSize(_combinedOutput.length)); + combinedOutput.position(_plaintext.length); + + outputbuffer = ByteBuffer.allocate(cipher.getOutputSize(combinedOutput.remaining())); + + combinedOutput.position(_plaintext.length); + plaintext.position(plaintext.limit()); + cipher.doFinal(combinedOutput, outputbuffer); + assertEquals("Cipher doFinal did not decrypt correctly", plaintext, outputbuffer); + assertEquals(" input was not shifted", combinedOutput.position(), combinedOutput.limit()); } + public void bufferBasedAssessment(Cipher cipher, byte[] aad, byte[] tag, byte[] _plaintext, + byte[] _ciphertext, Key key, AlgorithmParameterSpec params, + String transformation, Provider p, boolean inBoolDirect, boolean outBoolDirect) throws Exception { + cipher.init(Cipher.ENCRYPT_MODE, key, params); + if (aad.length > 0) { + cipher.updateAAD(aad); + } + byte[] _combinedOutput = new byte[_ciphertext.length + tag.length]; + ByteBuffer plaintext = ByteBuffer.wrap(_plaintext); + if (inBoolDirect) { + ByteBuffer plaintext_ = plaintext; + int incap = plaintext_.remaining(); + plaintext = ByteBuffer.allocateDirect(incap); + plaintext.mark(); + plaintext.put(plaintext_); + plaintext.reset(); + } + + assertEquals("Provider " + p.getName() + + ", algorithm " + transformation + + " reported the wrong output size", + _combinedOutput.length, cipher.getOutputSize(_plaintext.length)); + System.arraycopy(_ciphertext, 0, _combinedOutput, 0, _ciphertext.length); + System.arraycopy(tag, 0, _combinedOutput, _ciphertext.length, tag.length); + + ByteBuffer combinedOutput = ByteBuffer.wrap(_combinedOutput); + if (outBoolDirect) { + ByteBuffer combinedOutput_ = combinedOutput; + int outcap = combinedOutput_.remaining(); + combinedOutput = ByteBuffer.allocateDirect(outcap); + combinedOutput.mark(); + combinedOutput.put(combinedOutput_); + } + combinedOutput.position(combinedOutput.limit()); + ByteBuffer outputbuffer; + if (outBoolDirect) { + outputbuffer = ByteBuffer.allocateDirect(cipher.getOutputSize(plaintext.remaining())); + } else { + outputbuffer = ByteBuffer.allocate(cipher.getOutputSize(plaintext.remaining())); + } + + cipher.doFinal(plaintext, outputbuffer); + assertEquals("Cipher doFinal did not encrypt correctly", combinedOutput, outputbuffer); + assertEquals(" input was not shifted", plaintext.position(), plaintext.limit()); + + cipher.init(Cipher.DECRYPT_MODE, key, params); + if (aad.length > 0) { + cipher.updateAAD(aad); + } + assertEquals("Provider " + p.getName() + + ", algorithm " + transformation + + " reported the wrong output size", + _plaintext.length, cipher.getOutputSize(_combinedOutput.length)); + combinedOutput = ByteBuffer.wrap(_combinedOutput); + if (inBoolDirect) { + ByteBuffer combinedOutput_ = combinedOutput; + int incap = combinedOutput_.remaining(); + combinedOutput = ByteBuffer.allocateDirect(incap); + combinedOutput.mark(); + combinedOutput.put(combinedOutput_); + combinedOutput.reset(); + } + if (outBoolDirect) { + outputbuffer = ByteBuffer.allocateDirect(cipher.getOutputSize(combinedOutput.remaining())); + } else { + outputbuffer = ByteBuffer.allocate(cipher.getOutputSize(combinedOutput.remaining())); + } + combinedOutput.position(0); + plaintext.position(plaintext.limit()); + cipher.doFinal(combinedOutput, outputbuffer); + assertEquals("Cipher doFinal did not decrypt correctly", plaintext, outputbuffer); + assertEquals(" input was not shifted", combinedOutput.position(), combinedOutput.limit()); + } + + /** * Returns the underlying cipher name given a cipher transformation. For example, * passing {@code AES/CBC/NoPadding} returns {@code AES}. @@ -272,7 +397,92 @@ return transformation; } - private static byte[] toBytes(String hex) { - return TestUtils.decodeHex(hex, /* allowSingleChar= */ true); + /** + * Encryption with ByteBuffers should be copy-safe even if the buffers have different starting + * offsets and/or do not make the backing array visible. + * + * <p>Note that bugs in this often require a sizeable input to reproduce; the default + * implementation of engineUpdate(ByteBuffer, ByteBuffer) copies through 4KB bounce buffers, so we + * need to use something larger to see any problems - 8KB is what we use here. + * + * @see https://bugs.openjdk.java.net/browse/JDK-8181386 + */ + @Test + public void testByteBufferShiftedAlias() throws Exception { + byte[] ptVector = new byte[8192]; + + for (int i = 0; i < 3; i++) { + // outputOffset = offset relative to start of input. + for (int outputOffset = -1; outputOffset <= 1; outputOffset++) { + + SecretKeySpec key = new SecretKeySpec(new byte[16], "AES"); + GCMParameterSpec parameters = new GCMParameterSpec(128, new byte[12]); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, key, parameters); + + ByteBuffer output, input, inputRO; + + // We'll try three scenarios: Ordinary array backed buffers, array backed buffers where one + // is read-only, and direct byte buffers. + String mode; + // offsets relative to start of buffer + int inputOffsetInBuffer = 1; + int outputOffsetInBuffer = inputOffsetInBuffer + outputOffset; + int sliceLength = cipher.getOutputSize(ptVector.length); + int bufferSize = sliceLength + Math.max(inputOffsetInBuffer, outputOffsetInBuffer); + + mode = "direct buffers"; + ByteBuffer buf = ByteBuffer.allocateDirect(bufferSize); + output = buf.duplicate(); + output.position(outputOffsetInBuffer); + output.limit(sliceLength + outputOffsetInBuffer); + output = output.slice(); + + input = buf.duplicate(); + input.position(inputOffsetInBuffer); + input.limit(sliceLength + inputOffsetInBuffer); + input = input.slice(); + + inputRO = input.duplicate(); + + // Now that we have our overlapping 'input' and 'output' buffers, we can write our plaintext + // into the input buffer. + input.put(ptVector); + input.flip(); + // Make sure the RO input buffer has the same limit in case the plaintext is shorter than + // sliceLength (which it generally will be for anything other than ECB or CTR mode) + inputRO.limit(input.limit()); + + try { + int ctSize = cipher.doFinal(inputRO, output); + + // Now flip the buffers around and undo everything + byte[] tmp = new byte[ctSize]; + output.flip(); + output.get(tmp); + + output.clear(); + input.clear(); + inputRO.clear(); + + input.put(tmp); + input.flip(); + inputRO.limit(input.limit()); + + cipher.init(Cipher.DECRYPT_MODE, key, parameters); + cipher.doFinal(inputRO, output); + + output.flip(); + assertEquals(ByteBuffer.wrap(ptVector), output); + } catch (Throwable t) { + throw new AssertionError( + "Overlapping buffers test failed with buffer type: " + + mode + + " and output offset " + + outputOffset, + t); + } + } + } } }
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 65c7d15..8f4f896 100644 --- a/common/src/test/java/org/conscrypt/javax/crypto/CipherTest.java +++ b/common/src/test/java/org/conscrypt/javax/crypto/CipherTest.java
@@ -70,6 +70,7 @@ import javax.crypto.spec.PSource; import javax.crypto.spec.SecretKeySpec; import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; +import libcore.test.annotation.NonCts; import org.bouncycastle.asn1.x509.KeyUsage; import org.conscrypt.Conscrypt; import org.conscrypt.TestUtils; @@ -378,18 +379,14 @@ setExpectedBlockSize("AES/OFB/PKCS7PADDING", 16); setExpectedBlockSize("AES/OFB/NOPADDING", 16); setExpectedBlockSize("AES_128/CBC/PKCS5PADDING", 16); - setExpectedBlockSize("AES_128/CBC/PKCS7PADDING", 16); setExpectedBlockSize("AES_128/CBC/NOPADDING", 16); setExpectedBlockSize("AES_128/ECB/PKCS5PADDING", 16); - setExpectedBlockSize("AES_128/ECB/PKCS7PADDING", 16); setExpectedBlockSize("AES_128/ECB/NOPADDING", 16); setExpectedBlockSize("AES_128/GCM/NOPADDING", 16); setExpectedBlockSize("AES_128/GCM-SIV/NOPADDING", 16); setExpectedBlockSize("AES_256/CBC/PKCS5PADDING", 16); - setExpectedBlockSize("AES_256/CBC/PKCS7PADDING", 16); setExpectedBlockSize("AES_256/CBC/NOPADDING", 16); setExpectedBlockSize("AES_256/ECB/PKCS5PADDING", 16); - setExpectedBlockSize("AES_256/ECB/PKCS7PADDING", 16); setExpectedBlockSize("AES_256/ECB/NOPADDING", 16); setExpectedBlockSize("AES_256/GCM/NOPADDING", 16); setExpectedBlockSize("AES_256/GCM-SIV/NOPADDING", 16); @@ -410,7 +407,6 @@ } setExpectedBlockSize("ARC4", 0); - setExpectedBlockSize("ARCFOUR", 0); setExpectedBlockSize("CHACHA20", 0); setExpectedBlockSize("CHACHA20/POLY1305/NOPADDING", 0); setExpectedBlockSize("PBEWITHSHAAND40BITRC4", 0); @@ -424,27 +420,9 @@ setExpectedBlockSize("DESEDE", 8); setExpectedBlockSize("DESEDE/CBC/PKCS5PADDING", 8); - setExpectedBlockSize("DESEDE/CBC/PKCS7PADDING", 8); setExpectedBlockSize("DESEDE/CBC/NOPADDING", 8); - setExpectedBlockSize("DESEDE/CFB/PKCS5PADDING", 8); - setExpectedBlockSize("DESEDE/CFB/PKCS7PADDING", 8); - setExpectedBlockSize("DESEDE/CFB/NOPADDING", 8); - setExpectedBlockSize("DESEDE/CTR/PKCS5PADDING", 8); - setExpectedBlockSize("DESEDE/CTR/PKCS7PADDING", 8); - setExpectedBlockSize("DESEDE/CTR/NOPADDING", 8); - setExpectedBlockSize("DESEDE/CTS/PKCS5PADDING", 8); - setExpectedBlockSize("DESEDE/CTS/PKCS7PADDING", 8); - setExpectedBlockSize("DESEDE/CTS/NOPADDING", 8); - setExpectedBlockSize("DESEDE/ECB/PKCS5PADDING", 8); - setExpectedBlockSize("DESEDE/ECB/PKCS7PADDING", 8); - setExpectedBlockSize("DESEDE/ECB/NOPADDING", 8); - setExpectedBlockSize("DESEDE/OFB/PKCS5PADDING", 8); - setExpectedBlockSize("DESEDE/OFB/PKCS7PADDING", 8); - setExpectedBlockSize("DESEDE/OFB/NOPADDING", 8); setExpectedBlockSize("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", 8); setExpectedBlockSize("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", 8); - setExpectedBlockSize("PBEWITHMD5ANDTRIPLEDES", 8); - setExpectedBlockSize("PBEWITHSHA1ANDDESEDE", 8); if (StandardNames.IS_RI) { @@ -2152,7 +2130,7 @@ /* * echo -n 'This is a test of OAEP' | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_Plaintext = new byte[] { + private static final byte[] RSA_Vector2_Plaintext = new byte[] { (byte) 0x54, (byte) 0x68, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x61, (byte) 0x20, (byte) 0x74, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x6f, (byte) 0x66, (byte) 0x20, @@ -2164,7 +2142,7 @@ * -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha1 -pkeyopt rsa_mgf1_md:sha1 \ * | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA1_MGF1_SHA1 = new byte[] { + private static final byte[] RSA_Vector2_OAEP_SHA1_MGF1_SHA1 = new byte[] { (byte) 0x53, (byte) 0x71, (byte) 0x84, (byte) 0x2e, (byte) 0x01, (byte) 0x74, (byte) 0x82, (byte) 0xb3, (byte) 0x01, (byte) 0xac, (byte) 0x2b, (byte) 0xbd, (byte) 0x40, (byte) 0xa7, (byte) 0x5b, (byte) 0x60, (byte) 0xf1, (byte) 0xde, @@ -2213,7 +2191,7 @@ /* * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey rsakey.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha1 | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA1 = new byte[] { + private static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA1 = new byte[] { (byte) 0x25, (byte) 0x9f, (byte) 0xc3, (byte) 0x69, (byte) 0xbc, (byte) 0x3f, (byte) 0xe7, (byte) 0x9e, (byte) 0x76, (byte) 0xef, (byte) 0x6c, (byte) 0xd2, (byte) 0x2b, (byte) 0x7b, (byte) 0xf0, (byte) 0xeb, (byte) 0xc2, (byte) 0x28, @@ -2262,7 +2240,7 @@ /* * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha1 -pkeyopt rsa_oaep_label:010203FFA00A | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA1_LABEL = new byte[] { + private static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA1_LABEL = new byte[] { (byte) 0x80, (byte) 0xb1, (byte) 0xf2, (byte) 0xc2, (byte) 0x03, (byte) 0xc5, (byte) 0xdf, (byte) 0xbd, (byte) 0xed, (byte) 0xfe, (byte) 0xe6, (byte) 0xff, (byte) 0xd3, (byte) 0x38, (byte) 0x1e, (byte) 0x6d, (byte) 0xae, (byte) 0x47, @@ -2313,7 +2291,7 @@ * -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha224 -pkeyopt rsa_mgf1_md:sha224 \ * | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA224_MGF1_SHA224 = new byte[] { + private static final byte[] RSA_Vector2_OAEP_SHA224_MGF1_SHA224 = new byte[] { (byte) 0xae, (byte) 0xdd, (byte) 0xe6, (byte) 0xab, (byte) 0x00, (byte) 0xd6, (byte) 0x1e, (byte) 0x7e, (byte) 0x85, (byte) 0x63, (byte) 0xab, (byte) 0x51, (byte) 0x79, (byte) 0x92, (byte) 0xf1, (byte) 0xb9, (byte) 0x4f, (byte) 0x23, @@ -2364,7 +2342,7 @@ * -pkeyopt rsa_padding_mode:oaep -pkey rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 \ * | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA256 = new byte[] { + private static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA256 = new byte[] { (byte) 0x6a, (byte) 0x2b, (byte) 0xb2, (byte) 0xa3, (byte) 0x26, (byte) 0xa6, (byte) 0x7a, (byte) 0x4a, (byte) 0x1f, (byte) 0xe5, (byte) 0xc8, (byte) 0x94, (byte) 0x11, (byte) 0x1a, (byte) 0x92, (byte) 0x07, (byte) 0x0a, (byte) 0xf4, @@ -2415,7 +2393,7 @@ * -pkeyopt rsa_padding_mode:oaep -pkey rsa_oaep_md:sha384 -pkeyopt rsa_mgf1_md:sha384 \ * | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA384_MGF1_SHA384 = new byte[] { + private static final byte[] RSA_Vector2_OAEP_SHA384_MGF1_SHA384 = new byte[] { (byte) 0xa1, (byte) 0xb3, (byte) 0x3b, (byte) 0x34, (byte) 0x69, (byte) 0x9e, (byte) 0xd8, (byte) 0xa0, (byte) 0x37, (byte) 0x2c, (byte) 0xeb, (byte) 0xef, (byte) 0xf2, (byte) 0xaf, (byte) 0xfa, (byte) 0x63, (byte) 0x5d, (byte) 0x88, @@ -2466,7 +2444,7 @@ * -pkeyopt rsa_padding_mode:oaep -pkey rsa_oaep_md:sha512 -pkeyopt rsa_mgf1_md:sha512 \ * | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA512_MGF1_SHA512 = new byte[] { + private static final byte[] RSA_Vector2_OAEP_SHA512_MGF1_SHA512 = new byte[] { (byte) 0x75, (byte) 0x0f, (byte) 0xf9, (byte) 0x21, (byte) 0xca, (byte) 0xcc, (byte) 0x0e, (byte) 0x13, (byte) 0x9e, (byte) 0x38, (byte) 0xa4, (byte) 0xa7, (byte) 0xee, (byte) 0x61, (byte) 0x6d, (byte) 0x56, (byte) 0xea, (byte) 0x36, @@ -2515,7 +2493,7 @@ /* * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha512 -pkeyopt rsa_mgf1_md:sha512 -pkeyopt rsa_oaep_label:010203FFA00A | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA512_MGF1_SHA512_LABEL = new byte[] { + private static final byte[] RSA_Vector2_OAEP_SHA512_MGF1_SHA512_LABEL = new byte[] { (byte) 0x31, (byte) 0x3b, (byte) 0x23, (byte) 0xcf, (byte) 0x40, (byte) 0xfe, (byte) 0x15, (byte) 0x94, (byte) 0xd6, (byte) 0x81, (byte) 0x21, (byte) 0x69, (byte) 0x8e, (byte) 0x58, (byte) 0xd5, (byte) 0x0f, (byte) 0xa8, (byte) 0x72, @@ -4668,6 +4646,8 @@ * TODO(27995180): consider whether we keep this compatibility. Consider whether we only allow * if an IV is passed in the parameters. */ + @NonCts(bug = 287231726, reason = "The test asserts buggy or non-breaking " + + "behaviors, but the behavior has been fixed in the future ART module version.") @Test public void test_PBKDF2WITHHMACSHA1_SKFactory_and_PBEAESCBC_Cipher_noIV() throws Exception { Assume.assumeNotNull(Security.getProvider("BC"));
diff --git a/common/src/test/java/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java b/common/src/test/java/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java index b4df031..6464ba9 100644 --- a/common/src/test/java/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java +++ b/common/src/test/java/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java
@@ -463,6 +463,19 @@ if (providers == null) { return new Provider[0]; } + + // Do not test AndroidKeyStore as KeyAgreement provider. It only handles Android + // Keystore-backed keys. It's OKish not to test AndroidKeyStore here because it's tested by + // cts/tests/test/keystore. + List<Provider> filteredProvidersList = new ArrayList<Provider>(providers.length); + for (Provider provider : providers) { + if ("AndroidKeyStore".equals(provider.getName())) { + continue; + } + filteredProvidersList.add(provider); + } + providers = filteredProvidersList.toArray(new Provider[filteredProvidersList.size()]); + // Sort providers by name to guarantee deterministic order in which providers are used in // the tests. return sortByName(providers);
diff --git a/common/src/test/java/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java b/common/src/test/java/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java new file mode 100644 index 0000000..4cd0843 --- /dev/null +++ b/common/src/test/java/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java
@@ -0,0 +1,102 @@ +package org.conscrypt.javax.crypto; + +import static org.junit.Assert.assertArrayEquals; + +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Security; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import javax.crypto.KeyAgreement; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + + +/** + * Tests for all registered X25519 and X448 {@link KeyAgreement} providers. + */ +@RunWith(JUnit4.class) +public class XDHKeyAgreementTest { + private static final byte[] RFC_7748_X25519_OUR_PRIV_KEY = new byte[] { + (byte) 0x30, (byte) 0x2e, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x05, (byte) 0x06, + (byte) 0x03, (byte) 0x2b, (byte) 0x65, (byte) 0x6e, (byte) 0x04, (byte) 0x22, (byte) 0x04, (byte) 0x20, + (byte) 0xa5, (byte) 0x46, (byte) 0xe3, (byte) 0x6b, (byte) 0xf0, (byte) 0x52, (byte) 0x7c, (byte) 0x9d, + (byte) 0x3b, (byte) 0x16, (byte) 0x15, (byte) 0x4b, (byte) 0x82, (byte) 0x46, (byte) 0x5e, (byte) 0xdd, + (byte) 0x62, (byte) 0x14, (byte) 0x4c, (byte) 0x0a, (byte) 0xc1, (byte) 0xfc, (byte) 0x5a, (byte) 0x18, + (byte) 0x50, (byte) 0x6a, (byte) 0x22, (byte) 0x44, (byte) 0xba, (byte) 0x44, (byte) 0x9a, (byte) 0xc4, + }; + + // Broken key for testing with JDK 11. Instead of wrapping OCTET STRING with OCTET STRING. + private static final byte[] RFC_7748_X25519_OUR_PRIV_KEY_BROKEN = new byte[] { + (byte) 0x30, (byte) 0x2c, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x05, (byte) 0x06, + (byte) 0x03, (byte) 0x2b, (byte) 0x65, (byte) 0x6e, (byte) 0x04, (byte) 0x20, + (byte) 0xa5, (byte) 0x46, (byte) 0xe3, (byte) 0x6b, (byte) 0xf0, (byte) 0x52, (byte) 0x7c, (byte) 0x9d, + (byte) 0x3b, (byte) 0x16, (byte) 0x15, (byte) 0x4b, (byte) 0x82, (byte) 0x46, (byte) 0x5e, (byte) 0xdd, + (byte) 0x62, (byte) 0x14, (byte) 0x4c, (byte) 0x0a, (byte) 0xc1, (byte) 0xfc, (byte) 0x5a, (byte) 0x18, + (byte) 0x50, (byte) 0x6a, (byte) 0x22, (byte) 0x44, (byte) 0xba, (byte) 0x44, (byte) 0x9a, (byte) 0xc4, + }; + + private static final byte[] RFC_7748_X25519_THEIR_PUB_KEY = new byte[] { + (byte) 0x30, (byte) 0x2a, (byte) 0x30, (byte) 0x05, (byte) 0x06, (byte) 0x03, (byte) 0x2b, (byte) 0x65, + (byte) 0x6e, (byte) 0x03, (byte) 0x21, (byte) 0x00, (byte) 0xe6, (byte) 0xdb, (byte) 0x68, (byte) 0x67, + (byte) 0x58, (byte) 0x30, (byte) 0x30, (byte) 0xdb, (byte) 0x35, (byte) 0x94, (byte) 0xc1, (byte) 0xa4, + (byte) 0x24, (byte) 0xb1, (byte) 0x5f, (byte) 0x7c, (byte) 0x72, (byte) 0x66, (byte) 0x24, (byte) 0xec, + (byte) 0x26, (byte) 0xb3, (byte) 0x35, (byte) 0x3b, (byte) 0x10, (byte) 0xa9, (byte) 0x03, (byte) 0xa6, + (byte) 0xd0, (byte) 0xab, (byte) 0x1c, (byte) 0x4c, + }; + + private static final byte[] RFC_7748_X25519_SECRET = new byte[] { + (byte) 0xc3, (byte) 0xda, (byte) 0x55, (byte) 0x37, (byte) 0x9d, (byte) 0xe9, (byte) 0xc6, (byte) 0x90, + (byte) 0x8e, (byte) 0x94, (byte) 0xea, (byte) 0x4d, (byte) 0xf2, (byte) 0x8d, (byte) 0x08, (byte) 0x4f, + (byte) 0x32, (byte) 0xec, (byte) 0xcf, (byte) 0x03, (byte) 0x49, (byte) 0x1c, (byte) 0x71, (byte) 0xf7, + (byte) 0x54, (byte) 0xb4, (byte) 0x07, (byte) 0x55, (byte) 0x77, (byte) 0xa2, (byte) 0x85, (byte) 0x52, + }; + + private PrivateKey rfc7748X25519PrivateKey; + private PublicKey rfc7748X25519PublicKey; + + private void setupKeys(Provider p) throws Exception { + KeyFactory kf = KeyFactory.getInstance("XDH", p); + + byte[] privateKey; + if ("SunEC".equalsIgnoreCase(p.getName()) + && "11".equals(System.getProperty("java.specification.version"))) { + // SunEC in OpenJDK 11 has a bug where the format specified in RFC 8410 + // Section 7. It uses a single OCTET STRING to represent the key instead + // of an OCTET STRING inside of an OCTET STRING as defined in the RFC: + // ("For the keys defined in this document, the private key is always an + // opaque byte sequence. The ASN.1 type CurvePrivateKey is defined in + // this document to hold the byte sequence. Thus, when encoding a + // OneAsymmetricKey object, the private key is wrapped in a + // CurvePrivateKey object and wrapped by the OCTET STRING of the + // "privateKey" field.") + privateKey = RFC_7748_X25519_OUR_PRIV_KEY_BROKEN; + } else { + privateKey = RFC_7748_X25519_OUR_PRIV_KEY; + } + + rfc7748X25519PrivateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(privateKey)); + rfc7748X25519PublicKey = kf.generatePublic(new X509EncodedKeySpec(RFC_7748_X25519_THEIR_PUB_KEY)); + } + + @Test + public void test_XDHKeyAgreement() throws Exception { + for (Provider p : Security.getProviders("KeyAgreement.XDH")) { + setupKeys(p); + + KeyAgreement ka = KeyAgreement.getInstance("XDH", p); + + test_x25519_keyAgreement_rfc7748_kat_success(ka); + } + } + + private void test_x25519_keyAgreement_rfc7748_kat_success(KeyAgreement ka) throws Exception { + ka.init(rfc7748X25519PrivateKey); + ka.doPhase(rfc7748X25519PublicKey, true); + + assertArrayEquals(RFC_7748_X25519_SECRET, ka.generateSecret()); + } +}
diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLContextTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLContextTest.java index 29c0446..2c68c6f 100644 --- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLContextTest.java +++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLContextTest.java
@@ -16,6 +16,7 @@ package org.conscrypt.javax.net.ssl; +import static org.conscrypt.TestUtils.isWindows; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; @@ -37,6 +38,7 @@ import java.security.UnrecoverableKeyException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -664,14 +666,21 @@ } private static void assertContentsInOrder(List<String> expected, String... actual) { + List<String> actualList = Arrays.asList(actual); if (expected.size() != actual.length) { fail("Unexpected length. Expected len <" + expected.size() + ">, actual len <" + actual.length + ">, expected <" + expected + ">, actual <" - + Arrays.asList(actual) + ">"); + + actualList + ">"); } - if (!expected.equals(Arrays.asList(actual))) { - fail("Unexpected element(s). Expected <" + expected + ">, actual <" - + Arrays.asList(actual) + ">"); + + if (isWindows()) { + // TODO(prbprbprb): CpuFeatures.isAESHardwareAccelerated is not reliable on windows + Collections.sort(actualList); + Collections.sort(expected); + } + + if (!expected.equals(actualList)) { + fail("Unexpected element(s). Expected <" + expected + ">, actual <" + actualList + ">"); } } @@ -711,7 +720,7 @@ } if (version[0] == 1) { - assert version[1] >= 6; + assertTrue(version[1] >= 6); return version[1]; } else { return version[0];
diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java index 825cfb8..bb81f3c 100644 --- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java +++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java
@@ -17,6 +17,7 @@ package org.conscrypt.javax.net.ssl; import static org.conscrypt.TestUtils.UTF_8; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -27,6 +28,7 @@ import java.io.IOException; import java.net.Socket; import java.nio.ByteBuffer; +import java.nio.ReadOnlyBufferException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Arrays; @@ -48,6 +50,7 @@ import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; import org.conscrypt.TestUtils; +import org.conscrypt.TestUtils.BufferType; import org.conscrypt.java.security.StandardNames; import org.conscrypt.java.security.TestKeyStore; import org.junit.Test; @@ -782,7 +785,8 @@ final boolean serverClientMode, final boolean[] finished) throws Exception { TestSSLContext c; if (!clientClientMode && serverClientMode) { - c = TestSSLContext.create(TestKeyStore.getServer(), TestKeyStore.getClient()); + c = TestSSLContext.create(/* client= */ TestKeyStore.getServer(), + /* server= */ TestKeyStore.getClient()); } else { c = TestSSLContext.create(); } @@ -918,6 +922,187 @@ c.close(); } + @Test + public void wrapPreconditions() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate(10); + ByteBuffer[] buffers = new ByteBuffer[] { buffer, buffer, buffer }; + ByteBuffer[] badBuffers = new ByteBuffer[] { buffer, buffer, null, buffer }; + + // Client/server mode not set => IllegalStateException + try { + newUnconnectedEngine().wrap(buffer, buffer); + fail(); + } catch (IllegalStateException e) { + // Expected + } + + try { + newUnconnectedEngine().wrap(buffers, buffer); + fail(); + } catch (IllegalStateException e) { + // Expected + } + + try { + newUnconnectedEngine().wrap(buffers, 0, 1, buffer); + fail(); + } catch (IllegalStateException e) { + // Expected + } + + // Read-only destination => ReadOnlyBufferException + try { + newConnectedEngine().wrap(buffer, buffer.asReadOnlyBuffer()); + fail(); + } catch (ReadOnlyBufferException e) { + // Expected + } + + try { + newConnectedEngine().wrap(buffers, buffer.asReadOnlyBuffer()); + fail(); + } catch (ReadOnlyBufferException e) { + // Expected + } + + try { + newConnectedEngine().wrap(buffers, 0, 1, buffer.asReadOnlyBuffer()); + fail(); + } catch (ReadOnlyBufferException e) { + // Expected + } + + // Null destination => IllegalArgumentException + try { + newConnectedEngine().wrap(buffer, null); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + newConnectedEngine().wrap(buffers, null); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + newConnectedEngine().wrap(buffers, 0, 1, null); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + // Null source => IllegalArgumentException + try { + newConnectedEngine().wrap((ByteBuffer) null, buffer); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + newConnectedEngine().wrap((ByteBuffer[]) null, buffer); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + newConnectedEngine().wrap(null, 0, 1, buffer); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + // Null entries in buffer array => IllegalArgumentException + try { + newConnectedEngine().wrap(badBuffers, buffer); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + newConnectedEngine().wrap(badBuffers, 0, badBuffers.length, buffer); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + // Bad offset or length => IndexOutOfBoundsException + try { + newConnectedEngine().wrap(buffers, 0, 7, buffer); + fail(); + } catch (IndexOutOfBoundsException e) { + // Expected + } + } + + @Test + public void bufferArrayOffsets() throws Exception{ + TestSSLEnginePair pair = TestSSLEnginePair.create(); + ByteBuffer tlsBuffer = ByteBuffer.allocate(600); + int bufferSize = 100; + + for (BufferType bufferType : BufferType.values()) { + ByteBuffer[] sourceBuffers = bufferType.newRandomBuffers( + bufferSize, bufferSize, bufferSize, bufferSize, bufferSize); + for (int offset = 0; offset < sourceBuffers.length; offset++) { + for (int length = 1; length < sourceBuffers.length - offset; length++) { + // Reset source buffers + for (ByteBuffer buffer : sourceBuffers) { + if (buffer.remaining() == 0) { + buffer.flip(); + } + assertEquals(bufferSize, buffer.remaining()); + } + // Make an array copy of what we expect to send + byte[] sourceBytes = copyDataFromBuffers(sourceBuffers, offset, length); + byte[] destinationBytes = new byte[sourceBytes.length]; + ByteBuffer destination = ByteBuffer.wrap(destinationBytes); + // Send and compare + tlsBuffer.clear(); + pair.client.wrap(sourceBuffers, offset, length, tlsBuffer); + tlsBuffer.flip(); + pair.server.unwrap(tlsBuffer, destination); + assertArrayEquals(sourceBytes, destinationBytes); + } + } + } + } + + private byte[] copyDataFromBuffers(ByteBuffer[] buffers, int offset, int length) { + // NB avoids using Arrays.copyOfRange() to prevent any common bugs with + // ConscryptEngine.wrap(). + int size = 0; + for (int i = offset; i < offset + length; i++) { + size += buffers[i].remaining(); + } + byte[] data = new byte[size]; + int dataOffset = 0; + for (int i = offset; i < offset + length; i++) { + ByteBuffer buffer = buffers[i]; + int remaining = buffer.remaining(); + buffer.get(data, dataOffset, remaining); + buffer.flip(); + dataOffset += remaining; + } + return data; + } + + private SSLEngine newUnconnectedEngine() { + TestSSLContext context = TestSSLContext.create(); + return context.clientContext.createSSLEngine(); + } + + private SSLEngine newConnectedEngine() throws Exception { + TestSSLEnginePair pair = TestSSLEnginePair.create(); + assertConnected(pair); + return pair.client; + } + private void assertConnected(TestSSLEnginePair e) { assertConnected(e.client, e.server); }
diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineVersionCompatibilityTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineVersionCompatibilityTest.java index 1b84815..d4cdd2a 100644 --- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineVersionCompatibilityTest.java +++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineVersionCompatibilityTest.java
@@ -37,6 +37,7 @@ import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collections; +import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; @@ -755,7 +756,7 @@ }); fail(); } catch (SSLHandshakeException e) { - assertEquals(e.getMessage(), "SNI match failed: any.host"); + assertEquals("SNI match failed: any.host", e.getMessage()); } } @@ -806,6 +807,104 @@ assertTrue(serverAliasCalled.get()); } + // Splits a ByteArray into an array of ByteBuffers each no bigger than the specified size. + private ByteBuffer[] splitDataIntoBuffers(byte[] sourceData, int size) { + int nbuf = ((sourceData.length - 1) / size) + 1; + ByteBuffer[] buffers = new ByteBuffer[nbuf]; + int buffer = 0; + for (int offset = 0; offset < sourceData.length; offset += size, buffer++) { + buffers[buffer] = ByteBuffer.allocate(size); + int remaining = sourceData.length - offset; + buffers[buffer].put(sourceData, offset, Math.min( remaining, size)); + buffers[buffer].flip(); + } + return buffers; + } + + // Sends dataSize bytes of application data, split into an array of ByteBuffers + // of size bufferSize and verifies it arrives intact. If offset is non-zero then + // additional invalid buffers will be added to the start and end of the buffer array + // in order to test the offset and length arguments of wrap(). + private void sendAppDataInMultipleBuffers( + SSLEngine src, SSLEngine dst, int dataSize, int bufferSize) + throws SSLException { + + // Generate random data and split into multiple. + byte[] sourceData = new byte[dataSize]; + Random random = new Random(System.currentTimeMillis()); + random.nextBytes(sourceData); + ByteBuffer[] sourceBuffers = splitDataIntoBuffers(sourceData, bufferSize); + int length = sourceBuffers.length; + + // Ensure there is no pending outbound data or encrypted data and handshaking is complete. + ByteBuffer tlsBuffer = ByteBuffer.allocateDirect(src.getSession().getPacketBufferSize()); + SSLEngineResult result = src.wrap(EMPTY_BUFFER, tlsBuffer); + assertEquals(Status.OK, result.getStatus()); + assertEquals(HandshakeStatus.NOT_HANDSHAKING, result.getHandshakeStatus()); + assertEquals(0, result.bytesConsumed()); + assertEquals(0, result.bytesProduced()); + + // Ensure there is no pending inbound data + result = dst.unwrap(EMPTY_BUFFER, tlsBuffer); + assertEquals(Status.BUFFER_UNDERFLOW, result.getStatus()); + assertEquals(0, result.bytesConsumed()); + assertEquals(0, result.bytesProduced()); + + // Send the data. wrap() should consume as many source buffers as needed but + // never generate more than one full packet buffer of TLS data, and so unwrap() + // should only be needed once per loop iteration. + int consumed = 0, produced = 0; + ByteBuffer destBuffer = ByteBuffer.allocate(dataSize); + while (consumed < dataSize) { + String message = String.format("sendData: dataSize=%d, bufSize=%d", + dataSize, bufferSize); + + tlsBuffer.clear(); + result = src.wrap(sourceBuffers, 0, length, tlsBuffer); + assertEquals(message, Status.OK, result.getStatus()); + consumed += result.bytesConsumed(); + + tlsBuffer.flip(); + result = dst.unwrap(tlsBuffer, destBuffer); + assertEquals(message, Status.OK, result.getStatus()); + produced += result.bytesProduced(); + } + assertEquals(dataSize, consumed); + assertEquals(dataSize, produced); + + // Compare source and destination data. + destBuffer.flip(); + // destBuffer is non-direct so will always have a backing array + assertArrayEquals(sourceData, destBuffer.array()); + } + + /** + * Tests the multiple {@link ByteBuffer} cases of {@code wrap())} by sending blocks of + * application data split into arrays of ByteBuffers of different sizes. + * + * The main intention is to check that regardless of how the data is structured in + * buffers, each call to wrap() always generates a single TLS record that is smaller + * than the maximum allowed size, see https://github.com/google/conscrypt/issues/929 + */ + @Test + public void multipleBuffersOfDifferentSizes() throws Exception { + TestSSLEnginePair pair = TestSSLEnginePair.create(); + SSLSession session = pair.client.getSession(); + int appBufSize = session.getApplicationBufferSize(); + + int[] dataSizes = new int[] { 12, 512, 555, 1500, 8192, appBufSize, 5 * appBufSize}; + int[] bufferSizes = new int[] + { 53, 512, 8192, appBufSize, appBufSize - 53, appBufSize + 53, 5 * appBufSize}; + for (int dataSize : dataSizes) { + for (int bufSize : bufferSizes) { + sendAppDataInMultipleBuffers(pair.client, pair.server, dataSize, bufSize); + sendAppDataInMultipleBuffers(pair.server, pair.client, dataSize, bufSize); + sendAppDataInMultipleBuffers(pair.client, pair.server, dataSize, bufSize); + sendAppDataInMultipleBuffers(pair.server, pair.client, dataSize, bufSize); + } + } + } + private TestKeyStore addServerCertListener(final Runnable callback) { TestKeyStore store = TestKeyStore.getServer().copy(); X509ExtendedKeyManager tm = new ForwardingX509ExtendedKeyManager(
diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSessionContextTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSessionContextTest.java index 45ebd0e..0167162 100644 --- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSessionContextTest.java +++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSessionContextTest.java
@@ -69,6 +69,7 @@ } @Test + @SuppressWarnings("JdkObsolete") // Public API SSLSessionContext.getIds() uses Enumeration public void test_SSLSessionContext_getIds() { TestSSLContext c = newTestContext(); assertSSLSessionContextSize(0, c); @@ -95,6 +96,7 @@ } @Test + @SuppressWarnings("JdkObsolete") // Public API SSLSessionContext.getIds() uses Enumeration public void test_SSLSessionContext_getSession() { TestSSLContext c = newTestContext(); try {
diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSessionTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSessionTest.java index 9f48898..9f99c1f 100644 --- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSessionTest.java +++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSessionTest.java
@@ -198,14 +198,22 @@ fail(); } catch (SSLPeerUnverifiedException expected) { // Ignored. + } catch (UnsupportedOperationException e) { + if (!StandardNames.IS_15_OR_UP) { + fail("Should only throw UnsupportedOperationException on OpenJDK 15 or up"); + } } assertNotNull(s.client.getPeerCertificates()); - TestKeyStore.assertChainLength(s.client.getPeerCertificateChain()); + TestKeyStore.assertChainLength(s.client.getPeerCertificates()); try { assertNull(s.server.getPeerCertificateChain()); fail(); } catch (SSLPeerUnverifiedException expected) { // Ignored. + } catch (UnsupportedOperationException e) { + if (!StandardNames.IS_15_OR_UP) { + fail("Should only throw UnsupportedOperationException on OpenJDK 15 or up"); + } } s.close(); }
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 aa603b0..17c3e77 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,12 +72,12 @@ 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; import tests.net.DelegatingSSLSocketFactory; import tests.util.ForEachRunner; -import tests.util.ForEachRunner.Callback; import tests.util.Pair; @RunWith(JUnit4.class) @@ -422,6 +422,7 @@ * 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; @@ -455,6 +456,7 @@ * 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; @@ -875,7 +877,7 @@ @Test public void test_SSLSocket_ClientHello_cipherSuites() throws Exception { - ForEachRunner.runNamed(new Callback<SSLSocketFactory>() { + ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = TlsTester @@ -906,7 +908,7 @@ @Test public void test_SSLSocket_ClientHello_supportedCurves() throws Exception { - ForEachRunner.runNamed(new Callback<SSLSocketFactory>() { + ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = TlsTester @@ -932,7 +934,7 @@ @Test public void test_SSLSocket_ClientHello_clientProtocolVersion() throws Exception { - ForEachRunner.runNamed(new Callback<SSLSocketFactory>() { + ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = TlsTester @@ -944,7 +946,7 @@ @Test public void test_SSLSocket_ClientHello_compressionMethods() throws Exception { - ForEachRunner.runNamed(new Callback<SSLSocketFactory>() { + ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = TlsTester @@ -1009,6 +1011,7 @@ // 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 @@ -1049,6 +1052,7 @@ } @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(); @@ -1102,6 +1106,7 @@ } @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/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java index 09e351d..01eb6c9 100644 --- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java +++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java
@@ -16,6 +16,10 @@ package org.conscrypt.javax.net.ssl; +import static org.conscrypt.TestUtils.osName; +import static org.conscrypt.TestUtils.isOsx; +import static org.conscrypt.TestUtils.isLinux; +import static org.conscrypt.TestUtils.isWindows; import static org.conscrypt.TestUtils.UTF_8; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -110,7 +114,6 @@ import org.junit.runners.Parameterized; import tests.net.DelegatingSSLSocketFactory; import tests.util.ForEachRunner; -import tests.util.ForEachRunner.Callback; import tests.util.Pair; /** @@ -362,6 +365,7 @@ client.addHandshakeCompletedListener(new HandshakeCompletedListener() { @Override public void handshakeCompleted(HandshakeCompletedEvent event) { + SSLSocket socket = null; try { SSLSession session = event.getSession(); String cipherSuite = event.getCipherSuite(); @@ -371,7 +375,7 @@ event.getPeerCertificateChain(); Principal peerPrincipal = event.getPeerPrincipal(); Principal localPrincipal = event.getLocalPrincipal(); - SSLSocket socket = event.getSocket(); + socket = event.getSocket(); assertNotNull(session); byte[] id = session.getId(); assertNotNull(id); @@ -408,16 +412,19 @@ assertNotNull(socket); assertSame(client, socket); assertNull(socket.getHandshakeSession()); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } finally { synchronized (handshakeCompletedListenerCalled) { handshakeCompletedListenerCalled[0] = true; handshakeCompletedListenerCalled.notify(); } handshakeCompletedListenerCalled[0] = true; - socket.removeHandshakeCompletedListener(this); - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new RuntimeException(e); + if (socket != null) { + socket.removeHandshakeCompletedListener(this); + } } } }); @@ -1467,9 +1474,15 @@ * thread will interrupt another thread blocked writing on the same * socket. * + * Currently disabled: If the victim thread is not actually blocked in a write + * call then ConscryptEngineSocket can corrupt the output due to unsynchronized + * concurrent access to the socket's output stream and cause flakes: b/161347005 + * TODO(prb): Re-enable after underlying bug resolved + * * See also b/147323301 where close() triggered an infinite loop instead. */ @Test + @Ignore public void test_SSLSocket_interrupt_write_withAutoclose() throws Exception { final TestSSLContext c = new TestSSLContext.Builder() .clientProtocol(clientVersion) @@ -1480,6 +1493,8 @@ underlying, c.host.getHostName(), c.port, true); final byte[] data = new byte[1024 * 64]; + // TODO(b/161347005): Re-enable once engine-based socket interruption works correctly. + assumeFalse(isConscryptEngineSocket(wrapping)); Future<Void> clientFuture = runAsync(new Callable<Void>() { @Override public Void call() throws Exception { @@ -1493,13 +1508,6 @@ fail(); } catch (SocketException expected) { assertTrue(expected.getMessage().contains("closed")); - } catch (SSLException e) { - // TODO(b/159199048): Workaround for known TreeHugger - // cf_x86 presubmit failure which doesn't occur on non-TreeHugger - // cf_x86 or real devices. - if (!e.getMessage().contains("Engine bytesProduced")) { - throw e; - } } return null; } @@ -1553,7 +1561,7 @@ @Test public void test_SSLSocket_ClientHello_SNI() throws Exception { - ForEachRunner.runNamed(new Callback<SSLSocketFactory>() { + ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = TlsTester @@ -1572,7 +1580,7 @@ public void test_SSLSocket_ClientHello_ALPN() throws Exception { final String[] protocolList = new String[] { "h2", "http/1.1" }; - ForEachRunner.runNamed(new Callback<SSLSocketFactory>() { + ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = TlsTester.captureTlsHandshakeClientHello(executor, @@ -2082,23 +2090,6 @@ return "ConscryptEngineSocket".equals(clazz.getSimpleName()); } - private static String osName() { - return System.getProperty("os.name").toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", ""); - } - - private static boolean isLinux() { - return osName().startsWith("linux"); - } - - private static boolean isWindows() { - return osName().startsWith("windows"); - } - - private static boolean isOsx() { - String name = osName(); - return name.startsWith("macosx") || name.startsWith("osx"); - } - private <T> Future<T> runAsync(Callable<T> callable) { return executor.submit(callable); }
diff --git a/common/src/test/java/org/conscrypt/metrics/CipherSuiteTest.java b/common/src/test/java/org/conscrypt/metrics/CipherSuiteTest.java new file mode 100644 index 0000000..2ada251 --- /dev/null +++ b/common/src/test/java/org/conscrypt/metrics/CipherSuiteTest.java
@@ -0,0 +1,19 @@ +package org.conscrypt.metrics; + +import static org.junit.Assert.assertSame; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class CipherSuiteTest { + + @Test + public void consistency() { + for (CipherSuite cipherSuite : CipherSuite.values()) { + assertSame(cipherSuite, CipherSuite.forName(cipherSuite.name())); + } + assertSame(CipherSuite.UNKNOWN_CIPHER_SUITE, CipherSuite.forName("random junk")); + } +}
diff --git a/common/src/test/java/org/conscrypt/metrics/OptionalMethodTest.java b/common/src/test/java/org/conscrypt/metrics/OptionalMethodTest.java new file mode 100644 index 0000000..5e85539 --- /dev/null +++ b/common/src/test/java/org/conscrypt/metrics/OptionalMethodTest.java
@@ -0,0 +1,62 @@ +package org.conscrypt.metrics; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class OptionalMethodTest { + + @Test + public void workingMethod() { + OptionalMethod substring = + new OptionalMethod(String.class, "substring", int.class, int.class); + assertNotNull(substring); + + assertEquals("put", substring.invoke("input", 2, 5)); + } + + @Test + public void nullClass() { + OptionalMethod substring = + new OptionalMethod(null, "substring", int.class, int.class); + assertNotNull(substring); + + assertNull(substring.invoke("input", 2, 5)); + } + + @Test(expected = NullPointerException.class) + public void nullMethodName() { + new OptionalMethod(String.class, null, int.class, int.class); + } + + @Test + public void nullArgumentClasses() { + OptionalMethod substring = new OptionalMethod(String.class, "substring", int.class, null); + assertNotNull(substring); + + assertNull(substring.invoke("input", 2, 5)); + } + + @Test + public void noSuchMethodName() { + OptionalMethod subwrong = + new OptionalMethod(null, "subwrong", int.class, int.class); + assertNotNull(subwrong); + + assertNull(subwrong.invoke("input", 2, 5)); + } + + @Test + public void noSuchMethodArgs() { + OptionalMethod subwrong = + new OptionalMethod(null, "substring", long.class, byte[].class); + assertNotNull(subwrong); + + assertNull(subwrong.invoke("input", 2, 5)); + } +}
diff --git a/common/src/test/java/org/conscrypt/metrics/ProtocolTest.java b/common/src/test/java/org/conscrypt/metrics/ProtocolTest.java new file mode 100644 index 0000000..5cc4358 --- /dev/null +++ b/common/src/test/java/org/conscrypt/metrics/ProtocolTest.java
@@ -0,0 +1,20 @@ +package org.conscrypt.metrics; + +import static org.junit.Assert.assertSame; + +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class ProtocolTest { + @Test + @Ignore("Test intended for MTS only.") + public void consistency() { + for (Protocol protocol : Protocol.values()) { + String name = protocol.name().replace('_', '.'); + assertSame(protocol, Protocol.forName(name)); + } + } +}
diff --git a/common/src/test/resources/crypto/macs.csv b/common/src/test/resources/crypto/macs.csv new file mode 100644 index 0000000..3eea581 --- /dev/null +++ b/common/src/test/resources/crypto/macs.csv
@@ -0,0 +1,13 @@ +# AES-CMAC test vectors from RFC 4493 +# HMAC test vectors are a very small subset of NIST's +# Data is in the format: +# algorithm,key,message,mac +AESCMAC,2b7e151628aed2a6abf7158809cf4f3c,,bb1d6929e95937287fa37d129b756746 +AESCMAC,2b7e151628aed2a6abf7158809cf4f3c,6bc1bee22e409f96e93d7e117393172a,070a16b46b4d4144f79bdd9dd04a287c +AESCMAC,2b7e151628aed2a6abf7158809cf4f3c,6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411,dfa66747de9ae63030ca32611497c827 +AESCMAC,2b7e151628aed2a6abf7158809cf4f3c,6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710,51f0bebf7e3b9d92fc49741779363cfe +HmacSHA224,0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,4869205468657265,896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22 +HmacSHA256,0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,4869205468657265,b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7 +HmacSHA512,0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,4869205468657265,87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854 +HmacSHA224,4a656665,7768617420646f2079612077616e7420666f72206e6f7468696e673f,a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44 +HmacSHA256,4a656665,7768617420646f2079612077616e7420666f72206e6f7468696e673f,5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
diff --git a/constants/build.gradle b/constants/build.gradle index 977b21f..a43ea73 100644 --- a/constants/build.gradle +++ b/constants/build.gradle
@@ -71,4 +71,4 @@ } // Disable the javadoc task. -tasks.withType(Javadoc).all { enabled = false } +tasks.withType(Javadoc).configureEach { enabled = false }
diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..5bac8ac --- /dev/null +++ b/gradle.properties
@@ -0,0 +1 @@ +android.useAndroidX=true
diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index 94c8946..6b3ef1f 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle
@@ -3,19 +3,6 @@ def isSnapshot = project.version.contains('SNAPSHOT') -signing { - required false - sign publishing.publications -} - -tasks.register("signPublications").configure { - configurations.archives.allArtifacts.each { - if (it.type == 'jar' && it.classifier != 'sources' && it.classifier != 'javadoc') { - signJar(it.file.absolutePath) - } - } -} - publishing { publications { maven(MavenPublication) { @@ -69,3 +56,17 @@ } } } + +signing { + required false + sign publishing.publications.maven +} + +signMavenPublication.doFirst { + publishing.publications.maven.artifacts.each { + if (it.file.absolutePath.endsWith('.jar') && it.classifier != 'sources' && it.classifier != 'javadoc') { + logger.info("Signing jar: ${it.file.absolutePath}") + signJar(it.file.absolutePath) + } + } +}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf..62d4c05 100644 --- a/gradle/wrapper/gradle-wrapper.jar +++ b/gradle/wrapper/gradle-wrapper.jar Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0ebb310..186b715 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew index 83f2acf..fbd7c51 100755 --- a/gradlew +++ b/gradlew
@@ -82,6 +82,7 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -129,6 +130,7 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -154,19 +156,19 @@ else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -175,14 +177,9 @@ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat index 24467a1..a9f778a 100644 --- a/gradlew.bat +++ b/gradlew.bat
@@ -29,6 +29,9 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -81,6 +84,7 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
diff --git a/libcore-stub/build.gradle b/libcore-stub/build.gradle index 411d2aa..7411459 100644 --- a/libcore-stub/build.gradle +++ b/libcore-stub/build.gradle
@@ -16,4 +16,4 @@ } // Disable the javadoc task. -tasks.withType(Javadoc).all { enabled = false } +tasks.withType(Javadoc).configureEach { enabled = false }
diff --git a/lint-baseline.xml b/lint-baseline.xml new file mode 100644 index 0000000..70db717 --- /dev/null +++ b/lint-baseline.xml
@@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.system.ErrnoException#rethrowAsSocketException`" + errorLine1=" throw errnoException.rethrowAsSocketException();" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="external/conscrypt/repackaged/platform/src/main/java/com/android/org/conscrypt/Platform.java" + line="134" + column="34"/> + </issue> + +</issues>
diff --git a/openjdk-uber/build.gradle b/openjdk-uber/build.gradle index cf1b7f5..6755beb 100644 --- a/openjdk-uber/build.gradle +++ b/openjdk-uber/build.gradle
@@ -34,7 +34,8 @@ /** * Copy the native libraries to the resources directory. */ - task copySharedLibs(type: Copy, dependsOn: configurations.uberJar) { + def copySharedLibs = tasks.register("copySharedLibs", Copy) { + dependsOn configurations.uberJar from { configurations.uberJar.collect { zipTree(it) @@ -43,12 +44,15 @@ include '/META-INF/native/**' into file(resourcesDir) } - jar.dependsOn copySharedLibs + tasks.named("jar").configure { + dependsOn copySharedLibs + } /** * Copy the object files to the classes directory. */ - task copyClasses(type: Copy, dependsOn: configurations.uberJar) { + def copyClasses = tasks.register("copyClasses", Copy) { + dependsOn configurations.uberJar from { configurations.uberJar.collect { zipTree(it) @@ -57,15 +61,20 @@ exclude '/META-INF/**' into file(classesDir) } - jar.dependsOn copyClasses + tasks.named("jar").configure { + dependsOn copyClasses + } - task copySources(type: Copy, dependsOn: ":conscrypt-openjdk:sourcesJar") { + def copySources = tasks.register("copySources", Copy) { + dependsOn ":conscrypt-openjdk:sourcesJar" from { project(":conscrypt-openjdk").sourceSets.main.java } into file(sourcesDir) } - sourcesJar.dependsOn copySources + tasks.named("sourcesJar").configure { + dependsOn copySources + } // Note that this assumes that the version of BoringSSL for each // artifact exactly matches the one on the current system. @@ -84,7 +93,7 @@ } } else { // Not building an uber jar - disable all tasks. - tasks.collect { + tasks.configureEach { it.enabled = false } }
diff --git a/openjdk/build.gradle b/openjdk/build.gradle index c1e5181..56a2ea8 100644 --- a/openjdk/build.gradle +++ b/openjdk/build.gradle
@@ -1,4 +1,9 @@ +plugins { + id 'com.github.johnrengelman.shadow' version '6.0.0' +} + import aQute.bnd.gradle.BundleTaskConvention +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import org.codehaus.groovy.runtime.InvokerHelper apply plugin: 'biz.aQute.bnd.builder' @@ -82,12 +87,18 @@ dependsOn generateProperties } -task platformJar(type: Jar) { +tasks.register("platformJar", Jar) { from sourceSets.platform.output } +tasks.register("testJar", ShadowJar) { + classifier = 'tests' + configurations = [project.configurations.testRuntime] + from sourceSets.test.output +} + if (isExecutableOnPath('cpplint')) { - task cpplint(type: Exec) { + def cpplint = tasks.register("cpplint", Exec) { executable = 'cpplint' // TODO(nmittler): Is there a better way of getting the JNI sources? @@ -168,8 +179,8 @@ // This is listed as compile-only, but we absorb its contents. compileOnly project(':conscrypt-constants') - testImplementation project(':conscrypt-constants'), - project(':conscrypt-testing'), + testCompile project(':conscrypt-constants'), + project(path: ':conscrypt-testing', configuration: 'runtime'), libraries.junit, libraries.mockito @@ -190,8 +201,7 @@ def addNativeJar(nativeClassifier) { // Create a JAR for this configuration and add it to the output archives. SourceSet sourceSet = sourceSet(nativeClassifier) - def jarTaskName = sourceSet.jarTaskName - task "$jarTaskName"(type: Jar) { Jar t -> + def jarTask = tasks.register(sourceSet.jarTaskName, Jar) { Jar t -> // Depend on the regular classes task dependsOn classes manifest = jar.manifest @@ -206,13 +216,11 @@ } } - def jarTask = tasks["$jarTaskName"] - // Add the jar task to the standard build. jar.dependsOn jarTask // Add it to the publishing archives list. - publishing.publications.maven.artifact jarTask + publishing.publications.maven.artifact jarTask.get() } @@ -233,13 +241,13 @@ include suiteClass, "org/conscrypt/ConscryptOpenJdkSuite.class" } -task testEngineSocket(type: Test) { +def testFdSocket = tasks.register("testFdSocket", Test) { include suiteClass, "org/conscrypt/ConscryptOpenJdkSuite.class" InvokerHelper.setProperties(testLogging, test.testLogging.properties) systemProperties = test.systemProperties - systemProperty "org.conscrypt.useEngineSocketByDefault", true + systemProperty "org.conscrypt.useEngineSocketByDefault", false } -check.dependsOn testEngineSocket +check.dependsOn testFdSocket // Tests that involve interoperation with the OpenJDK TLS provider (generally to // test renegotiation, since we don't support initiating renegotiation but do @@ -247,7 +255,7 @@ // if Conscrypt is installed as the default provider, so these need to live in // a different task than the other tests, most of which need Conscrypt to be // installed to function. -task testInterop(type: Test) { +def testInterop = tasks.register("testInterop", Test) { include "org/conscrypt/ConscryptEngineTest.class" include "org/conscrypt/RenegotiationTest.class" } @@ -331,10 +339,18 @@ // Static link to BoringSSL linker.args "-O3", "-fvisibility=hidden", - "-lstdc++", "-lpthread", libPath + "/ssl/libssl.a", libPath + "/crypto/libcrypto.a" + if (targetPlatform.operatingSystem.isLinux()) { + // Static link libstdc++ and libgcc because + // they are not available in some restrictive Linux + // environments. + linker.args "-static-libstdc++", + "-static-libgcc" + } else { + linker.args "-lstdc++" + } } else if (toolChain in VisualCpp) { cppCompiler.define "DLL_EXPORT" cppCompiler.define "WIN32_LEAN_AND_MEAN" @@ -400,8 +416,8 @@ def source = binary.sharedLibraryFile // Copies the native library to a resource location that will be included in the jar. - def copyTaskName = "copyNativeLib${sourceSetName}" - task "$copyTaskName"(type: Copy, dependsOn: binary.tasks.link) { + def copyTask = project.tasks.register("copyNativeLib${sourceSetName}", Copy) { + dependsOn binary.tasks.link from source // Rename the artifact to include the generated classifier rename '(.+)(\\.[^\\.]+)', "\$1-$classifier\$2" @@ -413,12 +429,14 @@ if (osName == 'linux' && (!rootProject.hasProperty('nostrip') || !rootProject.nostrip.toBoolean())) { def stripTask = binary.tasks.taskName("strip") - task "$stripTask"(type: Exec) { - dependsOn binary.tasks.link - executable "strip" - args binary.tasks.link.linkedFile.asFile.get() - } - project.tasks["$copyTaskName"].dependsOn stripTask + project.tasks.register(stripTask as String, Exec) { + dependsOn binary.tasks.link + executable "strip" + args binary.tasks.link.linkedFile.asFile.get() + } + copyTask.configure { + dependsOn stripTask + } } } }
diff --git a/openjdk/src/main/java/org/conscrypt/Java8PlatformUtil.java b/openjdk/src/main/java/org/conscrypt/Java8PlatformUtil.java index bb625be..2206b60 100644 --- a/openjdk/src/main/java/org/conscrypt/Java8PlatformUtil.java +++ b/openjdk/src/main/java/org/conscrypt/Java8PlatformUtil.java
@@ -42,15 +42,6 @@ } } - static void getSSLParameters( - SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) { - getSSLParameters(params, impl); - if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) { - params.setServerNames(Collections.singletonList( - (SNIServerName) new SNIHostName(socket.getHostname()))); - } - } - static void setSSLParameters( SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) { setSSLParameters(params, impl); @@ -60,6 +51,16 @@ engine.setHostname(sniHost); } } + + static void getSSLParameters( + SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) { + getSSLParameters(params, impl); + if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) { + params.setServerNames(Collections.singletonList( + (SNIServerName) new SNIHostName(socket.getHostname()))); + } + } + static void getSSLParameters( SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) { getSSLParameters(params, impl);
diff --git a/openjdk/src/main/java/org/conscrypt/Platform.java b/openjdk/src/main/java/org/conscrypt/Platform.java index 5f9d0f6..d102524 100644 --- a/openjdk/src/main/java/org/conscrypt/Platform.java +++ b/openjdk/src/main/java/org/conscrypt/Platform.java
@@ -99,6 +99,7 @@ getCurveNameMethod = ECParameterSpec.class.getDeclaredMethod("getCurveName"); getCurveNameMethod.setAccessible(true); } catch (Exception ignored) { + // Method not available, leave it as null, which is checked before use } GET_CURVE_NAME_METHOD = getCurveNameMethod; } @@ -193,9 +194,19 @@ } try { - Field f_impl = Socket.class.getDeclaredField("impl"); - f_impl.setAccessible(true); - Object socketImpl = f_impl.get(s); + Method m_getImpl = Socket.class.getDeclaredMethod("getImpl"); + m_getImpl.setAccessible(true); + Object socketImpl = m_getImpl.invoke(s); + try { + Class<?> c_delegatingSocketImpl = Class.forName("java.net.DelegatingSocketImpl"); + if (c_delegatingSocketImpl.isAssignableFrom(socketImpl.getClass())) { + Method m_delegate = c_delegatingSocketImpl.getDeclaredMethod("delegate"); + m_delegate.setAccessible(true); + socketImpl = m_delegate.invoke(socketImpl); + } + } catch (Exception ignored) { + // Ignored + } Field f_fd = SocketImpl.class.getDeclaredField("fd"); f_fd.setAccessible(true); return (FileDescriptor) f_fd.get(socketImpl); @@ -580,10 +591,8 @@ return originalHostName; } catch (InvocationTargetException e) { throw new RuntimeException("Failed to get originalHostName", e); - } catch (ClassNotFoundException ignore) { + } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException ignore) { // passthrough and return addr.getHostAddress() - } catch (IllegalAccessException ignore) { - } catch (NoSuchMethodException ignore) { } return addr.getHostAddress(); @@ -657,9 +666,10 @@ KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); try { ks.load(null, null); - } catch (CertificateException ignored) { } catch (NoSuchAlgorithmException ignored) { - } catch (IOException ignored) { + // TODO(prb): Should this be re-thrown? It happens if "the algorithm used to check + // the integrity of the KeyStore cannot be found". + } catch (IOException | CertificateException ignored) { // We're not loading anything, so ignore it } // Find the highest-priority non-Conscrypt provider that provides a PKIX @@ -707,7 +717,7 @@ return null; } - static CertBlacklist newDefaultBlacklist() { + static CertBlocklist newDefaultBlocklist() { return null; } @@ -785,4 +795,21 @@ }); } } + + public static ConscryptHostnameVerifier getDefaultHostnameVerifier() { + return OkHostnameVerifier.strictInstance(); + } + + @SuppressWarnings("unused") + static long getMillisSinceBoot() { + return 0; + } + + @SuppressWarnings("unused") + static void countTlsHandshake( + boolean success, String protocol, String cipherSuite, long duration) {} + + public static boolean isJavaxCertificateSupported() { + return JAVA_VERSION < 15; + } }
diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java b/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java index f93c291..adb9184 100644 --- a/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java +++ b/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java
@@ -19,24 +19,29 @@ import static org.conscrypt.TestUtils.openTestFile; import static org.conscrypt.TestUtils.readTestFile; import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.when; import java.io.IOException; import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -79,12 +84,6 @@ OpenSSLContextImpl context, ServerSocket server, SSLSocketFactory factory) { return null; } - - @Override - Socket newServerSocket(OpenSSLContextImpl context, ServerSocket server, - SSLSocketFactory factory) throws IOException { - return server.accept(); - } }, PLAIN { @Override @@ -92,11 +91,14 @@ SSLSocketFactory factory) throws IOException { return new Socket(server.getInetAddress(), server.getLocalPort()); } - + }, + CHANNEL { @Override - Socket newServerSocket(OpenSSLContextImpl context, ServerSocket server, + Socket newClientSocket(OpenSSLContextImpl context, ServerSocket server, SSLSocketFactory factory) throws IOException { - return server.accept(); + SocketChannel channel = SocketChannel.open(); + channel.connect(server.getLocalSocketAddress()); + return channel.socket(); } }, SSL { @@ -121,8 +123,11 @@ abstract Socket newClientSocket(OpenSSLContextImpl context, ServerSocket server, SSLSocketFactory factory) throws IOException; - abstract Socket newServerSocket(OpenSSLContextImpl context, ServerSocket server, - SSLSocketFactory factory) throws IOException; + + Socket newServerSocket(OpenSSLContextImpl context, ServerSocket server, + SSLSocketFactory factory) throws IOException { + return server.accept(); + } } /** @@ -191,15 +196,43 @@ } } - @Parameters(name = "{0} wrapping {1}") + public enum ServerSocketType { + PLAIN { + @Override + public ServerSocket newServerSocket() throws IOException { + return new ServerSocket(0, 50, TestUtils.getLoopbackAddress()); + } + }, + CHANNEL { + @Override + public ServerSocket newServerSocket() throws IOException { + ServerSocketChannel channel = ServerSocketChannel.open(); + InetAddress localAddress = TestUtils.getLoopbackAddress(); + channel.socket().bind(new InetSocketAddress(localAddress.getHostName(), 0), 50); + return channel.socket(); + } + }; + public abstract ServerSocket newServerSocket() throws IOException; + } + + @Parameters(name = "{0} wrapping {1} connecting to {2}") public static Object[][] data() { return new Object[][] { - {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.NONE}, - {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.PLAIN}, + {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.NONE, ServerSocketType.PLAIN}, + {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.NONE, ServerSocketType.CHANNEL}, + {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.PLAIN, ServerSocketType.PLAIN}, + {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.PLAIN, ServerSocketType.CHANNEL}, + {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.CHANNEL, ServerSocketType.PLAIN}, + {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.CHANNEL, ServerSocketType.CHANNEL}, // Not supported: {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.SSL}, - {SocketType.ENGINE, UnderlyingSocketType.NONE}, - {SocketType.ENGINE, UnderlyingSocketType.PLAIN}, - {SocketType.ENGINE, UnderlyingSocketType.SSL}}; + {SocketType.ENGINE, UnderlyingSocketType.NONE, ServerSocketType.PLAIN}, + {SocketType.ENGINE, UnderlyingSocketType.NONE, ServerSocketType.CHANNEL}, + {SocketType.ENGINE, UnderlyingSocketType.PLAIN, ServerSocketType.PLAIN}, + {SocketType.ENGINE, UnderlyingSocketType.PLAIN, ServerSocketType.CHANNEL}, + {SocketType.ENGINE, UnderlyingSocketType.CHANNEL, ServerSocketType.PLAIN}, + {SocketType.ENGINE, UnderlyingSocketType.CHANNEL, ServerSocketType.CHANNEL}, + {SocketType.ENGINE, UnderlyingSocketType.SSL, ServerSocketType.PLAIN}, + {SocketType.ENGINE, UnderlyingSocketType.SSL, ServerSocketType.CHANNEL}}; } @Parameter @@ -208,6 +241,9 @@ @Parameter(1) public UnderlyingSocketType underlyingSocketType; + @Parameter(2) + public ServerSocketType serverSocketType; + private X509Certificate ca; private X509Certificate cert; private X509Certificate certEmbedded; @@ -215,6 +251,7 @@ private Field contextSSLParameters; private ExecutorService executor; + private final Random random = new Random(System.currentTimeMillis()); @Before public void setUp() throws Exception { @@ -388,7 +425,7 @@ } void doHandshake() throws Exception { - ServerSocket listener = newServerSocket(); + ServerSocket listener = serverSocketType.newServerSocket(); Future<AbstractConscryptSocket> clientFuture = handshake(listener, clientHooks); Future<AbstractConscryptSocket> serverFuture = handshake(listener, serverHooks); @@ -562,7 +599,7 @@ @Test @SuppressWarnings("deprecation") public void setAlpnProtocolWithNullShouldSucceed() throws Exception { - ServerSocket listening = newServerSocket(); + ServerSocket listening = serverSocketType.newServerSocket(); OpenSSLSocketImpl clientSocket = null; try { Socket underlying = new Socket(listening.getInetAddress(), listening.getLocalPort()); @@ -583,7 +620,7 @@ // http://b/27250522 @Test public void test_setSoTimeout_doesNotCreateSocketImpl() throws Exception { - ServerSocket listening = newServerSocket(); + ServerSocket listening = serverSocketType.newServerSocket(); try { Socket underlying = new Socket(listening.getInetAddress(), listening.getLocalPort()); Socket socket = socketType.newClientSocket( @@ -646,7 +683,44 @@ assertEquals(alpnProtocol, Conscrypt.getApplicationProtocol(connection.client)); } - private static ServerSocket newServerSocket() throws IOException { - return new ServerSocket(0, 50, TestUtils.getLoopbackAddress()); + @Test + public void dataFlows() throws Exception { + final TestConnection connection = + new TestConnection(new X509Certificate[] {cert, ca}, certKey); + connection.doHandshakeSuccess(); + + // Basic data flow assurance. Send random buffers in each direction, each less than 16K + // so should fit in a single TLS packet. 50% chance of sending in each direction on + // each iteration to randomize the flow. + for (int i = 0; i < 50; i++) { + if (random.nextBoolean()) { + sendData(connection.client, connection.server, randomBuffer()); + } + if (random.nextBoolean()) { + sendData(connection.server, connection.client, randomBuffer()); + } + } + } + + private void sendData(SSLSocket source, final SSLSocket destination, byte[] data) + throws Exception { + final byte[] received = new byte[data.length]; + + Future<Integer> readFuture = executor.submit(new Callable<Integer>() { + @Override + public Integer call() throws Exception { + return destination.getInputStream().read(received); + } + }); + + source.getOutputStream().write(data); + assertEquals(data.length, (int) readFuture.get()); + assertArrayEquals(data, received); + } + + private byte[] randomBuffer() { + byte[] buffer = new byte[random.nextInt(16 * 1024)]; + random.nextBytes(buffer); + return buffer; } }
diff --git a/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java b/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java index c8a55f9..0a02e91 100644 --- a/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java +++ b/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
@@ -157,6 +157,7 @@ /** * Lazily create shared test certificates. */ + @SuppressWarnings("JdkObsolete") // Public API KeyStore.aliases() uses Enumeration private static synchronized void initCerts() { if (SERVER_PRIVATE_KEY != null) { return;
diff --git a/openjdk/src/test/java/org/conscrypt/OpenSSLX509CertificateTest.java b/openjdk/src/test/java/org/conscrypt/OpenSSLX509CertificateTest.java index 0c5a017..c510e0d 100644 --- a/openjdk/src/test/java/org/conscrypt/OpenSSLX509CertificateTest.java +++ b/openjdk/src/test/java/org/conscrypt/OpenSSLX509CertificateTest.java
@@ -25,6 +25,8 @@ import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import junit.framework.TestCase; @@ -43,10 +45,28 @@ // Mark the field as non-final on JVM that need it. try { - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(targetUID, targetUID.getModifiers() & ~Modifier.FINAL); - } catch (NoSuchFieldException ignored) { + Field modifiersField = null; + try { + modifiersField = Field.class.getDeclaredField("modifiers"); + } catch (NoSuchFieldException e) { + try { + Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); + getDeclaredFields0.setAccessible(true); + Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false); + for (Field field : fields) { + if ("modifiers".equals(field.getName())) { + modifiersField = field; + break; + } + } + } catch (NoSuchMethodException | InvocationTargetException ignored) { + } + } + if (modifiersField != null) { + modifiersField.setAccessible(true); + modifiersField.setInt(targetUID, targetUID.getModifiers() & ~Modifier.FINAL); + } + } catch (Exception ignored) { } targetUID.set(null, clDesc.getSerialVersionUID()); @@ -106,31 +126,31 @@ cert.getTBSCertificate())); assertTrue(Arrays.equals( - certPoisoned.withDeletedExtension(CT_POISON_EXTENSION).getTBSCertificate(), + certPoisoned.getTBSCertificateWithoutExtension(CT_POISON_EXTENSION), cert.getTBSCertificate())); } public void test_deletingExtensionMakesCopy() throws Exception { - /* Calling withDeletedExtension should not modify the original certificate, only make a copy. + /* Calling getTBSCertificateWithoutExtension should not modify the original certificate. * Make sure the extension is still present in the original object. */ OpenSSLX509Certificate certPoisoned = loadTestCertificate("cert-ct-poisoned.pem"); assertTrue(certPoisoned.getCriticalExtensionOIDs().contains(CT_POISON_EXTENSION)); - OpenSSLX509Certificate certWithoutExtension = certPoisoned.withDeletedExtension(CT_POISON_EXTENSION); - + certPoisoned.getTBSCertificateWithoutExtension(CT_POISON_EXTENSION); assertTrue(certPoisoned.getCriticalExtensionOIDs().contains(CT_POISON_EXTENSION)); - assertFalse(certWithoutExtension.getCriticalExtensionOIDs().contains(CT_POISON_EXTENSION)); } public void test_deletingMissingExtension() throws Exception { - /* withDeletedExtension should be safe to call on a certificate without the extension, and - * return an identical copy. + /* getTBSCertificateWithoutExtension should throw on a certificate without the extension. */ OpenSSLX509Certificate cert = loadTestCertificate("cert.pem"); assertFalse(cert.getCriticalExtensionOIDs().contains(CT_POISON_EXTENSION)); - OpenSSLX509Certificate cert2 = cert.withDeletedExtension(CT_POISON_EXTENSION); - assertEquals(cert, cert2); + try { + cert.getTBSCertificateWithoutExtension(CT_POISON_EXTENSION); + fail(); + } catch (IllegalArgumentException expected) { + } } }
diff --git a/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java b/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java index 1b1ed0b..9941d7f 100644 --- a/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java +++ b/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java
@@ -34,6 +34,7 @@ } @Override + @SuppressWarnings("JdkObsolete") // Public API SSLSessionContext.getIds() uses Enumeration int size(ServerSessionContext context) { int count = 0; Enumeration<byte[]> ids = context.getIds();
diff --git a/openjdk/src/test/resources/blacklist_test_chain.pem b/openjdk/src/test/resources/blocklist_test_chain.pem similarity index 100% rename from openjdk/src/test/resources/blacklist_test_chain.pem rename to openjdk/src/test/resources/blocklist_test_chain.pem
diff --git a/openjdk/src/test/resources/blacklist_test_valid_ca.pem b/openjdk/src/test/resources/blocklist_test_valid_ca.pem similarity index 100% rename from openjdk/src/test/resources/blacklist_test_valid_ca.pem rename to openjdk/src/test/resources/blocklist_test_valid_ca.pem
diff --git a/openjdk/src/test/resources/blacklist_test_valid_chain.pem b/openjdk/src/test/resources/blocklist_test_valid_chain.pem similarity index 100% rename from openjdk/src/test/resources/blacklist_test_valid_chain.pem rename to openjdk/src/test/resources/blocklist_test_valid_chain.pem
diff --git a/openjdk/src/test/resources/test_blacklist_ca.pem b/openjdk/src/test/resources/test_blocklist_ca.pem similarity index 100% rename from openjdk/src/test/resources/test_blacklist_ca.pem rename to openjdk/src/test/resources/test_blocklist_ca.pem
diff --git a/openjdk/src/test/resources/test_blacklist_ca_key.pem b/openjdk/src/test/resources/test_blocklist_ca_key.pem similarity index 100% rename from openjdk/src/test/resources/test_blacklist_ca_key.pem rename to openjdk/src/test/resources/test_blocklist_ca_key.pem
diff --git a/openjdk/src/test/resources/test_intermediate.csr b/openjdk/src/test/resources/test_intermediate.csr new file mode 100644 index 0000000..dbe8d2e --- /dev/null +++ b/openjdk/src/test/resources/test_intermediate.csr
@@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICXDCCAUQCAQAwFzEVMBMGA1UEAwwMaW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqpirljyU3bstotA3bs1w97gBDbgSPX7HHPQa +VQeILG49E6sS8E7Tst9Fm5b7/BKnh5R2ekk6XzpR1l8MrysxEtUi64pfcDOaEDsj +Ry1Co+a2ZWEKzinrcfQmkk2oH5HyjiXn4lAz4I3PFzW0Ke0sLSgMtlWyhmD2sK3Y +FzKHoSn1G5J2IGFVaArHhkG38YOvsdgw/xuMTzsYkwHNkwWR9LbUGI8Cb7hccAV6 +eWTgu0CEDJ4tnNu+RcoLgyrJOviNBB8PsxEFEEDPnirzZt3BP3/BQhEsAItNlmxW +mOx46ifybwfwk7wZBmIvgeDnHr4MlQA5u6+epqxCfb9mw89yLwIDAQABoAAwDQYJ +KoZIhvcNAQELBQADggEBAHvZLREVOImWgGafwB5b+r0qo2TP/bua3M16m6beNwvx +df4H4Uym8CDd/53u1Bzicf19VR9ncjKt1GDnj+gTW+kVJ2S9mPyLbo7IEzM+rmEb +fq/OHgKjhzTsUJ3rIf0w//XqjjBUvYFOXkF0D4BNL3cSE2aguOWeNKneGFwFZiEM +4Zz2f17AGcRLJqcSJIFBDQzDAQkpxVf67TQpD9Q4yTjaJfdjlawPwJDkw4kHY9IM ++/eADQww6czzOTAYJxZBMJCuiWecHR37Kt+KOhqIOaWG/tRuJ72fyi/jdXItbCEw +bO4soja7MnEgLL3+1se46zqMeZeBqVo9r3cMCVX6K8s= +-----END CERTIFICATE REQUEST-----
diff --git a/openjdk/src/test/resources/test_intermediate_blockedroot.pem b/openjdk/src/test/resources/test_intermediate_blockedroot.pem new file mode 100644 index 0000000..84b2c5a --- /dev/null +++ b/openjdk/src/test/resources/test_intermediate_blockedroot.pem
@@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFDCCAfygAwIBAgIUKEpmhiN42JxqaXIMn0ZdTuvIwkEwDQYJKoZIhvcNAQEL +BQAwHDEaMBgGA1UEAwwRYmxhY2tsaXN0IHRlc3QgQ0EwHhcNMjMwMTE5MTc0MjQ1 +WhcNMzMwMTE2MTc0MjQ1WjAXMRUwEwYDVQQDDAxpbnRlcm1lZGlhdGUwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqmKuWPJTduy2i0DduzXD3uAENuBI9 +fscc9BpVB4gsbj0TqxLwTtOy30Wblvv8EqeHlHZ6STpfOlHWXwyvKzES1SLril9w +M5oQOyNHLUKj5rZlYQrOKetx9CaSTagfkfKOJefiUDPgjc8XNbQp7SwtKAy2VbKG +YPawrdgXMoehKfUbknYgYVVoCseGQbfxg6+x2DD/G4xPOxiTAc2TBZH0ttQYjwJv +uFxwBXp5ZOC7QIQMni2c275FyguDKsk6+I0EHw+zEQUQQM+eKvNm3cE/f8FCESwA +i02WbFaY7HjqJ/JvB/CTvBkGYi+B4OcevgyVADm7r56mrEJ9v2bDz3IvAgMBAAGj +UzBRMB0GA1UdDgQWBBQzP1H/yV66YjFeq6M6vy+JTaG1+zAfBgNVHSMEGDAWgBTG +uQuT0EWEs5bhdKZRvPGhz7MgkjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQDTcXx9PghFT+H1bKJMDGSH8Jr8730dpKs6e3IuVgCs00YyvMBgRYbA +v5ksMV80ZHOErim6JYTj8rpLSUXWYgq7xyFaTMWSt+YPPoXAkdW6p3fngyvCf9T2 +HqZenJTQw2g/xRDL6PTjWh5qumqipVuAR9ue4l+4fRb31VaDOL0U/OPkqjoD3C/c +3ni9cglpzCRotTTGaSpIIpaBWy77HounXjreVn+JbYsEEx1S4CBo6+EJA+CEtQQo +BSFBnvl62rfwNKHCEvMB1jmMELIATVxu1NL6fWp/bP3OTWxqLcJU4G3zf0M1qTLx +fuoKzzyqdyjaeeE+ibr7sgyOU6Q/zk0Q +-----END CERTIFICATE-----
diff --git a/openjdk/src/test/resources/test_intermediate_key.pem b/openjdk/src/test/resources/test_intermediate_key.pem new file mode 100644 index 0000000..f841c95 --- /dev/null +++ b/openjdk/src/test/resources/test_intermediate_key.pem
@@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCqmKuWPJTduy2i +0DduzXD3uAENuBI9fscc9BpVB4gsbj0TqxLwTtOy30Wblvv8EqeHlHZ6STpfOlHW +XwyvKzES1SLril9wM5oQOyNHLUKj5rZlYQrOKetx9CaSTagfkfKOJefiUDPgjc8X +NbQp7SwtKAy2VbKGYPawrdgXMoehKfUbknYgYVVoCseGQbfxg6+x2DD/G4xPOxiT +Ac2TBZH0ttQYjwJvuFxwBXp5ZOC7QIQMni2c275FyguDKsk6+I0EHw+zEQUQQM+e +KvNm3cE/f8FCESwAi02WbFaY7HjqJ/JvB/CTvBkGYi+B4OcevgyVADm7r56mrEJ9 +v2bDz3IvAgMBAAECggEAJH8B4mO2g1MqebTqzwEThGNwkkNJX6+SIF8WjQ9N8hdp +aJ5GMHPktVUvupAL+4rwHUDFMfcdjkbXQDHYcFcgqgM/870IGuRnNsa4Dt+fbJoM +dlbS7XUpYhkV8WG8sHhUOFXirFd1Kbqczb3W+8s6ErUJNy7RQQ9YZ0bhvmC4hGEw +gW+rbpexwGLBSzUZCBJNy8ePi0akcEiaTHVS6ZjyQCIuRekSvJh/DTxZdhI9QV2K +e2XONzCyCsJOEBxPXFEzXXApxPb6DmMnN2xzciQf+MppvUWFKDyDG2a/vNdgVr2O +wXcvgQp5yl9dp+tyP4usSqZPgmRyDtSiaZFsptjMEQKBgQDbaYTDsnFAUmq3DTTp +vVSNnugd1ss+LKMP3D+T986tRX4tVtB4a7Edrh5QLYmkXKZdPp+u6Yo0YDdNJGmP +jnREwCT/YutcQ6TlyFNXwzE0Uf56fhHOaSKW6WFlX6kNfHvfJNu+1mKJS0nTH4yG +NmxSjA8RcEkS9R4o1dTvth0fnwKBgQDHC0Epjiv3LMAIVH0HqWjl1gmfU1QUtkWQ +3GELQeA2KeeVTwakIdKcZ0tr8qqwOIkxGD+Fr0HsST4d1GGmMZ1LM+PgEg2OoKM5 +aBNH2znFwqc8fdd/mBc1Vw4B2yCKrTAbKK/OOV19fi4rs1DARjesknnVAvom9CkK +na9IoAZjcQKBgDmvPjZtHZU5ldDWagjhu+8XzhK6O+j2t1AeKaDvT6kCUi/9WQWv +2nrhIhsWPc+2hA6TvkuwHqOygBeJ8S7K1wqUMaXrDdHN/vZienbiXHdS70KpDmlj +/rIKXY7XXYysI60A9bzwhCtwXdJhwwIuIMB7DiMZkDypsOovfbIgAPwlAoGAKW3t +RUIDYrJc0h8L2zFm1RgE7rXAdYMu3aURSe+PRJbaThih0D3+AXH6n+BlqMJLw/1B +E4lUFmN0W28eWCJRlBqb3sLDMaG797Hy+WznDIOknZGv7i3w/rg9ASPkFRlRPwXr ++ee0zu8Zmxz6vNqgsfnXBABXow4FEOGbX2l3ivECgYEA1yVf+bG4CjG7hrUQmcLV +5WwHOggGRLDOzUJdHq1VpZyN31dagMs7DuV/1xk3uwcxY5M5D5RjRWV0b3BKDCDT +t8/f31FNxNf2JUPM9bwy2tFJO+ZXRFdOmm7S164IhSnv243OUbc6KGO0loBf4WBi +31pgngO3pFBKxtpX81ABrMg= +-----END PRIVATE KEY-----
diff --git a/openjdk/src/test/resources/test_intermediate_nonblockedroot.pem b/openjdk/src/test/resources/test_intermediate_nonblockedroot.pem new file mode 100644 index 0000000..1f7bbd2 --- /dev/null +++ b/openjdk/src/test/resources/test_intermediate_nonblockedroot.pem
@@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCjCCAfKgAwIBAgIUZzC6NfXFwaGgeTHeleM4k2izk8UwDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yMzAxMTkxNzQyNTVaFw0zMzAxMTYx +NzQyNTVaMBcxFTATBgNVBAMMDGludGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKqYq5Y8lN27LaLQN27NcPe4AQ24Ej1+xxz0GlUHiCxu +PROrEvBO07LfRZuW+/wSp4eUdnpJOl86UdZfDK8rMRLVIuuKX3AzmhA7I0ctQqPm +tmVhCs4p63H0JpJNqB+R8o4l5+JQM+CNzxc1tCntLC0oDLZVsoZg9rCt2Bcyh6Ep +9RuSdiBhVWgKx4ZBt/GDr7HYMP8bjE87GJMBzZMFkfS21BiPAm+4XHAFenlk4LtA +hAyeLZzbvkXKC4MqyTr4jQQfD7MRBRBAz54q82bdwT9/wUIRLACLTZZsVpjseOon +8m8H8JO8GQZiL4Hg5x6+DJUAObuvnqasQn2/ZsPPci8CAwEAAaNTMFEwHQYDVR0O +BBYEFDM/Uf/JXrpiMV6rozq/L4lNobX7MB8GA1UdIwQYMBaAFADwPCOf1D6SpTXG +Brj1Oa03yOzTMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAE5y +7OE6lmZlstL/15x8yZmUYzSXF0u365jfs1eJXDkWzn2BcmmzMSADn9spakDDZtN0 +daOpDGaB81TDjiBID1OwKMSRM1DmZzNI4PLFqWKUjWkwRtZjo5GsD0p/ATLV+S2z +eQIHcqTcAH8ay1sBReig/plALKyseTk4R2799Gi+tA08RQ4cIsdxyUFSUc0nqgFV +YsBM/cDeFCSYNwWLsNYAubJMIoUiKiweZ8bx+OoaS8Swc4p1M3Fk7lmh2g7APLjG +RkiPF4Ta3c41yZxNW7tEP4CCPB3hm0OkEdW68zc8oOPiNt1sNL5szJI3+cVT+k+5 +4387ICBvTGLQRHL6avw= +-----END CERTIFICATE-----
diff --git a/openjdk/src/test/resources/test_leaf.csr b/openjdk/src/test/resources/test_leaf.csr new file mode 100644 index 0000000..bc9a9b5 --- /dev/null +++ b/openjdk/src/test/resources/test_leaf.csr
@@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICVDCCATwCAQAwDzENMAsGA1UEAwwEbGVhZjCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBANVVRB6iPcrN0CLWUBXjO53vCdtKEdOBgCuMmA/2J8/YKc0g +f/IGCFV/bZ9RNmMyHsu1Mp2tu6posxayCzxPVqW7XYbLIKxHSR3ZarTtm5KZRJMt +R/ibpffrzrEOXy+7583sZPOdBNC7ujtY8sTTpL3ki2YmopX8TWINnl64WfltxYCQ +o+ox7cjO0lwrqbUiRk5vLGhvuoX+EYB1sXxC2+RWMjh3QNi0StyJwBCS++O+JYaJ +Mjo/8XCrkNuYyhn0Bmq6lMzT3Vu06skNpmDQnFgjcmQPET7yRBZeKFp5rNFcugjg +wmrpGJvEnLBkWbUxZ78Edd5z4lVUifGgWeXI29kCAwEAAaAAMA0GCSqGSIb3DQEB +CwUAA4IBAQB+gCFpLmV/ub4rVo4sA0bOiE7dN/wBA2tmaVVRxis/itZAFVze5o7P +LRKyjunOJ75BnbiylGcYOfiMQbgBDGw/QHg4cIQwMHdGPHes1JAeF9XYTBgVJT3b +qp1NTL7CDj8ry60wONDQ2X7Y7a2fU8LrfpflDv0+W0PaAK8/ptgi4a0rYPE9OHIC +qQ5aiUJ8QQgnW9S2KCPRBjiJW9lZ7hr7A/TGe6/i9L2NS6KYPGw3PD34Vmvz/rwf +jakm1GrQ3kGjPoc9yWSF60GLTAAXN+xF+9Htq+7PDmeo8krr28upSLokiO7uMWvQ +2/0t7gM1ta7+gVuf9SdUpTOEZ6d+chKb +-----END CERTIFICATE REQUEST-----
diff --git a/openjdk/src/test/resources/test_leaf_blockedroot.pem b/openjdk/src/test/resources/test_leaf_blockedroot.pem new file mode 100644 index 0000000..472abf3 --- /dev/null +++ b/openjdk/src/test/resources/test_leaf_blockedroot.pem
@@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIUODP0VFemD4Zonzl3hogYzAEZLLwwDQYJKoZIhvcNAQEL +BQAwHDEaMBgGA1UEAwwRYmxhY2tsaXN0IHRlc3QgQ0EwHhcNMjMwMTE5MTcxODQw +WhcNMzMwMTE2MTcxODQwWjAPMQ0wCwYDVQQDDARsZWFmMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA1VVEHqI9ys3QItZQFeM7ne8J20oR04GAK4yYD/Yn +z9gpzSB/8gYIVX9tn1E2YzIey7Uyna27qmizFrILPE9WpbtdhssgrEdJHdlqtO2b +kplEky1H+Jul9+vOsQ5fL7vnzexk850E0Lu6O1jyxNOkveSLZiailfxNYg2eXrhZ ++W3FgJCj6jHtyM7SXCuptSJGTm8saG+6hf4RgHWxfELb5FYyOHdA2LRK3InAEJL7 +474lhokyOj/xcKuQ25jKGfQGarqUzNPdW7TqyQ2mYNCcWCNyZA8RPvJEFl4oWnms +0Vy6CODCaukYm8ScsGRZtTFnvwR13nPiVVSJ8aBZ5cjb2QIDAQABo1MwUTAdBgNV +HQ4EFgQUMg9ifb+WI/uUTTf8Jd31XVHAI8MwHwYDVR0jBBgwFoAUxrkLk9BFhLOW +4XSmUbzxoc+zIJIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA +nxPXBF/Jrsig/5LMwaUyCX/VYb9W7W9tV6odKjghKxf6hA59VxevY/S/J0yK+mTJ +PAQGZC0vkcWjmBDQW8w9J0sUkX+8OCdcxqXGRnryCJ1lB7i/UOLhkCyPA1jZGNti +E43LYqs+iBxEvzPzeOggvXaE+ujtFZxCT5dLlzzVvTt9vomKvPmapC93ycorYjYV +89K54mNqj7aZeCHTmyJxsZGzUhVDdp83Dnl8YopYpnHd7jr0xX8fqbL9WZf81sRn +3u99Js6csv4Gi/ZDrbNONaUfpD5iH0Tm+2Kh7p6pI0lVBWaZzw59PNVGDRZp15sl +HeCO1zZqxh3hj+gZW0Ao1Q== +-----END CERTIFICATE-----
diff --git a/openjdk/src/test/resources/test_leaf_intermediate.pem b/openjdk/src/test/resources/test_leaf_intermediate.pem new file mode 100644 index 0000000..12ae380 --- /dev/null +++ b/openjdk/src/test/resources/test_leaf_intermediate.pem
@@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBzCCAe+gAwIBAgIUfCBnEPBeeqUTdsm3eI1CnjL8zIowDQYJKoZIhvcNAQEL +BQAwFzEVMBMGA1UEAwwMaW50ZXJtZWRpYXRlMB4XDTIzMDExOTE3NDUyMFoXDTMz +MDExNjE3NDUyMFowDzENMAsGA1UEAwwEbGVhZjCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBANVVRB6iPcrN0CLWUBXjO53vCdtKEdOBgCuMmA/2J8/YKc0g +f/IGCFV/bZ9RNmMyHsu1Mp2tu6posxayCzxPVqW7XYbLIKxHSR3ZarTtm5KZRJMt +R/ibpffrzrEOXy+7583sZPOdBNC7ujtY8sTTpL3ki2YmopX8TWINnl64WfltxYCQ +o+ox7cjO0lwrqbUiRk5vLGhvuoX+EYB1sXxC2+RWMjh3QNi0StyJwBCS++O+JYaJ +Mjo/8XCrkNuYyhn0Bmq6lMzT3Vu06skNpmDQnFgjcmQPET7yRBZeKFp5rNFcugjg +wmrpGJvEnLBkWbUxZ78Edd5z4lVUifGgWeXI29kCAwEAAaNTMFEwHQYDVR0OBBYE +FDIPYn2/liP7lE03/CXd9V1RwCPDMB8GA1UdIwQYMBaAFDM/Uf/JXrpiMV6rozq/ +L4lNobX7MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJh2wZtc +aDji9cI8LKJAr7V3raQFfk6Oq999CIoLoH3MiaEyTG9/FC/ZlaGUpC49c63bsRu8 +AQuPQydVOjrVTeUB4x12qoDGdz1lze+2zeY2jbIsd5VBEF0gObdkwwHgFQXKH5Lf +eSoBc4XPQ0I5dTYvR/P3+KX4fTyEmmjj+EWaH4yFPsW3JVu/2LrzI0IKq3+9VD0a +dB/mI42lI65cEtW2zGI+CSQGt0FGXdVsXGfne87QNByVxYCyS0wzdfSla3yLLGdf +8EJTqwZH0lKuRSf8xeNFr/pVXT9YfJWUAja7lIiwQVQfsD5MGsjNGcJMEDhM08PO ++lFxL3h3B85jIZw= +-----END CERTIFICATE-----
diff --git a/openjdk/src/test/resources/blacklist_test_valid_ca.pem b/openjdk/src/test/resources/test_nonblocklist_ca.pem similarity index 100% copy from openjdk/src/test/resources/blacklist_test_valid_ca.pem copy to openjdk/src/test/resources/test_nonblocklist_ca.pem
diff --git a/openjdk/src/test/resources/test_nonblocklist_ca_key.pem b/openjdk/src/test/resources/test_nonblocklist_ca_key.pem new file mode 100644 index 0000000..1d6b8bd --- /dev/null +++ b/openjdk/src/test/resources/test_nonblocklist_ca_key.pem
@@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1gY2VOIFa/lQf +PiJpXIKBEVYlnJdsJ6izpwmgi+2M8qTZGxvaov8xv9tObYNyENakHfdXu4RAF7Z/ +cCw/ntJo0mEKVzJflm/gMa6/02e+znp5s0lKYFLGY6+++H81neS8NHWF1E2D0Yxa +/47zQAw8QWI5T7Lw0uzWFT6gq2oTRpFBj4RatJhpXMX7/cSR3yV6Eeuq6vU9Ncce +urqzo1tg3bI4FKn09xDbnbpb+rxShqv+nKDCMxt7f2HRSVSgLEe2z8mpq2PXVwAt +v7TL+UXGZfniNG0wzO+PLGIYzctL64z6ebDy5LOIhrgcOSVE0mAMBA9XPuQiF65l +O6Bk+++PAgMBAAECggEAB3Z+kCgyr60thCTFhd8TquObYT4XGliv9Y8NveyFPGy7 +D7zBZO9SVUT4RoJSpw1a5xa6VW59tzZZPtQE+AYf8RAfFy2KOObtqWJobexKY6f1 +lRFJThg38VomqD39kQJxnLX+nQtIAFne/G6SRuaXMQ4yUA9fNFz7QKi+0WX0Cxbs +uMd0uTMnZorri/YGGMko/0jJrQXY4H1Oufc7Pawdr1jxJWLMqJ6Q26Xu4NGFcGFJ +bHsCtewIFdLvBmV+ef5V+JDWItYC7AkIhbibsGkZFKFbPRo3z+kx/lkB3IluRVcD +oH9wVm9YNkz51NH4FLdQPEGdySuzjiaF9RbDqp+w4QKBgQDKEesxK+Tm3q0AONxI +ELsdyfLyo76NrYfcBYvQs/zevlmgqy3uSRNMWWQE3Yq+NQVGT9NjF8cQn3KN1C6F +QIrGtbIAy1nQAG5KIp1OAkrG/dt3eVlP+9N5cJNZlQNEm0v3qA965hql5zjpM0wO +sk1tvMMa9++Cj4A6s7LVcCpl7wKBgQDl8qPBMgocbBJl1y00/Cw6S8wso7yaGSGz +OVpIpsSaVyVWWWF5H18/NlgrKKOALWkfREX0jbHH7FBxruUTz6FmEs/iE09JVsD5 +UMsYX9dMzbKUTtzw2vOKIJDNaWnbsTNDJlO89boUJrKnwQKAHfQZJU9uiiaoVIpK +XrYLXZmwYQKBgQC6Yda+zw7eSCvoVYoRSqVc76YQWipsAdCbh94TjcDDL236PYor +DOoo9RbFShcsJDmORhjjgM4TLg76dOjH7eVTLcpW4zofGhageNcBWing68wfoiVY +Gh5QGB9BdKnEAT4L288Te+S+e8zJhJA1yg6TFpYbbO9VTMlo29Eq/7+LrwKBgBai +arISreIcVTdHFgEYLXZTjbZ7K45zmNiedZ+fIs0adOdqBuk4SFTdkZI1/toYHjfg +rY4kAHLwdP6ru4rWrklw4pubUPukGXyxEjzE+llqCgEFPkRLGRvolrhRfwUMDUK3 +3BhGi9l98aoHmqpnyGZNQONdn+6D29T0O7EktoMhAoGAbKArB67pRoAOYX+1rc+i +OypUSXFUMyRNcuF8DHF9iKNU6/fXwmkPyfgUmZ/uIbTiiPaM+KI4/UBah4tKKXis +srzxm/1fln0zHJW+h5SnMp483Juml1yZenhYVlyfMs8ZpdpLDZbylvc5SK8m8Fau +75bAWg06Z28TEjq18O7L798= +-----END PRIVATE KEY-----
diff --git a/platform/build.gradle b/platform/build.gradle index 7a4eecd..eee8d25 100644 --- a/platform/build.gradle +++ b/platform/build.gradle
@@ -99,7 +99,9 @@ exclude module: 'appcompat-v7' exclude module: 'design' }) - testImplementation project(':conscrypt-testing'), + testCompileOnly project(':conscrypt-android-stub'), + project(':conscrypt-libcore-stub') + testImplementation project(path: ":conscrypt-testing", configuration: "runtime"), libraries.junit compileOnly project(':conscrypt-android-stub'), project(':conscrypt-libcore-stub') @@ -109,7 +111,7 @@ } // Disable running the tests. - tasks.withType(Test){ + tasks.withType(Test).configureEach { enabled = false } @@ -117,7 +119,7 @@ logger.warn('Android SDK has not been detected. The Android Platform module will not be built.') // Disable all tasks - tasks.collect { + tasks.configureEach { it.enabled = false } }
diff --git a/platform/src/main/java/org/conscrypt/CertBlacklistImpl.java b/platform/src/main/java/org/conscrypt/CertBlocklistImpl.java similarity index 82% rename from platform/src/main/java/org/conscrypt/CertBlacklistImpl.java rename to platform/src/main/java/org/conscrypt/CertBlocklistImpl.java index c2cdd80..2428d4c 100644 --- a/platform/src/main/java/org/conscrypt/CertBlacklistImpl.java +++ b/platform/src/main/java/org/conscrypt/CertBlocklistImpl.java
@@ -37,29 +37,29 @@ import java.util.logging.Logger; @Internal -public final class CertBlacklistImpl implements CertBlacklist { - private static final Logger logger = Logger.getLogger(CertBlacklistImpl.class.getName()); +public final class CertBlocklistImpl implements CertBlocklist { + private static final Logger logger = Logger.getLogger(CertBlocklistImpl.class.getName()); - private final Set<BigInteger> serialBlacklist; - private final Set<ByteString> pubkeyBlacklist; + private final Set<BigInteger> serialBlocklist; + private final Set<ByteString> pubkeyBlocklist; /** * public for testing only. */ - public CertBlacklistImpl(Set<BigInteger> serialBlacklist, Set<ByteString> pubkeyBlacklist) { - this.serialBlacklist = serialBlacklist; - this.pubkeyBlacklist = pubkeyBlacklist; + public CertBlocklistImpl(Set<BigInteger> serialBlocklist, Set<ByteString> pubkeyBlocklist) { + this.serialBlocklist = serialBlocklist; + this.pubkeyBlocklist = pubkeyBlocklist; } - public static CertBlacklist getDefault() { + public static CertBlocklist getDefault() { String androidData = System.getenv("ANDROID_DATA"); - String blacklistRoot = androidData + "/misc/keychain/"; - String defaultPubkeyBlacklistPath = blacklistRoot + "pubkey_blacklist.txt"; - String defaultSerialBlacklistPath = blacklistRoot + "serial_blacklist.txt"; + String blocklistRoot = androidData + "/misc/keychain/"; + String defaultPubkeyBlocklistPath = blocklistRoot + "pubkey_blacklist.txt"; + String defaultSerialBlocklistPath = blocklistRoot + "serial_blacklist.txt"; - Set<ByteString> pubkeyBlacklist = readPublicKeyBlackList(defaultPubkeyBlacklistPath); - Set<BigInteger> serialBlacklist = readSerialBlackList(defaultSerialBlacklistPath); - return new CertBlacklistImpl(serialBlacklist, pubkeyBlacklist); + Set<ByteString> pubkeyBlocklist = readPublicKeyBlockList(defaultPubkeyBlocklistPath); + Set<BigInteger> serialBlocklist = readSerialBlockList(defaultSerialBlocklistPath); + return new CertBlocklistImpl(serialBlocklist, pubkeyBlocklist); } private static boolean isHex(String value) { @@ -80,12 +80,13 @@ return isHex(value); } - private static String readBlacklist(String path) { + private static String readBlocklist(String path) { try { return readFileAsString(path); } catch (FileNotFoundException ignored) { + // Ignored } catch (IOException e) { - logger.log(Level.WARNING, "Could not read blacklist", e); + logger.log(Level.WARNING, "Could not read blocklist", e); } return ""; } @@ -122,11 +123,12 @@ } catch (RuntimeException rethrown) { throw rethrown; } catch (Exception ignored) { + // Ignored } } } - private static Set<BigInteger> readSerialBlackList(String path) { + private static Set<BigInteger> readSerialBlockList(String path) { /* Start out with a base set of known bad values. * @@ -153,9 +155,9 @@ )); // attempt to augment it with values taken from gservices - String serialBlacklist = readBlacklist(path); - if (!serialBlacklist.equals("")) { - for (String value : serialBlacklist.split(",", -1)) { + String serialBlocklist = readBlocklist(path); + if (!serialBlocklist.equals("")) { + for (String value : serialBlocklist.split(",", -1)) { try { bl.add(new BigInteger(value, 16)); } catch (NumberFormatException e) { @@ -168,13 +170,13 @@ return Collections.unmodifiableSet(bl); } - private static Set<ByteString> readPublicKeyBlackList(String path) { + private static Set<ByteString> readPublicKeyBlockList(String path) { // start out with a base set of known bad values Set<ByteString> bl = new HashSet<ByteString>(toByteStrings( - // Blacklist test cert for CTS. The cert and key can be found in - // src/test/resources/blacklist_test_ca.pem and - // src/test/resources/blacklist_test_ca_key.pem. + // Blocklist test cert for CTS. The cert and key can be found in + // src/test/resources/blocklist_test_ca.pem and + // src/test/resources/blocklist_test_ca_key.pem. "bae78e6bed65a2bf60ddedde7fd91e825865e93d".getBytes(UTF_8), // From http://src.chromium.org/viewvc/chrome/branches/782/src/net/base/x509_certificate.cc?r1=98750&r2=98749&pathrev=98750 // C=NL, O=DigiNotar, CN=DigiNotar Root CA/emailAddress=info@diginotar.nl @@ -207,14 +209,14 @@ )); // attempt to augment it with values taken from gservices - String pubkeyBlacklist = readBlacklist(path); - if (!pubkeyBlacklist.equals("")) { - for (String value : pubkeyBlacklist.split(",", -1)) { + String pubkeyBlocklist = readBlocklist(path); + if (!pubkeyBlocklist.equals("")) { + for (String value : pubkeyBlocklist.split(",", -1)) { value = value.trim(); if (isPubkeyHash(value)) { bl.add(new ByteString(value.getBytes(UTF_8))); } else { - logger.log(Level.WARNING, "Tried to blacklist invalid pubkey " + value); + logger.log(Level.WARNING, "Tried to blocklist invalid pubkey " + value); } } } @@ -223,7 +225,7 @@ } @Override - public boolean isPublicKeyBlackListed(PublicKey publicKey) { + public boolean isPublicKeyBlockListed(PublicKey publicKey) { byte[] encoded = publicKey.getEncoded(); MessageDigest md; try { @@ -233,8 +235,8 @@ return false; } byte[] out = toHex(md.digest(encoded)); - for (ByteString blacklisted : pubkeyBlacklist) { - if (Arrays.equals(blacklisted.bytes, out)) { + for (ByteString blocklisted : pubkeyBlocklist) { + if (Arrays.equals(blocklisted.bytes, out)) { return true; } } @@ -257,8 +259,8 @@ } @Override - public boolean isSerialNumberBlackListed(BigInteger serial) { - return serialBlacklist.contains(serial); + public boolean isSerialNumberBlockListed(BigInteger serial) { + return serialBlocklist.contains(serial); } private static List<ByteString> toByteStrings(byte[]... allBytes) {
diff --git a/platform/src/main/java/org/conscrypt/Platform.java b/platform/src/main/java/org/conscrypt/Platform.java index 7d6a002..7ce64d8 100644 --- a/platform/src/main/java/org/conscrypt/Platform.java +++ b/platform/src/main/java/org/conscrypt/Platform.java
@@ -26,6 +26,7 @@ import dalvik.system.CloseGuard; import java.io.FileDescriptor; import java.io.IOException; +import java.lang.System; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -48,6 +49,7 @@ import java.util.Collections; import java.util.List; import javax.crypto.spec.GCMParameterSpec; +import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SNIHostName; import javax.net.ssl.SNIMatcher; import javax.net.ssl.SNIServerName; @@ -63,6 +65,9 @@ import org.conscrypt.ct.CTLogStoreImpl; import org.conscrypt.ct.CTPolicy; import org.conscrypt.ct.CTPolicyImpl; +import org.conscrypt.metrics.CipherSuite; +import org.conscrypt.metrics.ConscryptStatsLog; +import org.conscrypt.metrics.Protocol; import sun.security.x509.AlgorithmId; final class Platform { @@ -503,8 +508,8 @@ return new TrustedCertificateStore(); } - static CertBlacklist newDefaultBlacklist() { - return CertBlacklistImpl.getDefault(); + static CertBlocklist newDefaultBlocklist() { + return CertBlocklistImpl.getDefault(); } static CTLogStore newDefaultLogStore() { @@ -530,4 +535,30 @@ } return false; } + + public static ConscryptHostnameVerifier getDefaultHostnameVerifier() { + return Conscrypt.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier()); + } + + /** + * Returns milliseconds elapsed since boot, including time spent in sleep. + * @return long number of milliseconds elapsed since boot + */ + static long getMillisSinceBoot() { + return System.currentTimeMillis(); + } + + static void countTlsHandshake( + boolean success, String protocol, String cipherSuite, long duration) { + Protocol proto = Protocol.forName(protocol); + CipherSuite suite = CipherSuite.forName(cipherSuite); + int dur = (int) duration; + + ConscryptStatsLog.write(ConscryptStatsLog.TLS_HANDSHAKE_REPORTED, success, proto.getId(), + suite.getId(), dur); + } + + public static boolean isJavaxCertificateSupported() { + return true; + } }
diff --git a/platform/src/main/java/org/conscrypt/TrustedCertificateStore.java b/platform/src/main/java/org/conscrypt/TrustedCertificateStore.java index c8f1625..333450d 100644 --- a/platform/src/main/java/org/conscrypt/TrustedCertificateStore.java +++ b/platform/src/main/java/org/conscrypt/TrustedCertificateStore.java
@@ -59,9 +59,8 @@ * <p>In addition to supporting the {@code * TrustedCertificateKeyStoreSpi} implementation, {@code * TrustedCertificateStore} also provides the additional public - * methods {@link #isTrustAnchor} and {@link #findIssuer} to allow - * efficient lookup operations for CAs again based on the file naming - * convention. + * method {@link #findIssuer} to allow efficient lookup operations + * for CAs again based on the file naming convention. * * <p>The KeyChainService users the {@link installCertificate} and * {@link #deleteCertificateEntry} to install user CAs as well as @@ -217,6 +216,7 @@ return getCertificateFile(deletedDir, x).exists(); } + @SuppressWarnings("JdkObsolete") // Used in public API TrustedCertificateKeyStoreSpi public Date getCreationDate(String alias) { // containsAlias check ensures the later fileForAlias result // was not a deleted system cert.
diff --git a/platform/src/main/java/org/conscrypt/ct/CTLogStoreImpl.java b/platform/src/main/java/org/conscrypt/ct/CTLogStoreImpl.java index fee9c3f..0f37a8d 100644 --- a/platform/src/main/java/org/conscrypt/ct/CTLogStoreImpl.java +++ b/platform/src/main/java/org/conscrypt/ct/CTLogStoreImpl.java
@@ -23,6 +23,7 @@ import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; @@ -37,7 +38,7 @@ @Internal public class CTLogStoreImpl implements CTLogStore { - private static final Charset US_ASCII = Charset.forName("US-ASCII"); + private static final Charset US_ASCII = StandardCharsets.US_ASCII; /** * Thrown when parsing of a log file fails. @@ -70,12 +71,13 @@ defaultSystemLogDir = new File(ANDROID_ROOT + "/etc/security/ct_known_logs/"); } - private File userLogDir; - private File systemLogDir; - private CTLogInfo[] fallbackLogs; + private final File userLogDir; + private final File systemLogDir; + private final CTLogInfo[] fallbackLogs; - private HashMap<ByteBuffer, CTLogInfo> logCache = new HashMap<>(); - private Set<ByteBuffer> missingLogCache = Collections.synchronizedSet(new HashSet<ByteBuffer>()); + private final HashMap<ByteBuffer, CTLogInfo> logCache = new HashMap<>(); + private final Set<ByteBuffer> missingLogCache + = Collections.synchronizedSet(new HashSet<ByteBuffer>()); public CTLogStoreImpl() { this(defaultUserLogDir, @@ -116,13 +118,17 @@ return loadLog(new File(userLogDir, filename)); } catch (InvalidLogFileException e) { return null; - } catch (FileNotFoundException e) {} + } catch (FileNotFoundException e) { + // Ignored + } try { return loadLog(new File(systemLogDir, filename)); } catch (InvalidLogFileException e) { return null; - } catch (FileNotFoundException e) {} + } catch (FileNotFoundException e) { + // Ignored + } // If the updateable logs dont exist then use the fallback logs. if (!userLogDir.exists()) {
diff --git a/platform/src/test/java/org/conscrypt/CertBlacklistTest.java b/platform/src/test/java/org/conscrypt/CertBlacklistTest.java deleted file mode 100644 index 3a9a85a..0000000 --- a/platform/src/test/java/org/conscrypt/CertBlacklistTest.java +++ /dev/null
@@ -1,136 +0,0 @@ -/* - * Copyright (C) 2016 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.assertTrue; -import static org.junit.Assert.fail; - -import java.io.InputStream; -import java.security.KeyStore; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.Collection; -import javax.net.ssl.X509TrustManager; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public class CertBlacklistTest { - - private static final String BLACKLIST_CA = "test_blacklist_ca.pem"; - private static final String BLACKLISTED_CHAIN = "blacklist_test_chain.pem"; - private static final String BLACKLIST_FALLBACK_VALID_CA = "blacklist_test_valid_ca.pem"; - private static final String BLACKLISTED_VALID_CHAIN = "blacklist_test_valid_chain.pem"; - - /** - * Ensure that the test blacklisted CA is actually blacklisted by default. - */ - @Test - public void testBlacklistedPublicKey() throws Exception { - // This class was renamed in the Android 12 based Conscrypt module. - // If such a module is installed, simply skip the test as it is covered by MTS - TestUtils.assumeBeforeAndroid12Mainline(); - X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA); - CertBlacklist blacklist = CertBlacklistImpl.getDefault(); - assertTrue(blacklist.isPublicKeyBlackListed(blacklistedCa.getPublicKey())); - } - - /** - * Check that the blacklisted CA is rejected even if it used as a root of trust - */ - @Test - public void testBlacklistedCaUntrusted() throws Exception { - X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA); - assertUntrusted(new X509Certificate[] {blacklistedCa}, getTrustManager(blacklistedCa)); - } - - /** - * Check that a chain that is rooted in a blacklisted trusted CA is rejected. - */ - @Test - public void testBlacklistedRootOfTrust() throws Exception { - // Chain is leaf -> blacklisted - X509Certificate[] chain = loadCertificates(BLACKLISTED_CHAIN); - X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA); - assertUntrusted(chain, getTrustManager(blacklistedCa)); - } - - /** Test that the path building correctly routes around a blacklisted cert where there are - * other valid paths available. This prevents breakage where a cert was cross signed by a - * blacklisted CA but is still valid due to also being cross signed by CAs that remain trusted. - * Path: - * - * leaf -> intermediate -> blacklisted_ca - * \ - * -------> trusted_ca - */ - @Test - public void testBlacklistedIntermediateFallback() throws Exception { - X509Certificate[] chain = loadCertificates(BLACKLISTED_VALID_CHAIN); - X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA); - X509Certificate validCa = loadCertificate(BLACKLIST_FALLBACK_VALID_CA); - assertTrusted(chain, getTrustManager(blacklistedCa, validCa)); - // Check that without the trusted_ca the chain is invalid (since it only chains to a - // blacklisted ca) - assertUntrusted(chain, getTrustManager(blacklistedCa)); - } - - private static X509Certificate loadCertificate(String file) throws Exception { - return loadCertificates(file)[0]; - } - - private static X509Certificate[] loadCertificates(String file) throws Exception { - CertificateFactory factory = CertificateFactory.getInstance("X.509"); - try (InputStream is = TestUtils.openTestFile(file)) { - Collection<? extends Certificate> collection = factory.generateCertificates(is); - is.close(); - X509Certificate[] certs = new X509Certificate[collection.size()]; - int i = 0; - for (Certificate cert : collection) { - certs[i++] = (X509Certificate) cert; - } - return certs; - } - } - - private static TrustManagerImpl getTrustManager(X509Certificate... trustedCas) - throws Exception { - KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); - ks.load(null); - int i = 0; - for (X509Certificate ca : trustedCas) { - ks.setCertificateEntry(String.valueOf(i++), ca); - } - return new TrustManagerImpl(ks); - } - - private static void assertTrusted(X509Certificate[] certs, X509TrustManager tm) - throws Exception { - tm.checkServerTrusted(certs, "RSA"); - } - - private static void assertUntrusted(X509Certificate[] certs, X509TrustManager tm) { - try { - tm.checkServerTrusted(certs, "RSA"); - fail(); - } catch (CertificateException expected) { - } - } -}
diff --git a/platform/src/test/java/org/conscrypt/CertBlocklistTest.java b/platform/src/test/java/org/conscrypt/CertBlocklistTest.java new file mode 100644 index 0000000..39561e5 --- /dev/null +++ b/platform/src/test/java/org/conscrypt/CertBlocklistTest.java
@@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 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 java.io.InputStream; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Collection; +import javax.net.ssl.X509TrustManager; +import junit.framework.TestCase; + +public class CertBlocklistTest extends TestCase { + + private static final String BLOCKLIST_CA = "test_blocklist_ca.pem"; + private static final String BLOCKLISTED_CHAIN = "blocklist_test_chain.pem"; + private static final String BLOCKLIST_FALLBACK_VALID_CA = "blocklist_test_valid_ca.pem"; + private static final String BLOCKLISTED_VALID_CHAIN = "blocklist_test_valid_chain.pem"; + + /** + * Ensure that the test blocklisted CA is actually blocklisted by default. + */ + public void testBlocklistedPublicKey() throws Exception { + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + CertBlocklist blocklist = CertBlocklistImpl.getDefault(); + assertTrue(blocklist.isPublicKeyBlockListed(blocklistedCa.getPublicKey())); + } + + /** + * Check that the blocklisted CA is rejected even if it used as a root of trust + */ + public void testBlocklistedCaUntrusted() throws Exception { + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + assertUntrusted(new X509Certificate[] {blocklistedCa}, getTrustManager(blocklistedCa)); + } + + /** + * Check that a chain that is rooted in a blocklisted trusted CA is rejected. + */ + public void testBlocklistedRootOfTrust() throws Exception { + // Chain is leaf -> blocklisted + X509Certificate[] chain = loadCertificates(BLOCKLISTED_CHAIN); + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + assertUntrusted(chain, getTrustManager(blocklistedCa)); + } + + /** Test that the path building correctly routes around a blocklisted cert where there are + * other valid paths available. This prevents breakage where a cert was cross signed by a + * blocklisted CA but is still valid due to also being cross signed by CAs that remain trusted. + * Path: + * + * leaf -> intermediate -> blocklisted_ca + * \ + * -------> trusted_ca + */ + public void testBlocklistedIntermediateFallback() throws Exception { + X509Certificate[] chain = loadCertificates(BLOCKLISTED_VALID_CHAIN); + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + X509Certificate validCa = loadCertificate(BLOCKLIST_FALLBACK_VALID_CA); + assertTrusted(chain, getTrustManager(blocklistedCa, validCa)); + // Check that without the trusted_ca the chain is invalid (since it only chains to a + // blocklisted ca) + assertUntrusted(chain, getTrustManager(blocklistedCa)); + } + + private static X509Certificate loadCertificate(String file) throws Exception { + return loadCertificates(file)[0]; + } + + private static X509Certificate[] loadCertificates(String file) throws Exception { + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + try (InputStream is = TestUtils.openTestFile(file)) { + Collection<? extends Certificate> collection = factory.generateCertificates(is); + is.close(); + X509Certificate[] certs = new X509Certificate[collection.size()]; + int i = 0; + for (Certificate cert : collection) { + certs[i++] = (X509Certificate) cert; + } + return certs; + } + } + + private static TrustManagerImpl getTrustManager(X509Certificate... trustedCas) + throws Exception { + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(null); + int i = 0; + for (X509Certificate ca : trustedCas) { + ks.setCertificateEntry(String.valueOf(i++), ca); + } + return new TrustManagerImpl(ks); + } + + private static void assertTrusted(X509Certificate[] certs, X509TrustManager tm) + throws Exception { + tm.checkServerTrusted(certs, "RSA"); + } + + private static void assertUntrusted(X509Certificate[] certs, X509TrustManager tm) { + try { + tm.checkServerTrusted(certs, "RSA"); + fail(); + } catch (CertificateException expected) { + } + } +}
diff --git a/platform/src/test/java/org/conscrypt/TrustedCertificateStoreTest.java b/platform/src/test/java/org/conscrypt/TrustedCertificateStoreTest.java index 33e6aac..679f630 100644 --- a/platform/src/test/java/org/conscrypt/TrustedCertificateStoreTest.java +++ b/platform/src/test/java/org/conscrypt/TrustedCertificateStoreTest.java
@@ -878,6 +878,7 @@ assertEquals(x, store.findIssuer(x)); } + @SuppressWarnings("JdkObsolete") // Date is used in public API TrustedCertificateKeyStoreSpi private void assertTrusted(X509Certificate x, String alias) { assertEquals(x, store.getCertificate(alias)); assertEquals(file(alias).lastModified(), store.getCreationDate(alias).getTime());
diff --git a/platform/src/test/java/org/conscrypt/metrics/MetricsTest.java b/platform/src/test/java/org/conscrypt/metrics/MetricsTest.java new file mode 100644 index 0000000..2584469 --- /dev/null +++ b/platform/src/test/java/org/conscrypt/metrics/MetricsTest.java
@@ -0,0 +1,81 @@ +/* + * 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.metrics; + +import static org.junit.Assert.assertEquals; + +import android.util.StatsEvent; +import org.conscrypt.TestUtils; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class MetricsTest { + public static final int TLS_HANDSHAKE_REPORTED = 317; + + // Tests that ReflexiveEvent produces the same event as framework's. + @Test + @Ignore // Ignore on CTS 12 only: b/259508875 + public void test_reflexiveEvent() throws Exception { + TestUtils.assumeStatsLogAvailable(); + + StatsEvent frameworkStatsEvent = StatsEvent.newBuilder() + .setAtomId(TLS_HANDSHAKE_REPORTED) + .writeBoolean(false) + .writeInt(1) // protocol + .writeInt(2) // cipher suite + .writeInt(100) // duration + .usePooledBuffer() + .build(); + + ReflexiveStatsEvent reflexiveStatsEvent = + ReflexiveStatsEvent.buildEvent(TLS_HANDSHAKE_REPORTED, false, 1, 2, 100); + StatsEvent constructedEvent = (StatsEvent) reflexiveStatsEvent.getStatsEvent(); + + // TODO(nikitai): Figure out how to use hidden (@hide) getters from StatsEvent + // to eliminate the use of reflection + int fid = (Integer) frameworkStatsEvent.getClass() + .getMethod("getAtomId") + .invoke(frameworkStatsEvent); + int cid = (Integer) constructedEvent.getClass() + .getMethod("getAtomId") + .invoke(constructedEvent); + assertEquals(fid, cid); + + int fnb = (Integer) frameworkStatsEvent.getClass() + .getMethod("getNumBytes") + .invoke(frameworkStatsEvent); + int cnb = (Integer) constructedEvent.getClass() + .getMethod("getNumBytes") + .invoke(constructedEvent); + assertEquals(fnb, cnb); + + byte[] fbytes = (byte[]) frameworkStatsEvent.getClass() + .getMethod("getBytes") + .invoke(frameworkStatsEvent); + byte[] cbytes = + (byte[]) constructedEvent.getClass().getMethod("getBytes").invoke(constructedEvent); + for (int i = 0; i < fnb; i++) { + // skip encoded timestamp (bytes 1-8) + if (i < 1 || i > 8) { + assertEquals(fbytes[i], cbytes[i]); + } + } + } +}
diff --git a/publicapi/src/main/java/android/net/ssl/SSLEngines.java b/publicapi/src/main/java/android/net/ssl/SSLEngines.java index 13bd771..e5967db 100644 --- a/publicapi/src/main/java/android/net/ssl/SSLEngines.java +++ b/publicapi/src/main/java/android/net/ssl/SSLEngines.java
@@ -18,7 +18,9 @@ import com.android.org.conscrypt.Conscrypt; import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; import libcore.util.NonNull; +import libcore.util.Nullable; /** * Static utility methods for accessing additional functionality of supported instances of @@ -55,4 +57,33 @@ checkSupported(engine); Conscrypt.setUseSessionTickets(engine, useSessionTickets); } + + /** + * Exports a value derived from the TLS master secret as described in RFC 5705. + * + * A number of protocols leverage Transport Layer Security (TLS) to perform key + * establishment but then use some of the keying material for their own purposes. + * + * This method allows an application to export keying material from a TLS connection. + * The exported material will be the same on the client and server if they pass in + * the same values for {@code label} and {@code context}. See RFC 5705 for further + * details. + * + * @param engine the engine to use for exporting keying material + * @param label the label to use in calculating the exported value. This must be + * an ASCII-only string. + * @param context the application-specific context value to use in calculating the + * exported value. This may be {@code null} to use no application context, which is + * treated differently than an empty byte array. + * @param length the number of bytes of keying material to return. + * @return a value of the specified length, or {@code null} if the handshake has not yet + * completed or the connection has been closed. + * @throws SSLException if the value could not be exported. + */ + @Nullable + public static byte[] exportKeyingMaterial(@NonNull SSLEngine engine, @NonNull String label, + @Nullable byte[] context, int length) throws SSLException { + checkSupported(engine); + return Conscrypt.exportKeyingMaterial(engine, label, context, length); + } }
diff --git a/publicapi/src/main/java/android/net/ssl/SSLSockets.java b/publicapi/src/main/java/android/net/ssl/SSLSockets.java index a669404..0c04428 100644 --- a/publicapi/src/main/java/android/net/ssl/SSLSockets.java +++ b/publicapi/src/main/java/android/net/ssl/SSLSockets.java
@@ -17,8 +17,10 @@ package android.net.ssl; import com.android.org.conscrypt.Conscrypt; +import javax.net.ssl.SSLException; import javax.net.ssl.SSLSocket; import libcore.util.NonNull; +import libcore.util.Nullable; /** * Static utility methods for accessing additional functionality of supported instances of @@ -55,4 +57,33 @@ checkSupported(socket); Conscrypt.setUseSessionTickets(socket, useSessionTickets); } + + /** + * Exports a value derived from the TLS master secret as described in RFC 5705. + * + * A number of protocols leverage Transport Layer Security (TLS) to perform key + * establishment but then use some of the keying material for their own purposes. + * + * This method allows an application to export keying material from a TLS connection. + * The exported material will be the same on the client and server if they pass in + * the same values for {@code label} and {@code context}. See RFC 5705 for further + * details. + * + * @param socket the socket to use for exporting keying material + * @param label the label to use in calculating the exported value. This must be + * an ASCII-only string. + * @param context the application-specific context value to use in calculating the + * exported value. This may be {@code null} to use no application context, which is + * treated differently than an empty byte array. + * @param length the number of bytes of keying material to return. + * @return a value of the specified length, or {@code null} if the handshake has not yet + * completed or the connection has been closed. + * @throws SSLException if the value could not be exported. + */ + @Nullable + public static byte[] exportKeyingMaterial(@NonNull SSLSocket socket, @NonNull String label, + @Nullable byte[] context, int length) throws SSLException { + checkSupported(socket); + return Conscrypt.exportKeyingMaterial(socket, label, context, length); + } }
diff --git a/publicapi/src/test/java/android/net/ssl/SSLEnginesTest.java b/publicapi/src/test/java/android/net/ssl/SSLEnginesTest.java index 66abec6..e4285f7 100644 --- a/publicapi/src/test/java/android/net/ssl/SSLEnginesTest.java +++ b/publicapi/src/test/java/android/net/ssl/SSLEnginesTest.java
@@ -16,12 +16,15 @@ package android.net.ssl; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import com.android.org.conscrypt.javax.net.ssl.TestSSLContext; +import com.android.org.conscrypt.javax.net.ssl.TestSSLEnginePair; import com.android.org.conscrypt.tlswire.TlsTester; import com.android.org.conscrypt.tlswire.handshake.ClientHello; import com.android.org.conscrypt.tlswire.handshake.HelloExtension; @@ -29,7 +32,6 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,11 +42,11 @@ private static class BrokenSSLEngine extends SSLEngine { @Override public SSLEngineResult wrap(ByteBuffer[] byteBuffers, int i, int i1, - ByteBuffer byteBuffer) throws SSLException { throw new AssertionError(); } + ByteBuffer byteBuffer) { throw new AssertionError(); } @Override public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers, - int i, int i1) throws SSLException { throw new AssertionError(); } + int i, int i1) { throw new AssertionError(); } @Override public Runnable getDelegatedTask() { throw new AssertionError(); } - @Override public void closeInbound() throws SSLException { throw new AssertionError(); } + @Override public void closeInbound() { throw new AssertionError(); } @Override public boolean isInboundDone() { throw new AssertionError(); } @Override public void closeOutbound() { throw new AssertionError(); } @Override public boolean isOutboundDone() { throw new AssertionError(); } @@ -55,7 +57,7 @@ @Override public String[] getEnabledProtocols() { throw new AssertionError(); } @Override public void setEnabledProtocols(String[] strings) { throw new AssertionError(); } @Override public SSLSession getSession() { throw new AssertionError(); } - @Override public void beginHandshake() throws SSLException { throw new AssertionError(); } + @Override public void beginHandshake() { throw new AssertionError(); } @Override public SSLEngineResult.HandshakeStatus getHandshakeStatus() { throw new AssertionError(); } @Override public void setUseClientMode(boolean b) { throw new AssertionError(); } @Override public boolean getUseClientMode() { throw new AssertionError(); } @@ -78,14 +80,13 @@ assertFalse(SSLEngines.isSupportedEngine(e)); } - @Test - public void testUseSessionTickets() throws Exception { - try { - SSLEngines.setUseSessionTickets(new BrokenSSLEngine(), true); - fail(); - } catch (IllegalArgumentException expected) { - } + @Test(expected = IllegalArgumentException.class) + public void useSessionTickets_InvalidEngine() { + SSLEngines.setUseSessionTickets(new BrokenSSLEngine(), true); + } + @Test + public void useSessionTickets_ValidEngine() throws Exception { SSLEngine e = SSLContext.getDefault().createSSLEngine(); e.setUseClientMode(true); SSLEngines.setUseSessionTickets(e, true); @@ -111,4 +112,25 @@ return TlsTester.parseClientHello(data); } + + @Test(expected = IllegalArgumentException.class) + public void exportKeyingMaterial_InvalidEngine() throws Exception { + SSLEngines.exportKeyingMaterial(new BrokenSSLEngine(), "label", null, 20); + } + + @Test + public void exportKeyingMaterial_ValidEngine() throws Exception { + String label = "Some label"; + int keyLength = 32; + + TestSSLEnginePair pair = TestSSLEnginePair.create(TestSSLContext.create()); + + byte[] clientEkm = SSLEngines.exportKeyingMaterial(pair.client, label, null, keyLength); + byte[] serverEkm = SSLEngines.exportKeyingMaterial(pair.server, label, null, keyLength); + assertNotNull(clientEkm); + assertNotNull(serverEkm); + assertEquals(keyLength, clientEkm.length); + assertEquals(keyLength, serverEkm.length); + assertArrayEquals(clientEkm, serverEkm); + } }
diff --git a/publicapi/src/test/java/android/net/ssl/SSLSocketsTest.java b/publicapi/src/test/java/android/net/ssl/SSLSocketsTest.java index 02df047..78a42c5 100644 --- a/publicapi/src/test/java/android/net/ssl/SSLSocketsTest.java +++ b/publicapi/src/test/java/android/net/ssl/SSLSocketsTest.java
@@ -16,12 +16,14 @@ package android.net.ssl; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import com.android.org.conscrypt.javax.net.ssl.TestSSLSocketPair; import com.android.org.conscrypt.tlswire.TlsTester; import com.android.org.conscrypt.tlswire.handshake.ClientHello; import com.android.org.conscrypt.tlswire.handshake.HelloExtension; @@ -87,14 +89,13 @@ assertFalse(SSLSockets.isSupportedSocket(s)); } - @Test - public void testUseSessionTickets() throws Exception { - try { - SSLSockets.setUseSessionTickets(new BrokenSSLSocket(), true); - fail(); - } catch (IllegalArgumentException expected) { - } + @Test(expected = IllegalArgumentException.class) + public void setUseSessionTickets_InvalidSocket() { + SSLSockets.setUseSessionTickets(new BrokenSSLSocket(), true); + } + @Test + public void setUseSessionTickets_ValidSocket() throws Exception { SSLSocket s = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); SSLSockets.setUseSessionTickets(s, true); @@ -116,4 +117,26 @@ }); assertNull(hello.findExtensionByType(HelloExtension.TYPE_SESSION_TICKET)); } + + @Test(expected = IllegalArgumentException.class) + public void exportKeyingMaterial_InvalidSocket() throws Exception { + SSLSockets.exportKeyingMaterial(new BrokenSSLSocket(), "label", null, 20); + } + + @Test + public void exportKeyingMaterial_ValidSocket() throws Exception { + TestSSLSocketPair pair = TestSSLSocketPair.create(); + String label = "Some label"; + int keyLength = 32; + + pair.connect(); + + byte[] clientEkm = SSLSockets.exportKeyingMaterial(pair.client, label, null, keyLength); + byte[] serverEkm = SSLSockets.exportKeyingMaterial(pair.server, label, null, keyLength); + assertNotNull(clientEkm); + assertNotNull(serverEkm); + assertEquals(keyLength, clientEkm.length); + assertEquals(keyLength, serverEkm.length); + assertArrayEquals(clientEkm, serverEkm); + } }
diff --git a/release/Dockerfile b/release/Dockerfile index f0d5164..c974566 100644 --- a/release/Dockerfile +++ b/release/Dockerfile
@@ -46,9 +46,9 @@ # Build and install CMake from source. WORKDIR /usr/src -RUN git clone git://cmake.org/cmake.git CMake && \ +RUN git clone https://gitlab.kitware.com/cmake/cmake.git CMake && \ cd CMake && \ - git checkout v3.4.1 && \ + git checkout tags/v3.5.2 && \ mkdir /usr/src/CMake-build && \ cd /usr/src/CMake-build && \ /usr/src/CMake/bootstrap \
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractConscryptSocket.java b/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractConscryptSocket.java index 6284fd9..f95d995 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractConscryptSocket.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractConscryptSocket.java
@@ -517,7 +517,7 @@ * Returns the hostname that was supplied during socket creation. No DNS resolution is * attempted before returning the hostname. */ - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) String getHostname() { return peerHostname; } @@ -529,7 +529,7 @@ */ @android.compat.annotation. UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, - publicAlternatives = "Use {@link javax.net.ssl.SSLParameters#setServerNames}.") + publicAlternatives = "Use {@code javax.net.ssl.SSLParameters#setServerNames}.") void setHostname(String hostname) { peerHostname = hostname; @@ -540,7 +540,7 @@ * or the IP address in a textual representation. We do not want to perform reverse DNS * lookups on this address. */ - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) String getHostnameOrIP() { if (peerHostname != null) { return peerHostname; @@ -557,7 +557,7 @@ /** * Note write timeouts are not part of the javax.net.ssl.SSLSocket API */ - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException { throw new SocketException("Method setSoWriteTimeout() is not supported."); } @@ -565,7 +565,7 @@ /** * Note write timeouts are not part of the javax.net.ssl.SSLSocket API */ - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) int getSoWriteTimeout() throws SocketException { return 0; } @@ -574,7 +574,7 @@ * Set the handshake timeout on this socket. This timeout is specified in * milliseconds and will be used only during the handshake process. */ - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException { throw new SocketException("Method setHandshakeTimeout() is not supported."); } @@ -646,7 +646,7 @@ * @throws IllegalStateException if this is a client socket or if the handshake has already * started. */ - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) abstract void setChannelIdEnabled(boolean enabled); /** @@ -659,7 +659,7 @@ * completed. * @throws SSLException if channel ID is available but could not be obtained. */ - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) abstract byte[] getChannelId() throws SSLException; /** @@ -674,14 +674,14 @@ * @throws IllegalStateException if this is a server socket or if the handshake has already * started. */ - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) abstract void setChannelIdPrivateKey(PrivateKey privateKey); /** * Returns null always for backward compatibility. * @deprecated NPN is not supported */ - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) @Deprecated byte[] getNpnSelectedProtocol() { return null; @@ -691,7 +691,7 @@ * This method does nothing and is kept for backward compatibility. * @deprecated NPN is not supported */ - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) @Deprecated void setNpnProtocols(byte[] npnProtocols) {} @@ -703,7 +703,7 @@ */ @android.compat.annotation. UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, - publicAlternatives = "Use {@link javax.net.ssl.SSLSocket#getApplicationProtocol()}.") + publicAlternatives = "Use {@code javax.net.ssl.SSLSocket#getApplicationProtocol()}.") @Deprecated abstract byte[] getAlpnSelectedProtocol(); @@ -717,7 +717,7 @@ @android.compat.annotation. UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, publicAlternatives = - "Use {@link javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}.") + "Use {@code javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}.") @Deprecated abstract void setAlpnProtocols(String[] alpnProtocols); @@ -732,7 +732,7 @@ @android.compat.annotation. UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, publicAlternatives = - "Use {@link javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}.") + "Use {@code javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}.") @Deprecated abstract void setAlpnProtocols(byte[] alpnProtocols); @@ -744,7 +744,7 @@ @android.compat.annotation. UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, publicAlternatives = - "Use {@link javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}.") + "Use {@code javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}.") @SuppressWarnings("MissingOverride") // For compiling pre Java 9. abstract void setApplicationProtocols(String[] protocols); @@ -754,7 +754,7 @@ @android.compat.annotation. UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, publicAlternatives = - "Use {@link javax.net.ssl.SSLParameters#getApplicationProtocols()}.") + "Use {@code javax.net.ssl.SSLParameters#getApplicationProtocols()}.") @SuppressWarnings("MissingOverride") // For compiling pre Java 9. abstract String[] getApplicationProtocols();
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractSessionContext.java b/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractSessionContext.java index d777735..6469d95 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractSessionContext.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractSessionContext.java
@@ -42,7 +42,6 @@ final long sslCtxNativePointer = NativeCrypto.SSL_CTX_new(); - @SuppressWarnings("serial") private final Map<ByteArray, NativeSslSession> sessions = new LinkedHashMap<ByteArray, NativeSslSession>() { @Override @@ -77,8 +76,7 @@ // Make a copy of the IDs. final Iterator<NativeSslSession> iter; synchronized (sessions) { - iter = Arrays.asList(sessions.values().toArray(new NativeSslSession[sessions.size()])) - .iterator(); + iter = Arrays.asList(sessions.values().toArray(new NativeSslSession[0])).iterator(); } return new Enumeration<byte[]>() { private NativeSslSession next; @@ -189,6 +187,7 @@ } @Override + @SuppressWarnings("deprecation") protected void finalize() throws Throwable { try { NativeCrypto.SSL_CTX_free(sslCtxNativePointer, this);
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ActiveSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ActiveSession.java index a648395..3ad5950 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/ActiveSession.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ActiveSession.java
@@ -34,7 +34,7 @@ */ final class ActiveSession implements ConscryptSession { private final NativeSsl ssl; - private AbstractSessionContext sessionContext; + private final AbstractSessionContext sessionContext; private byte[] id; private long creationTime; private String protocol; @@ -42,6 +42,7 @@ private String peerHost; private int peerPort = -1; private long lastAccessedTime = 0; + @SuppressWarnings("deprecation") private volatile javax.security.cert.X509Certificate[] peerCertificateChain; private X509Certificate[] localCertificates; private X509Certificate[] peerCertificates; @@ -109,7 +110,7 @@ @Override public List<byte[]> getStatusResponses() { if (peerCertificateOcspData == null) { - return Collections.<byte[]>emptyList(); + return Collections.emptyList(); } return Collections.singletonList(peerCertificateOcspData.clone()); @@ -206,8 +207,13 @@ * be verified. */ @Override + @SuppressWarnings("deprecation") // Public API public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { + if (!Platform.isJavaxCertificateSupported()) { + throw new UnsupportedOperationException("Use getPeerCertificates() instead"); + } + checkPeerCertificatesPresent(); // TODO(nathanmittler): Should we clone? javax.security.cert.X509Certificate[] result = peerCertificateChain;
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/BufferUtils.java b/repackaged/common/src/main/java/com/android/org/conscrypt/BufferUtils.java new file mode 100644 index 0000000..8861531 --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/BufferUtils.java
@@ -0,0 +1,140 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +/* + * Copyright 2021 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 java.lang.Math.min; +import static com.android.org.conscrypt.Preconditions.checkArgument; + +import java.nio.ByteBuffer; + +/** + * Utility methods for dealing with arrays of ByteBuffers. + * + * @hide This class is not part of the Android public SDK API + * @hide This class is not part of the Android public SDK API + */ +public final class BufferUtils { + private BufferUtils() {} + + /** + * Throws {@link IllegalArgumentException} if any of the buffers in the array are null. + */ + public static void checkNotNull(ByteBuffer[] buffers) { + for (ByteBuffer buffer : buffers) { + if (buffer == null) { + throw new IllegalArgumentException("Null buffer in array"); + } + } + } + + /** + * Returns the total number of bytes remaining in the buffer array. + */ + public static long remaining(ByteBuffer[] buffers) { + long size = 0; + for (ByteBuffer buffer : buffers) { + size += buffer.remaining(); + } + return size; + } + + /** + * Marks {@code toConsume} bytes of data as consumed from the buffer array. + * + * @throws IllegalArgumentException if there are fewer than {@code toConsume} bytes remaining + */ + public static void consume(ByteBuffer[] sourceBuffers, int toConsume) { + for (ByteBuffer sourceBuffer : sourceBuffers) { + int amount = min(sourceBuffer.remaining(), toConsume); + if (amount > 0) { + sourceBuffer.position(sourceBuffer.position() + amount); + toConsume -= amount; + if (toConsume == 0) { + break; + } + } + } + if (toConsume > 0) { + throw new IllegalArgumentException("toConsume > data size"); + } + } + + /** + * Looks for a buffer in the buffer array which EITHER is larger than {@code minSize} AND + * has no preceding non-empty buffers OR is the only non-empty buffer in the array. + */ + public static ByteBuffer getBufferLargerThan(ByteBuffer[] buffers, int minSize) { + int length = buffers.length; + for (int i = 0; i < length; i++) { + ByteBuffer buffer = buffers[i]; + int remaining = buffer.remaining(); + if (remaining > 0) { + if (remaining >= minSize) { + return buffer; + } + for (int j = i + 1; j < length; j++) { + if (buffers[j].remaining() > 0) { + return null; + } + } + return buffer; + } + } + return null; + } + + /** + * Copies up to {@code maxAmount} bytes from a buffer array to {@code destination}. + * The copied data is <b>not</b> marked as consumed from the source buffers, on the + * assumption the copy will be passed to some method which will consume between 0 and + * {@code maxAmount} bytes which can then be reflected in the source array using the + * {@code consume()} method. + * + */ + public static ByteBuffer copyNoConsume( + ByteBuffer[] buffers, ByteBuffer destination, int maxAmount) { + checkArgument(destination.remaining() >= maxAmount, "Destination buffer too small"); + int needed = maxAmount; + for (ByteBuffer buffer : buffers) { + int remaining = buffer.remaining(); + if (remaining > 0) { + // If this buffer can fit completely then copy it all, otherwise temporarily + // adjust its limit to fill so as to the output buffer completely + int oldPosition = buffer.position(); + if (remaining <= needed) { + destination.put(buffer); + needed -= remaining; + } else { + int oldLimit = buffer.limit(); + buffer.limit(buffer.position() + needed); + destination.put(buffer); + buffer.limit(oldLimit); + needed = 0; + } + // Restore the buffer's position, the data won't get marked as consumed until + // outputBuffer has been successfully consumed. + buffer.position(oldPosition); + if (needed == 0) { + break; + } + } + } + destination.flip(); + return destination; + } +}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ByteArray.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ByteArray.java index c4f7da3..6bd104d 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/ByteArray.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ByteArray.java
@@ -42,6 +42,9 @@ return false; } ByteArray lhs = (ByteArray) o; + if (hashCode != lhs.hashCode) { + return false; + } return Arrays.equals(bytes, lhs.bytes); } }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/CertBlacklist.java b/repackaged/common/src/main/java/com/android/org/conscrypt/CertBlocklist.java similarity index 87% rename from repackaged/common/src/main/java/com/android/org/conscrypt/CertBlacklist.java rename to repackaged/common/src/main/java/com/android/org/conscrypt/CertBlocklist.java index cecc8ba..14454d2 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/CertBlacklist.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/CertBlocklist.java
@@ -24,15 +24,15 @@ * A set of certificates that are blacklisted from trust. * @hide This class is not part of the Android public SDK API */ -public interface CertBlacklist { +public interface CertBlocklist { /** * Returns whether the given public key is in the blacklist. */ - boolean isPublicKeyBlackListed(PublicKey publicKey); + boolean isPublicKeyBlockListed(PublicKey publicKey); /** * Returns whether the given serial number is in the blacklist. */ - boolean isSerialNumberBlackListed(BigInteger serial); + boolean isSerialNumberBlockListed(BigInteger serial); }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/CertPinManager.java b/repackaged/common/src/main/java/com/android/org/conscrypt/CertPinManager.java index ff3bda2..50a36da 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/CertPinManager.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/CertPinManager.java
@@ -25,7 +25,7 @@ * Interface for classes that implement certificate pinning for use in {@link TrustManagerImpl}. * @hide This class is not part of the Android public SDK API */ -@libcore.api.CorePlatformApi +@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Internal public interface CertPinManager { /**
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/CertificatePriorityComparator.java b/repackaged/common/src/main/java/com/android/org/conscrypt/CertificatePriorityComparator.java index 34d6222..defb37e 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/CertificatePriorityComparator.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/CertificatePriorityComparator.java
@@ -77,6 +77,7 @@ } @Override + @SuppressWarnings("JdkObsolete") // Certificate uses Date public int compare(X509Certificate lhs, X509Certificate rhs) { int result; boolean lhsSelfSigned = lhs.getSubjectDN().equals(lhs.getIssuerDN());
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 2dbdd7b..41bc8d2 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
@@ -28,7 +28,7 @@ * looking to reuse any session for a given host and port. * @hide This class is not part of the Android public SDK API */ -@libcore.api.CorePlatformApi +@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Internal public final class ClientSessionContext extends AbstractSessionContext { /** @@ -54,7 +54,7 @@ */ @android.compat.annotation .UnsupportedAppUsage - @libcore.api.CorePlatformApi + @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/Conscrypt.java b/repackaged/common/src/main/java/com/android/org/conscrypt/Conscrypt.java index 33a3959..1eaed63 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/Conscrypt.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/Conscrypt.java
@@ -23,7 +23,9 @@ import java.security.KeyManagementException; import java.security.PrivateKey; import java.security.Provider; +import java.security.cert.X509Certificate; import java.util.Properties; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContextSpi; @@ -31,6 +33,7 @@ import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLException; import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSessionContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; @@ -41,7 +44,7 @@ * Core API for creating and configuring all Conscrypt types. * @hide This class is not part of the Android public SDK API */ -@libcore.api.CorePlatformApi +@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @SuppressWarnings("unused") public final class Conscrypt { private Conscrypt() {} @@ -59,6 +62,17 @@ } /** + * Return {@code true} if BoringSSL has been built in FIPS mode. + */ + public static boolean isBoringSSLFIPSBuild() { + try { + return NativeCrypto.usesBoringSSL_FIPS_mode(); + } catch (Throwable e) { + return false; + } + } + + /** * @hide This class is not part of the Android public SDK API */ public static class Version { @@ -94,6 +108,7 @@ patch = Integer.parseInt(props.getProperty("com.android.org.conscrypt.version.patch", "-1")); } } catch (IOException e) { + // TODO(prb): This should probably be fatal or have some fallback behaviour } finally { IoUtils.closeQuietly(stream); } @@ -212,7 +227,7 @@ /** * Gets the default X.509 trust manager. */ - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @ExperimentalApi public static X509TrustManager getDefaultX509TrustManager() throws KeyManagementException { checkAvailability(); @@ -793,4 +808,17 @@ public static ConscryptHostnameVerifier getHostnameVerifier(TrustManager trustManager) { return toConscrypt(trustManager).getHostnameVerifier(); } + + /** + * Wraps the HttpsURLConnection.HostnameVerifier into a ConscryptHostnameVerifier + */ + public static ConscryptHostnameVerifier wrapHostnameVerifier(final HostnameVerifier verifier) { + return new ConscryptHostnameVerifier() { + @Override + public boolean verify( + X509Certificate[] certificates, String hostname, SSLSession session) { + return verifier.verify(hostname, session); + } + }; + } }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptCertStore.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptCertStore.java index 472e2da..213f28a 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptCertStore.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptCertStore.java
@@ -26,10 +26,9 @@ * Android platform. * @hide This class is not part of the Android public SDK API */ -@libcore.api.CorePlatformApi +@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Internal public interface ConscryptCertStore { - /** * Returns a stored CA certificate with the same name and public key as the * provided {@link X509Certificate}.
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngine.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngine.java index 59996ba..4e4bd62 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngine.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngine.java
@@ -66,7 +66,6 @@ import static javax.net.ssl.SSLEngineResult.Status.CLOSED; import static javax.net.ssl.SSLEngineResult.Status.OK; -import com.android.org.conscrypt.ExternalSession.Provider; import com.android.org.conscrypt.NativeRef.SSL_SESSION; import com.android.org.conscrypt.NativeSsl.BioWrapper; import com.android.org.conscrypt.SSLParametersImpl.AliasChooser; @@ -81,6 +80,7 @@ import java.security.cert.X509Certificate; import java.security.interfaces.ECKey; import java.security.spec.ECParameterSpec; +import java.util.Arrays; import javax.crypto.SecretKey; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; @@ -157,12 +157,12 @@ * The session object exposed externally from this class. */ private final SSLSession externalSession = - Platform.wrapSSLSession(new ExternalSession(new Provider() { - @Override - public ConscryptSession provideSession() { - return ConscryptEngine.this.provideSession(); - } - })); + Platform.wrapSSLSession(new ExternalSession(new ExternalSession.Provider() { + @Override + public ConscryptSession provideSession() { + return ConscryptEngine.this.provideSession(); + } + })); /** * Private key for the TLS Channel ID extension. This field is client-side only. Set during @@ -587,7 +587,7 @@ SSLSession handshakeSession() { synchronized (ssl) { if (state == STATE_HANDSHAKE_STARTED) { - return Platform.wrapSSLSession(new ExternalSession(new Provider() { + return Platform.wrapSSLSession(new ExternalSession(new ExternalSession.Provider() { @Override public ConscryptSession provideSession() { return ConscryptEngine.this.provideHandshakeSession(); @@ -1389,6 +1389,11 @@ throw new ReadOnlyBufferException(); } + if ((srcsOffset != 0) || (srcsLength != srcs.length)) { + srcs = Arrays.copyOfRange(srcs, srcsOffset, srcsOffset + srcsLength); + } + BufferUtils.checkNotNull(srcs); + synchronized (ssl) { switch (state) { case STATE_MODE_SET: @@ -1428,118 +1433,110 @@ // NEED_WRAP - just fall through to perform the wrap. } - int srcsLen = 0; - final int endOffset = srcsOffset + srcsLength; - for (int i = srcsOffset; i < endOffset; ++i) { - final ByteBuffer src = srcs[i]; - if (src == null) { - throw new IllegalArgumentException("srcs[" + i + "] is null"); - } - if (srcsLen == SSL3_RT_MAX_PLAIN_LENGTH) { - continue; - } - - srcsLen += src.remaining(); - if (srcsLen > SSL3_RT_MAX_PLAIN_LENGTH || srcsLen < 0) { - // If srcLen > MAX_PLAINTEXT_LENGTH or secLen < 0 just set it to - // MAX_PLAINTEXT_LENGTH. - // This also help us to guard against overflow. - // We not break out here as we still need to check for null entries in srcs[]. - srcsLen = SSL3_RT_MAX_PLAIN_LENGTH; - } - } - - if (dst.remaining() < calculateOutNetBufSize(srcsLen)) { + int dataLength = (int) min(BufferUtils.remaining(srcs), SSL3_RT_MAX_PLAIN_LENGTH); + if (dst.remaining() < calculateOutNetBufSize(dataLength)) { return new SSLEngineResult( Status.BUFFER_OVERFLOW, getHandshakeStatusInternal(), 0, 0); } int bytesProduced = 0; int bytesConsumed = 0; - loop: - for (int i = srcsOffset; i < endOffset; ++i) { - final ByteBuffer src = srcs[i]; - checkArgument(src != null, "srcs[%d] is null", i); - while (src.hasRemaining()) { - final SSLEngineResult pendingNetResult; - // Write plaintext application data to the SSL engine - int result = writePlaintextData( - src, min(src.remaining(), SSL3_RT_MAX_PLAIN_LENGTH - bytesConsumed)); - if (result > 0) { - bytesConsumed += result; + if (dataLength > 0) { + // Try and find a single buffer to send, e.g. the first non-empty buffer has + // more than enough data remaining to fill a TLS record. Otherwise copy as much + // data as possible from the source buffers to fill a record. Note the we can't + // mark the data as consumed until we see how much the TLS layer actually consumes. + boolean isCopy = false; + ByteBuffer outputBuffer = + BufferUtils.getBufferLargerThan(srcs, SSL3_RT_MAX_PLAIN_LENGTH); + if (outputBuffer == null) { + // The buffer by getOrCreateLazyDirectBuffer() is also used by + // writePlainTextDataHeap(), but by filling it here the write path will go via + // writePlainTextDataDirect() and the cost will be approximately the same, + // especially if compacting multiple non-direct buffers into a single + // direct one. + // TODO(): use bufferAllocator if set. + // https://github.com/google/conscrypt/issues/974 + outputBuffer = BufferUtils.copyNoConsume( + srcs, getOrCreateLazyDirectBuffer(), SSL3_RT_MAX_PLAIN_LENGTH); + isCopy = true; + } + final SSLEngineResult pendingNetResult; + // Write plaintext application data to the SSL engine + int result = writePlaintextData( + outputBuffer, min(SSL3_RT_MAX_PLAIN_LENGTH, outputBuffer.remaining())); + if (result > 0) { + bytesConsumed = result; + if (isCopy) { + // Data was a copy, so mark it as consumed in the original buffers. + BufferUtils.consume(srcs, bytesConsumed); + } - pendingNetResult = readPendingBytesFromBIO( + pendingNetResult = readPendingBytesFromBIO( dst, bytesConsumed, bytesProduced, handshakeStatus); - if (pendingNetResult != null) { - if (pendingNetResult.getStatus() != OK) { - return pendingNetResult; - } - bytesProduced = pendingNetResult.bytesProduced(); + if (pendingNetResult != null) { + if (pendingNetResult.getStatus() != OK) { + return pendingNetResult; } - if (bytesConsumed == SSL3_RT_MAX_PLAIN_LENGTH) { - // If we consumed the maximum amount of bytes for the plaintext length - // break out of the loop and start to fill the dst buffer. - break loop; - } - } else { - int sslError = ssl.getError(result); - switch (sslError) { - case SSL_ERROR_ZERO_RETURN: - // This means the connection was shutdown correctly, close inbound - // and outbound - closeAll(); - pendingNetResult = readPendingBytesFromBIO( - dst, bytesConsumed, bytesProduced, handshakeStatus); - return pendingNetResult != null ? pendingNetResult - : CLOSED_NOT_HANDSHAKING; - case SSL_ERROR_WANT_READ: - // If there is no pending data to read from BIO we should go back to - // event loop and try - // to read more data [1]. It is also possible that event loop will - // detect the socket - // has been closed. [1] - // https://www.openssl.org/docs/manmaster/man3/SSL_write.html - pendingNetResult = readPendingBytesFromBIO( - dst, bytesConsumed, bytesProduced, handshakeStatus); - return pendingNetResult != null - ? pendingNetResult - : new SSLEngineResult(getEngineStatus(), NEED_UNWRAP, - bytesConsumed, bytesProduced); - case SSL_ERROR_WANT_WRITE: - // SSL_ERROR_WANT_WRITE typically means that the underlying - // transport is not writable - // and we should set the "want write" flag on the selector and try - // again when the - // underlying transport is writable [1]. However we are not directly - // writing to the - // underlying transport and instead writing to a BIO buffer. The - // OpenSsl documentation - // says we should do the following [1]: - // - // "When using a buffering BIO, like a BIO pair, data must be - // written into or retrieved - // out of the BIO before being able to continue." - // - // So we attempt to drain the BIO buffer below, but if there is no - // data this condition - // is undefined and we assume their is a fatal error with the - // openssl engine and close. - // [1] https://www.openssl.org/docs/manmaster/man3/SSL_write.html - pendingNetResult = readPendingBytesFromBIO( - dst, bytesConsumed, bytesProduced, handshakeStatus); - return pendingNetResult != null ? pendingNetResult - : NEED_WRAP_CLOSED; - default: - // Everything else is considered as error - closeAll(); - throw newSslExceptionWithMessage("SSL_write"); - } + bytesProduced = pendingNetResult.bytesProduced(); + } + } else { + int sslError = ssl.getError(result); + switch (sslError) { + case SSL_ERROR_ZERO_RETURN: + // This means the connection was shutdown correctly, close inbound + // and outbound + closeAll(); + pendingNetResult = readPendingBytesFromBIO( + dst, bytesConsumed, bytesProduced, handshakeStatus); + return pendingNetResult != null ? pendingNetResult + : CLOSED_NOT_HANDSHAKING; + case SSL_ERROR_WANT_READ: + // If there is no pending data to read from BIO we should go back to + // event loop and try + // to read more data [1]. It is also possible that event loop will + // detect the socket + // has been closed. [1] + // https://www.openssl.org/docs/manmaster/man3/SSL_write.html + pendingNetResult = readPendingBytesFromBIO( + dst, bytesConsumed, bytesProduced, handshakeStatus); + return pendingNetResult != null + ? pendingNetResult + : new SSLEngineResult(getEngineStatus(), NEED_UNWRAP, + bytesConsumed, bytesProduced); + case SSL_ERROR_WANT_WRITE: + // SSL_ERROR_WANT_WRITE typically means that the underlying + // transport is not writable + // and we should set the "want write" flag on the selector and try + // again when the + // underlying transport is writable [1]. However we are not directly + // writing to the + // underlying transport and instead writing to a BIO buffer. The + // OpenSsl documentation + // says we should do the following [1]: + // + // "When using a buffering BIO, like a BIO pair, data must be + // written into or retrieved + // out of the BIO before being able to continue." + // + // So we attempt to drain the BIO buffer below, but if there is no + // data this condition + // is undefined and we assume their is a fatal error with the + // openssl engine and close. + // [1] https://www.openssl.org/docs/manmaster/man3/SSL_write.html + pendingNetResult = readPendingBytesFromBIO( + dst, bytesConsumed, bytesProduced, handshakeStatus); + return pendingNetResult != null ? pendingNetResult : NEED_WRAP_CLOSED; + default: + // Everything else is considered as error + closeAll(); + throw newSslExceptionWithMessage("SSL_write: error " + sslError); } } } - // We need to check if pendingWrittenBytesInBIO was checked yet, as we may not checked - // if the srcs was - // empty, or only contained empty buffers. + + // We need to check if pendingWrittenBytesInBIO was checked yet, as we may not have + // checked if the srcs was empty, or only contained empty buffers. if (bytesConsumed == 0) { SSLEngineResult pendingNetResult = readPendingBytesFromBIO(dst, 0, bytesProduced, handshakeStatus); @@ -1547,9 +1544,6 @@ return pendingNetResult; } } - - // return new SSLEngineResult(OK, getHandshakeStatusInternal(), bytesConsumed, - // bytesProduced); return newResult(bytesConsumed, bytesProduced, handshakeStatus); } } @@ -1671,16 +1665,19 @@ private void closeAndFreeResources() { transitionTo(STATE_CLOSED); - if (!ssl.isClosed()) { + if (ssl != null) { ssl.close(); + } + if (networkBio != null) { networkBio.close(); } } @Override + @SuppressWarnings("deprecation") protected void finalize() throws Throwable { try { - transitionTo(STATE_CLOSED); + closeAndFreeResources(); } finally { super.finalize(); }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngineSocket.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngineSocket.java index 0eee041..69a341f 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngineSocket.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngineSocket.java
@@ -61,6 +61,8 @@ private SSLOutputStream out; private SSLInputStream in; + private long handshakeStartedMillis; + private BufferAllocator bufferAllocator = ConscryptEngine.getDefaultBufferAllocator(); // @GuardedBy("stateLock"); @@ -201,6 +203,7 @@ // Initialize the handshake if we haven't already. if (state == STATE_NEW) { state = STATE_HANDSHAKE_STARTED; + handshakeStartedMillis = Platform.getMillisSinceBoot(); engine.beginHandshake(); in = new SSLInputStream(); out = new SSLOutputStream(); @@ -255,6 +258,9 @@ case FINISHED: { // Handshake is complete. finished = true; + Platform.countTlsHandshake(true, engine.getSession().getProtocol(), + engine.getSession().getCipherSuite(), + Platform.getMillisSinceBoot() - handshakeStartedMillis); break; } default: { @@ -265,6 +271,9 @@ } } catch (SSLException e) { drainOutgoingQueue(); + Platform.countTlsHandshake(false, engine.getSession().getProtocol(), + engine.getSession().getCipherSuite(), + Platform.getMillisSinceBoot() - handshakeStartedMillis); close(); throw e; } catch (IOException e) { @@ -370,7 +379,7 @@ */ @android.compat.annotation. UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, - publicAlternatives = "Use {@link javax.net.ssl.SSLParameters#setServerNames}.") + publicAlternatives = "Use {@code javax.net.ssl.SSLParameters#setServerNames}.") @Override public final void setHostname(String hostname) {
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptFileDescriptorSocket.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptFileDescriptorSocket.java index ebe99f4..67a1ede 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptFileDescriptorSocket.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptFileDescriptorSocket.java
@@ -108,16 +108,18 @@ * The session object exposed externally from this class. */ private final SSLSession externalSession = - Platform.wrapSSLSession(new ExternalSession(new Provider() { - @Override - public ConscryptSession provideSession() { - return ConscryptFileDescriptorSocket.this.provideSession(); - } - })); + Platform.wrapSSLSession(new ExternalSession(new ExternalSession.Provider() { + @Override + public ConscryptSession provideSession() { + return ConscryptFileDescriptorSocket.this.provideSession(); + } + })); private int writeTimeoutMilliseconds = 0; private int handshakeTimeoutMilliseconds = -1; // -1 = same as timeout; 0 = infinite + private long handshakeStartedMillis; + // The constructors should not be called except from the Platform class, because we may // want to construct a subclass instead. ConscryptFileDescriptorSocket(SSLParametersImpl sslParameters) throws IOException { @@ -183,6 +185,7 @@ checkOpen(); synchronized (ssl) { if (state == STATE_NEW) { + handshakeStartedMillis = Platform.getMillisSinceBoot(); transitionTo(STATE_HANDSHAKE_STARTED); } else { // We've either started the handshake already or have been closed. @@ -228,6 +231,9 @@ // Update the session from the current state of the SSL object. activeSession.onPeerCertificateAvailable(getHostnameOrIP(), getPort()); } catch (CertificateException e) { + Platform.countTlsHandshake(false, activeSession.getProtocol(), + activeSession.getCipherSuite(), + Platform.getMillisSinceBoot() - handshakeStartedMillis); SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage()); wrapper.initCause(e); throw wrapper; @@ -285,6 +291,10 @@ } } } catch (SSLProtocolException e) { + Platform.countTlsHandshake(false, activeSession.getProtocol(), + activeSession.getCipherSuite(), + Platform.getMillisSinceBoot() - handshakeStartedMillis); + throw(SSLHandshakeException) new SSLHandshakeException("Handshake failed").initCause(e); } finally { // on exceptional exit, treat the socket as closed @@ -338,6 +348,10 @@ // The handshake has completed successfully ... + Platform.countTlsHandshake(true, activeSession.getProtocol(), + activeSession.getCipherSuite(), + Platform.getMillisSinceBoot() - handshakeStartedMillis); + // First, update the state. synchronized (ssl) { if (state == STATE_CLOSED) { @@ -715,7 +729,7 @@ public final SSLSession getHandshakeSession() { synchronized (ssl) { if (state >= STATE_HANDSHAKE_STARTED && state < STATE_READY) { - return Platform.wrapSSLSession(new ExternalSession(new Provider() { + return Platform.wrapSSLSession(new ExternalSession(new ExternalSession.Provider() { @Override public ConscryptSession provideSession() { return ConscryptFileDescriptorSocket.this.provideHandshakeSession(); @@ -788,7 +802,7 @@ */ @android.compat.annotation. UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, - publicAlternatives = "Use {@link javax.net.ssl.SSLParameters#setServerNames}.") + publicAlternatives = "Use {@code javax.net.ssl.SSLParameters#setServerNames}.") @Override public final void setHostname(String hostname) { @@ -959,7 +973,7 @@ * Note write timeouts are not part of the javax.net.ssl.SSLSocket API */ @Override - public final int getSoWriteTimeout() throws SocketException { + public final int getSoWriteTimeout() { return writeTimeoutMilliseconds; } @@ -1071,6 +1085,7 @@ } @Override + @SuppressWarnings("deprecation") protected final void finalize() throws Throwable { try { /*
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptHostnameVerifier.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptHostnameVerifier.java index a62cf9f..05784bc 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptHostnameVerifier.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptHostnameVerifier.java
@@ -17,6 +17,7 @@ package com.android.org.conscrypt; +import java.security.cert.X509Certificate; import javax.net.ssl.SSLSession; /** @@ -31,6 +32,5 @@ * Returns whether the given hostname is allowable given the peer's authentication information * from the given session. */ - boolean verify(String hostname, SSLSession session); - + boolean verify(X509Certificate[] certs, String hostname, SSLSession session); }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ExternalSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ExternalSession.java index fc7daee..ccdd7bb 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/ExternalSession.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ExternalSession.java
@@ -48,7 +48,7 @@ */ final class ExternalSession implements ConscryptSession { // Use an initial capacity of 2 to keep it small in the average case. - private final HashMap<String, Object> values = new HashMap<String, Object>(2); + private final HashMap<String, Object> values = new HashMap<>(2); private final Provider provider; public ExternalSession(Provider provider) { @@ -112,6 +112,7 @@ } @Override + @SuppressWarnings("deprecation") // Public API public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { return provider.provideSession().getPeerCertificateChain(); } @@ -171,7 +172,7 @@ @Override public String[] getValueNames() { - return values.keySet().toArray(new String[values.size()]); + return values.keySet().toArray(new String[0]); } @Override
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 7f56b7a..cdcae47 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
@@ -40,7 +40,7 @@ * underlying directory at a time. * @hide This class is not part of the Android public SDK API */ -@libcore.api.CorePlatformApi +@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Internal public final class FileClientSessionCache { private static final Logger logger = Logger.getLogger(FileClientSessionCache.class.getName()); @@ -333,7 +333,7 @@ */ @android.compat.annotation .UnsupportedAppUsage - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public static synchronized SSLClientSessionCache usingDirectory(File directory) throws IOException { FileClientSessionCache.Impl cache = caches.get(directory);
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/Java7ExtendedSSLSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/Java7ExtendedSSLSession.java index 5b665da..2911023 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/Java7ExtendedSSLSession.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/Java7ExtendedSSLSession.java
@@ -135,6 +135,7 @@ } @Override + @SuppressWarnings("deprecation") // Public API public final X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { return delegate.getPeerCertificateChain(); }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/Java8ExtendedSSLSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/Java8ExtendedSSLSession.java index 16c4c38..f57a35f 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/Java8ExtendedSSLSession.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/Java8ExtendedSSLSession.java
@@ -36,7 +36,7 @@ public final List<SNIServerName> getRequestedServerNames() { String requestedServerName = delegate.getRequestedServerName(); if (requestedServerName == null) { - return null; + return Collections.emptyList(); } return Collections.singletonList((SNIServerName) new SNIHostName(requestedServerName));
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/KeyManagerImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/KeyManagerImpl.java index 4682e1b..e85392c 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/KeyManagerImpl.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/KeyManagerImpl.java
@@ -56,36 +56,34 @@ /** * Creates Key manager */ + @SuppressWarnings("JdkObsolete") // KeyStore#aliases is the only way of enumerating all entries KeyManagerImpl(KeyStore keyStore, char[] pwd) { - this.hash = new HashMap<String, PrivateKeyEntry>(); + this.hash = new HashMap<>(); final Enumeration<String> aliases; try { aliases = keyStore.aliases(); } catch (KeyStoreException e) { return; } - for (; aliases.hasMoreElements();) { + while (aliases.hasMoreElements()) { final String alias = aliases.nextElement(); try { - if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) { - KeyStore.PrivateKeyEntry entry; + if (keyStore.entryInstanceOf(alias, PrivateKeyEntry.class)) { + PrivateKeyEntry entry; try { - entry = (KeyStore.PrivateKeyEntry) keyStore.getEntry( + entry = (PrivateKeyEntry) keyStore.getEntry( alias, new KeyStore.PasswordProtection(pwd)); } catch (UnsupportedOperationException e) { // If the KeyStore doesn't support getEntry(), as Android Keystore // doesn't, fall back to reading the two values separately. PrivateKey key = (PrivateKey) keyStore.getKey(alias, pwd); Certificate[] certs = keyStore.getCertificateChain(alias); - entry = new KeyStore.PrivateKeyEntry(key, certs); + entry = new PrivateKeyEntry(key, certs); } hash.put(alias, entry); } - } catch (KeyStoreException ignored) { - // Ignored. - } catch (UnrecoverableEntryException ignored) { - // Ignored. - } catch (NoSuchAlgorithmException ignored) { + } catch (KeyStoreException | UnrecoverableEntryException + | NoSuchAlgorithmException ignored) { // Ignored. } } @@ -160,7 +158,7 @@ return null; } List<Principal> issuersList = (issuers == null) ? null : Arrays.asList(issuers); - ArrayList<String> found = new ArrayList<String>(); + ArrayList<String> found = new ArrayList<>(); for (final Map.Entry<String, PrivateKeyEntry> entry : hash.entrySet()) { final String alias = entry.getKey(); final Certificate[] chain = entry.getValue().getCertificateChain(); @@ -225,7 +223,7 @@ } } if (!found.isEmpty()) { - return found.toArray(new String[found.size()]); + return found.toArray(new String[0]); } return null; }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/NativeCrypto.java b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeCrypto.java index df57d05..1edbd50 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/NativeCrypto.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeCrypto.java
@@ -23,6 +23,7 @@ import java.io.OutputStream; import java.net.SocketTimeoutException; import java.nio.Buffer; +import java.nio.ByteBuffer; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.MessageDigest; @@ -209,6 +210,13 @@ static native int ECDSA_verify(byte[] data, byte[] sig, NativeRef.EVP_PKEY pkey); + // --- Curve25519 -------------- + + static native boolean X25519(byte[] out, byte[] privateKey, byte[] publicKey) + throws InvalidKeyException; + + static native void X25519_keypair(byte[] outPublicKey, byte[] outPrivateKey); + // --- Message digest functions -------------- // These return const references @@ -341,11 +349,33 @@ static native int EVP_AEAD_CTX_seal(long evpAead, byte[] key, int tagLengthInBytes, byte[] out, int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength, byte[] ad) - throws ShortBufferException, BadPaddingException, IndexOutOfBoundsException; + throws ShortBufferException, BadPaddingException; + + static native int EVP_AEAD_CTX_seal_buf(long evpAead, byte[] key, int tagLengthInBytes, + ByteBuffer out, byte[] nonce, ByteBuffer input, byte[] ad) + throws ShortBufferException, BadPaddingException; static native int EVP_AEAD_CTX_open(long evpAead, byte[] key, int tagLengthInBytes, byte[] out, int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength, byte[] ad) - throws ShortBufferException, BadPaddingException, IndexOutOfBoundsException; + throws ShortBufferException, BadPaddingException; + + static native int EVP_AEAD_CTX_open_buf(long evpAead, byte[] key, int tagLengthInBytes, + ByteBuffer out, byte[] nonce, ByteBuffer input, byte[] ad) + throws ShortBufferException, BadPaddingException; + + // --- CMAC functions ------------------------------------------------------ + + static native long CMAC_CTX_new(); + + static native void CMAC_CTX_free(long ctx); + + static native void CMAC_Init(NativeRef.CMAC_CTX ctx, byte[] key); + + static native void CMAC_Update(NativeRef.CMAC_CTX ctx, byte[] in, int inOffset, int inLength); + + static native void CMAC_UpdateDirect(NativeRef.CMAC_CTX ctx, long inPtr, int inLength); + + static native byte[] CMAC_Final(NativeRef.CMAC_CTX ctx); // --- HMAC functions ------------------------------------------------------ @@ -427,8 +457,6 @@ static native void X509_free(long x509ctx, OpenSSLX509Certificate holder); - static native long X509_dup(long x509ctx, OpenSSLX509Certificate holder); - static native int X509_cmp(long x509ctx1, OpenSSLX509Certificate holder, long x509ctx2, OpenSSLX509Certificate holder2); static native void X509_print_ex(long bioCtx, long x509ctx, OpenSSLX509Certificate holder, long nmflag, long certflag); @@ -474,7 +502,10 @@ static native void X509_verify(long x509ctx, OpenSSLX509Certificate holder, NativeRef.EVP_PKEY pkeyCtx) throws BadPaddingException; - static native byte[] get_X509_cert_info_enc(long x509ctx, OpenSSLX509Certificate holder); + static native byte[] get_X509_tbs_cert(long x509ctx, OpenSSLX509Certificate holder); + + static native byte[] get_X509_tbs_cert_without_ext( + long x509ctx, OpenSSLX509Certificate holder, String oid); static native byte[] get_X509_signature(long x509ctx, OpenSSLX509Certificate holder); @@ -535,8 +566,6 @@ static native byte[] X509_CRL_get_ext_oid(long x509CrlCtx, OpenSSLX509CRL holder, String oid); - static native void X509_delete_ext(long x509, OpenSSLX509Certificate holder, String oid); - static native long X509_CRL_get_version(long x509CrlCtx, OpenSSLX509CRL holder); static native long X509_CRL_get_ext(long x509CrlCtx, OpenSSLX509CRL holder, String oid); @@ -1437,26 +1466,12 @@ SSLHandshakeCallbacks shc) throws IOException; /** - * Writes data from the given array to the BIO. - */ - static native int ENGINE_SSL_write_BIO_heap(long ssl, NativeSsl ssl_holder, long bioRef, byte[] sourceJava, - int sourceOffset, int sourceLength, SSLHandshakeCallbacks shc) - throws IOException, IndexOutOfBoundsException; - - /** * Reads data from the given BIO into a direct {@link java.nio.ByteBuffer}. */ static native int ENGINE_SSL_read_BIO_direct(long ssl, NativeSsl ssl_holder, long bioRef, long address, int len, SSLHandshakeCallbacks shc) throws IOException; /** - * Reads data from the given BIO into an array. - */ - static native int ENGINE_SSL_read_BIO_heap(long ssl, NativeSsl ssl_holder, long bioRef, byte[] destJava, - int destOffset, int destLength, SSLHandshakeCallbacks shc) - throws IOException, IndexOutOfBoundsException; - - /** * Forces the SSL object to process any data pending in the BIO. */ static native void ENGINE_SSL_force_read(long ssl, NativeSsl ssl_holder, @@ -1470,6 +1485,11 @@ throws IOException; /** + * Return {@code true} if BoringSSL has been built in FIPS mode. + */ + static native boolean usesBoringSSL_FIPS_mode(); + + /** * Used for testing only. */ static native int BIO_read(long bioRef, byte[] buffer) throws IOException;
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/NativeRef.java b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeRef.java index cc981c2..d33ac62 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/NativeRef.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeRef.java
@@ -47,6 +47,7 @@ } @Override + @SuppressWarnings("deprecation") protected void finalize() throws Throwable { try { if (address != 0) { @@ -59,6 +60,17 @@ abstract void doFree(long context); + static final class CMAC_CTX extends NativeRef { + CMAC_CTX(long nativePointer) { + super(nativePointer); + } + + @Override + void doFree(long context) { + NativeCrypto.CMAC_CTX_free(context); + } + } + static final class EC_GROUP extends NativeRef { EC_GROUP(long ctx) { super(ctx);
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSsl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSsl.java index 4675927..82b2cc3 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSsl.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSsl.java
@@ -26,11 +26,15 @@ import static com.android.org.conscrypt.NativeConstants.SSL_VERIFY_NONE; import static com.android.org.conscrypt.NativeConstants.SSL_VERIFY_PEER; +import com.android.org.conscrypt.NativeCrypto.SSLHandshakeCallbacks; +import com.android.org.conscrypt.SSLParametersImpl.AliasChooser; +import com.android.org.conscrypt.SSLParametersImpl.PSKCallbacks; import java.io.FileDescriptor; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.SocketException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.PrivateKey; import java.security.PublicKey; @@ -47,9 +51,6 @@ import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; -import com.android.org.conscrypt.NativeCrypto.SSLHandshakeCallbacks; -import com.android.org.conscrypt.SSLParametersImpl.AliasChooser; -import com.android.org.conscrypt.SSLParametersImpl.PSKCallbacks; /** * A utility wrapper that abstracts operations on the underlying native SSL instance. @@ -212,7 +213,7 @@ byte[][] asn1DerEncodedPrincipals) throws SSLException, CertificateEncodingException { Set<String> keyTypesSet = SSLUtils.getSupportedClientKeyTypes(keyTypeBytes, signatureAlgs); - String[] keyTypes = keyTypesSet.toArray(new String[keyTypesSet.size()]); + String[] keyTypes = keyTypesSet.toArray(new String[0]); X500Principal[] issuers; if (asn1DerEncodedPrincipals == null) { @@ -637,6 +638,7 @@ } @Override + @SuppressWarnings("deprecation") protected final void finalize() throws Throwable { try { close(); @@ -656,27 +658,51 @@ } int getPendingWrittenBytes() { - if (bio != 0) { - return NativeCrypto.SSL_pending_written_bytes_in_BIO(bio); - } else { - return 0; + lock.readLock().lock(); + try { + return (bio == 0L) ? 0 : NativeCrypto.SSL_pending_written_bytes_in_BIO(bio); + } finally { + lock.readLock().unlock(); } } int writeDirectByteBuffer(long address, int length) throws IOException { - return NativeCrypto.ENGINE_SSL_write_BIO_direct( - ssl, NativeSsl.this, bio, address, length, handshakeCallbacks); + lock.readLock().lock(); + try { + if (isClosed()) { + throw new SSLException("Connection closed"); + } + return NativeCrypto.ENGINE_SSL_write_BIO_direct( + ssl, NativeSsl.this, bio, address, length, handshakeCallbacks); + } finally { + lock.readLock().unlock(); + } } int readDirectByteBuffer(long destAddress, int destLength) throws IOException { - return NativeCrypto.ENGINE_SSL_read_BIO_direct( - ssl, NativeSsl.this, bio, destAddress, destLength, handshakeCallbacks); + lock.readLock().lock(); + try { + if (isClosed()) { + throw new SSLException("Connection closed"); + } + return NativeCrypto.ENGINE_SSL_read_BIO_direct( + ssl, NativeSsl.this, bio, destAddress, destLength, handshakeCallbacks); + } finally { + lock.readLock().unlock(); + } } void close() { - long toFree = bio; - bio = 0L; - NativeCrypto.BIO_free_all(toFree); + lock.writeLock().lock(); + try { + long toFree = bio; + bio = 0L; + if (toFree != 0L) { + NativeCrypto.BIO_free_all(toFree); + } + } finally { + lock.writeLock().unlock(); + } } } }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSslSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSslSession.java index 5ed56e1..6ecb553 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSslSession.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSslSession.java
@@ -429,7 +429,7 @@ } @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { + public Certificate[] getPeerCertificates() { throw new UnsupportedOperationException(); } @@ -439,13 +439,13 @@ } @Override - public X509Certificate[] getPeerCertificateChain() - throws SSLPeerUnverifiedException { + @SuppressWarnings("deprecation") + public X509Certificate[] getPeerCertificateChain() { throw new UnsupportedOperationException(); } @Override - public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { + public Principal getPeerPrincipal() { throw new UnsupportedOperationException(); }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OkHostnameVerifier.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OkHostnameVerifier.java new file mode 100644 index 0000000..33dd05b --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OkHostnameVerifier.java
@@ -0,0 +1,288 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.security.cert.Certificate; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; + +/** + * A HostnameVerifier consistent with <a + * href="http://www.ietf.org/rfc/rfc2818.txt">RFC 2818</a>. + * @hide This class is not part of the Android public SDK API + */ +public final class OkHostnameVerifier implements ConscryptHostnameVerifier { + // Android-changed: Add a mode which disallows top-level domain wildcards. b/144694112 + // public static final OkHostnameVerifier INSTANCE = new OkHostnameVerifier(); + public static final OkHostnameVerifier INSTANCE = new OkHostnameVerifier(false); + + /** + * Quick and dirty pattern to differentiate IP addresses from hostnames. This + * is an approximation of Android's private InetAddress#isNumeric API. + * + * <p>This matches IPv6 addresses as a hex string containing at least one + * colon, and possibly including dots after the first colon. It matches IPv4 + * addresses as strings containing only decimal digits and dots. This pattern + * matches strings like "a:.23" and "54" that are neither IP addresses nor + * hostnames; they will be verified as IP addresses (which is a more strict + * verification). + */ + private static final Pattern VERIFY_AS_IP_ADDRESS = Pattern.compile( + "([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)"); + + private static final int ALT_DNS_NAME = 2; + private static final int ALT_IPA_NAME = 7; + + // BEGIN Android-changed: Add a mode which disallows top-level domain wildcards. b/144694112 + // private OkHostnameVerifier() { + // } + private final boolean strictWildcardMode; + + private OkHostnameVerifier(boolean strictWildcardMode) { + this.strictWildcardMode = strictWildcardMode; + } + + public static OkHostnameVerifier strictInstance() { + return new OkHostnameVerifier(true); + } + // END Android-changed: Add a mode which disallows top-level domain wildcards. b/144694112 + + @Override + public boolean verify(X509Certificate[] certs, String host, SSLSession session) { + if (certs.length > 0) { + return verify(host, certs[0]); + } else { + try { + Certificate[] certificates = session.getPeerCertificates(); + return verify(host, (X509Certificate) certificates[0]); + } catch (SSLException e) { + return false; + } + } + } + + public boolean verify(String host, X509Certificate certificate) { + return verifyAsIpAddress(host) + ? verifyIpAddress(host, certificate) + : verifyHostName(host, certificate); + } + + static boolean verifyAsIpAddress(String host) { + return VERIFY_AS_IP_ADDRESS.matcher(host).matches(); + } + + /** + * Returns true if {@code certificate} matches {@code ipAddress}. + */ + private boolean verifyIpAddress(String ipAddress, X509Certificate certificate) { + List<String> altNames = getSubjectAltNames(certificate, ALT_IPA_NAME); + for (int i = 0, size = altNames.size(); i < size; i++) { + if (ipAddress.equalsIgnoreCase(altNames.get(i))) { + return true; + } + } + return false; + } + + /** + * Returns true if {@code certificate} matches {@code hostName}. + */ + @SuppressWarnings("UnusedVariable") + private boolean verifyHostName(String hostName, X509Certificate certificate) { + hostName = hostName.toLowerCase(Locale.US); + boolean hasDns = false; + List<String> altNames = getSubjectAltNames(certificate, ALT_DNS_NAME); + for (int i = 0, size = altNames.size(); i < size; i++) { + hasDns = true; + if (verifyHostName(hostName, altNames.get(i))) { + return true; + } + } + return false; + } + + // BEGIN Android-removed: Ignore common name in hostname verification. http://b/70278814 + /* + if (!hasDns) { + X500Principal principal = certificate.getSubjectX500Principal(); + // RFC 2818 advises using the most specific name for matching. + String cn = new DistinguishedNameParser(principal).findMostSpecific("cn"); + if (cn != null) { + return verifyHostName(hostName, cn); + } + } + */ + // END Android-removed: Ignore common name in hostname verification. http://b/70278814 + + public static List<String> allSubjectAltNames(X509Certificate certificate) { + List<String> altIpaNames = getSubjectAltNames(certificate, ALT_IPA_NAME); + List<String> altDnsNames = getSubjectAltNames(certificate, ALT_DNS_NAME); + List<String> result = new ArrayList<>(altIpaNames.size() + altDnsNames.size()); + result.addAll(altIpaNames); + result.addAll(altDnsNames); + return result; + } + + @SuppressWarnings("MixedMutabilityReturnType") + private static List<String> getSubjectAltNames(X509Certificate certificate, int type) { + List<String> result = new ArrayList<>(); + try { + Collection<?> subjectAltNames = certificate.getSubjectAlternativeNames(); + if (subjectAltNames == null) { + return Collections.emptyList(); + } + for (Object subjectAltName : subjectAltNames) { + List<?> entry = (List<?>) subjectAltName; + if (entry == null || entry.size() < 2) { + continue; + } + Integer altNameType = (Integer) entry.get(0); + if (altNameType == null) { + continue; + } + if (altNameType == type) { + String altName = (String) entry.get(1); + if (altName != null) { + result.add(altName); + } + } + } + return result; + } catch (CertificateParsingException e) { + return Collections.emptyList(); + } + } + + /** + * Returns {@code true} iff {@code hostName} matches the domain name {@code pattern}. + * + * @param hostName lower-case host name. + * @param pattern domain name pattern from certificate. May be a wildcard pattern such as + * {@code *.android.com}. + */ + private boolean verifyHostName(String hostName, String pattern) { + // Basic sanity checks + // Check length == 0 instead of .isEmpty() to support Java 5. + if (hostName == null || hostName.length() == 0 || hostName.startsWith(".") + || hostName.endsWith("..")) { + // Invalid domain name + return false; + } + if (pattern == null || pattern.length() == 0 || pattern.startsWith(".") + || pattern.endsWith("..")) { + // Invalid pattern/domain name + return false; + } + + // Normalize hostName and pattern by turning them into absolute domain names if they are not + // yet absolute. This is needed because server certificates do not normally contain absolute + // names or patterns, but they should be treated as absolute. At the same time, any hostName + // presented to this method should also be treated as absolute for the purposes of matching + // to the server certificate. + // www.android.com matches www.android.com + // www.android.com matches www.android.com. + // www.android.com. matches www.android.com. + // www.android.com. matches www.android.com + if (!hostName.endsWith(".")) { + hostName += '.'; + } + if (!pattern.endsWith(".")) { + pattern += '.'; + } + // hostName and pattern are now absolute domain names. + + pattern = pattern.toLowerCase(Locale.US); + // hostName and pattern are now in lower case -- domain names are case-insensitive. + + if (!pattern.contains("*")) { + // Not a wildcard pattern -- hostName and pattern must match exactly. + return hostName.equals(pattern); + } + // Wildcard pattern + + // WILDCARD PATTERN RULES: + // 1. Asterisk (*) is only permitted in the left-most domain name label and must be the + // only character in that label (i.e., must match the whole left-most label). + // For example, *.example.com is permitted, while *a.example.com, a*.example.com, + // a*b.example.com, a.*.example.com are not permitted. + // 2. Asterisk (*) cannot match across domain name labels. + // For example, *.example.com matches test.example.com but does not match + // sub.test.example.com. + // 3. Wildcard patterns for single-label domain names are not permitted. + // 4. Android-added: if strictWildcardMode is true then wildcards matching top-level domains, + // e.g. *.com, are not permitted. + + if (!pattern.startsWith("*.") || pattern.indexOf('*', 1) != -1) { + // Asterisk (*) is only permitted in the left-most domain name label and must be the only + // character in that label + return false; + } + + // Optimization: check whether hostName is too short to match the pattern. hostName must be at + // least as long as the pattern because asterisk must match the whole left-most label and + // hostName starts with a non-empty label. Thus, asterisk has to match one or more characters. + if (hostName.length() < pattern.length()) { + // hostName too short to match the pattern. + return false; + } + + if ("*.".equals(pattern)) { + // Wildcard pattern for single-label domain name -- not permitted. + return false; + } + + // BEGIN Android-added: Disallow top-level wildcards in strict mode. http://b/144694112 + if (strictWildcardMode) { + // By this point we know the pattern has been normalised and starts with a wildcard, + // i.e. "*.domainpart." + String domainPart = pattern.substring(2, pattern.length() - 1); + // If the domain part contains no dots then this pattern will match top level domains. + if (domainPart.indexOf('.') < 0) { + return false; + } + } + // END Android-added: Disallow top-level wildcards in strict mode. http://b/144694112 + + // hostName must end with the region of pattern following the asterisk. + String suffix = pattern.substring(1); + if (!hostName.endsWith(suffix)) { + // hostName does not end with the suffix + return false; + } + + // Check that asterisk did not match across domain name labels. + int suffixStartIndexInHostName = hostName.length() - suffix.length(); + if ((suffixStartIndexInHostName > 0) + && (hostName.lastIndexOf('.', suffixStartIndexInHostName - 1) != -1)) { + // Asterisk is matching across domain name labels -- not permitted. + return false; + } + + // hostName matches pattern + return true; + } +} \ No newline at end of file
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipher.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipher.java index 566c1fc..383ba4e 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipher.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipher.java
@@ -37,6 +37,11 @@ @Internal public abstract class OpenSSLAeadCipher extends OpenSSLCipher { /** + * Controls whether no-copy optimizations for direct ByteBuffers are enabled. + */ + private static final boolean ENABLE_BYTEBUFFER_OPTIMIZATIONS = true; + + /** * The default tag size when one is not specified. Default to * full-length tags (128-bits or 16 octets). */ @@ -88,7 +93,7 @@ */ int tagLengthInBytes; - public OpenSSLAeadCipher(Mode mode) { + protected OpenSSLAeadCipher(Mode mode) { super(mode, Padding.NOPADDING); } @@ -225,6 +230,48 @@ } @Override + protected int engineDoFinal(ByteBuffer input, ByteBuffer output) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + if (!ENABLE_BYTEBUFFER_OPTIMIZATIONS) { + return super.engineDoFinal(input, output); + } + if (input == null || output == null) { + throw new NullPointerException("Null ByteBuffer Error"); + } + if (getOutputSizeForFinal(input.remaining()) > output.remaining()) { + throw new ShortBufferWithoutStackTraceException("Insufficient Bytes for Output Buffer"); + } + if (output.isReadOnly()) { + throw new IllegalArgumentException("Cannot write to Read Only ByteBuffer"); + } + if (bufCount != 0) { + return super.engineDoFinal(input, output); // traditional case + } + int bytesWritten; + if (!input.isDirect()) { + int incap = input.remaining(); + ByteBuffer inputClone = ByteBuffer.allocateDirect(incap); + inputClone.mark(); + inputClone.put(input); + inputClone.reset(); + input = inputClone; + } + if (!output.isDirect()) { + ByteBuffer outputClone = + ByteBuffer.allocateDirect(getOutputSizeForFinal(input.remaining())); + bytesWritten = doFinalInternal(input, outputClone); + output.put(outputClone); + input.position(input.limit()); // API reasons + } else { + bytesWritten = doFinalInternal(input, output); + output.position(output.position() + bytesWritten); + input.position(input.limit()); // API reasons + } + + return bytesWritten; + } + + @Override protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { @@ -285,6 +332,28 @@ } } + int doFinalInternal(ByteBuffer input, ByteBuffer output) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + checkInitialization(); + final int bytesWritten; + try { + if (isEncrypting()) { + bytesWritten = NativeCrypto.EVP_AEAD_CTX_seal_buf( + evpAead, encodedKey, tagLengthInBytes, output, iv, input, aad); + } else { + bytesWritten = NativeCrypto.EVP_AEAD_CTX_open_buf( + evpAead, encodedKey, tagLengthInBytes, output, iv, input, aad); + } + } catch (BadPaddingException e) { + throwAEADBadTagExceptionIfAvailable(e.getMessage(), e.getCause()); + throw e; + } + if (isEncrypting()) { + mustInitialize = true; + } + return bytesWritten; + } + @Override int doFinalInternal(byte[] output, int outputOffset, int maximumLen) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOSink.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOSink.java deleted file mode 100644 index 76fc6e1..0000000 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOSink.java +++ /dev/null
@@ -1,78 +0,0 @@ -/* GENERATED SOURCE. DO NOT MODIFY. */ -/* - * Copyright (C) 2014 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 java.io.ByteArrayOutputStream; - -/** - * Wraps a BoringSSL BIO to act as a place to write out data. - */ -final class OpenSSLBIOSink { - private final long ctx; - private final ByteArrayOutputStream buffer; - private int position; - - static OpenSSLBIOSink create() { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - return new OpenSSLBIOSink(buffer); - } - - private OpenSSLBIOSink(ByteArrayOutputStream buffer) { - ctx = NativeCrypto.create_BIO_OutputStream(buffer); - this.buffer = buffer; - } - - int available() { - return buffer.size() - position; - } - - void reset() { - buffer.reset(); - position = 0; - } - - long skip(long byteCount) { - int maxLength = Math.min(available(), (int) byteCount); - position += maxLength; - if (position == buffer.size()) { - reset(); - } - return maxLength; - } - - long getContext() { - return ctx; - } - - byte[] toByteArray() { - return buffer.toByteArray(); - } - - int position() { - return position; - } - - @Override - protected void finalize() throws Throwable { - try { - NativeCrypto.BIO_free_all(ctx); - } finally { - super.finalize(); - } - } -}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOSource.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOSource.java deleted file mode 100644 index dbbd985..0000000 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOSource.java +++ /dev/null
@@ -1,107 +0,0 @@ -/* GENERATED SOURCE. DO NOT MODIFY. */ -/* - * Copyright (C) 2014 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 java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; - -/** - * Wrapped by a BoringSSL BIO to act as a source of bytes. - */ -final class OpenSSLBIOSource { - private OpenSSLBIOInputStream source; - - static OpenSSLBIOSource wrap(ByteBuffer buffer) { - return new OpenSSLBIOSource( - new OpenSSLBIOInputStream(new ByteBufferInputStream(buffer), false)); - } - - private OpenSSLBIOSource(OpenSSLBIOInputStream source) { - this.source = source; - } - - long getContext() { - return source.getBioContext(); - } - - private synchronized void release() { - if (source != null) { - NativeCrypto.BIO_free_all(source.getBioContext()); - source = null; - } - } - - @Override - protected void finalize() throws Throwable { - try { - release(); - } finally { - super.finalize(); - } - } - - private static class ByteBufferInputStream extends InputStream { - private final ByteBuffer source; - - ByteBufferInputStream(ByteBuffer source) { - this.source = source; - } - - @Override - public int read() throws IOException { - if (source.remaining() > 0) { - return source.get(); - } else { - return -1; - } - } - - @Override - public int available() throws IOException { - return source.limit() - source.position(); - } - - @Override - public int read(byte[] buffer) throws IOException { - int originalPosition = source.position(); - source.get(buffer); - return source.position() - originalPosition; - } - - @Override - public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { - int toRead = Math.min(source.remaining(), byteCount); - int originalPosition = source.position(); - source.get(buffer, byteOffset, toRead); - return source.position() - originalPosition; - } - - @Override - public void reset() throws IOException { - source.reset(); - } - - @Override - public long skip(long byteCount) throws IOException { - long originalPosition = source.position(); - source.position((int) (originalPosition + byteCount)); - return source.position() - originalPosition; - } - } -}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBaseDHKeyAgreement.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBaseDHKeyAgreement.java new file mode 100644 index 0000000..3261847 --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBaseDHKeyAgreement.java
@@ -0,0 +1,160 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +/* + * Copyright (C) 2013 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 java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.KeyAgreementSpi; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; + +/** + * Generic base classe for Diffie-Hellman style key agreement backed by the OpenSSL engine. + * @hide This class is not part of the Android public SDK API + */ +@Internal +public abstract class OpenSSLBaseDHKeyAgreement<T> extends KeyAgreementSpi { + + /** OpenSSL handle of the private key. Only available after the engine has been initialized. */ + private T mPrivateKey; + + /** + * Expected length (in bytes) of the agreed key ({@link #mResult}). Only available after the + * engine has been initialized. + */ + private int mExpectedResultLength; + + /** Agreed key. Only available after {@link #engineDoPhase(Key, boolean)} completes. */ + private byte[] mResult; + + protected OpenSSLBaseDHKeyAgreement() {} + + @Override + public Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException { + if (mPrivateKey == null) { + throw new IllegalStateException("Not initialized"); + } + if (!lastPhase) { + throw new IllegalStateException("DH only has one phase"); + } + + if (key == null) { + throw new InvalidKeyException("key == null"); + } + if (!(key instanceof PublicKey)) { + throw new InvalidKeyException("Not a public key: " + key.getClass()); + } + T openSslPublicKey = convertPublicKey((PublicKey) key); + + byte[] buffer = new byte[mExpectedResultLength]; + int actualResultLength = computeKey(buffer, openSslPublicKey, mPrivateKey); + byte[] result; + if (actualResultLength == -1) { + throw new RuntimeException("Engine returned -1"); + } else if (actualResultLength == mExpectedResultLength) { + // The output is as long as expected -- use the whole buffer + result = buffer; + } else if (actualResultLength < mExpectedResultLength) { + // The output is shorter than expected -- use only what's produced by the engine + result = new byte[actualResultLength]; + System.arraycopy(buffer, 0, mResult, 0, mResult.length); + } else { + // The output is longer than expected + throw new RuntimeException("Engine produced a longer than expected result. Expected: " + + mExpectedResultLength + ", actual: " + actualResultLength); + } + mResult = result; + + return null; // No intermediate key + } + + protected abstract T convertPublicKey(PublicKey key) throws InvalidKeyException; + + protected abstract T convertPrivateKey(PrivateKey key) throws InvalidKeyException; + + /** + * Given the public key {@code theirPublicKey} and the private key {@code ourPrivateKey} write the shared secret + * to {@code buffer} and return the actual output size. + */ + protected abstract int computeKey(byte[] buffer, T theirPublicKey, T ourPrivateKey) throws InvalidKeyException; + + @Override + protected int engineGenerateSecret(byte[] sharedSecret, int offset) + throws ShortBufferException { + checkCompleted(); + int available = sharedSecret.length - offset; + if (mResult.length > available) { + throw new ShortBufferWithoutStackTraceException( + "Needed: " + mResult.length + ", available: " + available); + } + + System.arraycopy(mResult, 0, sharedSecret, offset, mResult.length); + return mResult.length; + } + + @Override + protected byte[] engineGenerateSecret() { + checkCompleted(); + return mResult; + } + + @Override + protected SecretKey engineGenerateSecret(String algorithm) { + checkCompleted(); + return new SecretKeySpec(engineGenerateSecret(), algorithm); + } + + @Override + protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { + if (key == null) { + throw new InvalidKeyException("key == null"); + } + if (!(key instanceof PrivateKey)) { + throw new InvalidKeyException("Not a private key: " + key.getClass()); + } + + T privateKey = convertPrivateKey((PrivateKey) key); + mExpectedResultLength = getOutputSize(privateKey); + mPrivateKey = privateKey; + } + + /** Returns the expected result length for the given {@code key}. */ + protected abstract int getOutputSize(T key); + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params, + SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + // ECDH doesn't need an AlgorithmParameterSpec + if (params != null) { + throw new InvalidAlgorithmParameterException("No algorithm parameters supported"); + } + engineInit(key, random); + } + + private void checkCompleted() { + if (mResult == null) { + throw new IllegalStateException("Key agreement not completed"); + } + } +}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipherRSA.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipherRSA.java index 1a934b3..3910212 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipherRSA.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipherRSA.java
@@ -382,7 +382,7 @@ * @hide This class is not part of the Android public SDK API */ public abstract static class DirectRSA extends OpenSSLCipherRSA { - public DirectRSA(int padding) { + protected DirectRSA(int padding) { super(padding); }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECDHKeyAgreement.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECDHKeyAgreement.java index 4d659a5..69f614b 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECDHKeyAgreement.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECDHKeyAgreement.java
@@ -17,137 +17,39 @@ package com.android.org.conscrypt; -import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; -import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.spec.AlgorithmParameterSpec; -import javax.crypto.KeyAgreementSpi; -import javax.crypto.SecretKey; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.SecretKeySpec; /** * Elliptic Curve Diffie-Hellman key agreement backed by the OpenSSL engine. * @hide This class is not part of the Android public SDK API */ @Internal -public final class OpenSSLECDHKeyAgreement extends KeyAgreementSpi { - - /** OpenSSL handle of the private key. Only available after the engine has been initialized. */ - private OpenSSLKey mOpenSslPrivateKey; - - /** - * Expected length (in bytes) of the agreed key ({@link #mResult}). Only available after the - * engine has been initialized. - */ - private int mExpectedResultLength; - - /** Agreed key. Only available after {@link #engineDoPhase(Key, boolean)} completes. */ - private byte[] mResult; - +public final class OpenSSLECDHKeyAgreement extends OpenSSLBaseDHKeyAgreement<OpenSSLKey> { public OpenSSLECDHKeyAgreement() {} @Override - public Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException { - if (mOpenSslPrivateKey == null) { - throw new IllegalStateException("Not initialized"); - } - if (!lastPhase) { - throw new IllegalStateException("ECDH only has one phase"); - } - - if (key == null) { - throw new InvalidKeyException("key == null"); - } - if (!(key instanceof PublicKey)) { - throw new InvalidKeyException("Not a public key: " + key.getClass()); - } - OpenSSLKey openSslPublicKey = OpenSSLKey.fromPublicKey((PublicKey) key); - - byte[] buffer = new byte[mExpectedResultLength]; - int actualResultLength = NativeCrypto.ECDH_compute_key( - buffer, - 0, - openSslPublicKey.getNativeRef(), - mOpenSslPrivateKey.getNativeRef()); - byte[] result; - if (actualResultLength == -1) { - throw new RuntimeException("Engine returned " + actualResultLength); - } else if (actualResultLength == mExpectedResultLength) { - // The output is as long as expected -- use the whole buffer - result = buffer; - } else if (actualResultLength < mExpectedResultLength) { - // The output is shorter than expected -- use only what's produced by the engine - result = new byte[actualResultLength]; - System.arraycopy(buffer, 0, mResult, 0, mResult.length); - } else { - // The output is longer than expected - throw new RuntimeException("Engine produced a longer than expected result. Expected: " - + mExpectedResultLength + ", actual: " + actualResultLength); - } - mResult = result; - - return null; // No intermediate key + protected OpenSSLKey convertPublicKey(PublicKey key) throws InvalidKeyException { + return OpenSSLKey.fromPublicKey(key); } @Override - protected int engineGenerateSecret(byte[] sharedSecret, int offset) - throws ShortBufferException { - checkCompleted(); - int available = sharedSecret.length - offset; - if (mResult.length > available) { - throw new ShortBufferWithoutStackTraceException( - "Needed: " + mResult.length + ", available: " + available); - } - - System.arraycopy(mResult, 0, sharedSecret, offset, mResult.length); - return mResult.length; + protected OpenSSLKey convertPrivateKey(PrivateKey key) throws InvalidKeyException { + return OpenSSLKey.fromPrivateKey(key); } @Override - protected byte[] engineGenerateSecret() { - checkCompleted(); - return mResult; + protected int computeKey(byte[] buffer, OpenSSLKey theirPublicKey, OpenSSLKey ourPrivateKey) + throws InvalidKeyException { + return NativeCrypto.ECDH_compute_key( + buffer, 0, theirPublicKey.getNativeRef(), ourPrivateKey.getNativeRef()); } @Override - protected SecretKey engineGenerateSecret(String algorithm) { - checkCompleted(); - return new SecretKeySpec(engineGenerateSecret(), algorithm); - } - - @Override - protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { - if (key == null) { - throw new InvalidKeyException("key == null"); - } - if (!(key instanceof PrivateKey)) { - throw new InvalidKeyException("Not a private key: " + key.getClass()); - } - - OpenSSLKey openSslKey = OpenSSLKey.fromPrivateKey((PrivateKey) key); + protected int getOutputSize(OpenSSLKey openSslKey) { int fieldSizeBits = NativeCrypto.EC_GROUP_get_degree(new NativeRef.EC_GROUP( NativeCrypto.EC_KEY_get1_group(openSslKey.getNativeRef()))); - mExpectedResultLength = (fieldSizeBits + 7) / 8; - mOpenSslPrivateKey = openSslKey; - } - - @Override - protected void engineInit(Key key, AlgorithmParameterSpec params, - SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - // ECDH doesn't need an AlgorithmParameterSpec - if (params != null) { - throw new InvalidAlgorithmParameterException("No algorithm parameters supported"); - } - engineInit(key, random); - } - - private void checkCompleted() { - if (mResult == null) { - throw new IllegalStateException("Key agreement not completed"); - } + return (fieldSizeBits + 7) / 8; } }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPrivateKey.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPrivateKey.java index 15c3e04..6bb0c72 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPrivateKey.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPrivateKey.java
@@ -42,9 +42,9 @@ private static final String ALGORITHM = "EC"; - protected transient OpenSSLKey key; + private transient OpenSSLKey key; - protected transient OpenSSLECGroupContext group; + private transient OpenSSLECGroupContext group; OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key) { this.group = group;
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPublicKey.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPublicKey.java index e5b66e0..37fd102 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPublicKey.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPublicKey.java
@@ -38,9 +38,9 @@ private static final String ALGORITHM = "EC"; - protected transient OpenSSLKey key; + private transient OpenSSLKey key; - protected transient OpenSSLECGroupContext group; + private transient OpenSSLECGroupContext group; OpenSSLECPublicKey(OpenSSLECGroupContext group, OpenSSLKey key) { this.group = group;
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipher.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipher.java index a9b2d46..765282d 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipher.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipher.java
@@ -50,7 +50,7 @@ */ private int modeBlockSize; - public OpenSSLEvpCipher(Mode mode, Padding padding) { + protected OpenSSLEvpCipher(Mode mode, Padding padding) { super(mode, padding); }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherAES.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherAES.java index 0085903c..ee65b30 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherAES.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherAES.java
@@ -91,7 +91,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static class NoPadding extends CBC { + public static class NoPadding extends AES.CBC { public NoPadding() { super(Padding.NOPADDING); } @@ -100,7 +100,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static class PKCS5Padding extends CBC { + public static class PKCS5Padding extends AES.CBC { public PKCS5Padding() { super(Padding.PKCS5PADDING); } @@ -127,7 +127,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static class NoPadding extends ECB { + public static class NoPadding extends AES.ECB { public NoPadding() { super(Padding.NOPADDING); } @@ -136,7 +136,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static class PKCS5Padding extends ECB { + public static class PKCS5Padding extends AES.ECB { public PKCS5Padding() { super(Padding.PKCS5PADDING); } @@ -176,7 +176,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static class NoPadding extends CBC { + public static class NoPadding extends AES_128.CBC { public NoPadding() { super(Padding.NOPADDING); } @@ -185,7 +185,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static class PKCS5Padding extends CBC { + public static class PKCS5Padding extends AES_128.CBC { public PKCS5Padding() { super(Padding.PKCS5PADDING); } @@ -212,7 +212,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static class NoPadding extends ECB { + public static class NoPadding extends AES_128.ECB { public NoPadding() { super(Padding.NOPADDING); } @@ -221,7 +221,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static class PKCS5Padding extends ECB { + public static class PKCS5Padding extends AES_128.ECB { public PKCS5Padding() { super(Padding.PKCS5PADDING); } @@ -255,7 +255,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static class NoPadding extends CBC { + public static class NoPadding extends AES_256.CBC { public NoPadding() { super(Padding.NOPADDING); } @@ -264,7 +264,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static class PKCS5Padding extends CBC { + public static class PKCS5Padding extends AES_256.CBC { public PKCS5Padding() { super(Padding.PKCS5PADDING); } @@ -291,7 +291,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static class NoPadding extends ECB { + public static class NoPadding extends AES_256.ECB { public NoPadding() { super(Padding.NOPADDING); } @@ -300,7 +300,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static class PKCS5Padding extends ECB { + public static class PKCS5Padding extends AES_256.ECB { public PKCS5Padding() { super(Padding.PKCS5PADDING); }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLMac.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLMac.java index 5acb91b..cca9721 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLMac.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLMac.java
@@ -32,18 +32,10 @@ */ @Internal public abstract class OpenSSLMac extends MacSpi { - private NativeRef.HMAC_CTX ctx; - - /** - * Holds the EVP_MD for the hashing algorithm, e.g. - * EVP_get_digestbyname("sha1"); - */ - private final long evp_md; - /** * The secret key used in this keyed MAC. */ - private byte[] keyBytes; + protected byte[] keyBytes; /** * Holds the output size of the message digest. @@ -55,11 +47,20 @@ */ private final byte[] singleByte = new byte[1]; - private OpenSSLMac(long evp_md, int size) { - this.evp_md = evp_md; + private OpenSSLMac(int size) { this.size = size; } + /** + * Creates and initializes the relevant BoringSSL *MAC context. + */ + protected abstract void resetContext(); + + /** + * Passes the contents of a direct ByteBuffer to the relevant BoringSSL *MAC function. + */ + protected abstract void updateDirect(long ptr, int len); + @Override protected int engineGetMacLength() { return size; @@ -84,15 +85,6 @@ resetContext(); } - private final void resetContext() { - NativeRef.HMAC_CTX ctxLocal = new NativeRef.HMAC_CTX(NativeCrypto.HMAC_CTX_new()); - if (keyBytes != null) { - NativeCrypto.HMAC_Init_ex(ctxLocal, keyBytes, evp_md); - } - - this.ctx = ctxLocal; - } - @Override protected void engineUpdate(byte input) { singleByte[0] = input; @@ -100,17 +92,10 @@ } @Override - protected void engineUpdate(byte[] input, int offset, int len) { - final NativeRef.HMAC_CTX ctxLocal = ctx; - NativeCrypto.HMAC_Update(ctxLocal, input, offset, len); - } - - @Override protected void engineUpdate(ByteBuffer input) { // Optimization: Avoid copying/allocation for direct buffers because their contents are // stored as a contiguous region in memory and thus can be efficiently accessed from native // code. - if (!input.hasRemaining()) { return; } @@ -122,7 +107,7 @@ long baseAddress = NativeCrypto.getDirectBufferAddress(input); if (baseAddress == 0) { - // Direct buffer's contents can't be accessed from JNI -- superclass's implementation + // Direct buffers' contents can't be accessed from JNI -- superclass's implementation // is good enough to handle this. super.engineUpdate(input); return; @@ -139,19 +124,19 @@ throw new RuntimeException("Negative remaining amount"); } - final NativeRef.HMAC_CTX ctxLocal = ctx; - NativeCrypto.HMAC_UpdateDirect(ctxLocal, ptr, len); + updateDirect(ptr, len); input.position(position + len); } @Override protected byte[] engineDoFinal() { - final NativeRef.HMAC_CTX ctxLocal = ctx; - final byte[] output = NativeCrypto.HMAC_Final(ctxLocal); + final byte[] output = doFinal(); resetContext(); return output; } + protected abstract byte[] doFinal(); + @Override protected void engineReset() { resetContext(); @@ -160,7 +145,52 @@ /** * @hide This class is not part of the Android public SDK API */ - public static final class HmacMD5 extends OpenSSLMac { + public static class Hmac extends OpenSSLMac { + private NativeRef.HMAC_CTX ctx; + + /** + * Holds the EVP_MD for the hashing algorithm, e.g. + * EVP_get_digestbyname("sha1"); + */ + private final long evp_md; + + public Hmac(long evp_md, int size) { + super(size); + this.evp_md = evp_md; + } + + @Override + protected void resetContext() { + NativeRef.HMAC_CTX ctxLocal = new NativeRef.HMAC_CTX(NativeCrypto.HMAC_CTX_new()); + if (keyBytes != null) { + NativeCrypto.HMAC_Init_ex(ctxLocal, keyBytes, evp_md); + } + this.ctx = ctxLocal; + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + final NativeRef.HMAC_CTX ctxLocal = ctx; + NativeCrypto.HMAC_Update(ctxLocal, input, offset, len); + } + + @Override + protected void updateDirect(long ptr, int len) { + final NativeRef.HMAC_CTX ctxLocal = ctx; + NativeCrypto.HMAC_UpdateDirect(ctxLocal, ptr, len); + } + + @Override + protected byte[] doFinal() { + final NativeRef.HMAC_CTX ctxLocal = ctx; + return NativeCrypto.HMAC_Final(ctxLocal); + } + } + + /** + * @hide This class is not part of the Android public SDK API + */ + public static final class HmacMD5 extends Hmac { public HmacMD5() { super(EvpMdRef.MD5.EVP_MD, EvpMdRef.MD5.SIZE_BYTES); } @@ -169,7 +199,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static final class HmacSHA1 extends OpenSSLMac { + public static final class HmacSHA1 extends Hmac { public HmacSHA1() { super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.SIZE_BYTES); } @@ -178,7 +208,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static final class HmacSHA224 extends OpenSSLMac { + public static final class HmacSHA224 extends Hmac { public HmacSHA224() throws NoSuchAlgorithmException { super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.SIZE_BYTES); } @@ -187,7 +217,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static final class HmacSHA256 extends OpenSSLMac { + public static final class HmacSHA256 extends Hmac { public HmacSHA256() throws NoSuchAlgorithmException { super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.SIZE_BYTES); } @@ -196,7 +226,7 @@ /** * @hide This class is not part of the Android public SDK API */ - public static final class HmacSHA384 extends OpenSSLMac { + public static final class HmacSHA384 extends Hmac { public HmacSHA384() throws NoSuchAlgorithmException { super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.SIZE_BYTES); } @@ -205,9 +235,47 @@ /** * @hide This class is not part of the Android public SDK API */ - public static final class HmacSHA512 extends OpenSSLMac { + public static final class HmacSHA512 extends Hmac { public HmacSHA512() { super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.SIZE_BYTES); } } + + /** + * @hide This class is not part of the Android public SDK API + */ + public static final class AesCmac extends OpenSSLMac { + private NativeRef.CMAC_CTX ctx; + + public AesCmac() { + super(16); + } + + @Override + protected void resetContext() { + NativeRef.CMAC_CTX ctxLocal = new NativeRef.CMAC_CTX(NativeCrypto.CMAC_CTX_new()); + if (keyBytes != null) { + NativeCrypto.CMAC_Init(ctxLocal, keyBytes); + } + this.ctx = ctxLocal; + } + + @Override + protected void updateDirect(long ptr, int len) { + final NativeRef.CMAC_CTX ctxLocal = ctx; + NativeCrypto.CMAC_UpdateDirect(ctxLocal, ptr, len); + } + + @Override + protected byte[] doFinal() { + final NativeRef.CMAC_CTX ctxLocal = ctx; + return NativeCrypto.CMAC_Final(ctxLocal); + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + final NativeRef.CMAC_CTX ctxLocal = ctx; + NativeCrypto.CMAC_Update(ctxLocal, input, offset, len); + } + } }
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 6e6808b..42a5d33 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
@@ -34,7 +34,7 @@ */ @libcore. api.IntraCoreApi -@libcore.api.CorePlatformApi +@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Internal public final class OpenSSLProvider extends Provider { private static final long serialVersionUID = 2996752495318905136L; @@ -43,6 +43,8 @@ private static final String STANDARD_EC_PRIVATE_KEY_INTERFACE_CLASS_NAME = "java.security.interfaces.ECPrivateKey"; + private static final String STANDARD_XEC_PRIVATE_KEY_INTERFACE_CLASS_NAME = + "java.security.interfaces.XECPrivateKey"; private static final String STANDARD_RSA_PRIVATE_KEY_INTERFACE_CLASS_NAME = "java.security.interfaces.RSAPrivateKey"; private static final String STANDARD_RSA_PUBLIC_KEY_INTERFACE_CLASS_NAME = @@ -52,7 +54,7 @@ .UnsupportedAppUsage @libcore.api .IntraCoreApi - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public OpenSSLProvider() { this(Platform.getDefaultProviderName()); } @@ -200,6 +202,9 @@ put("Alg.Alias.KeyPairGenerator.1.2.840.10045.2.1", "EC"); put("Alg.Alias.KeyPairGenerator.1.3.133.16.840.63.0.2", "EC"); + put("KeyPairGenerator.XDH", PREFIX + "OpenSSLXDHKeyPairGenerator"); + put("Alg.Alias.KeyPairGenerator.1.3.101.110", "XDH"); + /* == KeyFactory == */ put("KeyFactory.RSA", PREFIX + "OpenSSLRSAKeyFactory"); put("Alg.Alias.KeyFactory.1.2.840.113549.1.1.1", "RSA"); @@ -210,12 +215,16 @@ put("Alg.Alias.KeyFactory.1.2.840.10045.2.1", "EC"); put("Alg.Alias.KeyFactory.1.3.133.16.840.63.0.2", "EC"); + put("KeyFactory.XDH", PREFIX + "OpenSSLXDHKeyFactory"); + put("Alg.Alias.KeyFactory.1.3.101.110", "XDH"); + /* == SecretKeyFactory == */ put("SecretKeyFactory.DESEDE", PREFIX + "DESEDESecretKeyFactory"); put("Alg.Alias.SecretKeyFactory.TDEA", "DESEDE"); /* == KeyAgreement == */ putECDHKeyAgreementImplClass("OpenSSLECDHKeyAgreement"); + putXDHKeyAgreementImplClass("OpenSSLXDHKeyAgreement"); /* == Signatures == */ putSignatureImplClass("MD5withRSA", "OpenSSLSignature$MD5RSA"); @@ -498,6 +507,8 @@ put("Alg.Alias.Mac.HMAC/SHA512", "HmacSHA512"); put("Alg.Alias.Mac.PBEWITHHMACSHA512", "HmacSHA512"); + putMacImplClass("AESCMAC", "OpenSSLMac$AesCmac"); + /* === Certificate === */ put("CertificateFactory.X509", PREFIX + "OpenSSLX509CertificateFactory"); @@ -596,6 +607,19 @@ supportedKeyFormats); } + private void putXDHKeyAgreementImplClass(String className) { + // Accept only keys for which any of the following is true: + // * the key is from this provider (subclass of OpenSSLKeyHolder), + // * the key provides its key material in "PKCS#8" encoding via Key.getEncoded. + // * the key is a transparent XEC private key (subclass of XECPrivateKey). + String supportedKeyClasses = PREFIX + "OpenSSLKeyHolder" + + "|" + STANDARD_XEC_PRIVATE_KEY_INTERFACE_CLASS_NAME + "|" + PREFIX + + "OpenSSLX25519PrivateKey"; + String supportedKeyFormats = "PKCS#8"; + putImplClassWithKeyConstraints( + "KeyAgreement.XDH", PREFIX + className, supportedKeyClasses, supportedKeyFormats); + } + private void putImplClassWithKeyConstraints(String typeAndAlgName, String fullyQualifiedClassName, String supportedKeyClasses,
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 dd7279f..ca2542c 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
@@ -31,7 +31,7 @@ * Conscrypt's extended socket API before the introduction of the {@link Conscrypt} class. * @hide This class is not part of the Android public SDK API */ -@libcore.api.CorePlatformApi +@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Internal public abstract class OpenSSLSocketImpl extends AbstractConscryptSocket { OpenSSLSocketImpl() throws IOException { @@ -61,7 +61,7 @@ super(socket, hostname, port, autoClose); } - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) @Override public String getHostname() { return super.getHostname(); @@ -69,15 +69,15 @@ @android.compat.annotation .UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, - publicAlternatives = "Use {@link javax.net.ssl.SSLParameters#setServerNames}.") - @libcore.api.CorePlatformApi + 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); } - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) @Override public String getHostnameOrIP() { return super.getHostnameOrIP(); @@ -89,22 +89,22 @@ } @android.compat.annotation - .UnsupportedAppUsage - @libcore.api.CorePlatformApi + .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); } - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) @Override public int getSoWriteTimeout() throws SocketException { return super.getSoWriteTimeout(); } @android.compat.annotation - .UnsupportedAppUsage - @libcore.api.CorePlatformApi + .UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Override public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException { @@ -118,21 +118,22 @@ .UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, publicAlternatives = "Use {@link android.net.ssl.SSLSockets#setUseSessionTickets}.") - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Override public abstract void setUseSessionTickets(boolean useSessionTickets); - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) @Override public abstract void setChannelIdEnabled(boolean enabled); - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) @Override public abstract byte[] getChannelId() throws SSLException; - @android.compat.annotation.UnsupportedAppUsage - @libcore.api.CorePlatformApi + @android.compat. + annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Override public abstract void setChannelIdPrivateKey(PrivateKey privateKey); @@ -141,7 +142,7 @@ */ @android.compat.annotation .UnsupportedAppUsage - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Override @Deprecated public final byte[] getNpnSelectedProtocol() { @@ -153,7 +154,7 @@ */ @android.compat.annotation .UnsupportedAppUsage - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Override @Deprecated public final void setNpnProtocols(byte[] npnProtocols) { @@ -166,7 +167,7 @@ @android.compat.annotation. UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, publicAlternatives = - "Use {@link javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}.") + "Use {@code javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}.") @Override @Deprecated public final void @@ -180,8 +181,8 @@ @android.compat.annotation .UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, publicAlternatives = - "Use {@link javax.net.ssl.SSLSocket#getApplicationProtocol()}.") - @libcore.api.CorePlatformApi + "Use {@code javax.net.ssl.SSLSocket#getApplicationProtocol()}.") + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Override @Deprecated public final byte[] getAlpnSelectedProtocol() { @@ -194,8 +195,8 @@ @android.compat.annotation .UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q, publicAlternatives = - "Use {@link javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}.") - @libcore.api.CorePlatformApi + "Use {@code javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}.") + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Override @Deprecated public final void
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX25519Key.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX25519Key.java new file mode 100644 index 0000000..00e7f91 --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX25519Key.java
@@ -0,0 +1,11 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.conscrypt; + +/** + * @hide This class is not part of the Android public SDK API + */ +public interface OpenSSLX25519Key { + int X25519_KEY_SIZE_BYTES = 32; + + byte[] getU(); +}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX25519PrivateKey.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX25519PrivateKey.java new file mode 100644 index 0000000..faa7871 --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX25519PrivateKey.java
@@ -0,0 +1,110 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.conscrypt; + +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Arrays; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class OpenSSLX25519PrivateKey implements OpenSSLX25519Key, PrivateKey { + private static final byte[] PKCS8_PREAMBLE = new byte[]{ + 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x04, 0x22, 0x04, 0x20, + }; + + private static final byte[] PKCS8_PREAMBLE_WITH_NULL = new byte[] { + 0x30, 0x30, 0x02, 0x01, 0x00, 0x30, 0x07, 0x06, 0x03, 0x2B, 0x65, 0x6E, 0x05, 0x00, 0x04, 0x22, 0x04, 0x20, + }; + + private byte[] uCoordinate; + + public OpenSSLX25519PrivateKey(PKCS8EncodedKeySpec keySpec) throws InvalidKeySpecException { + byte[] encoded = keySpec.getEncoded(); + if (encoded == null || !"PKCS#8".equals(keySpec.getFormat())) { + throw new InvalidKeySpecException("Key must be encoded in PKCS#8 format"); + } + + int preambleLength = matchesPreamble(PKCS8_PREAMBLE, encoded) | matchesPreamble(PKCS8_PREAMBLE_WITH_NULL, encoded); + if (preambleLength == 0) { + throw new InvalidKeySpecException("Key size is not correct size"); + } + + uCoordinate = Arrays.copyOfRange(encoded, PKCS8_PREAMBLE.length, encoded.length); + } + + private static int matchesPreamble(byte[] preamble, byte[] encoded) { + if (encoded.length != (preamble.length + X25519_KEY_SIZE_BYTES)) { + return 0; + } + int cmp = 0; + for (int i = 0; i < preamble.length; i++) { + cmp |= encoded[i] ^ preamble[i]; + } + if (cmp != 0) { + return 0; + } + return preamble.length; + } + + public OpenSSLX25519PrivateKey(byte[] coordinateBytes) { + uCoordinate = coordinateBytes.clone(); + } + + @Override + public String getAlgorithm() { + return "XDH"; + } + + @Override + public String getFormat() { + return "PKCS#8"; + } + + @Override + public byte[] getEncoded() { + if (uCoordinate == null) { + throw new IllegalStateException("key is destroyed"); + } + + byte[] encoded = Arrays.copyOf(PKCS8_PREAMBLE, PKCS8_PREAMBLE.length + uCoordinate.length); + System.arraycopy(uCoordinate, 0, encoded, PKCS8_PREAMBLE.length, uCoordinate.length); + return encoded; + } + + @Override + public byte[] getU() { + if (uCoordinate == null) { + throw new IllegalStateException("key is destroyed"); + } + + return uCoordinate.clone(); + } + + @Override + public void destroy() { + if (uCoordinate != null) { + Arrays.fill(uCoordinate, (byte) 0); + uCoordinate = null; + } + } + + @Override + public boolean isDestroyed() { + return uCoordinate == null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof OpenSSLX25519PrivateKey)) return false; + OpenSSLX25519PrivateKey that = (OpenSSLX25519PrivateKey) o; + return Arrays.equals(uCoordinate, that.uCoordinate); + } + + @Override + public int hashCode() { + return Arrays.hashCode(uCoordinate); + } +} \ No newline at end of file
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX25519PublicKey.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX25519PublicKey.java new file mode 100644 index 0000000..1c10e4a --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX25519PublicKey.java
@@ -0,0 +1,105 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.conscrypt; + +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class OpenSSLX25519PublicKey implements OpenSSLX25519Key, PublicKey { + private static final byte[] X509_PREAMBLE = new byte[] { + 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x03, 0x21, 0x00, + }; + + private static final byte[] X509_PREAMBLE_WITH_NULL = new byte[] { + 0x30, 0x2C, 0x30, 0x07, 0x06, 0x03, 0x2B, 0x65, 0x6E, 0x05, 0x00, 0x03, 0x21, 0x00, + }; + + private byte[] uCoordinate; + + public OpenSSLX25519PublicKey(X509EncodedKeySpec keySpec) throws InvalidKeySpecException { + byte[] encoded = keySpec.getEncoded(); + if (encoded == null || !"X.509".equals(keySpec.getFormat())) { + throw new InvalidKeySpecException("Encoding must be in X.509 format"); + } + + int preambleLength = matchesPreamble(X509_PREAMBLE, encoded) | matchesPreamble(X509_PREAMBLE_WITH_NULL, encoded); + if (preambleLength == 0) { + throw new InvalidKeySpecException("Key size is not correct size"); + } + + uCoordinate = Arrays.copyOfRange(encoded, preambleLength, encoded.length); + } + + private static int matchesPreamble(byte[] preamble, byte[] encoded) { + if (encoded.length != (preamble.length + X25519_KEY_SIZE_BYTES)) { + return 0; + } + int cmp = 0; + for (int i = 0; i < preamble.length; i++) { + cmp |= encoded[i] ^ preamble[i]; + } + if (cmp != 0) { + return 0; + } + return preamble.length; + } + + public OpenSSLX25519PublicKey(byte[] coordinateBytes) { + uCoordinate = coordinateBytes.clone(); + } + + @Override + public String getAlgorithm() { + return "XDH"; + } + + @Override + public String getFormat() { + return "X.509"; + } + + @Override + public byte[] getEncoded() { + if (uCoordinate == null) { + throw new IllegalStateException("key is destroyed"); + } + + byte[] encoded = Arrays.copyOf(X509_PREAMBLE, X509_PREAMBLE.length + X25519_KEY_SIZE_BYTES); + System.arraycopy(uCoordinate, 0, encoded, X509_PREAMBLE.length, uCoordinate.length); + return encoded; + } + + @Override + public byte[] getU() { + if (uCoordinate == null) { + throw new IllegalStateException("key is destroyed"); + } + + return uCoordinate.clone(); + } + + @Override + public boolean equals(Object o) { + if (uCoordinate == null) { + throw new IllegalStateException("key is destroyed"); + } + + if (this == o) return true; + if (!(o instanceof OpenSSLX25519PublicKey)) return false; + OpenSSLX25519PublicKey that = (OpenSSLX25519PublicKey) o; + return Arrays.equals(uCoordinate, that.uCoordinate); + } + + @Override + public int hashCode() { + if (uCoordinate == null) { + throw new IllegalStateException("key is destroyed"); + } + + return Arrays.hashCode(uCoordinate); + } +}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CRL.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CRL.java index 4f639a0..f0fb680 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CRL.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CRL.java
@@ -99,12 +99,12 @@ bis.release(); } - final List<OpenSSLX509CRL> certs = new ArrayList<OpenSSLX509CRL>(certRefs.length); - for (int i = 0; i < certRefs.length; i++) { - if (certRefs[i] == 0) { + final List<OpenSSLX509CRL> certs = new ArrayList<>(certRefs.length); + for (long certRef : certRefs) { + if (certRef == 0) { continue; } - certs.add(new OpenSSLX509CRL(certRefs[i])); + certs.add(new OpenSSLX509CRL(certRef)); } return certs; } @@ -139,12 +139,12 @@ bis.release(); } - final List<OpenSSLX509CRL> certs = new ArrayList<OpenSSLX509CRL>(certRefs.length); - for (int i = 0; i < certRefs.length; i++) { - if (certRefs[i] == 0) { + final List<OpenSSLX509CRL> certs = new ArrayList<>(certRefs.length); + for (long certRef : certRefs) { + if (certRef == 0) { continue; } - certs.add(new OpenSSLX509CRL(certRefs[i])); + certs.add(new OpenSSLX509CRL(certRef)); } return certs; } @@ -165,7 +165,7 @@ return null; } - return new HashSet<String>(Arrays.asList(critOids)); + return new HashSet<>(Arrays.asList(critOids)); } @Override @@ -190,7 +190,7 @@ return null; } - return new HashSet<String>(Arrays.asList(nonCritOids)); + return new HashSet<>(Arrays.asList(nonCritOids)); } @Override @@ -221,9 +221,9 @@ } } - private void verifyInternal(PublicKey key, String sigProvider) throws CRLException, - NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, - SignatureException { + private void verifyInternal(PublicKey key, String sigProvider) + throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, + SignatureException { String sigAlg = getSigAlgName(); if (sigAlg == null) { sigAlg = getSigAlgOID(); @@ -330,7 +330,7 @@ return null; } - final Set<OpenSSLX509CRLEntry> crlSet = new HashSet<OpenSSLX509CRLEntry>(); + final Set<OpenSSLX509CRLEntry> crlSet = new HashSet<>(); for (long entryRef : entryRefs) { try { crlSet.add(new OpenSSLX509CRLEntry(entryRef)); @@ -343,7 +343,7 @@ } @Override - public byte[] getTBSCertList() throws CRLException { + public byte[] getTBSCertList() { return NativeCrypto.get_X509_CRL_crl_enc(mContext, this); } @@ -413,6 +413,7 @@ } @Override + @SuppressWarnings("deprecation") protected void finalize() throws Throwable { try { if (mContext != 0) { @@ -422,5 +423,4 @@ super.finalize(); } } - }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertPath.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertPath.java index a76c1fc..eaa9f9a 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertPath.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertPath.java
@@ -158,6 +158,7 @@ try { inStream.reset(); } catch (IOException ignored) { + // Ignored } } throw new CertificateException(e); @@ -221,6 +222,7 @@ try { inStream.reset(); } catch (IOException ignored) { + // Ignored } } throw new CertificateException(e);
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509Certificate.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509Certificate.java index 1cc0fd1..6f664fc 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509Certificate.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509Certificate.java
@@ -132,16 +132,15 @@ if (certRefs == null) { // To avoid returning a immutable list in only one path, we create an // empty list here instead of using Collections.emptyList() - return new ArrayList<OpenSSLX509Certificate>(); + return new ArrayList<>(); } - final List<OpenSSLX509Certificate> certs = new ArrayList<OpenSSLX509Certificate>( - certRefs.length); - for (int i = 0; i < certRefs.length; i++) { - if (certRefs[i] == 0) { + final List<OpenSSLX509Certificate> certs = new ArrayList<>(certRefs.length); + for (long certRef : certRefs) { + if (certRef == 0) { continue; } - certs.add(new OpenSSLX509Certificate(certRefs[i])); + certs.add(new OpenSSLX509Certificate(certRef)); } return certs; } @@ -180,13 +179,12 @@ bis.release(); } - final List<OpenSSLX509Certificate> certs = new ArrayList<OpenSSLX509Certificate>( - certRefs.length); - for (int i = 0; i < certRefs.length; i++) { - if (certRefs[i] == 0) { + final List<OpenSSLX509Certificate> certs = new ArrayList<>(certRefs.length); + for (long certRef : certRefs) { + if (certRef == 0) { continue; } - certs.add(new OpenSSLX509Certificate(certRefs[i])); + certs.add(new OpenSSLX509Certificate(certRef)); } return certs; } @@ -218,7 +216,7 @@ return null; } - return new HashSet<String>(Arrays.asList(critOids)); + return new HashSet<>(Arrays.asList(critOids)); } @Override @@ -242,7 +240,7 @@ return null; } - return new HashSet<String>(Arrays.asList(nonCritOids)); + return new HashSet<>(Arrays.asList(nonCritOids)); } @Override @@ -251,14 +249,16 @@ } @Override - public void checkValidity() throws CertificateExpiredException, - CertificateNotYetValidException { + @SuppressWarnings("JdkObsolete") // Needed for API compatibility + public void checkValidity() + throws CertificateExpiredException, CertificateNotYetValidException { checkValidity(new Date()); } @Override - public void checkValidity(Date date) throws CertificateExpiredException, - CertificateNotYetValidException { + @SuppressWarnings("JdkObsolete") // Needed for API compatibility + public void checkValidity(Date date) + throws CertificateExpiredException, CertificateNotYetValidException { if (getNotBefore().compareTo(date) > 0) { throw new CertificateNotYetValidException("Certificate not valid until " + getNotBefore().toString() + " (compared to " + date.toString() + ")"); @@ -302,7 +302,7 @@ @Override public byte[] getTBSCertificate() throws CertificateEncodingException { - return NativeCrypto.get_X509_cert_info_enc(mContext, this); + return NativeCrypto.get_X509_tbs_cert(mContext, this); } @Override @@ -379,9 +379,7 @@ return NativeCrypto.i2d_X509(mContext, this); } - private void verifyOpenSSL(OpenSSLKey pkey) throws CertificateException, - NoSuchAlgorithmException, - InvalidKeyException, SignatureException { + private void verifyOpenSSL(OpenSSLKey pkey) throws CertificateException, SignatureException { try { NativeCrypto.X509_verify(mContext, this, pkey.getNativeRef()); } catch (RuntimeException e) { @@ -391,9 +389,9 @@ } } - private void verifyInternal(PublicKey key, String sigProvider) throws CertificateException, - NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, - SignatureException { + private void verifyInternal(PublicKey key, String sigProvider) + throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, + SignatureException, CertificateEncodingException { final Signature sig; if (sigProvider == null) { sig = Signature.getInstance(getSigAlgName()); @@ -417,7 +415,7 @@ return; } - verifyInternal(key, (String) null); + verifyInternal(key, null); } @Override @@ -471,8 +469,8 @@ try { OpenSSLKey pkey = new OpenSSLKey(NativeCrypto.X509_get_pubkey(mContext, this)); return pkey.getPublicKey(); - } catch (NoSuchAlgorithmException ignored) { - } catch (InvalidKeyException ignored) { + } catch (NoSuchAlgorithmException | InvalidKeyException ignored) { + // Ignored } /* Try generating the key using other Java providers. */ @@ -481,8 +479,8 @@ try { KeyFactory kf = KeyFactory.getInstance(oid); return kf.generatePublic(new X509EncodedKeySpec(encoded)); - } catch (NoSuchAlgorithmException ignored) { - } catch (InvalidKeySpecException ignored) { + } catch (NoSuchAlgorithmException | InvalidKeySpecException ignored) { + // Ignored } /* @@ -505,7 +503,7 @@ } @Override - public List<String> getExtendedKeyUsage() throws CertificateParsingException { + public List<String> getExtendedKeyUsage() { String[] extUsage = NativeCrypto.get_X509_ex_xkusage(mContext, this); if (extUsage == null) { return null; @@ -519,9 +517,9 @@ return null; } - Collection<List<?>> coll = new ArrayList<List<?>>(altNameArray.length); - for (int i = 0; i < altNameArray.length; i++) { - coll.add(Collections.unmodifiableList(Arrays.asList(altNameArray[i]))); + Collection<List<?>> coll = new ArrayList<>(altNameArray.length); + for (Object[] objects : altNameArray) { + coll.add(Collections.unmodifiableList(Arrays.asList(objects))); } return Collections.unmodifiableCollection(coll); @@ -570,19 +568,14 @@ } /** - * Delete an extension. - * - * A modified copy of the certificate is returned. The original object - * is unchanged. - * If the extension is not present, an unmodified copy is returned. + * Returns a re-encoded TBSCertificate with the extension identified by oid removed. */ - public OpenSSLX509Certificate withDeletedExtension(String oid) { - OpenSSLX509Certificate copy = new OpenSSLX509Certificate(NativeCrypto.X509_dup(mContext, this), notBefore, notAfter); - NativeCrypto.X509_delete_ext(copy.getContext(), copy, oid); - return copy; + public byte[] getTBSCertificateWithoutExtension(String oid) { + return NativeCrypto.get_X509_tbs_cert_without_ext(mContext, this, oid); } @Override + @SuppressWarnings("deprecation") protected void finalize() throws Throwable { try { if (mContext != 0) {
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertificateFactory.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertificateFactory.java index 542228d..a131de7 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertificateFactory.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertificateFactory.java
@@ -62,6 +62,40 @@ } } + private static boolean isMaybePkcs7(byte[] header) { + // The outer tag must be SEQUENCE. + if (header.length < 2 || header[0] != 0x30) { + return false; + } + + // Bytes are signed in Java. + int lengthByte = header[1] & 0xff; + + // Skip the length prefix to find the tag of the first child of SEQUENCE. This function is + // intentionally lax and does not attempt to parse the length itself. It is only necessary + // to return true on PKCS#7 inputs and false on X.509 inputs. Other structures can go either + // way. + int idx = 2; + if (lengthByte <= 0x80) { + // Short-form or indefinite length. + } else if (lengthByte == 0x81) { + idx += 1; + } else if (lengthByte == 0x82) { + idx += 2; + } else if (lengthByte == 0x83) { + idx += 3; + } else if (lengthByte == 0x84) { + idx += 4; + } else { + // BoringSSL stops at 4-byte lengths. A 5-byte length would require a 4GiB input. + return false; + } + + // The first element of a PKCS#7 structure is OBJECT IDENTIFIER, which has tag 6. The first + // element of an X.509 structure is never OBJECT IDENTIFIER. + return idx < header.length && header[idx] == 0x06; + } + /** * The code for X509 Certificates and CRL is pretty much the same. We use * this abstract class to share the code between them. This makes it ugly, @@ -90,19 +124,10 @@ pbis.unread(buffer, 0, len); if (buffer[0] == '-') { - if (len == PKCS7_MARKER.length && Arrays.equals(PKCS7_MARKER, buffer)) { - List<? extends T> items = fromPkcs7PemInputStream(pbis); - if (items.size() == 0) { - return null; - } - items.get(0); - } else { - return fromX509PemInputStream(pbis); - } + return fromX509PemInputStream(pbis); } - /* PKCS#7 bags have a byte 0x06 at position 4 in the stream. */ - if (buffer[4] == 0x06) { + if (isMaybePkcs7(buffer)) { List<? extends T> certs = fromPkcs7DerInputStream(pbis); if (certs.size() == 0) { return null; @@ -116,6 +141,7 @@ try { inStream.reset(); } catch (IOException ignored) { + // If resetting the stream fails, there's not much we can do } } throw new ParsingException(e); @@ -158,8 +184,7 @@ return fromPkcs7PemInputStream(pbis); } - /* PKCS#7 bags have a byte 0x06 at position 4 in the stream. */ - if (buffer[4] == 0x06) { + if (isMaybePkcs7(buffer)) { return fromPkcs7DerInputStream(pbis); } } catch (Exception e) { @@ -167,6 +192,7 @@ try { inStream.reset(); } catch (IOException ignored) { + // If resetting the stream fails, there's not much we can do } } throw new ParsingException(e); @@ -199,6 +225,7 @@ try { inStream.reset(); } catch (IOException ignored) { + // If resetting the stream fails, there's not much we can do } }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLXDHKeyAgreement.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLXDHKeyAgreement.java new file mode 100644 index 0000000..0ee62dd --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLXDHKeyAgreement.java
@@ -0,0 +1,68 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +/* + * Copyright (C) 2013 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 java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; + +/** + * Elliptic Curve Diffie-Hellman key agreement backed by the OpenSSL engine. + * @hide This class is not part of the Android public SDK API + */ +@Internal +public final class OpenSSLXDHKeyAgreement extends OpenSSLBaseDHKeyAgreement<byte[]> { + public OpenSSLXDHKeyAgreement() { + } + + @Override + protected byte[] convertPublicKey(PublicKey key) throws InvalidKeyException { + if (!(key instanceof OpenSSLX25519PublicKey)) { + throw new InvalidKeyException("Only OpenSSLX25519PublicKey accepted"); + } + + return ((OpenSSLX25519PublicKey) key).getU(); + } + + @Override + protected byte[] convertPrivateKey(PrivateKey key) throws InvalidKeyException { + if (!(key instanceof OpenSSLX25519PrivateKey)) { + throw new InvalidKeyException("Only OpenSSLX25519PublicKey accepted"); + } + + return ((OpenSSLX25519PrivateKey) key).getU(); + } + + @Override + protected int computeKey(byte[] buffer, byte[] theirPublicKey, byte[] ourPrivateKey) throws InvalidKeyException { + if (!NativeCrypto.X25519( + buffer, + ourPrivateKey, + theirPublicKey)) { + throw new InvalidKeyException("Error running X25519"); + } + + return OpenSSLX25519Key.X25519_KEY_SIZE_BYTES; + } + + @Override + protected int getOutputSize(byte[] key) { + // We only support X25519 which is 32-byte (256-bit) + return OpenSSLX25519Key.X25519_KEY_SIZE_BYTES; + } +}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLXDHKeyFactory.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLXDHKeyFactory.java new file mode 100644 index 0000000..e8a815f --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLXDHKeyFactory.java
@@ -0,0 +1,217 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +/* + * Copyright (C) 2013 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 java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactorySpi; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * An implementation of a {@link KeyFactorySpi} for EC keys based on BoringSSL. + * @hide This class is not part of the Android public SDK API + */ +@Internal +public final class OpenSSLXDHKeyFactory extends KeyFactorySpi { + + public OpenSSLXDHKeyFactory() {} + + @Override + protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException { + if (keySpec == null) { + throw new InvalidKeySpecException("keySpec == null"); + } + + if (keySpec instanceof X509EncodedKeySpec) { + return new OpenSSLX25519PublicKey((X509EncodedKeySpec) keySpec); + } + throw new InvalidKeySpecException("Must use ECPublicKeySpec or X509EncodedKeySpec; was " + + keySpec.getClass().getName()); + } + + @Override + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException { + if (keySpec == null) { + throw new InvalidKeySpecException("keySpec == null"); + } + + if (keySpec instanceof PKCS8EncodedKeySpec) { + return new OpenSSLX25519PrivateKey((PKCS8EncodedKeySpec) keySpec); + } + throw new InvalidKeySpecException("Must use ECPrivateKeySpec or PKCS8EncodedKeySpec; was " + + keySpec.getClass().getName()); + } + + @Override + protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec) + throws InvalidKeySpecException { + if (key == null) { + throw new InvalidKeySpecException("key == null"); + } + + if (keySpec == null) { + throw new InvalidKeySpecException("keySpec == null"); + } + + if (!"XDH".equals(key.getAlgorithm())) { + throw new InvalidKeySpecException("Key must be an XDH key"); + } + + Class<?> publicKeySpec = getJavaPublicKeySpec(); + Class<?> privateKeySpec = getJavaPrivateKeySpec(); + + if (publicKeySpec != null && key instanceof PublicKey && publicKeySpec.isAssignableFrom(keySpec)) { + final byte[] encoded = key.getEncoded(); + if (!"X.509".equals(key.getFormat()) || encoded == null) { + throw new InvalidKeySpecException("Not a valid X.509 encoding"); + } + OpenSSLX25519PublicKey publicKey = (OpenSSLX25519PublicKey) engineGeneratePublic(new X509EncodedKeySpec(encoded)); + @SuppressWarnings("unchecked") + T result = (T) constructJavaPublicKeySpec(publicKeySpec, publicKey); + return result; + } else if (privateKeySpec != null && key instanceof PrivateKey && privateKeySpec.isAssignableFrom(keySpec)) { + final byte[] encoded = key.getEncoded(); + if (!"PKCS#8".equals(key.getFormat()) || encoded == null) { + throw new InvalidKeySpecException("Not a valid PKCS#8 encoding"); + } + OpenSSLX25519PrivateKey privateKey = (OpenSSLX25519PrivateKey) engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded)); + @SuppressWarnings("unchecked") + T result = (T) constructJavaPrivateKeySpec(privateKeySpec, privateKey); + return result; + } else if (key instanceof PrivateKey && PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) { + final byte[] encoded = key.getEncoded(); + if (!"PKCS#8".equals(key.getFormat())) { + throw new InvalidKeySpecException("Encoding type must be PKCS#8; was " + + key.getFormat()); + } else if (encoded == null) { + throw new InvalidKeySpecException("Key is not encodable"); + } + @SuppressWarnings("unchecked") T result = (T) new PKCS8EncodedKeySpec(encoded); + return result; + } else if (key instanceof PublicKey && X509EncodedKeySpec.class.isAssignableFrom(keySpec)) { + final byte[] encoded = key.getEncoded(); + if (!"X.509".equals(key.getFormat())) { + throw new InvalidKeySpecException("Encoding type must be X.509; was " + + key.getFormat()); + } else if (encoded == null) { + throw new InvalidKeySpecException("Key is not encodable"); + } + @SuppressWarnings("unchecked") T result = (T) new X509EncodedKeySpec(encoded); + return result; + } + + throw new InvalidKeySpecException("Unsupported key type and key spec combination; key=" + + key.getClass().getName() + ", keySpec=" + keySpec.getName()); + } + + @Override + protected Key engineTranslateKey(Key key) throws InvalidKeyException { + if (key == null) { + throw new InvalidKeyException("key == null"); + } + if ((key instanceof OpenSSLX25519PublicKey) || (key instanceof OpenSSLX25519PrivateKey)) { + return key; + } else if ((key instanceof PrivateKey) && "PKCS#8".equals(key.getFormat())) { + byte[] encoded = key.getEncoded(); + if (encoded == null) { + throw new InvalidKeyException("Key does not support encoding"); + } + try { + return engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded)); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } + } else if ((key instanceof PublicKey) && "X.509".equals(key.getFormat())) { + byte[] encoded = key.getEncoded(); + if (encoded == null) { + throw new InvalidKeyException("Key does not support encoding"); + } + try { + return engineGeneratePublic(new X509EncodedKeySpec(encoded)); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } + } else { + throw new InvalidKeyException("Key must be EC public or private key; was " + + key.getClass().getName()); + } + } + + private static Class<?> getJavaPrivateKeySpec() { + try { + return Class.forName("java.security.spec.XECPrivateKeySpec"); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + private static Class<?> getJavaPublicKeySpec() { + try { + return Class.forName("java.security.spec.XECPublicKeySpec"); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + private KeySpec constructJavaPrivateKeySpec(Class<?> privateKeySpec, OpenSSLX25519PrivateKey privateKey) throws InvalidKeySpecException { + if (privateKeySpec == null) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPrivateKeySpec"); + } + + try { + Constructor<?> c = privateKeySpec.getConstructor(AlgorithmParameterSpec.class, byte[].class); + @SuppressWarnings("unchecked") + KeySpec result = (KeySpec) c.newInstance(new OpenSSLXECParameterSpec(OpenSSLXECParameterSpec.X25519), privateKey.getU()); + return result; + } catch (NoSuchMethodException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPrivateKeySpec", e); + } catch (InstantiationException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPrivateKeySpec", e); + } catch (IllegalAccessException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPrivateKeySpec", e); + } catch (InvocationTargetException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPrivateKeySpec", e); + } + } + + private KeySpec constructJavaPublicKeySpec(Class<?> publicKeySpec, OpenSSLX25519PublicKey publicKey) throws InvalidKeySpecException { + try { + Constructor<?> c = publicKeySpec.getConstructor(AlgorithmParameterSpec.class, BigInteger.class); + @SuppressWarnings("unchecked") + KeySpec result = (KeySpec) c.newInstance(new OpenSSLXECParameterSpec(OpenSSLXECParameterSpec.X25519), new BigInteger(1, publicKey.getU())); + return result; + } catch (NoSuchMethodException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPublicKeySpec", e); + } catch (InstantiationException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPublicKeySpec", e); + } catch (IllegalAccessException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPublicKeySpec", e); + } catch (InvocationTargetException e) { + throw new InvalidKeySpecException("Could not find java.security.spec.XECPublicKeySpec", e); + } + } +}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLXDHKeyPairGenerator.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLXDHKeyPairGenerator.java new file mode 100644 index 0000000..1672a78 --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLXDHKeyPairGenerator.java
@@ -0,0 +1,59 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +/* + * Copyright (C) 2012 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 java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +/** + * An implementation of {@link KeyPairGenerator} for XDH keys which uses BoringSSL to perform all the + * operations. This only supports X25519 keys. + * @hide This class is not part of the Android public SDK API + */ +@Internal +public final class OpenSSLXDHKeyPairGenerator extends KeyPairGenerator { + private static final String ALGORITHM = "XDH"; + + public OpenSSLXDHKeyPairGenerator() { + super(ALGORITHM); + } + + @Override + public KeyPair generateKeyPair() { + byte[] publicKeyBytes = new byte[OpenSSLX25519Key.X25519_KEY_SIZE_BYTES]; + byte[] privateKeyBytes = new byte[OpenSSLX25519Key.X25519_KEY_SIZE_BYTES]; + + NativeCrypto.X25519_keypair(publicKeyBytes, privateKeyBytes); + + return new KeyPair(new OpenSSLX25519PublicKey(publicKeyBytes), new OpenSSLX25519PrivateKey(privateKeyBytes)); + } + + @Override + public void initialize(int keysize, SecureRandom random) { + } + + @Override + public void initialize(AlgorithmParameterSpec param, SecureRandom random) + throws InvalidAlgorithmParameterException { + throw new InvalidAlgorithmParameterException( + "No AlgorithmParameterSpec classes are supported"); + } +}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLXECParameterSpec.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLXECParameterSpec.java new file mode 100644 index 0000000..d8f3746 --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLXECParameterSpec.java
@@ -0,0 +1,22 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.conscrypt; + +import java.security.spec.AlgorithmParameterSpec; + +/** + * Parameter markers to assist in future compatibility should other XEC curves be supported. + */ +@Internal +class OpenSSLXECParameterSpec implements AlgorithmParameterSpec { + public static final String X25519 = "1.3.101.110"; + + private final String oid; + + public OpenSSLXECParameterSpec(String oid) { + this.oid = oid; + } + + public String getOid() { + return oid; + } +}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/PSKKeyManager.java b/repackaged/common/src/main/java/com/android/org/conscrypt/PSKKeyManager.java index 6bc09ad..0243cb6 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/PSKKeyManager.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/PSKKeyManager.java
@@ -82,7 +82,7 @@ * The following example illustrates how to create an {@code SSLContext} which enables the use of * TLS-PSK in {@code SSLSocket}, {@code SSLServerSocket} and {@code SSLEngine} instances obtained * from it. - * <pre> {@code + * <pre> * PSKKeyManager myPskKeyManager = ...; * * SSLContext sslContext = SSLContext.getInstance("TLS"); @@ -93,7 +93,7 @@ * ); * * SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(...); - * }</pre> + * </pre> * * @deprecated This abstraction is deprecated because it does not work with TLS 1.3. * @hide This class is not part of the Android public SDK API
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLClientSessionCache.java b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLClientSessionCache.java index c63861d..0460f5b 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLClientSessionCache.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLClientSessionCache.java
@@ -31,7 +31,7 @@ * the {@code SSLClientSessionCache} implementation. * @hide This class is not part of the Android public SDK API */ -@libcore.api.CorePlatformApi +@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Internal public interface SSLClientSessionCache { /**
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLNullSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLNullSession.java index f2fc5d1..fd1311a 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLNullSession.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLNullSession.java
@@ -117,6 +117,7 @@ } @Override + @SuppressWarnings("deprecation") // Public API public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { throw new SSLPeerUnverifiedException("No peer certificate");
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLParametersImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLParametersImpl.java index 946f71e..af73a43 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLParametersImpl.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLParametersImpl.java
@@ -241,7 +241,7 @@ /** * @return X.509 trust manager or {@code null} for none. */ - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) X509TrustManager getX509TrustManager() { return x509TrustManager; } @@ -280,7 +280,7 @@ * Sets the list of available protocols for use in SSL connection. * @throws IllegalArgumentException if {@code protocols == null} */ - @android.compat.annotation.UnsupportedAppUsage + @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void setEnabledProtocols(String[] protocols) { if (protocols == null) { throw new IllegalArgumentException("protocols == null"); @@ -581,7 +581,9 @@ } else if (km != null) { try { return DuckTypedPSKKeyManager.getInstance(km); - } catch (NoSuchMethodException ignored) {} + } catch (NoSuchMethodException ignored) { + // This PSKKeyManager doesn't support the required methods, go to the next + } } } return null;
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLUtils.java b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLUtils.java index 8b2123c..029ee42 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLUtils.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLUtils.java
@@ -276,7 +276,7 @@ */ static Set<String> getSupportedClientKeyTypes(byte[] clientCertificateTypes, int[] signatureAlgs) { - Set<String> fromClientCerts = new HashSet<String>(clientCertificateTypes.length); + Set<String> fromClientCerts = new HashSet<>(clientCertificateTypes.length); for (byte keyTypeCode : clientCertificateTypes) { String keyType = SSLUtils.getClientKeyType(keyTypeCode); if (keyType == null) { @@ -286,7 +286,7 @@ fromClientCerts.add(keyType); } // Signature algorithms are listed in preference order - Set<String> fromSigAlgs = new LinkedHashSet<String>(signatureAlgs.length); + Set<String> fromSigAlgs = new LinkedHashSet<>(signatureAlgs.length); for (int signatureAlg : signatureAlgs) { String keyType = SSLUtils.getClientKeyTypeFromSignatureAlg(signatureAlg); if (keyType == null) { @@ -320,6 +320,7 @@ /** * Converts the peer certificates into a cert chain. */ + @SuppressWarnings("deprecation") // Used in public Conscrypt APIs static javax.security.cert.X509Certificate[] toCertificateChain(X509Certificate[] certificates) throws SSLPeerUnverifiedException { try { @@ -333,11 +334,11 @@ return chain; } catch (CertificateEncodingException e) { SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage()); - exception.initCause(exception); + exception.initCause(e); throw exception; } catch (CertificateException e) { SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage()); - exception.initCause(exception); + exception.initCause(e); throw exception; } }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/SessionSnapshot.java b/repackaged/common/src/main/java/com/android/org/conscrypt/SessionSnapshot.java index ef78b27..5950080 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/SessionSnapshot.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/SessionSnapshot.java
@@ -65,7 +65,7 @@ @Override public List<byte[]> getStatusResponses() { - List<byte[]> ret = new ArrayList<byte[]>(statusResponses.size()); + List<byte[]> ret = new ArrayList<>(statusResponses.size()); for (byte[] resp : statusResponses) { ret.add(resp.clone()); } @@ -142,8 +142,13 @@ } @Override + @SuppressWarnings("deprecation") // Public API public javax.security.cert.X509Certificate[] getPeerCertificateChain() - throws SSLPeerUnverifiedException { + throws SSLPeerUnverifiedException { + if (!Platform.isJavaxCertificateSupported()) { + throw new UnsupportedOperationException("Use getPeerCertificates() instead"); + } + throw new SSLPeerUnverifiedException("No peer certificates"); }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ShortBufferWithoutStackTraceException.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ShortBufferWithoutStackTraceException.java index aaed92b..d01ca64 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/ShortBufferWithoutStackTraceException.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ShortBufferWithoutStackTraceException.java
@@ -36,7 +36,8 @@ super(msg); } - @Override public Throwable fillInStackTrace() { + @Override + public synchronized Throwable fillInStackTrace() { return this; } }
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 4e2115a..79945c4 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
@@ -84,10 +84,9 @@ * @see javax.net.ssl.X509ExtendedTrustManager * @hide This class is not part of the Android public SDK API */ -@libcore.api.CorePlatformApi +@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Internal public final class TrustManagerImpl extends X509ExtendedTrustManager { - private static final Logger logger = Logger.getLogger(TrustManagerImpl.class.getName()); /** @@ -138,7 +137,7 @@ private final Exception err; private final CertificateFactory factory; - private final CertBlacklist blacklist; + private final CertBlocklist blocklist; private CTVerifier ctVerifier; private CTPolicy ctPolicy; @@ -149,12 +148,10 @@ /** * Creates X509TrustManager based on a keystore - * - * @param keyStore */ @android.compat.annotation .UnsupportedAppUsage - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public TrustManagerImpl(KeyStore keyStore) { this(keyStore, null); } @@ -163,24 +160,23 @@ this(keyStore, manager, null); } - @libcore.api.CorePlatformApi - public TrustManagerImpl(KeyStore keyStore, CertPinManager manager, - ConscryptCertStore certStore) { + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) + public TrustManagerImpl( + KeyStore keyStore, CertPinManager manager, ConscryptCertStore certStore) { this(keyStore, manager, certStore, null); } - public TrustManagerImpl(KeyStore keyStore, CertPinManager manager, - ConscryptCertStore certStore, - CertBlacklist blacklist) { - this(keyStore, manager, certStore, blacklist, null, null, null); + public TrustManagerImpl(KeyStore keyStore, CertPinManager manager, ConscryptCertStore certStore, + CertBlocklist blocklist) { + this(keyStore, manager, certStore, blocklist, null, null, null); } /** * For testing only. */ - public TrustManagerImpl(KeyStore keyStore, CertPinManager manager, - ConscryptCertStore certStore, CertBlacklist blacklist, CTLogStore ctLogStore, - CTVerifier ctVerifier, CTPolicy ctPolicy) { + public TrustManagerImpl(KeyStore keyStore, CertPinManager manager, ConscryptCertStore certStore, + CertBlocklist blocklist, CTLogStore ctLogStore, CTVerifier ctVerifier, + CTPolicy ctPolicy) { CertPathValidator validatorLocal = null; CertificateFactory factoryLocal = null; KeyStore rootKeyStoreLocal = null; @@ -212,8 +208,8 @@ errLocal = e; } - if (blacklist == null) { - blacklist = Platform.newDefaultBlacklist(); + if (blocklist == null) { + blocklist = Platform.newDefaultBlocklist(); } if (ctLogStore == null) { ctLogStore = Platform.newDefaultLogStore(); @@ -232,11 +228,12 @@ this.intermediateIndex = new TrustedCertificateIndex(); this.acceptedIssuers = acceptedIssuersLocal; this.err = errLocal; - this.blacklist = blacklist; + this.blocklist = blocklist; this.ctVerifier = new CTVerifier(ctLogStore); this.ctPolicy = ctPolicy; } + @SuppressWarnings("JdkObsolete") // KeyStore#aliases is the only API available private static X509Certificate[] acceptedIssuers(KeyStore ks) { try { // Note that unlike the PKIXParameters code to create a Set of @@ -270,7 +267,7 @@ return trustAnchors; } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { @@ -295,7 +292,7 @@ return session; } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Override public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { @@ -309,7 +306,7 @@ checkTrusted(chain, authType, session, parameters, true /* client auth */); } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Override public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { @@ -331,7 +328,7 @@ */ @android.compat.annotation .UnsupportedAppUsage - @libcore.api.CorePlatformApi + @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, @@ -343,9 +340,9 @@ * * Throws {@link CertificateException} when no trusted chain can be found from {@code certs}. */ - @libcore.api.CorePlatformApi - public List<X509Certificate> getTrustedChainForServer(X509Certificate[] certs, - String authType, Socket socket) throws CertificateException { + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) + public List<X509Certificate> getTrustedChainForServer( + X509Certificate[] certs, String authType, Socket socket) throws CertificateException { SSLSession session = null; SSLParameters parameters = null; if (socket instanceof SSLSocket) { @@ -361,9 +358,9 @@ * * Throws {@link CertificateException} when no trusted chain can be found from {@code certs}. */ - @libcore.api.CorePlatformApi - public List<X509Certificate> getTrustedChainForServer(X509Certificate[] certs, - String authType, SSLEngine engine) throws CertificateException { + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) + public List<X509Certificate> getTrustedChainForServer(X509Certificate[] certs, String authType, + SSLEngine engine) throws CertificateException { SSLSession session = engine.getHandshakeSession(); if (session == null) { throw new CertificateException("Not in handshake; no session available"); @@ -395,7 +392,7 @@ return checkTrusted(chain, authType, session, null, false /* client auth */); } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public void handleTrustStorageUpdate() { if (acceptedIssuers == null) { trustedCertificateIndex.reset(); @@ -420,7 +417,7 @@ String identificationAlgorithm = parameters.getEndpointIdentificationAlgorithm(); if ("HTTPS".equalsIgnoreCase(identificationAlgorithm)) { ConscryptHostnameVerifier verifier = getHttpsVerifier(); - if (!verifier.verify(hostname, session)) { + if (!verifier.verify(certs, hostname, session)) { throw new CertificateException("No subjectAltNames on the certificate match"); } } @@ -429,7 +426,7 @@ } @SuppressWarnings("unchecked") - private byte[] getOcspDataFromSession(SSLSession session) { + private static byte[] getOcspDataFromSession(SSLSession session) { List<byte[]> ocspResponses = null; if (session instanceof ConscryptSession) { ConscryptSession opensslSession = (ConscryptSession) session; @@ -443,10 +440,9 @@ if (rawResponses instanceof List) { ocspResponses = (List<byte[]>) rawResponses; } - } catch (NoSuchMethodException ignored) { - } catch (SecurityException ignored) { - } catch (IllegalAccessException ignored) { - } catch (IllegalArgumentException ignored) { + } catch (NoSuchMethodException | SecurityException | IllegalAccessException + | IllegalArgumentException ignored) { + // Method not available, fall through and return null } catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } @@ -473,10 +469,9 @@ if (rawData instanceof byte[]) { data = (byte[]) rawData; } - } catch (NoSuchMethodException ignored) { - } catch (SecurityException ignored) { - } catch (IllegalAccessException ignored) { - } catch (IllegalArgumentException ignored) { + } catch (NoSuchMethodException | SecurityException | IllegalAccessException + | IllegalArgumentException ignored) { + // Method not available, fall through and return null } catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } @@ -546,8 +541,8 @@ current = trustAnchorChain.get(trustAnchorChain.size() - 1).getTrustedCert(); } - // Check that the certificate isn't blacklisted. - checkBlacklist(current); + // Check that the certificate isn't blocklisted. + checkBlocklist(current); // 1. If the current certificate in the chain is self-signed verify the chain as is. if (current.getIssuerDN().equals(current.getSubjectDN())) { @@ -687,9 +682,9 @@ if (pinManager != null) { pinManager.checkChainPinning(host, wholeChain); } - // Check whole chain against the blacklist + // Check whole chain against the blocklist for (X509Certificate cert : wholeChain) { - checkBlacklist(cert); + checkBlocklist(cert); } // Check CT (if required). @@ -736,9 +731,9 @@ } } - private void checkBlacklist(X509Certificate cert) throws CertificateException { - if (blacklist != null && blacklist.isPublicKeyBlackListed(cert.getPublicKey())) { - throw new CertificateException("Certificate blacklisted by public key: " + cert); + private void checkBlocklist(X509Certificate cert) throws CertificateException { + if (blocklist != null && blocklist.isPublicKeyBlockListed(cert.getPublicKey())) { + throw new CertificateException("Certificate blocklisted by public key: " + cert); } } @@ -958,7 +953,7 @@ return trustAnchor; } if (trustedCertificateStore == null) { - // not trusted and no TrustedCertificateStore to check + // not trusted and no TrustedCertificateStore to check. return null; } // probe KeyStore for a cert. AndroidCAStore stores its @@ -1018,24 +1013,11 @@ return hostnameVerifier; } - private enum GlobalHostnameVerifierAdapter implements ConscryptHostnameVerifier { - INSTANCE; - - @Override - public boolean verify(String hostname, SSLSession session) { - return HttpsURLConnection.getDefaultHostnameVerifier().verify(hostname, session); - } - } - private ConscryptHostnameVerifier getHttpsVerifier() { if (hostnameVerifier != null) { return hostnameVerifier; } - ConscryptHostnameVerifier defaultVerifier = getDefaultHostnameVerifier(); - if (defaultVerifier != null) { - return defaultVerifier; - } - return GlobalHostnameVerifierAdapter.INSTANCE; + return Platform.getDefaultHostnameVerifier(); } public void setCTEnabledOverride(boolean enabled) {
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/TrustedCertificateIndex.java b/repackaged/common/src/main/java/com/android/org/conscrypt/TrustedCertificateIndex.java index 5df6c41..c6e199e 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/TrustedCertificateIndex.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/TrustedCertificateIndex.java
@@ -36,14 +36,13 @@ * time instead of O(N). * @hide This class is not part of the Android public SDK API */ -@libcore.api.CorePlatformApi +@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Internal public final class TrustedCertificateIndex { - private final Map<X500Principal, List<TrustAnchor>> subjectToTrustAnchors = new HashMap<X500Principal, List<TrustAnchor>>(); - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public TrustedCertificateIndex() {} public TrustedCertificateIndex(Set<TrustAnchor> anchors) { @@ -56,7 +55,7 @@ } } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public TrustAnchor index(X509Certificate cert) { TrustAnchor anchor = new TrustAnchor(cert, null); index(anchor); @@ -104,7 +103,7 @@ } } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public TrustAnchor findByIssuerAndSignature(X509Certificate cert) { X500Principal issuer = cert.getIssuerX500Principal(); synchronized (subjectToTrustAnchors) { @@ -125,13 +124,14 @@ cert.verify(publicKey); return anchor; } catch (Exception ignored) { + // Ignored } } } return null; } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public TrustAnchor findBySubjectAndPublicKey(X509Certificate cert) { X500Principal subject = cert.getSubjectX500Principal(); synchronized (subjectToTrustAnchors) { @@ -178,7 +178,7 @@ return null; } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public Set<TrustAnchor> findAllByIssuerAndSignature(X509Certificate cert) { X500Principal issuer = cert.getIssuerX500Principal(); synchronized (subjectToTrustAnchors) { @@ -203,10 +203,10 @@ cert.verify(publicKey); result.add(anchor); } catch (Exception ignored) { + // Ignored } } return result; } } - }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTVerifier.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTVerifier.java index 6851354..3d6945f 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTVerifier.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTVerifier.java
@@ -167,7 +167,7 @@ * @param origin used to create the SignedCertificateTimestamp instances. */ @SuppressWarnings("MixedMutabilityReturnType") - private List<SignedCertificateTimestamp> getSCTsFromSCTList( + private static List<SignedCertificateTimestamp> getSCTsFromSCTList( byte[] data, SignedCertificateTimestamp.Origin origin) { if (data == null) { return Collections.emptyList();
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CertificateEntry.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CertificateEntry.java index 69c9367..84dabe7 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CertificateEntry.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CertificateEntry.java
@@ -77,6 +77,8 @@ } /** + * Creates a CertificateEntry with type PRECERT_ENTRY + * * @throws IllegalArgumentException if issuerKeyHash isn't 32 bytes */ public static CertificateEntry createForPrecertificate(byte[] tbsCertificate, byte[] issuerKeyHash) { @@ -90,8 +92,7 @@ throw new CertificateException("Certificate does not contain embedded signed timestamps"); } - OpenSSLX509Certificate preCert = leaf.withDeletedExtension(CTConstants.X509_SCT_LIST_OID); - byte[] tbs = preCert.getTBSCertificate(); + byte[] tbs = leaf.getTBSCertificateWithoutExtension(CTConstants.X509_SCT_LIST_OID); byte[] issuerKey = issuer.getPublicKey().getEncoded(); MessageDigest md = MessageDigest.getInstance("SHA-256");
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/io/IoUtils.java b/repackaged/common/src/main/java/com/android/org/conscrypt/io/IoUtils.java index 84c3926..e7f9830 100644 --- a/repackaged/common/src/main/java/com/android/org/conscrypt/io/IoUtils.java +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/io/IoUtils.java
@@ -39,6 +39,7 @@ } catch (RuntimeException rethrown) { throw rethrown; } catch (Exception ignored) { + // Ignored } } } @@ -51,6 +52,7 @@ try { socket.close(); } catch (Exception ignored) { + // Ignored } } }
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/CipherSuite.java b/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/CipherSuite.java new file mode 100644 index 0000000..1ab9131 --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/CipherSuite.java
@@ -0,0 +1,83 @@ +/* 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.metrics; + +import com.android.org.conscrypt.Internal; + +/** + * Cipher suites to metric mapping for metrics instrumentation. + * + * Must be in sync with frameworks/base/cmds/statsd/src/atoms.proto + * + * Ids are based on IANA's database of SSL/TLS cipher suites + * @see https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4 + * @hide This class is not part of the Android public SDK API + */ +@Internal +public enum CipherSuite { + UNKNOWN_CIPHER_SUITE(0x0000), + + // Supported but not enabled + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(0xC00A), + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(0xC014), + TLS_RSA_WITH_AES_256_CBC_SHA(0x0035), + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(0xC009), + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(0xC013), + TLS_RSA_WITH_AES_128_CBC_SHA(0x002F), + TLS_RSA_WITH_3DES_EDE_CBC_SHA(0x000A), + + // TLSv1.2 cipher suites + TLS_RSA_WITH_AES_128_GCM_SHA256(0x009C), + TLS_RSA_WITH_AES_256_GCM_SHA384(0x009D), + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F), + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030), + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(0xC02B), + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(0xC02C), + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(0xCCA9), + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256(0xCCA8), + + // Pre-Shared Key (PSK) cipher suites + TLS_PSK_WITH_AES_128_CBC_SHA(0x008C), + TLS_PSK_WITH_AES_256_CBC_SHA(0x008D), + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA(0xC035), + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA(0xC036), + TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256(0xCCAC), + + // TLS 1.3 cipher suites + TLS_AES_128_GCM_SHA256(0x1301), + TLS_AES_256_GCM_SHA384(0x1302), + TLS_CHACHA20_POLY1305_SHA256(0x1303), + ; + + final short id; + + public int getId() { + return this.id; + } + + public static CipherSuite forName(String name) { + try { + return CipherSuite.valueOf(name); + } catch (IllegalArgumentException e) { + return CipherSuite.UNKNOWN_CIPHER_SUITE; + } + } + + private CipherSuite(int id) { + this.id = (short) id; + } +} \ No newline at end of file
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/ConscryptStatsLog.java b/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/ConscryptStatsLog.java new file mode 100644 index 0000000..498ef08 --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/ConscryptStatsLog.java
@@ -0,0 +1,47 @@ +/* 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.metrics; + +import com.android.org.conscrypt.Internal; + +/** + * Reimplement with reflection calls the logging class, + * generated by frameworks/statsd. + * + * In case atom is changed, generate new wrapper with stats-log-api-gen + * tool as shown below and add corresponding methods to ReflexiveStatsEvent's + * newEvent() method. + * + * $ stats-log-api-gen \ + * --java "common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java" \ + * --module conscrypt \ + * --javaPackage org.conscrypt.metrics \ + * --javaClass ConscryptStatsLog + * @hide This class is not part of the Android public SDK API + **/ +@Internal +public final class ConscryptStatsLog { + public static final int TLS_HANDSHAKE_REPORTED = 317; + + public static void write( + int code, boolean success, int protocol, int cipherSuite, int duration) { + ReflexiveStatsEvent event = + ReflexiveStatsEvent.buildEvent(code, success, protocol, cipherSuite, duration); + + ReflexiveStatsLog.write(event); + } +}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/OptionalMethod.java b/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/OptionalMethod.java new file mode 100644 index 0000000..eec769c --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/OptionalMethod.java
@@ -0,0 +1,86 @@ +/* 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.metrics; + +import com.android.org.conscrypt.Internal; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Helper class to handle reflexive loading and invocation of methods which may be absent. + * + * @hide This class is not part of the Android public SDK API + * @hide This class is not part of the Android public SDK API + */ +@Internal +public final class OptionalMethod { + private final Method cachedMethod; + + /** + * Instantiates a new OptionalMethod. + * <p>Does not throw any exceptions if the class or method can't be loaded, or if any parameter + * classes are {@code null} and instead behaves as a no-op, always returning {@code null}. + * + * @param clazz the Class to search for methods on + * @param methodName the name of the {@code Method} on {@code clazz} + * @param methodParams list of {@code Classes} of the {@code Method's} parameters + * + * @throws NullPointerException if the method name is {@code null} + */ + public OptionalMethod(Class<?> clazz, String methodName, Class<?>... methodParams) { + this.cachedMethod = initializeMethod(clazz, methodName, methodParams); + } + + private static Method initializeMethod( + Class<?> clazz, String methodName, Class<?>... methodParams) { + try { + for (Class<?> paramClass : methodParams) { + if (paramClass == null) { + return null; + } + } + if (clazz != null) { + return clazz.getMethod(checkNotNull(methodName), methodParams); + } + } catch (NoSuchMethodException ignored) { + // Ignored + } + return null; + } + + public Object invoke(Object target, Object... args) { + // no-op if failed to load method in constructor + if (cachedMethod == null) { + return null; + } + try { + return cachedMethod.invoke(target, args); + } catch (IllegalAccessException ignored) { + // Ignored + } catch (InvocationTargetException ignored) { + // Ignored + } + return null; + } + + private static <T> T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } + return reference; + } +}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/Protocol.java b/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/Protocol.java new file mode 100644 index 0000000..3165dc1 --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/Protocol.java
@@ -0,0 +1,63 @@ +/* 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.metrics; + +import com.android.org.conscrypt.Internal; + +/** + * Protocols to metric mapping for metrics instrumentation. + * + * Must be in sync with frameworks/base/cmds/statsd/src/atoms.proto + * @hide This class is not part of the Android public SDK API + */ +@Internal +public enum Protocol { + UNKNOWN_PROTO(0), + SSLv3(1), + TLSv1(2), + TLSv1_1(3), + TLSv1_2(4), + TLSv1_3(5), + ; + + final byte id; + + public int getId() { + return this.id; + } + + public static Protocol forName(String name) { + switch (name) { + case "SSLv3": + return SSLv3; + case "TLSv1": + return TLSv1; + case "TLSv1.1": + return TLSv1_1; + case "TLSv1.2": + return TLSv1_2; + case "TLSv1.3": + return TLSv1_3; + default: + return UNKNOWN_PROTO; + } + } + + private Protocol(int id) { + this.id = (byte) id; + } +} \ No newline at end of file
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/ReflexiveStatsEvent.java b/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/ReflexiveStatsEvent.java new file mode 100644 index 0000000..db447ed --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/ReflexiveStatsEvent.java
@@ -0,0 +1,127 @@ +/* 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.metrics; + +import com.android.org.conscrypt.Internal; + +/** + * Reflection wrapper around android.util.StatsEvent. + * @hide This class is not part of the Android public SDK API + */ +@Internal +public class ReflexiveStatsEvent { + private static final OptionalMethod newBuilder; + private static final Class<?> c_statsEvent; + + static { + c_statsEvent = initStatsEventClass(); + newBuilder = new OptionalMethod(c_statsEvent, "newBuilder"); + } + + private static Class<?> initStatsEventClass() { + try { + return Class.forName("android.util.StatsEvent"); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + private Object statsEvent; + + private ReflexiveStatsEvent(Object statsEvent) { + this.statsEvent = statsEvent; + } + + public Object getStatsEvent() { + return statsEvent; + } + + public static ReflexiveStatsEvent.Builder newBuilder() { + return new ReflexiveStatsEvent.Builder(); + } + + public static ReflexiveStatsEvent buildEvent( + int atomId, boolean success, int protocol, int cipherSuite, int duration) { + ReflexiveStatsEvent.Builder builder = ReflexiveStatsEvent.newBuilder(); + builder.setAtomId(atomId); + builder.writeBoolean(success); + builder.writeInt(protocol); + builder.writeInt(cipherSuite); + builder.writeInt(duration); + builder.usePooledBuffer(); + return builder.build(); + } + + /** + * @hide This class is not part of the Android public SDK API + */ + public static final class Builder { + private static final Class<?> c_statsEvent_Builder; + private static final OptionalMethod setAtomId; + private static final OptionalMethod writeBoolean; + private static final OptionalMethod writeInt; + private static final OptionalMethod build; + private static final OptionalMethod usePooledBuffer; + + static { + c_statsEvent_Builder = initStatsEventBuilderClass(); + setAtomId = new OptionalMethod(c_statsEvent_Builder, "setAtomId", int.class); + writeBoolean = new OptionalMethod(c_statsEvent_Builder, "writeBoolean", boolean.class); + writeInt = new OptionalMethod(c_statsEvent_Builder, "writeInt", int.class); + build = new OptionalMethod(c_statsEvent_Builder, "build"); + usePooledBuffer = new OptionalMethod(c_statsEvent_Builder, "usePooledBuffer"); + } + + private static Class<?> initStatsEventBuilderClass() { + try { + return Class.forName("android.util.StatsEvent$Builder"); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + private Object builder; + + private Builder() { + this.builder = newBuilder.invoke(null); + } + + public Builder setAtomId(final int atomId) { + setAtomId.invoke(this.builder, atomId); + return this; + } + + public Builder writeBoolean(final boolean value) { + writeBoolean.invoke(this.builder, value); + return this; + } + + public Builder writeInt(final int value) { + writeInt.invoke(this.builder, value); + return this; + } + + public void usePooledBuffer() { + usePooledBuffer.invoke(this.builder); + } + + public ReflexiveStatsEvent build() { + Object statsEvent = build.invoke(this.builder); + return new ReflexiveStatsEvent(statsEvent); + } + } +} \ No newline at end of file
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/ReflexiveStatsLog.java b/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/ReflexiveStatsLog.java new file mode 100644 index 0000000..6fcce54 --- /dev/null +++ b/repackaged/common/src/main/java/com/android/org/conscrypt/metrics/ReflexiveStatsLog.java
@@ -0,0 +1,58 @@ +/* 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.metrics; + +import com.android.org.conscrypt.Internal; + +/** + * Reflection wrapper around android.util.StatsLog. + * @hide This class is not part of the Android public SDK API + */ +@Internal +public class ReflexiveStatsLog { + private static final Class<?> c_statsLog; + private static final Class<?> c_statsEvent; + private static final OptionalMethod write; + + static { + c_statsLog = initStatsLogClass(); + c_statsEvent = initStatsEventClass(); + write = new OptionalMethod(c_statsLog, "write", c_statsEvent); + } + + private static Class<?> initStatsLogClass() { + try { + return Class.forName("android.util.StatsLog"); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + private static Class<?> initStatsEventClass() { + try { + return Class.forName("android.util.StatsEvent"); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + private ReflexiveStatsLog() {} + + public static void write(ReflexiveStatsEvent event) { + write.invoke(null, event.getStatsEvent()); + } +}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/BufferUtilsTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/BufferUtilsTest.java new file mode 100644 index 0000000..ef26e0d --- /dev/null +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/BufferUtilsTest.java
@@ -0,0 +1,212 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +/* + * Copyright 2021 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.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +import java.nio.ByteBuffer; +import com.android.org.conscrypt.TestUtils.BufferType; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + + +/** + * @hide This class is not part of the Android public SDK API + */ +@RunWith(Parameterized.class) +public class BufferUtilsTest { + private static final int K64 = 64 * 1024; + private static final int K16 = 16 * 1024; + + private static final int[][] TEST_SIZES = { + // All even numbers as several tests use size/2 + { 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 2 }, + { 2, 0, 0, 0 }, + { 100, 200, 300 }, + { 1000, 2000, 3000 }, + { K16 }, + { 0, 0, K16 }, + { K16, 0, 0 }, + { K64 }, + { 0, 0, K64 }, + { K64, 0, 0 }, + { 100, 100, K64 }, + { K64, 100, 100 }, + { K64, K64, K64 }, + }; + + + @Parameters(name = "{0}") + public static BufferType[] data() { + return new BufferType[] { BufferType.HEAP, BufferType.DIRECT }; + } + + @Parameter + public BufferType bufferType; + + @Test + public void checkNotNull() { + for (int[] sizes : TEST_SIZES) { + BufferUtils.checkNotNull(bufferType.newRandomBuffers(sizes)); + } + + ByteBuffer[] buffers = bufferType.newRandomBuffers(10, 10, 10, 10, 10); + buffers[2] = null; + try { + BufferUtils.checkNotNull(buffers); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + } + + @Test + public void remaining() { + for (int[] sizes : TEST_SIZES) { + assertEquals(arraySum(sizes), + BufferUtils.remaining(bufferType.newRandomBuffers(sizes))); + } + } + + @Test + public void consume() { + for (int[] sizes : TEST_SIZES) { + ByteBuffer[] buffers = bufferType.newRandomBuffers(sizes); + int totalSize = arraySum(sizes); + + BufferUtils.consume(buffers, 0); + assertEquals(totalSize, BufferUtils.remaining(buffers)); + + BufferUtils.consume(buffers,totalSize / 2); + assertEquals(totalSize / 2, BufferUtils.remaining(buffers)); + + BufferUtils.consume(buffers,totalSize / 2); + assertEquals(0, BufferUtils.remaining(buffers)); + + if (totalSize > 0) { + try { + BufferUtils.consume(buffers, totalSize / 2); + fail("Managed to consume past end of buffer array"); + } catch (IllegalArgumentException e) { + // Expected + } + } + } + } + + @Test + public void copyNoConsume() { + for (BufferType destinationType : BufferType.values()) { + for (int[] sizes : TEST_SIZES) { + ByteBuffer[] buffers = bufferType.newRandomBuffers(sizes); + int totalSize = arraySum(sizes); + + ByteBuffer destination = destinationType.newBuffer(totalSize); + BufferUtils.copyNoConsume(buffers, destination, totalSize); + assertEquals(totalSize, BufferUtils.remaining(buffers)); + + assertArrayEquals(toArray(buffers), toArray(destination)); + } + } + } + + private static byte[] toArray(ByteBuffer... buffers) { + byte[] bytes = new byte[(int) BufferUtils.remaining(buffers)]; + int offset = 0; + for (ByteBuffer buffer : buffers) { + int length = buffer.remaining(); + if (length > 0) { + buffer.get(bytes, offset, length); + offset += length; + } + } + return bytes; + } + + @Test + public void getBufferLargerThan_allSmall() { + ByteBuffer[] buffers = bufferType.newRandomBuffers(100, 200, 300, 400); + + assertNull(BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 300); + assertNull(BufferUtils.getBufferLargerThan(buffers, K16)); + assertSame(buffers[2], BufferUtils.getBufferLargerThan(buffers, 100)); + + BufferUtils.consume(buffers, 300); + assertSame(buffers[3], BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 200); + assertSame(buffers[3], BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 200); + ByteBuffer buffer = BufferUtils.getBufferLargerThan(buffers, K16); + assertNull(buffer); + } + + @Test + public void getBufferLargerThan_oneLarge() { + ByteBuffer[] buffers = bufferType.newRandomBuffers(100, K64, 300, 400); + + assertNull(BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 100); + assertSame(buffers[1], BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 1024); // 63K remaining in buffers[1] + assertSame(buffers[1], BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 60 * 1024); // 3K remaining in buffers[1] + assertNull(BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 3 * 1024); + assertEquals(0, buffers[1].remaining()); + assertNull(BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 300); + assertSame(buffers[3], BufferUtils.getBufferLargerThan(buffers, K16)); + + BufferUtils.consume(buffers, 400); + ByteBuffer buffer = BufferUtils.getBufferLargerThan(buffers, K16); + assertNull(buffer); + } + + @Test + public void getBufferLargerThan_onlyOneBuffer() { + ByteBuffer[] buffers = bufferType.newRandomBuffers(0, 0, 100, 0, 0); + + assertSame(buffers[2], BufferUtils.getBufferLargerThan(buffers, K16)); + } + + private int arraySum(int[] sizes) { + int sum = 0; + for (int i : sizes) { + sum += i; + } + return sum; + } +}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/HostnameVerifierTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/HostnameVerifierTest.java new file mode 100644 index 0000000..f3da396 --- /dev/null +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/HostnameVerifierTest.java
@@ -0,0 +1,662 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You 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.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.nio.charset.Charset; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collection; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.security.auth.x500.X500Principal; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +/** + * Tests for our hostname verifier. Most of these tests are from AOSP, which + * itself includes tests from the Apache HTTP Client test suite. + * @hide This class is not part of the Android public SDK API + */ +@RunWith(Parameterized.class) +public final class HostnameVerifierTest { + /** + * @hide This class is not part of the Android public SDK API + */ + public static final class FakeSSLSession extends com.android.org.conscrypt.javax.net.ssl.FakeSSLSession { + + private final Certificate[] certificates; + + public FakeSSLSession(Certificate... certificates) throws Exception { + super("FakeHost"); + this.certificates = certificates; + } + + @Override + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { + if (certificates.length == 0) { + throw new SSLPeerUnverifiedException("peer not authenticated"); + } + return certificates; + } + } + + private static final Charset UTF_8 = Charset.forName("UTF-8"); + // BEGIN Android-changed: Run tests for both default and strict verifiers. http://b/144694112 + // private HostnameVerifier verifier = OkHostnameVerifier.INSTANCE; + @Parameters() + public static Collection<Object[]> data() { + // Both verifiers should behave the same in all tests except for + // subjectAltNameWithToplevelWildcard(), and that test is not parameterized for clarity. + return Arrays.asList(new Object[][] { + { OkHostnameVerifier.INSTANCE }, + { OkHostnameVerifier.strictInstance() } + }); + } + + @Parameter + public OkHostnameVerifier verifier; + // END Android-changed: Run tests for both default and strict verifiers. http://b/144694112 + + @Test public void verify() throws Exception { + FakeSSLSession session = new FakeSSLSession(); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs,"localhost", session)); + } + + @Test public void verifyCn() throws Exception { + // CN=foo.com + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIERjCCAy6gAwIBAgIJAIz+EYMBU6aQMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzE0MVoXDTI4MTEwNTE1MzE0MVowgaQx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n" + + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n" + + "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n" + + "FJ8Ud78/OrbKOIJCSBYs2tDLXofYMB8GA1UdIwQYMBaAFHua2o+QmU5S0qzbswNS\n" + + "yoemDT4NMA0GCSqGSIb3DQEBBQUAA4IBAQC3jRmEya6sQCkmieULcvx8zz1euCk9\n" + + "fSez7BEtki8+dmfMXe3K7sH0lI8f4jJR0rbSCjpmCQLYmzC3NxBKeJOW0RcjNBpO\n" + + "c2JlGO9auXv2GDP4IYiXElLJ6VSqc8WvDikv0JmCCWm0Zga+bZbR/EWN5DeEtFdF\n" + + "815CLpJZNcYwiYwGy/CVQ7w2TnXlG+mraZOz+owr+cL6J/ZesbdEWfjoS1+cUEhE\n" + + "HwlNrAu8jlZ2UqSgskSWlhYdMTAP9CPHiUv9N7FcT58Itv/I4fKREINQYjDpvQcx\n" + + "SaTYb9dr5sB4WLNglk7zxDtM80H518VvihTcP7FHL+Gn6g4j5fkI98+S\n" + + "-----END CERTIFICATE-----\n"); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("foo.com", session)); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "foo.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + assertFalse(verifier.verify(certs, "bar.com", session)); + } + + @Test public void verifyNonAsciiCn() throws Exception { + // CN=花子.co.jp + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIESzCCAzOgAwIBAgIJAIz+EYMBU6aTMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1NDIxNVoXDTI4MTEwNTE1NDIxNVowgakx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n" + + "cnRpZmljYXRlczEVMBMGA1UEAwwM6Iqx5a2QLmNvLmpwMSUwIwYJKoZIhvcNAQkB\n" + + "FhZqdWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" + + "MIIBCgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjU\n" + + "g4pNjYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQc\n" + + "wHf0ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t\n" + + "7iu1JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAn\n" + + "AxK6q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArD\n" + + "qUYxqJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG\n" + + "CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV\n" + + "HQ4EFgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLS\n" + + "rNuzA1LKh6YNPg0wDQYJKoZIhvcNAQEFBQADggEBALJ27i3okV/KvlDp6KMID3gd\n" + + "ITl68PyItzzx+SquF8gahMh016NX73z/oVZoVUNdftla8wPUB1GwIkAnGkhQ9LHK\n" + + "spBdbRiCj0gMmLCsX8SrjFvr7cYb2cK6J/fJe92l1tg/7Y4o7V/s4JBe/cy9U9w8\n" + + "a0ctuDmEBCgC784JMDtT67klRfr/2LlqWhlOEq7pUFxRLbhpquaAHSOjmIcWnVpw\n" + + "9BsO7qe46hidgn39hKh1WjKK2VcL/3YRsC4wUi0PBtFW6ScMCuMhgIRXSPU55Rae\n" + + "UIlOdPjjr1SUNWGId1rD7W16Scpwnknn310FNxFMHVI0GTGFkNdkilNCFJcIoRA=\n" + + "-----END CERTIFICATE-----\n"); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session)); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify(certs, "a.\u82b1\u5b50.co.jp", session)); + } + + @Test public void verifySubjectAlt() throws Exception { + // CN=foo.com, subjectAlt=bar.com + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIEXDCCA0SgAwIBAgIJAIz+EYMBU6aRMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzYyOVoXDTI4MTEwNTE1MzYyOVowgaQx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n" + + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaOBkDCBjTAJBgNVHRMEAjAAMCwGCWCG\n" + + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n" + + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n" + + "A1LKh6YNPg0wEgYDVR0RBAswCYIHYmFyLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEA\n" + + "dQyprNZBmVnvuVWjV42sey/PTfkYShJwy1j0/jcFZR/ypZUovpiHGDO1DgL3Y3IP\n" + + "zVQ26uhUsSw6G0gGRiaBDe/0LUclXZoJzXX1qpS55OadxW73brziS0sxRgGrZE/d\n" + + "3g5kkio6IED47OP6wYnlmZ7EKP9cqjWwlnvHnnUcZ2SscoLNYs9rN9ccp8tuq2by\n" + + "88OyhKwGjJfhOudqfTNZcDzRHx4Fzm7UsVaycVw4uDmhEHJrAsmMPpj/+XRK9/42\n" + + "2xq+8bc6HojdtbCyug/fvBZvZqQXSmU8m8IVcMmWMz0ZQO8ee3QkBHMZfCy7P/kr\n" + + "VbWx/uETImUu+NZg22ewEw==\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "foo.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + assertTrue(verifier.verify(certs, "bar.com", session)); + assertFalse(verifier.verify(certs, "a.bar.com", session)); + } + + /** + * Ignored due to incompatibilities between Android and Java on how non-ASCII + * subject alt names are parsed. Android fails to parse these, which means we + * fall back to the CN. The RI does parse them, so the CN is unused. + */ + @Test @Ignore public void verifyNonAsciiSubjectAlt() throws Exception { + // CN=foo.com, subjectAlt=bar.com, subjectAlt=花子.co.jp + // (hanako.co.jp in kanji) + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIEajCCA1KgAwIBAgIJAIz+EYMBU6aSMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzgxM1oXDTI4MTEwNTE1MzgxM1owgaQx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n" + + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaOBnjCBmzAJBgNVHRMEAjAAMCwGCWCG\n" + + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n" + + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n" + + "A1LKh6YNPg0wIAYDVR0RBBkwF4IHYmFyLmNvbYIM6Iqx5a2QLmNvLmpwMA0GCSqG\n" + + "SIb3DQEBBQUAA4IBAQBeZs7ZIYyKtdnVxVvdLgwySEPOE4pBSXii7XYv0Q9QUvG/\n" + + "++gFGQh89HhABzA1mVUjH5dJTQqSLFvRfqTHqLpxSxSWqMHnvRM4cPBkIRp/XlMK\n" + + "PlXadYtJLPTgpbgvulA1ickC9EwlNYWnowZ4uxnfsMghW4HskBqaV+PnQ8Zvy3L0\n" + + "12c7Cg4mKKS5pb1HdRuiD2opZ+Hc77gRQLvtWNS8jQvd/iTbh6fuvTKfAOFoXw22\n" + + "sWIKHYrmhCIRshUNohGXv50m2o+1w9oWmQ6Dkq7lCjfXfUB4wIbggJjpyEtbNqBt\n" + + "j4MC2x5rfsLKKqToKmNE7pFEgqwe8//Aar1b+Qj+\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + assertTrue(verifier.verify(certs, "foo.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + // these checks test alternative subjects. The test data contains an + // alternative subject starting with a japanese kanji character. This is + // not supported by Android because the underlying implementation from + // harmony follows the definition from rfc 1034 page 10 for alternative + // subject names. This causes the code to drop all alternative subjects. + // assertTrue(verifier.verify("bar.com", session)); + // assertFalse(verifier.verify("a.bar.com", session)); + // assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session)); + } + + @Test public void verifySubjectAltOnly() throws Exception { + // subjectAlt=foo.com + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIESjCCAzKgAwIBAgIJAIz+EYMBU6aYMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MjYxMFoXDTI4MTEwNTE2MjYxMFowgZIx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n" + + "cnRpZmljYXRlczElMCMGCSqGSIb3DQEJARYWanVsaXVzZGF2aWVzQGdtYWlsLmNv\n" + + "bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMhjr5aCPoyp0R1iroWA\n" + + "fnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2BlYho4O84X244QrZTRl8kQbYt\n" + + "xnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRyzerA/ZtrlUqf+lKo0uWcocxe\n" + + "Rc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY07hNKXAb2odnVqgzcYiDkLV8\n" + + "ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8BqnGd87xQU3FVZI4tbtkB+Kz\n" + + "jD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiVJTxpTKqym93whYk93l3ocEe5\n" + + "5c0CAwEAAaOBkDCBjTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM\n" + + "IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUnxR3vz86tso4gkJIFiza\n" + + "0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuzA1LKh6YNPg0wEgYDVR0RBAsw\n" + + "CYIHZm9vLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAjl78oMjzFdsMy6F1sGg/IkO8\n" + + "tF5yUgPgFYrs41yzAca7IQu6G9qtFDJz/7ehh/9HoG+oqCCIHPuIOmS7Sd0wnkyJ\n" + + "Y7Y04jVXIb3a6f6AgBkEFP1nOT0z6kjT7vkA5LJ2y3MiDcXuRNMSta5PYVnrX8aZ\n" + + "yiqVUNi40peuZ2R8mAUSBvWgD7z2qWhF8YgDb7wWaFjg53I36vWKn90ZEti3wNCw\n" + + "qAVqixM+J0qJmQStgAc53i2aTMvAQu3A3snvH/PHTBo+5UL72n9S1kZyNCsVf1Qo\n" + + "n8jKTiRriEM+fMFlcgQP284EBFzYHyCXFb9O/hMjK2+6mY9euMB1U1aFFzM/Bg==\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + assertTrue(verifier.verify(certs, "foo.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + assertTrue(verifier.verify(certs, "foo.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + } + + @Test public void verifyMultipleCn() throws Exception { + // CN=foo.com, CN=bar.com, CN=花子.co.jp + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIEbzCCA1egAwIBAgIJAIz+EYMBU6aXMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTk0NVoXDTI4MTEwNTE2MTk0NVowgc0x\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n" + + "cnRpZmljYXRlczEQMA4GA1UEAwwHZm9vLmNvbTEQMA4GA1UEAwwHYmFyLmNvbTEV\n" + + "MBMGA1UEAwwM6Iqx5a2QLmNvLmpwMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyGOv\n" + + "loI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pNjYGViGjg7zhf\n" + + "bjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0ZHLN6sD9m2uV\n" + + "Sp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1JVjTuE0pcBva\n" + + "h2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6q/wGqcZ3zvFB\n" + + "TcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYxqJUlPGlMqrKb\n" + + "3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQf\n" + + "Fh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUnxR3vz86\n" + + "tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuzA1LKh6YNPg0w\n" + + "DQYJKoZIhvcNAQEFBQADggEBAGuZb8ai1NO2j4v3y9TLZvd5s0vh5/TE7n7RX+8U\n" + + "y37OL5k7x9nt0mM1TyAKxlCcY+9h6frue8MemZIILSIvMrtzccqNz0V1WKgA+Orf\n" + + "uUrabmn+CxHF5gpy6g1Qs2IjVYWA5f7FROn/J+Ad8gJYc1azOWCLQqSyfpNRLSvY\n" + + "EriQFEV63XvkJ8JrG62b+2OT2lqT4OO07gSPetppdlSa8NBSKP6Aro9RIX1ZjUZQ\n" + + "SpQFCfo02NO0uNRDPUdJx2huycdNb+AXHaO7eXevDLJ+QnqImIzxWiY6zLOdzjjI\n" + + "VBMkLHmnP7SjGSQ3XA4ByrQOxfOUTyLyE7NuemhHppuQPxE=\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "foo.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + assertFalse(verifier.verify(certs, "bar.com", session)); + assertFalse(verifier.verify(certs, "a.bar.com", session)); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify(certs, "\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify(certs, "a.\u82b1\u5b50.co.jp", session)); + } + + @Test public void verifyWilcardCn() throws Exception { + // CN=*.foo.com + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIESDCCAzCgAwIBAgIJAIz+EYMBU6aUMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTU1NVoXDTI4MTEwNTE2MTU1NVowgaYx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + + "cnRpZmljYXRlczESMBAGA1UEAxQJKi5mb28uY29tMSUwIwYJKoZIhvcNAQkBFhZq\n" + + "dWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" + + "CgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pN\n" + + "jYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0\n" + + "ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1\n" + + "JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6\n" + + "q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYx\n" + + "qJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCG\n" + + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n" + + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n" + + "A1LKh6YNPg0wDQYJKoZIhvcNAQEFBQADggEBAH0ipG6J561UKUfgkeW7GvYwW98B\n" + + "N1ZooWX+JEEZK7+Pf/96d3Ij0rw9ACfN4bpfnCq0VUNZVSYB+GthQ2zYuz7tf/UY\n" + + "A6nxVgR/IjG69BmsBl92uFO7JTNtHztuiPqBn59pt+vNx4yPvno7zmxsfI7jv0ww\n" + + "yfs+0FNm7FwdsC1k47GBSOaGw38kuIVWqXSAbL4EX9GkryGGOKGNh0qvAENCdRSB\n" + + "G9Z6tyMbmfRY+dLSh3a9JwoEcBUso6EWYBakLbq4nG/nvYdYvG9ehrnLVwZFL82e\n" + + "l3Q/RK95bnA6cuRClGusLad0e6bjkBzx/VQ3VarDEpAkTLUGVAa0CLXtnyc=\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "foo.com", session)); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("www.foo.com", session)); + assertFalse(verifier.verify(certs, "www.foo.com", session)); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("\u82b1\u5b50.foo.com", session)); + assertFalse(verifier.verify(certs, "\u82b1\u5b50.foo.com", session)); + assertFalse(verifier.verify(certs, "a.b.foo.com", session)); + } + + @Test public void verifyWilcardCnOnTld() throws Exception { + // It's the CA's responsibility to not issue broad-matching certificates! + // CN=*.co.jp + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIERjCCAy6gAwIBAgIJAIz+EYMBU6aVMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTYzMFoXDTI4MTEwNTE2MTYzMFowgaQx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + + "cnRpZmljYXRlczEQMA4GA1UEAxQHKi5jby5qcDElMCMGCSqGSIb3DQEJARYWanVs\n" + + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n" + + "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n" + + "FJ8Ud78/OrbKOIJCSBYs2tDLXofYMB8GA1UdIwQYMBaAFHua2o+QmU5S0qzbswNS\n" + + "yoemDT4NMA0GCSqGSIb3DQEBBQUAA4IBAQA0sWglVlMx2zNGvUqFC73XtREwii53\n" + + "CfMM6mtf2+f3k/d8KXhLNySrg8RRlN11zgmpPaLtbdTLrmG4UdAHHYr8O4y2BBmE\n" + + "1cxNfGxxechgF8HX10QV4dkyzp6Z1cfwvCeMrT5G/V1pejago0ayXx+GPLbWlNeZ\n" + + "S+Kl0m3p+QplXujtwG5fYcIpaGpiYraBLx3Tadih39QN65CnAh/zRDhLCUzKyt9l\n" + + "UGPLEUDzRHMPHLnSqT1n5UU5UDRytbjJPXzF+l/+WZIsanefWLsxnkgAuZe/oMMF\n" + + "EJMryEzOjg4Tfuc5qM0EXoPcQ/JlheaxZ40p2IyHqbsWV4MRYuFH4bkM\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("foo.co.jp", session)); + assertFalse(verifier.verify(certs, "foo.co.jp", session)); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify(certs, "\u82b1\u5b50.co.jp", session)); + } + + /** + * Ignored due to incompatibilities between Android and Java on how non-ASCII + * subject alt names are parsed. Android fails to parse these, which means we + * fall back to the CN. The RI does parse them, so the CN is unused. + */ + @Test @Ignore public void testWilcardNonAsciiSubjectAlt() throws Exception { + // CN=*.foo.com, subjectAlt=*.bar.com, subjectAlt=*.花子.co.jp + // (*.hanako.co.jp in kanji) + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIEcDCCA1igAwIBAgIJAIz+EYMBU6aWMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTczMVoXDTI4MTEwNTE2MTczMVowgaYx\n" + + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + + "cnRpZmljYXRlczESMBAGA1UEAxQJKi5mb28uY29tMSUwIwYJKoZIhvcNAQkBFhZq\n" + + "dWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" + + "CgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pN\n" + + "jYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0\n" + + "ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1\n" + + "JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6\n" + + "q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYx\n" + + "qJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo4GiMIGfMAkGA1UdEwQCMAAwLAYJ\n" + + "YIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1Ud\n" + + "DgQWBBSfFHe/Pzq2yjiCQkgWLNrQy16H2DAfBgNVHSMEGDAWgBR7mtqPkJlOUtKs\n" + + "27MDUsqHpg0+DTAkBgNVHREEHTAbggkqLmJhci5jb22CDiou6Iqx5a2QLmNvLmpw\n" + + "MA0GCSqGSIb3DQEBBQUAA4IBAQBobWC+D5/lx6YhX64CwZ26XLjxaE0S415ajbBq\n" + + "DK7lz+Rg7zOE3GsTAMi+ldUYnhyz0wDiXB8UwKXl0SDToB2Z4GOgqQjAqoMmrP0u\n" + + "WB6Y6dpkfd1qDRUzI120zPYgSdsXjHW9q2H77iV238hqIU7qCvEz+lfqqWEY504z\n" + + "hYNlknbUnR525ItosEVwXFBJTkZ3Yw8gg02c19yi8TAh5Li3Ad8XQmmSJMWBV4XK\n" + + "qFr0AIZKBlg6NZZFf/0dP9zcKhzSriW27bY0XfzA6GSiRDXrDjgXq6baRT6YwgIg\n" + + "pgJsDbJtZfHnV1nd3M6zOtQPm1TIQpNmMMMd/DPrGcUQerD3\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + // try the foo.com variations + assertTrue(verifier.verify(certs, "foo.com", session)); + assertTrue(verifier.verify(certs, "www.foo.com", session)); + assertTrue(verifier.verify(certs, "\u82b1\u5b50.foo.com", session)); + assertFalse(verifier.verify(certs, "a.b.foo.com", session)); + // these checks test alternative subjects. The test data contains an + // alternative subject starting with a japanese kanji character. This is + // not supported by Android because the underlying implementation from + // harmony follows the definition from rfc 1034 page 10 for alternative + // subject names. This causes the code to drop all alternative subjects. + // assertFalse(verifier.verify("bar.com", session)); + // assertTrue(verifier.verify("www.bar.com", session)); + // assertTrue(verifier.verify("\u82b1\u5b50.bar.com", session)); + // assertTrue(verifier.verify("a.b.bar.com", session)); + } + + @Test public void subjectAltUsesLocalDomainAndIp() throws Exception { + // cat cert.cnf + // [req] + // distinguished_name=distinguished_name + // req_extensions=req_extensions + // x509_extensions=x509_extensions + // [distinguished_name] + // [req_extensions] + // [x509_extensions] + // subjectAltName=DNS:localhost.localdomain,DNS:localhost,IP:127.0.0.1 + // + // $ openssl req -x509 -nodes -days 36500 -subj '/CN=localhost' -config ./cert.cnf \ + // -newkey rsa:512 -out cert.pem + X509Certificate certificate = certificate("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIBWDCCAQKgAwIBAgIJANS1EtICX2AZMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV\n" + + "BAMTCWxvY2FsaG9zdDAgFw0xMjAxMDIxOTA4NThaGA8yMTExMTIwOTE5MDg1OFow\n" + + "FDESMBAGA1UEAxMJbG9jYWxob3N0MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPpt\n" + + "atK8r4/hf4hSIs0os/BSlQLbRBaK9AfBReM4QdAklcQqe6CHsStKfI8pp0zs7Ptg\n" + + "PmMdpbttL0O7mUboBC8CAwEAAaM1MDMwMQYDVR0RBCowKIIVbG9jYWxob3N0Lmxv\n" + + "Y2FsZG9tYWlugglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQEFBQADQQD0ntfL\n" + + "DCzOCv9Ma6Lv5o5jcYWVxvBSTsnt22hsJpWD1K7iY9lbkLwl0ivn73pG2evsAn9G\n" + + "X8YKH52fnHsCrhSD\n" + + "-----END CERTIFICATE-----"); + + assertEquals(new X500Principal("CN=localhost"), certificate.getSubjectX500Principal()); + FakeSSLSession session = new FakeSSLSession(certificate); + + X509Certificate[] certs = {}; + + assertTrue(verifier.verify(certs, "localhost", session)); + assertTrue(verifier.verify(certs, "localhost.localdomain", session)); + assertFalse(verifier.verify(certs, "local.host", session)); + + assertTrue(verifier.verify(certs, "127.0.0.1", session)); + assertFalse(verifier.verify(certs, "127.0.0.2", session)); + } + + @Test public void wildcardsCannotMatchIpAddresses() throws Exception { + // openssl req -x509 -nodes -days 36500 -subj '/CN=*.0.0.1' -newkey rsa:512 -out cert.pem + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIBkjCCATygAwIBAgIJAMdemqOwd/BEMA0GCSqGSIb3DQEBBQUAMBIxEDAOBgNV\n" + + "BAMUByouMC4wLjEwIBcNMTAxMjIwMTY0NDI1WhgPMjExMDExMjYxNjQ0MjVaMBIx\n" + + "EDAOBgNVBAMUByouMC4wLjEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAqY8c9Qrt\n" + + "YPWCvb7lclI+aDHM6fgbJcHsS9Zg8nUOh5dWrS7AgeA25wyaokFl4plBbbHQe2j+\n" + + "cCjsRiJIcQo9HwIDAQABo3MwcTAdBgNVHQ4EFgQUJ436TZPJvwCBKklZZqIvt1Yt\n" + + "JjEwQgYDVR0jBDswOYAUJ436TZPJvwCBKklZZqIvt1YtJjGhFqQUMBIxEDAOBgNV\n" + + "BAMUByouMC4wLjGCCQDHXpqjsHfwRDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB\n" + + "BQUAA0EAk9i88xdjWoewqvE+iMC9tD2obMchgFDaHH0ogxxiRaIKeEly3g0uGxIt\n" + + "fl2WRY8hb4x+zRrwsFaLEpdEvqcjOQ==\n" + + "-----END CERTIFICATE-----"); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "127.0.0.1", session)); + } + + /** + * Earlier implementations of Android's hostname verifier required that + * wildcard names wouldn't match "*.com" or similar. This was a nonstandard + * check that we've since dropped. It is the CA's responsibility to not hand + * out certificates that match so broadly. + */ + @Test public void wildcardsDoesNotNeedTwoDots() throws Exception { + // openssl req -x509 -nodes -days 36500 -subj '/CN=*.com' -newkey rsa:512 -out cert.pem + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIBjDCCATagAwIBAgIJAOVulXCSu6HuMA0GCSqGSIb3DQEBBQUAMBAxDjAMBgNV\n" + + "BAMUBSouY29tMCAXDTEwMTIyMDE2NDkzOFoYDzIxMTAxMTI2MTY0OTM4WjAQMQ4w\n" + + "DAYDVQQDFAUqLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDJd8xqni+h7Iaz\n" + + "ypItivs9kPuiJUqVz+SuJ1C05SFc3PmlRCvwSIfhyD67fHcbMdl+A/LrIjhhKZJe\n" + + "1joO0+pFAgMBAAGjcTBvMB0GA1UdDgQWBBS4Iuzf5w8JdCp+EtBfdFNudf6+YzBA\n" + + "BgNVHSMEOTA3gBS4Iuzf5w8JdCp+EtBfdFNudf6+Y6EUpBIwEDEOMAwGA1UEAxQF\n" + + "Ki5jb22CCQDlbpVwkruh7jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA0EA\n" + + "U6LFxmZr31lFyis2/T68PpjAppc0DpNQuA2m/Y7oTHBDi55Fw6HVHCw3lucuWZ5d\n" + + "qUYo4ES548JdpQtcLrW2sA==\n" + + "-----END CERTIFICATE-----"); + // Android-changed: Ignore common name in hostname verification. http://b/70278814 + // assertTrue(verifier.verify("google.com", session)); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "google.com", session)); + } + + @Test public void subjectAltName() throws Exception { + // $ cat ./cert.cnf + // [req] + // distinguished_name=distinguished_name + // req_extensions=req_extensions + // x509_extensions=x509_extensions + // [distinguished_name] + // [req_extensions] + // [x509_extensions] + // subjectAltName=DNS:bar.com,DNS:baz.com + // + // $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \ + // -newkey rsa:512 -out cert.pem + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIBPTCB6KADAgECAgkA7zoHaaqNGHQwDQYJKoZIhvcNAQEFBQAwEjEQMA4GA1UE\n" + + "AxMHZm9vLmNvbTAgFw0xMDEyMjAxODM5MzZaGA8yMTEwMTEyNjE4MzkzNlowEjEQ\n" + + "MA4GA1UEAxMHZm9vLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC+gmoSxF+8\n" + + "hbV+rgRQqHIJd50216OWQJbU3BvdlPbca779NYO4+UZWTFdBM8BdQqs3H4B5Agvp\n" + + "y7HeSff1F7XRAgMBAAGjHzAdMBsGA1UdEQQUMBKCB2Jhci5jb22CB2Jhei5jb20w\n" + + "DQYJKoZIhvcNAQEFBQADQQBXpZZPOY2Dy1lGG81JTr8L4or9jpKacD7n51eS8iqI\n" + + "oTznPNuXHU5bFN0AAGX2ij47f/EahqTpo5RdS95P4sVm\n" + + "-----END CERTIFICATE-----"); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "foo.com", session)); + assertTrue(verifier.verify(certs, "bar.com", session)); + assertTrue(verifier.verify(certs, "baz.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + assertFalse(verifier.verify(certs, "quux.com", session)); + } + + @Test public void subjectAltNameWithWildcard() throws Exception { + // $ cat ./cert.cnf + // [req] + // distinguished_name=distinguished_name + // req_extensions=req_extensions + // x509_extensions=x509_extensions + // [distinguished_name] + // [req_extensions] + // [x509_extensions] + // subjectAltName=DNS:bar.com,DNS:*.baz.com + // + // $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \ + // -newkey rsa:512 -out cert.pem + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIBPzCB6qADAgECAgkAnv/7Jv5r7pMwDQYJKoZIhvcNAQEFBQAwEjEQMA4GA1UE\n" + + "AxMHZm9vLmNvbTAgFw0xMDEyMjAxODQ2MDFaGA8yMTEwMTEyNjE4NDYwMVowEjEQ\n" + + "MA4GA1UEAxMHZm9vLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDAz2YXnyog\n" + + "YdYLSFr/OEgSumtwqtZKJTB4wqTW/eKbBCEzxnyUMxWZIqUGu353PzwfOuWp2re3\n" + + "nvVV+QDYQlh9AgMBAAGjITAfMB0GA1UdEQQWMBSCB2Jhci5jb22CCSouYmF6LmNv\n" + + "bTANBgkqhkiG9w0BAQUFAANBAB8yrSl8zqy07i0SNYx2B/FnvQY734pxioaqFWfO\n" + + "Bqo1ZZl/9aPHEWIwBrxYNVB0SGu/kkbt/vxqOjzzrkXukmI=\n" + + "-----END CERTIFICATE-----"); + X509Certificate[] certs = {}; + assertFalse(verifier.verify(certs, "foo.com", session)); + assertTrue(verifier.verify(certs, "bar.com", session)); + assertTrue(verifier.verify(certs, "a.baz.com", session)); + assertFalse(verifier.verify(certs, "baz.com", session)); + assertFalse(verifier.verify(certs, "a.foo.com", session)); + assertFalse(verifier.verify(certs, "a.bar.com", session)); + assertFalse(verifier.verify(certs, "quux.com", session)); + } + + // BEGIN Android-added: Verify behaviour with top level wildcard SAN. http://b/144694112 + @Test + public void subjectAltNameWithToplevelWildcard() throws Exception { + // Default OkHostnameVerifier instance should allow SANs which + // have wildcards for top-level domains. The strict instance should not. + // + // Certificate generated using:- + // openssl req -x509 -nodes -days 36500 -subj "/CN=Google Inc" \ + // -addext "subjectAltName=DNS:*.com" -newkey rsa:512 + SSLSession session = session("" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIBlTCCAT+gAwIBAgIUe1RB6C61ZW/SEQpKiywSEJOEOUMwDQYJKoZIhvcNAQEL\n" + + "BQAwFTETMBEGA1UEAwwKR29vZ2xlIEluYzAgFw0xOTExMjExMjE1NTBaGA8yMTE5\n" + + "MTAyODEyMTU1MFowFTETMBEGA1UEAwwKR29vZ2xlIEluYzBcMA0GCSqGSIb3DQEB\n" + + "AQUAA0sAMEgCQQCu24jT8hktpvnmcde4dqC6e7G5F4cNNLUFnTi3Ay9BzPH1r7sN\n" + + "v2lHTIQLKSlvjxa48mpeRBlOjDQigv7c+rfRAgMBAAGjZTBjMB0GA1UdDgQWBBQd\n" + + "myvYKfluxb0+kNEJoh1ZER2wUTAfBgNVHSMEGDAWgBQdmyvYKfluxb0+kNEJoh1Z\n" + + "ER2wUTAPBgNVHRMBAf8EBTADAQH/MBAGA1UdEQQJMAeCBSouY29tMA0GCSqGSIb3\n" + + "DQEBCwUAA0EAK710g2hQpXSmpbOQH4dHG61fkVDtM/kR/4/R61vDDqVkgOuyHqXl\n" + + "GUZFKHMeOZ8peQLT8b+5ik6pIO7Vu2pF6w==\n" + + "-----END CERTIFICATE-----\n"); + X509Certificate[] certs = {}; + assertTrue(OkHostnameVerifier.INSTANCE.verify(certs, "google.com", session)); + assertFalse(OkHostnameVerifier.strictInstance().verify(certs, "google.com", session)); + } + // END Android-added: Verify behaviour with top level wildcard SAN. http://b/144694112 + + // Android-changed: OkHostnameVerifier.verifyAsIpAddress not accessible on platform builds + @Test + @Ignore + public void verifyAsIpAddress() { + // IPv4 + assertTrue(OkHostnameVerifier.verifyAsIpAddress("127.0.0.1")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("1.2.3.4")); + + // IPv6 + assertTrue(OkHostnameVerifier.verifyAsIpAddress("::1")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("2001:db8::1")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("::192.168.0.1")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("::ffff:192.168.0.1")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("FEDC:BA98:7654:3210:FEDC:BA98:7654:3210")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("1080:0:0:0:8:800:200C:417A")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("1080::8:800:200C:417A")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("FF01::101")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("0:0:0:0:0:0:13.1.68.3")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("0:0:0:0:0:FFFF:129.144.52.38")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("::13.1.68.3")); + assertTrue(OkHostnameVerifier.verifyAsIpAddress("::FFFF:129.144.52.38")); + + // Hostnames + assertFalse(OkHostnameVerifier.verifyAsIpAddress("go")); + assertFalse(OkHostnameVerifier.verifyAsIpAddress("localhost")); + assertFalse(OkHostnameVerifier.verifyAsIpAddress("squareup.com")); + assertFalse(OkHostnameVerifier.verifyAsIpAddress("www.nintendo.co.jp")); + } + + private X509Certificate certificate(String certificate) throws Exception { + return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate( + new ByteArrayInputStream(certificate.getBytes(UTF_8))); + } + + private SSLSession session(String certificate) throws Exception { + return new FakeSSLSession(certificate(certificate)); + } +}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/MacTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/MacTest.java new file mode 100644 index 0000000..577162b --- /dev/null +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/MacTest.java
@@ -0,0 +1,403 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +/* + * Copyright (C) 2021 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 com.android.org.conscrypt.TestUtils.decodeHex; +import static com.android.org.conscrypt.TestUtils.encodeHex; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.spec.AlgorithmParameterSpec; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import javax.crypto.Mac; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import tests.util.ServiceTester; + + +/** + * @hide This class is not part of the Android public SDK API + */ +@RunWith(JUnit4.class) +public class MacTest { + private final List<String[]> testVectors = readTestVectors(); + + // Column indices in test vector CSV file + private static final int ALGORITHM_INDEX = 0; + private static final int KEY_INDEX = 1; + private static final int MESSAGE_INDEX = 2; + private static final int MAC_INDEX = 3; + + // Number of splits to use when testing multiple buffers + private static final int NUM_SPLITS = 4; + + private final Random random = new Random(System.currentTimeMillis()); + + private final Provider conscryptProvider = TestUtils.getConscryptProvider(); + + @BeforeClass + public static void setUp() { + TestUtils.assumeAllowsUnsignedCrypto(); + } + + @Test + public void knownAnswerTest() throws Exception { + for (String[] entry : testVectors) { + String algorithm = entry[ALGORITHM_INDEX]; + String key = entry[KEY_INDEX]; + String msg = entry[MESSAGE_INDEX]; + String expected = entry[MAC_INDEX]; + + byte[] keyBytes = decodeHex(key); + byte[] msgBytes = decodeHex(msg); + byte[] expectedBytes = decodeHex(expected); + SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "RawBytes"); + + String baseFailMsg = String.format("Mac=%s\nKey=%s\nMsg=%s\nExpected=%s", + algorithm, key, msg, expected); + + // Calculate using Mac.update(byte[]) + byte[] macBytes = generateMacUsingUpdate(algorithm, secretKey, msgBytes); + assertArrayEquals(failMessage("Using update()", baseFailMsg, macBytes), + expectedBytes, macBytes); + + // Calculate using Mac.final(byte[]) + macBytes = generateMacUsingFinal(algorithm, secretKey, msgBytes); + assertArrayEquals(failMessage("Using final()", baseFailMsg, macBytes), + expectedBytes, macBytes); + + // Calculate using Mac.update(ByteBuffer) with a single non-direct ByteBuffer + ByteBuffer nondirectBuffer = ByteBuffer.wrap(msgBytes); + macBytes = generateMac(algorithm, secretKey, nondirectBuffer); + assertArrayEquals(failMessage("Non-direct ByteBuffer", baseFailMsg, macBytes), + expectedBytes, macBytes); + + // Calculate using Mac.update(ByteBuffer) with a single direct ByteBuffer + ByteBuffer directBuffer = ByteBuffer.allocateDirect(msgBytes.length); + directBuffer.put(msgBytes); + directBuffer.flip(); + macBytes = generateMac(algorithm, secretKey, directBuffer); + assertArrayEquals(failMessage("Direct ByteBuffer", baseFailMsg, macBytes), + expectedBytes, macBytes); + + // Calculate using Mac.update(ByteBuffer) with a multiple non-direct ByteBuffers + nondirectBuffer.flip(); + macBytes = generateMac(algorithm, secretKey, split(nondirectBuffer)); + assertArrayEquals(failMessage("Multiple non-direct ByteBuffers", baseFailMsg, macBytes), + expectedBytes, macBytes); + + // Calculate using Mac.update(ByteBuffer) with a multiple direct ByteBuffers + directBuffer.flip(); + macBytes = generateMac(algorithm, secretKey, split(directBuffer)); + assertArrayEquals(failMessage("Multiple direct ByteBuffers", baseFailMsg, macBytes), + expectedBytes, macBytes); + + // Calculated using a pre-loved Mac + macBytes = generateReusingMac(algorithm, keyBytes, msgBytes); + assertArrayEquals(failMessage("Re-use Mac", baseFailMsg, macBytes), + expectedBytes, macBytes); + } + } + + @Test + public void serviceCreation() { + newMacServiceTester() + // Android KeyStore can only be initialised with its own private keys - tested + // elsewhere. + .skipProvider("AndroidKeyStore") + .skipProvider("AndroidKeyStoreBCWorkaround") + .run(new ServiceTester.Test() { + @Override + public void test(final Provider provider, final String algorithm) + throws Exception { + SecretKeySpec key = findAnyKey(algorithm); + + Mac mac = Mac.getInstance(algorithm); + assertEquals(algorithm, mac.getAlgorithm()); + + mac = Mac.getInstance(algorithm, provider); + assertEquals(algorithm, mac.getAlgorithm()); + assertEquals(provider, mac.getProvider()); + if (key != null) { + // TODO(prb) Ensure we have at least one test vector for every + // MAC in Conscrypt and Android. + mac.init(key); + assertEquals(provider, mac.getProvider()); + } + + mac = Mac.getInstance(algorithm, provider.getName()); + assertEquals(algorithm, mac.getAlgorithm()); + assertEquals(provider, mac.getProvider()); + if (key != null) { + mac.init(key); + assertEquals(provider, mac.getProvider()); + } + } + }); + } + + @Test + public void invalidKeyThrows() { + newMacServiceTester() + // BC actually accepts RSA public keys for these algorithms for some reason. + .skipCombination("BC", "PBEWITHHMACSHA") + .skipCombination("BC", "PBEWITHHMACSHA1") + .run(new ServiceTester.Test() { + @Override + public void test(final Provider provider, final String algorithm) + throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair keyPair = generator.generateKeyPair(); + + try { + Mac mac = Mac.getInstance(algorithm, provider); + mac.init(keyPair.getPublic(), null); + fail(); + } catch (InvalidKeyException e) { + // Expected + } + } + }); + } + + @Test + public void uninitializedMacThrows() { + newMacServiceTester().run(new ServiceTester.Test() { + @Override + public void test(final Provider provider, final String algorithm) throws Exception { + byte[] message = "Message".getBytes(StandardCharsets.UTF_8); + + try { + Mac mac = Mac.getInstance(algorithm, provider); + mac.update(message); + fail(); + } catch (IllegalStateException e) { + // Expected + } + try { + Mac mac = Mac.getInstance(algorithm, provider); + mac.doFinal(message); + fail(); + } catch (IllegalStateException e) { + // Expected + } + try { + Mac mac = Mac.getInstance(algorithm, provider); + mac.doFinal(); + fail(); + } catch (IllegalStateException e) { + // Expected + } + } + }); + } + + private ServiceTester newMacServiceTester() { + return ServiceTester + .test("Mac") + // On Android 10 and 11 BC advertises these Macs but they are deprecated so throw + // on initialization. + .skipCombination("BC", "HMACMD5") + .skipCombination("BC", "HMACSHA1") + .skipCombination("BC", "HMACSHA224") + .skipCombination("BC", "HMACSHA256") + .skipCombination("BC", "HMACSHA384") + .skipCombination("BC", "HMACSHA512") + .skipCombination("BC", "PBEWITHHMACSHA224") + .skipCombination("BC", "PBEWITHHMACSHA256") + .skipCombination("BC", "PBEWITHHMACSHA384") + .skipCombination("BC", "PBEWITHHMACSHA512"); + } + + private static class DummyParameterSpec implements AlgorithmParameterSpec { } + + @Test + public void algorithmParameters() { + ServiceTester + .test("Mac") + // Android KeyStore can only be initialised with its own private keys - tested + // elsewhere. + .skipProvider("AndroidKeyStore") + .skipProvider("AndroidKeyStoreBCWorkaround") + .run(new ServiceTester.Test() { + @Override + public void test(final Provider provider, final String algorithm) + throws Exception { + SecretKeySpec key = findAnyKey(algorithm); + if (key != null) { + Mac mac = Mac.getInstance(algorithm, provider); + // Equivalent to mac.init(key) - allowed + mac.init(key, null); + + try { + mac = Mac.getInstance(algorithm, provider); + mac.init(key, new DummyParameterSpec()); + fail(); + } catch (InvalidAlgorithmParameterException exception) { + // Expected + } + } + } + }); + } + + private SecretKeySpec findAnyKey(String algorithm) { + for (String[] entry : testVectors) { + if (entry[ALGORITHM_INDEX].equals(algorithm)) { + return new SecretKeySpec(decodeHex(entry[KEY_INDEX]), "RawBytes"); + } + } + return null; + } + + @Test + public void anyAlgorithmParametersThrows() throws Exception { + Set<String> seen = new HashSet<>(); + for (String[] entry : testVectors) { + String algorithm = entry[ALGORITHM_INDEX]; + if (!seen.contains(algorithm)) { + seen.add(algorithm); + byte[] keyBytes = decodeHex(entry[KEY_INDEX]); + SecretKeySpec key = new SecretKeySpec(keyBytes, "RawBytes"); + Mac mac = Mac.getInstance(algorithm); + try { + mac.init(key, new IvParameterSpec(keyBytes)); + fail(algorithm); + } catch (InvalidAlgorithmParameterException exception) { + // Expected + } + } + } + } + + private String failMessage(String test, String base, byte[] mac) { + return String.format("Test %s\n%s\nActual= %s", test, base, encodeHex(mac)); + } + + // Splits a ByteBuffer into an array of NUM_SPLITS ByteBuffers containing the same data. + // If input.remaining < NUM_SPLITS then some buffers will be empty, which is fine. + private ByteBuffer[] split(ByteBuffer input) { + ByteBuffer[] buffers = new ByteBuffer[NUM_SPLITS]; + int targetSize = (input.remaining() / NUM_SPLITS) + 1; + ByteBuffer buffer; + for (int i = 0; i < NUM_SPLITS; i++) { + int size = Math.min(targetSize, input.remaining()); + buffer = input.isDirect() ? ByteBuffer.allocateDirect(size) : ByteBuffer.allocate(size); + buffers[i] = buffer; + + int savedLimit = input.limit(); + input.limit(input.position() + size); + buffer.put(input); + buffer.flip(); + input.limit(savedLimit); + } + assertEquals(0, input.remaining()); + return buffers; + } + + private byte[] generateMacUsingUpdate(String algorithm, SecretKeySpec key, byte[] message) + throws Exception { + Mac mac = getConscryptMac(algorithm, key); + mac.update(message); + return mac.doFinal(); + } + + private byte[] generateMacUsingFinal(String algorithm, SecretKeySpec key, byte[] message) + throws Exception { + Mac mac = getConscryptMac(algorithm, key); + return mac.doFinal(message); + } + + private byte[] generateMac(String algorithm, SecretKeySpec key, ByteBuffer buffer) + throws Exception { + return generateMac(algorithm, key, new ByteBuffer[] { buffer }); + } + + private byte[] generateMac(String algorithm, SecretKeySpec key, ByteBuffer[] buffers) + throws Exception { + Mac mac = getConscryptMac(algorithm, key); + for (ByteBuffer buffer : buffers) { + mac.update(buffer); + } + return mac.doFinal(); + } + + private byte[] generateReusingMac(String algorithm, byte[] keyBytes, byte[] message) + throws Exception { + Mac mac = getConscryptMac(algorithm); + + // Mutate the original message and key and calculate a MAC from them + byte[] otherKeyBytes = new byte[keyBytes.length]; + random.nextBytes(otherKeyBytes); + SecretKeySpec otherKey = new SecretKeySpec(otherKeyBytes, "RawBytes"); + byte[] otherMessage = new byte[message.length]; + random.nextBytes(otherMessage); + mac.init(otherKey); + mac.doFinal(otherMessage); + + // Then re-use the same Mac with the original key and message + SecretKeySpec key = new SecretKeySpec(keyBytes, "RawBytes"); + mac.reset(); + mac.init(key); + mac.update(message); + return mac.doFinal(); + } + + private Mac getConscryptMac(String algorithm) throws Exception { + return getConscryptMac(algorithm, null); + } + + private Mac getConscryptMac(String algorithm, SecretKeySpec key) throws Exception { + Mac mac = Mac.getInstance(algorithm, conscryptProvider); + assertNotNull(mac); + if (key != null) { + // Provider is not actually chosen until init + mac.init(key); + assertSame(conscryptProvider, mac.getProvider()); + } + return mac; + } + + private List<String[]> readTestVectors() { + try { + return TestUtils.readCsvResource("crypto/macs.csv"); + + } catch (IOException e) { + throw new AssertionError("Unable to load MAC test vectors", e); + } + } +}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/TrustManagerImplTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/TrustManagerImplTest.java index f08a46a..ea58efb 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/TrustManagerImplTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/TrustManagerImplTest.java
@@ -22,8 +22,8 @@ import static org.junit.Assert.fail; import com.android.org.conscrypt.java.security.TestKeyStore; +import com.android.org.conscrypt.javax.net.ssl.TestHostnameVerifier; import java.io.IOException; -import java.lang.reflect.Method; import java.security.KeyStore; import java.security.Principal; import java.security.cert.Certificate; @@ -32,8 +32,6 @@ import java.util.Arrays; import java.util.List; import javax.net.ssl.HandshakeCompletedListener; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; @@ -141,11 +139,7 @@ String goodHostname = TestKeyStore.CERT_HOSTNAME; String badHostname = "definitelywrong.nopenopenope"; - // The default hostname verifier on OpenJDK rejects all hostnames, so use our own - javax.net.ssl.HostnameVerifier oldDefault = HttpsURLConnection.getDefaultHostnameVerifier(); try { - HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier()); - SSLParameters params = new SSLParameters(); // Without endpoint identification this should pass despite the mismatched hostname @@ -171,13 +165,13 @@ // Override the global default hostname verifier with a Conscrypt-specific one that // always passes. Both scenarios should pass. - HostnameVerifier alwaysTrue = new HostnameVerifier() { + Conscrypt.setHostnameVerifier(tmi, new ConscryptHostnameVerifier() { @Override - public boolean verify(String hostname, SSLSession session) { + public boolean verify( + X509Certificate[] certificates, String s, SSLSession sslSession) { return true; } - }; - HttpsURLConnection.setDefaultHostnameVerifier(alwaysTrue); + }); certs = tmi.getTrustedChainForServer(chain, "RSA", new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); @@ -189,7 +183,8 @@ // Now set an instance-specific verifier on the trust manager. The bad hostname should // fail again. - Conscrypt.setHostnameVerifier(tmi, wrapVerifier(new TestHostnameVerifier())); + Conscrypt.setHostnameVerifier( + tmi, Conscrypt.wrapHostnameVerifier(new TestHostnameVerifier())); try { tmi.getTrustedChainForServer(chain, "RSA", @@ -205,43 +200,21 @@ // Remove the instance-specific verifier, and both should pass again. Conscrypt.setHostnameVerifier(tmi, null); - certs = tmi.getTrustedChainForServer(chain, "RSA", - new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); - assertEquals(Arrays.asList(chain), certs); + try { + tmi.getTrustedChainForServer(chain, "RSA", + new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); + fail(); + } catch (CertificateException expected) { + } certs = tmi.getTrustedChainForServer(chain, "RSA", new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); assertEquals(Arrays.asList(chain), certs); } finally { Conscrypt.setDefaultHostnameVerifier(null); - HttpsURLConnection.setDefaultHostnameVerifier(oldDefault); } } - /* - * Wrap a HostnameVerifier in a ConscryptHostnameVerifier. - * In the Android platform ConscryptHostnameVerifier is a private API and the interface - * definition changed between Android 11 and Android 12. - * If an Android 12 Conscrypt module is present then there will also be a (non-public) - * method to wrap it with the correct interface. - * If an earlier module is present then the interface is the same as in the CTS 11 codebase - * and so we can just wrap it directly with an anonymous class. - * See also b/195615915 - */ - private ConscryptHostnameVerifier wrapVerifier(final HostnameVerifier verifier) - throws Exception { - Method wrapMethod = TestUtils.findWrapVerifierMethod(); - if (wrapMethod != null) { - return (ConscryptHostnameVerifier) wrapMethod.invoke(null, verifier); - } - return new ConscryptHostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return verifier.verify(hostname, session); - } - }; - } - private X509TrustManager trustManager(X509Certificate ca) throws Exception { KeyStore keyStore = TestKeyStore.createKeyStore(); keyStore.setCertificateEntry("alias", ca); @@ -508,8 +481,4 @@ throw new UnsupportedOperationException(); } } - - private static class TestHostnameVerifier - extends com.android.org.conscrypt.javax.net.ssl.TestHostnameVerifier - implements ConscryptHostnameVerifier {} }
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestEC.java b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestEC.java index bee284a..de48861 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestEC.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestEC.java
@@ -27,6 +27,7 @@ import java.util.List; import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import org.junit.ClassRule; +import org.junit.Test; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -36,40 +37,38 @@ * @hide This class is not part of the Android public SDK API */ @RunWith(JUnit4.class) -public class KeyFactoryTestEC extends - AbstractKeyFactoryTest<ECPublicKeySpec, ECPrivateKeySpec> { +public class KeyFactoryTestEC extends AbstractKeyFactoryTest<ECPublicKeySpec, ECPrivateKeySpec> { + // BEGIN Android-Added: Allow access to deprecated BC algorithms. + // Allow access to deprecated BC algorithms in this test, so we can ensure they + // continue to work + @ClassRule + public static TestRule enableDeprecatedBCAlgorithmsRule = + EnableDeprecatedBouncyCastleAlgorithmsRule.getInstance(); + // END Android-Added: Allow access to deprecated BC algorithms. - // BEGIN Android-Added: Allow access to deprecated BC algorithms. - // Allow access to deprecated BC algorithms in this test, so we can ensure they - // continue to work - @ClassRule - public static TestRule enableDeprecatedBCAlgorithmsRule = - EnableDeprecatedBouncyCastleAlgorithmsRule.getInstance(); - // END Android-Added: Allow access to deprecated BC algorithms. + public KeyFactoryTestEC() { + super("EC", ECPublicKeySpec.class, ECPrivateKeySpec.class); + } - public KeyFactoryTestEC() { - super("EC", ECPublicKeySpec.class, ECPrivateKeySpec.class); - } + @Override + public ServiceTester customizeTester(ServiceTester tester) { + // BC's EC keys always use explicit params, even though it's a bad idea, and we don't + // support those, so don't test BC keys + return tester.skipProvider("BC"); + } - @Override - public ServiceTester customizeTester(ServiceTester tester) { - // BC's EC keys always use explicit params, even though it's a bad idea, and we don't support - // those, so don't test BC keys - return tester.skipProvider("BC"); - } + @Override + protected void check(KeyPair keyPair) throws Exception { + new SignatureHelper("SHA256withECDSA").test(keyPair); + } - @Override - protected void check(KeyPair keyPair) throws Exception { - new SignatureHelper("SHA256withECDSA").test(keyPair); - } - - @Override - protected List<KeyPair> getKeys() throws NoSuchAlgorithmException, InvalidKeySpecException { - return Arrays.asList( - new KeyPair(DefaultKeys.getPublicKey("EC"), DefaultKeys.getPrivateKey("EC")), - new KeyPair(new TestPublicKey(DefaultKeys.getPublicKey("EC")), - new TestPrivateKey(DefaultKeys.getPrivateKey("EC"))), - new KeyPair(new TestECPublicKey((ECPublicKey) DefaultKeys.getPublicKey("EC")), - new TestECPrivateKey((ECPrivateKey) DefaultKeys.getPrivateKey("EC")))); - } + @Override + protected List<KeyPair> getKeys() throws NoSuchAlgorithmException, InvalidKeySpecException { + return Arrays.asList( + new KeyPair(DefaultKeys.getPublicKey("EC"), DefaultKeys.getPrivateKey("EC")), + new KeyPair(new TestPublicKey(DefaultKeys.getPublicKey("EC")), + new TestPrivateKey(DefaultKeys.getPrivateKey("EC"))), + new KeyPair(new TestECPublicKey((ECPublicKey) DefaultKeys.getPublicKey("EC")), + new TestECPrivateKey((ECPrivateKey) DefaultKeys.getPrivateKey("EC")))); + } }
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 1ad01a7..6b0567e 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
@@ -20,7 +20,6 @@ import java.security.KeyFactory; import java.security.KeyPair; -import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; @@ -44,9 +43,7 @@ * @hide This class is not part of the Android public SDK API */ @RunWith(JUnit4.class) -public class KeyFactoryTestRSA extends - AbstractKeyFactoryTest<RSAPublicKeySpec, RSAPrivateKeySpec> { - +public class KeyFactoryTestRSA extends AbstractKeyFactoryTest<RSAPublicKeySpec, RSAPrivateKeySpec> { // BEGIN Android-Added: Allow access to deprecated BC algorithms. // Allow access to deprecated BC algorithms in this test, so we can ensure they // continue to work
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 074dd38..15b1628 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
@@ -19,9 +19,6 @@ import java.security.KeyPair; import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPublicKeySpec; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import tests.util.ServiceTester;
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSACustom.java b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSACustom.java index 570929f..05dd7ad 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSACustom.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSACustom.java
@@ -18,16 +18,11 @@ import java.security.KeyPair; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.Arrays; import java.util.List; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import tests.util.ServiceTester;
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestXDH.java b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestXDH.java new file mode 100644 index 0000000..31aeda9 --- /dev/null +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestXDH.java
@@ -0,0 +1,65 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +/* + * Copyright (C) 2019 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.java.security; + +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; +import java.util.List; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import tests.util.ServiceTester; + +/** + * @hide This class is not part of the Android public SDK API + */ +@RunWith(JUnit4.class) +public class KeyFactoryTestXDH extends + AbstractKeyFactoryTest<X509EncodedKeySpec, PKCS8EncodedKeySpec> { + + public KeyFactoryTestXDH() { + super("XDH", X509EncodedKeySpec.class, PKCS8EncodedKeySpec.class); + } + + @Override + protected void check(KeyPair keyPair) throws Exception { + new KeyAgreementHelper("XDH").test(keyPair); + } + + @Override + protected ServiceTester customizeTester(ServiceTester tester) { + // TODO: fix this test when Conscrypt's XDH keys can inherit from XECPublicKey and XECPrivateKey + return tester.skipProvider("SunEC"); + } + + @Override + protected List<KeyPair> getKeys() throws NoSuchAlgorithmException, InvalidKeySpecException { + return Arrays.asList( + new KeyPair( + DefaultKeys.getPublicKey("XDH"), + DefaultKeys.getPrivateKey("XDH") + ), + new KeyPair( + new TestPublicKey(DefaultKeys.getPublicKey("XDH")), + new TestPrivateKey(DefaultKeys.getPrivateKey("XDH")) + ) + ); + } +}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTest.java index 7ac65b6..9d746dc 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTest.java
@@ -319,6 +319,19 @@ // so ignore it. continue; } + if ("XDH".equals(k.getAlgorithm()) && "SunEC".equalsIgnoreCase(p.getName()) + && "11".equals(System.getProperty("java.specification.version"))) { + // SunEC in OpenJDK 11 has a bug where the format specified in RFC 8410 + // Section 7. It uses a single OCTET STRING to represent the key instead + // of an OCTET STRING inside of an OCTET STRING as defined in the RFC: + // ("For the keys defined in this document, the private key is always an + // opaque byte sequence. The ASN.1 type CurvePrivateKey is defined in + // this document to hold the byte sequence. Thus, when encoding a + // OneAsymmetricKey object, the private key is wrapped in a + // CurvePrivateKey object and wrapped by the OCTET STRING of the + // "privateKey" field.") + continue; + } if ("PKCS#8".equals(k.getFormat())) { PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encoded);
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestRSA.java b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestRSA.java index 2e9d074..f2f2539 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestRSA.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestRSA.java
@@ -25,10 +25,7 @@ @RunWith(JUnit4.class) public class KeyPairGeneratorTestRSA extends AbstractKeyPairGeneratorTest { - @SuppressWarnings("unchecked") public KeyPairGeneratorTestRSA() { super("RSA", new CipherAsymmetricCryptHelper("RSA")); } - } -
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestXDH.java b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestXDH.java new file mode 100644 index 0000000..3249c66 --- /dev/null +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestXDH.java
@@ -0,0 +1,36 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +/* + * Copyright (C) 2009 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.java.security; + +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 KeyPairGeneratorTestXDH extends AbstractKeyPairGeneratorTest { + + public KeyPairGeneratorTestXDH() { + super("XDH", new KeyAgreementHelper("XDH")); + } + + @Override + protected int getKeySize() { + return 255; + } +}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/SignatureTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/SignatureTest.java index e3c3122..1599bfc 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/SignatureTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/SignatureTest.java
@@ -116,6 +116,10 @@ // https://bugs.openjdk.java.net/browse/JDK-8044554), but skip verifying it all // the same. .skipProvider("SunPKCS11-NSS") + // We don't have code to generate key pairs for these yet. + .skipAlgorithm("Ed448") + .skipAlgorithm("Ed25519") + .skipAlgorithm("EdDSA") .run(new ServiceTester.Test() { @Override public void test(Provider provider, String algorithm) throws Exception { @@ -2939,13 +2943,54 @@ * randomized, so this won't be the exact signature you'll get out of * another signing operation unless you use a fixed RNG. */ - public static final byte[] SHA1withDSA_Vector2Signature = new byte[] { - (byte) 0x30, (byte) 0x2d, (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0x88, (byte) 0xef, (byte) 0xac, - (byte) 0x2b, (byte) 0x8b, (byte) 0xe2, (byte) 0x61, (byte) 0xc6, (byte) 0x2b, (byte) 0xea, (byte) 0xd5, - (byte) 0x96, (byte) 0xbc, (byte) 0xb0, (byte) 0xa1, (byte) 0x30, (byte) 0x0c, (byte) 0x1f, (byte) 0xed, - (byte) 0x11, (byte) 0x02, (byte) 0x14, (byte) 0x15, (byte) 0xc4, (byte) 0xfc, (byte) 0x82, (byte) 0x6f, - (byte) 0x17, (byte) 0xdc, (byte) 0x87, (byte) 0x82, (byte) 0x75, (byte) 0x23, (byte) 0xd4, (byte) 0x58, - (byte) 0xdc, (byte) 0x73, (byte) 0x3d, (byte) 0xf3, (byte) 0x51, (byte) 0xc0, (byte) 0x57, + private static final byte[] SHA1withDSA_Vector2Signature = new byte[] { + (byte) 0x30, + (byte) 0x2d, + (byte) 0x02, + (byte) 0x15, + (byte) 0x00, + (byte) 0x88, + (byte) 0xef, + (byte) 0xac, + (byte) 0x2b, + (byte) 0x8b, + (byte) 0xe2, + (byte) 0x61, + (byte) 0xc6, + (byte) 0x2b, + (byte) 0xea, + (byte) 0xd5, + (byte) 0x96, + (byte) 0xbc, + (byte) 0xb0, + (byte) 0xa1, + (byte) 0x30, + (byte) 0x0c, + (byte) 0x1f, + (byte) 0xed, + (byte) 0x11, + (byte) 0x02, + (byte) 0x14, + (byte) 0x15, + (byte) 0xc4, + (byte) 0xfc, + (byte) 0x82, + (byte) 0x6f, + (byte) 0x17, + (byte) 0xdc, + (byte) 0x87, + (byte) 0x82, + (byte) 0x75, + (byte) 0x23, + (byte) 0xd4, + (byte) 0x58, + (byte) 0xdc, + (byte) 0x73, + (byte) 0x3d, + (byte) 0xf3, + (byte) 0x51, + (byte) 0xc0, + (byte) 0x57, }; /** @@ -2953,28 +2998,30 @@ * randomized, so this won't be the exact signature you'll get out of * another signing operation unless you use a fixed RNG. */ - public static final byte[] SHA224withDSA_Vector2Signature = new byte[] { - (byte) 0x30, (byte) 0x2D, (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0xAD, (byte) 0xE5, (byte) 0x6D, - (byte) 0xF5, (byte) 0x11, (byte) 0x8D, (byte) 0x2E, (byte) 0x62, (byte) 0x5D, (byte) 0x98, (byte) 0x8A, - (byte) 0xC4, (byte) 0x88, (byte) 0x7E, (byte) 0xE6, (byte) 0xA3, (byte) 0x44, (byte) 0x99, (byte) 0xEF, - (byte) 0x49, (byte) 0x02, (byte) 0x14, (byte) 0x15, (byte) 0x3E, (byte) 0x32, (byte) 0xD6, (byte) 0xF9, - (byte) 0x79, (byte) 0x2C, (byte) 0x60, (byte) 0x6E, (byte) 0xF9, (byte) 0xA9, (byte) 0x78, (byte) 0xE7, - (byte) 0x4B, (byte) 0x87, (byte) 0x08, (byte) 0x96, (byte) 0x60, (byte) 0xDE, (byte) 0xB5 - }; + private static final byte[] SHA224withDSA_Vector2Signature = + new byte[] {(byte) 0x30, (byte) 0x2D, (byte) 0x02, (byte) 0x15, (byte) 0x00, + (byte) 0xAD, (byte) 0xE5, (byte) 0x6D, (byte) 0xF5, (byte) 0x11, (byte) 0x8D, + (byte) 0x2E, (byte) 0x62, (byte) 0x5D, (byte) 0x98, (byte) 0x8A, (byte) 0xC4, + (byte) 0x88, (byte) 0x7E, (byte) 0xE6, (byte) 0xA3, (byte) 0x44, (byte) 0x99, + (byte) 0xEF, (byte) 0x49, (byte) 0x02, (byte) 0x14, (byte) 0x15, (byte) 0x3E, + (byte) 0x32, (byte) 0xD6, (byte) 0xF9, (byte) 0x79, (byte) 0x2C, (byte) 0x60, + (byte) 0x6E, (byte) 0xF9, (byte) 0xA9, (byte) 0x78, (byte) 0xE7, (byte) 0x4B, + (byte) 0x87, (byte) 0x08, (byte) 0x96, (byte) 0x60, (byte) 0xDE, (byte) 0xB5}; /** * A possible signature using SHA256withDSA of Vector2Data. Note that DSS is * randomized, so this won't be the exact signature you'll get out of * another signing operation unless you use a fixed RNG. */ - public static final byte[] SHA256withDSA_Vector2Signature = new byte[] { - (byte) 0x30, (byte) 0x2D, (byte) 0x02, (byte) 0x14, (byte) 0x0A, (byte) 0xB1, (byte) 0x74, (byte) 0x45, - (byte) 0xE1, (byte) 0x63, (byte) 0x43, (byte) 0x68, (byte) 0x65, (byte) 0xBC, (byte) 0xCA, (byte) 0x45, - (byte) 0x27, (byte) 0x11, (byte) 0x4D, (byte) 0x52, (byte) 0xFB, (byte) 0x22, (byte) 0x93, (byte) 0xDD, - (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0x98, (byte) 0x32, (byte) 0x1A, (byte) 0x16, (byte) 0x77, - (byte) 0x49, (byte) 0xA7, (byte) 0x78, (byte) 0xFD, (byte) 0xE0, (byte) 0xF7, (byte) 0x71, (byte) 0xD4, - (byte) 0x80, (byte) 0x50, (byte) 0xA7, (byte) 0xDD, (byte) 0x94, (byte) 0xD1, (byte) 0x6C - }; + private static final byte[] SHA256withDSA_Vector2Signature = + new byte[] {(byte) 0x30, (byte) 0x2D, (byte) 0x02, (byte) 0x14, (byte) 0x0A, + (byte) 0xB1, (byte) 0x74, (byte) 0x45, (byte) 0xE1, (byte) 0x63, (byte) 0x43, + (byte) 0x68, (byte) 0x65, (byte) 0xBC, (byte) 0xCA, (byte) 0x45, (byte) 0x27, + (byte) 0x11, (byte) 0x4D, (byte) 0x52, (byte) 0xFB, (byte) 0x22, (byte) 0x93, + (byte) 0xDD, (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0x98, (byte) 0x32, + (byte) 0x1A, (byte) 0x16, (byte) 0x77, (byte) 0x49, (byte) 0xA7, (byte) 0x78, + (byte) 0xFD, (byte) 0xE0, (byte) 0xF7, (byte) 0x71, (byte) 0xD4, (byte) 0x80, + (byte) 0x50, (byte) 0xA7, (byte) 0xDD, (byte) 0x94, (byte) 0xD1, (byte) 0x6C}; @Test public void testSign_SHA1withDSA_Key_Success() throws Exception {
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/cert/CertificateFactoryTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/cert/CertificateFactoryTest.java index 1cb4537..9146ca9 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/cert/CertificateFactoryTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/cert/CertificateFactoryTest.java
@@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -53,6 +54,7 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Date; import java.util.GregorianCalendar; import java.util.Iterator; @@ -209,6 +211,48 @@ + "zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqA" + "ibAxWEEHXw=="; + // Generated with openssl crl2pkcs7 -nocrl -certfile cert.pem + private static final String VALID_CERTIFICATE_PKCS7_PEM = "-----BEGIN PKCS7-----\n" + + "MIIDUgYJKoZIhvcNAQcCoIIDQzCCAz8CAQExADALBgkqhkiG9w0BBwGgggMlMIID\n" + + "ITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBMMQsw\n" + + "CQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkgTHRk\n" + + "LjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0xMTEy\n" + + "MTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw\n" + + "FAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcwFQYD\n" + + "VQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" + + "6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jNgtXj\n" + + "9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L05vu\n" + + "uWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAMBgNV\n" + + "HRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3RlLmNv\n" + + "bS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUFBwMC\n" + + "BglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRwOi8v\n" + + "b2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0ZS5j\n" + + "b20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUFAAOB\n" + + "gQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5u2ON\n" + + "gJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6z5nR\n" + + "UP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHX6EAMQA=\n" + + "-----END PKCS7-----\n"; + + private static final String VALID_CERTIFICATE_PKCS7_DER_BASE64 = + "MIIDUgYJKoZIhvcNAQcCoIIDQzCCAz8CAQExADALBgkqhkiG9w0BBwGgggMlMIID" + + "ITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBMMQsw" + + "CQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkgTHRk" + + "LjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0xMTEy" + + "MTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw" + + "FAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcwFQYD" + + "VQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA" + + "6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jNgtXj" + + "9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L05vu" + + "uWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAMBgNV" + + "HRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3RlLmNv" + + "bS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUFBwMC" + + "BglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRwOi8v" + + "b2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0ZS5j" + + "b20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUFAAOB" + + "gQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5u2ON" + + "gJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6z5nR" + + "UP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHX6EAMQA="; + private static final String VALID_CRL_PEM = "-----BEGIN X509 CRL-----\n" + "MIIBUTCBuwIBATANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJHQjEkMCIGA1UE\n" @@ -243,6 +287,30 @@ + "CCHWAw8WN9XSJ4nGxdRiacG/5vEIx00ICUGCeGcnqWsSnFtagDtvry2c4MMexbSP" + "nDN0LLg="; + // Generated with openssl crl2pkcs7 -in crl.pem + private static final String VALID_CRL_PKCS7_PEM = "-----BEGIN PKCS7-----\n" + + "MIIBggYJKoZIhvcNAQcCoIIBczCCAW8CAQExADALBgkqhkiG9w0BBwGgAKGCAVUw\n" + + "ggFRMIG7AgEBMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQK\n" + + "ExtDZXJ0aWZpY2F0ZSBUcmFuc3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAw\n" + + "DgYDVQQHEwdFcncgV2VuFw0xOTA4MDcxMDI3MTBaFw0xOTA5MDYxMDI3MTBaMCIw\n" + + "IAIBBxcNMTkwODA3MTAyNjU0WjAMMAoGA1UdFQQDCgEBoA4wDDAKBgNVHRQEAwIB\n" + + "AjANBgkqhkiG9w0BAQsFAAOBgQDMX8MuIi9kNfgWlKM0KfApFuWeEktnU00EAfFx\n" + + "Ft8Vjemyhu9sYY6PHMJBb/TeCSeAblWtJ91U4syZAOsDGkqp5ioUOPQcB6da6BsI\n" + + "IdYDDxY31dInicbF1GJpwb/m8QjHTQgJQYJ4ZyepaxKcW1qAO2+vLZzgwx7FtI+c\n" + + "M3QsuDEA\n" + + "-----END PKCS7-----\n"; + + private static final String VALID_CRL_PKCS7_DER_BASE64 = + "MIIBggYJKoZIhvcNAQcCoIIBczCCAW8CAQExADALBgkqhkiG9w0BBwGgAKGCAVUw" + + "ggFRMIG7AgEBMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQK" + + "ExtDZXJ0aWZpY2F0ZSBUcmFuc3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAw" + + "DgYDVQQHEwdFcncgV2VuFw0xOTA4MDcxMDI3MTBaFw0xOTA5MDYxMDI3MTBaMCIw" + + "IAIBBxcNMTkwODA3MTAyNjU0WjAMMAoGA1UdFQQDCgEBoA4wDDAKBgNVHRQEAwIB" + + "AjANBgkqhkiG9w0BAQsFAAOBgQDMX8MuIi9kNfgWlKM0KfApFuWeEktnU00EAfFx" + + "Ft8Vjemyhu9sYY6PHMJBb/TeCSeAblWtJ91U4syZAOsDGkqp5ioUOPQcB6da6BsI" + + "IdYDDxY31dInicbF1GJpwb/m8QjHTQgJQYJ4ZyepaxKcW1qAO2+vLZzgwx7FtI+c" + + "M3QsuDEA"; + private static final String INVALID_CRL_PEM = "-----BEGIN X509 CRL-----\n" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" @@ -281,6 +349,15 @@ + "AAAAAAAA\n" + "-----END X509 CRL-----\n"; + // PKCS#7 file containing a small certificate. This input is small enough to use a two-byte + // length prefix. + private static final String SMALL_PKCS7_DER_BASE64 = + "MIHrBgkqhkiG9w0BBwKggd0wgdoCAQExADALBgkqhkiG9w0BBwGggcEwgb4wcgIB" + + "ADAFBgMrZXAwDDEKMAgGA1UEAxMBQTAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMy" + + "MzIxNTdaMAwxCjAIBgNVBAMTAUEwKjAFBgMrZXADIQDXWpgBgrEKt9VL/tPJZAc6" + + "DuFy89qmIyWvAhpo9wdRGjAFBgMrZXADQQBuCzqji8VP9xU8mHEMjXGChX7YP5J6" + + "64UyVKHKH9Z1u4wEbB8dJ3ScaWSLr+VHVKUhsrvcdCelnXRrrSD7xWALoQAxAA=="; + @Test public void test_generateCertificate() throws Exception { ServiceTester.test("CertificateFactory") @@ -302,22 +379,59 @@ } private void test_generateCertificate(CertificateFactory cf) throws Exception { + Certificate cert; { byte[] valid = VALID_CERTIFICATE_PEM.getBytes(Charset.defaultCharset()); Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid)); assertNotNull(c); + cert = c; } { byte[] valid = VALID_CERTIFICATE_PEM_CRLF.getBytes(Charset.defaultCharset()); Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid)); assertNotNull(c); + assertEquals(c, cert); } { byte[] valid = TestUtils.decodeBase64(VALID_CERTIFICATE_DER_BASE64); Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid)); assertNotNull(c); + assertEquals(c, cert); + } + + // The RI only supports PKCS#7 blobs with generateCertificates, not + // generateCertificate. See https://github.com/google/conscrypt/issues/987 + if (!StandardNames.IS_RI) { + byte[] valid = TestUtils.decodeBase64(VALID_CERTIFICATE_PKCS7_DER_BASE64); + Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid)); + assertNotNull(c); + assertEquals(c, cert); + } + + { + byte[] valid = VALID_CERTIFICATE_PKCS7_PEM.getBytes(Charset.defaultCharset()); + Collection<? extends Certificate> cs = + cf.generateCertificates(new ByteArrayInputStream(valid)); + assertEquals(1, cs.size()); + assertEquals(cs.iterator().next(), cert); + } + + { + byte[] valid = TestUtils.decodeBase64(VALID_CERTIFICATE_PKCS7_DER_BASE64); + Collection<? extends Certificate> cs = + cf.generateCertificates(new ByteArrayInputStream(valid)); + assertEquals(1, cs.size()); + assertEquals(cs.iterator().next(), cert); + } + + { + byte[] valid = TestUtils.decodeBase64(SMALL_PKCS7_DER_BASE64); + Collection<? extends Certificate> cs = + cf.generateCertificates(new ByteArrayInputStream(valid)); + assertEquals(1, cs.size()); + assertNotNull(cs.iterator().next()); } try { @@ -650,7 +764,7 @@ assertEquals(providerName, Arrays.toString(pathFromList.getEncoded(encoding)), Arrays.toString(encoded)); } - assertFalse(providerName, Arrays.toString(encoded).equals(Arrays.toString(encodedCopy))); + assertNotEquals(providerName, Arrays.toString(encoded), Arrays.toString(encodedCopy)); encodedCopy[0] ^= (byte) 0xFF; assertEquals(providerName, Arrays.toString(encoded), Arrays.toString(encodedCopy)); @@ -688,7 +802,7 @@ Object output = ois.readObject(); assertTrue(providerName, output instanceof CertPath); - assertEquals(providerName, actualPath, (CertPath) output); + assertEquals(providerName, actualPath, output); } /** @@ -789,12 +903,33 @@ assertNotNull(c); valid = VALID_CRL_PEM_CRLF.getBytes(Charset.defaultCharset()); - c = cf.generateCRL(new ByteArrayInputStream(valid)); - assertNotNull(c); + CRL c2 = cf.generateCRL(new ByteArrayInputStream(valid)); + assertNotNull(c2); + assertEquals(c, c2); valid = TestUtils.decodeBase64(VALID_CRL_DER_BASE64); - c = cf.generateCRL(new ByteArrayInputStream(valid)); + c2 = cf.generateCRL(new ByteArrayInputStream(valid)); assertNotNull(c); + assertEquals(c, c2); + + // The RI only supports PKCS#7 with generateCRLs, not generateCRL. + // See https://github.com/google/conscrypt/issues/987 + if (!StandardNames.IS_RI) { + valid = TestUtils.decodeBase64(VALID_CRL_PKCS7_DER_BASE64); + c2 = cf.generateCRL(new ByteArrayInputStream(valid)); + assertNotNull(c); + assertEquals(c, c2); + } + + valid = TestUtils.decodeBase64(VALID_CRL_PKCS7_DER_BASE64); + Collection<? extends CRL> crls = cf.generateCRLs(new ByteArrayInputStream(valid)); + assertEquals(1, crls.size()); + assertEquals(c, crls.iterator().next()); + + valid = VALID_CRL_PKCS7_PEM.getBytes(Charset.defaultCharset()); + crls = cf.generateCRLs(new ByteArrayInputStream(valid)); + assertEquals(1, crls.size()); + assertEquals(c, crls.iterator().next()); try { byte[] invalid = INVALID_CRL_PEM.getBytes(Charset.defaultCharset());
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/cert/X509CRLTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/cert/X509CRLTest.java index 8ce395b..ac3ee2b 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/cert/X509CRLTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/cert/X509CRLTest.java
@@ -17,12 +17,14 @@ package com.android.org.conscrypt.java.security.cert; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.android.org.conscrypt.TestUtils; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import java.security.Provider; @@ -106,6 +108,23 @@ + "nDN0LLg=\n" + "-----END X509 CRL-----\n"; + private static final String CRL_TBS_BASE64 = + "MIG7AgEBMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQKExtD" + + "ZXJ0aWZpY2F0ZSBUcmFuc3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAwDgYD" + + "VQQHEwdFcncgV2VuFw0xOTA4MDcxMDI3MTBaFw0xOTA5MDYxMDI3MTBaMCIwIAIB" + + "BxcNMTkwODA3MTAyNjU0WjAMMAoGA1UdFQQDCgEBoA4wDDAKBgNVHRQEAwIBAg=="; + + private static final String UNKNOWN_SIGNATURE_OID = "-----BEGIN X509 CRL-----\n" + + "MIIBVzCBvgIBATAQBgwqhkiG9xIEAYS3CQIFADBVMQswCQYDVQQGEwJHQjEkMCIG\n" + + "A1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVXYWxl\n" + + "czEQMA4GA1UEBxMHRXJ3IFdlbhcNMTkwODA3MTAyNzEwWhcNMTkwOTA2MTAyNzEw\n" + + "WjAiMCACAQcXDTE5MDgwNzEwMjY1NFowDDAKBgNVHRUEAwoBAaAOMAwwCgYDVR0U\n" + + "BAMCAQIwEAYMKoZIhvcSBAGEtwkCBQADgYEAzF/DLiIvZDX4FpSjNCnwKRblnhJL\n" + + "Z1NNBAHxcRbfFY3psobvbGGOjxzCQW/03gkngG5VrSfdVOLMmQDrAxpKqeYqFDj0\n" + + "HAenWugbCCHWAw8WN9XSJ4nGxdRiacG/5vEIx00ICUGCeGcnqWsSnFtagDtvry2c\n" + + "4MMexbSPnDN0LLg=\n" + + "-----END X509 CRL-----\n"; + @Test public void testCrl() throws Exception { ServiceTester.test("CertificateFactory") @@ -130,6 +149,9 @@ } catch (SignatureException expected) { } + byte[] expectedTBSCertList = TestUtils.decodeBase64(CRL_TBS_BASE64); + assertArrayEquals(expectedTBSCertList, crl.getTBSCertList()); + assertTrue(crl.isRevoked(revoked)); X509CRLEntry entry = crl.getRevokedCertificate(revoked); assertEquals(CRLReason.KEY_COMPROMISE, entry.getRevocationReason()); @@ -144,4 +166,19 @@ } }); } + + @Test + public void testUnknownSigAlgOID() throws Exception { + ServiceTester.test("CertificateFactory") + .withAlgorithm("X509") + .run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + CertificateFactory cf = CertificateFactory.getInstance("X509", p); + X509CRL crl = (X509CRL) cf.generateCRL(new ByteArrayInputStream( + UNKNOWN_SIGNATURE_OID.getBytes(StandardCharsets.US_ASCII))); + assertEquals("1.2.840.113554.4.1.72585.2", crl.getSigAlgOID()); + } + }); + } }
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/cert/X509CertificateTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/cert/X509CertificateTest.java index 56a8a68..473b176 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/cert/X509CertificateTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/java/security/cert/X509CertificateTest.java
@@ -17,24 +17,38 @@ package com.android.org.conscrypt.java.security.cert; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; +import com.android.org.conscrypt.TestUtils; import java.io.ByteArrayInputStream; +import java.math.BigInteger; import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.Provider; -import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; +import javax.security.auth.x500.X500Principal; import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import tests.util.Pair; import tests.util.ServiceTester; /** @@ -139,6 +153,247 @@ + "Qhy0YgIgYWr0qSCLqxUQv3oQHMUpSmfHtP0Pwvb3DbbH6lY7TkI=\n" + "-----END CERTIFICATE-----\n"; + /** + * This cert is signed with OID 1.2.840.113554.4.1.72585.2 instead of a + * standard one. + */ + private static final String UNKNOWN_SIGNATURE_OID = "-----BEGIN CERTIFICATE-----\n" + + "MIIB2TCCAXugAwIBAgIJANlMBNpJfb/rMA4GDCqGSIb3EgQBhLcJAjBFMQswCQYD\n" + + "VQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQg\n" + + "V2lkZ2l0cyBQdHkgTHRkMB4XDTE0MDQyMzIzMjE1N1oXDTE0MDUyMzIzMjE1N1ow\n" + + "RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu\n" + + "dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA\n" + + "BOYraeK/ZZ+Xvi8eDZSKTNWXa7epHg1G+92pqR6d3LpaAefWl6gKGPnDxKMeVuJ8\n" + + "g0jbFhoc9R1+8ZQtS89yIsGjUDBOMB0GA1UdDgQWBBSrhNKsq5Xwgk4WeAdVV1/k\n" + + "Jo2C0TAfBgNVHSMEGDAWgBSrhNKsq5Xwgk4WeAdVV1/kJo2C0TAMBgNVHRMEBTAD\n" + + "AQH/MA4GDCqGSIb3EgQBhLcJAgNIADBFAiEA8qA1XlE6NsOCeZvuJ1CFjnAGdJVX\n" + + "0il0APS+FYddxAcCIHweeRRqIYPwenRoeV8UmZpotPHLnhVe5h8yUmFedckU\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is an X.509v1 certificatea, so most fields are missing. It exists to test accessors + * correctly handle the lack of fields. It was constructed by hand, so the signature itself is + * invalid. + */ + private static final String X509V1_CERT = "-----BEGIN CERTIFICATE-----\n" + + "MIIBGjCBwgIJANlMBNpJfb/rMAkGByqGSM49BAEwFjEUMBIGA1UEAwwLVGVzdCBJ\n" + + "c3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjAXMRUwEwYDVQQD\n" + + "DAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2niv2Wf\n" + + "l74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYaHPUd\n" + + "fvGULUvPciLBMAkGByqGSM49BAEDSAAwRQIhAPKgNV5ROjbDgnmb7idQhY5wBnSV\n" + + "V9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTxy54VXuYfMlJhXnXJFA==\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is a certificate with many extensions filled it. It exists to test accessors correctly + * report fields. It was constructed by hand, so the signature itself is invalid. Add more + * fields as necessary with https://github.com/google/der-ascii. + */ + private static final String MANY_EXTENSIONS = "-----BEGIN CERTIFICATE-----\n" + + "MIIEADCCAuigAwIBAgIJALW2IrlaBKUhMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV\n" + + "BAMMC1Rlc3QgSXNzdWVyMB4XDTE2MDcwOTA0MzgwOVoXDTE2MDgwODA0MzgwOVow\n" + + "FzEVMBMGA1UEAwwMVGVzdCBTdWJqZWN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" + + "MIIBCgKCAQEAugvahBkSAUF1fC49vb1bvlPrcl80kop1iLpiuYoz4Qptwy57+EWs\n" + + "sZBcHprZ5BkWf6PeGZ7F5AX1PyJbGHZLqvMCvViP6pd4MFox/igESISEHEixoiXC\n" + + "zepBrhtp5UQSjHD4D4hKtgdMgVxX+LRtwgW3mnu/vBu7rzpr/DS8io99p3lqZ1Ak\n" + + "y+aNlcMj6MYy8U+YFEevb/V0lRY9oqwmW7BHnXikm/vi6sjIS350U8zb/mRzYeIs\n" + + "2R65LUduTL50+UMgat9ocewI2dv8aO9Dph+8NdGtg8LFYyTTHcUxJoMr1PTOgnmE\n" + + "T19WJH4PrFwk7ZE1QJQQ1L4iKmPeQistuQIDAQABgQIEoIICA1CjggFGMIIBQjAP\n" + + "BgNVHRMECDAGAQH/AgEKMCEGA1UdJQQaMBgGCCsGAQUFBwMBBgwqhkiG9xIEAYS3\n" + + "CQIwfwYDVR0RBHgwdoETc3ViamVjdEBleGFtcGxlLmNvbYITc3ViamVjdC5leGFt\n" + + "cGxlLmNvbaQZMBcxFTATBgNVBAMMDFRlc3QgU3ViamVjdIYbaHR0cHM6Ly9leGFt\n" + + "cGxlLmNvbS9zdWJqZWN0hwR/AAABiAwqhkiG9xIEAYS3CQIwewYDVR0SBHQwcoES\n" + + "aXNzdWVyQGV4YW1wbGUuY29tghJpc3N1ZXIuZXhhbXBsZS5jb22kGDAWMRQwEgYD\n" + + "VQQDDAtUZXN0IElzc3VlcoYaaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXKHBH8A\n" + + "AAGIDCqGSIb3EgQBhLcJAjAOBgNVHQ8BAf8EBAMCBaAwDQYJKoZIhvcNAQELBQAD\n" + + "ggEBAD7Jg68SArYWlcoHfZAB90Pmyrt5H6D8LRi+W2Ri1fBNxREELnezWJ2scjl4\n" + + "UMcsKYp4Pi950gVN+62IgrImcCNvtb5I1Cfy/MNNur9ffas6X334D0hYVIQTePyF\n" + + "k3umI+2mJQrtZZyMPIKSY/sYGQHhGGX6wGK+GO/og0PQk/Vu6D+GU2XRnDV0YZg1\n" + + "lsAsHd21XryK6fDmNkEMwbIWrts4xc7scRrGHWy+iMf6/7p/Ak/SIicM4XSwmlQ8\n" + + "pPxAZPr+E2LoVd9pMpWUwpW2UbtO5wsGTrY5sO45tFNN/y+jtUheB1C2ijObG/tX\n" + + "ELaiyCdM+S/waeuv0MXtI4xnn1A=\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is a certificate whose basicConstraints extension marks it as a CA, with no pathlen + * constraint. + */ + private static final String BASIC_CONSTRAINTS_NO_PATHLEN = "-----BEGIN CERTIFICATE-----\n" + + "MIIBMzCB2qADAgECAgkA2UwE2kl9v+swCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwL\n" + + "VGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjAXMRUw\n" + + "EwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATm\n" + + "K2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI\n" + + "2xYaHPUdfvGULUvPciLBoxAwDjAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0gA\n" + + "MEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13EBwIgfB55FGohg/B6\n" + + "dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is a certificate whose basicConstraints extension marks it as a CA with a pathlen + * constraint of zero. + */ + private static final String BASIC_CONSTRAINTS_PATHLEN_0 = "-----BEGIN CERTIFICATE-----\n" + + "MIIBNjCB3aADAgECAgkA2UwE2kl9v+swCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwL\n" + + "VGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjAXMRUw\n" + + "EwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATm\n" + + "K2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI\n" + + "2xYaHPUdfvGULUvPciLBoxMwETAPBgNVHRMECDAGAQH/AgEAMAoGCCqGSM49BAMC\n" + + "A0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13EBwIgfB55FGoh\n" + + "g/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is a certificate whose basicConstraints extension marks it as a leaf certificate. + */ + private static final String BASIC_CONSTRAINTS_LEAF = "-----BEGIN CERTIFICATE-----\n" + + "MIIBMDCB16ADAgECAgkA2UwE2kl9v+swCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwL\n" + + "VGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjAXMRUw\n" + + "EwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATm\n" + + "K2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI\n" + + "2xYaHPUdfvGULUvPciLBow0wCzAJBgNVHRMEAjAAMAoGCCqGSM49BAMCA0gAMEUC\n" + + "IQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13EBwIgfB55FGohg/B6dGh5\n" + + "XxSZmmi08cueFV7mHzJSYV51yRQ=\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is a certificate with a pathlen constraint of 10, but there is an unrelated invalid + * subjectAltNames extension. + */ + private static final String BASIC_CONSTRAINTS_PATHLEN_10_BAD_SAN = + "-----BEGIN CERTIFICATE-----\n" + + "MIIBRjCB7aADAgECAgkA2UwE2kl9v+swCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwL\n" + + "VGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjAXMRUw\n" + + "EwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATm\n" + + "K2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI\n" + + "2xYaHPUdfvGULUvPciLBoyMwITAPBgNVHRMECDAGAQH/AgEKMA4GA1UdEQQHSU5W\n" + + "QUxJRDAKBggqhkjOPQQDAgNIADBFAiEA8qA1XlE6NsOCeZvuJ1CFjnAGdJVX0il0\n" + + "APS+FYddxAcCIHweeRRqIYPwenRoeV8UmZpotPHLnhVe5h8yUmFedckU\n" + + "-----END CERTIFICATE-----\n"; + + /** + * This is a certificate whose keyUsage extension has more than nine bits. The getKeyUsage() + * method internally rounds up to nine bits, so this tests what happens when it does not need to + * round. + */ + private static final String LARGE_KEY_USAGE = "-----BEGIN CERTIFICATE-----\n" + + "MIIBNjCB3aADAgECAgkA2UwE2kl9v+swCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwL\n" + + "VGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjAXMRUw\n" + + "EwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATm\n" + + "K2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI\n" + + "2xYaHPUdfvGULUvPciLBoxMwETAPBgNVHQ8BAf8EBQMDBaAAMAoGCCqGSM49BAMC\n" + + "A0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13EBwIgfB55FGoh\n" + + "g/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=\n" + + "-----END CERTIFICATE-----\n"; + + /* + * OpenSSLX509Certificate needs to compensate for OpenSSL's AlgorithmIdentifier representation + * by re-encoding the parameter field. Test this behaves correctly against a variety of + * different parameter types. + */ + private static final String SIGALG_NO_PARAMETER = "-----BEGIN CERTIFICATE-----\n" + + "MIIBKTCBzKADAgECAgkA2UwE2kl9v+swDgYMKoZIhvcSBAGEtwkCMBYxFDASBgNV\n" + + "BAMMC1Rlc3QgSXNzdWVyMB4XDTE0MDQyMzIzMjE1N1oXDTE0MDUyMzIzMjE1N1ow\n" + + "FzEVMBMGA1UEAwwMVGVzdCBTdWJqZWN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\n" + + "QgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPEox5W\n" + + "4nyDSNsWGhz1HX7xlC1Lz3IiwTAOBgwqhkiG9xIEAYS3CQIDSAAwRQIhAPKgNV5R\n" + + "OjbDgnmb7idQhY5wBnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTx\n" + + "y54VXuYfMlJhXnXJFA==\n" + + "-----END CERTIFICATE-----\n"; + private static final String SIGALG_NULL_PARAMETER = "-----BEGIN CERTIFICATE-----\n" + + "MIIBLTCBzqADAgECAgkA2UwE2kl9v+swEAYMKoZIhvcSBAGEtwkCBQAwFjEUMBIG\n" + + "A1UEAwwLVGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3\n" + + "WjAXMRUwEwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMB\n" + + "BwNCAATmK2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8Sj\n" + + "HlbifINI2xYaHPUdfvGULUvPciLBMBAGDCqGSIb3EgQBhLcJAgUAA0gAMEUCIQDy\n" + + "oDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13EBwIgfB55FGohg/B6dGh5XxSZ\n" + + "mmi08cueFV7mHzJSYV51yRQ=\n" + + "-----END CERTIFICATE-----\n"; + private static final String SIGALG_STRING_PARAMETER = "-----BEGIN CERTIFICATE-----\n" + + "MIIBNzCB06ADAgECAgkA2UwE2kl9v+swFQYMKoZIhvcSBAGEtwkCDAVwYXJhbTAW\n" + + "MRQwEgYDVQQDDAtUZXN0IElzc3VlcjAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMy\n" + + "MzIxNTdaMBcxFTATBgNVBAMMDFRlc3QgU3ViamVjdDBZMBMGByqGSM49AgEGCCqG\n" + + "SM49AwEHA0IABOYraeK/ZZ+Xvi8eDZSKTNWXa7epHg1G+92pqR6d3LpaAefWl6gK\n" + + "GPnDxKMeVuJ8g0jbFhoc9R1+8ZQtS89yIsEwFQYMKoZIhvcSBAGEtwkCDAVwYXJh\n" + + "bQNIADBFAiEA8qA1XlE6NsOCeZvuJ1CFjnAGdJVX0il0APS+FYddxAcCIHweeRRq\n" + + "IYPwenRoeV8UmZpotPHLnhVe5h8yUmFedckU\n" + + "-----END CERTIFICATE-----\n"; + private static final String SIGALG_BOOLEAN_PARAMETER = "-----BEGIN CERTIFICATE-----\n" + + "MIIBLzCBz6ADAgECAgkA2UwE2kl9v+swEQYMKoZIhvcSBAGEtwkCAQH/MBYxFDAS\n" + + "BgNVBAMMC1Rlc3QgSXNzdWVyMB4XDTE0MDQyMzIzMjE1N1oXDTE0MDUyMzIzMjE1\n" + + "N1owFzEVMBMGA1UEAwwMVGVzdCBTdWJqZWN0MFkwEwYHKoZIzj0CAQYIKoZIzj0D\n" + + "AQcDQgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPE\n" + + "ox5W4nyDSNsWGhz1HX7xlC1Lz3IiwTARBgwqhkiG9xIEAYS3CQIBAf8DSAAwRQIh\n" + + "APKgNV5ROjbDgnmb7idQhY5wBnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlf\n" + + "FJmaaLTxy54VXuYfMlJhXnXJFA==\n" + + "-----END CERTIFICATE-----\n"; + private static final String SIGALG_SEQUENCE_PARAMETER = "-----BEGIN CERTIFICATE-----\n" + + "MIIBLTCBzqADAgECAgkA2UwE2kl9v+swEAYMKoZIhvcSBAGEtwkCMAAwFjEUMBIG\n" + + "A1UEAwwLVGVzdCBJc3N1ZXIwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3\n" + + "WjAXMRUwEwYDVQQDDAxUZXN0IFN1YmplY3QwWTATBgcqhkjOPQIBBggqhkjOPQMB\n" + + "BwNCAATmK2niv2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8Sj\n" + + "HlbifINI2xYaHPUdfvGULUvPciLBMBAGDCqGSIb3EgQBhLcJAjAAA0gAMEUCIQDy\n" + + "oDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13EBwIgfB55FGohg/B6dGh5XxSZ\n" + + "mmi08cueFV7mHzJSYV51yRQ=\n" + + "-----END CERTIFICATE-----\n"; + + private static Date dateFromUTC(int year, int month, int day, int hour, int minute, int second) + throws Exception { + Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + c.set(year, month, day, hour, minute, second); + c.set(Calendar.MILLISECOND, 0); + return c.getTime(); + } + + private static X509Certificate certificateFromPEM(Provider p, String pem) + throws CertificateException { + CertificateFactory cf = CertificateFactory.getInstance("X509", p); + return (X509Certificate) cf.generateCertificate( + new ByteArrayInputStream(pem.getBytes(Charset.forName("US-ASCII")))); + } + + private static List<Pair<Integer, String>> normalizeGeneralNames(Collection<List<?>> names) { + // Extract a more convenient type than Java's Collection<List<?>>. + List<Pair<Integer, String>> result = new ArrayList<Pair<Integer, String>>(); + for (List<?> tuple : names) { + assertEquals(2, tuple.size()); + int type = ((Integer) tuple.get(0)).intValue(); + // TODO(davidben): Most name types are expected to have a String value, but some use + // byte[]. Update this logic when testing those name types. See + // X509Certificate.getSubjectAlternativeNames(). + String value = (String) tuple.get(1); + result.add(Pair.of(type, value)); + } + // Although there is a natural order (the order in the certificate), Java's API returns a + // Collection, so there is no guarantee of the provider using a particular order. Normalize + // the order before comparing. + Collections.sort(result, new Comparator<Pair<Integer, String>>() { + @Override + public int compare(Pair<Integer, String> a, Pair<Integer, String> b) { + int cmp = a.getFirst().compareTo(b.getFirst()); + if (cmp != 0) { + return cmp; + } + return a.getSecond().compareTo(b.getSecond()); + } + }); + return result; + } + + private static void assertGeneralNamesEqual( + Collection<List<?>> expected, Collection<List<?>> actual) throws Exception { + assertEquals(normalizeGeneralNames(expected), normalizeGeneralNames(actual)); + } + + // Error Prone flags Date.equals(), but Instant and LocalDateTime are not available in Java 7. + // We could compare Date.getTime(), but this trips another warning in Error Prone. We do not use + // Date subclasses, so stick with Date.equals for now. + // + // https://errorprone.info/bugpattern/UndefinedEquals + @SuppressWarnings("UndefinedEquals") + private static void assertDatesEqual(Date expected, Date actual) throws Exception { + assertEquals(expected, actual); + } + // See issue #539. @Test public void testMismatchedAlgorithm() throws Exception { @@ -147,10 +402,8 @@ .run(new ServiceTester.Test() { @Override public void test(Provider p, String algorithm) throws Exception { - CertificateFactory cf = CertificateFactory.getInstance("X509", p); try { - Certificate c = cf.generateCertificate(new ByteArrayInputStream( - MISMATCHED_ALGORITHM_CERT.getBytes(Charset.forName("US-ASCII")))); + X509Certificate c = certificateFromPEM(p, MISMATCHED_ALGORITHM_CERT); c.verify(c.getPublicKey()); fail(); } catch (CertificateException expected) { @@ -173,9 +426,7 @@ @Override public void test(Provider p, String algorithm) throws Exception { try { - CertificateFactory cf = CertificateFactory.getInstance("X509", p); - Certificate c = cf.generateCertificate(new ByteArrayInputStream( - EC_EXPLICIT_KEY_CERT.getBytes(Charset.forName("US-ASCII")))); + X509Certificate c = certificateFromPEM(p, EC_EXPLICIT_KEY_CERT); c.verify(c.getPublicKey()); fail(); } catch (InvalidKeyException expected) { @@ -194,12 +445,224 @@ .run(new ServiceTester.Test() { @Override public void test(Provider p, String algorithm) throws Exception { - CertificateFactory cf = CertificateFactory.getInstance("X509", p); - Certificate c = cf.generateCertificate(new ByteArrayInputStream( - VALID_CERT.getBytes(Charset.forName("US-ASCII")))); - assertEquals("SHA256WITHRSA", - ((X509Certificate) c).getSigAlgName().toUpperCase()); + X509Certificate c = certificateFromPEM(p, VALID_CERT); + assertEquals("SHA256WITHRSA", c.getSigAlgName().toUpperCase()); } }); } + + @Test + public void testUnknownSigAlgOID() throws Exception { + ServiceTester.test("CertificateFactory") + .withAlgorithm("X509") + .run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + X509Certificate c = certificateFromPEM(p, UNKNOWN_SIGNATURE_OID); + assertEquals("1.2.840.113554.4.1.72585.2", c.getSigAlgOID()); + } + }); + } + + @Test + public void testV1Cert() throws Exception { + ServiceTester tester = ServiceTester.test("CertificateFactory").withAlgorithm("X509"); + tester.run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + X509Certificate c = certificateFromPEM(p, X509V1_CERT); + + // Check basic certificate properties. + assertEquals(1, c.getVersion()); + assertEquals(new BigInteger("d94c04da497dbfeb", 16), c.getSerialNumber()); + assertDatesEqual( + dateFromUTC(2014, Calendar.APRIL, 23, 23, 21, 57), c.getNotBefore()); + assertDatesEqual(dateFromUTC(2014, Calendar.MAY, 23, 23, 21, 57), c.getNotAfter()); + assertEquals(new X500Principal("CN=Test Issuer"), c.getIssuerX500Principal()); + assertEquals(new X500Principal("CN=Test Subject"), c.getSubjectX500Principal()); + assertEquals("1.2.840.10045.4.1", c.getSigAlgOID()); + String signatureHex = "3045022100f2a0355e513a36c382799bee27" + + "50858e7006749557d2297400f4be15875dc4" + + "0702207c1e79146a2183f07a7468795f1499" + + "9a68b4f1cb9e155ee61f3252615e75c914"; + assertArrayEquals(TestUtils.decodeHex(signatureHex), c.getSignature()); + + // ECDSA signature AlgorithmIdentifiers omit parameters. + assertNull(c.getSigAlgParams()); + + // The certificate does not have UIDs. + assertNull(c.getIssuerUniqueID()); + assertNull(c.getSubjectUniqueID()); + + // The certificate does not have any extensions. + assertEquals(-1, c.getBasicConstraints()); + assertNull(c.getExtendedKeyUsage()); + assertNull(c.getIssuerAlternativeNames()); + assertNull(c.getKeyUsage()); + assertNull(c.getSubjectAlternativeNames()); + } + }); + } + + @Test + public void testManyExtensions() throws Exception { + ServiceTester tester = ServiceTester.test("CertificateFactory").withAlgorithm("X509"); + tester.run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + X509Certificate c = certificateFromPEM(p, MANY_EXTENSIONS); + + assertEquals(3, c.getVersion()); + assertEquals(new BigInteger("b5b622b95a04a521", 16), c.getSerialNumber()); + assertDatesEqual(dateFromUTC(2016, Calendar.JULY, 9, 4, 38, 9), c.getNotBefore()); + assertDatesEqual(dateFromUTC(2016, Calendar.AUGUST, 8, 4, 38, 9), c.getNotAfter()); + assertEquals(new X500Principal("CN=Test Issuer"), c.getIssuerX500Principal()); + assertEquals(new X500Principal("CN=Test Subject"), c.getSubjectX500Principal()); + assertEquals("1.2.840.113549.1.1.11", c.getSigAlgOID()); + String signatureHex = "3ec983af1202b61695ca077d9001f743e6ca" + + "bb791fa0fc2d18be5b6462d5f04dc511042e" + + "77b3589dac72397850c72c298a783e2f79d2" + + "054dfbad8882b22670236fb5be48d427f2fc" + + "c34dbabf5f7dab3a5f7df80f485854841378" + + "fc85937ba623eda6250aed659c8c3c829263" + + "fb181901e11865fac062be18efe88343d093" + + "f56ee83f865365d19c357461983596c02c1d" + + "ddb55ebc8ae9f0e636410cc1b216aedb38c5" + + "ceec711ac61d6cbe88c7faffba7f024fd222" + + "270ce174b09a543ca4fc4064fafe1362e855" + + "df69329594c295b651bb4ee70b064eb639b0" + + "ee39b4534dff2fa3b5485e0750b68a339b1b" + + "fb5710b6a2c8274cf92ff069ebafd0c5ed23" + + "8c679f50"; + assertArrayEquals(TestUtils.decodeHex(signatureHex), c.getSignature()); + + // Although documented to only return null when there are no parameters, the SUN + // provider also returns null when the algorithm uses an explicit parameter with a + // value of ASN.1 NULL. + if (c.getSigAlgParams() != null) { + assertArrayEquals(TestUtils.decodeHex("0500"), c.getSigAlgParams()); + } + + assertArrayEquals(new boolean[] {true, false, true, false}, c.getIssuerUniqueID()); + assertArrayEquals( + new boolean[] {false, true, false, true, false}, c.getSubjectUniqueID()); + assertEquals(10, c.getBasicConstraints()); + assertEquals(Arrays.asList("1.3.6.1.5.5.7.3.1", "1.2.840.113554.4.1.72585.2"), + c.getExtendedKeyUsage()); + + // TODO(davidben): Test the other name types. + assertGeneralNamesEqual( + Arrays.<List<?>>asList(Arrays.asList(1, "issuer@example.com"), + Arrays.asList(2, "issuer.example.com"), + Arrays.asList(4, "CN=Test Issuer"), + Arrays.asList(6, "https://example.com/issuer"), + // TODO(https://github.com/google/conscrypt/issues/938): Fix IPv6 + // handling and include it in this test. + Arrays.asList(7, "127.0.0.1"), + Arrays.asList(8, "1.2.840.113554.4.1.72585.2")), + c.getIssuerAlternativeNames()); + assertGeneralNamesEqual( + Arrays.<List<?>>asList(Arrays.asList(1, "subject@example.com"), + Arrays.asList(2, "subject.example.com"), + Arrays.asList(4, "CN=Test Subject"), + Arrays.asList(6, "https://example.com/subject"), + // TODO(https://github.com/google/conscrypt/issues/938): Fix IPv6 + // handling and include it in this test. + Arrays.asList(7, "127.0.0.1"), + Arrays.asList(8, "1.2.840.113554.4.1.72585.2")), + c.getSubjectAlternativeNames()); + + // Although the BIT STRING in the certificate only has three bits, getKeyUsage() + // rounds up to at least 9 bits. + assertArrayEquals( + new boolean[] {true, false, true, false, false, false, false, false, false}, + c.getKeyUsage()); + } + }); + } + + @Test + public void testBasicConstraints() throws Exception { + ServiceTester tester = ServiceTester.test("CertificateFactory").withAlgorithm("X509"); + tester.run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + // Test some additional edge cases in getBasicConstraints() beyond that + // testManyExtensions() and testV1Cert() covered. + + // If there is no pathLen constraint but the certificate is a CA, + // getBasicConstraints() returns Integer.MAX_VALUE. + X509Certificate c = certificateFromPEM(p, BASIC_CONSTRAINTS_NO_PATHLEN); + assertEquals(Integer.MAX_VALUE, c.getBasicConstraints()); + + // If there is a pathLen constraint of zero, getBasicConstraints() returns it. + c = certificateFromPEM(p, BASIC_CONSTRAINTS_PATHLEN_0); + assertEquals(0, c.getBasicConstraints()); + + // If there is basicConstraints extension indicating a leaf certficate, + // getBasicConstraints() returns -1. The accessor does not distinguish between no + // basicConstraints extension and a leaf one. + c = certificateFromPEM(p, BASIC_CONSTRAINTS_LEAF); + assertEquals(-1, c.getBasicConstraints()); + + // If some unrelated extension has a syntax error, and that syntax error does not + // fail when constructing the certificate, it should not interfere with + // getBasicConstraints(). + try { + c = certificateFromPEM(p, BASIC_CONSTRAINTS_PATHLEN_10_BAD_SAN); + } catch (CertificateParsingException e) { + // The certificate has a syntax error, so it would also be valid for the + // provider to reject the certificate at construction. X.509 is an extensible + // format, so different implementations may notice errors at different points. + c = null; + } + if (c != null) { + assertEquals(10, c.getBasicConstraints()); + } + } + }); + } + + @Test + public void testLargeKeyUsage() throws Exception { + ServiceTester tester = ServiceTester.test("CertificateFactory").withAlgorithm("X509"); + tester.run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + X509Certificate c = certificateFromPEM(p, LARGE_KEY_USAGE); + assertArrayEquals(new boolean[] {true, false, true, false, false, false, false, + false, false, false, false}, + c.getKeyUsage()); + } + }); + } + + @Test + public void testSigAlgParams() throws Exception { + ServiceTester tester = ServiceTester.test("CertificateFactory").withAlgorithm("X509"); + tester.run(new ServiceTester.Test() { + @Override + public void test(Provider p, String algorithm) throws Exception { + X509Certificate c = certificateFromPEM(p, SIGALG_NO_PARAMETER); + assertNull(c.getSigAlgParams()); + + c = certificateFromPEM(p, SIGALG_NULL_PARAMETER); + // Although documented to only return null when there are no parameters, the SUN + // provider also returns null when the algorithm uses an explicit parameter with a + // value of ASN.1 NULL. + if (c.getSigAlgParams() != null) { + assertArrayEquals(TestUtils.decodeHex("0500"), c.getSigAlgParams()); + } + + c = certificateFromPEM(p, SIGALG_STRING_PARAMETER); + assertArrayEquals(TestUtils.decodeHex("0c05706172616d"), c.getSigAlgParams()); + + c = certificateFromPEM(p, SIGALG_BOOLEAN_PARAMETER); + assertArrayEquals(TestUtils.decodeHex("0101ff"), c.getSigAlgParams()); + + c = certificateFromPEM(p, SIGALG_SEQUENCE_PARAMETER); + assertArrayEquals(TestUtils.decodeHex("3000"), c.getSigAlgParams()); + } + }); + } }
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/CipherBasicsTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/CipherBasicsTest.java index 0ae78f4..2c09f04 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/CipherBasicsTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/CipherBasicsTest.java
@@ -17,13 +17,12 @@ package com.android.org.conscrypt.javax.crypto; +import static com.android.org.conscrypt.TestUtils.decodeHex; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import com.android.org.conscrypt.TestUtils; +import java.nio.ByteBuffer; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -32,7 +31,6 @@ import java.security.Provider; import java.security.Security; import java.security.spec.AlgorithmParameterSpec; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -41,7 +39,6 @@ import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import com.android.org.conscrypt.TestUtils; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -55,27 +52,26 @@ */ @RunWith(JUnit4.class) public final class CipherBasicsTest { - - private static final Map<String, String> BASIC_CIPHER_TO_TEST_DATA = new HashMap<String, String>(); + private static final Map<String, String> BASIC_CIPHER_TO_TEST_DATA = new HashMap<>(); static { - BASIC_CIPHER_TO_TEST_DATA.put("AES/ECB/NoPadding", "/crypto/aes-ecb.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("AES/CBC/NoPadding", "/crypto/aes-cbc.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("AES/CFB8/NoPadding", "/crypto/aes-cfb8.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("AES/CFB128/NoPadding", "/crypto/aes-cfb128.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("AES/OFB/NoPadding", "/crypto/aes-ofb.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/ECB/NoPadding", "/crypto/desede-ecb.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CBC/NoPadding", "/crypto/desede-cbc.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CFB8/NoPadding", "/crypto/desede-cfb8.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CFB64/NoPadding", "/crypto/desede-cfb64.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/OFB/NoPadding", "/crypto/desede-ofb.csv"); - BASIC_CIPHER_TO_TEST_DATA.put("ChaCha20", "/crypto/chacha20.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("AES/ECB/NoPadding", "crypto/aes-ecb.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("AES/CBC/NoPadding", "crypto/aes-cbc.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("AES/CFB8/NoPadding", "crypto/aes-cfb8.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("AES/CFB128/NoPadding", "crypto/aes-cfb128.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("AES/OFB/NoPadding", "crypto/aes-ofb.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/ECB/NoPadding", "crypto/desede-ecb.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CBC/NoPadding", "crypto/desede-cbc.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CFB8/NoPadding", "crypto/desede-cfb8.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CFB64/NoPadding", "crypto/desede-cfb64.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/OFB/NoPadding", "crypto/desede-ofb.csv"); + BASIC_CIPHER_TO_TEST_DATA.put("ChaCha20", "crypto/chacha20.csv"); } - private static final Map<String, String> AEAD_CIPHER_TO_TEST_DATA = new HashMap<String, String>(); + private static final Map<String, String> AEAD_CIPHER_TO_TEST_DATA = new HashMap<>(); static { - AEAD_CIPHER_TO_TEST_DATA.put("AES/GCM/NoPadding", "/crypto/aes-gcm.csv"); - AEAD_CIPHER_TO_TEST_DATA.put("AES/GCM-SIV/NoPadding", "/crypto/aes-gcm-siv.csv"); - AEAD_CIPHER_TO_TEST_DATA.put("ChaCha20/Poly1305/NoPadding", "/crypto/chacha20-poly1305.csv"); + AEAD_CIPHER_TO_TEST_DATA.put("AES/GCM/NoPadding", "crypto/aes-gcm.csv"); + AEAD_CIPHER_TO_TEST_DATA.put("AES/GCM-SIV/NoPadding", "crypto/aes-gcm-siv.csv"); + AEAD_CIPHER_TO_TEST_DATA.put("ChaCha20/Poly1305/NoPadding", "crypto/chacha20-poly1305.csv"); } private static final int KEY_INDEX = 0; @@ -119,13 +115,13 @@ continue; } - List<String[]> data = readCsvResource(entry.getValue()); + List<String[]> data = TestUtils.readCsvResource(entry.getValue()); for (String[] line : data) { - Key key = new SecretKeySpec(toBytes(line[KEY_INDEX]), - getBaseAlgorithm(transformation)); - byte[] iv = toBytes(line[IV_INDEX]); - byte[] plaintext = toBytes(line[PLAINTEXT_INDEX]); - byte[] ciphertext = toBytes(line[CIPHERTEXT_INDEX]); + Key key = new SecretKeySpec( + decodeHex(line[KEY_INDEX]), getBaseAlgorithm(transformation)); + byte[] iv = decodeHex(line[IV_INDEX]); + byte[] plaintext = decodeHex(line[PLAINTEXT_INDEX]); + byte[] ciphertext = decodeHex(line[CIPHERTEXT_INDEX]); // Initialize the IV, if there is one AlgorithmParameters params; @@ -142,20 +138,20 @@ + ", algorithm " + transformation + " reported the wrong output size", ciphertext.length, cipher.getOutputSize(plaintext.length)); - assertTrue("Provider " + p.getName() - + ", algorithm " + transformation - + " failed on encryption, data is " + Arrays.toString(line), - Arrays.equals(ciphertext, cipher.doFinal(plaintext))); + assertArrayEquals("Provider " + p.getName() + ", algorithm " + + transformation + " failed on encryption, data is " + + Arrays.toString(line), + ciphertext, cipher.doFinal(plaintext)); cipher.init(Cipher.DECRYPT_MODE, key, params); assertEquals("Provider " + p.getName() + ", algorithm " + transformation + " reported the wrong output size", plaintext.length, cipher.getOutputSize(ciphertext.length)); - assertTrue("Provider " + p.getName() - + ", algorithm " + transformation - + " failed on decryption, data is " + Arrays.toString(line), - Arrays.equals(plaintext, cipher.doFinal(ciphertext))); + assertArrayEquals("Provider " + p.getName() + ", algorithm " + + transformation + " failed on decryption, data is " + + Arrays.toString(line), + plaintext, cipher.doFinal(ciphertext)); } catch (InvalidKeyException e) { // Some providers may not support raw SecretKeySpec keys, that's allowed } @@ -164,6 +160,35 @@ } } + public void arrayBasedAssessment(Cipher cipher, byte[] aad, byte[] tag, byte[] plaintext, + byte[] ciphertext, Key key, AlgorithmParameterSpec params, String transformation, + Provider p, String[] line) throws Exception { + cipher.init(Cipher.ENCRYPT_MODE, key, params); + if (aad.length > 0) { + cipher.updateAAD(aad); + } + byte[] combinedOutput = new byte[ciphertext.length + tag.length]; + assertEquals("Provider " + p.getName() + ", algorithm " + transformation + + " reported the wrong output size", + combinedOutput.length, cipher.getOutputSize(plaintext.length)); + System.arraycopy(ciphertext, 0, combinedOutput, 0, ciphertext.length); + System.arraycopy(tag, 0, combinedOutput, ciphertext.length, tag.length); + assertArrayEquals("Provider " + p.getName() + ", algorithm " + transformation + + " failed on encryption, data is " + Arrays.toString(line), + combinedOutput, cipher.doFinal(plaintext)); + + cipher.init(Cipher.DECRYPT_MODE, key, params); + if (aad.length > 0) { + cipher.updateAAD(aad); + } + assertEquals("Provider " + p.getName() + ", algorithm " + transformation + + " reported the wrong output size", + plaintext.length, cipher.getOutputSize(combinedOutput.length)); + assertArrayEquals("Provider " + p.getName() + ", algorithm " + transformation + + " failed on decryption, data is " + Arrays.toString(line), + plaintext, cipher.doFinal(combinedOutput)); + } + @Test public void testAeadEncryption() throws Exception { TestUtils.assumeAEADAvailable(); @@ -171,6 +196,13 @@ for (Map.Entry<String, String> entry : AEAD_CIPHER_TO_TEST_DATA.entrySet()) { String transformation = entry.getKey(); + // On Android 10 and below, BC can return AES/GCM/NoPadding when asked for + // AES/GCM-SIV/NoPadding. Android will never actually ship AES/GCM-SIV/NoPadding + // in BC, so skip that combination. + if (p.getName().equals("BC") && transformation.equals("AES/GCM-SIV/NoPadding")) { + continue; + } + Cipher cipher; try { cipher = Cipher.getInstance(transformation, p); @@ -179,15 +211,15 @@ continue; } - List<String[]> data = readCsvResource(entry.getValue()); + List<String[]> data = TestUtils.readCsvResource(entry.getValue()); for (String[] line : data) { - Key key = new SecretKeySpec(toBytes(line[KEY_INDEX]), - getBaseAlgorithm(transformation)); - byte[] iv = toBytes(line[IV_INDEX]); - byte[] plaintext = toBytes(line[PLAINTEXT_INDEX]); - byte[] ciphertext = toBytes(line[CIPHERTEXT_INDEX]); - byte[] tag = toBytes(line[TAG_INDEX]); - byte[] aad = toBytes(line[AAD_INDEX]); + Key key = new SecretKeySpec( + decodeHex(line[KEY_INDEX]), getBaseAlgorithm(transformation)); + byte[] iv = decodeHex(line[IV_INDEX]); + byte[] plaintext = decodeHex(line[PLAINTEXT_INDEX]); + byte[] ciphertext = decodeHex(line[CIPHERTEXT_INDEX]); + byte[] tag = decodeHex(line[TAG_INDEX]); + byte[] aad = decodeHex(line[AAD_INDEX]); // Some ChaCha20 tests include truncated tags, which the Java API doesn't // support. Skip those tests. @@ -203,34 +235,18 @@ } try { - cipher.init(Cipher.ENCRYPT_MODE, key, params); - if (aad.length > 0) { - cipher.updateAAD(aad); - } - byte[] combinedOutput = new byte[ciphertext.length + tag.length]; - assertEquals("Provider " + p.getName() - + ", algorithm " + transformation - + " reported the wrong output size", - combinedOutput.length, cipher.getOutputSize(plaintext.length)); - System.arraycopy(ciphertext, 0, combinedOutput, 0, ciphertext.length); - System.arraycopy(tag, 0, combinedOutput, ciphertext.length, tag.length); - assertTrue("Provider " + p.getName() - + ", algorithm " + transformation - + " failed on encryption, data is " + Arrays.toString(line), - Arrays.equals(combinedOutput, cipher.doFinal(plaintext))); - - cipher.init(Cipher.DECRYPT_MODE, key, params); - if (aad.length > 0) { - cipher.updateAAD(aad); - } - assertEquals("Provider " + p.getName() - + ", algorithm " + transformation - + " reported the wrong output size", - plaintext.length, cipher.getOutputSize(combinedOutput.length)); - assertTrue("Provider " + p.getName() - + ", algorithm " + transformation - + " failed on decryption, data is " + Arrays.toString(line), - Arrays.equals(plaintext, cipher.doFinal(combinedOutput))); + arrayBasedAssessment(cipher, aad, tag, plaintext, ciphertext, key, params, + transformation, p, line); + bufferBasedAssessment(cipher, aad, tag, plaintext, ciphertext, key, params, + transformation, p, false, false); + bufferBasedAssessment(cipher, aad, tag, plaintext, ciphertext, key, params, + transformation, p, true, true); + bufferBasedAssessment(cipher, aad, tag, plaintext, ciphertext, key, params, + transformation, p, true, false); + bufferBasedAssessment(cipher, aad, tag, plaintext, ciphertext, key, params, + transformation, p, false, true); + sharedBufferBasedAssessment(cipher, aad, tag, plaintext, ciphertext, key, + params, transformation, p); } catch (InvalidKeyException e) { // Some providers may not support raw SecretKeySpec keys, that's allowed } catch (InvalidAlgorithmParameterException e) { @@ -242,25 +258,127 @@ } } - private static List<String[]> readCsvResource(String resourceName) throws IOException { - InputStream stream = CipherBasicsTest.class.getResourceAsStream(resourceName); - List<String[]> lines = new ArrayList<String[]>(); - BufferedReader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(stream, "UTF-8")); - String line; - while ((line = reader.readLine()) != null) { - if (line.isEmpty() || line.startsWith("#")) { - continue; - } - lines.add(line.split(",", -1)); - } - } finally { - if (reader != null) { - reader.close(); - } + public void sharedBufferBasedAssessment(Cipher cipher, byte[] aad, byte[] tag, + byte[] _plaintext, byte[] _ciphertext, Key key, AlgorithmParameterSpec params, + String transformation, Provider p) throws Exception { + cipher.init(Cipher.ENCRYPT_MODE, key, params); + if (aad.length > 0) { + cipher.updateAAD(aad); } - return lines; + byte[] _combinedOutput = new byte[_ciphertext.length + tag.length]; + byte[] _commonBacking = new byte[_plaintext.length + _combinedOutput.length]; + + assertEquals("Provider " + p.getName() + ", algorithm " + transformation + + " reported the wrong output size", + _combinedOutput.length, cipher.getOutputSize(_plaintext.length)); + System.arraycopy(_ciphertext, 0, _combinedOutput, 0, _ciphertext.length); + System.arraycopy(tag, 0, _combinedOutput, _ciphertext.length, tag.length); + System.arraycopy(_plaintext, 0, _commonBacking, 0, _plaintext.length); + System.arraycopy( + _combinedOutput, 0, _commonBacking, _plaintext.length, _combinedOutput.length); + ByteBuffer combinedOutput = ByteBuffer.wrap(_commonBacking); + ByteBuffer plaintext = combinedOutput.slice(); + plaintext.limit(_plaintext.length); + combinedOutput.position(_plaintext.length); + // both byte buffers have been created from common backed array and have correct respecting + // positions and limits + + combinedOutput.position(combinedOutput.limit()); + ByteBuffer outputbuffer = ByteBuffer.allocate(cipher.getOutputSize(plaintext.remaining())); + + cipher.doFinal(plaintext, outputbuffer); + assertEquals("Cipher doFinal did not encrypt correctly", combinedOutput, outputbuffer); + assertEquals(" input was not shifted", plaintext.position(), plaintext.limit()); + + cipher.init(Cipher.DECRYPT_MODE, key, params); + if (aad.length > 0) { + cipher.updateAAD(aad); + } + assertEquals("Provider " + p.getName() + ", algorithm " + transformation + + " reported the wrong output size", + _plaintext.length, cipher.getOutputSize(_combinedOutput.length)); + combinedOutput.position(_plaintext.length); + + outputbuffer = ByteBuffer.allocate(cipher.getOutputSize(combinedOutput.remaining())); + + combinedOutput.position(_plaintext.length); + plaintext.position(plaintext.limit()); + cipher.doFinal(combinedOutput, outputbuffer); + assertEquals("Cipher doFinal did not decrypt correctly", plaintext, outputbuffer); + assertEquals(" input was not shifted", combinedOutput.position(), combinedOutput.limit()); + } + + public void bufferBasedAssessment(Cipher cipher, byte[] aad, byte[] tag, byte[] _plaintext, + byte[] _ciphertext, Key key, AlgorithmParameterSpec params, String transformation, + Provider p, boolean inBoolDirect, boolean outBoolDirect) throws Exception { + cipher.init(Cipher.ENCRYPT_MODE, key, params); + if (aad.length > 0) { + cipher.updateAAD(aad); + } + byte[] _combinedOutput = new byte[_ciphertext.length + tag.length]; + ByteBuffer plaintext = ByteBuffer.wrap(_plaintext); + if (inBoolDirect) { + ByteBuffer plaintext_ = plaintext; + int incap = plaintext_.remaining(); + plaintext = ByteBuffer.allocateDirect(incap); + plaintext.mark(); + plaintext.put(plaintext_); + plaintext.reset(); + } + + assertEquals("Provider " + p.getName() + ", algorithm " + transformation + + " reported the wrong output size", + _combinedOutput.length, cipher.getOutputSize(_plaintext.length)); + System.arraycopy(_ciphertext, 0, _combinedOutput, 0, _ciphertext.length); + System.arraycopy(tag, 0, _combinedOutput, _ciphertext.length, tag.length); + + ByteBuffer combinedOutput = ByteBuffer.wrap(_combinedOutput); + if (outBoolDirect) { + ByteBuffer combinedOutput_ = combinedOutput; + int outcap = combinedOutput_.remaining(); + combinedOutput = ByteBuffer.allocateDirect(outcap); + combinedOutput.mark(); + combinedOutput.put(combinedOutput_); + } + combinedOutput.position(combinedOutput.limit()); + ByteBuffer outputbuffer; + if (outBoolDirect) { + outputbuffer = ByteBuffer.allocateDirect(cipher.getOutputSize(plaintext.remaining())); + } else { + outputbuffer = ByteBuffer.allocate(cipher.getOutputSize(plaintext.remaining())); + } + + cipher.doFinal(plaintext, outputbuffer); + assertEquals("Cipher doFinal did not encrypt correctly", combinedOutput, outputbuffer); + assertEquals(" input was not shifted", plaintext.position(), plaintext.limit()); + + cipher.init(Cipher.DECRYPT_MODE, key, params); + if (aad.length > 0) { + cipher.updateAAD(aad); + } + assertEquals("Provider " + p.getName() + ", algorithm " + transformation + + " reported the wrong output size", + _plaintext.length, cipher.getOutputSize(_combinedOutput.length)); + combinedOutput = ByteBuffer.wrap(_combinedOutput); + if (inBoolDirect) { + ByteBuffer combinedOutput_ = combinedOutput; + int incap = combinedOutput_.remaining(); + combinedOutput = ByteBuffer.allocateDirect(incap); + combinedOutput.mark(); + combinedOutput.put(combinedOutput_); + combinedOutput.reset(); + } + if (outBoolDirect) { + outputbuffer = + ByteBuffer.allocateDirect(cipher.getOutputSize(combinedOutput.remaining())); + } else { + outputbuffer = ByteBuffer.allocate(cipher.getOutputSize(combinedOutput.remaining())); + } + combinedOutput.position(0); + plaintext.position(plaintext.limit()); + cipher.doFinal(combinedOutput, outputbuffer); + assertEquals("Cipher doFinal did not decrypt correctly", plaintext, outputbuffer); + assertEquals(" input was not shifted", combinedOutput.position(), combinedOutput.limit()); } /** @@ -274,7 +392,89 @@ return transformation; } - private static byte[] toBytes(String hex) { - return TestUtils.decodeHex(hex, /* allowSingleChar= */ true); + /** + * Encryption with ByteBuffers should be copy-safe even if the buffers have different starting + * offsets and/or do not make the backing array visible. + * + * <p>Note that bugs in this often require a sizeable input to reproduce; the default + * implementation of engineUpdate(ByteBuffer, ByteBuffer) copies through 4KB bounce buffers, so + * we need to use something larger to see any problems - 8KB is what we use here. + * + * @see https://bugs.openjdk.java.net/browse/JDK-8181386 + */ + @Test + public void testByteBufferShiftedAlias() throws Exception { + byte[] ptVector = new byte[8192]; + + for (int i = 0; i < 3; i++) { + // outputOffset = offset relative to start of input. + for (int outputOffset = -1; outputOffset <= 1; outputOffset++) { + SecretKeySpec key = new SecretKeySpec(new byte[16], "AES"); + GCMParameterSpec parameters = new GCMParameterSpec(128, new byte[12]); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, key, parameters); + + ByteBuffer output, input, inputRO; + + // We'll try three scenarios: Ordinary array backed buffers, array backed buffers + // where one is read-only, and direct byte buffers. + String mode; + // offsets relative to start of buffer + int inputOffsetInBuffer = 1; + int outputOffsetInBuffer = inputOffsetInBuffer + outputOffset; + int sliceLength = cipher.getOutputSize(ptVector.length); + int bufferSize = sliceLength + Math.max(inputOffsetInBuffer, outputOffsetInBuffer); + + mode = "direct buffers"; + ByteBuffer buf = ByteBuffer.allocateDirect(bufferSize); + output = buf.duplicate(); + output.position(outputOffsetInBuffer); + output.limit(sliceLength + outputOffsetInBuffer); + output = output.slice(); + + input = buf.duplicate(); + input.position(inputOffsetInBuffer); + input.limit(sliceLength + inputOffsetInBuffer); + input = input.slice(); + + inputRO = input.duplicate(); + + // Now that we have our overlapping 'input' and 'output' buffers, we can write our + // plaintext into the input buffer. + input.put(ptVector); + input.flip(); + // Make sure the RO input buffer has the same limit in case the plaintext is shorter + // than sliceLength (which it generally will be for anything other than ECB or CTR + // mode) + inputRO.limit(input.limit()); + + try { + int ctSize = cipher.doFinal(inputRO, output); + + // Now flip the buffers around and undo everything + byte[] tmp = new byte[ctSize]; + output.flip(); + output.get(tmp); + + output.clear(); + input.clear(); + inputRO.clear(); + + input.put(tmp); + input.flip(); + inputRO.limit(input.limit()); + + cipher.init(Cipher.DECRYPT_MODE, key, parameters); + cipher.doFinal(inputRO, output); + + output.flip(); + assertEquals(ByteBuffer.wrap(ptVector), output); + } catch (Throwable t) { + throw new AssertionError("Overlapping buffers test failed with buffer type: " + + mode + " and output offset " + outputOffset, + t); + } + } + } } }
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 198e751..0a762d2 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
@@ -75,6 +75,7 @@ import javax.crypto.spec.PSource; import javax.crypto.spec.SecretKeySpec; import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; +import libcore.test.annotation.NonCts; import org.bouncycastle.asn1.x509.KeyUsage; import org.junit.Assume; import org.junit.BeforeClass; @@ -382,18 +383,14 @@ setExpectedBlockSize("AES/OFB/PKCS7PADDING", 16); setExpectedBlockSize("AES/OFB/NOPADDING", 16); setExpectedBlockSize("AES_128/CBC/PKCS5PADDING", 16); - setExpectedBlockSize("AES_128/CBC/PKCS7PADDING", 16); setExpectedBlockSize("AES_128/CBC/NOPADDING", 16); setExpectedBlockSize("AES_128/ECB/PKCS5PADDING", 16); - setExpectedBlockSize("AES_128/ECB/PKCS7PADDING", 16); setExpectedBlockSize("AES_128/ECB/NOPADDING", 16); setExpectedBlockSize("AES_128/GCM/NOPADDING", 16); setExpectedBlockSize("AES_128/GCM-SIV/NOPADDING", 16); setExpectedBlockSize("AES_256/CBC/PKCS5PADDING", 16); - setExpectedBlockSize("AES_256/CBC/PKCS7PADDING", 16); setExpectedBlockSize("AES_256/CBC/NOPADDING", 16); setExpectedBlockSize("AES_256/ECB/PKCS5PADDING", 16); - setExpectedBlockSize("AES_256/ECB/PKCS7PADDING", 16); setExpectedBlockSize("AES_256/ECB/NOPADDING", 16); setExpectedBlockSize("AES_256/GCM/NOPADDING", 16); setExpectedBlockSize("AES_256/GCM-SIV/NOPADDING", 16); @@ -414,7 +411,6 @@ } setExpectedBlockSize("ARC4", 0); - setExpectedBlockSize("ARCFOUR", 0); setExpectedBlockSize("CHACHA20", 0); setExpectedBlockSize("CHACHA20/POLY1305/NOPADDING", 0); setExpectedBlockSize("PBEWITHSHAAND40BITRC4", 0); @@ -428,27 +424,9 @@ setExpectedBlockSize("DESEDE", 8); setExpectedBlockSize("DESEDE/CBC/PKCS5PADDING", 8); - setExpectedBlockSize("DESEDE/CBC/PKCS7PADDING", 8); setExpectedBlockSize("DESEDE/CBC/NOPADDING", 8); - setExpectedBlockSize("DESEDE/CFB/PKCS5PADDING", 8); - setExpectedBlockSize("DESEDE/CFB/PKCS7PADDING", 8); - setExpectedBlockSize("DESEDE/CFB/NOPADDING", 8); - setExpectedBlockSize("DESEDE/CTR/PKCS5PADDING", 8); - setExpectedBlockSize("DESEDE/CTR/PKCS7PADDING", 8); - setExpectedBlockSize("DESEDE/CTR/NOPADDING", 8); - setExpectedBlockSize("DESEDE/CTS/PKCS5PADDING", 8); - setExpectedBlockSize("DESEDE/CTS/PKCS7PADDING", 8); - setExpectedBlockSize("DESEDE/CTS/NOPADDING", 8); - setExpectedBlockSize("DESEDE/ECB/PKCS5PADDING", 8); - setExpectedBlockSize("DESEDE/ECB/PKCS7PADDING", 8); - setExpectedBlockSize("DESEDE/ECB/NOPADDING", 8); - setExpectedBlockSize("DESEDE/OFB/PKCS5PADDING", 8); - setExpectedBlockSize("DESEDE/OFB/PKCS7PADDING", 8); - setExpectedBlockSize("DESEDE/OFB/NOPADDING", 8); setExpectedBlockSize("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", 8); setExpectedBlockSize("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", 8); - setExpectedBlockSize("PBEWITHMD5ANDTRIPLEDES", 8); - setExpectedBlockSize("PBEWITHSHA1ANDDESEDE", 8); if (StandardNames.IS_RI) { @@ -2156,414 +2134,405 @@ /* * echo -n 'This is a test of OAEP' | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_Plaintext = new byte[] { - (byte) 0x54, (byte) 0x68, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x69, - (byte) 0x73, (byte) 0x20, (byte) 0x61, (byte) 0x20, (byte) 0x74, (byte) 0x65, - (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x6f, (byte) 0x66, (byte) 0x20, - (byte) 0x4f, (byte) 0x41, (byte) 0x45, (byte) 0x50 - }; + private static final byte[] RSA_Vector2_Plaintext = + new byte[] {(byte) 0x54, (byte) 0x68, (byte) 0x69, (byte) 0x73, (byte) 0x20, + (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x61, (byte) 0x20, (byte) 0x74, + (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x6f, (byte) 0x66, + (byte) 0x20, (byte) 0x4f, (byte) 0x41, (byte) 0x45, (byte) 0x50}; /* * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey rsakey.pem \ * -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha1 -pkeyopt rsa_mgf1_md:sha1 \ * | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA1_MGF1_SHA1 = new byte[] { - (byte) 0x53, (byte) 0x71, (byte) 0x84, (byte) 0x2e, (byte) 0x01, (byte) 0x74, - (byte) 0x82, (byte) 0xb3, (byte) 0x01, (byte) 0xac, (byte) 0x2b, (byte) 0xbd, - (byte) 0x40, (byte) 0xa7, (byte) 0x5b, (byte) 0x60, (byte) 0xf1, (byte) 0xde, - (byte) 0x54, (byte) 0x1d, (byte) 0x94, (byte) 0xc1, (byte) 0x10, (byte) 0x31, - (byte) 0x6f, (byte) 0xa3, (byte) 0xd8, (byte) 0x41, (byte) 0x2e, (byte) 0x82, - (byte) 0xad, (byte) 0x07, (byte) 0x6f, (byte) 0x25, (byte) 0x6c, (byte) 0xb5, - (byte) 0xef, (byte) 0xc6, (byte) 0xa6, (byte) 0xfb, (byte) 0xb1, (byte) 0x9d, - (byte) 0x75, (byte) 0x67, (byte) 0xb0, (byte) 0x97, (byte) 0x21, (byte) 0x3c, - (byte) 0x17, (byte) 0x04, (byte) 0xdc, (byte) 0x4e, (byte) 0x7e, (byte) 0x3f, - (byte) 0x5c, (byte) 0x13, (byte) 0x5e, (byte) 0x15, (byte) 0x0f, (byte) 0xe2, - (byte) 0xa7, (byte) 0x62, (byte) 0x6a, (byte) 0x08, (byte) 0xb1, (byte) 0xbc, - (byte) 0x2f, (byte) 0xcb, (byte) 0xb5, (byte) 0x96, (byte) 0x2d, (byte) 0xec, - (byte) 0x71, (byte) 0x4d, (byte) 0x59, (byte) 0x6e, (byte) 0x27, (byte) 0x85, - (byte) 0x87, (byte) 0x9b, (byte) 0xcc, (byte) 0x40, (byte) 0x32, (byte) 0x09, - (byte) 0x06, (byte) 0xe6, (byte) 0x7d, (byte) 0xdf, (byte) 0xeb, (byte) 0x2f, - (byte) 0xa8, (byte) 0x1c, (byte) 0x53, (byte) 0xdb, (byte) 0xa7, (byte) 0x48, - (byte) 0xf5, (byte) 0xbf, (byte) 0x2f, (byte) 0xbb, (byte) 0xee, (byte) 0xc7, - (byte) 0x55, (byte) 0x5e, (byte) 0xc4, (byte) 0x1c, (byte) 0x84, (byte) 0xed, - (byte) 0x97, (byte) 0x7e, (byte) 0xce, (byte) 0xa5, (byte) 0x69, (byte) 0x73, - (byte) 0xb3, (byte) 0xe0, (byte) 0x8c, (byte) 0x2a, (byte) 0xf2, (byte) 0xc7, - (byte) 0x65, (byte) 0xff, (byte) 0x10, (byte) 0xed, (byte) 0x25, (byte) 0xf0, - (byte) 0xf8, (byte) 0xda, (byte) 0x2f, (byte) 0x7f, (byte) 0xe0, (byte) 0x69, - (byte) 0xed, (byte) 0xb1, (byte) 0x0e, (byte) 0xcb, (byte) 0x43, (byte) 0xe4, - (byte) 0x31, (byte) 0xe6, (byte) 0x52, (byte) 0xfd, (byte) 0xa7, (byte) 0xe5, - (byte) 0x21, (byte) 0xd0, (byte) 0x67, (byte) 0x0a, (byte) 0xc1, (byte) 0xa1, - (byte) 0xb9, (byte) 0x04, (byte) 0xdb, (byte) 0x98, (byte) 0x4f, (byte) 0xf9, - (byte) 0x5c, (byte) 0x60, (byte) 0x4d, (byte) 0xac, (byte) 0x7a, (byte) 0x69, - (byte) 0xbd, (byte) 0x63, (byte) 0x0d, (byte) 0xb2, (byte) 0x01, (byte) 0x83, - (byte) 0xd7, (byte) 0x22, (byte) 0x5d, (byte) 0xed, (byte) 0xbd, (byte) 0x32, - (byte) 0x98, (byte) 0xd1, (byte) 0x4a, (byte) 0x2e, (byte) 0xb7, (byte) 0xb1, - (byte) 0x6d, (byte) 0x8a, (byte) 0x8f, (byte) 0xef, (byte) 0xc3, (byte) 0x89, - (byte) 0xdf, (byte) 0xa5, (byte) 0xac, (byte) 0xfb, (byte) 0x38, (byte) 0x61, - (byte) 0x32, (byte) 0xc5, (byte) 0x19, (byte) 0x83, (byte) 0x1f, (byte) 0x9c, - (byte) 0x45, (byte) 0x58, (byte) 0xdd, (byte) 0xa3, (byte) 0x57, (byte) 0xe4, - (byte) 0x91, (byte) 0xd2, (byte) 0x11, (byte) 0xf8, (byte) 0x96, (byte) 0x36, - (byte) 0x67, (byte) 0x99, (byte) 0x2b, (byte) 0x62, (byte) 0x21, (byte) 0xe3, - (byte) 0xa8, (byte) 0x5e, (byte) 0xa4, (byte) 0x2e, (byte) 0x0c, (byte) 0x29, - (byte) 0xf9, (byte) 0xcd, (byte) 0xfa, (byte) 0xbe, (byte) 0x3f, (byte) 0xd8, - (byte) 0xec, (byte) 0x6b, (byte) 0x32, (byte) 0xb3, (byte) 0x40, (byte) 0x4f, - (byte) 0x48, (byte) 0xe3, (byte) 0x14, (byte) 0x87, (byte) 0xa7, (byte) 0x5c, - (byte) 0xba, (byte) 0xdf, (byte) 0x0e, (byte) 0x64, (byte) 0xdc, (byte) 0xe2, - (byte) 0x51, (byte) 0xf4, (byte) 0x41, (byte) 0x25, (byte) 0x23, (byte) 0xc8, - (byte) 0x50, (byte) 0x1e, (byte) 0x9e, (byte) 0xb0 - }; + private static final byte[] RSA_Vector2_OAEP_SHA1_MGF1_SHA1 = + new byte[] {(byte) 0x53, (byte) 0x71, (byte) 0x84, (byte) 0x2e, (byte) 0x01, + (byte) 0x74, (byte) 0x82, (byte) 0xb3, (byte) 0x01, (byte) 0xac, (byte) 0x2b, + (byte) 0xbd, (byte) 0x40, (byte) 0xa7, (byte) 0x5b, (byte) 0x60, (byte) 0xf1, + (byte) 0xde, (byte) 0x54, (byte) 0x1d, (byte) 0x94, (byte) 0xc1, (byte) 0x10, + (byte) 0x31, (byte) 0x6f, (byte) 0xa3, (byte) 0xd8, (byte) 0x41, (byte) 0x2e, + (byte) 0x82, (byte) 0xad, (byte) 0x07, (byte) 0x6f, (byte) 0x25, (byte) 0x6c, + (byte) 0xb5, (byte) 0xef, (byte) 0xc6, (byte) 0xa6, (byte) 0xfb, (byte) 0xb1, + (byte) 0x9d, (byte) 0x75, (byte) 0x67, (byte) 0xb0, (byte) 0x97, (byte) 0x21, + (byte) 0x3c, (byte) 0x17, (byte) 0x04, (byte) 0xdc, (byte) 0x4e, (byte) 0x7e, + (byte) 0x3f, (byte) 0x5c, (byte) 0x13, (byte) 0x5e, (byte) 0x15, (byte) 0x0f, + (byte) 0xe2, (byte) 0xa7, (byte) 0x62, (byte) 0x6a, (byte) 0x08, (byte) 0xb1, + (byte) 0xbc, (byte) 0x2f, (byte) 0xcb, (byte) 0xb5, (byte) 0x96, (byte) 0x2d, + (byte) 0xec, (byte) 0x71, (byte) 0x4d, (byte) 0x59, (byte) 0x6e, (byte) 0x27, + (byte) 0x85, (byte) 0x87, (byte) 0x9b, (byte) 0xcc, (byte) 0x40, (byte) 0x32, + (byte) 0x09, (byte) 0x06, (byte) 0xe6, (byte) 0x7d, (byte) 0xdf, (byte) 0xeb, + (byte) 0x2f, (byte) 0xa8, (byte) 0x1c, (byte) 0x53, (byte) 0xdb, (byte) 0xa7, + (byte) 0x48, (byte) 0xf5, (byte) 0xbf, (byte) 0x2f, (byte) 0xbb, (byte) 0xee, + (byte) 0xc7, (byte) 0x55, (byte) 0x5e, (byte) 0xc4, (byte) 0x1c, (byte) 0x84, + (byte) 0xed, (byte) 0x97, (byte) 0x7e, (byte) 0xce, (byte) 0xa5, (byte) 0x69, + (byte) 0x73, (byte) 0xb3, (byte) 0xe0, (byte) 0x8c, (byte) 0x2a, (byte) 0xf2, + (byte) 0xc7, (byte) 0x65, (byte) 0xff, (byte) 0x10, (byte) 0xed, (byte) 0x25, + (byte) 0xf0, (byte) 0xf8, (byte) 0xda, (byte) 0x2f, (byte) 0x7f, (byte) 0xe0, + (byte) 0x69, (byte) 0xed, (byte) 0xb1, (byte) 0x0e, (byte) 0xcb, (byte) 0x43, + (byte) 0xe4, (byte) 0x31, (byte) 0xe6, (byte) 0x52, (byte) 0xfd, (byte) 0xa7, + (byte) 0xe5, (byte) 0x21, (byte) 0xd0, (byte) 0x67, (byte) 0x0a, (byte) 0xc1, + (byte) 0xa1, (byte) 0xb9, (byte) 0x04, (byte) 0xdb, (byte) 0x98, (byte) 0x4f, + (byte) 0xf9, (byte) 0x5c, (byte) 0x60, (byte) 0x4d, (byte) 0xac, (byte) 0x7a, + (byte) 0x69, (byte) 0xbd, (byte) 0x63, (byte) 0x0d, (byte) 0xb2, (byte) 0x01, + (byte) 0x83, (byte) 0xd7, (byte) 0x22, (byte) 0x5d, (byte) 0xed, (byte) 0xbd, + (byte) 0x32, (byte) 0x98, (byte) 0xd1, (byte) 0x4a, (byte) 0x2e, (byte) 0xb7, + (byte) 0xb1, (byte) 0x6d, (byte) 0x8a, (byte) 0x8f, (byte) 0xef, (byte) 0xc3, + (byte) 0x89, (byte) 0xdf, (byte) 0xa5, (byte) 0xac, (byte) 0xfb, (byte) 0x38, + (byte) 0x61, (byte) 0x32, (byte) 0xc5, (byte) 0x19, (byte) 0x83, (byte) 0x1f, + (byte) 0x9c, (byte) 0x45, (byte) 0x58, (byte) 0xdd, (byte) 0xa3, (byte) 0x57, + (byte) 0xe4, (byte) 0x91, (byte) 0xd2, (byte) 0x11, (byte) 0xf8, (byte) 0x96, + (byte) 0x36, (byte) 0x67, (byte) 0x99, (byte) 0x2b, (byte) 0x62, (byte) 0x21, + (byte) 0xe3, (byte) 0xa8, (byte) 0x5e, (byte) 0xa4, (byte) 0x2e, (byte) 0x0c, + (byte) 0x29, (byte) 0xf9, (byte) 0xcd, (byte) 0xfa, (byte) 0xbe, (byte) 0x3f, + (byte) 0xd8, (byte) 0xec, (byte) 0x6b, (byte) 0x32, (byte) 0xb3, (byte) 0x40, + (byte) 0x4f, (byte) 0x48, (byte) 0xe3, (byte) 0x14, (byte) 0x87, (byte) 0xa7, + (byte) 0x5c, (byte) 0xba, (byte) 0xdf, (byte) 0x0e, (byte) 0x64, (byte) 0xdc, + (byte) 0xe2, (byte) 0x51, (byte) 0xf4, (byte) 0x41, (byte) 0x25, (byte) 0x23, + (byte) 0xc8, (byte) 0x50, (byte) 0x1e, (byte) 0x9e, (byte) 0xb0}; /* * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey rsakey.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha1 | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA1 = new byte[] { - (byte) 0x25, (byte) 0x9f, (byte) 0xc3, (byte) 0x69, (byte) 0xbc, (byte) 0x3f, - (byte) 0xe7, (byte) 0x9e, (byte) 0x76, (byte) 0xef, (byte) 0x6c, (byte) 0xd2, - (byte) 0x2b, (byte) 0x7b, (byte) 0xf0, (byte) 0xeb, (byte) 0xc2, (byte) 0x28, - (byte) 0x40, (byte) 0x4e, (byte) 0x9b, (byte) 0x2a, (byte) 0x4e, (byte) 0xa4, - (byte) 0x79, (byte) 0x66, (byte) 0xf1, (byte) 0x10, (byte) 0x96, (byte) 0x8c, - (byte) 0x58, (byte) 0x92, (byte) 0xb7, (byte) 0x70, (byte) 0xed, (byte) 0x3a, - (byte) 0xe0, (byte) 0x99, (byte) 0xd1, (byte) 0x80, (byte) 0x4b, (byte) 0x53, - (byte) 0x70, (byte) 0x9b, (byte) 0x51, (byte) 0xbf, (byte) 0xc1, (byte) 0x3a, - (byte) 0x70, (byte) 0xc5, (byte) 0x79, (byte) 0x21, (byte) 0x6e, (byte) 0xb3, - (byte) 0xf7, (byte) 0xa9, (byte) 0xe6, (byte) 0xcb, (byte) 0x70, (byte) 0xe4, - (byte) 0xf3, (byte) 0x4f, (byte) 0x45, (byte) 0xcf, (byte) 0xb7, (byte) 0x2b, - (byte) 0x38, (byte) 0xfd, (byte) 0x5d, (byte) 0x9a, (byte) 0x53, (byte) 0xc5, - (byte) 0x05, (byte) 0x74, (byte) 0x8d, (byte) 0x1d, (byte) 0x6e, (byte) 0x83, - (byte) 0xaa, (byte) 0x71, (byte) 0xc5, (byte) 0xe1, (byte) 0xa1, (byte) 0xa6, - (byte) 0xf3, (byte) 0xee, (byte) 0x5f, (byte) 0x9e, (byte) 0x4f, (byte) 0xe8, - (byte) 0x15, (byte) 0xd5, (byte) 0xa9, (byte) 0x1b, (byte) 0xa6, (byte) 0x41, - (byte) 0x2b, (byte) 0x18, (byte) 0x13, (byte) 0x20, (byte) 0x9f, (byte) 0x6b, - (byte) 0xf1, (byte) 0xd8, (byte) 0xf4, (byte) 0x87, (byte) 0xfa, (byte) 0x80, - (byte) 0xec, (byte) 0x0e, (byte) 0xa4, (byte) 0x4b, (byte) 0x24, (byte) 0x03, - (byte) 0x14, (byte) 0x25, (byte) 0xf2, (byte) 0x20, (byte) 0xfc, (byte) 0x52, - (byte) 0xf9, (byte) 0xd6, (byte) 0x7a, (byte) 0x4a, (byte) 0x45, (byte) 0x33, - (byte) 0xec, (byte) 0xde, (byte) 0x3c, (byte) 0x5b, (byte) 0xf2, (byte) 0xdc, - (byte) 0x8e, (byte) 0xc6, (byte) 0xb3, (byte) 0x26, (byte) 0xd3, (byte) 0x68, - (byte) 0xa7, (byte) 0xd8, (byte) 0x3a, (byte) 0xde, (byte) 0xa9, (byte) 0x25, - (byte) 0x1d, (byte) 0x42, (byte) 0x75, (byte) 0x66, (byte) 0x16, (byte) 0x29, - (byte) 0xad, (byte) 0x09, (byte) 0x74, (byte) 0x41, (byte) 0xbb, (byte) 0x45, - (byte) 0x39, (byte) 0x04, (byte) 0x7a, (byte) 0x93, (byte) 0xad, (byte) 0x1c, - (byte) 0xa6, (byte) 0x38, (byte) 0xf4, (byte) 0xac, (byte) 0xca, (byte) 0x5a, - (byte) 0xab, (byte) 0x92, (byte) 0x76, (byte) 0x26, (byte) 0x3c, (byte) 0xeb, - (byte) 0xda, (byte) 0xfc, (byte) 0x25, (byte) 0x93, (byte) 0x23, (byte) 0x01, - (byte) 0xe2, (byte) 0xac, (byte) 0x5e, (byte) 0x4c, (byte) 0xb7, (byte) 0xbc, - (byte) 0x5b, (byte) 0xaa, (byte) 0x14, (byte) 0xe9, (byte) 0xbf, (byte) 0x2d, - (byte) 0x3a, (byte) 0xdc, (byte) 0x2f, (byte) 0x6b, (byte) 0x4d, (byte) 0x0e, - (byte) 0x0a, (byte) 0x82, (byte) 0x3c, (byte) 0xd9, (byte) 0x32, (byte) 0xc1, - (byte) 0xc4, (byte) 0xa2, (byte) 0x46, (byte) 0x71, (byte) 0x10, (byte) 0x54, - (byte) 0x1a, (byte) 0xa6, (byte) 0xaa, (byte) 0x64, (byte) 0xe7, (byte) 0xc2, - (byte) 0xae, (byte) 0xbc, (byte) 0x3d, (byte) 0xa4, (byte) 0xa8, (byte) 0xd1, - (byte) 0xb7, (byte) 0x27, (byte) 0xef, (byte) 0x5f, (byte) 0xe7, (byte) 0xa7, - (byte) 0x5d, (byte) 0xa0, (byte) 0xcd, (byte) 0x57, (byte) 0xf1, (byte) 0xe0, - (byte) 0xd8, (byte) 0x42, (byte) 0x10, (byte) 0x77, (byte) 0xc3, (byte) 0xa7, - (byte) 0x1e, (byte) 0x0c, (byte) 0x37, (byte) 0x16, (byte) 0x11, (byte) 0x94, - (byte) 0x21, (byte) 0xf2, (byte) 0xca, (byte) 0x60, (byte) 0xce, (byte) 0xca, - (byte) 0x59, (byte) 0xf9, (byte) 0xe5, (byte) 0xe4 - }; + private static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA1 = + new byte[] {(byte) 0x25, (byte) 0x9f, (byte) 0xc3, (byte) 0x69, (byte) 0xbc, + (byte) 0x3f, (byte) 0xe7, (byte) 0x9e, (byte) 0x76, (byte) 0xef, (byte) 0x6c, + (byte) 0xd2, (byte) 0x2b, (byte) 0x7b, (byte) 0xf0, (byte) 0xeb, (byte) 0xc2, + (byte) 0x28, (byte) 0x40, (byte) 0x4e, (byte) 0x9b, (byte) 0x2a, (byte) 0x4e, + (byte) 0xa4, (byte) 0x79, (byte) 0x66, (byte) 0xf1, (byte) 0x10, (byte) 0x96, + (byte) 0x8c, (byte) 0x58, (byte) 0x92, (byte) 0xb7, (byte) 0x70, (byte) 0xed, + (byte) 0x3a, (byte) 0xe0, (byte) 0x99, (byte) 0xd1, (byte) 0x80, (byte) 0x4b, + (byte) 0x53, (byte) 0x70, (byte) 0x9b, (byte) 0x51, (byte) 0xbf, (byte) 0xc1, + (byte) 0x3a, (byte) 0x70, (byte) 0xc5, (byte) 0x79, (byte) 0x21, (byte) 0x6e, + (byte) 0xb3, (byte) 0xf7, (byte) 0xa9, (byte) 0xe6, (byte) 0xcb, (byte) 0x70, + (byte) 0xe4, (byte) 0xf3, (byte) 0x4f, (byte) 0x45, (byte) 0xcf, (byte) 0xb7, + (byte) 0x2b, (byte) 0x38, (byte) 0xfd, (byte) 0x5d, (byte) 0x9a, (byte) 0x53, + (byte) 0xc5, (byte) 0x05, (byte) 0x74, (byte) 0x8d, (byte) 0x1d, (byte) 0x6e, + (byte) 0x83, (byte) 0xaa, (byte) 0x71, (byte) 0xc5, (byte) 0xe1, (byte) 0xa1, + (byte) 0xa6, (byte) 0xf3, (byte) 0xee, (byte) 0x5f, (byte) 0x9e, (byte) 0x4f, + (byte) 0xe8, (byte) 0x15, (byte) 0xd5, (byte) 0xa9, (byte) 0x1b, (byte) 0xa6, + (byte) 0x41, (byte) 0x2b, (byte) 0x18, (byte) 0x13, (byte) 0x20, (byte) 0x9f, + (byte) 0x6b, (byte) 0xf1, (byte) 0xd8, (byte) 0xf4, (byte) 0x87, (byte) 0xfa, + (byte) 0x80, (byte) 0xec, (byte) 0x0e, (byte) 0xa4, (byte) 0x4b, (byte) 0x24, + (byte) 0x03, (byte) 0x14, (byte) 0x25, (byte) 0xf2, (byte) 0x20, (byte) 0xfc, + (byte) 0x52, (byte) 0xf9, (byte) 0xd6, (byte) 0x7a, (byte) 0x4a, (byte) 0x45, + (byte) 0x33, (byte) 0xec, (byte) 0xde, (byte) 0x3c, (byte) 0x5b, (byte) 0xf2, + (byte) 0xdc, (byte) 0x8e, (byte) 0xc6, (byte) 0xb3, (byte) 0x26, (byte) 0xd3, + (byte) 0x68, (byte) 0xa7, (byte) 0xd8, (byte) 0x3a, (byte) 0xde, (byte) 0xa9, + (byte) 0x25, (byte) 0x1d, (byte) 0x42, (byte) 0x75, (byte) 0x66, (byte) 0x16, + (byte) 0x29, (byte) 0xad, (byte) 0x09, (byte) 0x74, (byte) 0x41, (byte) 0xbb, + (byte) 0x45, (byte) 0x39, (byte) 0x04, (byte) 0x7a, (byte) 0x93, (byte) 0xad, + (byte) 0x1c, (byte) 0xa6, (byte) 0x38, (byte) 0xf4, (byte) 0xac, (byte) 0xca, + (byte) 0x5a, (byte) 0xab, (byte) 0x92, (byte) 0x76, (byte) 0x26, (byte) 0x3c, + (byte) 0xeb, (byte) 0xda, (byte) 0xfc, (byte) 0x25, (byte) 0x93, (byte) 0x23, + (byte) 0x01, (byte) 0xe2, (byte) 0xac, (byte) 0x5e, (byte) 0x4c, (byte) 0xb7, + (byte) 0xbc, (byte) 0x5b, (byte) 0xaa, (byte) 0x14, (byte) 0xe9, (byte) 0xbf, + (byte) 0x2d, (byte) 0x3a, (byte) 0xdc, (byte) 0x2f, (byte) 0x6b, (byte) 0x4d, + (byte) 0x0e, (byte) 0x0a, (byte) 0x82, (byte) 0x3c, (byte) 0xd9, (byte) 0x32, + (byte) 0xc1, (byte) 0xc4, (byte) 0xa2, (byte) 0x46, (byte) 0x71, (byte) 0x10, + (byte) 0x54, (byte) 0x1a, (byte) 0xa6, (byte) 0xaa, (byte) 0x64, (byte) 0xe7, + (byte) 0xc2, (byte) 0xae, (byte) 0xbc, (byte) 0x3d, (byte) 0xa4, (byte) 0xa8, + (byte) 0xd1, (byte) 0xb7, (byte) 0x27, (byte) 0xef, (byte) 0x5f, (byte) 0xe7, + (byte) 0xa7, (byte) 0x5d, (byte) 0xa0, (byte) 0xcd, (byte) 0x57, (byte) 0xf1, + (byte) 0xe0, (byte) 0xd8, (byte) 0x42, (byte) 0x10, (byte) 0x77, (byte) 0xc3, + (byte) 0xa7, (byte) 0x1e, (byte) 0x0c, (byte) 0x37, (byte) 0x16, (byte) 0x11, + (byte) 0x94, (byte) 0x21, (byte) 0xf2, (byte) 0xca, (byte) 0x60, (byte) 0xce, + (byte) 0xca, (byte) 0x59, (byte) 0xf9, (byte) 0xe5, (byte) 0xe4}; /* * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha1 -pkeyopt rsa_oaep_label:010203FFA00A | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA1_LABEL = new byte[] { - (byte) 0x80, (byte) 0xb1, (byte) 0xf2, (byte) 0xc2, (byte) 0x03, (byte) 0xc5, - (byte) 0xdf, (byte) 0xbd, (byte) 0xed, (byte) 0xfe, (byte) 0xe6, (byte) 0xff, - (byte) 0xd3, (byte) 0x38, (byte) 0x1e, (byte) 0x6d, (byte) 0xae, (byte) 0x47, - (byte) 0xfe, (byte) 0x19, (byte) 0xf9, (byte) 0x8c, (byte) 0xf1, (byte) 0x4d, - (byte) 0x18, (byte) 0x2b, (byte) 0x7e, (byte) 0x8e, (byte) 0x47, (byte) 0x39, - (byte) 0xa8, (byte) 0x04, (byte) 0xc4, (byte) 0x7d, (byte) 0x56, (byte) 0x03, - (byte) 0x15, (byte) 0x92, (byte) 0x18, (byte) 0xde, (byte) 0x56, (byte) 0xb3, - (byte) 0x01, (byte) 0x93, (byte) 0x16, (byte) 0xe3, (byte) 0xfa, (byte) 0xaa, - (byte) 0xf3, (byte) 0x73, (byte) 0x39, (byte) 0x26, (byte) 0xfb, (byte) 0xb0, - (byte) 0x18, (byte) 0x20, (byte) 0xdb, (byte) 0xa1, (byte) 0xbf, (byte) 0x31, - (byte) 0x22, (byte) 0xc8, (byte) 0x1d, (byte) 0xdb, (byte) 0xa0, (byte) 0x5a, - (byte) 0x22, (byte) 0xcd, (byte) 0x09, (byte) 0xb3, (byte) 0xcb, (byte) 0xa2, - (byte) 0x46, (byte) 0x14, (byte) 0x35, (byte) 0x66, (byte) 0xe8, (byte) 0xb8, - (byte) 0x07, (byte) 0x23, (byte) 0xc5, (byte) 0xae, (byte) 0xe6, (byte) 0xf1, - (byte) 0x7a, (byte) 0x8f, (byte) 0x5c, (byte) 0x44, (byte) 0x34, (byte) 0xbf, - (byte) 0xd6, (byte) 0xf8, (byte) 0x0c, (byte) 0xc7, (byte) 0x8d, (byte) 0xcd, - (byte) 0x23, (byte) 0x84, (byte) 0xbe, (byte) 0x9b, (byte) 0xbf, (byte) 0x9a, - (byte) 0x70, (byte) 0x0f, (byte) 0x18, (byte) 0xc0, (byte) 0x6f, (byte) 0x23, - (byte) 0x67, (byte) 0xf8, (byte) 0xbb, (byte) 0xce, (byte) 0xc2, (byte) 0x47, - (byte) 0x82, (byte) 0xa0, (byte) 0xa5, (byte) 0x60, (byte) 0xcd, (byte) 0x25, - (byte) 0xa5, (byte) 0x4b, (byte) 0xe4, (byte) 0x06, (byte) 0x7f, (byte) 0x46, - (byte) 0x62, (byte) 0x86, (byte) 0x94, (byte) 0xbc, (byte) 0x7f, (byte) 0xb0, - (byte) 0x2e, (byte) 0xc1, (byte) 0x8c, (byte) 0x6c, (byte) 0x58, (byte) 0x05, - (byte) 0x6f, (byte) 0x35, (byte) 0x76, (byte) 0xd3, (byte) 0xdf, (byte) 0xc0, - (byte) 0xdd, (byte) 0x66, (byte) 0xbe, (byte) 0xa1, (byte) 0x7e, (byte) 0x52, - (byte) 0xed, (byte) 0x81, (byte) 0x0e, (byte) 0x2d, (byte) 0x5b, (byte) 0x2b, - (byte) 0xe3, (byte) 0x52, (byte) 0x0e, (byte) 0x56, (byte) 0x9b, (byte) 0x05, - (byte) 0x72, (byte) 0xa8, (byte) 0xc8, (byte) 0x57, (byte) 0x22, (byte) 0x67, - (byte) 0x0e, (byte) 0x5f, (byte) 0x01, (byte) 0xf2, (byte) 0x69, (byte) 0x66, - (byte) 0x6a, (byte) 0x47, (byte) 0x4f, (byte) 0x78, (byte) 0xb3, (byte) 0x1e, - (byte) 0x7d, (byte) 0xce, (byte) 0xb3, (byte) 0x35, (byte) 0xdf, (byte) 0x23, - (byte) 0xac, (byte) 0xf8, (byte) 0x88, (byte) 0xa1, (byte) 0xde, (byte) 0x38, - (byte) 0x96, (byte) 0xfd, (byte) 0xa2, (byte) 0x5d, (byte) 0x09, (byte) 0x52, - (byte) 0x11, (byte) 0x2b, (byte) 0x21, (byte) 0xf0, (byte) 0x0d, (byte) 0x4c, - (byte) 0x15, (byte) 0xc3, (byte) 0x88, (byte) 0x2b, (byte) 0xf6, (byte) 0x2b, - (byte) 0xe3, (byte) 0xfd, (byte) 0x52, (byte) 0xf0, (byte) 0x09, (byte) 0x5c, - (byte) 0x4f, (byte) 0x5b, (byte) 0x8b, (byte) 0x84, (byte) 0x71, (byte) 0x72, - (byte) 0x8d, (byte) 0xaa, (byte) 0x6c, (byte) 0x55, (byte) 0xba, (byte) 0xe7, - (byte) 0x9c, (byte) 0xba, (byte) 0xbf, (byte) 0xf4, (byte) 0x09, (byte) 0x0a, - (byte) 0x60, (byte) 0xec, (byte) 0x53, (byte) 0xa4, (byte) 0x01, (byte) 0xa5, - (byte) 0xf2, (byte) 0x58, (byte) 0xab, (byte) 0x95, (byte) 0x68, (byte) 0x79, - (byte) 0x0b, (byte) 0xc3, (byte) 0xc4, (byte) 0x00, (byte) 0x68, (byte) 0x19, - (byte) 0xca, (byte) 0x07, (byte) 0x0d, (byte) 0x32 - }; + private static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA1_LABEL = + new byte[] {(byte) 0x80, (byte) 0xb1, (byte) 0xf2, (byte) 0xc2, (byte) 0x03, + (byte) 0xc5, (byte) 0xdf, (byte) 0xbd, (byte) 0xed, (byte) 0xfe, (byte) 0xe6, + (byte) 0xff, (byte) 0xd3, (byte) 0x38, (byte) 0x1e, (byte) 0x6d, (byte) 0xae, + (byte) 0x47, (byte) 0xfe, (byte) 0x19, (byte) 0xf9, (byte) 0x8c, (byte) 0xf1, + (byte) 0x4d, (byte) 0x18, (byte) 0x2b, (byte) 0x7e, (byte) 0x8e, (byte) 0x47, + (byte) 0x39, (byte) 0xa8, (byte) 0x04, (byte) 0xc4, (byte) 0x7d, (byte) 0x56, + (byte) 0x03, (byte) 0x15, (byte) 0x92, (byte) 0x18, (byte) 0xde, (byte) 0x56, + (byte) 0xb3, (byte) 0x01, (byte) 0x93, (byte) 0x16, (byte) 0xe3, (byte) 0xfa, + (byte) 0xaa, (byte) 0xf3, (byte) 0x73, (byte) 0x39, (byte) 0x26, (byte) 0xfb, + (byte) 0xb0, (byte) 0x18, (byte) 0x20, (byte) 0xdb, (byte) 0xa1, (byte) 0xbf, + (byte) 0x31, (byte) 0x22, (byte) 0xc8, (byte) 0x1d, (byte) 0xdb, (byte) 0xa0, + (byte) 0x5a, (byte) 0x22, (byte) 0xcd, (byte) 0x09, (byte) 0xb3, (byte) 0xcb, + (byte) 0xa2, (byte) 0x46, (byte) 0x14, (byte) 0x35, (byte) 0x66, (byte) 0xe8, + (byte) 0xb8, (byte) 0x07, (byte) 0x23, (byte) 0xc5, (byte) 0xae, (byte) 0xe6, + (byte) 0xf1, (byte) 0x7a, (byte) 0x8f, (byte) 0x5c, (byte) 0x44, (byte) 0x34, + (byte) 0xbf, (byte) 0xd6, (byte) 0xf8, (byte) 0x0c, (byte) 0xc7, (byte) 0x8d, + (byte) 0xcd, (byte) 0x23, (byte) 0x84, (byte) 0xbe, (byte) 0x9b, (byte) 0xbf, + (byte) 0x9a, (byte) 0x70, (byte) 0x0f, (byte) 0x18, (byte) 0xc0, (byte) 0x6f, + (byte) 0x23, (byte) 0x67, (byte) 0xf8, (byte) 0xbb, (byte) 0xce, (byte) 0xc2, + (byte) 0x47, (byte) 0x82, (byte) 0xa0, (byte) 0xa5, (byte) 0x60, (byte) 0xcd, + (byte) 0x25, (byte) 0xa5, (byte) 0x4b, (byte) 0xe4, (byte) 0x06, (byte) 0x7f, + (byte) 0x46, (byte) 0x62, (byte) 0x86, (byte) 0x94, (byte) 0xbc, (byte) 0x7f, + (byte) 0xb0, (byte) 0x2e, (byte) 0xc1, (byte) 0x8c, (byte) 0x6c, (byte) 0x58, + (byte) 0x05, (byte) 0x6f, (byte) 0x35, (byte) 0x76, (byte) 0xd3, (byte) 0xdf, + (byte) 0xc0, (byte) 0xdd, (byte) 0x66, (byte) 0xbe, (byte) 0xa1, (byte) 0x7e, + (byte) 0x52, (byte) 0xed, (byte) 0x81, (byte) 0x0e, (byte) 0x2d, (byte) 0x5b, + (byte) 0x2b, (byte) 0xe3, (byte) 0x52, (byte) 0x0e, (byte) 0x56, (byte) 0x9b, + (byte) 0x05, (byte) 0x72, (byte) 0xa8, (byte) 0xc8, (byte) 0x57, (byte) 0x22, + (byte) 0x67, (byte) 0x0e, (byte) 0x5f, (byte) 0x01, (byte) 0xf2, (byte) 0x69, + (byte) 0x66, (byte) 0x6a, (byte) 0x47, (byte) 0x4f, (byte) 0x78, (byte) 0xb3, + (byte) 0x1e, (byte) 0x7d, (byte) 0xce, (byte) 0xb3, (byte) 0x35, (byte) 0xdf, + (byte) 0x23, (byte) 0xac, (byte) 0xf8, (byte) 0x88, (byte) 0xa1, (byte) 0xde, + (byte) 0x38, (byte) 0x96, (byte) 0xfd, (byte) 0xa2, (byte) 0x5d, (byte) 0x09, + (byte) 0x52, (byte) 0x11, (byte) 0x2b, (byte) 0x21, (byte) 0xf0, (byte) 0x0d, + (byte) 0x4c, (byte) 0x15, (byte) 0xc3, (byte) 0x88, (byte) 0x2b, (byte) 0xf6, + (byte) 0x2b, (byte) 0xe3, (byte) 0xfd, (byte) 0x52, (byte) 0xf0, (byte) 0x09, + (byte) 0x5c, (byte) 0x4f, (byte) 0x5b, (byte) 0x8b, (byte) 0x84, (byte) 0x71, + (byte) 0x72, (byte) 0x8d, (byte) 0xaa, (byte) 0x6c, (byte) 0x55, (byte) 0xba, + (byte) 0xe7, (byte) 0x9c, (byte) 0xba, (byte) 0xbf, (byte) 0xf4, (byte) 0x09, + (byte) 0x0a, (byte) 0x60, (byte) 0xec, (byte) 0x53, (byte) 0xa4, (byte) 0x01, + (byte) 0xa5, (byte) 0xf2, (byte) 0x58, (byte) 0xab, (byte) 0x95, (byte) 0x68, + (byte) 0x79, (byte) 0x0b, (byte) 0xc3, (byte) 0xc4, (byte) 0x00, (byte) 0x68, + (byte) 0x19, (byte) 0xca, (byte) 0x07, (byte) 0x0d, (byte) 0x32}; /* * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey rsakey.pem \ * -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha224 -pkeyopt rsa_mgf1_md:sha224 \ * | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA224_MGF1_SHA224 = new byte[] { - (byte) 0xae, (byte) 0xdd, (byte) 0xe6, (byte) 0xab, (byte) 0x00, (byte) 0xd6, - (byte) 0x1e, (byte) 0x7e, (byte) 0x85, (byte) 0x63, (byte) 0xab, (byte) 0x51, - (byte) 0x79, (byte) 0x92, (byte) 0xf1, (byte) 0xb9, (byte) 0x4f, (byte) 0x23, - (byte) 0xae, (byte) 0xf7, (byte) 0x1b, (byte) 0x5f, (byte) 0x10, (byte) 0x5b, - (byte) 0xa5, (byte) 0x15, (byte) 0x87, (byte) 0xa3, (byte) 0xbb, (byte) 0x26, - (byte) 0xfe, (byte) 0x7f, (byte) 0xc0, (byte) 0xa3, (byte) 0x67, (byte) 0x95, - (byte) 0xda, (byte) 0xc4, (byte) 0x6f, (byte) 0x6e, (byte) 0x08, (byte) 0x23, - (byte) 0x28, (byte) 0x0b, (byte) 0xdd, (byte) 0x29, (byte) 0x29, (byte) 0xdc, - (byte) 0xb0, (byte) 0x35, (byte) 0x16, (byte) 0x2e, (byte) 0x0f, (byte) 0xb9, - (byte) 0x1d, (byte) 0x90, (byte) 0x27, (byte) 0x68, (byte) 0xc7, (byte) 0x92, - (byte) 0x52, (byte) 0x8a, (byte) 0x1d, (byte) 0x48, (byte) 0x6a, (byte) 0x7d, - (byte) 0x0b, (byte) 0xf6, (byte) 0x35, (byte) 0xca, (byte) 0xe1, (byte) 0x57, - (byte) 0xdd, (byte) 0x36, (byte) 0x3b, (byte) 0x51, (byte) 0x45, (byte) 0x77, - (byte) 0x28, (byte) 0x4f, (byte) 0x98, (byte) 0xc0, (byte) 0xe0, (byte) 0xa7, - (byte) 0x51, (byte) 0x98, (byte) 0x84, (byte) 0x7a, (byte) 0x29, (byte) 0x05, - (byte) 0x9f, (byte) 0x60, (byte) 0x66, (byte) 0xf6, (byte) 0x83, (byte) 0xcd, - (byte) 0x03, (byte) 0x3e, (byte) 0x82, (byte) 0x0f, (byte) 0x57, (byte) 0x4b, - (byte) 0x27, (byte) 0x14, (byte) 0xf6, (byte) 0xc8, (byte) 0x5b, (byte) 0xed, - (byte) 0xc3, (byte) 0x77, (byte) 0x6f, (byte) 0xec, (byte) 0x0e, (byte) 0xae, - (byte) 0x59, (byte) 0xbe, (byte) 0x68, (byte) 0x76, (byte) 0x16, (byte) 0x17, - (byte) 0x77, (byte) 0xe2, (byte) 0xbd, (byte) 0xe0, (byte) 0x5a, (byte) 0x14, - (byte) 0xd9, (byte) 0xf4, (byte) 0x3f, (byte) 0x50, (byte) 0x31, (byte) 0xf0, - (byte) 0x0c, (byte) 0x82, (byte) 0x6c, (byte) 0xcc, (byte) 0x81, (byte) 0x84, - (byte) 0x3e, (byte) 0x63, (byte) 0x93, (byte) 0xe7, (byte) 0x12, (byte) 0x2d, - (byte) 0xc9, (byte) 0xa3, (byte) 0xe3, (byte) 0xce, (byte) 0xfd, (byte) 0xc7, - (byte) 0xe1, (byte) 0xef, (byte) 0xa4, (byte) 0x16, (byte) 0x5c, (byte) 0x60, - (byte) 0xb1, (byte) 0x80, (byte) 0x31, (byte) 0x15, (byte) 0x5c, (byte) 0x35, - (byte) 0x25, (byte) 0x0b, (byte) 0x89, (byte) 0xe4, (byte) 0x56, (byte) 0x74, - (byte) 0x8b, (byte) 0xaf, (byte) 0x8e, (byte) 0xe9, (byte) 0xe2, (byte) 0x37, - (byte) 0x17, (byte) 0xe6, (byte) 0x7b, (byte) 0x78, (byte) 0xd8, (byte) 0x2c, - (byte) 0x27, (byte) 0x52, (byte) 0x21, (byte) 0x96, (byte) 0xa0, (byte) 0x92, - (byte) 0x95, (byte) 0x64, (byte) 0xc3, (byte) 0x7f, (byte) 0x45, (byte) 0xfc, - (byte) 0x3d, (byte) 0x48, (byte) 0x4a, (byte) 0xd5, (byte) 0xa4, (byte) 0x0a, - (byte) 0x57, (byte) 0x07, (byte) 0x57, (byte) 0x95, (byte) 0x9f, (byte) 0x2f, - (byte) 0x75, (byte) 0x32, (byte) 0x2a, (byte) 0x4d, (byte) 0x64, (byte) 0xbd, - (byte) 0xb1, (byte) 0xe0, (byte) 0x46, (byte) 0x4f, (byte) 0xe8, (byte) 0x6c, - (byte) 0x4b, (byte) 0x77, (byte) 0xcc, (byte) 0x36, (byte) 0x87, (byte) 0x05, - (byte) 0x56, (byte) 0x9a, (byte) 0xe4, (byte) 0x2c, (byte) 0x43, (byte) 0xfd, - (byte) 0x34, (byte) 0x97, (byte) 0xf8, (byte) 0xd7, (byte) 0x91, (byte) 0xff, - (byte) 0x56, (byte) 0x86, (byte) 0x17, (byte) 0x49, (byte) 0x0a, (byte) 0x52, - (byte) 0xfb, (byte) 0xe5, (byte) 0x49, (byte) 0xdf, (byte) 0xc1, (byte) 0x28, - (byte) 0x9d, (byte) 0x85, (byte) 0x66, (byte) 0x9d, (byte) 0x1d, (byte) 0xa4, - (byte) 0x7e, (byte) 0x9a, (byte) 0x5b, (byte) 0x30 - }; + private static final byte[] RSA_Vector2_OAEP_SHA224_MGF1_SHA224 = + new byte[] {(byte) 0xae, (byte) 0xdd, (byte) 0xe6, (byte) 0xab, (byte) 0x00, + (byte) 0xd6, (byte) 0x1e, (byte) 0x7e, (byte) 0x85, (byte) 0x63, (byte) 0xab, + (byte) 0x51, (byte) 0x79, (byte) 0x92, (byte) 0xf1, (byte) 0xb9, (byte) 0x4f, + (byte) 0x23, (byte) 0xae, (byte) 0xf7, (byte) 0x1b, (byte) 0x5f, (byte) 0x10, + (byte) 0x5b, (byte) 0xa5, (byte) 0x15, (byte) 0x87, (byte) 0xa3, (byte) 0xbb, + (byte) 0x26, (byte) 0xfe, (byte) 0x7f, (byte) 0xc0, (byte) 0xa3, (byte) 0x67, + (byte) 0x95, (byte) 0xda, (byte) 0xc4, (byte) 0x6f, (byte) 0x6e, (byte) 0x08, + (byte) 0x23, (byte) 0x28, (byte) 0x0b, (byte) 0xdd, (byte) 0x29, (byte) 0x29, + (byte) 0xdc, (byte) 0xb0, (byte) 0x35, (byte) 0x16, (byte) 0x2e, (byte) 0x0f, + (byte) 0xb9, (byte) 0x1d, (byte) 0x90, (byte) 0x27, (byte) 0x68, (byte) 0xc7, + (byte) 0x92, (byte) 0x52, (byte) 0x8a, (byte) 0x1d, (byte) 0x48, (byte) 0x6a, + (byte) 0x7d, (byte) 0x0b, (byte) 0xf6, (byte) 0x35, (byte) 0xca, (byte) 0xe1, + (byte) 0x57, (byte) 0xdd, (byte) 0x36, (byte) 0x3b, (byte) 0x51, (byte) 0x45, + (byte) 0x77, (byte) 0x28, (byte) 0x4f, (byte) 0x98, (byte) 0xc0, (byte) 0xe0, + (byte) 0xa7, (byte) 0x51, (byte) 0x98, (byte) 0x84, (byte) 0x7a, (byte) 0x29, + (byte) 0x05, (byte) 0x9f, (byte) 0x60, (byte) 0x66, (byte) 0xf6, (byte) 0x83, + (byte) 0xcd, (byte) 0x03, (byte) 0x3e, (byte) 0x82, (byte) 0x0f, (byte) 0x57, + (byte) 0x4b, (byte) 0x27, (byte) 0x14, (byte) 0xf6, (byte) 0xc8, (byte) 0x5b, + (byte) 0xed, (byte) 0xc3, (byte) 0x77, (byte) 0x6f, (byte) 0xec, (byte) 0x0e, + (byte) 0xae, (byte) 0x59, (byte) 0xbe, (byte) 0x68, (byte) 0x76, (byte) 0x16, + (byte) 0x17, (byte) 0x77, (byte) 0xe2, (byte) 0xbd, (byte) 0xe0, (byte) 0x5a, + (byte) 0x14, (byte) 0xd9, (byte) 0xf4, (byte) 0x3f, (byte) 0x50, (byte) 0x31, + (byte) 0xf0, (byte) 0x0c, (byte) 0x82, (byte) 0x6c, (byte) 0xcc, (byte) 0x81, + (byte) 0x84, (byte) 0x3e, (byte) 0x63, (byte) 0x93, (byte) 0xe7, (byte) 0x12, + (byte) 0x2d, (byte) 0xc9, (byte) 0xa3, (byte) 0xe3, (byte) 0xce, (byte) 0xfd, + (byte) 0xc7, (byte) 0xe1, (byte) 0xef, (byte) 0xa4, (byte) 0x16, (byte) 0x5c, + (byte) 0x60, (byte) 0xb1, (byte) 0x80, (byte) 0x31, (byte) 0x15, (byte) 0x5c, + (byte) 0x35, (byte) 0x25, (byte) 0x0b, (byte) 0x89, (byte) 0xe4, (byte) 0x56, + (byte) 0x74, (byte) 0x8b, (byte) 0xaf, (byte) 0x8e, (byte) 0xe9, (byte) 0xe2, + (byte) 0x37, (byte) 0x17, (byte) 0xe6, (byte) 0x7b, (byte) 0x78, (byte) 0xd8, + (byte) 0x2c, (byte) 0x27, (byte) 0x52, (byte) 0x21, (byte) 0x96, (byte) 0xa0, + (byte) 0x92, (byte) 0x95, (byte) 0x64, (byte) 0xc3, (byte) 0x7f, (byte) 0x45, + (byte) 0xfc, (byte) 0x3d, (byte) 0x48, (byte) 0x4a, (byte) 0xd5, (byte) 0xa4, + (byte) 0x0a, (byte) 0x57, (byte) 0x07, (byte) 0x57, (byte) 0x95, (byte) 0x9f, + (byte) 0x2f, (byte) 0x75, (byte) 0x32, (byte) 0x2a, (byte) 0x4d, (byte) 0x64, + (byte) 0xbd, (byte) 0xb1, (byte) 0xe0, (byte) 0x46, (byte) 0x4f, (byte) 0xe8, + (byte) 0x6c, (byte) 0x4b, (byte) 0x77, (byte) 0xcc, (byte) 0x36, (byte) 0x87, + (byte) 0x05, (byte) 0x56, (byte) 0x9a, (byte) 0xe4, (byte) 0x2c, (byte) 0x43, + (byte) 0xfd, (byte) 0x34, (byte) 0x97, (byte) 0xf8, (byte) 0xd7, (byte) 0x91, + (byte) 0xff, (byte) 0x56, (byte) 0x86, (byte) 0x17, (byte) 0x49, (byte) 0x0a, + (byte) 0x52, (byte) 0xfb, (byte) 0xe5, (byte) 0x49, (byte) 0xdf, (byte) 0xc1, + (byte) 0x28, (byte) 0x9d, (byte) 0x85, (byte) 0x66, (byte) 0x9d, (byte) 0x1d, + (byte) 0xa4, (byte) 0x7e, (byte) 0x9a, (byte) 0x5b, (byte) 0x30}; /* * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt \ * -pkeyopt rsa_padding_mode:oaep -pkey rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 \ * | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA256 = new byte[] { - (byte) 0x6a, (byte) 0x2b, (byte) 0xb2, (byte) 0xa3, (byte) 0x26, (byte) 0xa6, - (byte) 0x7a, (byte) 0x4a, (byte) 0x1f, (byte) 0xe5, (byte) 0xc8, (byte) 0x94, - (byte) 0x11, (byte) 0x1a, (byte) 0x92, (byte) 0x07, (byte) 0x0a, (byte) 0xf4, - (byte) 0x07, (byte) 0x0b, (byte) 0xd6, (byte) 0x37, (byte) 0xa5, (byte) 0x5d, - (byte) 0x16, (byte) 0x0a, (byte) 0x7d, (byte) 0x13, (byte) 0x27, (byte) 0x32, - (byte) 0x5a, (byte) 0xc3, (byte) 0x0d, (byte) 0x7a, (byte) 0x54, (byte) 0xfe, - (byte) 0x02, (byte) 0x28, (byte) 0xc6, (byte) 0x8e, (byte) 0x32, (byte) 0x7b, - (byte) 0x0a, (byte) 0x52, (byte) 0xf8, (byte) 0xe6, (byte) 0xab, (byte) 0x16, - (byte) 0x77, (byte) 0x7c, (byte) 0x53, (byte) 0xcd, (byte) 0xb0, (byte) 0xb6, - (byte) 0x90, (byte) 0xce, (byte) 0x7b, (byte) 0xa5, (byte) 0xdb, (byte) 0xab, - (byte) 0xfd, (byte) 0xf5, (byte) 0xbb, (byte) 0x49, (byte) 0x63, (byte) 0xb7, - (byte) 0xa8, (byte) 0x3e, (byte) 0x53, (byte) 0xf1, (byte) 0x00, (byte) 0x4d, - (byte) 0x72, (byte) 0x15, (byte) 0x34, (byte) 0xa8, (byte) 0x5b, (byte) 0x00, - (byte) 0x01, (byte) 0x75, (byte) 0xdc, (byte) 0xb6, (byte) 0xd1, (byte) 0xdf, - (byte) 0xcb, (byte) 0x93, (byte) 0xf3, (byte) 0x31, (byte) 0x04, (byte) 0x7e, - (byte) 0x48, (byte) 0x3e, (byte) 0xc9, (byte) 0xaf, (byte) 0xd7, (byte) 0xbd, - (byte) 0x9e, (byte) 0x73, (byte) 0x01, (byte) 0x79, (byte) 0xf8, (byte) 0xdc, - (byte) 0x46, (byte) 0x31, (byte) 0x55, (byte) 0x83, (byte) 0x21, (byte) 0xd1, - (byte) 0x19, (byte) 0x0b, (byte) 0x57, (byte) 0xf1, (byte) 0x06, (byte) 0xb9, - (byte) 0x32, (byte) 0x0e, (byte) 0x9d, (byte) 0x38, (byte) 0x53, (byte) 0x94, - (byte) 0x96, (byte) 0xd4, (byte) 0x6d, (byte) 0x18, (byte) 0xe2, (byte) 0xe3, - (byte) 0xcd, (byte) 0xfa, (byte) 0xfe, (byte) 0xb3, (byte) 0xe3, (byte) 0x27, - (byte) 0xd7, (byte) 0x45, (byte) 0xe8, (byte) 0x46, (byte) 0x6b, (byte) 0x06, - (byte) 0x0f, (byte) 0x5e, (byte) 0x24, (byte) 0x02, (byte) 0xef, (byte) 0xa2, - (byte) 0x69, (byte) 0xe6, (byte) 0x15, (byte) 0xb3, (byte) 0x8f, (byte) 0x71, - (byte) 0x97, (byte) 0x39, (byte) 0xfb, (byte) 0x32, (byte) 0xe0, (byte) 0xe5, - (byte) 0xac, (byte) 0x46, (byte) 0xb4, (byte) 0xe7, (byte) 0x3d, (byte) 0x89, - (byte) 0xba, (byte) 0xd9, (byte) 0x4c, (byte) 0x25, (byte) 0x97, (byte) 0xef, - (byte) 0xe6, (byte) 0x17, (byte) 0x23, (byte) 0x4e, (byte) 0xc8, (byte) 0xdb, - (byte) 0x18, (byte) 0x9b, (byte) 0xba, (byte) 0xb5, (byte) 0x7e, (byte) 0x19, - (byte) 0x4d, (byte) 0x95, (byte) 0x7d, (byte) 0x60, (byte) 0x1b, (byte) 0xa7, - (byte) 0x06, (byte) 0x1e, (byte) 0x99, (byte) 0x4a, (byte) 0xf2, (byte) 0x82, - (byte) 0x71, (byte) 0x62, (byte) 0x41, (byte) 0xa4, (byte) 0xa7, (byte) 0xdb, - (byte) 0x88, (byte) 0xb0, (byte) 0x4a, (byte) 0xc7, (byte) 0x3b, (byte) 0xce, - (byte) 0x91, (byte) 0x4f, (byte) 0xc7, (byte) 0xca, (byte) 0x6f, (byte) 0x89, - (byte) 0xac, (byte) 0x1a, (byte) 0x36, (byte) 0x84, (byte) 0x0c, (byte) 0x97, - (byte) 0xa0, (byte) 0x1a, (byte) 0x08, (byte) 0x6f, (byte) 0x70, (byte) 0xf3, - (byte) 0x94, (byte) 0xa0, (byte) 0x0f, (byte) 0x44, (byte) 0xdd, (byte) 0x86, - (byte) 0x9d, (byte) 0x2c, (byte) 0xac, (byte) 0x43, (byte) 0xed, (byte) 0xb8, - (byte) 0xa1, (byte) 0x66, (byte) 0xf3, (byte) 0xd3, (byte) 0x5c, (byte) 0xe5, - (byte) 0xe2, (byte) 0x4c, (byte) 0x7e, (byte) 0xda, (byte) 0x20, (byte) 0xbd, - (byte) 0x5a, (byte) 0x75, (byte) 0x12, (byte) 0x31, (byte) 0x23, (byte) 0x02, - (byte) 0xb5, (byte) 0x1f, (byte) 0x38, (byte) 0x98 - }; + private static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA256 = + new byte[] {(byte) 0x6a, (byte) 0x2b, (byte) 0xb2, (byte) 0xa3, (byte) 0x26, + (byte) 0xa6, (byte) 0x7a, (byte) 0x4a, (byte) 0x1f, (byte) 0xe5, (byte) 0xc8, + (byte) 0x94, (byte) 0x11, (byte) 0x1a, (byte) 0x92, (byte) 0x07, (byte) 0x0a, + (byte) 0xf4, (byte) 0x07, (byte) 0x0b, (byte) 0xd6, (byte) 0x37, (byte) 0xa5, + (byte) 0x5d, (byte) 0x16, (byte) 0x0a, (byte) 0x7d, (byte) 0x13, (byte) 0x27, + (byte) 0x32, (byte) 0x5a, (byte) 0xc3, (byte) 0x0d, (byte) 0x7a, (byte) 0x54, + (byte) 0xfe, (byte) 0x02, (byte) 0x28, (byte) 0xc6, (byte) 0x8e, (byte) 0x32, + (byte) 0x7b, (byte) 0x0a, (byte) 0x52, (byte) 0xf8, (byte) 0xe6, (byte) 0xab, + (byte) 0x16, (byte) 0x77, (byte) 0x7c, (byte) 0x53, (byte) 0xcd, (byte) 0xb0, + (byte) 0xb6, (byte) 0x90, (byte) 0xce, (byte) 0x7b, (byte) 0xa5, (byte) 0xdb, + (byte) 0xab, (byte) 0xfd, (byte) 0xf5, (byte) 0xbb, (byte) 0x49, (byte) 0x63, + (byte) 0xb7, (byte) 0xa8, (byte) 0x3e, (byte) 0x53, (byte) 0xf1, (byte) 0x00, + (byte) 0x4d, (byte) 0x72, (byte) 0x15, (byte) 0x34, (byte) 0xa8, (byte) 0x5b, + (byte) 0x00, (byte) 0x01, (byte) 0x75, (byte) 0xdc, (byte) 0xb6, (byte) 0xd1, + (byte) 0xdf, (byte) 0xcb, (byte) 0x93, (byte) 0xf3, (byte) 0x31, (byte) 0x04, + (byte) 0x7e, (byte) 0x48, (byte) 0x3e, (byte) 0xc9, (byte) 0xaf, (byte) 0xd7, + (byte) 0xbd, (byte) 0x9e, (byte) 0x73, (byte) 0x01, (byte) 0x79, (byte) 0xf8, + (byte) 0xdc, (byte) 0x46, (byte) 0x31, (byte) 0x55, (byte) 0x83, (byte) 0x21, + (byte) 0xd1, (byte) 0x19, (byte) 0x0b, (byte) 0x57, (byte) 0xf1, (byte) 0x06, + (byte) 0xb9, (byte) 0x32, (byte) 0x0e, (byte) 0x9d, (byte) 0x38, (byte) 0x53, + (byte) 0x94, (byte) 0x96, (byte) 0xd4, (byte) 0x6d, (byte) 0x18, (byte) 0xe2, + (byte) 0xe3, (byte) 0xcd, (byte) 0xfa, (byte) 0xfe, (byte) 0xb3, (byte) 0xe3, + (byte) 0x27, (byte) 0xd7, (byte) 0x45, (byte) 0xe8, (byte) 0x46, (byte) 0x6b, + (byte) 0x06, (byte) 0x0f, (byte) 0x5e, (byte) 0x24, (byte) 0x02, (byte) 0xef, + (byte) 0xa2, (byte) 0x69, (byte) 0xe6, (byte) 0x15, (byte) 0xb3, (byte) 0x8f, + (byte) 0x71, (byte) 0x97, (byte) 0x39, (byte) 0xfb, (byte) 0x32, (byte) 0xe0, + (byte) 0xe5, (byte) 0xac, (byte) 0x46, (byte) 0xb4, (byte) 0xe7, (byte) 0x3d, + (byte) 0x89, (byte) 0xba, (byte) 0xd9, (byte) 0x4c, (byte) 0x25, (byte) 0x97, + (byte) 0xef, (byte) 0xe6, (byte) 0x17, (byte) 0x23, (byte) 0x4e, (byte) 0xc8, + (byte) 0xdb, (byte) 0x18, (byte) 0x9b, (byte) 0xba, (byte) 0xb5, (byte) 0x7e, + (byte) 0x19, (byte) 0x4d, (byte) 0x95, (byte) 0x7d, (byte) 0x60, (byte) 0x1b, + (byte) 0xa7, (byte) 0x06, (byte) 0x1e, (byte) 0x99, (byte) 0x4a, (byte) 0xf2, + (byte) 0x82, (byte) 0x71, (byte) 0x62, (byte) 0x41, (byte) 0xa4, (byte) 0xa7, + (byte) 0xdb, (byte) 0x88, (byte) 0xb0, (byte) 0x4a, (byte) 0xc7, (byte) 0x3b, + (byte) 0xce, (byte) 0x91, (byte) 0x4f, (byte) 0xc7, (byte) 0xca, (byte) 0x6f, + (byte) 0x89, (byte) 0xac, (byte) 0x1a, (byte) 0x36, (byte) 0x84, (byte) 0x0c, + (byte) 0x97, (byte) 0xa0, (byte) 0x1a, (byte) 0x08, (byte) 0x6f, (byte) 0x70, + (byte) 0xf3, (byte) 0x94, (byte) 0xa0, (byte) 0x0f, (byte) 0x44, (byte) 0xdd, + (byte) 0x86, (byte) 0x9d, (byte) 0x2c, (byte) 0xac, (byte) 0x43, (byte) 0xed, + (byte) 0xb8, (byte) 0xa1, (byte) 0x66, (byte) 0xf3, (byte) 0xd3, (byte) 0x5c, + (byte) 0xe5, (byte) 0xe2, (byte) 0x4c, (byte) 0x7e, (byte) 0xda, (byte) 0x20, + (byte) 0xbd, (byte) 0x5a, (byte) 0x75, (byte) 0x12, (byte) 0x31, (byte) 0x23, + (byte) 0x02, (byte) 0xb5, (byte) 0x1f, (byte) 0x38, (byte) 0x98}; /* * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt \ * -pkeyopt rsa_padding_mode:oaep -pkey rsa_oaep_md:sha384 -pkeyopt rsa_mgf1_md:sha384 \ * | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA384_MGF1_SHA384 = new byte[] { - (byte) 0xa1, (byte) 0xb3, (byte) 0x3b, (byte) 0x34, (byte) 0x69, (byte) 0x9e, - (byte) 0xd8, (byte) 0xa0, (byte) 0x37, (byte) 0x2c, (byte) 0xeb, (byte) 0xef, - (byte) 0xf2, (byte) 0xaf, (byte) 0xfa, (byte) 0x63, (byte) 0x5d, (byte) 0x88, - (byte) 0xac, (byte) 0x51, (byte) 0xd4, (byte) 0x7f, (byte) 0x85, (byte) 0xf0, - (byte) 0x5e, (byte) 0xb4, (byte) 0x81, (byte) 0x7c, (byte) 0x82, (byte) 0x4f, - (byte) 0x92, (byte) 0xf7, (byte) 0x77, (byte) 0x48, (byte) 0x4c, (byte) 0xb1, - (byte) 0x42, (byte) 0xb3, (byte) 0x0e, (byte) 0x94, (byte) 0xc8, (byte) 0x5a, - (byte) 0xae, (byte) 0xed, (byte) 0x8d, (byte) 0x51, (byte) 0x72, (byte) 0x6b, - (byte) 0xa9, (byte) 0xd4, (byte) 0x1e, (byte) 0xbe, (byte) 0x38, (byte) 0x2c, - (byte) 0xd0, (byte) 0x43, (byte) 0xae, (byte) 0xb4, (byte) 0x30, (byte) 0xa9, - (byte) 0x93, (byte) 0x47, (byte) 0xb5, (byte) 0x9d, (byte) 0x03, (byte) 0x92, - (byte) 0x25, (byte) 0x74, (byte) 0xed, (byte) 0xfa, (byte) 0xfe, (byte) 0xf1, - (byte) 0xba, (byte) 0x04, (byte) 0x3a, (byte) 0x4d, (byte) 0x6d, (byte) 0x9a, - (byte) 0x0d, (byte) 0x95, (byte) 0x02, (byte) 0xb0, (byte) 0xac, (byte) 0x77, - (byte) 0x11, (byte) 0x44, (byte) 0xeb, (byte) 0xd2, (byte) 0x02, (byte) 0x90, - (byte) 0xea, (byte) 0x2f, (byte) 0x68, (byte) 0x2a, (byte) 0x69, (byte) 0xcf, - (byte) 0x45, (byte) 0x34, (byte) 0xff, (byte) 0x00, (byte) 0xc6, (byte) 0x3c, - (byte) 0x0b, (byte) 0x2c, (byte) 0x5f, (byte) 0x8c, (byte) 0x2c, (byte) 0xbf, - (byte) 0xc2, (byte) 0x4b, (byte) 0x16, (byte) 0x07, (byte) 0x84, (byte) 0x74, - (byte) 0xf0, (byte) 0x7a, (byte) 0x01, (byte) 0x7e, (byte) 0x74, (byte) 0x01, - (byte) 0x88, (byte) 0xce, (byte) 0xda, (byte) 0xe4, (byte) 0x21, (byte) 0x89, - (byte) 0xfc, (byte) 0xac, (byte) 0x68, (byte) 0xdb, (byte) 0xfc, (byte) 0x5f, - (byte) 0x3f, (byte) 0x00, (byte) 0xd9, (byte) 0x32, (byte) 0x1d, (byte) 0xa5, - (byte) 0xec, (byte) 0x72, (byte) 0x46, (byte) 0x23, (byte) 0xe5, (byte) 0x7f, - (byte) 0x49, (byte) 0x0e, (byte) 0x3e, (byte) 0xf2, (byte) 0x2b, (byte) 0x16, - (byte) 0x52, (byte) 0x9f, (byte) 0x9d, (byte) 0x0c, (byte) 0xfe, (byte) 0xab, - (byte) 0xdd, (byte) 0x77, (byte) 0x77, (byte) 0x94, (byte) 0xa4, (byte) 0x92, - (byte) 0xa2, (byte) 0x41, (byte) 0x0d, (byte) 0x4b, (byte) 0x57, (byte) 0x80, - (byte) 0xd6, (byte) 0x74, (byte) 0x63, (byte) 0xd5, (byte) 0xbf, (byte) 0x5c, - (byte) 0xa0, (byte) 0xda, (byte) 0x3c, (byte) 0xe6, (byte) 0xbf, (byte) 0xa4, - (byte) 0xc3, (byte) 0xfb, (byte) 0x46, (byte) 0x3b, (byte) 0x73, (byte) 0x30, - (byte) 0x4b, (byte) 0x57, (byte) 0x27, (byte) 0x0c, (byte) 0x81, (byte) 0xde, - (byte) 0x8a, (byte) 0x01, (byte) 0xe5, (byte) 0x7e, (byte) 0xe0, (byte) 0x16, - (byte) 0x11, (byte) 0x24, (byte) 0x34, (byte) 0x22, (byte) 0x01, (byte) 0x9f, - (byte) 0xe6, (byte) 0xa9, (byte) 0xfb, (byte) 0xad, (byte) 0x55, (byte) 0x17, - (byte) 0x2a, (byte) 0x92, (byte) 0x87, (byte) 0xf3, (byte) 0x72, (byte) 0xc9, - (byte) 0x3d, (byte) 0xc9, (byte) 0x2e, (byte) 0x32, (byte) 0x8e, (byte) 0xbb, - (byte) 0xdc, (byte) 0x1b, (byte) 0xa7, (byte) 0x7b, (byte) 0x73, (byte) 0xd7, - (byte) 0xf4, (byte) 0xad, (byte) 0xa9, (byte) 0x3a, (byte) 0xf7, (byte) 0xa8, - (byte) 0x82, (byte) 0x92, (byte) 0x40, (byte) 0xd4, (byte) 0x51, (byte) 0x87, - (byte) 0xe1, (byte) 0xb7, (byte) 0x4f, (byte) 0x91, (byte) 0x75, (byte) 0x5b, - (byte) 0x03, (byte) 0x9d, (byte) 0xa1, (byte) 0xd4, (byte) 0x00, (byte) 0x05, - (byte) 0x79, (byte) 0x42, (byte) 0x93, (byte) 0x76 - }; + private static final byte[] RSA_Vector2_OAEP_SHA384_MGF1_SHA384 = + new byte[] {(byte) 0xa1, (byte) 0xb3, (byte) 0x3b, (byte) 0x34, (byte) 0x69, + (byte) 0x9e, (byte) 0xd8, (byte) 0xa0, (byte) 0x37, (byte) 0x2c, (byte) 0xeb, + (byte) 0xef, (byte) 0xf2, (byte) 0xaf, (byte) 0xfa, (byte) 0x63, (byte) 0x5d, + (byte) 0x88, (byte) 0xac, (byte) 0x51, (byte) 0xd4, (byte) 0x7f, (byte) 0x85, + (byte) 0xf0, (byte) 0x5e, (byte) 0xb4, (byte) 0x81, (byte) 0x7c, (byte) 0x82, + (byte) 0x4f, (byte) 0x92, (byte) 0xf7, (byte) 0x77, (byte) 0x48, (byte) 0x4c, + (byte) 0xb1, (byte) 0x42, (byte) 0xb3, (byte) 0x0e, (byte) 0x94, (byte) 0xc8, + (byte) 0x5a, (byte) 0xae, (byte) 0xed, (byte) 0x8d, (byte) 0x51, (byte) 0x72, + (byte) 0x6b, (byte) 0xa9, (byte) 0xd4, (byte) 0x1e, (byte) 0xbe, (byte) 0x38, + (byte) 0x2c, (byte) 0xd0, (byte) 0x43, (byte) 0xae, (byte) 0xb4, (byte) 0x30, + (byte) 0xa9, (byte) 0x93, (byte) 0x47, (byte) 0xb5, (byte) 0x9d, (byte) 0x03, + (byte) 0x92, (byte) 0x25, (byte) 0x74, (byte) 0xed, (byte) 0xfa, (byte) 0xfe, + (byte) 0xf1, (byte) 0xba, (byte) 0x04, (byte) 0x3a, (byte) 0x4d, (byte) 0x6d, + (byte) 0x9a, (byte) 0x0d, (byte) 0x95, (byte) 0x02, (byte) 0xb0, (byte) 0xac, + (byte) 0x77, (byte) 0x11, (byte) 0x44, (byte) 0xeb, (byte) 0xd2, (byte) 0x02, + (byte) 0x90, (byte) 0xea, (byte) 0x2f, (byte) 0x68, (byte) 0x2a, (byte) 0x69, + (byte) 0xcf, (byte) 0x45, (byte) 0x34, (byte) 0xff, (byte) 0x00, (byte) 0xc6, + (byte) 0x3c, (byte) 0x0b, (byte) 0x2c, (byte) 0x5f, (byte) 0x8c, (byte) 0x2c, + (byte) 0xbf, (byte) 0xc2, (byte) 0x4b, (byte) 0x16, (byte) 0x07, (byte) 0x84, + (byte) 0x74, (byte) 0xf0, (byte) 0x7a, (byte) 0x01, (byte) 0x7e, (byte) 0x74, + (byte) 0x01, (byte) 0x88, (byte) 0xce, (byte) 0xda, (byte) 0xe4, (byte) 0x21, + (byte) 0x89, (byte) 0xfc, (byte) 0xac, (byte) 0x68, (byte) 0xdb, (byte) 0xfc, + (byte) 0x5f, (byte) 0x3f, (byte) 0x00, (byte) 0xd9, (byte) 0x32, (byte) 0x1d, + (byte) 0xa5, (byte) 0xec, (byte) 0x72, (byte) 0x46, (byte) 0x23, (byte) 0xe5, + (byte) 0x7f, (byte) 0x49, (byte) 0x0e, (byte) 0x3e, (byte) 0xf2, (byte) 0x2b, + (byte) 0x16, (byte) 0x52, (byte) 0x9f, (byte) 0x9d, (byte) 0x0c, (byte) 0xfe, + (byte) 0xab, (byte) 0xdd, (byte) 0x77, (byte) 0x77, (byte) 0x94, (byte) 0xa4, + (byte) 0x92, (byte) 0xa2, (byte) 0x41, (byte) 0x0d, (byte) 0x4b, (byte) 0x57, + (byte) 0x80, (byte) 0xd6, (byte) 0x74, (byte) 0x63, (byte) 0xd5, (byte) 0xbf, + (byte) 0x5c, (byte) 0xa0, (byte) 0xda, (byte) 0x3c, (byte) 0xe6, (byte) 0xbf, + (byte) 0xa4, (byte) 0xc3, (byte) 0xfb, (byte) 0x46, (byte) 0x3b, (byte) 0x73, + (byte) 0x30, (byte) 0x4b, (byte) 0x57, (byte) 0x27, (byte) 0x0c, (byte) 0x81, + (byte) 0xde, (byte) 0x8a, (byte) 0x01, (byte) 0xe5, (byte) 0x7e, (byte) 0xe0, + (byte) 0x16, (byte) 0x11, (byte) 0x24, (byte) 0x34, (byte) 0x22, (byte) 0x01, + (byte) 0x9f, (byte) 0xe6, (byte) 0xa9, (byte) 0xfb, (byte) 0xad, (byte) 0x55, + (byte) 0x17, (byte) 0x2a, (byte) 0x92, (byte) 0x87, (byte) 0xf3, (byte) 0x72, + (byte) 0xc9, (byte) 0x3d, (byte) 0xc9, (byte) 0x2e, (byte) 0x32, (byte) 0x8e, + (byte) 0xbb, (byte) 0xdc, (byte) 0x1b, (byte) 0xa7, (byte) 0x7b, (byte) 0x73, + (byte) 0xd7, (byte) 0xf4, (byte) 0xad, (byte) 0xa9, (byte) 0x3a, (byte) 0xf7, + (byte) 0xa8, (byte) 0x82, (byte) 0x92, (byte) 0x40, (byte) 0xd4, (byte) 0x51, + (byte) 0x87, (byte) 0xe1, (byte) 0xb7, (byte) 0x4f, (byte) 0x91, (byte) 0x75, + (byte) 0x5b, (byte) 0x03, (byte) 0x9d, (byte) 0xa1, (byte) 0xd4, (byte) 0x00, + (byte) 0x05, (byte) 0x79, (byte) 0x42, (byte) 0x93, (byte) 0x76}; /* * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt \ * -pkeyopt rsa_padding_mode:oaep -pkey rsa_oaep_md:sha512 -pkeyopt rsa_mgf1_md:sha512 \ * | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA512_MGF1_SHA512 = new byte[] { - (byte) 0x75, (byte) 0x0f, (byte) 0xf9, (byte) 0x21, (byte) 0xca, (byte) 0xcc, - (byte) 0x0e, (byte) 0x13, (byte) 0x9e, (byte) 0x38, (byte) 0xa4, (byte) 0xa7, - (byte) 0xee, (byte) 0x61, (byte) 0x6d, (byte) 0x56, (byte) 0xea, (byte) 0x36, - (byte) 0xeb, (byte) 0xec, (byte) 0xfa, (byte) 0x1a, (byte) 0xeb, (byte) 0x0c, - (byte) 0xb2, (byte) 0x58, (byte) 0x9d, (byte) 0xde, (byte) 0x47, (byte) 0x27, - (byte) 0x2d, (byte) 0xbd, (byte) 0x8b, (byte) 0xa7, (byte) 0xf1, (byte) 0x8b, - (byte) 0xba, (byte) 0x4c, (byte) 0xab, (byte) 0x39, (byte) 0x6a, (byte) 0x82, - (byte) 0x0d, (byte) 0xaf, (byte) 0x4c, (byte) 0xde, (byte) 0xdb, (byte) 0x5e, - (byte) 0xdb, (byte) 0x08, (byte) 0x98, (byte) 0x06, (byte) 0xc5, (byte) 0x99, - (byte) 0xb6, (byte) 0x6d, (byte) 0xbc, (byte) 0x5b, (byte) 0xf9, (byte) 0xe4, - (byte) 0x97, (byte) 0x0b, (byte) 0xba, (byte) 0xe3, (byte) 0x17, (byte) 0xa9, - (byte) 0x3c, (byte) 0x4b, (byte) 0x21, (byte) 0xd8, (byte) 0x29, (byte) 0xf8, - (byte) 0xa7, (byte) 0x1c, (byte) 0x15, (byte) 0xd7, (byte) 0xf6, (byte) 0xfc, - (byte) 0x53, (byte) 0x64, (byte) 0x97, (byte) 0x9e, (byte) 0x22, (byte) 0xb1, - (byte) 0x93, (byte) 0x26, (byte) 0x80, (byte) 0xdc, (byte) 0xaa, (byte) 0x1b, - (byte) 0xae, (byte) 0x69, (byte) 0x0f, (byte) 0x74, (byte) 0x3d, (byte) 0x61, - (byte) 0x80, (byte) 0x68, (byte) 0xb8, (byte) 0xaf, (byte) 0x63, (byte) 0x72, - (byte) 0x37, (byte) 0x4f, (byte) 0xf3, (byte) 0x29, (byte) 0x4a, (byte) 0x75, - (byte) 0x4f, (byte) 0x29, (byte) 0x40, (byte) 0x01, (byte) 0xd3, (byte) 0xc6, - (byte) 0x56, (byte) 0x1a, (byte) 0xaf, (byte) 0xc3, (byte) 0xb3, (byte) 0xd2, - (byte) 0xb9, (byte) 0x91, (byte) 0x35, (byte) 0x1b, (byte) 0x89, (byte) 0x4c, - (byte) 0x61, (byte) 0xa2, (byte) 0x8e, (byte) 0x6f, (byte) 0x12, (byte) 0x4a, - (byte) 0x10, (byte) 0xc2, (byte) 0xcc, (byte) 0xab, (byte) 0x51, (byte) 0xec, - (byte) 0x1b, (byte) 0xb5, (byte) 0xfe, (byte) 0x20, (byte) 0x16, (byte) 0xb2, - (byte) 0xc5, (byte) 0x0f, (byte) 0xe1, (byte) 0x6a, (byte) 0xb4, (byte) 0x6c, - (byte) 0x27, (byte) 0xd9, (byte) 0x42, (byte) 0xb9, (byte) 0xb6, (byte) 0x55, - (byte) 0xa8, (byte) 0xbc, (byte) 0x1c, (byte) 0x32, (byte) 0x54, (byte) 0x84, - (byte) 0xec, (byte) 0x1e, (byte) 0x95, (byte) 0xd8, (byte) 0xae, (byte) 0xca, - (byte) 0xc1, (byte) 0xad, (byte) 0x4c, (byte) 0x65, (byte) 0xd6, (byte) 0xc2, - (byte) 0x19, (byte) 0x66, (byte) 0xad, (byte) 0x9f, (byte) 0x55, (byte) 0x15, - (byte) 0xe1, (byte) 0x5d, (byte) 0x8f, (byte) 0xab, (byte) 0x18, (byte) 0x68, - (byte) 0x42, (byte) 0x7c, (byte) 0x48, (byte) 0xb7, (byte) 0x2c, (byte) 0xfd, - (byte) 0x1a, (byte) 0x07, (byte) 0xa1, (byte) 0x6a, (byte) 0xfb, (byte) 0x81, - (byte) 0xc6, (byte) 0x93, (byte) 0xbf, (byte) 0xa3, (byte) 0x5d, (byte) 0xfd, - (byte) 0xce, (byte) 0xf3, (byte) 0x17, (byte) 0x26, (byte) 0xf0, (byte) 0xda, - (byte) 0x0e, (byte) 0xd1, (byte) 0x86, (byte) 0x9d, (byte) 0x61, (byte) 0xd1, - (byte) 0x8a, (byte) 0xdb, (byte) 0x36, (byte) 0x39, (byte) 0x1c, (byte) 0xd4, - (byte) 0x99, (byte) 0x53, (byte) 0x30, (byte) 0x5a, (byte) 0x01, (byte) 0xf4, - (byte) 0xa0, (byte) 0xca, (byte) 0x94, (byte) 0x72, (byte) 0x3d, (byte) 0xe3, - (byte) 0x50, (byte) 0x95, (byte) 0xcb, (byte) 0xa9, (byte) 0x37, (byte) 0xeb, - (byte) 0x66, (byte) 0x21, (byte) 0x20, (byte) 0x2e, (byte) 0xf2, (byte) 0xfd, - (byte) 0xfa, (byte) 0x54, (byte) 0xbf, (byte) 0x17, (byte) 0x23, (byte) 0xbb, - (byte) 0x9e, (byte) 0x77, (byte) 0xe0, (byte) 0xaa - }; + private static final byte[] RSA_Vector2_OAEP_SHA512_MGF1_SHA512 = + new byte[] {(byte) 0x75, (byte) 0x0f, (byte) 0xf9, (byte) 0x21, (byte) 0xca, + (byte) 0xcc, (byte) 0x0e, (byte) 0x13, (byte) 0x9e, (byte) 0x38, (byte) 0xa4, + (byte) 0xa7, (byte) 0xee, (byte) 0x61, (byte) 0x6d, (byte) 0x56, (byte) 0xea, + (byte) 0x36, (byte) 0xeb, (byte) 0xec, (byte) 0xfa, (byte) 0x1a, (byte) 0xeb, + (byte) 0x0c, (byte) 0xb2, (byte) 0x58, (byte) 0x9d, (byte) 0xde, (byte) 0x47, + (byte) 0x27, (byte) 0x2d, (byte) 0xbd, (byte) 0x8b, (byte) 0xa7, (byte) 0xf1, + (byte) 0x8b, (byte) 0xba, (byte) 0x4c, (byte) 0xab, (byte) 0x39, (byte) 0x6a, + (byte) 0x82, (byte) 0x0d, (byte) 0xaf, (byte) 0x4c, (byte) 0xde, (byte) 0xdb, + (byte) 0x5e, (byte) 0xdb, (byte) 0x08, (byte) 0x98, (byte) 0x06, (byte) 0xc5, + (byte) 0x99, (byte) 0xb6, (byte) 0x6d, (byte) 0xbc, (byte) 0x5b, (byte) 0xf9, + (byte) 0xe4, (byte) 0x97, (byte) 0x0b, (byte) 0xba, (byte) 0xe3, (byte) 0x17, + (byte) 0xa9, (byte) 0x3c, (byte) 0x4b, (byte) 0x21, (byte) 0xd8, (byte) 0x29, + (byte) 0xf8, (byte) 0xa7, (byte) 0x1c, (byte) 0x15, (byte) 0xd7, (byte) 0xf6, + (byte) 0xfc, (byte) 0x53, (byte) 0x64, (byte) 0x97, (byte) 0x9e, (byte) 0x22, + (byte) 0xb1, (byte) 0x93, (byte) 0x26, (byte) 0x80, (byte) 0xdc, (byte) 0xaa, + (byte) 0x1b, (byte) 0xae, (byte) 0x69, (byte) 0x0f, (byte) 0x74, (byte) 0x3d, + (byte) 0x61, (byte) 0x80, (byte) 0x68, (byte) 0xb8, (byte) 0xaf, (byte) 0x63, + (byte) 0x72, (byte) 0x37, (byte) 0x4f, (byte) 0xf3, (byte) 0x29, (byte) 0x4a, + (byte) 0x75, (byte) 0x4f, (byte) 0x29, (byte) 0x40, (byte) 0x01, (byte) 0xd3, + (byte) 0xc6, (byte) 0x56, (byte) 0x1a, (byte) 0xaf, (byte) 0xc3, (byte) 0xb3, + (byte) 0xd2, (byte) 0xb9, (byte) 0x91, (byte) 0x35, (byte) 0x1b, (byte) 0x89, + (byte) 0x4c, (byte) 0x61, (byte) 0xa2, (byte) 0x8e, (byte) 0x6f, (byte) 0x12, + (byte) 0x4a, (byte) 0x10, (byte) 0xc2, (byte) 0xcc, (byte) 0xab, (byte) 0x51, + (byte) 0xec, (byte) 0x1b, (byte) 0xb5, (byte) 0xfe, (byte) 0x20, (byte) 0x16, + (byte) 0xb2, (byte) 0xc5, (byte) 0x0f, (byte) 0xe1, (byte) 0x6a, (byte) 0xb4, + (byte) 0x6c, (byte) 0x27, (byte) 0xd9, (byte) 0x42, (byte) 0xb9, (byte) 0xb6, + (byte) 0x55, (byte) 0xa8, (byte) 0xbc, (byte) 0x1c, (byte) 0x32, (byte) 0x54, + (byte) 0x84, (byte) 0xec, (byte) 0x1e, (byte) 0x95, (byte) 0xd8, (byte) 0xae, + (byte) 0xca, (byte) 0xc1, (byte) 0xad, (byte) 0x4c, (byte) 0x65, (byte) 0xd6, + (byte) 0xc2, (byte) 0x19, (byte) 0x66, (byte) 0xad, (byte) 0x9f, (byte) 0x55, + (byte) 0x15, (byte) 0xe1, (byte) 0x5d, (byte) 0x8f, (byte) 0xab, (byte) 0x18, + (byte) 0x68, (byte) 0x42, (byte) 0x7c, (byte) 0x48, (byte) 0xb7, (byte) 0x2c, + (byte) 0xfd, (byte) 0x1a, (byte) 0x07, (byte) 0xa1, (byte) 0x6a, (byte) 0xfb, + (byte) 0x81, (byte) 0xc6, (byte) 0x93, (byte) 0xbf, (byte) 0xa3, (byte) 0x5d, + (byte) 0xfd, (byte) 0xce, (byte) 0xf3, (byte) 0x17, (byte) 0x26, (byte) 0xf0, + (byte) 0xda, (byte) 0x0e, (byte) 0xd1, (byte) 0x86, (byte) 0x9d, (byte) 0x61, + (byte) 0xd1, (byte) 0x8a, (byte) 0xdb, (byte) 0x36, (byte) 0x39, (byte) 0x1c, + (byte) 0xd4, (byte) 0x99, (byte) 0x53, (byte) 0x30, (byte) 0x5a, (byte) 0x01, + (byte) 0xf4, (byte) 0xa0, (byte) 0xca, (byte) 0x94, (byte) 0x72, (byte) 0x3d, + (byte) 0xe3, (byte) 0x50, (byte) 0x95, (byte) 0xcb, (byte) 0xa9, (byte) 0x37, + (byte) 0xeb, (byte) 0x66, (byte) 0x21, (byte) 0x20, (byte) 0x2e, (byte) 0xf2, + (byte) 0xfd, (byte) 0xfa, (byte) 0x54, (byte) 0xbf, (byte) 0x17, (byte) 0x23, + (byte) 0xbb, (byte) 0x9e, (byte) 0x77, (byte) 0xe0, (byte) 0xaa}; /* * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha512 -pkeyopt rsa_mgf1_md:sha512 -pkeyopt rsa_oaep_label:010203FFA00A | xxd -p -i | sed 's/0x/(byte) 0x/g' */ - public static final byte[] RSA_Vector2_OAEP_SHA512_MGF1_SHA512_LABEL = new byte[] { - (byte) 0x31, (byte) 0x3b, (byte) 0x23, (byte) 0xcf, (byte) 0x40, (byte) 0xfe, - (byte) 0x15, (byte) 0x94, (byte) 0xd6, (byte) 0x81, (byte) 0x21, (byte) 0x69, - (byte) 0x8e, (byte) 0x58, (byte) 0xd5, (byte) 0x0f, (byte) 0xa8, (byte) 0x72, - (byte) 0x94, (byte) 0x13, (byte) 0xfe, (byte) 0xf9, (byte) 0xa1, (byte) 0x47, - (byte) 0x49, (byte) 0x91, (byte) 0xcb, (byte) 0x66, (byte) 0xe6, (byte) 0x5d, - (byte) 0x02, (byte) 0xad, (byte) 0xd4, (byte) 0x2f, (byte) 0x4f, (byte) 0xab, - (byte) 0xb7, (byte) 0x9e, (byte) 0xc0, (byte) 0xf0, (byte) 0x3d, (byte) 0x66, - (byte) 0x0e, (byte) 0x20, (byte) 0x82, (byte) 0x7f, (byte) 0x22, (byte) 0x8f, - (byte) 0x81, (byte) 0xba, (byte) 0x47, (byte) 0xc7, (byte) 0xaf, (byte) 0xb6, - (byte) 0x0e, (byte) 0x78, (byte) 0xe3, (byte) 0x30, (byte) 0xd7, (byte) 0x6c, - (byte) 0x81, (byte) 0xc2, (byte) 0x05, (byte) 0x7e, (byte) 0xe9, (byte) 0xac, - (byte) 0x8d, (byte) 0x45, (byte) 0x25, (byte) 0xe8, (byte) 0x26, (byte) 0x39, - (byte) 0x88, (byte) 0x64, (byte) 0x2e, (byte) 0xc6, (byte) 0xed, (byte) 0xd4, - (byte) 0xad, (byte) 0x94, (byte) 0xc8, (byte) 0x4e, (byte) 0x4a, (byte) 0x71, - (byte) 0x1e, (byte) 0x11, (byte) 0x14, (byte) 0x03, (byte) 0x56, (byte) 0x02, - (byte) 0x28, (byte) 0x32, (byte) 0x8f, (byte) 0xe2, (byte) 0x16, (byte) 0x4a, - (byte) 0x62, (byte) 0xa6, (byte) 0x9a, (byte) 0x8d, (byte) 0xf8, (byte) 0x33, - (byte) 0x35, (byte) 0xa2, (byte) 0xc7, (byte) 0x70, (byte) 0xcc, (byte) 0x26, - (byte) 0x1e, (byte) 0x4d, (byte) 0x9c, (byte) 0x4e, (byte) 0x2b, (byte) 0xe8, - (byte) 0xfd, (byte) 0x07, (byte) 0x33, (byte) 0x15, (byte) 0x53, (byte) 0x11, - (byte) 0x5c, (byte) 0x6f, (byte) 0x5d, (byte) 0x23, (byte) 0x7b, (byte) 0x3f, - (byte) 0x73, (byte) 0xff, (byte) 0xf4, (byte) 0xbe, (byte) 0x1f, (byte) 0xe6, - (byte) 0x5a, (byte) 0xb8, (byte) 0x2b, (byte) 0xd2, (byte) 0xbe, (byte) 0xa0, - (byte) 0x91, (byte) 0x5d, (byte) 0xca, (byte) 0x89, (byte) 0xb3, (byte) 0xce, - (byte) 0x0a, (byte) 0x2b, (byte) 0xce, (byte) 0xb9, (byte) 0xbe, (byte) 0x5d, - (byte) 0xb2, (byte) 0xc2, (byte) 0xd6, (byte) 0xa9, (byte) 0xbc, (byte) 0x37, - (byte) 0xed, (byte) 0x9a, (byte) 0xba, (byte) 0x35, (byte) 0xf8, (byte) 0x6e, - (byte) 0x63, (byte) 0x76, (byte) 0xd1, (byte) 0x12, (byte) 0xf5, (byte) 0x89, - (byte) 0xf0, (byte) 0x13, (byte) 0x86, (byte) 0xe7, (byte) 0x1b, (byte) 0x94, - (byte) 0xcb, (byte) 0xc8, (byte) 0x5c, (byte) 0x4c, (byte) 0x1b, (byte) 0x8a, - (byte) 0x2d, (byte) 0x6b, (byte) 0x24, (byte) 0x1a, (byte) 0x38, (byte) 0x14, - (byte) 0x77, (byte) 0x49, (byte) 0xe5, (byte) 0x08, (byte) 0x25, (byte) 0xe4, - (byte) 0xa6, (byte) 0xcf, (byte) 0x62, (byte) 0xfd, (byte) 0x66, (byte) 0x28, - (byte) 0xf0, (byte) 0x3a, (byte) 0x9c, (byte) 0x31, (byte) 0xef, (byte) 0x48, - (byte) 0x2a, (byte) 0xd3, (byte) 0x3e, (byte) 0x29, (byte) 0xfa, (byte) 0x18, - (byte) 0x8f, (byte) 0xd6, (byte) 0xaa, (byte) 0x1d, (byte) 0x10, (byte) 0xcd, - (byte) 0x35, (byte) 0x25, (byte) 0x92, (byte) 0x48, (byte) 0xa0, (byte) 0x2c, - (byte) 0xc1, (byte) 0x31, (byte) 0xeb, (byte) 0x47, (byte) 0x5b, (byte) 0x22, - (byte) 0x52, (byte) 0x7c, (byte) 0xf5, (byte) 0xec, (byte) 0x76, (byte) 0x90, - (byte) 0x94, (byte) 0x58, (byte) 0xd9, (byte) 0xd6, (byte) 0xe0, (byte) 0x0a, - (byte) 0x3f, (byte) 0x09, (byte) 0x98, (byte) 0x03, (byte) 0xc5, (byte) 0x07, - (byte) 0x8f, (byte) 0x89, (byte) 0x1e, (byte) 0x62, (byte) 0x2c, (byte) 0xea, - (byte) 0x17, (byte) 0x0a, (byte) 0x2e, (byte) 0x68 - }; + private static final byte[] RSA_Vector2_OAEP_SHA512_MGF1_SHA512_LABEL = + new byte[] {(byte) 0x31, (byte) 0x3b, (byte) 0x23, (byte) 0xcf, (byte) 0x40, + (byte) 0xfe, (byte) 0x15, (byte) 0x94, (byte) 0xd6, (byte) 0x81, (byte) 0x21, + (byte) 0x69, (byte) 0x8e, (byte) 0x58, (byte) 0xd5, (byte) 0x0f, (byte) 0xa8, + (byte) 0x72, (byte) 0x94, (byte) 0x13, (byte) 0xfe, (byte) 0xf9, (byte) 0xa1, + (byte) 0x47, (byte) 0x49, (byte) 0x91, (byte) 0xcb, (byte) 0x66, (byte) 0xe6, + (byte) 0x5d, (byte) 0x02, (byte) 0xad, (byte) 0xd4, (byte) 0x2f, (byte) 0x4f, + (byte) 0xab, (byte) 0xb7, (byte) 0x9e, (byte) 0xc0, (byte) 0xf0, (byte) 0x3d, + (byte) 0x66, (byte) 0x0e, (byte) 0x20, (byte) 0x82, (byte) 0x7f, (byte) 0x22, + (byte) 0x8f, (byte) 0x81, (byte) 0xba, (byte) 0x47, (byte) 0xc7, (byte) 0xaf, + (byte) 0xb6, (byte) 0x0e, (byte) 0x78, (byte) 0xe3, (byte) 0x30, (byte) 0xd7, + (byte) 0x6c, (byte) 0x81, (byte) 0xc2, (byte) 0x05, (byte) 0x7e, (byte) 0xe9, + (byte) 0xac, (byte) 0x8d, (byte) 0x45, (byte) 0x25, (byte) 0xe8, (byte) 0x26, + (byte) 0x39, (byte) 0x88, (byte) 0x64, (byte) 0x2e, (byte) 0xc6, (byte) 0xed, + (byte) 0xd4, (byte) 0xad, (byte) 0x94, (byte) 0xc8, (byte) 0x4e, (byte) 0x4a, + (byte) 0x71, (byte) 0x1e, (byte) 0x11, (byte) 0x14, (byte) 0x03, (byte) 0x56, + (byte) 0x02, (byte) 0x28, (byte) 0x32, (byte) 0x8f, (byte) 0xe2, (byte) 0x16, + (byte) 0x4a, (byte) 0x62, (byte) 0xa6, (byte) 0x9a, (byte) 0x8d, (byte) 0xf8, + (byte) 0x33, (byte) 0x35, (byte) 0xa2, (byte) 0xc7, (byte) 0x70, (byte) 0xcc, + (byte) 0x26, (byte) 0x1e, (byte) 0x4d, (byte) 0x9c, (byte) 0x4e, (byte) 0x2b, + (byte) 0xe8, (byte) 0xfd, (byte) 0x07, (byte) 0x33, (byte) 0x15, (byte) 0x53, + (byte) 0x11, (byte) 0x5c, (byte) 0x6f, (byte) 0x5d, (byte) 0x23, (byte) 0x7b, + (byte) 0x3f, (byte) 0x73, (byte) 0xff, (byte) 0xf4, (byte) 0xbe, (byte) 0x1f, + (byte) 0xe6, (byte) 0x5a, (byte) 0xb8, (byte) 0x2b, (byte) 0xd2, (byte) 0xbe, + (byte) 0xa0, (byte) 0x91, (byte) 0x5d, (byte) 0xca, (byte) 0x89, (byte) 0xb3, + (byte) 0xce, (byte) 0x0a, (byte) 0x2b, (byte) 0xce, (byte) 0xb9, (byte) 0xbe, + (byte) 0x5d, (byte) 0xb2, (byte) 0xc2, (byte) 0xd6, (byte) 0xa9, (byte) 0xbc, + (byte) 0x37, (byte) 0xed, (byte) 0x9a, (byte) 0xba, (byte) 0x35, (byte) 0xf8, + (byte) 0x6e, (byte) 0x63, (byte) 0x76, (byte) 0xd1, (byte) 0x12, (byte) 0xf5, + (byte) 0x89, (byte) 0xf0, (byte) 0x13, (byte) 0x86, (byte) 0xe7, (byte) 0x1b, + (byte) 0x94, (byte) 0xcb, (byte) 0xc8, (byte) 0x5c, (byte) 0x4c, (byte) 0x1b, + (byte) 0x8a, (byte) 0x2d, (byte) 0x6b, (byte) 0x24, (byte) 0x1a, (byte) 0x38, + (byte) 0x14, (byte) 0x77, (byte) 0x49, (byte) 0xe5, (byte) 0x08, (byte) 0x25, + (byte) 0xe4, (byte) 0xa6, (byte) 0xcf, (byte) 0x62, (byte) 0xfd, (byte) 0x66, + (byte) 0x28, (byte) 0xf0, (byte) 0x3a, (byte) 0x9c, (byte) 0x31, (byte) 0xef, + (byte) 0x48, (byte) 0x2a, (byte) 0xd3, (byte) 0x3e, (byte) 0x29, (byte) 0xfa, + (byte) 0x18, (byte) 0x8f, (byte) 0xd6, (byte) 0xaa, (byte) 0x1d, (byte) 0x10, + (byte) 0xcd, (byte) 0x35, (byte) 0x25, (byte) 0x92, (byte) 0x48, (byte) 0xa0, + (byte) 0x2c, (byte) 0xc1, (byte) 0x31, (byte) 0xeb, (byte) 0x47, (byte) 0x5b, + (byte) 0x22, (byte) 0x52, (byte) 0x7c, (byte) 0xf5, (byte) 0xec, (byte) 0x76, + (byte) 0x90, (byte) 0x94, (byte) 0x58, (byte) 0xd9, (byte) 0xd6, (byte) 0xe0, + (byte) 0x0a, (byte) 0x3f, (byte) 0x09, (byte) 0x98, (byte) 0x03, (byte) 0xc5, + (byte) 0x07, (byte) 0x8f, (byte) 0x89, (byte) 0x1e, (byte) 0x62, (byte) 0x2c, + (byte) 0xea, (byte) 0x17, (byte) 0x0a, (byte) 0x2e, (byte) 0x68}; @Test public void testRSA_ECB_NoPadding_Private_OnlyDoFinal_Success() throws Exception { @@ -4673,6 +4642,8 @@ * TODO(27995180): consider whether we keep this compatibility. Consider whether we only allow * if an IV is passed in the parameters. */ + @NonCts(bug = 287231726, reason = "The test asserts buggy or non-breaking " + + "behaviors, but the behavior has been fixed in the future ART module version.") @Test public void test_PBKDF2WITHHMACSHA1_SKFactory_and_PBEAESCBC_Cipher_noIV() throws Exception { Assume.assumeNotNull(Security.getProvider("BC"));
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java index 1cad3d5..a357ecb 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java
@@ -465,6 +465,19 @@ if (providers == null) { return new Provider[0]; } + + // Do not test AndroidKeyStore as KeyAgreement provider. It only handles Android + // Keystore-backed keys. It's OKish not to test AndroidKeyStore here because it's tested by + // cts/tests/test/keystore. + List<Provider> filteredProvidersList = new ArrayList<Provider>(providers.length); + for (Provider provider : providers) { + if ("AndroidKeyStore".equals(provider.getName())) { + continue; + } + filteredProvidersList.add(provider); + } + providers = filteredProvidersList.toArray(new Provider[filteredProvidersList.size()]); + // Sort providers by name to guarantee deterministic order in which providers are used in // the tests. return sortByName(providers);
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 new file mode 100644 index 0000000..ade5447 --- /dev/null +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java
@@ -0,0 +1,104 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.conscrypt.javax.crypto; + +import static org.junit.Assert.assertArrayEquals; + +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Security; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import javax.crypto.KeyAgreement; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + + +/** + * Tests for all registered X25519 and X448 {@link KeyAgreement} providers. + * @hide This class is not part of the Android public SDK API + */ +@RunWith(JUnit4.class) +public class XDHKeyAgreementTest { + private static final byte[] RFC_7748_X25519_OUR_PRIV_KEY = new byte[] { + (byte) 0x30, (byte) 0x2e, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x05, (byte) 0x06, + (byte) 0x03, (byte) 0x2b, (byte) 0x65, (byte) 0x6e, (byte) 0x04, (byte) 0x22, (byte) 0x04, (byte) 0x20, + (byte) 0xa5, (byte) 0x46, (byte) 0xe3, (byte) 0x6b, (byte) 0xf0, (byte) 0x52, (byte) 0x7c, (byte) 0x9d, + (byte) 0x3b, (byte) 0x16, (byte) 0x15, (byte) 0x4b, (byte) 0x82, (byte) 0x46, (byte) 0x5e, (byte) 0xdd, + (byte) 0x62, (byte) 0x14, (byte) 0x4c, (byte) 0x0a, (byte) 0xc1, (byte) 0xfc, (byte) 0x5a, (byte) 0x18, + (byte) 0x50, (byte) 0x6a, (byte) 0x22, (byte) 0x44, (byte) 0xba, (byte) 0x44, (byte) 0x9a, (byte) 0xc4, + }; + + // Broken key for testing with JDK 11. Instead of wrapping OCTET STRING with OCTET STRING. + private static final byte[] RFC_7748_X25519_OUR_PRIV_KEY_BROKEN = new byte[] { + (byte) 0x30, (byte) 0x2c, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x05, (byte) 0x06, + (byte) 0x03, (byte) 0x2b, (byte) 0x65, (byte) 0x6e, (byte) 0x04, (byte) 0x20, + (byte) 0xa5, (byte) 0x46, (byte) 0xe3, (byte) 0x6b, (byte) 0xf0, (byte) 0x52, (byte) 0x7c, (byte) 0x9d, + (byte) 0x3b, (byte) 0x16, (byte) 0x15, (byte) 0x4b, (byte) 0x82, (byte) 0x46, (byte) 0x5e, (byte) 0xdd, + (byte) 0x62, (byte) 0x14, (byte) 0x4c, (byte) 0x0a, (byte) 0xc1, (byte) 0xfc, (byte) 0x5a, (byte) 0x18, + (byte) 0x50, (byte) 0x6a, (byte) 0x22, (byte) 0x44, (byte) 0xba, (byte) 0x44, (byte) 0x9a, (byte) 0xc4, + }; + + private static final byte[] RFC_7748_X25519_THEIR_PUB_KEY = new byte[] { + (byte) 0x30, (byte) 0x2a, (byte) 0x30, (byte) 0x05, (byte) 0x06, (byte) 0x03, (byte) 0x2b, (byte) 0x65, + (byte) 0x6e, (byte) 0x03, (byte) 0x21, (byte) 0x00, (byte) 0xe6, (byte) 0xdb, (byte) 0x68, (byte) 0x67, + (byte) 0x58, (byte) 0x30, (byte) 0x30, (byte) 0xdb, (byte) 0x35, (byte) 0x94, (byte) 0xc1, (byte) 0xa4, + (byte) 0x24, (byte) 0xb1, (byte) 0x5f, (byte) 0x7c, (byte) 0x72, (byte) 0x66, (byte) 0x24, (byte) 0xec, + (byte) 0x26, (byte) 0xb3, (byte) 0x35, (byte) 0x3b, (byte) 0x10, (byte) 0xa9, (byte) 0x03, (byte) 0xa6, + (byte) 0xd0, (byte) 0xab, (byte) 0x1c, (byte) 0x4c, + }; + + private static final byte[] RFC_7748_X25519_SECRET = new byte[] { + (byte) 0xc3, (byte) 0xda, (byte) 0x55, (byte) 0x37, (byte) 0x9d, (byte) 0xe9, (byte) 0xc6, (byte) 0x90, + (byte) 0x8e, (byte) 0x94, (byte) 0xea, (byte) 0x4d, (byte) 0xf2, (byte) 0x8d, (byte) 0x08, (byte) 0x4f, + (byte) 0x32, (byte) 0xec, (byte) 0xcf, (byte) 0x03, (byte) 0x49, (byte) 0x1c, (byte) 0x71, (byte) 0xf7, + (byte) 0x54, (byte) 0xb4, (byte) 0x07, (byte) 0x55, (byte) 0x77, (byte) 0xa2, (byte) 0x85, (byte) 0x52, + }; + + private PrivateKey rfc7748X25519PrivateKey; + private PublicKey rfc7748X25519PublicKey; + + private void setupKeys(Provider p) throws Exception { + KeyFactory kf = KeyFactory.getInstance("XDH", p); + + byte[] privateKey; + if ("SunEC".equalsIgnoreCase(p.getName()) + && "11".equals(System.getProperty("java.specification.version"))) { + // SunEC in OpenJDK 11 has a bug where the format specified in RFC 8410 + // Section 7. It uses a single OCTET STRING to represent the key instead + // of an OCTET STRING inside of an OCTET STRING as defined in the RFC: + // ("For the keys defined in this document, the private key is always an + // opaque byte sequence. The ASN.1 type CurvePrivateKey is defined in + // this document to hold the byte sequence. Thus, when encoding a + // OneAsymmetricKey object, the private key is wrapped in a + // CurvePrivateKey object and wrapped by the OCTET STRING of the + // "privateKey" field.") + privateKey = RFC_7748_X25519_OUR_PRIV_KEY_BROKEN; + } else { + privateKey = RFC_7748_X25519_OUR_PRIV_KEY; + } + + rfc7748X25519PrivateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(privateKey)); + rfc7748X25519PublicKey = kf.generatePublic(new X509EncodedKeySpec(RFC_7748_X25519_THEIR_PUB_KEY)); + } + + @Test + public void test_XDHKeyAgreement() throws Exception { + for (Provider p : Security.getProviders("KeyAgreement.XDH")) { + setupKeys(p); + + KeyAgreement ka = KeyAgreement.getInstance("XDH", p); + + test_x25519_keyAgreement_rfc7748_kat_success(ka); + } + } + + private void test_x25519_keyAgreement_rfc7748_kat_success(KeyAgreement ka) throws Exception { + ka.init(rfc7748X25519PrivateKey); + ka.doPhase(rfc7748X25519PublicKey, true); + + assertArrayEquals(RFC_7748_X25519_SECRET, ka.generateSecret()); + } +}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLContextTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLContextTest.java index 5122f70..972928b 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLContextTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLContextTest.java
@@ -17,6 +17,7 @@ package com.android.org.conscrypt.javax.net.ssl; +import static com.android.org.conscrypt.TestUtils.isWindows; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; @@ -25,6 +26,8 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.android.org.conscrypt.TestUtils; +import com.android.org.conscrypt.java.security.StandardNames; import java.io.IOException; import java.security.AccessController; import java.security.InvalidAlgorithmParameterException; @@ -38,6 +41,7 @@ import java.security.UnrecoverableKeyException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -60,8 +64,6 @@ import javax.net.ssl.TrustManagerFactorySpi; import javax.net.ssl.X509KeyManager; import junit.framework.AssertionFailedError; -import com.android.org.conscrypt.TestUtils; -import com.android.org.conscrypt.java.security.StandardNames; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -677,14 +679,21 @@ } private static void assertContentsInOrder(List<String> expected, String... actual) { + List<String> actualList = Arrays.asList(actual); if (expected.size() != actual.length) { fail("Unexpected length. Expected len <" + expected.size() + ">, actual len <" - + actual.length + ">, expected <" + expected + ">, actual <" - + Arrays.asList(actual) + ">"); + + actual.length + ">, expected <" + expected + ">, actual <" + actualList + + ">"); } - if (!expected.equals(Arrays.asList(actual))) { - fail("Unexpected element(s). Expected <" + expected + ">, actual <" - + Arrays.asList(actual) + ">"); + + if (isWindows()) { + // TODO(prbprbprb): CpuFeatures.isAESHardwareAccelerated is not reliable on windows + Collections.sort(actualList); + Collections.sort(expected); + } + + if (!expected.equals(actualList)) { + fail("Unexpected element(s). Expected <" + expected + ">, actual <" + actualList + ">"); } } @@ -724,7 +733,7 @@ } if (version[0] == 1) { - assert version[1] >= 6; + assertTrue(version[1] >= 6); return version[1]; } else { return version[0];
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineTest.java index 7996c32..f3b5148 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineTest.java
@@ -18,6 +18,7 @@ package com.android.org.conscrypt.javax.net.ssl; import static com.android.org.conscrypt.TestUtils.UTF_8; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -26,11 +27,13 @@ import static org.junit.Assert.fail; import com.android.org.conscrypt.TestUtils; +import com.android.org.conscrypt.TestUtils.BufferType; import com.android.org.conscrypt.java.security.StandardNames; import com.android.org.conscrypt.java.security.TestKeyStore; import java.io.IOException; import java.net.Socket; import java.nio.ByteBuffer; +import java.nio.ReadOnlyBufferException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Arrays; @@ -786,7 +789,8 @@ final boolean serverClientMode, final boolean[] finished) throws Exception { TestSSLContext c; if (!clientClientMode && serverClientMode) { - c = TestSSLContext.create(TestKeyStore.getServer(), TestKeyStore.getClient()); + c = TestSSLContext.create(/* client= */ TestKeyStore.getServer(), + /* server= */ TestKeyStore.getClient()); } else { c = TestSSLContext.create(); } @@ -922,6 +926,187 @@ c.close(); } + @Test + public void wrapPreconditions() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate(10); + ByteBuffer[] buffers = new ByteBuffer[] {buffer, buffer, buffer}; + ByteBuffer[] badBuffers = new ByteBuffer[] {buffer, buffer, null, buffer}; + + // Client/server mode not set => IllegalStateException + try { + newUnconnectedEngine().wrap(buffer, buffer); + fail(); + } catch (IllegalStateException e) { + // Expected + } + + try { + newUnconnectedEngine().wrap(buffers, buffer); + fail(); + } catch (IllegalStateException e) { + // Expected + } + + try { + newUnconnectedEngine().wrap(buffers, 0, 1, buffer); + fail(); + } catch (IllegalStateException e) { + // Expected + } + + // Read-only destination => ReadOnlyBufferException + try { + newConnectedEngine().wrap(buffer, buffer.asReadOnlyBuffer()); + fail(); + } catch (ReadOnlyBufferException e) { + // Expected + } + + try { + newConnectedEngine().wrap(buffers, buffer.asReadOnlyBuffer()); + fail(); + } catch (ReadOnlyBufferException e) { + // Expected + } + + try { + newConnectedEngine().wrap(buffers, 0, 1, buffer.asReadOnlyBuffer()); + fail(); + } catch (ReadOnlyBufferException e) { + // Expected + } + + // Null destination => IllegalArgumentException + try { + newConnectedEngine().wrap(buffer, null); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + newConnectedEngine().wrap(buffers, null); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + newConnectedEngine().wrap(buffers, 0, 1, null); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + // Null source => IllegalArgumentException + try { + newConnectedEngine().wrap((ByteBuffer) null, buffer); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + newConnectedEngine().wrap((ByteBuffer[]) null, buffer); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + newConnectedEngine().wrap(null, 0, 1, buffer); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + // Null entries in buffer array => IllegalArgumentException + try { + newConnectedEngine().wrap(badBuffers, buffer); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + newConnectedEngine().wrap(badBuffers, 0, badBuffers.length, buffer); + fail(); + } catch (IllegalArgumentException e) { + // Expected + } + + // Bad offset or length => IndexOutOfBoundsException + try { + newConnectedEngine().wrap(buffers, 0, 7, buffer); + fail(); + } catch (IndexOutOfBoundsException e) { + // Expected + } + } + + @Test + public void bufferArrayOffsets() throws Exception { + TestSSLEnginePair pair = TestSSLEnginePair.create(); + ByteBuffer tlsBuffer = ByteBuffer.allocate(600); + int bufferSize = 100; + + for (BufferType bufferType : BufferType.values()) { + ByteBuffer[] sourceBuffers = bufferType.newRandomBuffers( + bufferSize, bufferSize, bufferSize, bufferSize, bufferSize); + for (int offset = 0; offset < sourceBuffers.length; offset++) { + for (int length = 1; length < sourceBuffers.length - offset; length++) { + // Reset source buffers + for (ByteBuffer buffer : sourceBuffers) { + if (buffer.remaining() == 0) { + buffer.flip(); + } + assertEquals(bufferSize, buffer.remaining()); + } + // Make an array copy of what we expect to send + byte[] sourceBytes = copyDataFromBuffers(sourceBuffers, offset, length); + byte[] destinationBytes = new byte[sourceBytes.length]; + ByteBuffer destination = ByteBuffer.wrap(destinationBytes); + // Send and compare + tlsBuffer.clear(); + pair.client.wrap(sourceBuffers, offset, length, tlsBuffer); + tlsBuffer.flip(); + pair.server.unwrap(tlsBuffer, destination); + assertArrayEquals(sourceBytes, destinationBytes); + } + } + } + } + + private byte[] copyDataFromBuffers(ByteBuffer[] buffers, int offset, int length) { + // NB avoids using Arrays.copyOfRange() to prevent any common bugs with + // ConscryptEngine.wrap(). + int size = 0; + for (int i = offset; i < offset + length; i++) { + size += buffers[i].remaining(); + } + byte[] data = new byte[size]; + int dataOffset = 0; + for (int i = offset; i < offset + length; i++) { + ByteBuffer buffer = buffers[i]; + int remaining = buffer.remaining(); + buffer.get(data, dataOffset, remaining); + buffer.flip(); + dataOffset += remaining; + } + return data; + } + + private SSLEngine newUnconnectedEngine() { + TestSSLContext context = TestSSLContext.create(); + return context.clientContext.createSSLEngine(); + } + + private SSLEngine newConnectedEngine() throws Exception { + TestSSLEnginePair pair = TestSSLEnginePair.create(); + assertConnected(pair); + return pair.client; + } + private void assertConnected(TestSSLEnginePair e) { assertConnected(e.client, e.server); }
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineVersionCompatibilityTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineVersionCompatibilityTest.java index b75860e..24621c5 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineVersionCompatibilityTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineVersionCompatibilityTest.java
@@ -17,9 +17,9 @@ package com.android.org.conscrypt.javax.net.ssl; -import static java.util.Collections.singleton; import static com.android.org.conscrypt.TestUtils.UTF_8; import static com.android.org.conscrypt.TestUtils.assumeJava8; +import static java.util.Collections.singleton; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -30,6 +30,18 @@ import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; +import com.android.org.conscrypt.Conscrypt; +import com.android.org.conscrypt.TestUtils; +import com.android.org.conscrypt.java.security.TestKeyStore; +import com.android.org.conscrypt.testing.FailingSniMatcher; +import com.android.org.conscrypt.tlswire.TlsTester; +import com.android.org.conscrypt.tlswire.handshake.AlpnHelloExtension; +import com.android.org.conscrypt.tlswire.handshake.ClientHello; +import com.android.org.conscrypt.tlswire.handshake.HandshakeMessage; +import com.android.org.conscrypt.tlswire.handshake.HelloExtension; +import com.android.org.conscrypt.tlswire.handshake.ServerNameHelloExtension; +import com.android.org.conscrypt.tlswire.record.TlsProtocols; +import com.android.org.conscrypt.tlswire.record.TlsRecord; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.nio.ByteBuffer; @@ -38,6 +50,7 @@ import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collections; +import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; @@ -62,18 +75,6 @@ import javax.net.ssl.SSLSession; import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509TrustManager; -import com.android.org.conscrypt.Conscrypt; -import com.android.org.conscrypt.TestUtils; -import com.android.org.conscrypt.java.security.TestKeyStore; -import com.android.org.conscrypt.testing.FailingSniMatcher; -import com.android.org.conscrypt.tlswire.TlsTester; -import com.android.org.conscrypt.tlswire.handshake.AlpnHelloExtension; -import com.android.org.conscrypt.tlswire.handshake.ClientHello; -import com.android.org.conscrypt.tlswire.handshake.HandshakeMessage; -import com.android.org.conscrypt.tlswire.handshake.HelloExtension; -import com.android.org.conscrypt.tlswire.handshake.ServerNameHelloExtension; -import com.android.org.conscrypt.tlswire.record.TlsProtocols; -import com.android.org.conscrypt.tlswire.record.TlsRecord; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -757,7 +758,7 @@ }); fail(); } catch (SSLHandshakeException e) { - assertEquals(e.getMessage(), "SNI match failed: any.host"); + assertEquals("SNI match failed: any.host", e.getMessage()); } } @@ -808,6 +809,102 @@ assertTrue(serverAliasCalled.get()); } + // Splits a ByteArray into an array of ByteBuffers each no bigger than the specified size. + private ByteBuffer[] splitDataIntoBuffers(byte[] sourceData, int size) { + int nbuf = ((sourceData.length - 1) / size) + 1; + ByteBuffer[] buffers = new ByteBuffer[nbuf]; + int buffer = 0; + for (int offset = 0; offset < sourceData.length; offset += size, buffer++) { + buffers[buffer] = ByteBuffer.allocate(size); + int remaining = sourceData.length - offset; + buffers[buffer].put(sourceData, offset, Math.min(remaining, size)); + buffers[buffer].flip(); + } + return buffers; + } + + // Sends dataSize bytes of application data, split into an array of ByteBuffers + // of size bufferSize and verifies it arrives intact. If offset is non-zero then + // additional invalid buffers will be added to the start and end of the buffer array + // in order to test the offset and length arguments of wrap(). + private void sendAppDataInMultipleBuffers( + SSLEngine src, SSLEngine dst, int dataSize, int bufferSize) throws SSLException { + // Generate random data and split into multiple. + byte[] sourceData = new byte[dataSize]; + Random random = new Random(System.currentTimeMillis()); + random.nextBytes(sourceData); + ByteBuffer[] sourceBuffers = splitDataIntoBuffers(sourceData, bufferSize); + int length = sourceBuffers.length; + + // Ensure there is no pending outbound data or encrypted data and handshaking is complete. + ByteBuffer tlsBuffer = ByteBuffer.allocateDirect(src.getSession().getPacketBufferSize()); + SSLEngineResult result = src.wrap(EMPTY_BUFFER, tlsBuffer); + assertEquals(Status.OK, result.getStatus()); + assertEquals(HandshakeStatus.NOT_HANDSHAKING, result.getHandshakeStatus()); + assertEquals(0, result.bytesConsumed()); + assertEquals(0, result.bytesProduced()); + + // Ensure there is no pending inbound data + result = dst.unwrap(EMPTY_BUFFER, tlsBuffer); + assertEquals(Status.BUFFER_UNDERFLOW, result.getStatus()); + assertEquals(0, result.bytesConsumed()); + assertEquals(0, result.bytesProduced()); + + // Send the data. wrap() should consume as many source buffers as needed but + // never generate more than one full packet buffer of TLS data, and so unwrap() + // should only be needed once per loop iteration. + int consumed = 0, produced = 0; + ByteBuffer destBuffer = ByteBuffer.allocate(dataSize); + while (consumed < dataSize) { + String message = + String.format("sendData: dataSize=%d, bufSize=%d", dataSize, bufferSize); + + tlsBuffer.clear(); + result = src.wrap(sourceBuffers, 0, length, tlsBuffer); + assertEquals(message, Status.OK, result.getStatus()); + consumed += result.bytesConsumed(); + + tlsBuffer.flip(); + result = dst.unwrap(tlsBuffer, destBuffer); + assertEquals(message, Status.OK, result.getStatus()); + produced += result.bytesProduced(); + } + assertEquals(dataSize, consumed); + assertEquals(dataSize, produced); + + // Compare source and destination data. + destBuffer.flip(); + // destBuffer is non-direct so will always have a backing array + assertArrayEquals(sourceData, destBuffer.array()); + } + + /** + * Tests the multiple {@link ByteBuffer} cases of {@code wrap())} by sending blocks of + * application data split into arrays of ByteBuffers of different sizes. + * + * The main intention is to check that regardless of how the data is structured in + * buffers, each call to wrap() always generates a single TLS record that is smaller + * than the maximum allowed size, see https://github.com/google/conscrypt/issues/929 + */ + @Test + public void multipleBuffersOfDifferentSizes() throws Exception { + TestSSLEnginePair pair = TestSSLEnginePair.create(); + SSLSession session = pair.client.getSession(); + int appBufSize = session.getApplicationBufferSize(); + + int[] dataSizes = new int[] {12, 512, 555, 1500, 8192, appBufSize, 5 * appBufSize}; + int[] bufferSizes = new int[] { + 53, 512, 8192, appBufSize, appBufSize - 53, appBufSize + 53, 5 * appBufSize}; + for (int dataSize : dataSizes) { + for (int bufSize : bufferSizes) { + sendAppDataInMultipleBuffers(pair.client, pair.server, dataSize, bufSize); + sendAppDataInMultipleBuffers(pair.server, pair.client, dataSize, bufSize); + sendAppDataInMultipleBuffers(pair.client, pair.server, dataSize, bufSize); + sendAppDataInMultipleBuffers(pair.server, pair.client, dataSize, bufSize); + } + } + } + private TestKeyStore addServerCertListener(final Runnable callback) { TestKeyStore store = TestKeyStore.getServer().copy(); X509ExtendedKeyManager tm = new ForwardingX509ExtendedKeyManager(
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionContextTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionContextTest.java index 9b1fa5c..19a6bee 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionContextTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionContextTest.java
@@ -73,6 +73,7 @@ } @Test + @SuppressWarnings("JdkObsolete") // Public API SSLSessionContext.getIds() uses Enumeration public void test_SSLSessionContext_getIds() { TestSSLContext c = newTestContext(); assertSSLSessionContextSize(0, c); @@ -99,6 +100,7 @@ } @Test + @SuppressWarnings("JdkObsolete") // Public API SSLSessionContext.getIds() uses Enumeration public void test_SSLSessionContext_getSession() { TestSSLContext c = newTestContext(); try {
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionTest.java index 670ee52..f80dea4 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionTest.java
@@ -202,14 +202,22 @@ fail(); } catch (SSLPeerUnverifiedException expected) { // Ignored. + } catch (UnsupportedOperationException e) { + if (!StandardNames.IS_15_OR_UP) { + fail("Should only throw UnsupportedOperationException on OpenJDK 15 or up"); + } } assertNotNull(s.client.getPeerCertificates()); - TestKeyStore.assertChainLength(s.client.getPeerCertificateChain()); + TestKeyStore.assertChainLength(s.client.getPeerCertificates()); try { assertNull(s.server.getPeerCertificateChain()); fail(); } catch (SSLPeerUnverifiedException expected) { // Ignored. + } catch (UnsupportedOperationException e) { + if (!StandardNames.IS_15_OR_UP) { + fail("Should only throw UnsupportedOperationException on OpenJDK 15 or up"); + } } s.close(); }
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 77b0179..5a556fd 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,12 +73,12 @@ 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; import tests.net.DelegatingSSLSocketFactory; import tests.util.ForEachRunner; -import tests.util.ForEachRunner.Callback; import tests.util.Pair; /** @@ -426,6 +426,7 @@ * 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; @@ -459,6 +460,7 @@ * 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; @@ -879,7 +881,7 @@ @Test public void test_SSLSocket_ClientHello_cipherSuites() throws Exception { - ForEachRunner.runNamed(new Callback<SSLSocketFactory>() { + ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = TlsTester @@ -910,7 +912,7 @@ @Test public void test_SSLSocket_ClientHello_supportedCurves() throws Exception { - ForEachRunner.runNamed(new Callback<SSLSocketFactory>() { + ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = TlsTester @@ -936,7 +938,7 @@ @Test public void test_SSLSocket_ClientHello_clientProtocolVersion() throws Exception { - ForEachRunner.runNamed(new Callback<SSLSocketFactory>() { + ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = TlsTester @@ -948,7 +950,7 @@ @Test public void test_SSLSocket_ClientHello_compressionMethods() throws Exception { - ForEachRunner.runNamed(new Callback<SSLSocketFactory>() { + ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = TlsTester @@ -1013,6 +1015,7 @@ // 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 @@ -1053,6 +1056,7 @@ } @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,6 +1110,7 @@ } @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/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java index ad109ba..3b2f5f1 100644 --- a/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java
@@ -18,6 +18,10 @@ package com.android.org.conscrypt.javax.net.ssl; import static com.android.org.conscrypt.TestUtils.UTF_8; +import static com.android.org.conscrypt.TestUtils.isLinux; +import static com.android.org.conscrypt.TestUtils.isOsx; +import static com.android.org.conscrypt.TestUtils.isWindows; +import static com.android.org.conscrypt.TestUtils.osName; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -111,7 +115,6 @@ import org.junit.runners.Parameterized; import tests.net.DelegatingSSLSocketFactory; import tests.util.ForEachRunner; -import tests.util.ForEachRunner.Callback; import tests.util.Pair; /** @@ -364,6 +367,7 @@ client.addHandshakeCompletedListener(new HandshakeCompletedListener() { @Override public void handshakeCompleted(HandshakeCompletedEvent event) { + SSLSocket socket = null; try { SSLSession session = event.getSession(); String cipherSuite = event.getCipherSuite(); @@ -373,7 +377,7 @@ event.getPeerCertificateChain(); Principal peerPrincipal = event.getPeerPrincipal(); Principal localPrincipal = event.getLocalPrincipal(); - SSLSocket socket = event.getSocket(); + socket = event.getSocket(); assertNotNull(session); byte[] id = session.getId(); assertNotNull(id); @@ -410,16 +414,19 @@ assertNotNull(socket); assertSame(client, socket); assertNull(socket.getHandshakeSession()); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } finally { synchronized (handshakeCompletedListenerCalled) { handshakeCompletedListenerCalled[0] = true; handshakeCompletedListenerCalled.notify(); } handshakeCompletedListenerCalled[0] = true; - socket.removeHandshakeCompletedListener(this); - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new RuntimeException(e); + if (socket != null) { + socket.removeHandshakeCompletedListener(this); + } } } }); @@ -1469,9 +1476,15 @@ * thread will interrupt another thread blocked writing on the same * socket. * + * Currently disabled: If the victim thread is not actually blocked in a write + * call then ConscryptEngineSocket can corrupt the output due to unsynchronized + * concurrent access to the socket's output stream and cause flakes: b/161347005 + * TODO(prb): Re-enable after underlying bug resolved + * * See also b/147323301 where close() triggered an infinite loop instead. */ @Test + @Ignore public void test_SSLSocket_interrupt_write_withAutoclose() throws Exception { final TestSSLContext c = new TestSSLContext.Builder() .clientProtocol(clientVersion) @@ -1482,6 +1495,8 @@ underlying, c.host.getHostName(), c.port, true); final byte[] data = new byte[1024 * 64]; + // TODO(b/161347005): Re-enable once engine-based socket interruption works correctly. + assumeFalse(isConscryptEngineSocket(wrapping)); Future<Void> clientFuture = runAsync(new Callable<Void>() { @Override public Void call() throws Exception { @@ -1495,13 +1510,6 @@ fail(); } catch (SocketException expected) { assertTrue(expected.getMessage().contains("closed")); - } catch (SSLException e) { - // TODO(b/159199048): Workaround for known TreeHugger - // cf_x86 presubmit failure which doesn't occur on non-TreeHugger - // cf_x86 or real devices. - if (!e.getMessage().contains("Engine bytesProduced")) { - throw e; - } } return null; } @@ -1554,7 +1562,7 @@ @Test public void test_SSLSocket_ClientHello_SNI() throws Exception { - ForEachRunner.runNamed(new Callback<SSLSocketFactory>() { + ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = TlsTester @@ -1572,8 +1580,8 @@ @Test public void test_SSLSocket_ClientHello_ALPN() throws Exception { final String[] protocolList = new String[] { "h2", "http/1.1" }; - - ForEachRunner.runNamed(new Callback<SSLSocketFactory>() { + + ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = TlsTester.captureTlsHandshakeClientHello(executor, @@ -2083,23 +2091,6 @@ return "ConscryptEngineSocket".equals(clazz.getSimpleName()); } - private static String osName() { - return System.getProperty("os.name").toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", ""); - } - - private static boolean isLinux() { - return osName().startsWith("linux"); - } - - private static boolean isWindows() { - return osName().startsWith("windows"); - } - - private static boolean isOsx() { - String name = osName(); - return name.startsWith("macosx") || name.startsWith("osx"); - } - private <T> Future<T> runAsync(Callable<T> callable) { return executor.submit(callable); }
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/metrics/CipherSuiteTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/metrics/CipherSuiteTest.java new file mode 100644 index 0000000..63283d1 --- /dev/null +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/metrics/CipherSuiteTest.java
@@ -0,0 +1,23 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.conscrypt.metrics; + +import static org.junit.Assert.assertSame; + +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 CipherSuiteTest { + + @Test + public void consistency() { + for (CipherSuite cipherSuite : CipherSuite.values()) { + assertSame(cipherSuite, CipherSuite.forName(cipherSuite.name())); + } + assertSame(CipherSuite.UNKNOWN_CIPHER_SUITE, CipherSuite.forName("random junk")); + } +}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/metrics/OptionalMethodTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/metrics/OptionalMethodTest.java new file mode 100644 index 0000000..4675042 --- /dev/null +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/metrics/OptionalMethodTest.java
@@ -0,0 +1,66 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.conscrypt.metrics; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +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 OptionalMethodTest { + + @Test + public void workingMethod() { + OptionalMethod substring = + new OptionalMethod(String.class, "substring", int.class, int.class); + assertNotNull(substring); + + assertEquals("put", substring.invoke("input", 2, 5)); + } + + @Test + public void nullClass() { + OptionalMethod substring = + new OptionalMethod(null, "substring", int.class, int.class); + assertNotNull(substring); + + assertNull(substring.invoke("input", 2, 5)); + } + + @Test(expected = NullPointerException.class) + public void nullMethodName() { + new OptionalMethod(String.class, null, int.class, int.class); + } + + @Test + public void nullArgumentClasses() { + OptionalMethod substring = new OptionalMethod(String.class, "substring", int.class, null); + assertNotNull(substring); + + assertNull(substring.invoke("input", 2, 5)); + } + + @Test + public void noSuchMethodName() { + OptionalMethod subwrong = + new OptionalMethod(null, "subwrong", int.class, int.class); + assertNotNull(subwrong); + + assertNull(subwrong.invoke("input", 2, 5)); + } + + @Test + public void noSuchMethodArgs() { + OptionalMethod subwrong = + new OptionalMethod(null, "substring", long.class, byte[].class); + assertNotNull(subwrong); + + assertNull(subwrong.invoke("input", 2, 5)); + } +}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/metrics/ProtocolTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/metrics/ProtocolTest.java new file mode 100644 index 0000000..002cadf --- /dev/null +++ b/repackaged/common/src/test/java/com/android/org/conscrypt/metrics/ProtocolTest.java
@@ -0,0 +1,24 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.conscrypt.metrics; + +import static org.junit.Assert.assertSame; + +import org.junit.Ignore; +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 ProtocolTest { + @Test + @Ignore("Test intended for MTS only.") + public void consistency() { + for (Protocol protocol : Protocol.values()) { + String name = protocol.name().replace('_', '.'); + assertSame(protocol, Protocol.forName(name)); + } + } +}
diff --git a/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Java8PlatformUtil.java b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Java8PlatformUtil.java index 9cdb922..c5b8304 100644 --- a/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Java8PlatformUtil.java +++ b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Java8PlatformUtil.java
@@ -43,15 +43,6 @@ } } - static void getSSLParameters( - SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) { - getSSLParameters(params, impl); - if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) { - params.setServerNames(Collections.singletonList( - (SNIServerName) new SNIHostName(socket.getHostname()))); - } - } - static void setSSLParameters( SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) { setSSLParameters(params, impl); @@ -61,6 +52,16 @@ engine.setHostname(sniHost); } } + + static void getSSLParameters( + SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) { + getSSLParameters(params, impl); + if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) { + params.setServerNames(Collections.singletonList( + (SNIServerName) new SNIHostName(socket.getHostname()))); + } + } + static void getSSLParameters( SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) { getSSLParameters(params, impl);
diff --git a/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Platform.java b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Platform.java index 13166d8..d71d2f8 100644 --- a/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Platform.java +++ b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Platform.java
@@ -100,6 +100,7 @@ getCurveNameMethod = ECParameterSpec.class.getDeclaredMethod("getCurveName"); getCurveNameMethod.setAccessible(true); } catch (Exception ignored) { + // Method not available, leave it as null, which is checked before use } GET_CURVE_NAME_METHOD = getCurveNameMethod; } @@ -194,9 +195,19 @@ } try { - Field f_impl = Socket.class.getDeclaredField("impl"); - f_impl.setAccessible(true); - Object socketImpl = f_impl.get(s); + Method m_getImpl = Socket.class.getDeclaredMethod("getImpl"); + m_getImpl.setAccessible(true); + Object socketImpl = m_getImpl.invoke(s); + try { + Class<?> c_delegatingSocketImpl = Class.forName("java.net.DelegatingSocketImpl"); + if (c_delegatingSocketImpl.isAssignableFrom(socketImpl.getClass())) { + Method m_delegate = c_delegatingSocketImpl.getDeclaredMethod("delegate"); + m_delegate.setAccessible(true); + socketImpl = m_delegate.invoke(socketImpl); + } + } catch (Exception ignored) { + // Ignored + } Field f_fd = SocketImpl.class.getDeclaredField("fd"); f_fd.setAccessible(true); return (FileDescriptor) f_fd.get(socketImpl); @@ -581,10 +592,8 @@ return originalHostName; } catch (InvocationTargetException e) { throw new RuntimeException("Failed to get originalHostName", e); - } catch (ClassNotFoundException ignore) { + } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException ignore) { // passthrough and return addr.getHostAddress() - } catch (IllegalAccessException ignore) { - } catch (NoSuchMethodException ignore) { } return addr.getHostAddress(); @@ -658,9 +667,10 @@ KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); try { ks.load(null, null); - } catch (CertificateException ignored) { } catch (NoSuchAlgorithmException ignored) { - } catch (IOException ignored) { + // TODO(prb): Should this be re-thrown? It happens if "the algorithm used to check + // the integrity of the KeyStore cannot be found". + } catch (IOException | CertificateException ignored) { // We're not loading anything, so ignore it } // Find the highest-priority non-Conscrypt provider that provides a PKIX @@ -708,7 +718,7 @@ return null; } - static CertBlacklist newDefaultBlacklist() { + static CertBlocklist newDefaultBlocklist() { return null; } @@ -786,4 +796,21 @@ }); } } + + public static ConscryptHostnameVerifier getDefaultHostnameVerifier() { + return OkHostnameVerifier.strictInstance(); + } + + @SuppressWarnings("unused") + static long getMillisSinceBoot() { + return 0; + } + + @SuppressWarnings("unused") + static void countTlsHandshake( + boolean success, String protocol, String cipherSuite, long duration) {} + + public static boolean isJavaxCertificateSupported() { + return JAVA_VERSION < 15; + } }
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptSocketTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptSocketTest.java index 4af2a49..2d169ab 100644 --- a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptSocketTest.java +++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptSocketTest.java
@@ -20,24 +20,29 @@ import static com.android.org.conscrypt.TestUtils.openTestFile; import static com.android.org.conscrypt.TestUtils.readTestFile; import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.when; import java.io.IOException; import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -84,12 +89,6 @@ OpenSSLContextImpl context, ServerSocket server, SSLSocketFactory factory) { return null; } - - @Override - Socket newServerSocket(OpenSSLContextImpl context, ServerSocket server, - SSLSocketFactory factory) throws IOException { - return server.accept(); - } }, PLAIN { @Override @@ -97,11 +96,14 @@ SSLSocketFactory factory) throws IOException { return new Socket(server.getInetAddress(), server.getLocalPort()); } - + }, + CHANNEL { @Override - Socket newServerSocket(OpenSSLContextImpl context, ServerSocket server, + Socket newClientSocket(OpenSSLContextImpl context, ServerSocket server, SSLSocketFactory factory) throws IOException { - return server.accept(); + SocketChannel channel = SocketChannel.open(); + channel.connect(server.getLocalSocketAddress()); + return channel.socket(); } }, SSL { @@ -126,8 +128,11 @@ abstract Socket newClientSocket(OpenSSLContextImpl context, ServerSocket server, SSLSocketFactory factory) throws IOException; - abstract Socket newServerSocket(OpenSSLContextImpl context, ServerSocket server, - SSLSocketFactory factory) throws IOException; + + Socket newServerSocket(OpenSSLContextImpl context, ServerSocket server, + SSLSocketFactory factory) throws IOException { + return server.accept(); + } } /** @@ -197,15 +202,47 @@ } } - @Parameters(name = "{0} wrapping {1}") + /** + * @hide This class is not part of the Android public SDK API + */ + public enum ServerSocketType { + PLAIN { + @Override + public ServerSocket newServerSocket() throws IOException { + return new ServerSocket(0, 50, TestUtils.getLoopbackAddress()); + } + }, + CHANNEL { + @Override + public ServerSocket newServerSocket() throws IOException { + ServerSocketChannel channel = ServerSocketChannel.open(); + InetAddress localAddress = TestUtils.getLoopbackAddress(); + channel.socket().bind(new InetSocketAddress(localAddress.getHostName(), 0), 50); + return channel.socket(); + } + }; + public abstract ServerSocket newServerSocket() throws IOException; + } + + @Parameters(name = "{0} wrapping {1} connecting to {2}") public static Object[][] data() { return new Object[][] { - {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.NONE}, - {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.PLAIN}, - // Not supported: {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.SSL}, - {SocketType.ENGINE, UnderlyingSocketType.NONE}, - {SocketType.ENGINE, UnderlyingSocketType.PLAIN}, - {SocketType.ENGINE, UnderlyingSocketType.SSL}}; + {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.NONE, ServerSocketType.PLAIN}, + {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.NONE, ServerSocketType.CHANNEL}, + {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.PLAIN, ServerSocketType.PLAIN}, + {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.PLAIN, ServerSocketType.CHANNEL}, + {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.CHANNEL, ServerSocketType.PLAIN}, + {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.CHANNEL, + ServerSocketType.CHANNEL}, + // Not supported: {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.SSL}, + {SocketType.ENGINE, UnderlyingSocketType.NONE, ServerSocketType.PLAIN}, + {SocketType.ENGINE, UnderlyingSocketType.NONE, ServerSocketType.CHANNEL}, + {SocketType.ENGINE, UnderlyingSocketType.PLAIN, ServerSocketType.PLAIN}, + {SocketType.ENGINE, UnderlyingSocketType.PLAIN, ServerSocketType.CHANNEL}, + {SocketType.ENGINE, UnderlyingSocketType.CHANNEL, ServerSocketType.PLAIN}, + {SocketType.ENGINE, UnderlyingSocketType.CHANNEL, ServerSocketType.CHANNEL}, + {SocketType.ENGINE, UnderlyingSocketType.SSL, ServerSocketType.PLAIN}, + {SocketType.ENGINE, UnderlyingSocketType.SSL, ServerSocketType.CHANNEL}}; } @Parameter @@ -214,6 +251,8 @@ @Parameter(1) public UnderlyingSocketType underlyingSocketType; + @Parameter(2) public ServerSocketType serverSocketType; + private X509Certificate ca; private X509Certificate cert; private X509Certificate certEmbedded; @@ -221,6 +260,7 @@ private Field contextSSLParameters; private ExecutorService executor; + private final Random random = new Random(System.currentTimeMillis()); @Before public void setUp() throws Exception { @@ -394,7 +434,7 @@ } void doHandshake() throws Exception { - ServerSocket listener = newServerSocket(); + ServerSocket listener = serverSocketType.newServerSocket(); Future<AbstractConscryptSocket> clientFuture = handshake(listener, clientHooks); Future<AbstractConscryptSocket> serverFuture = handshake(listener, serverHooks); @@ -570,7 +610,7 @@ @Test @SuppressWarnings("deprecation") public void setAlpnProtocolWithNullShouldSucceed() throws Exception { - ServerSocket listening = newServerSocket(); + ServerSocket listening = serverSocketType.newServerSocket(); OpenSSLSocketImpl clientSocket = null; try { Socket underlying = new Socket(listening.getInetAddress(), listening.getLocalPort()); @@ -591,7 +631,7 @@ // http://b/27250522 @Test public void test_setSoTimeout_doesNotCreateSocketImpl() throws Exception { - ServerSocket listening = newServerSocket(); + ServerSocket listening = serverSocketType.newServerSocket(); try { Socket underlying = new Socket(listening.getInetAddress(), listening.getLocalPort()); Socket socket = socketType.newClientSocket( @@ -654,7 +694,44 @@ assertEquals(alpnProtocol, Conscrypt.getApplicationProtocol(connection.client)); } - private static ServerSocket newServerSocket() throws IOException { - return new ServerSocket(0, 50, TestUtils.getLoopbackAddress()); + @Test + public void dataFlows() throws Exception { + final TestConnection connection = + new TestConnection(new X509Certificate[] {cert, ca}, certKey); + connection.doHandshakeSuccess(); + + // Basic data flow assurance. Send random buffers in each direction, each less than 16K + // so should fit in a single TLS packet. 50% chance of sending in each direction on + // each iteration to randomize the flow. + for (int i = 0; i < 50; i++) { + if (random.nextBoolean()) { + sendData(connection.client, connection.server, randomBuffer()); + } + if (random.nextBoolean()) { + sendData(connection.server, connection.client, randomBuffer()); + } + } + } + + private void sendData(SSLSocket source, final SSLSocket destination, byte[] data) + throws Exception { + final byte[] received = new byte[data.length]; + + Future<Integer> readFuture = executor.submit(new Callable<Integer>() { + @Override + public Integer call() throws Exception { + return destination.getInputStream().read(received); + } + }); + + source.getOutputStream().write(data); + assertEquals(data.length, (int) readFuture.get()); + assertArrayEquals(data, received); + } + + private byte[] randomBuffer() { + byte[] buffer = new byte[random.nextInt(16 * 1024)]; + random.nextBytes(buffer); + return buffer; } }
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeCryptoTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeCryptoTest.java index 1569348..ea274a1 100644 --- a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeCryptoTest.java +++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeCryptoTest.java
@@ -161,6 +161,7 @@ /** * Lazily create shared test certificates. */ + @SuppressWarnings("JdkObsolete") // Public API KeyStore.aliases() uses Enumeration private static synchronized void initCerts() { if (SERVER_PRIVATE_KEY != null) { return;
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLX509CertificateTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLX509CertificateTest.java index 07439e0..ab89f45 100644 --- a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLX509CertificateTest.java +++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLX509CertificateTest.java
@@ -19,6 +19,7 @@ import static com.android.org.conscrypt.TestUtils.openTestFile; +import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; @@ -26,10 +27,11 @@ import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import junit.framework.TestCase; -import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException; /** * @hide This class is not part of the Android public SDK API @@ -47,10 +49,29 @@ // Mark the field as non-final on JVM that need it. try { - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(targetUID, targetUID.getModifiers() & ~Modifier.FINAL); - } catch (NoSuchFieldException ignored) { + Field modifiersField = null; + try { + modifiersField = Field.class.getDeclaredField("modifiers"); + } catch (NoSuchFieldException e) { + try { + Method getDeclaredFields0 = + Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); + getDeclaredFields0.setAccessible(true); + Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false); + for (Field field : fields) { + if ("modifiers".equals(field.getName())) { + modifiersField = field; + break; + } + } + } catch (NoSuchMethodException | InvocationTargetException ignored) { + } + } + if (modifiersField != null) { + modifiersField.setAccessible(true); + modifiersField.setInt(targetUID, targetUID.getModifiers() & ~Modifier.FINAL); + } + } catch (Exception ignored) { } targetUID.set(null, clDesc.getSerialVersionUID()); @@ -109,32 +130,32 @@ certPoisoned.getTBSCertificate(), cert.getTBSCertificate())); - assertTrue(Arrays.equals( - certPoisoned.withDeletedExtension(CT_POISON_EXTENSION).getTBSCertificate(), - cert.getTBSCertificate())); + assertTrue( + Arrays.equals(certPoisoned.getTBSCertificateWithoutExtension(CT_POISON_EXTENSION), + cert.getTBSCertificate())); } public void test_deletingExtensionMakesCopy() throws Exception { - /* Calling withDeletedExtension should not modify the original certificate, only make a copy. + /* Calling getTBSCertificateWithoutExtension should not modify the original certificate. * Make sure the extension is still present in the original object. */ OpenSSLX509Certificate certPoisoned = loadTestCertificate("cert-ct-poisoned.pem"); assertTrue(certPoisoned.getCriticalExtensionOIDs().contains(CT_POISON_EXTENSION)); - OpenSSLX509Certificate certWithoutExtension = certPoisoned.withDeletedExtension(CT_POISON_EXTENSION); - + certPoisoned.getTBSCertificateWithoutExtension(CT_POISON_EXTENSION); assertTrue(certPoisoned.getCriticalExtensionOIDs().contains(CT_POISON_EXTENSION)); - assertFalse(certWithoutExtension.getCriticalExtensionOIDs().contains(CT_POISON_EXTENSION)); } public void test_deletingMissingExtension() throws Exception { - /* withDeletedExtension should be safe to call on a certificate without the extension, and - * return an identical copy. + /* getTBSCertificateWithoutExtension should throw on a certificate without the extension. */ OpenSSLX509Certificate cert = loadTestCertificate("cert.pem"); assertFalse(cert.getCriticalExtensionOIDs().contains(CT_POISON_EXTENSION)); - OpenSSLX509Certificate cert2 = cert.withDeletedExtension(CT_POISON_EXTENSION); - assertEquals(cert, cert2); + try { + cert.getTBSCertificateWithoutExtension(CT_POISON_EXTENSION); + fail(); + } catch (IllegalArgumentException expected) { + } } }
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ServerSessionContextTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ServerSessionContextTest.java index c5986cc..2502615 100644 --- a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ServerSessionContextTest.java +++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ServerSessionContextTest.java
@@ -38,6 +38,7 @@ } @Override + @SuppressWarnings("JdkObsolete") // Public API SSLSessionContext.getIds() uses Enumeration int size(ServerSessionContext context) { int count = 0; Enumeration<byte[]> ids = context.getIds();
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/CertBlacklistImpl.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/CertBlacklistImpl.java deleted file mode 100644 index 2b9ab24..0000000 --- a/repackaged/platform/src/main/java/com/android/org/conscrypt/CertBlacklistImpl.java +++ /dev/null
@@ -1,301 +0,0 @@ -/* GENERATED SOURCE. DO NOT MODIFY. */ -/* - * Copyright (C) 2012 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 java.nio.charset.StandardCharsets.UTF_8; - -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.math.BigInteger; -import java.security.GeneralSecurityException; -import java.security.MessageDigest; -import java.security.PublicKey; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @hide This class is not part of the Android public SDK API - */ -@Internal -public final class CertBlacklistImpl implements CertBlacklist { - private static final Logger logger = Logger.getLogger(CertBlacklistImpl.class.getName()); - - private final Set<BigInteger> serialBlacklist; - private final Set<ByteString> pubkeyBlacklist; - - /** - * public for testing only. - */ - public CertBlacklistImpl(Set<BigInteger> serialBlacklist, Set<ByteString> pubkeyBlacklist) { - this.serialBlacklist = serialBlacklist; - this.pubkeyBlacklist = pubkeyBlacklist; - } - - public static CertBlacklist getDefault() { - String androidData = System.getenv("ANDROID_DATA"); - String blacklistRoot = androidData + "/misc/keychain/"; - String defaultPubkeyBlacklistPath = blacklistRoot + "pubkey_blacklist.txt"; - String defaultSerialBlacklistPath = blacklistRoot + "serial_blacklist.txt"; - - Set<ByteString> pubkeyBlacklist = readPublicKeyBlackList(defaultPubkeyBlacklistPath); - Set<BigInteger> serialBlacklist = readSerialBlackList(defaultSerialBlacklistPath); - return new CertBlacklistImpl(serialBlacklist, pubkeyBlacklist); - } - - private static boolean isHex(String value) { - try { - new BigInteger(value, 16); - return true; - } catch (NumberFormatException e) { - logger.log(Level.WARNING, "Could not parse hex value " + value, e); - return false; - } - } - - private static boolean isPubkeyHash(String value) { - if (value.length() != 40) { - logger.log(Level.WARNING, "Invalid pubkey hash length: " + value.length()); - return false; - } - return isHex(value); - } - - private static String readBlacklist(String path) { - try { - return readFileAsString(path); - } catch (FileNotFoundException ignored) { - } catch (IOException e) { - logger.log(Level.WARNING, "Could not read blacklist", e); - } - return ""; - } - - // From IoUtils.readFileAsString - private static String readFileAsString(String path) throws IOException { - return readFileAsBytes(path).toString("UTF-8"); - } - - // Based on IoUtils.readFileAsBytes - private static ByteArrayOutputStream readFileAsBytes(String path) throws IOException { - RandomAccessFile f = null; - try { - f = new RandomAccessFile(path, "r"); - ByteArrayOutputStream bytes = new ByteArrayOutputStream((int) f.length()); - byte[] buffer = new byte[8192]; - while (true) { - int byteCount = f.read(buffer); - if (byteCount == -1) { - return bytes; - } - bytes.write(buffer, 0, byteCount); - } - } finally { - closeQuietly(f); - } - } - - // Base on IoUtils.closeQuietly - private static void closeQuietly(Closeable closeable) { - if (closeable != null) { - try { - closeable.close(); - } catch (RuntimeException rethrown) { - throw rethrown; - } catch (Exception ignored) { - } - } - } - - private static Set<BigInteger> readSerialBlackList(String path) { - - /* Start out with a base set of known bad values. - * - * WARNING: Do not add short serials to this list! - * - * Since this currently doesn't compare the serial + issuer, you - * should only add serials that have enough entropy here. Short - * serials may inadvertently match a certificate that was issued - * not in compliance with the Baseline Requirements. - */ - Set<BigInteger> bl = new HashSet<BigInteger>(Arrays.asList( - // From http://src.chromium.org/viewvc/chrome/trunk/src/net/base/x509_certificate.cc?revision=78748&view=markup - // Not a real certificate. For testing only. - new BigInteger("077a59bcd53459601ca6907267a6dd1c", 16), - new BigInteger("047ecbe9fca55f7bd09eae36e10cae1e", 16), - new BigInteger("d8f35f4eb7872b2dab0692e315382fb0", 16), - new BigInteger("b0b7133ed096f9b56fae91c874bd3ac0", 16), - new BigInteger("9239d5348f40d1695a745470e1f23f43", 16), - new BigInteger("e9028b9578e415dc1a710a2b88154447", 16), - new BigInteger("d7558fdaf5f1105bb213282b707729a3", 16), - new BigInteger("f5c86af36162f13a64f54f6dc9587c06", 16), - new BigInteger("392a434f0e07df1f8aa305de34e0c229", 16), - new BigInteger("3e75ced46b693021218830ae86a82a71", 16) - )); - - // attempt to augment it with values taken from gservices - String serialBlacklist = readBlacklist(path); - if (!serialBlacklist.equals("")) { - for (String value : serialBlacklist.split(",", -1)) { - try { - bl.add(new BigInteger(value, 16)); - } catch (NumberFormatException e) { - logger.log(Level.WARNING, "Tried to blacklist invalid serial number " + value, e); - } - } - } - - // whether that succeeds or fails, send it on its merry way - return Collections.unmodifiableSet(bl); - } - - private static Set<ByteString> readPublicKeyBlackList(String path) { - // start out with a base set of known bad values - Set<ByteString> bl = new HashSet<ByteString>(toByteStrings( - // Blacklist test cert for CTS. The cert and key can be found in - // src/test/resources/blacklist_test_ca.pem and - // src/test/resources/blacklist_test_ca_key.pem. - "bae78e6bed65a2bf60ddedde7fd91e825865e93d".getBytes(UTF_8), - // From - // http://src.chromium.org/viewvc/chrome/branches/782/src/net/base/x509_certificate.cc?r1=98750&r2=98749&pathrev=98750 - // C=NL, O=DigiNotar, CN=DigiNotar Root CA/emailAddress=info@diginotar.nl - "410f36363258f30b347d12ce4863e433437806a8".getBytes(UTF_8), - // Subject: CN=DigiNotar Cyber CA - // Issuer: CN=GTE CyberTrust Global Root - "ba3e7bd38cd7e1e6b9cd4c219962e59d7a2f4e37".getBytes(UTF_8), - // Subject: CN=DigiNotar Services 1024 CA - // Issuer: CN=Entrust.net - "e23b8d105f87710a68d9248050ebefc627be4ca6".getBytes(UTF_8), - // Subject: CN=DigiNotar PKIoverheid CA Organisatie - G2 - // Issuer: CN=Staat der Nederlanden Organisatie CA - G2 - "7b2e16bc39bcd72b456e9f055d1de615b74945db".getBytes(UTF_8), - // Subject: CN=DigiNotar PKIoverheid CA Overheid en Bedrijven - // Issuer: CN=Staat der Nederlanden Overheid CA - "e8f91200c65cee16e039b9f883841661635f81c5".getBytes(UTF_8), - // From http://src.chromium.org/viewvc/chrome?view=rev&revision=108479 - // Subject: O=Digicert Sdn. Bhd. - // Issuer: CN=GTE CyberTrust Global Root - "0129bcd5b448ae8d2496d1c3e19723919088e152".getBytes(UTF_8), - // Subject: - // CN=e-islem.kktcmerkezbankasi.org/emailAddress=ileti@kktcmerkezbankasi.org Issuer: - // CN=T\xC3\x9CRKTRUST Elektronik Sunucu Sertifikas\xC4\xB1 Hizmetleri - "5f3ab33d55007054bc5e3e5553cd8d8465d77c61".getBytes(UTF_8), - // Subject: CN=*.EGO.GOV.TR 93 - // Issuer: CN=T\xC3\x9CRKTRUST Elektronik Sunucu Sertifikas\xC4\xB1 Hizmetleri - "783333c9687df63377efceddd82efa9101913e8e".getBytes(UTF_8), - // Subject: Subject: C=FR, O=DG Tr\xC3\xA9sor, CN=AC DG Tr\xC3\xA9sor SSL - // Issuer: C=FR, O=DGTPE, CN=AC DGTPE Signature Authentification - "3ecf4bbbe46096d514bb539bb913d77aa4ef31bf".getBytes(UTF_8))); - - // attempt to augment it with values taken from gservices - String pubkeyBlacklist = readBlacklist(path); - if (!pubkeyBlacklist.equals("")) { - for (String value : pubkeyBlacklist.split(",", -1)) { - value = value.trim(); - if (isPubkeyHash(value)) { - bl.add(new ByteString(value.getBytes(UTF_8))); - } else { - logger.log(Level.WARNING, "Tried to blacklist invalid pubkey " + value); - } - } - } - - return bl; - } - - @Override - public boolean isPublicKeyBlackListed(PublicKey publicKey) { - byte[] encoded = publicKey.getEncoded(); - MessageDigest md; - try { - md = MessageDigest.getInstance("SHA1"); - } catch (GeneralSecurityException e) { - logger.log(Level.SEVERE, "Unable to get SHA1 MessageDigest", e); - return false; - } - byte[] out = toHex(md.digest(encoded)); - for (ByteString blacklisted : pubkeyBlacklist) { - if (Arrays.equals(blacklisted.bytes, out)) { - return true; - } - } - return false; - } - - private static final byte[] HEX_TABLE = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', - (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', - (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f'}; - - private static byte[] toHex(byte[] in) { - byte[] out = new byte[in.length * 2]; - int outIndex = 0; - for (int i = 0; i < in.length; i++) { - int value = in[i] & 0xff; - out[outIndex++] = HEX_TABLE[value >> 4]; - out[outIndex++] = HEX_TABLE[value & 0xf]; - } - return out; - } - - @Override - public boolean isSerialNumberBlackListed(BigInteger serial) { - return serialBlacklist.contains(serial); - } - - private static List<ByteString> toByteStrings(byte[]... allBytes) { - List<ByteString> byteStrings = new ArrayList<>(allBytes.length + 1); - for (byte[] bytes : allBytes) { - byteStrings.add(new ByteString(bytes)); - } - return byteStrings; - } - - private static class ByteString { - final byte[] bytes; - - public ByteString(byte[] bytes) { - this.bytes = bytes; - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (!(o instanceof ByteString)) { - return false; - } - - ByteString other = (ByteString) o; - return Arrays.equals(bytes, other.bytes); - } - - @Override - public int hashCode() { - return Arrays.hashCode(bytes); - } - } -}
diff --git a/platform/src/main/java/org/conscrypt/CertBlacklistImpl.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/CertBlocklistImpl.java similarity index 81% copy from platform/src/main/java/org/conscrypt/CertBlacklistImpl.java copy to repackaged/platform/src/main/java/com/android/org/conscrypt/CertBlocklistImpl.java index c2cdd80..06de29a 100644 --- a/platform/src/main/java/org/conscrypt/CertBlacklistImpl.java +++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/CertBlocklistImpl.java
@@ -1,3 +1,4 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ /* * Copyright (C) 2012 The Android Open Source Project * @@ -14,7 +15,7 @@ * limitations under the License. */ -package org.conscrypt; +package com.android.org.conscrypt; import static java.nio.charset.StandardCharsets.UTF_8; @@ -36,30 +37,33 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * @hide This class is not part of the Android public SDK API + */ @Internal -public final class CertBlacklistImpl implements CertBlacklist { - private static final Logger logger = Logger.getLogger(CertBlacklistImpl.class.getName()); +public final class CertBlocklistImpl implements CertBlocklist { + private static final Logger logger = Logger.getLogger(CertBlocklistImpl.class.getName()); - private final Set<BigInteger> serialBlacklist; - private final Set<ByteString> pubkeyBlacklist; + private final Set<BigInteger> serialBlocklist; + private final Set<ByteString> pubkeyBlocklist; /** * public for testing only. */ - public CertBlacklistImpl(Set<BigInteger> serialBlacklist, Set<ByteString> pubkeyBlacklist) { - this.serialBlacklist = serialBlacklist; - this.pubkeyBlacklist = pubkeyBlacklist; + public CertBlocklistImpl(Set<BigInteger> serialBlocklist, Set<ByteString> pubkeyBlocklist) { + this.serialBlocklist = serialBlocklist; + this.pubkeyBlocklist = pubkeyBlocklist; } - public static CertBlacklist getDefault() { + public static CertBlocklist getDefault() { String androidData = System.getenv("ANDROID_DATA"); - String blacklistRoot = androidData + "/misc/keychain/"; - String defaultPubkeyBlacklistPath = blacklistRoot + "pubkey_blacklist.txt"; - String defaultSerialBlacklistPath = blacklistRoot + "serial_blacklist.txt"; + String blocklistRoot = androidData + "/misc/keychain/"; + String defaultPubkeyBlocklistPath = blocklistRoot + "pubkey_blacklist.txt"; + String defaultSerialBlocklistPath = blocklistRoot + "serial_blacklist.txt"; - Set<ByteString> pubkeyBlacklist = readPublicKeyBlackList(defaultPubkeyBlacklistPath); - Set<BigInteger> serialBlacklist = readSerialBlackList(defaultSerialBlacklistPath); - return new CertBlacklistImpl(serialBlacklist, pubkeyBlacklist); + Set<ByteString> pubkeyBlocklist = readPublicKeyBlockList(defaultPubkeyBlocklistPath); + Set<BigInteger> serialBlocklist = readSerialBlockList(defaultSerialBlocklistPath); + return new CertBlocklistImpl(serialBlocklist, pubkeyBlocklist); } private static boolean isHex(String value) { @@ -80,12 +84,13 @@ return isHex(value); } - private static String readBlacklist(String path) { + private static String readBlocklist(String path) { try { return readFileAsString(path); } catch (FileNotFoundException ignored) { + // Ignored } catch (IOException e) { - logger.log(Level.WARNING, "Could not read blacklist", e); + logger.log(Level.WARNING, "Could not read blocklist", e); } return ""; } @@ -122,11 +127,12 @@ } catch (RuntimeException rethrown) { throw rethrown; } catch (Exception ignored) { + // Ignored } } } - private static Set<BigInteger> readSerialBlackList(String path) { + private static Set<BigInteger> readSerialBlockList(String path) { /* Start out with a base set of known bad values. * @@ -153,9 +159,9 @@ )); // attempt to augment it with values taken from gservices - String serialBlacklist = readBlacklist(path); - if (!serialBlacklist.equals("")) { - for (String value : serialBlacklist.split(",", -1)) { + String serialBlocklist = readBlocklist(path); + if (!serialBlocklist.equals("")) { + for (String value : serialBlocklist.split(",", -1)) { try { bl.add(new BigInteger(value, 16)); } catch (NumberFormatException e) { @@ -168,13 +174,13 @@ return Collections.unmodifiableSet(bl); } - private static Set<ByteString> readPublicKeyBlackList(String path) { + private static Set<ByteString> readPublicKeyBlockList(String path) { // start out with a base set of known bad values Set<ByteString> bl = new HashSet<ByteString>(toByteStrings( - // Blacklist test cert for CTS. The cert and key can be found in - // src/test/resources/blacklist_test_ca.pem and - // src/test/resources/blacklist_test_ca_key.pem. + // Blocklist test cert for CTS. The cert and key can be found in + // src/test/resources/blocklist_test_ca.pem and + // src/test/resources/blocklist_test_ca_key.pem. "bae78e6bed65a2bf60ddedde7fd91e825865e93d".getBytes(UTF_8), // From http://src.chromium.org/viewvc/chrome/branches/782/src/net/base/x509_certificate.cc?r1=98750&r2=98749&pathrev=98750 // C=NL, O=DigiNotar, CN=DigiNotar Root CA/emailAddress=info@diginotar.nl @@ -207,14 +213,14 @@ )); // attempt to augment it with values taken from gservices - String pubkeyBlacklist = readBlacklist(path); - if (!pubkeyBlacklist.equals("")) { - for (String value : pubkeyBlacklist.split(",", -1)) { + String pubkeyBlocklist = readBlocklist(path); + if (!pubkeyBlocklist.equals("")) { + for (String value : pubkeyBlocklist.split(",", -1)) { value = value.trim(); if (isPubkeyHash(value)) { bl.add(new ByteString(value.getBytes(UTF_8))); } else { - logger.log(Level.WARNING, "Tried to blacklist invalid pubkey " + value); + logger.log(Level.WARNING, "Tried to blocklist invalid pubkey " + value); } } } @@ -223,7 +229,7 @@ } @Override - public boolean isPublicKeyBlackListed(PublicKey publicKey) { + public boolean isPublicKeyBlockListed(PublicKey publicKey) { byte[] encoded = publicKey.getEncoded(); MessageDigest md; try { @@ -233,8 +239,8 @@ return false; } byte[] out = toHex(md.digest(encoded)); - for (ByteString blacklisted : pubkeyBlacklist) { - if (Arrays.equals(blacklisted.bytes, out)) { + for (ByteString blocklisted : pubkeyBlocklist) { + if (Arrays.equals(blocklisted.bytes, out)) { return true; } } @@ -257,8 +263,8 @@ } @Override - public boolean isSerialNumberBlackListed(BigInteger serial) { - return serialBlacklist.contains(serial); + public boolean isSerialNumberBlockListed(BigInteger serial) { + return serialBlocklist.contains(serial); } private static List<ByteString> toByteStrings(byte[]... allBytes) {
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/Platform.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/Platform.java index 573bbb2..8f9b11e 100644 --- a/repackaged/platform/src/main/java/com/android/org/conscrypt/Platform.java +++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/Platform.java
@@ -27,10 +27,14 @@ import com.android.org.conscrypt.ct.CTLogStoreImpl; import com.android.org.conscrypt.ct.CTPolicy; import com.android.org.conscrypt.ct.CTPolicyImpl; +import com.android.org.conscrypt.metrics.CipherSuite; +import com.android.org.conscrypt.metrics.ConscryptStatsLog; +import com.android.org.conscrypt.metrics.Protocol; import dalvik.system.BlockGuard; import dalvik.system.CloseGuard; import java.io.FileDescriptor; import java.io.IOException; +import java.lang.System; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -53,6 +57,7 @@ import java.util.Collections; import java.util.List; import javax.crypto.spec.GCMParameterSpec; +import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SNIHostName; import javax.net.ssl.SNIMatcher; import javax.net.ssl.SNIServerName; @@ -504,8 +509,8 @@ return new TrustedCertificateStore(); } - static CertBlacklist newDefaultBlacklist() { - return CertBlacklistImpl.getDefault(); + static CertBlocklist newDefaultBlocklist() { + return CertBlocklistImpl.getDefault(); } static CTLogStore newDefaultLogStore() { @@ -531,4 +536,30 @@ } return false; } + + public static ConscryptHostnameVerifier getDefaultHostnameVerifier() { + return Conscrypt.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier()); + } + + /** + * Returns milliseconds elapsed since boot, including time spent in sleep. + * @return long number of milliseconds elapsed since boot + */ + static long getMillisSinceBoot() { + return System.currentTimeMillis(); + } + + static void countTlsHandshake( + boolean success, String protocol, String cipherSuite, long duration) { + Protocol proto = Protocol.forName(protocol); + CipherSuite suite = CipherSuite.forName(cipherSuite); + int dur = (int) duration; + + ConscryptStatsLog.write(ConscryptStatsLog.TLS_HANDSHAKE_REPORTED, success, proto.getId(), + suite.getId(), dur); + } + + public static boolean isJavaxCertificateSupported() { + return true; + } }
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 ba233be..16128b9 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
@@ -60,9 +60,8 @@ * <p>In addition to supporting the {@code * TrustedCertificateKeyStoreSpi} implementation, {@code * TrustedCertificateStore} also provides the additional public - * methods {@link #isTrustAnchor} and {@link #findIssuer} to allow - * efficient lookup operations for CAs again based on the file naming - * convention. + * method {@link #findIssuer} to allow efficient lookup operations + * for CAs again based on the file naming convention. * * <p>The KeyChainService users the {@link installCertificate} and * {@link #deleteCertificateEntry} to install user CAs as well as @@ -82,17 +81,16 @@ * user. * @hide This class is not part of the Android public SDK API */ -@libcore.api.CorePlatformApi +@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Internal public class TrustedCertificateStore implements ConscryptCertStore { - private static final String PREFIX_SYSTEM = "system:"; private static final String PREFIX_USER = "user:"; public static final boolean isSystem(String alias) { return alias.startsWith(PREFIX_SYSTEM); } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public static final boolean isUser(String alias) { return alias.startsWith(PREFIX_USER); } @@ -119,7 +117,7 @@ } } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public static void setDefaultUserDirectory(File root) { PreloadHolder.defaultCaCertsAddedDir = new File(root, "cacerts-added"); PreloadHolder.defaultCaCertsDeletedDir = new File(root, "cacerts-removed"); @@ -131,7 +129,7 @@ @android.compat.annotation .UnsupportedAppUsage - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public TrustedCertificateStore() { this(PreloadHolder.defaultCaCertsSystemDir, PreloadHolder.defaultCaCertsAddedDir, PreloadHolder.defaultCaCertsDeletedDir); @@ -143,14 +141,13 @@ this.deletedDir = deletedDir; } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public Certificate getCertificate(String alias) { return getCertificate(alias, false); } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public Certificate getCertificate(String alias, boolean includeDeletedSystem) { - File file = fileForAlias(alias); if (file == null || (isUser(alias) && isTombstone(file))) { return null; @@ -227,7 +224,8 @@ return getCertificateFile(deletedDir, x).exists(); } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) + @SuppressWarnings("JdkObsolete") // Used in public API TrustedCertificateKeyStoreSpi public Date getCreationDate(String alias) { // containsAlias check ensures the later fileForAlias result // was not a deleted system cert. @@ -245,7 +243,7 @@ return new Date(time); } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public Set<String> aliases() { Set<String> result = new HashSet<String>(); addAliases(result, PREFIX_USER, addedDir); @@ -253,7 +251,7 @@ return result; } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public Set<String> userAliases() { Set<String> result = new HashSet<String>(); addAliases(result, PREFIX_USER, addedDir); @@ -273,7 +271,7 @@ } } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public Set<String> allSystemAliases() { Set<String> result = new HashSet<String>(); String[] files = systemDir.list(); @@ -289,7 +287,7 @@ return result; } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public boolean containsAlias(String alias) { return containsAlias(alias, false); } @@ -298,12 +296,12 @@ return getCertificate(alias, includeDeletedSystem) != null; } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public String getCertificateAlias(Certificate c) { return getCertificateAlias(c, false); } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public String getCertificateAlias(Certificate c, boolean includeDeletedSystem) { if (c == null || !(c instanceof X509Certificate)) { return null; @@ -327,7 +325,7 @@ * Returns true to indicate that the certificate was added by the * user, false otherwise. */ - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public boolean isUserAddedCertificate(X509Certificate cert) { return getCertificateFile(addedDir, cert).exists(); } @@ -340,7 +338,7 @@ * * @VisibleForTesting */ - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public File getCertificateFile(File dir, final X509Certificate x) { // compare X509Certificate.getEncoded values CertSelector selector = new CertSelector() { @@ -361,7 +359,7 @@ * with other differences (for example when switching signature * from md2WithRSAEncryption to SHA1withRSA) */ - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Override public X509Certificate getTrustAnchor(final X509Certificate c) { // compare X509Certificate.getPublicKey values @@ -393,7 +391,7 @@ * TrustManagerImpl} to locate the CA certificate that signed the * provided {@code X509Certificate}. */ - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public X509Certificate findIssuer(final X509Certificate c) { // match on verified issuer of Certificate CertSelector selector = new CertSelector() { @@ -419,7 +417,7 @@ return null; } - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) @Override public Set<X509Certificate> findAllIssuers(final X509Certificate c) { Set<X509Certificate> issuers = null; @@ -502,7 +500,7 @@ */ @android.compat.annotation .UnsupportedAppUsage - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public List<X509Certificate> getCertificateChain(X509Certificate leaf) throws CertificateException { final LinkedHashSet<OpenSSLX509Certificate> chain @@ -600,7 +598,7 @@ * silently ignores the certificate if it already exists in the * store. */ - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public void installCertificate(X509Certificate cert) throws IOException, CertificateException { if (cert == null) { throw new NullPointerException("cert == null"); @@ -636,7 +634,7 @@ * only. Instead, this is used by the {@code KeyChainService} to * delete CA certificates. */ - @libcore.api.CorePlatformApi + @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) public void deleteCertificateEntry(String alias) throws IOException, CertificateException { if (alias == null) { return;
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/CTLogStoreImpl.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/CTLogStoreImpl.java index 37c84c8..d34b683 100644 --- a/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/CTLogStoreImpl.java +++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/CTLogStoreImpl.java
@@ -17,6 +17,8 @@ package com.android.org.conscrypt.ct; +import com.android.org.conscrypt.Internal; +import com.android.org.conscrypt.InternalUtil; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; @@ -24,6 +26,7 @@ import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; @@ -33,15 +36,13 @@ import java.util.HashSet; import java.util.Scanner; import java.util.Set; -import com.android.org.conscrypt.Internal; -import com.android.org.conscrypt.InternalUtil; /** * @hide This class is not part of the Android public SDK API */ @Internal public class CTLogStoreImpl implements CTLogStore { - private static final Charset US_ASCII = Charset.forName("US-ASCII"); + private static final Charset US_ASCII = StandardCharsets.US_ASCII; /** * Thrown when parsing of a log file fails. @@ -75,12 +76,13 @@ defaultSystemLogDir = new File(ANDROID_ROOT + "/etc/security/ct_known_logs/"); } - private File userLogDir; - private File systemLogDir; - private CTLogInfo[] fallbackLogs; + private final File userLogDir; + private final File systemLogDir; + private final CTLogInfo[] fallbackLogs; - private HashMap<ByteBuffer, CTLogInfo> logCache = new HashMap<>(); - private Set<ByteBuffer> missingLogCache = Collections.synchronizedSet(new HashSet<ByteBuffer>()); + private final HashMap<ByteBuffer, CTLogInfo> logCache = new HashMap<>(); + private final Set<ByteBuffer> missingLogCache = + Collections.synchronizedSet(new HashSet<ByteBuffer>()); public CTLogStoreImpl() { this(defaultUserLogDir, @@ -121,13 +123,17 @@ return loadLog(new File(userLogDir, filename)); } catch (InvalidLogFileException e) { return null; - } catch (FileNotFoundException e) {} + } catch (FileNotFoundException e) { + // Ignored + } try { return loadLog(new File(systemLogDir, filename)); } catch (InvalidLogFileException e) { return null; - } catch (FileNotFoundException e) {} + } catch (FileNotFoundException e) { + // Ignored + } // If the updateable logs dont exist then use the fallback logs. if (!userLogDir.exists()) {
diff --git a/repackaged/platform/src/test/java/com/android/org/conscrypt/CertBlacklistTest.java b/repackaged/platform/src/test/java/com/android/org/conscrypt/CertBlacklistTest.java deleted file mode 100644 index b45e2d3..0000000 --- a/repackaged/platform/src/test/java/com/android/org/conscrypt/CertBlacklistTest.java +++ /dev/null
@@ -1,139 +0,0 @@ -/* GENERATED SOURCE. DO NOT MODIFY. */ -/* - * Copyright (C) 2016 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.assertTrue; -import static org.junit.Assert.fail; - -import java.io.InputStream; -import java.security.KeyStore; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.Collection; -import javax.net.ssl.X509TrustManager; -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 CertBlacklistTest { - private static final String BLACKLIST_CA = "test_blacklist_ca.pem"; - private static final String BLACKLISTED_CHAIN = "blacklist_test_chain.pem"; - private static final String BLACKLIST_FALLBACK_VALID_CA = "blacklist_test_valid_ca.pem"; - private static final String BLACKLISTED_VALID_CHAIN = "blacklist_test_valid_chain.pem"; - - /** - * Ensure that the test blacklisted CA is actually blacklisted by default. - */ - @Test - public void testBlacklistedPublicKey() throws Exception { - // This class was renamed in the Android 12 based Conscrypt module. - // If such a module is installed, simply skip the test as it is covered by MTS - TestUtils.assumeBeforeAndroid12Mainline(); - X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA); - CertBlacklist blacklist = CertBlacklistImpl.getDefault(); - assertTrue(blacklist.isPublicKeyBlackListed(blacklistedCa.getPublicKey())); - } - - /** - * Check that the blacklisted CA is rejected even if it used as a root of trust - */ - @Test - public void testBlacklistedCaUntrusted() throws Exception { - X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA); - assertUntrusted(new X509Certificate[] {blacklistedCa}, getTrustManager(blacklistedCa)); - } - - /** - * Check that a chain that is rooted in a blacklisted trusted CA is rejected. - */ - @Test - public void testBlacklistedRootOfTrust() throws Exception { - // Chain is leaf -> blacklisted - X509Certificate[] chain = loadCertificates(BLACKLISTED_CHAIN); - X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA); - assertUntrusted(chain, getTrustManager(blacklistedCa)); - } - - /** Test that the path building correctly routes around a blacklisted cert where there are - * other valid paths available. This prevents breakage where a cert was cross signed by a - * blacklisted CA but is still valid due to also being cross signed by CAs that remain trusted. - * Path: - * - * leaf -> intermediate -> blacklisted_ca - * \ - * -------> trusted_ca - */ - @Test - public void testBlacklistedIntermediateFallback() throws Exception { - X509Certificate[] chain = loadCertificates(BLACKLISTED_VALID_CHAIN); - X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA); - X509Certificate validCa = loadCertificate(BLACKLIST_FALLBACK_VALID_CA); - assertTrusted(chain, getTrustManager(blacklistedCa, validCa)); - // Check that without the trusted_ca the chain is invalid (since it only chains to a - // blacklisted ca) - assertUntrusted(chain, getTrustManager(blacklistedCa)); - } - - private static X509Certificate loadCertificate(String file) throws Exception { - return loadCertificates(file)[0]; - } - - private static X509Certificate[] loadCertificates(String file) throws Exception { - CertificateFactory factory = CertificateFactory.getInstance("X.509"); - try (InputStream is = TestUtils.openTestFile(file)) { - Collection<? extends Certificate> collection = factory.generateCertificates(is); - is.close(); - X509Certificate[] certs = new X509Certificate[collection.size()]; - int i = 0; - for (Certificate cert : collection) { - certs[i++] = (X509Certificate) cert; - } - return certs; - } - } - - private static TrustManagerImpl getTrustManager(X509Certificate... trustedCas) - throws Exception { - KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); - ks.load(null); - int i = 0; - for (X509Certificate ca : trustedCas) { - ks.setCertificateEntry(String.valueOf(i++), ca); - } - return new TrustManagerImpl(ks); - } - - private static void assertTrusted(X509Certificate[] certs, X509TrustManager tm) - throws Exception { - tm.checkServerTrusted(certs, "RSA"); - } - - private static void assertUntrusted(X509Certificate[] certs, X509TrustManager tm) { - try { - tm.checkServerTrusted(certs, "RSA"); - fail(); - } catch (CertificateException expected) { - } - } -}
diff --git a/repackaged/platform/src/test/java/com/android/org/conscrypt/CertBlocklistTest.java b/repackaged/platform/src/test/java/com/android/org/conscrypt/CertBlocklistTest.java new file mode 100644 index 0000000..c970f22 --- /dev/null +++ b/repackaged/platform/src/test/java/com/android/org/conscrypt/CertBlocklistTest.java
@@ -0,0 +1,127 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +/* + * Copyright (C) 2016 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 java.io.InputStream; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Collection; +import javax.net.ssl.X509TrustManager; +import junit.framework.TestCase; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class CertBlocklistTest extends TestCase { + + private static final String BLOCKLIST_CA = "test_blocklist_ca.pem"; + private static final String BLOCKLISTED_CHAIN = "blocklist_test_chain.pem"; + private static final String BLOCKLIST_FALLBACK_VALID_CA = "blocklist_test_valid_ca.pem"; + private static final String BLOCKLISTED_VALID_CHAIN = "blocklist_test_valid_chain.pem"; + + /** + * Ensure that the test blocklisted CA is actually blocklisted by default. + */ + public void testBlocklistedPublicKey() throws Exception { + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + CertBlocklist blocklist = CertBlocklistImpl.getDefault(); + assertTrue(blocklist.isPublicKeyBlockListed(blocklistedCa.getPublicKey())); + } + + /** + * Check that the blocklisted CA is rejected even if it used as a root of trust + */ + public void testBlocklistedCaUntrusted() throws Exception { + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + assertUntrusted(new X509Certificate[] {blocklistedCa}, getTrustManager(blocklistedCa)); + } + + /** + * Check that a chain that is rooted in a blocklisted trusted CA is rejected. + */ + public void testBlocklistedRootOfTrust() throws Exception { + // Chain is leaf -> blocklisted + X509Certificate[] chain = loadCertificates(BLOCKLISTED_CHAIN); + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + assertUntrusted(chain, getTrustManager(blocklistedCa)); + } + + /** Test that the path building correctly routes around a blocklisted cert where there are + * other valid paths available. This prevents breakage where a cert was cross signed by a + * blocklisted CA but is still valid due to also being cross signed by CAs that remain trusted. + * Path: + * + * leaf -> intermediate -> blocklisted_ca + * \ + * -------> trusted_ca + */ + public void testBlocklistedIntermediateFallback() throws Exception { + X509Certificate[] chain = loadCertificates(BLOCKLISTED_VALID_CHAIN); + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + X509Certificate validCa = loadCertificate(BLOCKLIST_FALLBACK_VALID_CA); + assertTrusted(chain, getTrustManager(blocklistedCa, validCa)); + // Check that without the trusted_ca the chain is invalid (since it only chains to a + // blocklisted ca) + assertUntrusted(chain, getTrustManager(blocklistedCa)); + } + + private static X509Certificate loadCertificate(String file) throws Exception { + return loadCertificates(file)[0]; + } + + private static X509Certificate[] loadCertificates(String file) throws Exception { + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + try (InputStream is = TestUtils.openTestFile(file)) { + Collection<? extends Certificate> collection = factory.generateCertificates(is); + is.close(); + X509Certificate[] certs = new X509Certificate[collection.size()]; + int i = 0; + for (Certificate cert : collection) { + certs[i++] = (X509Certificate) cert; + } + return certs; + } + } + + private static TrustManagerImpl getTrustManager(X509Certificate... trustedCas) + throws Exception { + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(null); + int i = 0; + for (X509Certificate ca : trustedCas) { + ks.setCertificateEntry(String.valueOf(i++), ca); + } + return new TrustManagerImpl(ks); + } + + private static void assertTrusted(X509Certificate[] certs, X509TrustManager tm) + throws Exception { + tm.checkServerTrusted(certs, "RSA"); + } + + private static void assertUntrusted(X509Certificate[] certs, X509TrustManager tm) { + try { + tm.checkServerTrusted(certs, "RSA"); + fail(); + } catch (CertificateException expected) { + } + } +}
diff --git a/repackaged/platform/src/test/java/com/android/org/conscrypt/TrustedCertificateStoreTest.java b/repackaged/platform/src/test/java/com/android/org/conscrypt/TrustedCertificateStoreTest.java index 5dbc8eb..40c136b 100644 --- a/repackaged/platform/src/test/java/com/android/org/conscrypt/TrustedCertificateStoreTest.java +++ b/repackaged/platform/src/test/java/com/android/org/conscrypt/TrustedCertificateStoreTest.java
@@ -882,6 +882,7 @@ assertEquals(x, store.findIssuer(x)); } + @SuppressWarnings("JdkObsolete") // Date is used in public API TrustedCertificateKeyStoreSpi private void assertTrusted(X509Certificate x, String alias) { assertEquals(x, store.getCertificate(alias)); assertEquals(file(alias).lastModified(), store.getCreationDate(alias).getTime());
diff --git a/repackaged/platform/src/test/java/com/android/org/conscrypt/metrics/MetricsTest.java b/repackaged/platform/src/test/java/com/android/org/conscrypt/metrics/MetricsTest.java new file mode 100644 index 0000000..2f053fe --- /dev/null +++ b/repackaged/platform/src/test/java/com/android/org/conscrypt/metrics/MetricsTest.java
@@ -0,0 +1,85 @@ +/* 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.metrics; + +import static org.junit.Assert.assertEquals; + +import android.util.StatsEvent; +import com.android.org.conscrypt.TestUtils; +import org.junit.Ignore; +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 MetricsTest { + public static final int TLS_HANDSHAKE_REPORTED = 317; + + // Tests that ReflexiveEvent produces the same event as framework's. + @Test + @Ignore // Ignore on CTS 12 only: b/259508875 + public void test_reflexiveEvent() throws Exception { + TestUtils.assumeStatsLogAvailable(); + + StatsEvent frameworkStatsEvent = StatsEvent.newBuilder() + .setAtomId(TLS_HANDSHAKE_REPORTED) + .writeBoolean(false) + .writeInt(1) // protocol + .writeInt(2) // cipher suite + .writeInt(100) // duration + .usePooledBuffer() + .build(); + + ReflexiveStatsEvent reflexiveStatsEvent = + ReflexiveStatsEvent.buildEvent(TLS_HANDSHAKE_REPORTED, false, 1, 2, 100); + StatsEvent constructedEvent = (StatsEvent) reflexiveStatsEvent.getStatsEvent(); + + // TODO(nikitai): Figure out how to use hidden (@hide) getters from StatsEvent + // to eliminate the use of reflection + int fid = (Integer) frameworkStatsEvent.getClass() + .getMethod("getAtomId") + .invoke(frameworkStatsEvent); + int cid = (Integer) constructedEvent.getClass() + .getMethod("getAtomId") + .invoke(constructedEvent); + assertEquals(fid, cid); + + int fnb = (Integer) frameworkStatsEvent.getClass() + .getMethod("getNumBytes") + .invoke(frameworkStatsEvent); + int cnb = (Integer) constructedEvent.getClass() + .getMethod("getNumBytes") + .invoke(constructedEvent); + assertEquals(fnb, cnb); + + byte[] fbytes = (byte[]) frameworkStatsEvent.getClass() + .getMethod("getBytes") + .invoke(frameworkStatsEvent); + byte[] cbytes = + (byte[]) constructedEvent.getClass().getMethod("getBytes").invoke(constructedEvent); + for (int i = 0; i < fnb; i++) { + // skip encoded timestamp (bytes 1-8) + if (i < 1 || i > 8) { + assertEquals(fbytes[i], cbytes[i]); + } + } + } +}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/TestUtils.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/TestUtils.java index 11b757e..fedb2e4 100644 --- a/repackaged/testing/src/main/java/com/android/org/conscrypt/TestUtils.java +++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/TestUtils.java
@@ -24,9 +24,11 @@ import com.android.org.conscrypt.java.security.StandardNames; import com.android.org.conscrypt.java.security.TestKeyStore; import com.android.org.conscrypt.testing.Streams; +import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.InetAddress; @@ -48,11 +50,10 @@ import java.util.Base64; import java.util.HashSet; import java.util.List; +import java.util.Locale; +import java.util.Random; import java.util.Set; -import java.util.function.IntFunction; import java.util.function.Predicate; - -import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; @@ -83,6 +84,44 @@ static final String TEST_CIPHER = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"; + /** + * @hide This class is not part of the Android public SDK API + */ + public enum BufferType { + HEAP { + @Override + ByteBuffer newBuffer(int size) { + return ByteBuffer.allocate(size); + } + }, + DIRECT { + @Override + ByteBuffer newBuffer(int size) { + return ByteBuffer.allocateDirect(size); + } + }; + private static final Random random = new Random(System.currentTimeMillis()); + abstract ByteBuffer newBuffer(int size); + + public ByteBuffer[] newRandomBuffers(int... sizes) { + int numBuffers = sizes.length; + ByteBuffer[] result = new ByteBuffer[numBuffers]; + for (int i = 0; i < numBuffers; i++) { + result[i] = newRandomBuffer(sizes[i]); + } + return result; + } + + public ByteBuffer newRandomBuffer(int size) { + byte[] data = new byte[size]; + random.nextBytes(data); + ByteBuffer buffer = newBuffer(size); + buffer.put(data); + buffer.flip(); + return buffer; + } + } + private TestUtils() {} private static Provider getNonConscryptTlsProvider() { @@ -128,6 +167,10 @@ assumeClassAvailable("javax.net.ssl.X509ExtendedTrustManager"); } + public static void assumeStatsLogAvailable() { + assumeClassAvailable("android.util.StatsEvent"); + } + public static void assumeSetEndpointIdentificationAlgorithmAvailable() { boolean supported = false; try { @@ -154,21 +197,10 @@ } } - // Is a pre-Android 12 mainline module installed. Detect based on a class that - // was renamed in the Android 12 codebase. - private static boolean isBeforeAndroid12Mainline() { - return isClassAvailable("com.android.org.conscrypt.CertBlacklistImpl"); - } - public static void assumeAndroid() { Assume.assumeTrue(isAndroid()); } - public static void assumeBeforeAndroid12Mainline() { - Assume.assumeTrue(isAndroid() && isBeforeAndroid12Mainline()); - } - - // Assume a pre-Android 12 mainline module is installed. public static void assumeAllowsUnsignedCrypto() { // The Oracle JRE disallows loading crypto providers from unsigned jars Assume.assumeTrue(isAndroid() @@ -186,18 +218,6 @@ Assume.assumeTrue("SHA2 with DSA signatures not available", available); } - private static Method findMethod(Class<?> cls, String methodName, Class<?>... methodParams) { - try { - return cls.getDeclaredMethod(methodName, methodParams); - } catch (NoSuchMethodException e) { - return null; - } - } - - public static Method findWrapVerifierMethod() { - return findMethod(Conscrypt.class, "wrapHostnameVerifier", HostnameVerifier.class); - } - public static InetAddress getLoopbackAddress() { try { Method method = InetAddress.class.getMethod("getLoopbackAddress"); @@ -253,7 +273,7 @@ public static PublicKey readPublicKeyPemFile(String name) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException { - String keyData = new String(readTestFile(name), "US-ASCII"); + String keyData = new String(readTestFile(name), StandardCharsets.US_ASCII); keyData = keyData.replace("-----BEGIN PUBLIC KEY-----", ""); keyData = keyData.replace("-----END PUBLIC KEY-----", ""); keyData = keyData.replace("\r", ""); @@ -262,6 +282,22 @@ new X509EncodedKeySpec(decodeBase64(keyData))); } + public static List<String[]> readCsvResource(String resourceName) throws IOException { + InputStream stream = openTestFile(resourceName); + List<String[]> lines = new ArrayList<>(); + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + lines.add(line.split(",", -1)); + } + } + return lines; + } + /** * Looks up the conscrypt class for the given simple name (i.e. no package prefix). */ @@ -314,24 +350,6 @@ } } - public static SSLSocketFactory getConscryptSocketFactory(boolean useEngineSocket) { - return setUseEngineSocket(getSocketFactory(getConscryptProvider()), useEngineSocket); - } - - public static SSLServerSocketFactory getConscryptServerSocketFactory(boolean useEngineSocket) { - return setUseEngineSocket(getServerSocketFactory(getConscryptProvider()), useEngineSocket); - } - - private static SSLSocketFactory getSocketFactory(Provider provider) { - SSLContext clientContext = initClientSslContext(newContext(provider)); - return clientContext.getSocketFactory(); - } - - private static SSLServerSocketFactory getServerSocketFactory(Provider provider) { - SSLContext serverContext = initServerSslContext(newContext(provider)); - return serverContext.getServerSocketFactory(); - } - static SSLContext newContext(Provider provider) { try { return SSLContext.getInstance("TLS", provider); @@ -350,27 +368,19 @@ SSLContext jdkContext = newClientSslContext(getJdkProvider()); SSLContext conscryptContext = newClientSslContext(getConscryptProvider()); // No point building a Set here due to small list sizes. - final List<String> conscryptProtocols = getSupportedProtocols(conscryptContext); - // TODO(prb): Certificate auth fails when connecting Conscrypt and JDK's TLS 1.3. - Predicate<String> predicate = new Predicate<String>() { - @Override - public boolean test(String string) { - return conscryptProtocols.contains(string) && !string.equals(PROTOCOL_TLS_V1_3); - } - }; + List<String> conscryptProtocols = getSupportedProtocols(conscryptContext); + Predicate<String> predicate = p + -> conscryptProtocols.contains(p) + // TODO(prb): Certificate auth fails when connecting Conscrypt and JDK's TLS 1.3. + && !p.equals(PROTOCOL_TLS_V1_3); return getSupportedProtocols(jdkContext, predicate); } public static String[] getCommonCipherSuites() { SSLContext jdkContext = newClientSslContext(getJdkProvider()); SSLContext conscryptContext = newClientSslContext(getConscryptProvider()); - final Set<String> conscryptCiphers = new HashSet<>(getSupportedCiphers(conscryptContext)); - Predicate<String> predicate = new Predicate<String>() { - @Override - public boolean test(String string) { - return isTlsCipherSuite(string) && conscryptCiphers.contains(string); - } - }; + Set<String> conscryptCiphers = new HashSet<>(getSupportedCiphers(conscryptContext)); + Predicate<String> predicate = c -> isTlsCipherSuite(c) && conscryptCiphers.contains(c); return getSupportedCiphers(jdkContext, predicate); } @@ -379,15 +389,9 @@ } public static String[] getSupportedCiphers(SSLContext ctx, Predicate<String> predicate) { - IntFunction<String[]> transform = new IntFunction<String[]>() { - @Override - public String[] apply(int value) { - return new String[value]; - } - }; return Arrays.stream(ctx.getDefaultSSLParameters().getCipherSuites()) .filter(predicate) - .toArray(transform); + .toArray(String[] ::new); } public static List<String> getSupportedProtocols(SSLContext ctx) { @@ -395,15 +399,9 @@ } public static String[] getSupportedProtocols(SSLContext ctx, Predicate<String> predicate) { - IntFunction<String[]> transform = new IntFunction<String[]>() { - @Override - public String[] apply(int value) { - return new String[value]; - } - }; return Arrays.stream(ctx.getDefaultSSLParameters().getProtocols()) .filter(predicate) - .toArray(transform); + .toArray(String[] ::new); } private static boolean isTlsCipherSuite(String cipher) { @@ -483,7 +481,7 @@ } /** - * Performs the intial TLS handshake between the two {@link SSLEngine} instances. + * Performs the initial TLS handshake between the two {@link SSLEngine} instances. */ public static void doEngineHandshake(SSLEngine clientEngine, SSLEngine serverEngine, ByteBuffer clientAppBuffer, ByteBuffer clientPacketBuffer, ByteBuffer serverAppBuffer, @@ -654,7 +652,6 @@ return result; } - private static int toDigit(char[] str, int offset) throws IllegalArgumentException { // NOTE: that this isn't really a code point in the traditional sense, since we're // just rejecting surrogate pairs outright. @@ -672,6 +669,18 @@ " at offset " + offset); } + private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray(); + + public static String encodeHex(byte[] data) { + char[] hex = new char[data.length * 2]; + for (int i = 0; i < data.length; i++) { + int value = data[i] & 0xff; + hex[2 * i] = HEX_CHARS[value >>> 4]; + hex[2 * i + 1] = HEX_CHARS[value & 0x0f]; + } + return new String(hex); + } + private static final String BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -757,4 +766,21 @@ public static void assumeJava8() { Assume.assumeTrue("Require Java 8: " + javaVersion(), isJavaVersion(8)); } + + public static String osName() { + return System.getProperty("os.name").toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", ""); + } + + public static boolean isLinux() { + return osName().startsWith("linux"); + } + + public static boolean isWindows() { + return osName().startsWith("windows"); + } + + public static boolean isOsx() { + String name = osName(); + return name.startsWith("macosx") || name.startsWith("osx"); + } }
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyFactoryTest.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyFactoryTest.java index c66e210..8a751aa 100644 --- a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyFactoryTest.java +++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyFactoryTest.java
@@ -38,8 +38,7 @@ private final Class<PublicKeySpec> publicKeySpecClass; private final Class<PrivateKeySpec> privateKeySpecClass; - public AbstractKeyFactoryTest(String algorithmName, - Class<PublicKeySpec> publicKeySpecClass, + protected AbstractKeyFactoryTest(String algorithmName, Class<PublicKeySpec> publicKeySpecClass, Class<PrivateKeySpec> privateKeySpecClass) { this.algorithmName = algorithmName; this.publicKeySpecClass = publicKeySpecClass;
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyPairGeneratorTest.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyPairGeneratorTest.java index f9ea7cf..105bd48 100644 --- a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyPairGeneratorTest.java +++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyPairGeneratorTest.java
@@ -44,9 +44,13 @@ generator = KeyPairGenerator.getInstance(algorithmName); } + protected int getKeySize() { + return 1024; + } + @Test public void testKeyPairGenerator() throws Exception { - generator.initialize(1024); + generator.initialize(getKeySize()); KeyPair keyPair = generator.generateKeyPair();
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CipherHelper.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CipherHelper.java index 73f59f7..29eb63a 100644 --- a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CipherHelper.java +++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CipherHelper.java
@@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals; +import java.nio.charset.StandardCharsets; import java.security.Key; import javax.crypto.Cipher; @@ -32,7 +33,7 @@ private final int mode1; private final int mode2; - public CipherHelper(String algorithmName, String plainData, int mode1, int mode2) { + protected CipherHelper(String algorithmName, String plainData, int mode1, int mode2) { this.algorithmName = algorithmName; this.plainData = plainData; this.mode1 = mode1; @@ -42,11 +43,11 @@ public void test(Key encryptKey, Key decryptKey) throws Exception { Cipher cipher = Cipher.getInstance(algorithmName); cipher.init(mode1, encryptKey); - byte[] encrypted = cipher.doFinal(plainData.getBytes("UTF-8")); + byte[] encrypted = cipher.doFinal(plainData.getBytes(StandardCharsets.UTF_8)); cipher.init(mode2, decryptKey); byte[] decrypted = cipher.doFinal(encrypted); - String decryptedString = new String(decrypted, "UTF-8"); + String decryptedString = new String(decrypted, StandardCharsets.UTF_8); assertEquals("transformed data does not match", plainData, decryptedString); }
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CpuFeatures.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CpuFeatures.java index f75fb94..553a714 100644 --- a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CpuFeatures.java +++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CpuFeatures.java
@@ -63,9 +63,13 @@ EVP_has_aes_hardware.setAccessible(true); return ((Integer) EVP_has_aes_hardware.invoke(null)) == 1; } catch (NoSuchMethodException ignored) { + // Ignored } catch (SecurityException ignored) { + // Ignored } catch (IllegalAccessException ignored) { + // Ignored } catch (IllegalArgumentException ignored) { + // Ignored } catch (InvocationTargetException e) { throw new IllegalArgumentException(e); }
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/DefaultKeys.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/DefaultKeys.java index 66737c3..0ce676f 100644 --- a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/DefaultKeys.java +++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/DefaultKeys.java
@@ -193,6 +193,104 @@ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKoHwjEdyQBvyYUd/Oi+m05qO103dQdKBwj2qjz+f" + "mC8y+cGAGwxMWgnc1xJYw767qY59R36o2TQlQHNI9d0CDA=="); + private static final byte[] X25519_private = new byte[] { + (byte) 0x30, + (byte) 0x2e, + (byte) 0x02, + (byte) 0x01, + (byte) 0x00, + (byte) 0x30, + (byte) 0x05, + (byte) 0x06, + (byte) 0x03, + (byte) 0x2b, + (byte) 0x65, + (byte) 0x6e, + (byte) 0x04, + (byte) 0x22, + (byte) 0x04, + (byte) 0x20, + (byte) 0xa5, + (byte) 0x46, + (byte) 0xe3, + (byte) 0x6b, + (byte) 0xf0, + (byte) 0x52, + (byte) 0x7c, + (byte) 0x9d, + (byte) 0x3b, + (byte) 0x16, + (byte) 0x15, + (byte) 0x4b, + (byte) 0x82, + (byte) 0x46, + (byte) 0x5e, + (byte) 0xdd, + (byte) 0x62, + (byte) 0x14, + (byte) 0x4c, + (byte) 0x0a, + (byte) 0xc1, + (byte) 0xfc, + (byte) 0x5a, + (byte) 0x18, + (byte) 0x50, + (byte) 0x6a, + (byte) 0x22, + (byte) 0x44, + (byte) 0xba, + (byte) 0x44, + (byte) 0x9a, + (byte) 0xc4, + }; + + private static final byte[] X25519_public = new byte[] { + (byte) 0x30, + (byte) 0x2a, + (byte) 0x30, + (byte) 0x05, + (byte) 0x06, + (byte) 0x03, + (byte) 0x2b, + (byte) 0x65, + (byte) 0x6e, + (byte) 0x03, + (byte) 0x21, + (byte) 0x00, + (byte) 0xe6, + (byte) 0xdb, + (byte) 0x68, + (byte) 0x67, + (byte) 0x58, + (byte) 0x30, + (byte) 0x30, + (byte) 0xdb, + (byte) 0x35, + (byte) 0x94, + (byte) 0xc1, + (byte) 0xa4, + (byte) 0x24, + (byte) 0xb1, + (byte) 0x5f, + (byte) 0x7c, + (byte) 0x72, + (byte) 0x66, + (byte) 0x24, + (byte) 0xec, + (byte) 0x26, + (byte) 0xb3, + (byte) 0x35, + (byte) 0x3b, + (byte) 0x10, + (byte) 0xa9, + (byte) 0x03, + (byte) 0xa6, + (byte) 0xd0, + (byte) 0xab, + (byte) 0x1c, + (byte) 0x4c, + }; + private static final HashMap<String, KeySpec> keys = new HashMap<String, KeySpec>(); static { keys.put("DH_public", new X509EncodedKeySpec(DH_public)); @@ -203,6 +301,8 @@ keys.put("RSA_private", new PKCS8EncodedKeySpec(RSA_private)); keys.put("EC_public", new X509EncodedKeySpec(EC_public)); keys.put("EC_private", new PKCS8EncodedKeySpec(EC_private)); + keys.put("XDH_public", new X509EncodedKeySpec(X25519_public)); + keys.put("XDH_private", new PKCS8EncodedKeySpec(X25519_private)); } public static PrivateKey getPrivateKey(String algorithmName) throws NoSuchAlgorithmException, InvalidKeySpecException
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/StandardNames.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/StandardNames.java index 2825677..e0cb275 100644 --- a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/StandardNames.java +++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/StandardNames.java
@@ -66,6 +66,27 @@ public static final String KEY_STORE_ALGORITHM = IS_RI ? "JKS" : "BKS"; + public static final boolean IS_15_OR_UP = majorVersionFromJavaSpecificationVersion() >= 15; + + private static int majorVersionFromJavaSpecificationVersion() { + return majorVersion(System.getProperty("java.specification.version", "1.6")); + } + + private static int majorVersion(final String javaSpecVersion) { + final String[] components = javaSpecVersion.split("\\.", -1); + final int[] version = new int[components.length]; + for (int i = 0; i < components.length; i++) { + version[i] = Integer.parseInt(components[i]); + } + + if (version[0] == 1) { + assertTrue(version[1] >= 6); + return version[1]; + } else { + return version[0]; + } + } + /** * RFC 5746's Signaling Cipher Suite Value to indicate a request for secure renegotiation */
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/FakeSSLSession.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/FakeSSLSession.java index ecd8b24..a373c72 100644 --- a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/FakeSSLSession.java +++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/FakeSSLSession.java
@@ -20,6 +20,7 @@ import java.nio.charset.Charset; import java.security.Principal; import java.security.cert.Certificate; +import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSessionContext; @@ -80,7 +81,7 @@ } @Override - public Certificate[] getPeerCertificates() { + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { throw new UnsupportedOperationException(); }
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestHostnameVerifier.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestHostnameVerifier.java index 1c62bac..b2d5552 100644 --- a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestHostnameVerifier.java +++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestHostnameVerifier.java
@@ -37,7 +37,7 @@ private static final int DNS_NAME_TYPE = 2; @SuppressWarnings("MixedMutabilityReturnType") - private List<String> getHostnames(X509Certificate cert) { + private static List<String> getHostnames(X509Certificate cert) { List<String> result = new ArrayList<String>(); try { Collection<List<?>> altNamePairs = cert.getSubjectAlternativeNames();
diff --git a/repackaged/testing/src/main/java/tests/util/ServiceTester.java b/repackaged/testing/src/main/java/tests/util/ServiceTester.java index 02bb5dd..90b188f 100644 --- a/repackaged/testing/src/main/java/tests/util/ServiceTester.java +++ b/repackaged/testing/src/main/java/tests/util/ServiceTester.java
@@ -49,11 +49,13 @@ void test(Provider p, String algorithm) throws Exception; } + private static final String SEPARATOR = "||"; private final String service; - private Set<Provider> providers = new LinkedHashSet<>(); - private Set<Provider> skipProviders = new HashSet<>(); - private Set<String> algorithms = new LinkedHashSet<>(); - private Set<String> skipAlgorithms = new HashSet<>(); + private final Set<Provider> providers = new LinkedHashSet<>(); + private final Set<Provider> skipProviders = new HashSet<>(); + private final Set<String> algorithms = new LinkedHashSet<>(); + private final Set<String> skipAlgorithms = new HashSet<>(); + private final Set<String> skipCombinations = new HashSet<>(); private ServiceTester(String service) { this.service = service; @@ -133,6 +135,18 @@ } /** + * Causes the given combination of provider and algorithm to be omitted from this instance's + * testing. If no tested provider provides the given algorithm, does nothing. + */ + public ServiceTester skipCombination(String provider, String algorithm) { + Provider p = Security.getProvider(provider); + if (p != null) { + skipCombinations.add(makeCombination(provider, algorithm)); + } + return this; + } + + /** * Runs the given test against the configured combination of providers and algorithms. Continues * running all combinations even if some fail. If any of the test runs fail, this throws * an exception with the details of the failure(s). @@ -147,14 +161,16 @@ for (Provider p : providers) { if (algorithms.isEmpty()) { for (Provider.Service s : p.getServices()) { - if (s.getType().equals(service) && !skipAlgorithms.contains(s.getAlgorithm())) { + if (s.getType().equals(service) && !skipAlgorithms.contains(s.getAlgorithm()) + && !shouldSkipCombination(p.getName(), s.getAlgorithm())) { doTest(test, p, s.getAlgorithm(), errors); } } } else { algorithms.removeAll(skipAlgorithms); for (String algorithm : algorithms) { - if (p.getService(service, algorithm) != null) { + if (p.getService(service, algorithm) != null + && !shouldSkipCombination(p.getName(), algorithm)) { doTest(test, p, algorithm, errors); } } @@ -166,6 +182,14 @@ } } + private String makeCombination(String provider, String algorithm) { + return provider + SEPARATOR + algorithm; + } + + private boolean shouldSkipCombination(String provider, String algorithm) { + return skipCombinations.contains(makeCombination(provider, algorithm)); + } + private void doTest(Test test, Provider p, String algorithm, PrintStream errors) { try { test.test(p, algorithm);
diff --git a/srcgen/generate_android_src.sh b/srcgen/generate_android_src.sh index 486c229..fc03064 100755 --- a/srcgen/generate_android_src.sh +++ b/srcgen/generate_android_src.sh
@@ -29,7 +29,6 @@ benchmark-base \ common \ openjdk \ - common \ platform \ testing \ "
diff --git a/srcgen/core-platform-api.txt b/srcgen/stable-core-platform-api.txt similarity index 100% rename from srcgen/core-platform-api.txt rename to srcgen/stable-core-platform-api.txt
diff --git a/srcgen/unsupported-app-usage.json b/srcgen/unsupported-app-usage.json index d4ba3bf..12935e9 100644 --- a/srcgen/unsupported-app-usage.json +++ b/srcgen/unsupported-app-usage.json
@@ -4,62 +4,82 @@ { "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getAlpnSelectedProtocol()", "maxTargetSdk": "dalvik.annotation.compat.VersionCodes.Q", - "publicAlternatives": "Use {@link javax.net.ssl.SSLSocket#getApplicationProtocol()}." + "publicAlternatives": "Use {@code javax.net.ssl.SSLSocket#getApplicationProtocol()}." }, { "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getApplicationProtocols()", "maxTargetSdk": "dalvik.annotation.compat.VersionCodes.Q", - "publicAlternatives": "Use {@link javax.net.ssl.SSLParameters#getApplicationProtocols()}." + "publicAlternatives": "Use {@code javax.net.ssl.SSLParameters#getApplicationProtocols()}." }, { - "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getChannelId()" + "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getChannelId()", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { - "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getHostname()" + "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getHostname()", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { - "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getHostnameOrIP()" + "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getHostnameOrIP()", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { - "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getNpnSelectedProtocol()" + "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getNpnSelectedProtocol()", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { - "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getSoWriteTimeout()" + "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getSoWriteTimeout()", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setAlpnProtocols(byte[])", "maxTargetSdk": "dalvik.annotation.compat.VersionCodes.Q", - "publicAlternatives": "Use {@link javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}." + "publicAlternatives": "Use {@code javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}." }, { "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setAlpnProtocols(String[])", "maxTargetSdk": "dalvik.annotation.compat.VersionCodes.Q", - "publicAlternatives": "Use {@link javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}." + "publicAlternatives": "Use {@code javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}." }, { "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setApplicationProtocols(String[])", "maxTargetSdk": "dalvik.annotation.compat.VersionCodes.Q", - "publicAlternatives": "Use {@link javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}." + "publicAlternatives": "Use {@code javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}." }, { - "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setChannelIdEnabled(boolean)" + "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setChannelIdEnabled(boolean)", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { - "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setChannelIdPrivateKey(PrivateKey)" + "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setChannelIdPrivateKey(PrivateKey)", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { - "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setHandshakeTimeout(int)" + "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setHandshakeTimeout(int)", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setHostname(String)", "maxTargetSdk": "dalvik.annotation.compat.VersionCodes.Q", - "publicAlternatives": "Use {@link javax.net.ssl.SSLParameters#setServerNames}." + "publicAlternatives": "Use {@code javax.net.ssl.SSLParameters#setServerNames}." }, { - "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setNpnProtocols(byte[])" + "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setNpnProtocols(byte[])", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { - "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setSoWriteTimeout(int)" + "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setSoWriteTimeout(int)", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setUseSessionTickets(boolean)", @@ -75,7 +95,7 @@ { "@location": "method:com.android.org.conscrypt.ConscryptEngineSocket#setHostname(String)", "maxTargetSdk": "dalvik.annotation.compat.VersionCodes.Q", - "publicAlternatives": "Use {@link javax.net.ssl.SSLParameters#setServerNames}." + "publicAlternatives": "Use {@code javax.net.ssl.SSLParameters#setServerNames}." }, { "@location": "method:com.android.org.conscrypt.ConscryptEngineSocket#setUseSessionTickets(boolean)", @@ -85,7 +105,7 @@ { "@location": "method:com.android.org.conscrypt.ConscryptFileDescriptorSocket#setHostname(String)", "maxTargetSdk": "dalvik.annotation.compat.VersionCodes.Q", - "publicAlternatives": "Use {@link javax.net.ssl.SSLParameters#setServerNames}." + "publicAlternatives": "Use {@code javax.net.ssl.SSLParameters#setServerNames}." }, { "@location": "method:com.android.org.conscrypt.ConscryptFileDescriptorSocket#setUseSessionTickets(boolean)", @@ -281,52 +301,68 @@ { "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getAlpnSelectedProtocol()", "maxTargetSdk": "dalvik.annotation.compat.VersionCodes.Q", - "publicAlternatives": "Use {@link javax.net.ssl.SSLSocket#getApplicationProtocol()}." + "publicAlternatives": "Use {@code javax.net.ssl.SSLSocket#getApplicationProtocol()}." }, { - "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getChannelId()" + "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getChannelId()", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { - "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getHostname()" + "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getHostname()", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { - "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getHostnameOrIP()" + "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getHostnameOrIP()", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getNpnSelectedProtocol()" }, { - "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getSoWriteTimeout()" + "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getSoWriteTimeout()", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setAlpnProtocols(byte[])", "maxTargetSdk": "dalvik.annotation.compat.VersionCodes.Q", - "publicAlternatives": "Use {@link javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}." + "publicAlternatives": "Use {@code javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}." }, { "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setAlpnProtocols(String[])", "maxTargetSdk": "dalvik.annotation.compat.VersionCodes.Q", - "publicAlternatives": "Use {@link javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}." + "publicAlternatives": "Use {@code javax.net.ssl.SSLParameters#setApplicationProtocols(java.lang.String[])}." }, { - "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setChannelIdEnabled(boolean)" + "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setChannelIdEnabled(boolean)", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { - "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setChannelIdPrivateKey(PrivateKey)" + "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setChannelIdPrivateKey(PrivateKey)", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { - "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setHandshakeTimeout(int)" + "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setHandshakeTimeout(int)", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setHostname(String)", "maxTargetSdk": "dalvik.annotation.compat.VersionCodes.Q", - "publicAlternatives": "Use {@link javax.net.ssl.SSLParameters#setServerNames}." + "publicAlternatives": "Use {@code javax.net.ssl.SSLParameters#setServerNames}." }, { "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setNpnProtocols(byte[])" }, { - "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setSoWriteTimeout(int)" + "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setSoWriteTimeout(int)", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setUseSessionTickets(boolean)", @@ -346,10 +382,14 @@ "@location": "method:com.android.org.conscrypt.SSLParametersImpl#getDefaultX509TrustManager()" }, { - "@location": "method:com.android.org.conscrypt.SSLParametersImpl#getX509TrustManager()" + "@location": "method:com.android.org.conscrypt.SSLParametersImpl#getX509TrustManager()", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { - "@location": "method:com.android.org.conscrypt.SSLParametersImpl#setEnabledProtocols(String[])" + "@location": "method:com.android.org.conscrypt.SSLParametersImpl#setEnabledProtocols(String[])", + "maxTargetSdk": 30, + "trackingBug": 170729553 }, { "@location": "field:com.android.org.conscrypt.SSLParametersImpl#x509TrustManager"
diff --git a/test-support/Android.bp b/test-support/Android.bp index f8a025f..c122bc6 100644 --- a/test-support/Android.bp +++ b/test-support/Android.bp
@@ -13,6 +13,15 @@ // limitations under the License. // Conscrypt test support classes. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "external_conscrypt_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["external_conscrypt_license"], +} + java_library { name: "conscrypt-support", visibility: [
diff --git a/testing/build.gradle b/testing/build.gradle index 0d0d4cb..7585637 100644 --- a/testing/build.gradle +++ b/testing/build.gradle
@@ -1,3 +1,7 @@ +plugins { + id 'com.github.johnrengelman.shadow' version '6.0.0' +} + description = 'Conscrypt: Testing' sourceSets { @@ -9,10 +13,13 @@ } dependencies { - // Only compile against this. Other modules will embed the generated code directly. - compileOnly project(':conscrypt-constants') + // Only compile against these. Other modules will embed the generated + // constants directly. The stubs libraries should not end up in the + // final build. + compileOnly project(':conscrypt-constants'), + project(':conscrypt-libcore-stub'), + project(':conscrypt-android-stub') - compile project(':conscrypt-libcore-stub'), - libraries.bouncycastle_apis, + compile libraries.bouncycastle_apis, libraries.bouncycastle_provider }
diff --git a/testing/src/main/java/org/conscrypt/TestUtils.java b/testing/src/main/java/org/conscrypt/TestUtils.java index a434b15..b0ca971 100644 --- a/testing/src/main/java/org/conscrypt/TestUtils.java +++ b/testing/src/main/java/org/conscrypt/TestUtils.java
@@ -20,9 +20,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; +import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.InetAddress; @@ -44,11 +46,11 @@ import java.util.Base64; import java.util.HashSet; import java.util.List; +import java.util.Locale; +import java.util.Random; import java.util.Set; -import java.util.function.IntFunction; import java.util.function.Predicate; -import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; @@ -81,6 +83,41 @@ static final String TEST_CIPHER = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"; + public enum BufferType { + HEAP { + @Override + ByteBuffer newBuffer(int size) { + return ByteBuffer.allocate(size); + } + }, + DIRECT { + @Override + ByteBuffer newBuffer(int size) { + return ByteBuffer.allocateDirect(size); + } + }; + private static final Random random = new Random(System.currentTimeMillis()); + abstract ByteBuffer newBuffer(int size); + + public ByteBuffer[] newRandomBuffers(int... sizes) { + int numBuffers = sizes.length; + ByteBuffer[] result = new ByteBuffer[numBuffers]; + for (int i = 0; i < numBuffers; i++) { + result[i] = newRandomBuffer(sizes[i]); + } + return result; + } + + public ByteBuffer newRandomBuffer(int size) { + byte[] data = new byte[size]; + random.nextBytes(data); + ByteBuffer buffer = newBuffer(size); + buffer.put(data); + buffer.flip(); + return buffer; + } + } + private TestUtils() {} private static Provider getNonConscryptTlsProvider() { @@ -126,6 +163,10 @@ assumeClassAvailable("javax.net.ssl.X509ExtendedTrustManager"); } + public static void assumeStatsLogAvailable() { + assumeClassAvailable("android.util.StatsEvent"); + } + public static void assumeSetEndpointIdentificationAlgorithmAvailable() { boolean supported = false; try { @@ -152,21 +193,10 @@ } } - // Is a pre-Android 12 mainline module installed. Detect based on a class that - // was renamed in the Android 12 codebase. - private static boolean isBeforeAndroid12Mainline() { - return isClassAvailable("org.conscrypt.CertBlacklistImpl"); - } - public static void assumeAndroid() { Assume.assumeTrue(isAndroid()); } - public static void assumeBeforeAndroid12Mainline() { - Assume.assumeTrue(isAndroid() && isBeforeAndroid12Mainline()); - } - - // Assume a pre-Android 12 mainline module is installed. public static void assumeAllowsUnsignedCrypto() { // The Oracle JRE disallows loading crypto providers from unsigned jars Assume.assumeTrue(isAndroid() @@ -184,18 +214,6 @@ Assume.assumeTrue("SHA2 with DSA signatures not available", available); } - private static Method findMethod(Class<?> cls, String methodName, Class<?>... methodParams) { - try { - return cls.getDeclaredMethod(methodName, methodParams); - } catch (NoSuchMethodException e) { - return null; - } - } - - public static Method findWrapVerifierMethod() { - return findMethod(Conscrypt.class, "wrapHostnameVerifier", HostnameVerifier.class); - } - public static InetAddress getLoopbackAddress() { try { Method method = InetAddress.class.getMethod("getLoopbackAddress"); @@ -250,7 +268,7 @@ public static PublicKey readPublicKeyPemFile(String name) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException { - String keyData = new String(readTestFile(name), "US-ASCII"); + String keyData = new String(readTestFile(name), StandardCharsets.US_ASCII); keyData = keyData.replace("-----BEGIN PUBLIC KEY-----", ""); keyData = keyData.replace("-----END PUBLIC KEY-----", ""); keyData = keyData.replace("\r", ""); @@ -259,6 +277,22 @@ new X509EncodedKeySpec(decodeBase64(keyData))); } + public static List<String[]> readCsvResource(String resourceName) throws IOException { + InputStream stream = openTestFile(resourceName); + List<String[]> lines = new ArrayList<>(); + try (BufferedReader reader + = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + lines.add(line.split(",", -1)); + } + } + return lines; + } + /** * Looks up the conscrypt class for the given simple name (i.e. no package prefix). */ @@ -311,24 +345,6 @@ } } - public static SSLSocketFactory getConscryptSocketFactory(boolean useEngineSocket) { - return setUseEngineSocket(getSocketFactory(getConscryptProvider()), useEngineSocket); - } - - public static SSLServerSocketFactory getConscryptServerSocketFactory(boolean useEngineSocket) { - return setUseEngineSocket(getServerSocketFactory(getConscryptProvider()), useEngineSocket); - } - - private static SSLSocketFactory getSocketFactory(Provider provider) { - SSLContext clientContext = initClientSslContext(newContext(provider)); - return clientContext.getSocketFactory(); - } - - private static SSLServerSocketFactory getServerSocketFactory(Provider provider) { - SSLContext serverContext = initServerSslContext(newContext(provider)); - return serverContext.getServerSocketFactory(); - } - static SSLContext newContext(Provider provider) { try { return SSLContext.getInstance("TLS", provider); @@ -347,27 +363,18 @@ SSLContext jdkContext = newClientSslContext(getJdkProvider()); SSLContext conscryptContext = newClientSslContext(getConscryptProvider()); // No point building a Set here due to small list sizes. - final List<String> conscryptProtocols = getSupportedProtocols(conscryptContext); - // TODO(prb): Certificate auth fails when connecting Conscrypt and JDK's TLS 1.3. - Predicate<String> predicate = new Predicate<String>() { - @Override - public boolean test(String string) { - return conscryptProtocols.contains(string) && !string.equals(PROTOCOL_TLS_V1_3); - } - }; + List<String> conscryptProtocols = getSupportedProtocols(conscryptContext); + Predicate<String> predicate = p -> conscryptProtocols.contains(p) + // TODO(prb): Certificate auth fails when connecting Conscrypt and JDK's TLS 1.3. + && !p.equals(PROTOCOL_TLS_V1_3); return getSupportedProtocols(jdkContext, predicate); } public static String[] getCommonCipherSuites() { SSLContext jdkContext = newClientSslContext(getJdkProvider()); SSLContext conscryptContext = newClientSslContext(getConscryptProvider()); - final Set<String> conscryptCiphers = new HashSet<>(getSupportedCiphers(conscryptContext)); - Predicate<String> predicate = new Predicate<String>() { - @Override - public boolean test(String string) { - return isTlsCipherSuite(string) && conscryptCiphers.contains(string); - } - }; + Set<String> conscryptCiphers = new HashSet<>(getSupportedCiphers(conscryptContext)); + Predicate<String> predicate = c -> isTlsCipherSuite(c) && conscryptCiphers.contains(c); return getSupportedCiphers(jdkContext, predicate); } @@ -376,15 +383,9 @@ } public static String[] getSupportedCiphers(SSLContext ctx, Predicate<String> predicate) { - IntFunction<String[]> transform = new IntFunction<String[]>() { - @Override - public String[] apply(int value) { - return new String[value]; - } - }; return Arrays.stream(ctx.getDefaultSSLParameters().getCipherSuites()) .filter(predicate) - .toArray(transform); + .toArray(String[]::new); } public static List<String> getSupportedProtocols(SSLContext ctx) { @@ -392,15 +393,9 @@ } public static String[] getSupportedProtocols(SSLContext ctx, Predicate<String> predicate) { - IntFunction<String[]> transform = new IntFunction<String[]>() { - @Override - public String[] apply(int value) { - return new String[value]; - } - }; return Arrays.stream(ctx.getDefaultSSLParameters().getProtocols()) .filter(predicate) - .toArray(transform); + .toArray(String[]::new); } private static boolean isTlsCipherSuite(String cipher) { @@ -481,7 +476,7 @@ } /** - * Performs the intial TLS handshake between the two {@link SSLEngine} instances. + * Performs the initial TLS handshake between the two {@link SSLEngine} instances. */ public static void doEngineHandshake(SSLEngine clientEngine, SSLEngine serverEngine, ByteBuffer clientAppBuffer, ByteBuffer clientPacketBuffer, ByteBuffer serverAppBuffer, @@ -652,7 +647,6 @@ return result; } - private static int toDigit(char[] str, int offset) throws IllegalArgumentException { // NOTE: that this isn't really a code point in the traditional sense, since we're // just rejecting surrogate pairs outright. @@ -670,6 +664,18 @@ " at offset " + offset); } + private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray(); + + public static String encodeHex(byte[] data) { + char[] hex = new char[data.length * 2]; + for (int i = 0; i < data.length; i++) { + int value = data[i] & 0xff; + hex[2 * i] = HEX_CHARS[value >>> 4]; + hex[2 * i + 1] = HEX_CHARS[value & 0x0f]; + } + return new String(hex); + } + private static final String BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -755,4 +761,21 @@ public static void assumeJava8() { Assume.assumeTrue("Require Java 8: " + javaVersion(), isJavaVersion(8)); } + + public static String osName() { + return System.getProperty("os.name").toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", ""); + } + + public static boolean isLinux() { + return osName().startsWith("linux"); + } + + public static boolean isWindows() { + return osName().startsWith("windows"); + } + + public static boolean isOsx() { + String name = osName(); + return name.startsWith("macosx") || name.startsWith("osx"); + } }
diff --git a/testing/src/main/java/org/conscrypt/java/security/AbstractKeyFactoryTest.java b/testing/src/main/java/org/conscrypt/java/security/AbstractKeyFactoryTest.java index def5a88..3523e45 100644 --- a/testing/src/main/java/org/conscrypt/java/security/AbstractKeyFactoryTest.java +++ b/testing/src/main/java/org/conscrypt/java/security/AbstractKeyFactoryTest.java
@@ -34,7 +34,7 @@ private final Class<PublicKeySpec> publicKeySpecClass; private final Class<PrivateKeySpec> privateKeySpecClass; - public AbstractKeyFactoryTest(String algorithmName, + protected AbstractKeyFactoryTest(String algorithmName, Class<PublicKeySpec> publicKeySpecClass, Class<PrivateKeySpec> privateKeySpecClass) { this.algorithmName = algorithmName;
diff --git a/testing/src/main/java/org/conscrypt/java/security/AbstractKeyPairGeneratorTest.java b/testing/src/main/java/org/conscrypt/java/security/AbstractKeyPairGeneratorTest.java index 109cf90..3353998 100644 --- a/testing/src/main/java/org/conscrypt/java/security/AbstractKeyPairGeneratorTest.java +++ b/testing/src/main/java/org/conscrypt/java/security/AbstractKeyPairGeneratorTest.java
@@ -40,9 +40,13 @@ generator = KeyPairGenerator.getInstance(algorithmName); } + protected int getKeySize() { + return 1024; + } + @Test public void testKeyPairGenerator() throws Exception { - generator.initialize(1024); + generator.initialize(getKeySize()); KeyPair keyPair = generator.generateKeyPair();
diff --git a/testing/src/main/java/org/conscrypt/java/security/CipherHelper.java b/testing/src/main/java/org/conscrypt/java/security/CipherHelper.java index 30ac58e..d9c1d4b 100644 --- a/testing/src/main/java/org/conscrypt/java/security/CipherHelper.java +++ b/testing/src/main/java/org/conscrypt/java/security/CipherHelper.java
@@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals; +import java.nio.charset.StandardCharsets; import java.security.Key; import javax.crypto.Cipher; @@ -28,7 +29,7 @@ private final int mode1; private final int mode2; - public CipherHelper(String algorithmName, String plainData, int mode1, int mode2) { + protected CipherHelper(String algorithmName, String plainData, int mode1, int mode2) { this.algorithmName = algorithmName; this.plainData = plainData; this.mode1 = mode1; @@ -38,11 +39,11 @@ public void test(Key encryptKey, Key decryptKey) throws Exception { Cipher cipher = Cipher.getInstance(algorithmName); cipher.init(mode1, encryptKey); - byte[] encrypted = cipher.doFinal(plainData.getBytes("UTF-8")); + byte[] encrypted = cipher.doFinal(plainData.getBytes(StandardCharsets.UTF_8)); cipher.init(mode2, decryptKey); byte[] decrypted = cipher.doFinal(encrypted); - String decryptedString = new String(decrypted, "UTF-8"); + String decryptedString = new String(decrypted, StandardCharsets.UTF_8); assertEquals("transformed data does not match", plainData, decryptedString); }
diff --git a/testing/src/main/java/org/conscrypt/java/security/CpuFeatures.java b/testing/src/main/java/org/conscrypt/java/security/CpuFeatures.java index 2b40bf7..721f453 100644 --- a/testing/src/main/java/org/conscrypt/java/security/CpuFeatures.java +++ b/testing/src/main/java/org/conscrypt/java/security/CpuFeatures.java
@@ -59,9 +59,13 @@ EVP_has_aes_hardware.setAccessible(true); return ((Integer) EVP_has_aes_hardware.invoke(null)) == 1; } catch (NoSuchMethodException ignored) { + // Ignored } catch (SecurityException ignored) { + // Ignored } catch (IllegalAccessException ignored) { + // Ignored } catch (IllegalArgumentException ignored) { + // Ignored } catch (InvocationTargetException e) { throw new IllegalArgumentException(e); }
diff --git a/testing/src/main/java/org/conscrypt/java/security/DefaultKeys.java b/testing/src/main/java/org/conscrypt/java/security/DefaultKeys.java index 0d2a278..bb8fa9f 100644 --- a/testing/src/main/java/org/conscrypt/java/security/DefaultKeys.java +++ b/testing/src/main/java/org/conscrypt/java/security/DefaultKeys.java
@@ -189,6 +189,24 @@ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKoHwjEdyQBvyYUd/Oi+m05qO103dQdKBwj2qjz+f" + "mC8y+cGAGwxMWgnc1xJYw767qY59R36o2TQlQHNI9d0CDA=="); + private static final byte[] X25519_private = new byte[] { + (byte) 0x30, (byte) 0x2e, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x05, (byte) 0x06, + (byte) 0x03, (byte) 0x2b, (byte) 0x65, (byte) 0x6e, (byte) 0x04, (byte) 0x22, (byte) 0x04, (byte) 0x20, + (byte) 0xa5, (byte) 0x46, (byte) 0xe3, (byte) 0x6b, (byte) 0xf0, (byte) 0x52, (byte) 0x7c, (byte) 0x9d, + (byte) 0x3b, (byte) 0x16, (byte) 0x15, (byte) 0x4b, (byte) 0x82, (byte) 0x46, (byte) 0x5e, (byte) 0xdd, + (byte) 0x62, (byte) 0x14, (byte) 0x4c, (byte) 0x0a, (byte) 0xc1, (byte) 0xfc, (byte) 0x5a, (byte) 0x18, + (byte) 0x50, (byte) 0x6a, (byte) 0x22, (byte) 0x44, (byte) 0xba, (byte) 0x44, (byte) 0x9a, (byte) 0xc4, + }; + + private static final byte[] X25519_public = new byte[] { + (byte) 0x30, (byte) 0x2a, (byte) 0x30, (byte) 0x05, (byte) 0x06, (byte) 0x03, (byte) 0x2b, (byte) 0x65, + (byte) 0x6e, (byte) 0x03, (byte) 0x21, (byte) 0x00, (byte) 0xe6, (byte) 0xdb, (byte) 0x68, (byte) 0x67, + (byte) 0x58, (byte) 0x30, (byte) 0x30, (byte) 0xdb, (byte) 0x35, (byte) 0x94, (byte) 0xc1, (byte) 0xa4, + (byte) 0x24, (byte) 0xb1, (byte) 0x5f, (byte) 0x7c, (byte) 0x72, (byte) 0x66, (byte) 0x24, (byte) 0xec, + (byte) 0x26, (byte) 0xb3, (byte) 0x35, (byte) 0x3b, (byte) 0x10, (byte) 0xa9, (byte) 0x03, (byte) 0xa6, + (byte) 0xd0, (byte) 0xab, (byte) 0x1c, (byte) 0x4c, + }; + private static final HashMap<String, KeySpec> keys = new HashMap<String, KeySpec>(); static { keys.put("DH_public", new X509EncodedKeySpec(DH_public)); @@ -199,6 +217,8 @@ keys.put("RSA_private", new PKCS8EncodedKeySpec(RSA_private)); keys.put("EC_public", new X509EncodedKeySpec(EC_public)); keys.put("EC_private", new PKCS8EncodedKeySpec(EC_private)); + keys.put("XDH_public", new X509EncodedKeySpec(X25519_public)); + keys.put("XDH_private", new PKCS8EncodedKeySpec(X25519_private)); } public static PrivateKey getPrivateKey(String algorithmName) throws NoSuchAlgorithmException, InvalidKeySpecException
diff --git a/testing/src/main/java/org/conscrypt/java/security/StandardNames.java b/testing/src/main/java/org/conscrypt/java/security/StandardNames.java index 86c7d48..7a8672a 100644 --- a/testing/src/main/java/org/conscrypt/java/security/StandardNames.java +++ b/testing/src/main/java/org/conscrypt/java/security/StandardNames.java
@@ -64,6 +64,27 @@ public static final String KEY_STORE_ALGORITHM = IS_RI ? "JKS" : "BKS"; + public static final boolean IS_15_OR_UP = majorVersionFromJavaSpecificationVersion() >= 15; + + private static int majorVersionFromJavaSpecificationVersion() { + return majorVersion(System.getProperty("java.specification.version", "1.6")); + } + + private static int majorVersion(final String javaSpecVersion) { + final String[] components = javaSpecVersion.split("\\.", -1); + final int[] version = new int[components.length]; + for (int i = 0; i < components.length; i++) { + version[i] = Integer.parseInt(components[i]); + } + + if (version[0] == 1) { + assertTrue(version[1] >= 6); + return version[1]; + } else { + return version[0]; + } + } + /** * RFC 5746's Signaling Cipher Suite Value to indicate a request for secure renegotiation */
diff --git a/testing/src/main/java/org/conscrypt/javax/net/ssl/FakeSSLSession.java b/testing/src/main/java/org/conscrypt/javax/net/ssl/FakeSSLSession.java index f83b62e..f001db2 100644 --- a/testing/src/main/java/org/conscrypt/javax/net/ssl/FakeSSLSession.java +++ b/testing/src/main/java/org/conscrypt/javax/net/ssl/FakeSSLSession.java
@@ -19,6 +19,7 @@ import java.nio.charset.Charset; import java.security.Principal; import java.security.cert.Certificate; +import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSessionContext; @@ -76,7 +77,7 @@ } @Override - public Certificate[] getPeerCertificates() { + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { throw new UnsupportedOperationException(); }
diff --git a/testing/src/main/java/org/conscrypt/javax/net/ssl/TestHostnameVerifier.java b/testing/src/main/java/org/conscrypt/javax/net/ssl/TestHostnameVerifier.java index 7f8148e..9762bc3 100644 --- a/testing/src/main/java/org/conscrypt/javax/net/ssl/TestHostnameVerifier.java +++ b/testing/src/main/java/org/conscrypt/javax/net/ssl/TestHostnameVerifier.java
@@ -35,7 +35,7 @@ private static final int DNS_NAME_TYPE = 2; @SuppressWarnings("MixedMutabilityReturnType") - private List<String> getHostnames(X509Certificate cert) { + private static List<String> getHostnames(X509Certificate cert) { List<String> result = new ArrayList<String>(); try { Collection<List<?>> altNamePairs = cert.getSubjectAlternativeNames();
diff --git a/testing/src/main/java/tests/util/ServiceTester.java b/testing/src/main/java/tests/util/ServiceTester.java index 25b5881..813f845 100644 --- a/testing/src/main/java/tests/util/ServiceTester.java +++ b/testing/src/main/java/tests/util/ServiceTester.java
@@ -44,11 +44,13 @@ void test(Provider p, String algorithm) throws Exception; } + private static final String SEPARATOR = "||"; private final String service; - private Set<Provider> providers = new LinkedHashSet<>(); - private Set<Provider> skipProviders = new HashSet<>(); - private Set<String> algorithms = new LinkedHashSet<>(); - private Set<String> skipAlgorithms = new HashSet<>(); + private final Set<Provider> providers = new LinkedHashSet<>(); + private final Set<Provider> skipProviders = new HashSet<>(); + private final Set<String> algorithms = new LinkedHashSet<>(); + private final Set<String> skipAlgorithms = new HashSet<>(); + private final Set<String> skipCombinations = new HashSet<>(); private ServiceTester(String service) { this.service = service; @@ -128,6 +130,18 @@ } /** + * Causes the given combination of provider and algorithm to be omitted from this instance's + * testing. If no tested provider provides the given algorithm, does nothing. + */ + public ServiceTester skipCombination(String provider, String algorithm) { + Provider p = Security.getProvider(provider); + if (p != null) { + skipCombinations.add(makeCombination(provider, algorithm)); + } + return this; + } + + /** * Runs the given test against the configured combination of providers and algorithms. Continues * running all combinations even if some fail. If any of the test runs fail, this throws * an exception with the details of the failure(s). @@ -142,14 +156,17 @@ for (Provider p : providers) { if (algorithms.isEmpty()) { for (Provider.Service s : p.getServices()) { - if (s.getType().equals(service) && !skipAlgorithms.contains(s.getAlgorithm())) { + if (s.getType().equals(service) + && !skipAlgorithms.contains(s.getAlgorithm()) + && !shouldSkipCombination(p.getName(), s.getAlgorithm())) { doTest(test, p, s.getAlgorithm(), errors); } } } else { algorithms.removeAll(skipAlgorithms); for (String algorithm : algorithms) { - if (p.getService(service, algorithm) != null) { + if (p.getService(service, algorithm) != null + && !shouldSkipCombination(p.getName(), algorithm)) { doTest(test, p, algorithm, errors); } } @@ -161,6 +178,14 @@ } } + private String makeCombination(String provider, String algorithm) { + return provider + SEPARATOR + algorithm; + } + + private boolean shouldSkipCombination(String provider, String algorithm) { + return skipCombinations.contains(makeCombination(provider, algorithm)); + } + private void doTest(Test test, Provider p, String algorithm, PrintStream errors) { try { test.test(p, algorithm);