]>
git.saurik.com Git - apple/securityd.git/blob - src/entropy.cpp
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // EntropyManager - manage entropy on the system.
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.
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.
40 #include <sys/sysctl.h>
41 #include <mach/clock_types.h>
42 #include <mach/mach_time.h>
44 #include <security_utilities/logging.h>
45 #include <sys/sysctl.h>
46 #include <security_utilities/debugging.h>
49 /* when true, action() called every 15 seconds */
50 #define ENTROPY_QUICK_UPDATE 0
51 #if ENTROPY_QUICK_UPDATE
52 #define COLLECT_INTERVAL 15
54 #define COLLECT_INTERVAL collectInterval
55 #endif //ENTROPY_QUICK_UPDATE
57 using namespace UnixPlusPlus
;
61 // During construction, we perform initial entropy file recovery.
63 EntropyManager::EntropyManager(MachPlusPlus::MachServer
&srv
, const char *entropyFile
)
64 : DevRandomGenerator(true), server(srv
),
65 mEntropyFilePath(entropyFile
), mNextUpdate(Time::now())
67 // Read the entropy file and seed the RNG. It is not an error if we can't find one.
69 AutoFileDesc
oldEntropyFile(entropyFile
, O_RDONLY
);
70 char buffer
[entropyFileSize
];
71 if (size_t size
= oldEntropyFile
.read(buffer
))
72 addEntropy(buffer
, size
);
75 // go through a collect/update/reschedule cycle immediately
83 void EntropyManager::action()
88 server
.setTimer(this, Time::Interval(COLLECT_INTERVAL
)); // drifting reschedule (desired)
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...
95 static const int kExpectedLoops
= 10;
97 // Calculate the amount of entropy in the buffer (per Shannon's Entropy Calculation)
98 static double CalculateEntropy(const void* buffer
, size_t bufferSize
)
100 double sizef
= bufferSize
;
101 const u_int8_t
* charBuffer
= (const u_int8_t
*) buffer
;
103 // zero the tabulation array
105 memset(counts
, 0, sizeof(counts
));
107 // tabulate the occurances of each byte in the array
109 for (i
= 0; i
< bufferSize
; ++i
)
111 counts
[charBuffer
[i
]] += 1;
114 // calculate the number of bits/byte of entropy
115 double entropy
= 0.0;
117 for (i
= 0; i
< 256; ++i
)
121 double p
= ((double) counts
[i
]) / sizef
;
122 double term
= p
* -log2(p
);
127 double entropicBytes
= bufferSize
* entropy
/ 8.0;
129 return entropicBytes
;
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.
142 void EntropyManager::collectEntropy()
144 SECURITYD_ENTROPY_COLLECT();
148 mib
[1] = KERN_KDEBUG
;
149 mib
[2] = KERN_KDGETENTROPY
;
150 mib
[3] = 1; // milliseconds maximum delay
152 mach_timespec_t buffer
[timingsToCollect
];
156 double bytesRemaining
= kBytesOfEntropyToCollect
;
160 while (bytesRemaining
>= 0)
162 size_t size
= sizeof(mach_timespec_t
) * timingsToCollect
;
164 result
= sysctl(mib
,4, buffer
, &size
, NULL
, 0);
166 Syslog::alert("entropy measurement returned no entropy (errno=%d)", errno
);
171 Syslog::alert("entropy measurement returned no entropy.");
175 // remove the non-entropic pieces from the buffer
176 u_int16_t nonEnt
[timingsToCollect
];
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
;
182 for (i
= 0; i
< timingsToCollect
; ++i
)
188 SECURITYD_ENTROPY_SEED((void *)nonEnt
, (unsigned int) sizeof(nonEnt
));
189 addEntropy(nonEnt
, sizeof(nonEnt
));
191 double entropyRead
= CalculateEntropy(nonEnt
, sizeof(nonEnt
));
192 bytesRemaining
-= entropyRead
;
197 if (loopCount
> kExpectedLoops
)
199 Syslog::alert("Entropy collection fulfillment took %d loops", loopCount
);
205 // (Re)write the entropy file with random data pulled from the RNG
207 void EntropyManager::updateEntropyFile()
209 if (Time::now() >= mNextUpdate
) {
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());
220 Syslog::warning("error writing entropy file %s", mEntropyFilePath
.c_str());