blob: b63a70ecc54844d1a06f911c22e4b996270c1b3a [file] [log] [blame]
Nick Kralevich4fb25612009-06-17 16:03:22 -07001/*
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;
Nick Kralevichb8cba952009-06-19 09:45:35 -070020import java.io.FileOutputStream;
Nick Kralevich4fb25612009-06-17 16:03:22 -070021import java.io.IOException;
Nick Kralevichb8cba952009-06-19 09:45:35 -070022import java.io.OutputStream;
23import java.io.PrintWriter;
Nick Kralevich4fb25612009-06-17 16:03:22 -070024
25import android.os.Binder;
26import android.os.Environment;
27import android.os.Handler;
28import android.os.Message;
Nick Kralevichb8cba952009-06-19 09:45:35 -070029import android.os.SystemProperties;
Joe Onorato8a9b2202010-02-26 18:56:32 -080030import android.util.Slog;
Nick Kralevich4fb25612009-06-17 16:03:22 -070031
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 */
Nick Kralevich6967cbc2011-11-17 13:24:32 -080050public class EntropyMixer extends Binder {
51 private static final String TAG = "EntropyMixer";
Nick Kralevich4fb25612009-06-17 16:03:22 -070052 private static final int ENTROPY_WHAT = 1;
53 private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs
Nick Kralevichb8cba952009-06-19 09:45:35 -070054 private static final long START_TIME = System.currentTimeMillis();
55 private static final long START_NANOTIME = System.nanoTime();
Nick Kralevich4fb25612009-06-17 16:03:22 -070056
Nick Kralevich93a68392010-03-19 16:57:21 -070057 private final String randomDevice;
58 private final String entropyFile;
59
Nick Kralevich4fb25612009-06-17 16:03:22 -070060 /**
61 * Handler that periodically updates the entropy on disk.
62 */
63 private final Handler mHandler = new Handler() {
64 @Override
65 public void handleMessage(Message msg) {
66 if (msg.what != ENTROPY_WHAT) {
Joe Onorato8a9b2202010-02-26 18:56:32 -080067 Slog.e(TAG, "Will not process invalid message");
Nick Kralevich4fb25612009-06-17 16:03:22 -070068 return;
69 }
70 writeEntropy();
71 scheduleEntropyWriter();
72 }
73 };
74
Nick Kralevich6967cbc2011-11-17 13:24:32 -080075 public EntropyMixer() {
Nick Kralevich93a68392010-03-19 16:57:21 -070076 this(getSystemDir() + "/entropy.dat", "/dev/urandom");
77 }
78
79 /** Test only interface, not for public use */
Nick Kralevich6967cbc2011-11-17 13:24:32 -080080 public EntropyMixer(String entropyFile, String randomDevice) {
Nick Kralevich93a68392010-03-19 16:57:21 -070081 if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
82 if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
83
84 this.randomDevice = randomDevice;
85 this.entropyFile = entropyFile;
Nick Kralevich4fb25612009-06-17 16:03:22 -070086 loadInitialEntropy();
Nick Kralevichb8cba952009-06-19 09:45:35 -070087 addDeviceSpecificEntropy();
Nick Kralevich4fb25612009-06-17 16:03:22 -070088 writeEntropy();
89 scheduleEntropyWriter();
90 }
91
92 private void scheduleEntropyWriter() {
93 mHandler.removeMessages(ENTROPY_WHAT);
94 mHandler.sendEmptyMessageDelayed(ENTROPY_WHAT, ENTROPY_WRITE_PERIOD);
95 }
96
97 private void loadInitialEntropy() {
98 try {
Elliott Hughes69078912011-04-04 12:15:34 -070099 RandomBlock.fromFile(entropyFile).toFile(randomDevice, false);
Nick Kralevich4fb25612009-06-17 16:03:22 -0700100 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800101 Slog.w(TAG, "unable to load initial entropy (first boot?)", e);
Nick Kralevich4fb25612009-06-17 16:03:22 -0700102 }
103 }
104
105 private void writeEntropy() {
106 try {
Elliott Hughes69078912011-04-04 12:15:34 -0700107 RandomBlock.fromFile(randomDevice).toFile(entropyFile, true);
Nick Kralevich4fb25612009-06-17 16:03:22 -0700108 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800109 Slog.w(TAG, "unable to write entropy", e);
Nick Kralevichb8cba952009-06-19 09:45:35 -0700110 }
111 }
112
113 /**
114 * Add additional information to the kernel entropy pool. The
115 * information isn't necessarily "random", but that's ok. Even
116 * sending non-random information to {@code /dev/urandom} is useful
117 * because, while it doesn't increase the "quality" of the entropy pool,
118 * it mixes more bits into the pool, which gives us a higher degree
119 * of uncertainty in the generated randomness. Like nature, writes to
120 * the random device can only cause the quality of the entropy in the
121 * kernel to stay the same or increase.
122 *
123 * <p>For maximum effect, we try to target information which varies
124 * on a per-device basis, and is not easily observable to an
125 * attacker.
126 */
127 private void addDeviceSpecificEntropy() {
128 PrintWriter out = null;
129 try {
Nick Kralevich93a68392010-03-19 16:57:21 -0700130 out = new PrintWriter(new FileOutputStream(randomDevice));
Nick Kralevichb8cba952009-06-19 09:45:35 -0700131 out.println("Copyright (C) 2009 The Android Open Source Project");
132 out.println("All Your Randomness Are Belong To Us");
133 out.println(START_TIME);
134 out.println(START_NANOTIME);
135 out.println(SystemProperties.get("ro.serialno"));
136 out.println(SystemProperties.get("ro.bootmode"));
137 out.println(SystemProperties.get("ro.baseband"));
138 out.println(SystemProperties.get("ro.carrier"));
139 out.println(SystemProperties.get("ro.bootloader"));
140 out.println(SystemProperties.get("ro.hardware"));
141 out.println(SystemProperties.get("ro.revision"));
Nick Kralevichb91ec412010-09-27 14:49:00 -0700142 out.println(new Object().hashCode());
Nick Kralevichb8cba952009-06-19 09:45:35 -0700143 out.println(System.currentTimeMillis());
144 out.println(System.nanoTime());
145 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800146 Slog.w(TAG, "Unable to add device specific data to the entropy pool", e);
Nick Kralevichb8cba952009-06-19 09:45:35 -0700147 } finally {
148 if (out != null) {
149 out.close();
150 }
Nick Kralevich4fb25612009-06-17 16:03:22 -0700151 }
152 }
153
154 private static String getSystemDir() {
155 File dataDir = Environment.getDataDirectory();
156 File systemDir = new File(dataDir, "system");
157 systemDir.mkdirs();
158 return systemDir.toString();
159 }
160}