Make EntropyMixer mix in output of Hardware RNG into Linux RNG.

On devices with Hardware RNG (HW RNG), this makes EntropyMixer
periodically (every three hours) read 512 bytes from HW RNG and mix
them into the Linux RNG.

This is done without increasing Linux RNG's entropy estimates to
avoid having to trust the quality of the HW RNG. This approach cannot
reduce the quality of the Linux RNG output, but may increase it.

Change-Id: Ifde851004301ffd41b2189151a64a0c5989c630f
diff --git a/services/java/com/android/server/EntropyMixer.java b/services/java/com/android/server/EntropyMixer.java
index fbb66f9..cfdbf7d 100644
--- a/services/java/com/android/server/EntropyMixer.java
+++ b/services/java/com/android/server/EntropyMixer.java
@@ -36,7 +36,8 @@
 
 /**
  * A service designed to load and periodically save "randomness"
- * for the Linux kernel.
+ * for the Linux kernel RNG and to mix in data from Hardware RNG (if present)
+ * into the Linux RNG.
  *
  * <p>When a Linux system starts up, the entropy pool associated with
  * {@code /dev/random} may be in a fairly predictable state.  Applications which
@@ -45,6 +46,13 @@
  * this effect, it's helpful to carry the entropy pool information across
  * shutdowns and startups.
  *
+ * <p>On systems with Hardware RNG (/dev/hw_random), a block of output from HW
+ * RNG is mixed into the Linux RNG on EntropyMixer's startup and whenever
+ * EntropyMixer periodically runs to save a block of output from Linux RNG on
+ * disk. This mixing is done in a way that does not increase the Linux RNG's
+ * entropy estimate is not increased. This is to avoid having to trust/verify
+ * the quality and authenticity of the &quot;randomness&quot; of the HW RNG.
+ *
  * <p>This class was modeled after the script in
  * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">man
  * 4 random</a>.
@@ -57,6 +65,7 @@
     private static final long START_NANOTIME = System.nanoTime();
 
     private final String randomDevice;
+    private final String hwRandomDevice;
     private final String entropyFile;
 
     /**
@@ -69,6 +78,7 @@
                 Slog.e(TAG, "Will not process invalid message");
                 return;
             }
+            addHwRandomEntropy();
             writeEntropy();
             scheduleEntropyWriter();
         }
@@ -82,18 +92,25 @@
     };
 
     public EntropyMixer(Context context) {
-        this(context, getSystemDir() + "/entropy.dat", "/dev/urandom");
+        this(context, getSystemDir() + "/entropy.dat", "/dev/urandom", "/dev/hw_random");
     }
 
     /** Test only interface, not for public use */
-    public EntropyMixer(Context context, String entropyFile, String randomDevice) {
+    public EntropyMixer(
+            Context context,
+            String entropyFile,
+            String randomDevice,
+            String hwRandomDevice) {
         if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
+        if (hwRandomDevice == null) { throw new NullPointerException("hwRandomDevice"); }
         if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
 
         this.randomDevice = randomDevice;
+        this.hwRandomDevice = hwRandomDevice;
         this.entropyFile = entropyFile;
         loadInitialEntropy();
         addDeviceSpecificEntropy();
+        addHwRandomEntropy();
         writeEntropy();
         scheduleEntropyWriter();
         IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
@@ -168,6 +185,20 @@
         }
     }
 
+    /**
+     * Mixes in the output from HW RNG (if present) into the Linux RNG.
+     */
+    private void addHwRandomEntropy() {
+        try {
+            RandomBlock.fromFile(hwRandomDevice).toFile(randomDevice, false);
+            Slog.i(TAG, "Added HW RNG output to entropy pool");
+        } catch (FileNotFoundException ignored) {
+            // HW RNG not present/exposed -- ignore
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to add HW RNG output to entropy pool", e);
+        }
+    }
+
     private static String getSystemDir() {
         File dataDir = Environment.getDataDirectory();
         File systemDir = new File(dataDir, "system");