%% 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;
}