]> git.saurik.com Git - apple/securityd.git/blobdiff - src/entropy.cpp
securityd-55199.3.tar.gz
[apple/securityd.git] / src / entropy.cpp
index 6cb47b2865f08e54aea0e8fac6a4a38525cc2d91..c936d8f5bea3b438247d8a4ac45ae6a43676a8be 100644 (file)
 // file that some fool administrator removed yesterday.
 //
 #include "entropy.h"
+#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
@@ -86,6 +89,48 @@ void EntropyManager::action()
 }
 
 
+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.
@@ -93,29 +138,66 @@ void EntropyManager::action()
 // 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);
-    int ret = sysctl(mib, 4, timings, &size, NULL, 0);
-    if (ret == -1) {
-        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);
     }
-    char buffer[timingsToCollect];
-    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]);
-    addEntropy(buffer, size);
 }
 
 
@@ -126,6 +208,7 @@ void EntropyManager::updateEntropyFile()
 {
     if (Time::now() >= mNextUpdate) {
         try {
+                       SECURITYD_ENTROPY_SAVE((char *)mEntropyFilePath.c_str());
                        mNextUpdate = Time::now() + Time::Interval(updateInterval);
             secdebug("entropy", "updating %s", mEntropyFilePath.c_str());
                char buffer[entropyFileSize];