+++ /dev/null
-/*
- * Copyright (c) 2000-2004,2007-2008,2010,2012-2013 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-//
-// EntropyManager - manage entropy on the system.
-//
-// Here is our mission:
-// (1) On startup, read the entropy file and seed it into the RNG for initial use
-// (2) Periodically, collect entropy from the system and seed it into the RNG
-// (3) Once in a while, take entropy from the RNG and write it to the entropy file
-// for use across reboots.
-//
-// This class will fail to operate if the process has (and retains) root privileges.
-// We re-open the entropy file on each use so that we don't work with a "phantom"
-// 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
-#if ENTROPY_QUICK_UPDATE
-#define COLLECT_INTERVAL 15
-#else
-#define COLLECT_INTERVAL collectInterval
-#endif //ENTROPY_QUICK_UPDATE
-
-using namespace UnixPlusPlus;
-
-
-//
-// During construction, we perform initial entropy file recovery.
-//
-EntropyManager::EntropyManager(MachPlusPlus::MachServer &srv, const char *entropyFile)
- : DevRandomGenerator(true), server(srv),
- mEntropyFilePath(entropyFile), mNextUpdate(Time::now())
-{
- // Read the entropy file and seed the RNG. It is not an error if we can't find one.
- try {
- AutoFileDesc oldEntropyFile(entropyFile, O_RDONLY);
- char buffer[entropyFileSize];
- if (size_t size = oldEntropyFile.read(buffer))
- addEntropy(buffer, size);
- } catch (...) { }
-
- // go through a collect/update/reschedule cycle immediately
- action();
-}
-
-
-//
-// Timer action
-//
-void EntropyManager::action()
-{
- collectEntropy();
- updateEntropyFile();
-
- server.setTimer(this, Time::Interval(COLLECT_INTERVAL)); // drifting reschedule (desired)
-}
-
-
-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.
-// We currently use a 1ms timeout, which almost always fills the buffer and
-// 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()
-{
- secinfo("SS", "Collecting entropy");
-
- int mib[4];
- mib[0] = CTL_KERN;
- mib[1] = KERN_KDEBUG;
- mib[2] = KERN_KDGETENTROPY;
- mib[3] = 1; // milliseconds maximum delay
-
- 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;
- }
-
- 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);
- }
-}
-
-
-//
-// (Re)write the entropy file with random data pulled from the RNG
-//
-void EntropyManager::updateEntropyFile()
-{
- if (Time::now() >= mNextUpdate) {
- try {
- mNextUpdate = Time::now() + Time::Interval(updateInterval);
- secinfo("entropy", "updating %s", mEntropyFilePath.c_str());
- char buffer[entropyFileSize];
- random(buffer, entropyFileSize);
- AutoFileDesc entropyFile(mEntropyFilePath.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0600);
- if (entropyFile.write(buffer) != entropyFileSize)
- Syslog::warning("short write on entropy file %s", mEntropyFilePath.c_str());
- } catch (...) {
- Syslog::warning("error writing entropy file %s", mEntropyFilePath.c_str());
- }
- }
-}
-