diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librandom.c	Tue Jun 10 16:18:50 2025 -0400
@@ -0,0 +1,226 @@
+#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);
+}