]> git.saurik.com Git - apple/securityd.git/blob - src/entropy.cpp
securityd-55137.5.tar.gz
[apple/securityd.git] / src / entropy.cpp
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, 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
57 using namespace UnixPlusPlus;
58
59
60 //
61 // During construction, we perform initial entropy file recovery.
62 //
63 EntropyManager::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 //
83 void EntropyManager::action()
84 {
85 collectEntropy();
86 updateEntropyFile();
87
88 server.setTimer(this, Time::Interval(COLLECT_INTERVAL)); // drifting reschedule (desired)
89 }
90
91
92 static 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
95 static const int kExpectedLoops = 10;
96
97 // Calculate the amount of entropy in the buffer (per Shannon's Entropy Calculation)
98 static 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
142 void 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 size_t size = sizeof(mach_timespec_t) * timingsToCollect;
157
158 double bytesRemaining = kBytesOfEntropyToCollect;
159
160 int loopCount = 0;
161
162 while (bytesRemaining >= 0)
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 }
168 else if (size == 0)
169 {
170 Syslog::alert("entropy measurement returned no entropy.");
171 }
172
173 // remove the non-entropic pieces from the buffer
174 u_int16_t nonEnt[timingsToCollect];
175
176 // treat the received buffer as an array of u_int16 and only take the first two bytes of each
177 u_int16_t *rawEnt = (u_int16_t*) buffer;
178
179 int i;
180 for (i = 0; i < timingsToCollect; ++i)
181 {
182 nonEnt[i] = *rawEnt;
183 rawEnt += 4;
184 }
185
186 SECURITYD_ENTROPY_SEED((void *)nonEnt, (unsigned int) sizeof(nonEnt));
187 addEntropy(nonEnt, sizeof(nonEnt));
188
189 double entropyRead = CalculateEntropy(nonEnt, sizeof(nonEnt));
190 bytesRemaining -= entropyRead;
191
192 loopCount += 1;
193 }
194
195 if (loopCount > kExpectedLoops)
196 {
197 Syslog::alert("Entropy collection fulfillment took %d loops", loopCount);
198 }
199 }
200
201
202 //
203 // (Re)write the entropy file with random data pulled from the RNG
204 //
205 void EntropyManager::updateEntropyFile()
206 {
207 if (Time::now() >= mNextUpdate) {
208 try {
209 SECURITYD_ENTROPY_SAVE((char *)mEntropyFilePath.c_str());
210 mNextUpdate = Time::now() + Time::Interval(updateInterval);
211 secdebug("entropy", "updating %s", mEntropyFilePath.c_str());
212 char buffer[entropyFileSize];
213 random(buffer, entropyFileSize);
214 AutoFileDesc entropyFile(mEntropyFilePath.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0600);
215 if (entropyFile.write(buffer) != entropyFileSize)
216 Syslog::warning("short write on entropy file %s", mEntropyFilePath.c_str());
217 } catch (...) {
218 Syslog::warning("error writing entropy file %s", mEntropyFilePath.c_str());
219 }
220 }
221 }
222