#include "entropy.h"
#include "dtrace.h"
#include <sys/sysctl.h>
#include <mach/clock_types.h>
#include <errno.h>
#include <security_utilities/logging.h>
#include <sys/sysctl.h>
#include <security_utilities/debugging.h>
#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;
EntropyManager::EntropyManager(MachPlusPlus::MachServer &srv, const char *entropyFile)
: DevRandomGenerator(true), server(srv),
mEntropyFilePath(entropyFile), mNextUpdate(Time::now())
{
try {
AutoFileDesc oldEntropyFile(entropyFile, O_RDONLY);
char buffer[entropyFileSize];
if (size_t size = oldEntropyFile.read(buffer))
addEntropy(buffer, size);
} catch (...) { }
action();
}
void EntropyManager::action()
{
collectEntropy();
updateEntropyFile();
server.setTimer(this, Time::Interval(COLLECT_INTERVAL)); }
void EntropyManager::collectEntropy()
{
SECURITYD_ENTROPY_COLLECT();
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_KDEBUG;
mib[2] = KERN_KDGETENTROPY;
mib[3] = 1; 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;
}
size /= sizeof(mach_timespec_t); if (size > timingsToCollect)
size = timingsToCollect; char buffer[timingsToCollect];
size /= sizeof(mach_timespec_t); if (size > timingsToCollect)
size = timingsToCollect; for (unsigned n = 0; n < size; n++)
buffer[n] = timings[n].tv_nsec; 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);
}
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];
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());
}
}
}