Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
124 lines (107 sloc) 3.55 KB
/*
* PCG Random Number Generation for C.
*
* Copyright 2014 Melissa O'Neill <oneill@pcg-random.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For additional information about the PCG random number generation scheme,
* including its license and other licensing options, visit
*
* http://www.pcg-random.org
*/
/* This code provides a mechanism for getting external randomness for
* seeding purposes. Usually, it's just a wrapper around reading
* /dev/random.
*
* Alas, because not every system provides /dev/random, we need a fallback.
* We also need to try to test whether or not to use the fallback.
*/
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include "pcg_variants.h"
#include "pcg_spinlock.h"
#include "entropy.h"
#ifndef IS_UNIX
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) \
|| (defined(__APPLE__) && defined(__MACH__)))
#define IS_UNIX 1
#else
#define IS_UNIX 0
#endif
#endif
// If HAVE_DEV_RANDOM is set, we use that value, otherwise we guess
#ifndef HAVE_DEV_RANDOM
#define HAVE_DEV_RANDOM IS_UNIX
#endif
#if HAVE_DEV_RANDOM
#include <fcntl.h>
#include <unistd.h>
#endif
#if HAVE_DEV_RANDOM
/* entropy_getbytes(dest, size):
* Use /dev/random to get some external entropy for seeding purposes.
*
* Note:
* If reading /dev/random fails (which ought to never happen), it returns
* false, otherwise it returns true. If it fails, you could instead call
* fallback_entropy_getbytes which always succeeds.
*/
bool entropy_getbytes(void* dest, size_t size)
{
int fd = open("/dev/random", O_RDONLY);
if (fd < 0)
return false;
int sz = read(fd, dest, size);
if (sz < size)
return false;
return close(fd) == 0;
}
#else
bool entropy_getbytes(void* dest, size_t size)
{
fallback_entropy_getbytes(dest, size);
return true;
}
#endif
/* fallback_entropy_getbytes(dest, size):
* Works like the /dev/random version above, but avoids using /dev/random.
* Instead, it uses a private RNG (so that repeated calls will return
* different seeds). Makes no attempt at cryptographic security.
*/
void fallback_entropy_getbytes(void* dest, size_t size)
{
// Most modern OSs use address-space randomization, meaning that we can
// use the address of stack variables and system library code as
// initializers. It's not as good as using /dev/random, but probably
// better than using the current time alone.
static PCG_SPINLOCK_DECLARE(mutex);
PCG_SPINLOCK_LOCK(mutex);
static int intitialized = 0;
static pcg32_random_t entropy_rng;
if (!intitialized) {
int dummyvar;
pcg32_srandom_r(&entropy_rng,
time(NULL) ^ (intptr_t)&fallback_entropy_getbytes,
(intptr_t)&dummyvar);
intitialized = 1;
}
char* dest_cp = (char*) dest;
for (size_t i = 0; i < size; ++i) {
dest_cp[i] = (char) pcg32_random_r(&entropy_rng);
}
PCG_SPINLOCK_UNLOCK(mutex);
}
You can’t perform that action at this time.