From b98a6b1c57062e27486950a764589caa195fd60a Mon Sep 17 00:00:00 2001 From: Vivien Kraus Date: Wed, 1 Jan 2020 00:00:00 +0100 Subject: Add a random number generator. --- ChangeLog | 4 + Makefile.am | 1 + NEWS | 2 + po/ChangeLog | 4 + po/POTFILES.in | 5 +- po/fr.po | 195 ++++++++++-------- po/webid-oidc.pot | 98 ++++++++- src/ChangeLog | 5 + src/Makefile.am | 1 + src/libwebidoidc.c | 2 + src/random/ChangeLog | 11 + src/random/Makefile.am | 20 ++ src/random/generate-random.c | 42 ++++ src/random/libwebidoidc-random.c | 35 ++++ src/random/random.c | 423 +++++++++++++++++++++++++++++++++++++++ src/random/webid-oidc/random.h | 57 ++++++ src/scm/webid-oidc/stubs.scm | 4 +- src/scm/webid-oidc/testing.scm | 1 + tests/Makefile.am | 15 +- tests/random.scm | 17 ++ 20 files changed, 843 insertions(+), 99 deletions(-) create mode 100644 src/random/ChangeLog create mode 100644 src/random/Makefile.am create mode 100644 src/random/generate-random.c create mode 100644 src/random/libwebidoidc-random.c create mode 100644 src/random/random.c create mode 100644 src/random/webid-oidc/random.h create mode 100644 tests/random.scm diff --git a/ChangeLog b/ChangeLog index f93cc8e..3a6d4da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,10 @@ * get-version: Never print a newline. +2020-11-25 Vivien Kraus + + * NEWS (A random number generator): Update NEWS + 2020-11-22 Vivien Kraus * README: Initial version diff --git a/Makefile.am b/Makefile.am index 207bba2..73af906 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,6 +12,7 @@ AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" -I. -I$(srcdir) AM_CFLAGS = $(GUILE_CFLAGS) $(NETTLE_CFLAGS) nodist_noinst_SCRIPTS = pre-inst-env INDENTED = +include_HEADERS = include src/Makefile.am include doc/Makefile.am diff --git a/NEWS b/NEWS index 816a170..0c91be2 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ * Initial features ** Add base64 encoding and decoding +** A random number generator +The code provides a thread-safe, parallel, random number generator. # Local Variables: # mode: org diff --git a/po/ChangeLog b/po/ChangeLog index f303b24..7001ddd 100644 --- a/po/ChangeLog +++ b/po/ChangeLog @@ -1,3 +1,7 @@ +2020-11-25 Vivien Kraus + + * POTFILES.in: Put the random submodule in the list. + 2020-11-22 Vivien Kraus * LINGUAS: add fr diff --git a/po/POTFILES.in b/po/POTFILES.in index 987cc14..3b2cece 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,3 +1,6 @@ # List of source files which contain translatable strings. src/libwebidoidc.c -src/scm/webid-oidc/errors.scm \ No newline at end of file +src/random/random.c +src/random/libwebidoidc-random.c +src/random/generate-random.c +src/scm/webid-oidc/errors.scm diff --git a/po/fr.po b/po/fr.po index 6fcba28..857f50c 100644 --- a/po/fr.po +++ b/po/fr.po @@ -12,10 +12,115 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: src/libwebidoidc.c:7 +#: src/libwebidoidc.c:8 msgid "This is the main function." msgstr "Ceci est la fonction principale." +#: src/random/random.c:199 +#, c-format +msgid "Could not set the global random generator up.\n" +msgstr "Impossible d'initialiser le générateur de nombres aléatoires global.\n" + +#: src/random/random.c:213 src/random/random.c:225 src/random/random.c:238 +#: src/random/random.c:249 src/random/random.c:260 +#, c-format +msgid "Could not set the global random generator up: out of memory.\n" +msgstr "" +"Impossible d'initialiser le générateur de nombres aléatoires global : plus " +"de mémoire.\n" + +#: src/random/random.c:270 +#, c-format +msgid "" +"Warning: could not open the seed file, maybe the parent directory is " +"missing...\n" +msgstr "" +"Attention : impossible d'ouvrir le fichier graine, peut-être que le dossier " +"parent n'existe pas...\n" + +#: src/random/random.c:274 +#, c-format +msgid "Warning: could not create the cache directory '%s'.\n" +msgstr "Attention : impossible de créer le dossier de cache « %s ».\n" + +#: src/random/random.c:276 +msgid "when creating the cache directory" +msgstr "en créant le dossier de cache" + +#: src/random/random.c:282 +#, c-format +msgid "Warning: could not create the package cache directory '%s'.\n" +msgstr "" +"Attention : impossible de créer le dossier de cache du paquet « %s ».\n" + +#: src/random/random.c:284 +msgid "when creating the package cache directory" +msgstr "en créant le dossier de cache du paquet" + +#: src/random/random.c:290 +#, c-format +msgid "Could not open the seed file '%s'.\n" +msgstr "Impossible d'ouvrir le fichier graine « %s ».\n" + +#: src/random/random.c:291 +msgid "when opening the seed file" +msgstr "en ouvrant le fichier graine" + +#: src/random/random.c:311 +#, c-format +msgid "Could not lock the seed file '%s'.\n" +msgstr "Impossible de verrouiller le fichier graine « %s ».\n" + +#: src/random/random.c:313 +msgid "when locking the seed file" +msgstr "en verrouillant le fichier graine" + +#: src/random/random.c:327 +#, c-format +msgid "" +"Warning: the seed file '%s' is too short. This weakens the random number " +"generator. Please write more random data in it.\n" +msgstr "" +"Attention : le fichier de graine « %s » est trop court. Ceci affaiblit le " +"générateur de nombres aléatoires. Veuillez écrire plus de données dedans.\n" + +#: src/random/random.c:344 +#, c-format +msgid "Could not update the seed file '%s'.\n" +msgstr "Impossible de mettre à jour le fichier graine « %s ».\n" + +#: src/random/random.c:345 +msgid "when rewinding the seed file" +msgstr "en rembobinant le fichier de graine" + +#: src/random/random.c:377 +#, c-format +msgid "Could not unlock the seed file '%s'.\n" +msgstr "Impossible de déverrouiller le fichier graine « %s ».\n" + +#: src/random/random.c:379 +msgid "when unlocking the seed file" +msgstr "en déverrouillant le fichier graine" + +#: src/random/random.c:407 +#, c-format +msgid "Could not set the thread-local random generator up.\n" +msgstr "Impossible d'initialiser le générateur de nombres aléatoires du fil.\n" + +#: src/random/random.c:415 +#, c-format +msgid "" +"The random module has not been initialized. Please call " +"webid_oidc_random_init first.\n" +msgstr "" +"Le module aléatoire n'a pas été initialisé. Veuillez appeler " +"webid_oidc_random_init d'abort.\n" + +#: src/random/generate-random.c:29 +#, c-format +msgid "Usage: generate-random [NUMBER OF BYTES]\n" +msgstr "Utilisation : generate-random [NOMBRE D'OCTETS]\n" + #: src/scm/webid-oidc/errors.scm:35 msgid "that’s how it is" msgstr "c’est comme ça" @@ -67,94 +172,6 @@ msgstr "il y a un type et des arguments" msgid "Unhandled exception type ~a." msgstr "Type d’exception non pris en charge ~a." -#, c-format -#~ msgid "Could not set the global random generator up.\n" -#~ msgstr "" -#~ "Impossible d'initialiser le générateur de nombres aléatoires global.\n" - -#, c-format -#~ msgid "Could not set the global random generator up: out of memory.\n" -#~ msgstr "" -#~ "Impossible d'initialiser le générateur de nombres aléatoires global : " -#~ "plus de mémoire.\n" - -#, c-format -#~ msgid "" -#~ "Warning: could not open the seed file, maybe the parent directory is " -#~ "missing...\n" -#~ msgstr "" -#~ "Attention : impossible d'ouvrir le fichier graine, peut-être que le " -#~ "dossier parent n'existe pas...\n" - -#, c-format -#~ msgid "Warning: could not create the cache directory '%s'.\n" -#~ msgstr "Attention : impossible de créer le dossier de cache « %s ».\n" - -#~ msgid "when creating the cache directory" -#~ msgstr "en créant le dossier de cache" - -#, c-format -#~ msgid "Warning: could not create the package cache directory '%s'.\n" -#~ msgstr "" -#~ "Attention : impossible de créer le dossier de cache du paquet « %s ».\n" - -#~ msgid "when creating the package cache directory" -#~ msgstr "en créant le dossier de cache du paquet" - -#, c-format -#~ msgid "Could not open the seed file '%s'.\n" -#~ msgstr "Impossible d'ouvrir le fichier graine « %s ».\n" - -#~ msgid "when opening the seed file" -#~ msgstr "en ouvrant le fichier graine" - -#, c-format -#~ msgid "Could not lock the seed file '%s'.\n" -#~ msgstr "Impossible de verrouiller le fichier graine « %s ».\n" - -#~ msgid "when locking the seed file" -#~ msgstr "en verrouillant le fichier graine" - -#, c-format -#~ msgid "" -#~ "Warning: the seed file '%s' is too short. This weakens the random number " -#~ "generator. Please write more random data in it.\n" -#~ msgstr "" -#~ "Attention : le fichier de graine « %s » est trop court. Ceci affaiblit le " -#~ "générateur de nombres aléatoires. Veuillez écrire plus de données " -#~ "dedans.\n" - -#, c-format -#~ msgid "Could not update the seed file '%s'.\n" -#~ msgstr "Impossible de mettre à jour le fichier graine « %s ».\n" - -#~ msgid "when rewinding the seed file" -#~ msgstr "en rembobinant le fichier de graine" - -#, c-format -#~ msgid "Could not unlock the seed file '%s'.\n" -#~ msgstr "Impossible de déverrouiller le fichier graine « %s ».\n" - -#~ msgid "when unlocking the seed file" -#~ msgstr "en déverrouillant le fichier graine" - -#, c-format -#~ msgid "Could not set the thread-local random generator up.\n" -#~ msgstr "" -#~ "Impossible d'initialiser le générateur de nombres aléatoires du fil.\n" - -#, c-format -#~ msgid "" -#~ "The random module has not been initialized. Please call " -#~ "webid_oidc_random_init first.\n" -#~ msgstr "" -#~ "Le module aléatoire n'a pas été initialisé. Veuillez appeler " -#~ "webid_oidc_random_init d'abort.\n" - -#, c-format -#~ msgid "Usage: generate-random [NUMBER OF BYTES]\n" -#~ msgstr "Utilisation : generate-random [NOMBRE D'OCTETS]\n" - #, c-format #~ msgid "Usage: generate-key [NUMBER OF BITS | CURVE]\n" #~ msgstr "Utilisation : generate-key [NOMBRE DE BITS | COURBE]\n" diff --git a/po/webid-oidc.pot b/po/webid-oidc.pot index dc31531..4c608e8 100644 --- a/po/webid-oidc.pot +++ b/po/webid-oidc.pot @@ -17,10 +17,106 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: src/libwebidoidc.c:7 +#: src/libwebidoidc.c:8 msgid "This is the main function." msgstr "" +#: src/random/random.c:199 +#, c-format +msgid "Could not set the global random generator up.\n" +msgstr "" + +#: src/random/random.c:213 src/random/random.c:225 src/random/random.c:238 +#: src/random/random.c:249 src/random/random.c:260 +#, c-format +msgid "Could not set the global random generator up: out of memory.\n" +msgstr "" + +#: src/random/random.c:270 +#, c-format +msgid "" +"Warning: could not open the seed file, maybe the parent directory is " +"missing...\n" +msgstr "" + +#: src/random/random.c:274 +#, c-format +msgid "Warning: could not create the cache directory '%s'.\n" +msgstr "" + +#: src/random/random.c:276 +msgid "when creating the cache directory" +msgstr "" + +#: src/random/random.c:282 +#, c-format +msgid "Warning: could not create the package cache directory '%s'.\n" +msgstr "" + +#: src/random/random.c:284 +msgid "when creating the package cache directory" +msgstr "" + +#: src/random/random.c:290 +#, c-format +msgid "Could not open the seed file '%s'.\n" +msgstr "" + +#: src/random/random.c:291 +msgid "when opening the seed file" +msgstr "" + +#: src/random/random.c:311 +#, c-format +msgid "Could not lock the seed file '%s'.\n" +msgstr "" + +#: src/random/random.c:313 +msgid "when locking the seed file" +msgstr "" + +#: src/random/random.c:327 +#, c-format +msgid "" +"Warning: the seed file '%s' is too short. This weakens the random number " +"generator. Please write more random data in it.\n" +msgstr "" + +#: src/random/random.c:344 +#, c-format +msgid "Could not update the seed file '%s'.\n" +msgstr "" + +#: src/random/random.c:345 +msgid "when rewinding the seed file" +msgstr "" + +#: src/random/random.c:377 +#, c-format +msgid "Could not unlock the seed file '%s'.\n" +msgstr "" + +#: src/random/random.c:379 +msgid "when unlocking the seed file" +msgstr "" + +#: src/random/random.c:407 +#, c-format +msgid "Could not set the thread-local random generator up.\n" +msgstr "" + +#: src/random/random.c:415 +#, c-format +msgid "" +"The random module has not been initialized. Please call " +"webid_oidc_random_init first.\n" +msgstr "" + +#: src/random/generate-random.c:29 +#, c-format +msgid "Usage: generate-random [NUMBER OF BYTES]\n" +msgstr "" + #: src/scm/webid-oidc/errors.scm:35 msgid "that’s how it is" msgstr "" diff --git a/src/ChangeLog b/src/ChangeLog index 75ed3d1..e2e9bea 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -3,3 +3,8 @@ * Makefile.am (%canon_reldir%_libwebidoidc_la_SOURCES): the common code is considered a source. * utilities.h: Put the common code in that header. + + * Makefile.am: build the "random" submodule. + + * libwebidoidc.c (init_webidoidc): Initialize the "random" submodule. + diff --git a/src/Makefile.am b/src/Makefile.am index 93fbada..732a941 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,6 +24,7 @@ install_go_targets = install-webidoidcgoDATA install_mod_targets = install-webidoidcmodDATA install-dist_webidoidcmodDATA include %reldir%/base64/Makefile.am +include %reldir%/random/Makefile.am include %reldir%/pre-inst/Makefile.am include %reldir%/inst/Makefile.am include %reldir%/scm/Makefile.am diff --git a/src/libwebidoidc.c b/src/libwebidoidc.c index 09af3fd..af26ae0 100644 --- a/src/libwebidoidc.c +++ b/src/libwebidoidc.c @@ -1,9 +1,11 @@ #define N_(s) void init_webidoidc_base64 (void); +void init_webidoidc_random (void); void init_webidoidc (void) { N_("This is the main function."); init_webidoidc_base64 (); + init_webidoidc_random (); } diff --git a/src/random/ChangeLog b/src/random/ChangeLog new file mode 100644 index 0000000..b2a1cc3 --- /dev/null +++ b/src/random/ChangeLog @@ -0,0 +1,11 @@ +2020-11-26 Vivien Kraus + + * libwebidoidc-random.c: Fix the submodule to use the package name. + (fopen_cache_seed): Fix in this function. + +2020-11-25 Vivien Kraus + + * libwebidoidc-random.c: New submodule. + + * generate-random.c: New program. + diff --git a/src/random/Makefile.am b/src/random/Makefile.am new file mode 100644 index 0000000..fbd548c --- /dev/null +++ b/src/random/Makefile.am @@ -0,0 +1,20 @@ +noinst_LTLIBRARIES += %reldir%/libwebidoidc-random.la +noinst_PROGRAMS += %reldir%/generate-random +EXTRA_DIST += %reldir%/libwebidoidc-random.x +BUILT_SOURCES += %reldir%/libwebidoidc-random.x +include_HEADERS += %reldir%/webid-oidc/random.h + +%canon_reldir%_libwebidoidc_random_la_LIBADD = $(GUILE_LIBS) $(NETTLE_LIBS) +%canon_reldir%_libwebidoidc_random_la_SOURCES = \ + %reldir%/libwebidoidc-random.c \ + %reldir%/random.c \ + %reldir%/webid-oidc/random.h +%canon_reldir%_generate_random_LDADD = %reldir%/libwebidoidc-random.la $(GUILE_LIBS) + +AM_CFLAGS += -I %reldir% -I $(srcdir)/%reldir% + +INDENTED += \ + %reldir%/generate-random.c \ + $(%canon_reldir%_libwebidoidc_random_la_SOURCES) + +%reldir%/libwebidoidc-random.o: %reldir%/libwebidoidc-random.x diff --git a/src/random/generate-random.c b/src/random/generate-random.c new file mode 100644 index 0000000..bde01b9 --- /dev/null +++ b/src/random/generate-random.c @@ -0,0 +1,42 @@ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include + +#define _(s) gettext (s) + +SCM webidoidc_random_g (SCM length); + +int init_webidoidc_random (void); + +static void +run (void *params, int argc, char *argv[]) +{ + size_t n_bytes; + char *end; + SCM data; + (void) params; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + if (argc != 2 + || (n_bytes = strtoull (argv[1], &end, 10)) == 0 || *end != '\0') + { + fprintf (stderr, _("Usage: generate-random [NUMBER OF BYTES]\n")); + exit (1); + } + init_webidoidc_random (); + data = webidoidc_random_g (scm_from_size_t (n_bytes)); + printf ("%s\n", scm_to_locale_string (data)); +} + +int +main (int argc, char *argv[]) +{ + scm_boot_guile (argc, argv, run, NULL); + return 0; +} diff --git a/src/random/libwebidoidc-random.c b/src/random/libwebidoidc-random.c new file mode 100644 index 0000000..2bea9bb --- /dev/null +++ b/src/random/libwebidoidc-random.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include + +#define _(s) dgettext (PACKAGE, s) + +/* Register "random", a guile function to generate random data. */ +void init_webidoidc_random (void); + +SCM_DEFINE (webidoidc_random_g, "random", 1, 0, 0, (SCM length), + "Get @var{length} random bytes, and encode them in a base64-url string.") +{ + size_t c_length = scm_to_size_t (length); + uint8_t *data = scm_gc_malloc_pointerless (c_length, "random data"); + webid_oidc_random (c_length, data); + return wrap_bytevector (c_length, data); +} + +SCM_DEFINE (webidoidc_random_init_g, "random-init!", 0, 0, 0, (void), + "Re-initialize the random number generator so that future threads are not predictable.") +{ + webid_oidc_random_init (); + return SCM_UNDEFINED; +} + +void +init_webidoidc_random (void) +{ + webid_oidc_random_init (); +#ifndef SCM_MAGIC_SNARFER +#include "libwebidoidc-random.x" +#endif /* not SCM_MAGIC_SNARFER */ +} diff --git a/src/random/random.c b/src/random/random.c new file mode 100644 index 0000000..d5c45f5 --- /dev/null +++ b/src/random/random.c @@ -0,0 +1,423 @@ +#ifdef HAVE_CONFIG_H +#include +#endif /* not HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define _(s) dgettext (PACKAGE, s) + +struct webid_oidc_random_context +{ + struct yarrow256_ctx ctx; + enum webid_oidc_random_context_spawn_flags flags; + enum webid_oidc_random_context_state state; + unsigned int state_progress; + uint8_t seed[YARROW256_SEED_FILE_SIZE]; + mtx_t mutex; +}; + +struct webid_oidc_random_context * +webid_oidc_random_context_alloc (void) +{ + struct webid_oidc_random_context *ret = + malloc (sizeof (struct webid_oidc_random_context)); + if (ret && mtx_init (&(ret->mutex), mtx_plain) != thrd_success) + { + free (ret); + ret = NULL; + } + if (ret) + { + yarrow256_init (&(ret->ctx), 0, NULL); + ret->flags = 0; + ret->state = WEBID_OIDC_RANDOM_CONTEXT_WAIT_LOCK_FILE; + } + return ret; +} + +void +webid_oidc_random_context_free (struct webid_oidc_random_context *ctx) +{ + if (ctx) + { + mtx_destroy (&(ctx->mutex)); + } + free (ctx); +} + +enum webid_oidc_random_context_state +webid_oidc_random_context_get_state (const struct webid_oidc_random_context + *ctx) +{ + return ctx->state; +} + +int +webid_oidc_random_context_get_wait_bytes (const struct + webid_oidc_random_context *ctx, + size_t max, size_t start, + uint8_t * bytes, size_t *n_bytes) +{ + if (ctx->state == WEBID_OIDC_RANDOM_CONTEXT_WAIT_FILE_WRITE) + { + if (start >= YARROW256_SEED_FILE_SIZE) + { + *n_bytes = 0; + } + else + { + if (max + start > YARROW256_SEED_FILE_SIZE) + { + max = YARROW256_SEED_FILE_SIZE - start; + } + memcpy (bytes, ctx->seed + start, max); + *n_bytes = YARROW256_SEED_FILE_SIZE - start; + } + return 1; + } + return 0; +} + +int +webid_oidc_random_context_ok (struct webid_oidc_random_context *ctx) +{ + switch (ctx->state) + { + case WEBID_OIDC_RANDOM_CONTEXT_WAIT_LOCK_FILE: + ctx->state = WEBID_OIDC_RANDOM_CONTEXT_WAIT_FILE_READ; + ctx->state_progress = 0; + break; + case WEBID_OIDC_RANDOM_CONTEXT_WAIT_FILE_WRITE: + ctx->state = WEBID_OIDC_RANDOM_CONTEXT_WAIT_UNLOCK_FILE; + break; + case WEBID_OIDC_RANDOM_CONTEXT_WAIT_UNLOCK_FILE: + ctx->state = WEBID_OIDC_RANDOM_CONTEXT_READY; + break; + default: + return 0; + } + return 1; +} + +int +webid_oidc_random_context_bytes (struct webid_oidc_random_context *ctx, + size_t n_bytes, const uint8_t * bytes) +{ + switch (ctx->state) + { + case WEBID_OIDC_RANDOM_CONTEXT_WAIT_FILE_READ: + if (n_bytes >= (YARROW256_SEED_FILE_SIZE - ctx->state_progress)) + { + n_bytes = (YARROW256_SEED_FILE_SIZE - ctx->state_progress); + } + memcpy (ctx->seed + ctx->state_progress, bytes, n_bytes); + ctx->state_progress += n_bytes; + if (ctx->state_progress == YARROW256_SEED_FILE_SIZE) + { + yarrow256_seed (&(ctx->ctx), YARROW256_SEED_FILE_SIZE, ctx->seed); + yarrow256_random (&(ctx->ctx), YARROW256_SEED_FILE_SIZE, ctx->seed); + ctx->state = WEBID_OIDC_RANDOM_CONTEXT_WAIT_FILE_WRITE; + } + break; + default: + return 0; + } + return 1; +} + +int +webid_oidc_random_context_spawn (struct webid_oidc_random_context *parent, + struct webid_oidc_random_context *child, + enum webid_oidc_random_context_spawn_flags + flags) +{ + if (!webid_oidc_random_context_get + (parent, YARROW256_SEED_FILE_SIZE, child->seed)) + { + return 0; + } + child->flags = flags; + child->state = WEBID_OIDC_RANDOM_CONTEXT_READY; + yarrow256_seed (&(child->ctx), YARROW256_SEED_FILE_SIZE, child->seed); + return 1; +} + +int +webid_oidc_random_context_get (struct webid_oidc_random_context *ctx, + size_t request_size, uint8_t * data) +{ + if (ctx->state == WEBID_OIDC_RANDOM_CONTEXT_READY) + { + if (!(ctx->flags & WEBID_OIDC_RANDOM_CONTEXT_THREAD_UNSAFE)) + { + if (mtx_lock (&(ctx->mutex)) != thrd_success) + { + assert (0); + } + } + yarrow256_random (&(ctx->ctx), request_size, data); + if (!(ctx->flags & WEBID_OIDC_RANDOM_CONTEXT_THREAD_UNSAFE)) + { + if (mtx_unlock (&(ctx->mutex)) != thrd_success) + { + assert (0); + } + } + } + return (ctx->state == WEBID_OIDC_RANDOM_CONTEXT_READY); +} + +static struct webid_oidc_random_context *global_ctx = NULL; + +void +webid_oidc_random_init (void) +{ + char *cache_dir, *pkg_cache_dir, *filename; + char *home = getenv ("HOME"); + char *xdg_cache_home = getenv ("XDG_CACHE_HOME"); + char *application = getenv ("WEBID_OIDC_APPLICATION_NAME"); + static const char *default_application = PACKAGE; + FILE *seed_file; + FILE *system_rng_file; + struct flock lock; + if (global_ctx) + { + webid_oidc_random_context_free (global_ctx); + global_ctx = NULL; + } + global_ctx = webid_oidc_random_context_alloc (); + if (!global_ctx) + { + fprintf (stderr, _("Could not set the global random generator up.\n")); + abort (); + } + if (!application) + { + application = (char *) default_application; + } + if (xdg_cache_home) + { + cache_dir = malloc (strlen (xdg_cache_home) + 1); + if (!cache_dir) + { + fprintf (stderr, + _ + ("Could not set the global random generator up: out of memory.\n")); + abort (); + } + strcpy (cache_dir, xdg_cache_home); + } + else if (home) + { + cache_dir = malloc (strlen (home) + strlen ("/.cache") + 1); + if (!cache_dir) + { + fprintf (stderr, + _ + ("Could not set the global random generator up: out of memory.\n")); + abort (); + } + strcpy (cache_dir, home); + strcat (cache_dir, "/.cache"); + } + else + { + cache_dir = malloc (strlen ("/var/cache") + 1); + if (!cache_dir) + { + fprintf (stderr, + _ + ("Could not set the global random generator up: out of memory.\n")); + abort (); + } + strcpy (cache_dir, "/var/cache"); + } + pkg_cache_dir = malloc (strlen (cache_dir) + + strlen ("/") + strlen (application) + 1); + if (!pkg_cache_dir) + { + fprintf (stderr, + _ + ("Could not set the global random generator up: out of memory.\n")); + abort (); + } + strcpy (pkg_cache_dir, cache_dir); + strcat (pkg_cache_dir, "/"); + strcat (pkg_cache_dir, application); + filename = malloc (strlen (pkg_cache_dir) + strlen ("/seed") + 1); + if (!filename) + { + fprintf (stderr, + _ + ("Could not set the global random generator up: out of memory.\n")); + abort (); + } + strcpy (filename, pkg_cache_dir); + strcat (filename, "/seed"); + seed_file = fopen (filename, "r+"); + if (!seed_file) + { + fprintf (stderr, + _ + ("Warning: could not open the seed file, maybe the parent directory is missing...\n")); + if (mkdir (cache_dir, 0777) != 0) + { + fprintf (stderr, + _("Warning: could not create the cache directory '%s'.\n"), + cache_dir); + perror (_("when creating the cache directory")); + } + if (mkdir (pkg_cache_dir, 0777) != 0) + { + fprintf (stderr, + _ + ("Warning: could not create the package cache directory '%s'.\n"), + pkg_cache_dir); + perror (_("when creating the package cache directory")); + } + seed_file = fopen (filename, "w+"); + } + if (!seed_file) + { + fprintf (stderr, _("Could not open the seed file '%s'.\n"), filename); + perror (_("when opening the seed file")); + abort (); + } + system_rng_file = fopen ("/dev/random", "r"); + while (global_ctx->state != WEBID_OIDC_RANDOM_CONTEXT_READY) + { + int error; + int c; + uint8_t byte = 0; + size_t i, n; + switch (global_ctx->state) + { + case WEBID_OIDC_RANDOM_CONTEXT_WAIT_LOCK_FILE: + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + error = fcntl (fileno (seed_file), F_SETLKW, &lock); + if (error != 0) + { + fprintf (stderr, _("Could not lock the seed file '%s'.\n"), + filename); + perror (_("when locking the seed file")); + abort (); + } + if (!webid_oidc_random_context_ok (global_ctx)) + { + assert (0); + } + break; + case WEBID_OIDC_RANDOM_CONTEXT_WAIT_FILE_READ: + c = fgetc (seed_file); + if (c == EOF && system_rng_file == NULL) + { + fprintf (stderr, + _ + ("Warning: the seed file '%s' is too short. This weakens the random number generator. Please write more random data in it.\n"), + filename); + } + else if (c == EOF) + { + c = fgetc (system_rng_file); + } + byte = c; + if (!webid_oidc_random_context_bytes (global_ctx, 1, &byte)) + { + assert (0); + } + break; + case WEBID_OIDC_RANDOM_CONTEXT_WAIT_FILE_WRITE: + if (fseek (seed_file, 0, SEEK_SET) != 0) + { + fprintf (stderr, + _("Could not update the seed file '%s'.\n"), filename); + perror (_("when rewinding the seed file")); + abort (); + } + if (!webid_oidc_random_context_get_wait_bytes + (global_ctx, 0, 0, NULL, &n)) + { + assert (0); + } + for (i = 0; i < n; i++) + { + size_t remaining; + if (!webid_oidc_random_context_get_wait_bytes + (global_ctx, 1, i, &byte, &remaining)) + { + assert (0); + } + assert (i + remaining == n); + fputc (byte, seed_file); + } + if (!webid_oidc_random_context_ok (global_ctx)) + { + assert (0); + } + break; + case WEBID_OIDC_RANDOM_CONTEXT_WAIT_UNLOCK_FILE: + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + error = fcntl (fileno (seed_file), F_SETLKW, &lock); + if (error != 0) + { + fprintf (stderr, _("Could not unlock the seed file '%s'.\n"), + filename); + perror (_("when unlocking the seed file")); + abort (); + } + if (!webid_oidc_random_context_ok (global_ctx)) + { + assert (0); + } + break; + default: + assert (0); + } + } + fclose (seed_file); + fclose (system_rng_file); +} + +void +webid_oidc_random (size_t request_size, uint8_t * data) +{ + static _Thread_local struct webid_oidc_random_context *thrd_ctx = NULL; + if (thrd_ctx == NULL) + { + assert (global_ctx); + thrd_ctx = webid_oidc_random_context_alloc (); + if (thrd_ctx == NULL) + { + fprintf (stderr, + _ + ("Could not set the thread-local random generator up.\n")); + abort (); + } + if (!webid_oidc_random_context_spawn + (global_ctx, thrd_ctx, WEBID_OIDC_RANDOM_CONTEXT_THREAD_UNSAFE)) + { + fprintf (stderr, + _ + ("The random module has not been initialized. Please call webid_oidc_random_init first.\n")); + abort (); + } + } + if (!webid_oidc_random_context_get (thrd_ctx, request_size, data)) + { + assert (0); + } +} diff --git a/src/random/webid-oidc/random.h b/src/random/webid-oidc/random.h new file mode 100644 index 0000000..ae1adfc --- /dev/null +++ b/src/random/webid-oidc/random.h @@ -0,0 +1,57 @@ +#ifndef H_RANDOM_INCLUDED +#define H_RANDOM_INCLUDED + +struct webid_oidc_random_context; +typedef struct webid_oidc_random_context webid_oidc_random_context; + +/* These functions allocate data, they are not reentrant. */ +struct webid_oidc_random_context *webid_oidc_random_context_alloc (void); +void webid_oidc_random_context_free (struct webid_oidc_random_context *ctx); + +enum webid_oidc_random_context_state +{ + WEBID_OIDC_RANDOM_CONTEXT_READY = 0, + WEBID_OIDC_RANDOM_CONTEXT_WAIT_LOCK_FILE, + WEBID_OIDC_RANDOM_CONTEXT_WAIT_FILE_READ, + WEBID_OIDC_RANDOM_CONTEXT_WAIT_FILE_WRITE, + WEBID_OIDC_RANDOM_CONTEXT_WAIT_UNLOCK_FILE +}; + +typedef enum webid_oidc_random_context_state webid_oidc_random_context_state; + +enum webid_oidc_random_context_state +webid_oidc_random_context_get_state (const struct webid_oidc_random_context + *ctx); +int webid_oidc_random_context_get_wait_bytes (const struct + webid_oidc_random_context *ctx, + size_t max, size_t start, + uint8_t * bytes, + size_t *n_bytes); +int webid_oidc_random_context_ok (struct webid_oidc_random_context *ctx); +int webid_oidc_random_context_bytes (struct webid_oidc_random_context *ctx, + size_t n_bytes, const uint8_t * bytes); + +enum webid_oidc_random_context_spawn_flags +{ + WEBID_OIDC_RANDOM_CONTEXT_THREAD_UNSAFE = 1 +}; +typedef enum webid_oidc_random_context_spawn_flags + webid_oidc_random_context_spawn_flags; + +int webid_oidc_random_context_spawn (struct webid_oidc_random_context *parent, + struct webid_oidc_random_context *child, + enum + webid_oidc_random_context_spawn_flags + flags); + +int webid_oidc_random_context_get (struct webid_oidc_random_context *ctx, + size_t request_size, uint8_t * data); + +/* These functions set up and use a global state. Neither are + reentrant. */ +void webid_oidc_random_init (void); + +/* This function is thread-safe. */ +void webid_oidc_random (size_t request_size, uint8_t * data); + +#endif /* not H_RANDOM_INCLUDED */ diff --git a/src/scm/webid-oidc/stubs.scm b/src/scm/webid-oidc/stubs.scm index 12006e6..b022ef2 100644 --- a/src/scm/webid-oidc/stubs.scm +++ b/src/scm/webid-oidc/stubs.scm @@ -15,4 +15,6 @@ (export base64-encode - (fix-base64-decode . base64-decode)) + (fix-base64-decode . base64-decode) + random + random-init!) diff --git a/src/scm/webid-oidc/testing.scm b/src/scm/webid-oidc/testing.scm index 40284cf..d4b7f4d 100644 --- a/src/scm/webid-oidc/testing.scm +++ b/src/scm/webid-oidc/testing.scm @@ -24,4 +24,5 @@ (error->str error)) (raise-exception error)) (lambda () + (random-init!) (f)))) diff --git a/tests/Makefile.am b/tests/Makefile.am index 12699f9..adff911 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,18 +1,19 @@ -TESTS = \ - %reldir%/load-library.scm \ +TESTS = %reldir%/load-library.scm \ %reldir%/base64-ok.scm \ - %reldir%/base64-error.scm + %reldir%/base64-error.scm \ + %reldir%/random.scm EXTRA_DIST += $(TESTS) TEST_EXTENSIONS = .scm -AM_TESTS_ENVIRONMENT = $(top_builddir)/pre-inst-env -SCM_LOG_COMPILER = $(GUILE) -AM_SCM_LOG_FLAGS = --no-auto-compile -s +.PHONY: %canon_reldir%-clean-local clean-local: %canon_reldir%-clean-local %canon_reldir%-clean-local: rm -rf %reldir%/*.cache - rm -rf %reldir%/*.home + +AM_TESTS_ENVIRONMENT = $(top_builddir)/pre-inst-env +SCM_LOG_COMPILER = $(GUILE) +AM_SCM_LOG_FLAGS = --no-auto-compile -s diff --git a/tests/random.scm b/tests/random.scm new file mode 100644 index 0000000..9b85b15 --- /dev/null +++ b/tests/random.scm @@ -0,0 +1,17 @@ +(use-modules (webid-oidc stubs) + (webid-oidc testing)) + +(with-test-environment + "random" + (lambda () + (let ((data (random 12)) + (expected "68OMG_V5x-KmI6TI")) + (unless (string=? data expected) + (format (current-error-port) + "Non-reproducibility issue with the random number generator: +expected: ~a +obtained: ~a +" + expected + data) + (exit 1))))) -- cgit v1.2.3