view librandom.c @ 0:9ee92e7a1cb5 default tip

*: initial commit this is a very simple, bare-bones randomness API. if the system can't provide it, so be it -- init will fail.
author Paper <paper@tflc.us>
date Tue, 10 Jun 2025 16:18:50 -0400
parents
children
line wrap: on
line source

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include "librandom.h"

#ifdef _WIN32
# define RANDOM_WIN32
#elif defined(__MACH__) && defined(__APPLE__)
# define RANDOM_ARC4RANDOM
#else
# define RANDOM_UNIX
#endif

#ifdef RANDOM_WIN32
# include <windows.h>
# include <wincrypt.h>
#endif

#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*(x)))

/* NOTE: I am not sure if other platforms provide real APIs for good
 * randomness. Macintosh provides SetRandomSeed and RandomBits, but I
 * am skeptical of its use as a good random number generator. */

#ifdef RANDOM_UNIX

struct random_unix {
	FILE *f;
};

static void random_unix_close(struct random *r)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
{
	struct random_unix *ru = r->userdata;

	if (ru->f)
		fclose(ru->f);

	free(ru);
}

static size_t random_unix_read(struct random *r, void *ptr, size_t s)
{
	struct random_unix *ru = r->userdata;

	return fread(ptr, s, 1, ru->f);
}

static int random_unix_init(struct random *r)
{
	/* hmmm. */
	static const char *names[] = {
		"/dev/random",
		"/dev/urandom",
	};
	struct random_unix *ru = malloc(sizeof(*ru));
	size_t i;

	for (i = 0; i < ARRAY_SIZE(names); i++) {
		ru->f = fopen(names[i], "rb");
		if (ru->f)
			goto gotdevice;
	}

	free(ru);
	return -1;

gotdevice:

	r->userdata = ru;
	r->close = random_unix_close;
	r->read = random_unix_read;

	return 0;
}

#endif

/* ------------------------------------------------------------------------ */

#ifdef RANDOM_ARC4RANDOM

static size_t random_arc4random_read(struct random *r, void *ptr, size_t s)
{
	size_t l = s;

	while (l > 0) {
		size_t amt;
		uint32_t x;

		amt = MIN(sizeof(x), l);
		x = arc4random();

		memcpy(ptr, &x, amt);

		ptr += amt;
		l -= amt;
	}

	return s;
}

static void random_arc4random_close(struct random *r)
{
	/* nothing */
}

static int random_arc4random_init(struct random *r)
{
	/* always successful */
	r->read = random_arc4random_read;
	r->close = random_arc4random_close;
	return 0;
}

#endif

/* ------------------------------------------------------------------------ */

#ifdef RANDOM_WIN32

typedef BOOL (*pADVAPI32_CryptReleaseContext)(HCRYPTPROV hProv, DWORD dwFlags);
typedef BOOL (*pADVAPI32_CryptGenRandom)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer);
typedef BOOL (*pADVAPI32_CryptAcquireContextA)(HCRYPTPROV *phProv, LPCSTR szContainer, LPCSTR szProvider, DWORD dwProvType,DWORD dwFlags);

struct random_win32 {
	HCRYPTPROV c;

	HMODULE advapi32;
	pADVAPI32_CryptReleaseContext ADVAPI32_CryptReleaseContext;
	pADVAPI32_CryptGenRandom ADVAPI32_CryptGenRandom;
	pADVAPI32_CryptAcquireContextA ADVAPI32_CryptAcquireContextA;
};

static void random_win32_close(struct random *r)
{
	struct random_win32 *rw = r->userdata;

	rw->ADVAPI32_CryptReleaseContext(rw->c, 0);
	FreeLibrary(rw->advapi32);

	free(rw);
}

static size_t random_win32_read(struct random *r, void *ptr, size_t s)
{
	struct random_win32 *rw = r->userdata;

	if (!rw->ADVAPI32_CryptGenRandom(rw->c, s, ptr))
		return 0;

	return s;
}

static int random_win32_init(struct random *r)
{
	HCRYPTPROV c;
	struct random_win32 *rw;

	rw = malloc(sizeof(*rw));
	if (!rw)
		return -2;

	rw->advapi32 = LoadLibraryA("ADVAPI32.DLL");
	if (!rw->advapi32)
		return -3;

#define LOADFUNC(x) \
	rw->ADVAPI32_##x = (pADVAPI32_##x)GetProcAddress(rw->advapi32, #x); \
	if (!rw->ADVAPI32_##x) \
		return -4

	LOADFUNC(CryptAcquireContextA);
	LOADFUNC(CryptGenRandom);
	LOADFUNC(CryptReleaseContext);

	if (!rw->ADVAPI32_CryptAcquireContextA(&c, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
		return -1;


	rw->c = c;

	r->userdata = rw;
	r->close = random_win32_close;
	r->read = random_win32_read;
	return 0;
}

#endif

/* ------------------------------------------------------------------------ */
/* platform-independent routines */

int random_init(struct random *r)
{
	int i;

	int (*funcs[])(struct random *r) = {
#ifdef RANDOM_ARC4RANDOM
		random_arc4random_init,
#endif
#ifdef RANDOM_UNIX
		random_unix_init,
#endif
#ifdef RANDOM_WIN32
		random_win32_init,
#endif
		NULL,
	};

	for (i = 0; funcs[i]; i++)
		if (!funcs[i](r))
			return 0;

	return -1;
}

void random_close(struct random *r)
{
	r->close(r);
}

size_t random_read(struct random *r, void *ptr, size_t s)
{
	return r->read(r, ptr, s);
}