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