#include "dtrace.h"
#include <sys/sysctl.h>
#include <mach/clock_types.h>
+#include <mach/mach_time.h>
#include <errno.h>
#include <security_utilities/logging.h>
#include <sys/sysctl.h>
#include <security_utilities/debugging.h>
+#include <math.h>
/* when true, action() called every 15 seconds */
#define ENTROPY_QUICK_UPDATE 0
}
+static const double kBytesOfEntropyToCollect = 240;
+// that gives us a minimum of 2.16 * 10^609 possible combinations. It's a finite number to be sure...
+
+static const int kExpectedLoops = 10;
+
+// Calculate the amount of entropy in the buffer (per Shannon's Entropy Calculation)
+static double CalculateEntropy(const void* buffer, size_t bufferSize)
+{
+ double sizef = bufferSize;
+ const u_int8_t* charBuffer = (const u_int8_t*) buffer;
+
+ // zero the tabulation array
+ int counts[256];
+ memset(counts, 0, sizeof(counts));
+
+ // tabulate the occurances of each byte in the array
+ size_t i;
+ for (i = 0; i < bufferSize; ++i)
+ {
+ counts[charBuffer[i]] += 1;
+ }
+
+ // calculate the number of bits/byte of entropy
+ double entropy = 0.0;
+
+ for (i = 0; i < 256; ++i)
+ {
+ if (counts[i] > 0)
+ {
+ double p = ((double) counts[i]) / sizef;
+ double term = p * -log2(p);
+ entropy += term;
+ }
+ }
+
+ double entropicBytes = bufferSize * entropy / 8.0;
+
+ return entropicBytes;
+}
+
+
+
//
// Collect system timings and seed into the RNG.
// Note that the sysctl will block until the buffer is full or the timeout expires.
// does not provide enough of a delay to worry about it. If we ever get worried,
// we could call longTermActivity on the server object to get another thread going.
//
+
void EntropyManager::collectEntropy()
{
SECURITYD_ENTROPY_COLLECT();
+
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_KDEBUG;
mib[2] = KERN_KDGETENTROPY;
mib[3] = 1; // milliseconds maximum delay
- mach_timespec_t timings[timingsToCollect];
- size_t size = sizeof(timings);
- if (sysctl(mib, 4, timings, &size, NULL, 0)) {
- Syslog::alert("entropy collection failed (errno=%d)", errno);
- return;
+
+ mach_timespec_t buffer[timingsToCollect];
+
+ int result;
+
+ double bytesRemaining = kBytesOfEntropyToCollect;
+
+ int loopCount = 0;
+
+ while (bytesRemaining >= 0)
+ {
+ size_t size = sizeof(mach_timespec_t) * timingsToCollect;
+
+ result = sysctl(mib,4, buffer, &size, NULL, 0);
+ if (result == -1) {
+ Syslog::alert("entropy measurement returned no entropy (errno=%d)", errno);
+ sleep(1);
+ }
+ else if (size == 0)
+ {
+ Syslog::alert("entropy measurement returned no entropy.");
+ sleep(1);
+ }
+
+ // remove the non-entropic pieces from the buffer
+ u_int16_t nonEnt[timingsToCollect];
+
+ // treat the received buffer as an array of u_int16 and only take the first two bytes of each
+ u_int16_t *rawEnt = (u_int16_t*) buffer;
+
+ int i;
+ for (i = 0; i < timingsToCollect; ++i)
+ {
+ nonEnt[i] = *rawEnt;
+ rawEnt += 4;
+ }
+
+ SECURITYD_ENTROPY_SEED((void *)nonEnt, (unsigned int) sizeof(nonEnt));
+ addEntropy(nonEnt, sizeof(nonEnt));
+
+ double entropyRead = CalculateEntropy(nonEnt, sizeof(nonEnt));
+ bytesRemaining -= entropyRead;
+
+ loopCount += 1;
+ }
+
+ if (loopCount > kExpectedLoops)
+ {
+ Syslog::alert("Entropy collection fulfillment took %d loops", loopCount);
}
- size /= sizeof(mach_timespec_t); // convert to element count
- if (size > timingsToCollect)
- size = timingsToCollect; // pure paranoia
- char buffer[timingsToCollect];
- size /= sizeof(mach_timespec_t); // convert to element count
- if (size > timingsToCollect)
- size = timingsToCollect; // pure paranoia
- for (unsigned n = 0; n < size; n++)
- buffer[n] = timings[n].tv_nsec; // truncating to LSB
- secdebug("entropy", "Entropy size %d: %02x %02x %02x %02x %02x %02x %02x %02x...",
- (int)size,
- (unsigned char)buffer[0], (unsigned char)buffer[1], (unsigned char)buffer[2],
- (unsigned char)buffer[3], (unsigned char)buffer[4], (unsigned char)buffer[5],
- (unsigned char)buffer[6], (unsigned char)buffer[7]);
- SECURITYD_ENTROPY_SEED((void *)buffer, size);
- addEntropy(buffer, size);
}