%% options copyright owner = Dirk Krause copyright year = 2015-xxxx license = bsd %% header /** @file dk4rnd.h Random number generation. NOT A GOOD IDEA: WE EITHER NEED CRYPTOGRAPHICALLY STRONG RANDOM DATA OR LIGHTWEIGHT RANDOM DATA FOR GAMES OR SIMULATIONS. ONE SIZE DOESN'T FIT ALL! */ #ifdef __cplusplus extern "C" { #endif /** Retrieve PRNG type. @return PRNG type. */ int dk4rnd_get_prng_type(void); /** Initialize (seed) PRNG. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_NOT_FOUND
if the function failed to obtain seed data. */ int dk4rnd_init(dk4_er_t *erp); /** Retrieve random bytes. @param bptr Pointer to destination buffer. @param bs Destination buffer size. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if bptr is NULL or bs is either 0 or too large, - DK4_E_SYNTAX
if the PRNG was not yet initialized successfully, - DK4_E_NOT_FOUND
if the function failed to obtain random data. */ int dk4rnd_get_data(void *bptr, size_t bs, dk4_er_t *erp); /** Clean up PRNG. */ void dk4rnd_cleanup(void); #ifdef __cplusplus } #endif /** Random number generators. */ enum { /** Use rand() function. */ DK4_PRNG_SIMPLE = 1, /** Use nrand48() function. */ DK4_PRNG_RAND48 = 2, /** Use initstate()/setstate() functions. */ DK4_PRNG_STATE = 4, /** Use OpenSSL PRNG. */ DK4_PRNG_OPENSSL = 8, /** All the PRNGs above. */ DK4_PRNG_ALL = ( DK4_PRNG_SIMPLE | DK4_PRNG_RAND48 | DK4_PRNG_STATE | DK4_PRNG_OPENSSL ) }; %% module #include "dk4conf.h" #include #if DK4_HAVE_STDLIB_H #ifndef STDLIB_H_INCLUDED #include #endif #endif #if DK4_HAVE_SYS_TYPES_H #ifndef SYS_TYPES_H_INCLUDED #include #define SYS_TYPES_H_INCLUDED 1 #endif #endif #if DK4_HAVE_OPENSSL_RAND_H #ifndef OPENSSL_RAND_H_INCLUDED #include #define OPENSSL_RAND_H_INCLUDED 1 #endif #endif #include "dk4rnd.h" $!trace-include static const int dk4rnd_used_type = #if DK4_HAVE_OPENSSL_RAND_H DK4_PRNG_OPENSSL #else #if DK4_HAVE_RANDOM DK4_PRNG_STATE #else #if DK4_HAVE_LRAND48 DK4_PRNG_RAND48 #else #if DK4_HAVE_RAND DK4_PRNG_SIMPLE #else 0 #endif #endif #endif #endif ; /** Static keywords used by the module, not localized. */ static const char * const dk4rnd_kwnl[] = { $!string-table # # 0 Environment variable name, pointing to EGD socket # EGDSOCKET # # 1 The /dev/random device # /dev/random # # 2 The /dev/urandom device # /dev/urandom $!end }; /** Flag: Already seeded. */ static int dk4rnd_seeded = 0; #if !DK4_HAVE_OPENSSL_RAND_H #if DK4_HAVE_RANDOM || DK4_HAVE_LRAND48 || DK4_HAVE_RAND /** Attempt to obtain seed data. @param bptr Buffer pointer. @param sz Buffer size. @return 1 on success, 0 on error. */ static int dk4rnd_find_seed_data(void *bptr, size_t sz) { #if DK4_ON_WINDOWS HCRYPTPROV hcp; /* Crypto provider */ BOOL res; /* Operation result */ #else #if DK4_HAVE_SOCKET struct sockaddr_un soun; /* Unix domain socket addr */ socklen_t szs; /* Socket length */ ssize_t sobytes; /* Bytes obtained */ int udsock; /* Unix domain socket */ #endif char *egdsn = NULL; #endif FILE *fipo; size_t rdb; int back = 0; /* EGD socket */ #if !DK4_ON_WINDOWS egdsn = getenv(dk4rnd_kwnl[0]); if (NULL != egdsn) { if (108 > strlen(egdsn)) { udsock = socket(AF_UNIX, SOCK_STREAM, 0); if (-1 < udsock) { soun.sun_family = AF_UNIX; strcpy(soun.sun_path, egdsn); szs = sizeof(struct sockaddr_un) - sizeof(soun.sun_path) + strlen(soun.sun_path); if (0 == connect(udsock, (const struct sockaddr *)(&soun), szs)) { sobytes = recv(udsock, bptr, sz, 0); if (0 < sobytes) { if ((size_t)sobytes == sz) { back = 1; } } else { $? "! recv" } } else { $? "! connect" } close(udsock); } else { $? "! socket" } } else { $? "! name too long" } } else { $? "! not env var" } if (1 == back) { goto finished; } #endif /* System mechanisms. */ #if DK4_ON_WINDOWS res = CryptAcquireContext( &hcp, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ); if (res) { res = CryptGenRandom(hcp, (DWORD)sz, bptr); if (res) { back = 1; } CryptReleaseContext(hcp, (DWORD)0L); } #else fipo = fopen(dk4rnd_kwnl[1], "rb"); if (NULL != fipo) { rdb = fread(bptr, 1, sz, fipo); fclose(fipo); if ((0 < rdb) && (sz == rdb)) { back = 1; } } else { fipo = fopen(dk4rnd_kwnl[2], "rb"); if (NULL != fipo) { rdb = fread(bptr, 1, sz, fipo); fclose(fipo); if ((0 < rdb) && (sz == rdb)) { back = 1; } } } #endif finished: return back; } #endif #endif #if DK4_HAVE_OPENSSL_RAND_H /* +++++ OpenSSL available */ int dk4rnd_init(dk4_er_t *erp) { char sbuf[256]; /* Buffer for seed data */ #if DK4_ON_WINDOWS HCRYPTPROV hcp; #endif FILE *fipo = NULL; /* Input file */ size_t rdb = 0; /* Number of bytes read */ char *egdsn = NULL; /* EGD socket name */ int back = 0; #if DK4_ON_WINDOWS BOOL res = FALSE; #endif /* Let OpenSSL initialize itself. */ if (1 == RAND_status()) { back = 1; goto finished; } /* Attempt EGD socket. */ #if DK4_HAVE_RAND_EGD && DK4_HAVE_STRUCT_SOCKADDR_UN egdsn = getenv(dk4rnd_kwnl[0]); if (NULL != egdsn) { RAND_egd(egdsn); if (1 == RAND_status()) { back = 1; goto finished; } } #endif /* Attempt native mechanisms. */ #if DK4_ON_WINDOWS res = CryptAcquireContext( &hcp, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ); if (res) { res = CryptGenRandom(hcp, (DWORD)sizeof(sbuf), sbuf); if (res) { RAND_seed(sbuf, sizeof(sbuf)); if (1 == RAND_status()) { back = 1; } } CryptReleaseContext(hcp, (DWORD)0L); } if (1 == back) { goto finished; } #else fipo = fopen(dk4rnd_kwnl[1], "rb"); if (NULL != fipo) { rdb = fread(sbuf, 1, sizeof(sbuf), fipo); fclose(fipo); if (rdb > 0) { RAND_seed(sbuf, rdb); if (1 == RAND_status()) { back = 1; goto finished; } } } else { fipo = fopen(dk4rnd_kwnl[2], "rb"); if (NULL != fipo) { rdb = fread(sbuf, 1, sizeof(sbuf), fipo); fclose(fipo); if (rdb > 0) { RAND_seed(sbuf, rdb); if (1 == RAND_status()) { back = 1; goto finished; } } } } #endif dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND); finished: if (1 == back) { dk4rnd_seeded = 1; } return back; } int dk4rnd_get_data(void *bptr, size_t bs, dk4_er_t *erp) { int back = 0; if ((NULL != bptr) && (0 < bs)) { if ((dk4_um_t)bs <= (dk4_um_t)INT_MAX) { if (0 != dk4rnd_seeded) { back = RAND_bytes(bptr, (int)bs); if (1 > back) { dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND); } } else { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } return back; } void dk4rnd_cleanup(void) { RAND_cleanup(); dk4rnd_seeded = 0; } /* ----- OpenSSL available */ #else #if DK4_HAVE_RANDOM /* +++++ initstate(), setstate() and random() available */ /** State array. */ static char dk4rnd_random_state[256]; /** The initial state array. */ static char *dk4rnd_old_state = NULL; int dk4rnd_init(dk4_er_t *erp) { unsigned int sv; /* Seed value */ int back = 0; if (0 != dk4rnd_find_seed_data(&sv, sizeof(sv))) { DK4_MEMRES(dk4rnd_random_state, sizeof(dk4rnd_random_state)); if (NULL == dk4rnd_old_state) { dk4rnd_old_state = initstate(sv, dk4rnd_random_state, 256); } else { (void)initstate(sv, dk4rnd_random_state, 256); } back = 1; } if (1 == back) { dk4rnd_seeded = 1; } return back; } int dk4rnd_get_data(void *bptr, size_t bs, dk4_er_t *erp) { int back = 0; if ((NULL != bptr) && (0 < bs)) { if (0 != dk4rnd_seeded) { } else { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } return back; } void dk4rnd_cleanup(void) { } /* ----- initstate(), setstate() and random() available */ #else #if DK4_HAVE_LRAND48 /* +++++ lrand48(), nrand48() and seed48() available */ int dk4rnd_init(dk4_er_t *erp) { int back = 0; finished: if (1 == back) { dk4rnd_seeded = 1; } return back; } int dk4rnd_get_data(void *bptr, size_t bs, dk4_er_t *erp) { int back = 0; if ((NULL != bptr) && (0 < bs)) { if (0 != dk4rnd_seeded) { } else { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } return back; } void dk4rnd_cleanup(void) { } /* ----- lrand48(), nrand48() and seed48() available */ #else #if DK4_HAVE_RAND /* +++++ rand() and srand() available */ int dk4rnd_init(dk4_er_t *erp) { int back = 0; finished: if (1 == back) { dk4rnd_seeded = 1; } return back; } int dk4rnd_get_data(void *bptr, size_t bs, dk4_er_t *erp) { int back = 0; if ((NULL != bptr) && (0 < bs)) { if (0 != dk4rnd_seeded) { } else { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } return back; } void dk4rnd_cleanup(void) { } /* ----- rand() and srand() available */ #else /* +++++ no PRNG available */ int dk4rnd_init(dk4_er_t *erp) { int back = 0; return back; } int dk4rnd_get_data(void *bptr, size_t bs, dk4_er_t *erp) { int back = 0; if ((NULL != bptr) && (0 < bs)) { } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } return back; } void dk4rnd_cleanup(void) { } /* ----- no PRNG available */ #endif #endif #endif #endif int dk4rnd_get_prng_type(void) { return dk4rnd_used_type; }