Mercurial > librandom
changeset 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 | |
files | .hgignore librandom.c librandom.h main.c |
diffstat | 3 files changed, 270 insertions(+), 0 deletions(-) [+] |
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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librandom.h Tue Jun 10 16:18:50 2025 -0400 @@ -0,0 +1,15 @@ +#ifndef LIBRANDOM_H_ +#define LIBRANDOM_H_ + +struct random { + void (*close)(struct random *r); + size_t (*read)(struct random *r, void *ptr, size_t s); + + void *userdata; +}; + +int random_init(struct random *r); +void random_close(struct random *r); +size_t random_read(struct random *r, void *ptr, size_t s); + +#endif /* LIBRANDOM_H_ */ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.c Tue Jun 10 16:18:50 2025 -0400 @@ -0,0 +1,29 @@ +#include <stdio.h> +#include <stdint.h> +#include <inttypes.h> +#include <time.h> + +#include "librandom.h" + +int main(void) +{ + clock_t start, end; + int i; + uint32_t x; + struct random r; + + random_init(&r); + + start = clock(); + + for (i = 0; i < 1000000; i++) + random_read(&r, &x, sizeof(x)); + + end = clock(); + + printf("%f\n", (double)(end - start) / CLOCKS_PER_SEC); + + random_close(&r); + + return 0; +}