am 7dcd2b0d: Merge "Add CTS tests for Hardware RNG (/dev/hw_random)."
* commit '7dcd2b0d49397af54aeffd3202218fc6e7482cfa':
Add CTS tests for Hardware RNG (/dev/hw_random).
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index b6df70d..83f2a36 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -742,7 +742,6 @@
new File("/dev/felica_uicc"), // b/11142586
new File("/dev/full"),
new File("/dev/genlock"), // b/9035217
- new File("/dev/hw_random"), // b/9191279
new File("/dev/ion"),
new File("/dev/kgsl-2d0"), // b/11271533
new File("/dev/kgsl-2d1"), // b/11271533
@@ -806,6 +805,24 @@
(status.mode & 0666) == 0666);
}
+ public void testDevHwRandomLockedDown() throws Exception {
+ File f = new File("/dev/hw_random");
+ if (!f.exists()) {
+ // HW RNG is not required to be exposed on all devices.
+ return;
+ }
+
+ FileUtils.FileStatus status = new FileUtils.FileStatus();
+ assertTrue(FileUtils.getFileStatus(f.getCanonicalPath(), status, false));
+ assertTrue(
+ f + " has wrong file mode: 0"
+ + Integer.toOctalString(status.mode),
+ (status.mode & 0777) == 0440);
+
+ assertFileOwnedBy(f, "root");
+ assertFileOwnedByGroup(f, "system");
+ }
+
/**
* Test that the /system/bin/run-as command has setuid and setgid
* attributes set on the file. If these calls fail, debugger
diff --git a/tests/tests/security/src/android/security/cts/HwRngTest.java b/tests/tests/security/src/android/security/cts/HwRngTest.java
new file mode 100644
index 0000000..3dc11af
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/HwRngTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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 android.security.cts;
+
+import junit.framework.TestCase;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tests for hardware random number generator device {@code /dev/hw_random}.
+ */
+public class HwRngTest extends TestCase {
+
+ // The block of constants below is from hw_random framework documentation and source code:
+ // see https://www.kernel.org/doc/Documentation/hw_random.txt.
+ private static final File DEV_HW_RANDOM = new File("/dev/hw_random");
+ private static final File SYSFS_HW_RANDOM = new File("/sys/class/misc/hw_random");
+ private static final String HWRNG_DRIVER_NAME = "hwrng";
+ private static final int HWRNG_DRIVER_MAJOR = 10;
+ private static final int HWRNG_DRIVER_MINOR = 183;
+
+ /**
+ * Asserts that the {@code /dev/hw_random} device is configured in an acceptable way or is not
+ * present at all.
+ */
+ public void testConfigurationIfFilePresent() throws Exception {
+ if (!DEV_HW_RANDOM.exists()) {
+ // The device is not present
+ return;
+ }
+
+ // Sanity check based on https://www.kernel.org/doc/Documentation/hw_random.txt.
+
+ // In short, assert that:
+ // 1. hwrng kernel driver is using MAJOR 10 and MINOR 183,
+ // 2. a driver is selected in the hrwng framework, and
+ // 3. /dev/hw_random references a character device with the above MAJOR and MINOR.
+
+ // 1. Assert that the hwrng driver is using MAJOR 10 and MINOR 183.
+ // We achieve this by inspecting /sys/class/misc/hw_random/dev and uevent.
+ assertTrue(SYSFS_HW_RANDOM + " not found", SYSFS_HW_RANDOM.isDirectory());
+ assertEquals(
+ "Driver major:minor",
+ HWRNG_DRIVER_MAJOR + ":" + HWRNG_DRIVER_MINOR,
+ readyFullyAsciiFile(new File(SYSFS_HW_RANDOM, "dev")).trim());
+
+ Map<String, String> ueventVars = parseUeventFile(new File(SYSFS_HW_RANDOM, "uevent"));
+ assertEquals("uevent MAJOR", String.valueOf(HWRNG_DRIVER_MAJOR), ueventVars.get("MAJOR"));
+ assertEquals("uevent MINOR", String.valueOf(HWRNG_DRIVER_MINOR), ueventVars.get("MINOR"));
+ assertEquals("uevent DEVNAME", HWRNG_DRIVER_NAME, ueventVars.get("DEVNAME"));
+
+ // 2. Assert that a driver is selected in the hrwng framework.
+ // We achieve this by inspecting /sys/class/misc/hw_random/rng_current.
+ File rngCurrentFile = new File(SYSFS_HW_RANDOM, "rng_current");
+ String rngCurrent = readyFullyAsciiFile(rngCurrentFile);
+ assertFalse(
+ "No driver selected according to " + rngCurrentFile,
+ rngCurrent.trim().isEmpty());
+
+ // 3. Assert that /dev/hw_random references a character device with the above MAJOR+MINOR.
+ assertEquals(
+ DEV_HW_RANDOM + " major",
+ HWRNG_DRIVER_MAJOR,
+ LinuxRngTest.getCharDeviceMajor(DEV_HW_RANDOM.getCanonicalPath()));
+ assertEquals(
+ DEV_HW_RANDOM + " minor",
+ HWRNG_DRIVER_MINOR,
+ LinuxRngTest.getCharDeviceMinor(DEV_HW_RANDOM.getCanonicalPath()));
+ }
+
+ private static String readyFullyAsciiFile(File file) throws IOException {
+ InputStream in = null;
+ try {
+ in = new FileInputStream(file);
+ return new String(readyFully(in), "US-ASCII");
+ } finally {
+ closeSilently(in);
+ }
+ }
+
+ private static byte[] readyFully(InputStream in) throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ byte[] buffer = new byte[16 * 1024];
+ int chunkSize;
+ while ((chunkSize = in.read(buffer)) != -1) {
+ result.write(buffer, 0, chunkSize);
+ }
+ return result.toByteArray();
+ }
+
+ private static Map<String, String> parseUeventFile(File file) throws IOException {
+ // The format of the file is line-oriented.
+ // Each variable takes up one line.
+ // The typical format of a variable is KEY=VALUE
+
+ Map<String, String> result = new HashMap<String, String>();
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "US-ASCII"));
+ String line;
+ while ((line = in.readLine()) != null) {
+ if (line.trim().length() == 0) {
+ // Ignore empty lines
+ continue;
+ }
+
+ String key;
+ String value;
+ int delimiterIndex = line.indexOf('=');
+ if (delimiterIndex != -1) {
+ key = line.substring(0, delimiterIndex);
+ value = line.substring(delimiterIndex + 1);
+ } else {
+ key = line;
+ value = "";
+ }
+
+ if (result.containsKey(key)) {
+ throw new IllegalArgumentException("Multiple values for key: " + key);
+ }
+ result.put(key, value);
+ }
+ return result;
+ } finally {
+ closeSilently(in);
+ }
+ }
+
+ private static void closeSilently(Closeable in) {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException ignored) {}
+ }
+ }
+}
\ No newline at end of file