• 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.Slog;
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 TAG = "EntropyService";
52     private static final int ENTROPY_WHAT = 1;
53     private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000;  // 3 hrs
54     private static final long START_TIME = System.currentTimeMillis();
55     private static final long START_NANOTIME = System.nanoTime();
56 
57     private final String randomDevice;
58     private final String entropyFile;
59 
60     /**
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) {
67                 Slog.e(TAG, "Will not process invalid message");
68                 return;
69             }
70             writeEntropy();
71             scheduleEntropyWriter();
72         }
73     };
74 
EntropyService()75     public EntropyService() {
76         this(getSystemDir() + "/entropy.dat", "/dev/urandom");
77     }
78 
79     /** Test only interface, not for public use */
EntropyService(String entropyFile, String randomDevice)80     public EntropyService(String entropyFile, String randomDevice) {
81         if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
82         if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
83 
84         this.randomDevice = randomDevice;
85         this.entropyFile = entropyFile;
86         loadInitialEntropy();
87         addDeviceSpecificEntropy();
88         writeEntropy();
89         scheduleEntropyWriter();
90     }
91 
scheduleEntropyWriter()92     private void scheduleEntropyWriter() {
93         mHandler.removeMessages(ENTROPY_WHAT);
94         mHandler.sendEmptyMessageDelayed(ENTROPY_WHAT, ENTROPY_WRITE_PERIOD);
95     }
96 
loadInitialEntropy()97     private void loadInitialEntropy() {
98         try {
99             RandomBlock.fromFile(entropyFile).toFile(randomDevice);
100         } catch (IOException e) {
101             Slog.w(TAG, "unable to load initial entropy (first boot?)", e);
102         }
103     }
104 
writeEntropy()105     private void writeEntropy() {
106         try {
107             RandomBlock.fromFile(randomDevice).toFile(entropyFile);
108         } catch (IOException e) {
109             Slog.w(TAG, "unable to write entropy", e);
110         }
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      */
addDeviceSpecificEntropy()127     private void addDeviceSpecificEntropy() {
128         PrintWriter out = null;
129         try {
130             out = new PrintWriter(new FileOutputStream(randomDevice));
131             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"));
142             out.println(new Object().hashCode());
143             out.println(System.currentTimeMillis());
144             out.println(System.nanoTime());
145         } catch (IOException e) {
146             Slog.w(TAG, "Unable to add device specific data to the entropy pool", e);
147         } finally {
148             if (out != null) {
149                 out.close();
150             }
151         }
152     }
153 
getSystemDir()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 }
161