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