#include "CryptoRandom.h"
#include "BAssert.h"
#include "BPlatform.h"
#include "Mutex.h"
#include "PerProcess.h"
#include "VMAllocate.h"
#include <mutex>
#if !BOS(DARWIN)
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#if BOS(DARWIN)
typedef struct __CCRandom* CCRandomRef;
extern "C" {
extern const CCRandomRef kCCRandomDefault;
int CCRandomCopyBytes(CCRandomRef rnd, void *bytes, size_t count);
}
#endif
namespace bmalloc {
class ARC4Stream {
public:
ARC4Stream();
uint8_t i;
uint8_t j;
uint8_t s[256];
};
class ARC4RandomNumberGenerator {
public:
ARC4RandomNumberGenerator(const std::lock_guard<Mutex>&);
uint32_t randomNumber();
void randomValues(void* buffer, size_t length);
private:
inline void addRandomData(unsigned char *data, int length);
void stir();
void stirIfNeeded();
inline uint8_t getByte();
ARC4Stream m_stream;
int m_count;
Mutex m_mutex;
};
ARC4Stream::ARC4Stream()
{
for (int n = 0; n < 256; n++)
s[n] = n;
i = 0;
j = 0;
}
ARC4RandomNumberGenerator::ARC4RandomNumberGenerator(const std::lock_guard<Mutex>&)
: m_count(0)
{
}
void ARC4RandomNumberGenerator::addRandomData(unsigned char* data, int length)
{
m_stream.i--;
for (int n = 0; n < 256; n++) {
m_stream.i++;
uint8_t si = m_stream.s[m_stream.i];
m_stream.j += si + data[n % length];
m_stream.s[m_stream.i] = m_stream.s[m_stream.j];
m_stream.s[m_stream.j] = si;
}
m_stream.j = m_stream.i;
}
void ARC4RandomNumberGenerator::stir()
{
unsigned char randomness[128];
size_t length = sizeof(randomness);
#if BOS(DARWIN)
RELEASE_BASSERT(!CCRandomCopyBytes(kCCRandomDefault, randomness, length));
#else
static std::once_flag onceFlag;
static int fd;
std::call_once(
onceFlag,
[] {
int ret = 0;
do {
ret = open("/dev/urandom", O_RDONLY, 0);
} while (ret == -1 && errno == EINTR);
RELEASE_BASSERT(ret >= 0);
fd = ret;
});
ssize_t amountRead = 0;
while (static_cast<size_t>(amountRead) < length) {
ssize_t currentRead = read(fd, randomness + amountRead, length - amountRead);
if (currentRead == -1)
RELEASE_BASSERT(errno == EAGAIN || errno == EINTR);
else
amountRead += currentRead;
}
#endif
addRandomData(randomness, length);
for (int i = 0; i < 256; i++)
getByte();
m_count = 1600000;
}
void ARC4RandomNumberGenerator::stirIfNeeded()
{
if (m_count <= 0)
stir();
}
uint8_t ARC4RandomNumberGenerator::getByte()
{
m_stream.i++;
uint8_t si = m_stream.s[m_stream.i];
m_stream.j += si;
uint8_t sj = m_stream.s[m_stream.j];
m_stream.s[m_stream.i] = sj;
m_stream.s[m_stream.j] = si;
return (m_stream.s[(si + sj) & 0xff]);
}
void ARC4RandomNumberGenerator::randomValues(void* buffer, size_t length)
{
std::lock_guard<Mutex> lock(m_mutex);
unsigned char* result = reinterpret_cast<unsigned char*>(buffer);
stirIfNeeded();
while (length--) {
m_count--;
stirIfNeeded();
result[length] = getByte();
}
}
void cryptoRandom(void* buffer, size_t length)
{
PerProcess<ARC4RandomNumberGenerator>::get()->randomValues(buffer, length);
}
}