• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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