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. 40 * 41 * <p>When a Linux system starts up, the entropy pool associated with 42 * {@code /dev/random} may be in a fairly predictable state. Applications which 43 * depend strongly on randomness may find {@code /dev/random} or 44 * {@code /dev/urandom} returning predictable data. In order to counteract 45 * this effect, it's helpful to carry the entropy pool information across 46 * shutdowns and startups. 47 * 48 * <p>This class was modeled after the script in 49 * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">man 50 * 4 random</a>. 51 */ 52public class EntropyMixer extends Binder { 53 private static final String TAG = "EntropyMixer"; 54 private static final int ENTROPY_WHAT = 1; 55 private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs 56 private static final long START_TIME = System.currentTimeMillis(); 57 private static final long START_NANOTIME = System.nanoTime(); 58 59 private final String randomDevice; 60 private final String entropyFile; 61 62 /** 63 * Handler that periodically updates the entropy on disk. 64 */ 65 private final Handler mHandler = new Handler() { 66 @Override 67 public void handleMessage(Message msg) { 68 if (msg.what != ENTROPY_WHAT) { 69 Slog.e(TAG, "Will not process invalid message"); 70 return; 71 } 72 writeEntropy(); 73 scheduleEntropyWriter(); 74 } 75 }; 76 77 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 78 @Override 79 public void onReceive(Context context, Intent intent) { 80 writeEntropy(); 81 } 82 }; 83 84 public EntropyMixer(Context context) { 85 this(context, getSystemDir() + "/entropy.dat", "/dev/urandom"); 86 } 87 88 /** Test only interface, not for public use */ 89 public EntropyMixer(Context context, String entropyFile, String randomDevice) { 90 if (randomDevice == null) { throw new NullPointerException("randomDevice"); } 91 if (entropyFile == null) { throw new NullPointerException("entropyFile"); } 92 93 this.randomDevice = randomDevice; 94 this.entropyFile = entropyFile; 95 loadInitialEntropy(); 96 addDeviceSpecificEntropy(); 97 writeEntropy(); 98 scheduleEntropyWriter(); 99 IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); 100 broadcastFilter.addAction(Intent.ACTION_POWER_CONNECTED); 101 broadcastFilter.addAction(Intent.ACTION_REBOOT); 102 context.registerReceiver(mBroadcastReceiver, broadcastFilter); 103 } 104 105 private void scheduleEntropyWriter() { 106 mHandler.removeMessages(ENTROPY_WHAT); 107 mHandler.sendEmptyMessageDelayed(ENTROPY_WHAT, ENTROPY_WRITE_PERIOD); 108 } 109 110 private void loadInitialEntropy() { 111 try { 112 RandomBlock.fromFile(entropyFile).toFile(randomDevice, false); 113 } catch (FileNotFoundException e) { 114 Slog.w(TAG, "No existing entropy file -- first boot?"); 115 } catch (IOException e) { 116 Slog.w(TAG, "Failure loading existing entropy file", e); 117 } 118 } 119 120 private void writeEntropy() { 121 try { 122 Slog.i(TAG, "Writing entropy..."); 123 RandomBlock.fromFile(randomDevice).toFile(entropyFile, true); 124 } catch (IOException e) { 125 Slog.w(TAG, "Unable to write entropy", e); 126 } 127 } 128 129 /** 130 * Add additional information to the kernel entropy pool. The 131 * information isn't necessarily "random", but that's ok. Even 132 * sending non-random information to {@code /dev/urandom} is useful 133 * because, while it doesn't increase the "quality" of the entropy pool, 134 * it mixes more bits into the pool, which gives us a higher degree 135 * of uncertainty in the generated randomness. Like nature, writes to 136 * the random device can only cause the quality of the entropy in the 137 * kernel to stay the same or increase. 138 * 139 * <p>For maximum effect, we try to target information which varies 140 * on a per-device basis, and is not easily observable to an 141 * attacker. 142 */ 143 private void addDeviceSpecificEntropy() { 144 PrintWriter out = null; 145 try { 146 out = new PrintWriter(new FileOutputStream(randomDevice)); 147 out.println("Copyright (C) 2009 The Android Open Source Project"); 148 out.println("All Your Randomness Are Belong To Us"); 149 out.println(START_TIME); 150 out.println(START_NANOTIME); 151 out.println(SystemProperties.get("ro.serialno")); 152 out.println(SystemProperties.get("ro.bootmode")); 153 out.println(SystemProperties.get("ro.baseband")); 154 out.println(SystemProperties.get("ro.carrier")); 155 out.println(SystemProperties.get("ro.bootloader")); 156 out.println(SystemProperties.get("ro.hardware")); 157 out.println(SystemProperties.get("ro.revision")); 158 out.println(SystemProperties.get("ro.build.fingerprint")); 159 out.println(new Object().hashCode()); 160 out.println(System.currentTimeMillis()); 161 out.println(System.nanoTime()); 162 } catch (IOException e) { 163 Slog.w(TAG, "Unable to add device specific data to the entropy pool", e); 164 } finally { 165 if (out != null) { 166 out.close(); 167 } 168 } 169 } 170 171 private static String getSystemDir() { 172 File dataDir = Environment.getDataDirectory(); 173 File systemDir = new File(dataDir, "system"); 174 systemDir.mkdirs(); 175 return systemDir.toString(); 176 } 177} 178