Merge "Make test for hardware AES support explicit." am: df3055d2ec
Change-Id: I7c117d6454af4abcad4ceb1f73d7a4e26d5b003f
diff --git a/luni/src/test/java/libcore/javax/crypto/HardwareAesTest.java b/luni/src/test/java/libcore/javax/crypto/HardwareAesTest.java
new file mode 100644
index 0000000..f79bb81
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/HardwareAesTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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 libcore.javax.crypto;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import libcore.java.security.CpuFeatures;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class HardwareAesTest {
+
+ @Test
+ public void hardwareAesAvailability() {
+ // Test is only applicable if we know for sure that the device should support
+ // hardware AES. That covers the important cases (non-emulated ARM and x86_64),
+ // For everything else we assume BoringSSL does the right thing.
+ assumeTrue(CpuFeatures.isKnownToSupportHardwareAes());
+ assertTrue(CpuFeatures.isAesHardwareAccelerated());
+ }
+}
diff --git a/support/src/test/java/libcore/java/security/CpuFeatures.java b/support/src/test/java/libcore/java/security/CpuFeatures.java
index 8ab610f..df65ed2 100644
--- a/support/src/test/java/libcore/java/security/CpuFeatures.java
+++ b/support/src/test/java/libcore/java/security/CpuFeatures.java
@@ -16,47 +16,87 @@
package libcore.java.security;
+import android.system.Os;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import dalvik.system.VMRuntime;
-class CpuFeatures {
+public class CpuFeatures {
+ /** Machine architecture, determined from the "machine" value returned by uname() */
+ private enum Arch {
+ // 64bit ARM can return armv8 or aarch64.
+ // 32bit ARM should return armv7 or armv7a
+ ARM("^aarch.*|^arm.*"),
+ // 64bit Android and Linux generally return x86_64.
+ // 32bit Android and Linux generally return i686
+ // Other host architectures can potentially return x86 or i386.
+ X86("^x86.*|i386|i686");
+
+ private final String machineRegex;
+
+ Arch(String machineRegex) {
+ this.machineRegex = machineRegex;
+ }
+
+ /**
+ * Returns the architecture of this machine by matching against output from uname()
+ * against the regex for each known family.
+ */
+ public static Arch currentArch() {
+ String machine = Os.uname().machine;
+ for (Arch type : values()) {
+ if (machine.matches(type.machineRegex)) {
+ return type;
+ }
+ }
+ throw new IllegalStateException("Unknown machine value: " + machine);
+ }
+ }
+
+ private enum InstructionSet {
+ ARM_32(Arch.ARM, "arm"),
+ ARM_64(Arch.ARM, "arm64"),
+ X86_32(Arch.X86, "x86"),
+ X86_64(Arch.X86, "x86_64");
+
+ private final Arch arch;
+ private final String name;
+
+ InstructionSet(Arch arch, String name) {
+ this.arch = arch;
+ this.name = name;
+ }
+
+ public Arch architecture() {
+ return arch;
+ }
+
+ /**
+ * Returns the current InstructionSet set by matching against the name fields above.
+ */
+ public static InstructionSet currentInstructionSet() {
+ // Always returns one of the values from VMRuntime.ABI_TO_INSTRUCTION_SET_MAP.
+ String instructionSet = VMRuntime.getCurrentInstructionSet();
+ for (InstructionSet set : values()) {
+ if (instructionSet.equals(set.name)) {
+ return set;
+ }
+ }
+ throw new IllegalStateException("Unknown instruction set: " + instructionSet);
+ }
+ }
+
private CpuFeatures() {
}
- static boolean isAESHardwareAccelerated() {
- // Expectations based on CPU type: If these aren't met then Conscrypt
- // integration tests will fail and the cause should be investigated.
- String instructionSet = VMRuntime.getCurrentInstructionSet();
- if (instructionSet.startsWith("arm")) {
- // All ARM CPUs with the "aes" feature should have hardware AES.
- List<String> features = getListFromCpuinfo("Features");
- if (features != null && features.contains("aes")) {
- return true;
- }
- } else if (instructionSet.startsWith("x86")) {
- // x86 CPUs with the "aes" flag and running in 64bit mode should have hardware AES.
- if ("x86_64".equals(instructionSet)) {
- List<String> flags = getListFromCpuinfo("flags");
- if (flags != null && flags.contains("aes")) {
- return true;
- }
- } else {
- // Hardware AES not supported in 32bit mode.
- return false;
- }
- }
-
- // Otherwise trust Conscrypt NativeCrypto's own checks, for example if we're in an
- // emulated ABI, it might bridge to a library that has accelerated AES instructions.
+ /**
+ * Returns true if this device has hardware AES support as determined by BoringSSL.
+ */
+ public static boolean isAesHardwareAccelerated() {
try {
Class<?> nativeCrypto = Class.forName("com.android.org.conscrypt.NativeCrypto");
Method EVP_has_aes_hardware = nativeCrypto.getDeclaredMethod("EVP_has_aes_hardware");
@@ -71,33 +111,51 @@
return false;
}
- private static String getFieldFromCpuinfo(String field) {
- try {
- BufferedReader br = new BufferedReader(new FileReader("/proc/cpuinfo"));
- Pattern p = Pattern.compile(field + "\\s*:\\s*(.*)");
+ /**
+ * Returns true if this device should have hardware AES support based on CPU information.
+ *
+ * A return value of false means that acceleration isn't expected, but it may still be available
+ * e.g. via bridging to a native library in an emulated environment.
+ */
+ public static boolean isKnownToSupportHardwareAes() {
+ Arch architecture = Arch.currentArch();
+ InstructionSet instructionSet = InstructionSet.currentInstructionSet();
- try {
- String line;
- while ((line = br.readLine()) != null) {
- Matcher m = p.matcher(line);
- if (m.matches()) {
- return m.group(1);
- }
+ if (!instructionSet.architecture().equals(architecture)) {
+ // Different architectures imply an emulated environment, so unable to determine if
+ // hardware acceleration is expected. Assume not.
+ return false;
+ }
+
+ if (architecture.equals(Arch.ARM)) {
+ // All ARM CPUs (32 and 64 bit) with the "aes" feature should have hardware AES.
+ return cpuFieldContainsAes("Features");
+ } else if (instructionSet.equals(InstructionSet.X86_64)) {
+ // x86 CPUs with the "aes" flag and running in 64bit mode should have hardware AES.
+ // Hardware AES is not *expected* in 32bit mode, but may be available.
+ return cpuFieldContainsAes("flags");
+ }
+ return false;
+ }
+
+
+ /**
+ * Returns true if any line in the output from /proc/cpuinfo matches the provided
+ * field name and contains the word "aes" in its list of values.
+ *
+ * Example line from /proc/cpuinfo: Features : fp asimd evtstrm aes pmull sha1 sha2 crc32
+ */
+ private static boolean cpuFieldContainsAes(String fieldName) {
+ try (BufferedReader br = new BufferedReader(new FileReader("/proc/cpuinfo"))) {
+ String regex = "^" + fieldName + "\\s*:.*\\baes\\b.*";
+ String line;
+ while ((line = br.readLine()) != null) {
+ if (line.matches(regex)) {
+ return true;
}
- } finally {
- br.close();
}
} catch (IOException ignored) {
}
-
- return null;
- }
-
- private static List<String> getListFromCpuinfo(String fieldName) {
- String features = getFieldFromCpuinfo(fieldName);
- if (features == null)
- return null;
-
- return Arrays.asList(features.split("\\s"));
+ return false;
}
}
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index d53117b..7521cf9 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -757,7 +757,7 @@
"SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
"SSL_RSA_WITH_RC4_128_MD5",
"TLS_EMPTY_RENEGOTIATION_INFO_SCSV")
- : CpuFeatures.isAESHardwareAccelerated() ? CIPHER_SUITES_ANDROID_AES_HARDWARE
+ : CpuFeatures.isAesHardwareAccelerated() ? CIPHER_SUITES_ANDROID_AES_HARDWARE
: CIPHER_SUITES_ANDROID_SOFTWARE;
private static final Map<String, Class<? extends KeySpec>> PRIVATE_KEY_SPEC_CLASSES;