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