Commit 97b84a11 authored by Philipp Schafft's avatar Philipp Schafft 🦁 Committed by Philipp Schafft
Browse files

Feature: Added basic PRNG

parent cbc8b50d
......@@ -86,12 +86,16 @@ AC_CHECK_FUNCS_ONCE([clock_gettime gettimeofday ftime clock_nanosleep nanosleep
AC_CHECK_FUNCS_ONCE([sysconf])
AC_CHECK_FUNCS_ONCE([uname getuid getpid getppid])
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AC_TYPE_UINTPTR_T
ACX_PTHREAD([], [AC_MSG_ERROR([POSIX threads missing])])
AC_SEARCH_LIBS([log2], [m], [], [AC_MSG_ERROR([unable to find the log2() function])])
CFLAGS="${CFLAGS} ${PTHREAD_CFLAGS}"
CPPFLAGS="${CPPFLAGS} ${PTHREAD_CPPFLAGS}"
LIBS="${LIBS} ${PTHREAD_LIBS}"
......
......@@ -23,8 +23,27 @@
extern "C" {
#endif
#include <stdint.h>
#include <sys/types.h>
#include <igloo/types.h>
typedef uint64_t igloo_prng_flags_t;
/* Change options */
igloo_error_t igloo_prng_configure(igloo_ro_t instance, igloo_prng_flags_t addflags, igloo_prng_flags_t removeflags, ssize_t overcommitment);
/* Run a reseed now, useful to be run at times of idle */
igloo_error_t igloo_prng_auto_reseed(igloo_ro_t instance);
/* write data to the PRNG to seed it */
igloo_error_t igloo_prng_write(igloo_ro_t instance, const void *buffer, size_t len, ssize_t bits);
/* read pseudo random bytes from the PRNG */
ssize_t igloo_prng_read(igloo_ro_t instance, void *buffer, size_t len);
/* write len pseudo random bytes to a file. If len is -1 a default value is used. */
igloo_error_t igloo_prng_write_file(igloo_ro_t instance, const char *filename, ssize_t len);
/* read at max len bytes from the file and see the PRNG with it. if len is -1 all of the file is read. */
igloo_error_t igloo_prng_read_file(igloo_ro_t instance, const char *filename, ssize_t len, ssize_t bits);
#ifdef __cplusplus
}
......
......@@ -41,6 +41,7 @@ struct igloo_instance_tag {
igloo_ro_t logger;
igloo_instance_log_handler_t *logger_handler;
igloo_sp_state_t stringpool_state;
igloo_prng_state_t prng_state;
};
static igloo_error_t igloo_instance__new(igloo_ro_t self, const igloo_ro_type_t *type, va_list ap);
......@@ -70,6 +71,10 @@ igloo_error_t igloo_instance__new(igloo_ro_t self, const igloo_ro_type_t *type,
if (error != igloo_ERROR_NONE)
return error;
error = igloo_instance_prng_init(&(instance->prng_state));
if (error != igloo_ERROR_NONE)
return error;
return igloo_ERROR_NONE;
}
......@@ -79,6 +84,7 @@ static void igloo_instance__free(igloo_ro_t self)
igloo_ro_weak_unref(&(instance->logger));
igloo_instance_prng_destroy(&(instance->prng_state));
igloo_instance_sp_destroy(&(instance->stringpool_state));
}
......@@ -109,7 +115,7 @@ static igloo_error_t render_error(igloo_error_t error, char *buffer, size_t len)
static igloo_error_t igloo_instance__stringify(igloo_ro_t self, char **result, igloo_ro_sy_t flags)
{
#define STRINGIFY_FORMAT "%s, error=%s, stringpool=%s}", from_parent, errorbuffer, stringpool
#define STRINGIFY_FORMAT "%s, error=%s, stringpool=%s, prng=%s}", from_parent, errorbuffer, stringpool, prng
igloo_instance_t *instance = igloo_ro_to_type(self, igloo_instance_t);
igloo_error_t error;
char *from_parent;
......@@ -117,6 +123,7 @@ static igloo_error_t igloo_instance__stringify(igloo_ro_t self, char **result, i
int ret, res;
char errorbuffer[80];
char stringpool[256];
char prng[80];
if (!(flags & igloo_RO_SY_DEBUG))
return igloo_ERROR_NOSYS;
......@@ -129,6 +136,10 @@ static igloo_error_t igloo_instance__stringify(igloo_ro_t self, char **result, i
if (error != igloo_ERROR_NONE)
return error;
error = igloo_instance_prng_stringify(&(instance->prng_state), prng, sizeof(prng));
if (error != igloo_ERROR_NONE)
return error;
error = igloo_ro_stringify_parent(self, &from_parent, flags, igloo_ro_full_t);
if (error != igloo_ERROR_NONE)
return error;
......@@ -294,3 +305,16 @@ igloo_sp_state_t *igloo_instance_get_stringpool_state(igloo_ro_t self)
return &(instance->stringpool_state);
}
igloo_prng_state_t *igloo_instance_get_prng_state(igloo_ro_t self, size_t *instancelen)
{
igloo_instance_t *instance = get_instance(self);
if (instancelen)
*instancelen = sizeof(*instance);
if (!instance)
return NULL;
return &(instance->prng_state);
}
......@@ -24,6 +24,7 @@ extern "C" {
#endif
#include <stdbool.h>
#include <pthread.h>
#include <igloo/types.h>
#include <igloo/rwlock.h>
......@@ -49,16 +50,37 @@ typedef struct {
igloo_sp_area_t *areas[3];
} igloo_sp_state_t;
typedef char igloo_digest_512_t[512/8];
typedef struct {
uint64_t state;
uint64_t flags;
size_t bits;
size_t overcommitment;
size_t callcount;
pthread_mutex_t lock;
igloo_digest_512_t initial;
igloo_digest_512_t auto_seed0;
igloo_digest_512_t auto_seed1;
igloo_digest_512_t manual_seed0;
igloo_digest_512_t manual_seed1;
igloo_digest_512_t last_result;
} igloo_prng_state_t;
extern const igloo_ro_type_t * igloo_instance_type;
igloo_error_t igloo_ro_bootstrap(igloo_ro_t *instance, const igloo_ro_type_t *type);
igloo_ro_t igloo_ro_get_instance_unsafe(igloo_ro_t self, const igloo_ro_type_t *type);
igloo_sp_state_t *igloo_instance_get_stringpool_state(igloo_ro_t self);
igloo_prng_state_t *igloo_instance_get_prng_state(igloo_ro_t self, size_t *instancelen);
igloo_error_t igloo_instance_sp_init(igloo_sp_state_t *state);
void igloo_instance_sp_destroy(igloo_sp_state_t *state);
igloo_error_t igloo_instance_sp_stringify(igloo_sp_state_t *state, char *buf, size_t len);
igloo_error_t igloo_instance_prng_init(igloo_prng_state_t *state);
void igloo_instance_prng_destroy(igloo_prng_state_t *state);
igloo_error_t igloo_instance_prng_stringify(igloo_prng_state_t *state, char *buf, size_t len);
#ifdef __cplusplus
}
#endif
......
......@@ -20,7 +20,421 @@
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <pthread.h>
#ifdef HAVE_UNAME
#include <sys/utsname.h>
#endif
#if defined(HAVE_GETUID) || defined(HAVE_GETPID) || defined(HAVE_GETPPID)
#include <unistd.h>
#include <sys/types.h>
#endif
#include <igloo/prng.h>
#include <igloo/error.h>
#include <igloo/digest.h>
#include <igloo/ro.h>
#include <igloo/time.h>
#include "private.h"
#define HASHFUNC "SHA3-512" // must be a 512 bit function!
#define MAX_BITS_PER_HASH 192
#define MAX_BITS_PER_STATE (MAX_BITS_PER_HASH*3)
#define STATE_INITED 0x01U
#define STATE_AUTO_SEED_0 0x02U
#define STATE_MAN_SEED_0 0x04U
typedef struct {
igloo_ctime_t ctime;
pthread_t thread;
const void *stack;
const void *instance;
const void *buffer;
size_t bufferlen;
} igloo_prng_buffer_extra_t;
static inline void buffer_extra_init(igloo_prng_buffer_extra_t *extra, const char *instance)
{
igloo_error_t error;
memset(extra, 0, sizeof(*extra));
extra->thread = pthread_self();
extra->stack = &error; // any object on the current stack.
extra->instance = instance;
error = igloo_ctime_from_now(&(extra->ctime), igloo_CLOCK_MONOTONIC);
if (error != igloo_ERROR_NONE)
igloo_ctime_from_now(&(extra->ctime), igloo_CLOCK_REALTIME);
}
igloo_error_t igloo_instance_prng_init(igloo_prng_state_t *state)
{
if (pthread_mutex_init(&(state->lock), NULL) != 0)
return igloo_ERROR_GENERIC;
state->overcommitment = 32;
return igloo_ERROR_NONE;
}
void igloo_instance_prng_destroy(igloo_prng_state_t *state)
{
pthread_mutex_destroy(&(state->lock));
}
igloo_error_t igloo_instance_prng_stringify(igloo_prng_state_t *state, char *buf, size_t len)
{
(void)state;
pthread_mutex_lock(&(state->lock));
snprintf(buf, len, "{bits=%zu}", state->bits);
pthread_mutex_unlock(&(state->lock));
return igloo_ERROR_NONE;
}
static igloo_error_t get_inited_state(igloo_prng_state_t **state, size_t *instancelen, igloo_ro_t instance)
{
igloo_prng_state_t *self;
size_t len;
*state = self = igloo_instance_get_prng_state(instance, &len);
if (!*state)
return igloo_ERROR_BADSTATE;
if (instancelen)
*instancelen = len;
pthread_mutex_lock(&(self->lock));
if (!(self->state & STATE_INITED)) {
igloo_digest_t *digest = NULL;
igloo_error_t error;
const void *instanceraw = igloo_ro_to_type(instance, igloo_ro_stub_t);
struct {
igloo_prng_buffer_extra_t extra;
int debian;
#ifdef HAVE_UNAME
struct utsname utsname;
#endif
#ifdef HAVE_GETUID
uid_t uid;
#endif
#ifdef HAVE_GETPID
pid_t pid;
#endif
#ifdef HAVE_GETPPID
pid_t ppid;
#endif
} seed;
buffer_extra_init(&(seed.extra), instanceraw);
seed.extra.buffer = instanceraw;
seed.extra.bufferlen = len;
memset(&seed, 0, sizeof(seed));
seed.debian = 4;
#ifdef HAVE_UNAME
uname(&seed.utsname);
#endif
#ifdef HAVE_GETUID
seed.uid = getuid();
#endif
#ifdef HAVE_GETPID
seed.pid = getpid();
#endif
#ifdef HAVE_GETPPID
seed.ppid = getppid();
#endif
error = igloo_digest_new(&digest, instance, HASHFUNC);
if (error != igloo_ERROR_NONE)
return error;
if (igloo_digest_write(digest, instanceraw, len) != (ssize_t)len) {
igloo_ro_unref(&digest);
return igloo_ERROR_GENERIC;
}
if (igloo_digest_write(digest, &seed, sizeof(seed)) != (ssize_t)sizeof(seed)) {
igloo_ro_unref(&digest);
return igloo_ERROR_GENERIC;
}
if (igloo_digest_read(digest, self->initial, sizeof(self->initial)) != sizeof(self->initial)) {
igloo_ro_unref(&digest);
return igloo_ERROR_GENERIC;
}
error = igloo_ro_unref(&digest);
if (error != igloo_ERROR_NONE)
return error;
self->bits = 5;
self->state |= STATE_INITED;
}
return igloo_ERROR_NONE;
}
static ssize_t igloo_prng_estimate_bits(const void *buffer, size_t len)
{
size_t counts[256];
size_t i;
double entpb = 0;
if (!buffer)
return -1;
if (len < 2)
return 0;
memset(counts, 0, sizeof(counts));
for (i = 0; i < len; i++)
counts[((const unsigned char *)buffer)[i]]++;
for (i = 0; i < 256; i++) {
double probability;
if (!counts[i])
continue;
probability = counts[i]/(double)len;
entpb += probability * log2(1. / probability);
}
return entpb * len;
}
igloo_error_t igloo_prng_configure(igloo_ro_t instance, igloo_prng_flags_t addflags, igloo_prng_flags_t removeflags, ssize_t overcommitment)
{
igloo_prng_state_t *self;
self = igloo_instance_get_prng_state(instance, NULL);
if (!self)
return igloo_ERROR_BADSTATE;
pthread_mutex_lock(&(self->lock));
self->flags |= addflags|removeflags;
self->flags -= removeflags;
if (overcommitment > 0)
self->overcommitment = overcommitment;
pthread_mutex_unlock(&(self->lock));
return igloo_ERROR_NONE;
}
static igloo_error_t digest_instance_extra_once(const void *instanceraw, size_t instancelen, igloo_ro_t instance, igloo_prng_buffer_extra_t *extra, const void *mid, size_t midlen, igloo_digest_512_t out)
{
igloo_digest_t *digest = NULL;
igloo_error_t error;
error = igloo_digest_new(&digest, instance, HASHFUNC);
if (error != igloo_ERROR_NONE)
return error;
if (igloo_digest_write(digest, instanceraw, instancelen) != (ssize_t)instancelen) {
igloo_ro_unref(&digest);
return igloo_ERROR_GENERIC;
}
if (mid && midlen) {
if (igloo_digest_write(digest, mid, midlen) != (ssize_t)midlen) {
igloo_ro_unref(&digest);
return igloo_ERROR_GENERIC;
}
}
if (igloo_digest_write(digest, extra, sizeof(*extra)) != (ssize_t)sizeof(*extra)) {
igloo_ro_unref(&digest);
return igloo_ERROR_GENERIC;
}
if (igloo_digest_read(digest, out, sizeof(igloo_digest_512_t)) != sizeof(igloo_digest_512_t)) {
igloo_ro_unref(&digest);
return igloo_ERROR_GENERIC;
}
error = igloo_ro_unref(&digest);
if (error != igloo_ERROR_NONE)
return error;
return igloo_ERROR_NONE;
}
igloo_error_t igloo_prng_auto_reseed_unlocked(igloo_prng_state_t *self, const void *instanceraw, size_t instancelen, igloo_ro_t instance)
{
igloo_prng_buffer_extra_t extra;
igloo_error_t error;
buffer_extra_init(&extra, instanceraw);
self->callcount++;
if (self->state & STATE_AUTO_SEED_0) {
error = digest_instance_extra_once(instanceraw, instancelen, instance, &extra, NULL, 0, self->auto_seed0);
} else {
error = digest_instance_extra_once(instanceraw, instancelen, instance, &extra, NULL, 0, self->auto_seed1);
}
if (error != igloo_ERROR_NONE) {
pthread_mutex_unlock(&(self->lock));
return error;
}
self->state ^= STATE_AUTO_SEED_0;
self->bits += 3;
return igloo_ERROR_NONE;
}
igloo_error_t igloo_prng_auto_reseed(igloo_ro_t instance)
{
igloo_prng_state_t *self;
size_t instancelen;
igloo_error_t error = get_inited_state(&self, &instancelen, instance);
const void *instanceraw = igloo_ro_to_type(instance, igloo_ro_stub_t);
if (error != igloo_ERROR_NONE)
return error;
error = igloo_prng_auto_reseed_unlocked(self, instanceraw, instancelen, instance);
pthread_mutex_unlock(&(self->lock));
return error;
}
igloo_error_t igloo_prng_write(igloo_ro_t instance, const void *buffer, size_t len, ssize_t bits)
{
igloo_prng_state_t *self;
size_t instancelen;
igloo_error_t error;
const void *instanceraw;
igloo_prng_buffer_extra_t extra;
if (bits > (ssize_t)(len*8))
return igloo_ERROR_INVAL;
if (bits < 0) {
ssize_t ent = igloo_prng_estimate_bits(buffer, len);
bits = (len * 8) / 32;
if (ent > 0 && bits > ent)
bits = ent;
}
if (bits > MAX_BITS_PER_HASH)
bits = MAX_BITS_PER_HASH;
error = get_inited_state(&self, &instancelen, instance);
if (error != igloo_ERROR_NONE)
return error;
instanceraw = igloo_ro_to_type(instance, igloo_ro_stub_t);
buffer_extra_init(&extra, instanceraw);
self->callcount++;
if (self->state & STATE_MAN_SEED_0) {
error = digest_instance_extra_once(instanceraw, instancelen, instance, &extra, buffer, len, self->manual_seed0);
} else {
error = digest_instance_extra_once(instanceraw, instancelen, instance, &extra, buffer, len, self->manual_seed1);
}
if (error != igloo_ERROR_NONE) {
pthread_mutex_unlock(&(self->lock));
return error;
}
self->state ^= STATE_MAN_SEED_0;
self->bits += bits;
self->bits += 1;
if (self->bits > MAX_BITS_PER_STATE)
self->bits = MAX_BITS_PER_STATE;
pthread_mutex_unlock(&(self->lock));
return igloo_ERROR_NONE;
}
static igloo_error_t igloo_prng_read__inner(igloo_prng_state_t *self, const void *instanceraw, size_t instancelen, igloo_ro_t instance, igloo_prng_buffer_extra_t *extra)
{
self->callcount++;
return digest_instance_extra_once(instanceraw, instancelen, instance, extra, NULL, 0, self->last_result);
}
ssize_t igloo_prng_read(igloo_ro_t instance, void *buffer, size_t len)
{
igloo_prng_state_t *self;
size_t instancelen;
igloo_error_t error = get_inited_state(&self, &instancelen, instance);
const void *instanceraw = igloo_ro_to_type(instance, igloo_ro_stub_t);
size_t done = 0;
size_t bitswant;
igloo_prng_buffer_extra_t extra;
if (error != igloo_ERROR_NONE)
return -1;
bitswant = ((len * 8) / self->overcommitment) + 1;
if (bitswant > self->bits) {
size_t try;
for (try = 0; bitswant > self->bits && try < 2; try++) {
error = igloo_prng_auto_reseed_unlocked(self, instanceraw, instancelen, instance);
if (error != igloo_ERROR_NONE)
break;
}
if (bitswant > self->bits) {
pthread_mutex_unlock(&(self->lock));
return -1;
}
}
self->bits -= bitswant;
buffer_extra_init(&extra, instanceraw);
extra.buffer = buffer;
extra.bufferlen = len;
while (done < len) {
size_t todo = (len - done) < sizeof(self->last_result) ? len - done : sizeof(self->last_result);
error = igloo_prng_read__inner(self, instanceraw, instancelen, instance, &extra);
if (error != igloo_ERROR_NONE) {
pthread_mutex_unlock(&(self->lock));
if (done > 0) {
return done;
} else {
return -1;
}
}
memcpy(buffer + done, self->last_result, todo);
done += todo;
}
pthread_mutex_unlock(&(self->lock));
return done;
}
/* write len pseudo random bytes to a file. If len is -1 a default value is used. */
igloo_error_t igloo_prng_write_file(igloo_ro_t instance, const char *filename, ssize_t len);
/* read at max len bytes from the file and see the PRNG with it. if len is -1 all of the file is read. */
igloo_error_t igloo_prng_read_file(igloo_ro_t instance, const char *filename, ssize_t len, ssize_t bits);
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment