mirror of https://github.com/D4-project/d4-core
				
				
				
			
		
			
				
	
	
		
			257 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			257 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
/*
 | 
						|
 * General purpose random utilities
 | 
						|
 *
 | 
						|
 * Based on libuuid code.
 | 
						|
 *
 | 
						|
 * This file may be redistributed under the terms of the
 | 
						|
 * GNU Lesser General Public License.
 | 
						|
 */
 | 
						|
#include <stdio.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <sys/time.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <sys/syscall.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
//#include "c.h"
 | 
						|
#define UL_BUILD_BUG_ON_ZERO(e) __extension__ (sizeof(struct { int:-!!(e); }))  
 | 
						|
 | 
						|
# define __must_be_array(a) \
 | 
						|
    UL_BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(__typeof__(a), __typeof__(&a[0])))
 | 
						|
 | 
						|
# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
 | 
						|
 | 
						|
#include "randutils.h"
 | 
						|
//#include "nls.h"
 | 
						|
 | 
						|
#ifdef HAVE_TLS
 | 
						|
#define THREAD_LOCAL static __thread
 | 
						|
#else
 | 
						|
#define THREAD_LOCAL static
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_GETRANDOM
 | 
						|
# include <sys/random.h>
 | 
						|
#elif defined (__linux__)
 | 
						|
# if !defined(SYS_getrandom) && defined(__NR_getrandom)
 | 
						|
   /* usable kernel-headers, but old glibc-headers */
 | 
						|
#  define SYS_getrandom __NR_getrandom
 | 
						|
# endif
 | 
						|
#endif
 | 
						|
 | 
						|
#if !defined(HAVE_GETRANDOM) && defined(SYS_getrandom)
 | 
						|
/* libc without function, but we have syscal */
 | 
						|
#define GRND_NONBLOCK 0x01
 | 
						|
#define GRND_RANDOM 0x02
 | 
						|
//static int getrandom(void *buf, size_t buflen, unsigned int flags)
 | 
						|
//{
 | 
						|
//	return (syscall(SYS_getrandom, buf, buflen, flags));
 | 
						|
//}
 | 
						|
# define HAVE_GETRANDOM
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
 | 
						|
#define DO_JRAND_MIX
 | 
						|
THREAD_LOCAL unsigned short ul_jrand_seed[3];
 | 
						|
#endif
 | 
						|
 | 
						|
int rand_get_number(int low_n, int high_n)
 | 
						|
{
 | 
						|
	return rand() % (high_n - low_n + 1) + low_n;
 | 
						|
}
 | 
						|
 | 
						|
static void crank_random(void)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	struct timeval tv;
 | 
						|
 | 
						|
	gettimeofday(&tv, NULL);
 | 
						|
	srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
 | 
						|
 | 
						|
#ifdef DO_JRAND_MIX
 | 
						|
	ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
 | 
						|
	ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
 | 
						|
	ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
 | 
						|
#endif
 | 
						|
	/* Crank the random number generator a few times */
 | 
						|
	gettimeofday(&tv, NULL);
 | 
						|
	for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
 | 
						|
		rand();
 | 
						|
}
 | 
						|
 | 
						|
int random_get_fd(void)
 | 
						|
{
 | 
						|
	int i, fd;
 | 
						|
 | 
						|
	fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
 | 
						|
	if (fd == -1)
 | 
						|
		fd = open("/dev/random", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
 | 
						|
	if (fd >= 0) {
 | 
						|
		i = fcntl(fd, F_GETFD);
 | 
						|
		if (i >= 0)
 | 
						|
			fcntl(fd, F_SETFD, i | FD_CLOEXEC);
 | 
						|
	}
 | 
						|
	crank_random();
 | 
						|
	return fd;
 | 
						|
}
 | 
						|
 | 
						|
int my_getentropy(void *buf, size_t buflen)
 | 
						|
{
 | 
						|
    #ifdef __GLIBC__
 | 
						|
    if (buflen > 256) {
 | 
						|
        errno = EIO;
 | 
						|
       return -1;
 | 
						|
   }
 | 
						|
   return syscall(SYS_getrandom, buf, buflen, 0);
 | 
						|
   #else
 | 
						|
   return getentropy(buf, buflen);
 | 
						|
   #endif
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Generate a stream of random nbytes into buf.
 | 
						|
 * Use /dev/urandom if possible, and if not,
 | 
						|
 * use glibc pseudo-random functions.
 | 
						|
 */
 | 
						|
#define UL_RAND_READ_ATTEMPTS	8
 | 
						|
#define UL_RAND_READ_DELAY	125000	/* microseconds */
 | 
						|
 | 
						|
void random_get_bytes(void *buf, size_t nbytes)
 | 
						|
{
 | 
						|
	unsigned char *cp = (unsigned char *)buf;
 | 
						|
	size_t i, n = nbytes;
 | 
						|
	int lose_counter = 0;
 | 
						|
 | 
						|
#ifdef HAVE_GETRANDOM
 | 
						|
	while (n > 0) {
 | 
						|
		int x;
 | 
						|
 | 
						|
		errno = 0;
 | 
						|
		x =  my_getentropy(cp, n);
 | 
						|
		if (x > 0) {			/* success */
 | 
						|
		       n -= x;
 | 
						|
		       cp += x;
 | 
						|
		       lose_counter = 0;
 | 
						|
 | 
						|
		} else if (errno == ENOSYS) {	/* kernel without getrandom() */
 | 
						|
			break;
 | 
						|
 | 
						|
		} else if (errno == EAGAIN && lose_counter < UL_RAND_READ_ATTEMPTS) {
 | 
						|
			usleep(UL_RAND_READ_DELAY);	/* no etropy, wait and try again */
 | 
						|
			lose_counter++;
 | 
						|
		} else
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	if (errno == ENOSYS)
 | 
						|
#endif
 | 
						|
	/*
 | 
						|
	 * We've been built against headers that support getrandom, but the
 | 
						|
	 * running kernel does not.  Fallback to reading from /dev/{u,}random
 | 
						|
	 * as before
 | 
						|
	 */
 | 
						|
	{
 | 
						|
		int fd = random_get_fd();
 | 
						|
 | 
						|
		lose_counter = 0;
 | 
						|
		if (fd >= 0) {
 | 
						|
			while (n > 0) {
 | 
						|
				ssize_t x = read(fd, cp, n);
 | 
						|
				if (x <= 0) {
 | 
						|
					if (lose_counter++ > UL_RAND_READ_ATTEMPTS)
 | 
						|
						break;
 | 
						|
					usleep(UL_RAND_READ_DELAY);
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
				n -= x;
 | 
						|
				cp += x;
 | 
						|
				lose_counter = 0;
 | 
						|
			}
 | 
						|
 | 
						|
			close(fd);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * We do this all the time, but this is the only source of
 | 
						|
	 * randomness if /dev/random/urandom is out to lunch.
 | 
						|
	 */
 | 
						|
	crank_random();
 | 
						|
	for (cp = buf, i = 0; i < nbytes; i++)
 | 
						|
		*cp++ ^= (rand() >> 7) & 0xFF;
 | 
						|
 | 
						|
#ifdef DO_JRAND_MIX
 | 
						|
	{
 | 
						|
		unsigned short tmp_seed[3];
 | 
						|
 | 
						|
		memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
 | 
						|
		ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
 | 
						|
		for (cp = buf, i = 0; i < nbytes; i++)
 | 
						|
			*cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
 | 
						|
		memcpy(ul_jrand_seed, tmp_seed,
 | 
						|
		       sizeof(ul_jrand_seed)-sizeof(unsigned short));
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	return;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Tell source of randomness.
 | 
						|
 */
 | 
						|
const char *random_tell_source(void)
 | 
						|
{
 | 
						|
//#ifdef HAVE_GETRANDOM
 | 
						|
//	return _("getrandom() function");
 | 
						|
//#else
 | 
						|
	size_t i;
 | 
						|
	static const char *random_sources[] = {
 | 
						|
		"/dev/urandom",
 | 
						|
		"/dev/random"
 | 
						|
	};
 | 
						|
 | 
						|
	for (i = 0; i < ARRAY_SIZE(random_sources); i++) {
 | 
						|
		if (!access(random_sources[i], R_OK))
 | 
						|
			return random_sources[i];
 | 
						|
	}
 | 
						|
    //Non standard if no random sources are available abort
 | 
						|
    abort();
 | 
						|
}
 | 
						|
 | 
						|
#ifdef TEST_PROGRAM_RANDUTILS
 | 
						|
#include <inttypes.h>
 | 
						|
 | 
						|
int main(int argc, char *argv[])
 | 
						|
{
 | 
						|
	size_t i, n;
 | 
						|
	int64_t *vp, v;
 | 
						|
	char *buf;
 | 
						|
	size_t bufsz;
 | 
						|
 | 
						|
	n = argc == 1 ? 16 : atoi(argv[1]);
 | 
						|
 | 
						|
	printf("Multiple random calls:\n");
 | 
						|
	for (i = 0; i < n; i++) {
 | 
						|
		random_get_bytes(&v, sizeof(v));
 | 
						|
		printf("#%02zu: %25"PRIu64"\n", i, v);
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	printf("One random call:\n");
 | 
						|
	bufsz = n * sizeof(*vp);
 | 
						|
	buf = malloc(bufsz);
 | 
						|
	if (!buf)
 | 
						|
		err(EXIT_FAILURE, "failed to allocate buffer");
 | 
						|
 | 
						|
	random_get_bytes(buf, bufsz);
 | 
						|
	for (i = 0; i < n; i++) {
 | 
						|
		vp = (int64_t *) (buf + (i * sizeof(*vp)));
 | 
						|
		printf("#%02zu: %25"PRIu64"\n", i, *vp);
 | 
						|
	}
 | 
						|
 | 
						|
	return EXIT_SUCCESS;
 | 
						|
}
 | 
						|
#endif /* TEST_PROGRAM_RANDUTILS */
 |