EntropyMixer.java revision 9158825f9c41869689d6b1786d7c7aa8bdd524ce
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server; 18 19import java.io.File; 20import java.io.FileNotFoundException; 21import java.io.FileOutputStream; 22import java.io.IOException; 23import java.io.OutputStream; 24import java.io.PrintWriter; 25 26import android.content.BroadcastReceiver; 27import android.content.Context; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.os.Binder; 31import android.os.Environment; 32import android.os.Handler; 33import android.os.Message; 34import android.os.SystemProperties; 35import android.util.Slog; 36 37/** 38 * A service designed to load and periodically save "randomness" 39 * for the Linux kernel RNG and to mix in data from Hardware RNG (if present) 40 * into the Linux RNG. 41 * 42 * <p>When a Linux system starts up, the entropy pool associated with 43 * {@code /dev/random} may be in a fairly predictable state. Applications which 44 * depend strongly on randomness may find {@code /dev/random} or 45 * {@code /dev/urandom} returning predictable data. In order to counteract 46 * this effect, it's helpful to carry the entropy pool information across 47 * shutdowns and startups. 48 * 49 * <p>On systems with Hardware RNG (/dev/hw_random), a block of output from HW 50 * RNG is mixed into the Linux RNG on EntropyMixer's startup and whenever 51 * EntropyMixer periodically runs to save a block of output from Linux RNG on 52 * disk. This mixing is done in a way that does not increase the Linux RNG's 53 * entropy estimate is not increased. This is to avoid having to trust/verify 54 * the quality and authenticity of the "randomness" of the HW RNG. 55 * 56 * <p>This class was modeled after the script in 57 * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">man 58 * 4 random</a>. 59 */ 60public class EntropyMixer extends Binder { 61 private static final String TAG = "EntropyMixer"; 62 private static final int ENTROPY_WHAT = 1; 63 private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs 64 private static final long START_TIME = System.currentTimeMillis(); 65 private static final long START_NANOTIME = System.nanoTime(); 66 67 private final String randomDevice; 68 private final String hwRandomDevice; 69 private final String entropyFile; 70 71 /** 72 * Handler that periodically updates the entropy on disk. 73 */ 74 private final Handler mHandler = new Handler() { 75 @Override 76 public void handleMessage(Message msg) { 77 if (msg.what != ENTROPY_WHAT) { 78 Slog.e(TAG, "Will not process invalid message"); 79 return; 80 } 81 addHwRandomEntropy(); 82 writeEntropy(); 83 scheduleEntropyWriter(); 84 } 85 }; 86 87 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 88 @Override 89 public void onReceive(Context context, Intent intent) { 90 writeEntropy(); 91 } 92 }; 93 94 public EntropyMixer(Context context) { 95 this(context, getSystemDir() + "/entropy.dat", "/dev/urandom", "/dev/hw_random"); 96 } 97 98 /** Test only interface, not for public use */ 99 public EntropyMixer( 100 Context context, 101 String entropyFile, 102 String randomDevice, 103 String hwRandomDevice) { 104 if (randomDevice == null) { throw new NullPointerException("randomDevice"); } 105 if (hwRandomDevice == null) { throw new NullPointerException("hwRandomDevice"); } 106 if (entropyFile == null) { throw new NullPointerException("entropyFile"); } 107 108 this.randomDevice = randomDevice; 109 this.hwRandomDevice = hwRandomDevice; 110 this.entropyFile = entropyFile; 111 loadInitialEntropy(); 112 addDeviceSpecificEntropy(); 113 addHwRandomEntropy(); 114 writeEntropy(); 115 scheduleEntropyWriter(); 116 IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); 117 broadcastFilter.addAction(Intent.ACTION_POWER_CONNECTED); 118 broadcastFilter.addAction(Intent.ACTION_REBOOT); 119 context.registerReceiver(mBroadcastReceiver, broadcastFilter); 120 } 121 122 private void scheduleEntropyWriter() { 123 mHandler.removeMessages(ENTROPY_WHAT); 124 mHandler.sendEmptyMessageDelayed(ENTROPY_WHAT, ENTROPY_WRITE_PERIOD); 125 } 126 127 private void loadInitialEntropy() { 128 try { 129 RandomBlock.fromFile(entropyFile).toFile(randomDevice, false); 130 } catch (FileNotFoundException e) { 131 Slog.w(TAG, "No existing entropy file -- first boot?"); 132 } catch (IOException e) { 133 Slog.w(TAG, "Failure loading existing entropy file", e); 134 } 135 } 136 137 private void writeEntropy() { 138 try { 139 Slog.i(TAG, "Writing entropy..."); 140 RandomBlock.fromFile(randomDevice).toFile(entropyFile, true); 141 } catch (IOException e) { 142 Slog.w(TAG, "Unable to write entropy", e); 143 } 144 } 145 146 /** 147 * Add additional information to the kernel entropy pool. The 148 * information isn't necessarily "random", but that's ok. Even 149 * sending non-random information to {@code /dev/urandom} is useful 150 * because, while it doesn't increase the "quality" of the entropy pool, 151 * it mixes more bits into the pool, which gives us a higher degree 152 * of uncertainty in the generated randomness. Like nature, writes to 153 * the random device can only cause the quality of the entropy in the 154 * kernel to stay the same or increase. 155 * 156 * <p>For maximum effect, we try to target information which varies 157 * on a per-device basis, and is not easily observable to an 158 * attacker. 159 */ 160 private void addDeviceSpecificEntropy() { 161 PrintWriter out = null; 162 try { 163 out = new PrintWriter(new FileOutputStream(randomDevice)); 164 out.println("Copyright (C) 2009 The Android Open Source Project"); 165 out.println("All Your Randomness Are Belong To Us"); 166 out.println(START_TIME); 167 out.println(START_NANOTIME); 168 out.println(SystemProperties.get("ro.serialno")); 169 out.println(SystemProperties.get("ro.bootmode")); 170 out.println(SystemProperties.get("ro.baseband")); 171 out.println(SystemProperties.get("ro.carrier")); 172 out.println(SystemProperties.get("ro.bootloader")); 173 out.println(SystemProperties.get("ro.hardware")); 174 out.println(SystemProperties.get("ro.revision")); 175 out.println(SystemProperties.get("ro.build.fingerprint")); 176 out.println(new Object().hashCode()); 177 out.println(System.currentTimeMillis()); 178 out.println(System.nanoTime()); 179 } catch (IOException e) { 180 Slog.w(TAG, "Unable to add device specific data to the entropy pool", e); 181 } finally { 182 if (out != null) { 183 out.close(); 184 } 185 } 186 } 187 188 /** 189 * Mixes in the output from HW RNG (if present) into the Linux RNG. 190 */ 191 private void addHwRandomEntropy() { 192 try { 193 RandomBlock.fromFile(hwRandomDevice).toFile(randomDevice, false); 194 Slog.i(TAG, "Added HW RNG output to entropy pool"); 195 } catch (FileNotFoundException ignored) { 196 // HW RNG not present/exposed -- ignore 197 } catch (IOException e) { 198 Slog.w(TAG, "Failed to add HW RNG output to entropy pool", e); 199 } 200 } 201 202 private static String getSystemDir() { 203 File dataDir = Environment.getDataDirectory(); 204 File systemDir = new File(dataDir, "system"); 205 systemDir.mkdirs(); 206 return systemDir.toString(); 207 } 208} 209