1/* 2 * Copyright (c) 2000-2004,2007-2008,2010,2012-2013 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// EntropyManager - manage entropy on the system. 27// 28// Here is our mission: 29// (1) On startup, read the entropy file and seed it into the RNG for initial use 30// (2) Periodically, collect entropy from the system and seed it into the RNG 31// (3) Once in a while, take entropy from the RNG and write it to the entropy file 32// for use across reboots. 33// 34// This class will fail to operate if the process has (and retains) root privileges. 35// We re-open the entropy file on each use so that we don't work with a "phantom" 36// file that some fool administrator removed yesterday. 37// 38#include "entropy.h" 39#include "dtrace.h" 40#include <sys/sysctl.h> 41#include <mach/clock_types.h> 42#include <mach/mach_time.h> 43#include <errno.h> 44#include <security_utilities/logging.h> 45#include <sys/sysctl.h> 46#include <security_utilities/debugging.h> 47#include <math.h> 48 49/* when true, action() called every 15 seconds */ 50#define ENTROPY_QUICK_UPDATE 0 51#if ENTROPY_QUICK_UPDATE 52#define COLLECT_INTERVAL 15 53#else 54#define COLLECT_INTERVAL collectInterval 55#endif //ENTROPY_QUICK_UPDATE 56 57using namespace UnixPlusPlus; 58 59 60// 61// During construction, we perform initial entropy file recovery. 62// 63EntropyManager::EntropyManager(MachPlusPlus::MachServer &srv, const char *entropyFile) 64 : DevRandomGenerator(true), server(srv), 65 mEntropyFilePath(entropyFile), mNextUpdate(Time::now()) 66{ 67 // Read the entropy file and seed the RNG. It is not an error if we can't find one. 68 try { 69 AutoFileDesc oldEntropyFile(entropyFile, O_RDONLY); 70 char buffer[entropyFileSize]; 71 if (size_t size = oldEntropyFile.read(buffer)) 72 addEntropy(buffer, size); 73 } catch (...) { } 74 75 // go through a collect/update/reschedule cycle immediately 76 action(); 77} 78 79 80// 81// Timer action 82// 83void EntropyManager::action() 84{ 85 collectEntropy(); 86 updateEntropyFile(); 87 88 server.setTimer(this, Time::Interval(COLLECT_INTERVAL)); // drifting reschedule (desired) 89} 90 91 92static const double kBytesOfEntropyToCollect = 240; 93// that gives us a minimum of 2.16 * 10^609 possible combinations. It's a finite number to be sure... 94 95static const int kExpectedLoops = 10; 96 97// Calculate the amount of entropy in the buffer (per Shannon's Entropy Calculation) 98static double CalculateEntropy(const void* buffer, size_t bufferSize) 99{ 100 double sizef = bufferSize; 101 const u_int8_t* charBuffer = (const u_int8_t*) buffer; 102 103 // zero the tabulation array 104 int counts[256]; 105 memset(counts, 0, sizeof(counts)); 106 107 // tabulate the occurances of each byte in the array 108 size_t i; 109 for (i = 0; i < bufferSize; ++i) 110 { 111 counts[charBuffer[i]] += 1; 112 } 113 114 // calculate the number of bits/byte of entropy 115 double entropy = 0.0; 116 117 for (i = 0; i < 256; ++i) 118 { 119 if (counts[i] > 0) 120 { 121 double p = ((double) counts[i]) / sizef; 122 double term = p * -log2(p); 123 entropy += term; 124 } 125 } 126 127 double entropicBytes = bufferSize * entropy / 8.0; 128 129 return entropicBytes; 130} 131 132 133 134// 135// Collect system timings and seed into the RNG. 136// Note that the sysctl will block until the buffer is full or the timeout expires. 137// We currently use a 1ms timeout, which almost always fills the buffer and 138// does not provide enough of a delay to worry about it. If we ever get worried, 139// we could call longTermActivity on the server object to get another thread going. 140// 141 142void EntropyManager::collectEntropy() 143{ 144 SECURITYD_ENTROPY_COLLECT(); 145 146 int mib[4]; 147 mib[0] = CTL_KERN; 148 mib[1] = KERN_KDEBUG; 149 mib[2] = KERN_KDGETENTROPY; 150 mib[3] = 1; // milliseconds maximum delay 151 152 mach_timespec_t buffer[timingsToCollect]; 153 154 int result; 155 156 double bytesRemaining = kBytesOfEntropyToCollect; 157 158 int loopCount = 0; 159 160 while (bytesRemaining >= 0) 161 { 162 size_t size = sizeof(mach_timespec_t) * timingsToCollect; 163 164 result = sysctl(mib,4, buffer, &size, NULL, 0); 165 if (result == -1) { 166 Syslog::alert("entropy measurement returned no entropy (errno=%d)", errno); 167 sleep(1); 168 } 169 else if (size == 0) 170 { 171 Syslog::alert("entropy measurement returned no entropy."); 172 sleep(1); 173 } 174 175 // remove the non-entropic pieces from the buffer 176 u_int16_t nonEnt[timingsToCollect]; 177 178 // treat the received buffer as an array of u_int16 and only take the first two bytes of each 179 u_int16_t *rawEnt = (u_int16_t*) buffer; 180 181 int i; 182 for (i = 0; i < timingsToCollect; ++i) 183 { 184 nonEnt[i] = *rawEnt; 185 rawEnt += 4; 186 } 187 188 SECURITYD_ENTROPY_SEED((void *)nonEnt, (unsigned int) sizeof(nonEnt)); 189 addEntropy(nonEnt, sizeof(nonEnt)); 190 191 double entropyRead = CalculateEntropy(nonEnt, sizeof(nonEnt)); 192 bytesRemaining -= entropyRead; 193 194 loopCount += 1; 195 } 196 197 if (loopCount > kExpectedLoops) 198 { 199 Syslog::alert("Entropy collection fulfillment took %d loops", loopCount); 200 } 201} 202 203 204// 205// (Re)write the entropy file with random data pulled from the RNG 206// 207void EntropyManager::updateEntropyFile() 208{ 209 if (Time::now() >= mNextUpdate) { 210 try { 211 SECURITYD_ENTROPY_SAVE((char *)mEntropyFilePath.c_str()); 212 mNextUpdate = Time::now() + Time::Interval(updateInterval); 213 secdebug("entropy", "updating %s", mEntropyFilePath.c_str()); 214 char buffer[entropyFileSize]; 215 random(buffer, entropyFileSize); 216 AutoFileDesc entropyFile(mEntropyFilePath.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0600); 217 if (entropyFile.write(buffer) != entropyFileSize) 218 Syslog::warning("short write on entropy file %s", mEntropyFilePath.c_str()); 219 } catch (...) { 220 Syslog::warning("error writing entropy file %s", mEntropyFilePath.c_str()); 221 } 222 } 223} 224 225