aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS8
-rw-r--r--doc/gpgme.texi19
-rw-r--r--src/genrandom.c83
-rw-r--r--src/gpgme.def1
-rw-r--r--src/gpgme.h.in7
-rw-r--r--src/libgpgme.vers1
-rw-r--r--tests/run-genrandom.c83
7 files changed, 182 insertions, 20 deletions
diff --git a/NEWS b/NEWS
index 481da3a0..7544d42c 100644
--- a/NEWS
+++ b/NEWS
@@ -1,8 +1,11 @@
Noteworthy changes in version 2.0.0 (unreleased)
------------------------------------------------
- * New function gpgme_op_random_bytes to get cryptographically strng
- random bytes from gpg.
+ * New function gpgme_op_random_bytes to get cryptographically
+ strong random bytes from gpg. [T6694]
+
+ * New function gpgme_op_random_value to get a cryptographically
+ strong unsigned integer random value. [T6694]
* Removed the gpgme_attr_t enums and their functions which were
deprecated since 2003. [rMd54d6eaa64]
@@ -16,6 +19,7 @@ Noteworthy changes in version 2.0.0 (unreleased)
* Interface changes relative to the 1.24 branch:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgme_op_random_bytes NEW.
+ gpgme_op_random_value NEW.
GPGME_RANDOM_MODE_NORMAL NEW.
GPGME_RANDOM_MODE_ZBASE32 NEW.
gpgme_attr_t REMOVED.
diff --git a/doc/gpgme.texi b/doc/gpgme.texi
index a586631e..fa93083c 100644
--- a/doc/gpgme.texi
+++ b/doc/gpgme.texi
@@ -7142,11 +7142,26 @@ The function @code{gpgme_op_random_bytes} returns random bytes.
from gpg. However, if @var{mode} is @code{GPGME_RANDOM_MODE_ZBASE32}
@var{bufsize} needs to be at least 31 and will be filled with a string
of 30 ASCII characters followed by a Nul; the remainder of the buffer
-is not changed. This function has a limit of 1024 bytes to avoid
-accidental overuse of the random generator
+is not changed. The caller must provide a context @var{ctx}
+initialized for GPGME_PROTOCOL_OPENPGP. This function has a limit of
+1024 bytes to avoid accidental overuse of the random generator
@end deftypefun
+@deftypefun {gpgme_error_t} gpgme_op_random_values ( @
+ @w{gpgme_ctx_t @var{ctx}}, @
+ @w{size_t @var{limit}}, @
+ @w{size_t *@var{retval}})
+
+@since{2.0.0}
+
+The function @code{gpgme_op_random_value} returns an unbiased random
+value in the range 0 <= value < @var{limit}. The value is returned at
+@var{retval} if and only if the function returns with success. The
+caller must also provide a context @var{ctx} initialized for
+GPGME_PROTOCOL_OPENPGP.
+@end deftypefun
+
@node Miscellaneous
diff --git a/src/genrandom.c b/src/genrandom.c
index a4baa0b2..0f787cf4 100644
--- a/src/genrandom.c
+++ b/src/genrandom.c
@@ -58,6 +58,46 @@ do_genrandom (gpgme_ctx_t ctx, gpgme_data_t dataout, size_t length, int zbase)
}
+/* Store a random value into RETVAL or return an error. */
+static gpgme_error_t
+getrandom_size_t (gpgme_ctx_t ctx, size_t *retval)
+{
+ gpgme_error_t err = 0;
+ gpgme_data_t data = NULL;
+ char *datap = NULL;
+ size_t datalen;
+
+ err = gpgme_data_new (&data);
+ if (err)
+ goto leave;
+
+ err = do_genrandom (ctx, data, sizeof (size_t), 0);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ if (err)
+ goto leave;
+
+ datap = gpgme_data_release_and_get_mem (data, &datalen);
+ data = NULL;
+ if (!datap)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (datalen != sizeof (size_t))
+ {
+ err = gpg_error (GPG_ERR_INTERNAL);
+ goto leave;
+ }
+ memcpy (retval, datap, datalen);
+
+ leave:
+ free (datap);
+ gpgme_data_release (data);
+ return err;
+}
+
+
/* Fill BUFFER of size BUFSIZE with random bytes retrieved from gpg.
* If GPGME_RANDOM_MODE_ZBASE32 is used BUFSIZE needs to be at least
* 31 and will be filled with a string of 30 ascii characters followed
@@ -147,3 +187,46 @@ gpgme_op_random_bytes (gpgme_ctx_t ctx, gpgme_random_mode_t mode,
gpgme_data_release (data);
return TRACE_ERR (err);
}
+
+
+
+/* On success the function stores an unbiased random value in the
+ * range [0,limit) at RETVAL. On error the function returns an error
+ * code and the value at RETVAL is undefined. A context must be
+ * provided so that the function can get raw random bytes from the gpg
+ * enine (i.e. from Libgcrypt). */
+gpgme_error_t
+gpgme_op_random_value (gpgme_ctx_t ctx, size_t limit, size_t *retval)
+{
+ gpgme_error_t err;
+ size_t t, x;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_random_value", ctx, "limit=%zu", limit);
+
+ if (!ctx || limit < 2 || !retval)
+ {
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+
+ /* This is the OpenBSD algorithm as used by arc4random_uniform and
+ * described in "Fast Random Integer Generation in an Interval" by
+ * Daniel Lemire (arXiv:1805.10941v4, 2018). */
+ t = (-limit) % limit;
+ x = 0; /* Avoid (false) compiler warning. */
+ do
+ {
+ err = getrandom_size_t (ctx, &x);
+ if (err)
+ goto leave;
+ /* fprintf (stderr, "getrandom returned %zu (limit=%zu, t=%zu)\n", */
+ /* x, limit, t); */
+ }
+ while (x < t);
+ x %= limit;
+
+ *retval = x;
+
+ leave:
+ return TRACE_ERR (err);
+}
diff --git a/src/gpgme.def b/src/gpgme.def
index d203901f..3bdaac4e 100644
--- a/src/gpgme.def
+++ b/src/gpgme.def
@@ -276,4 +276,5 @@ EXPORTS
gpgme_op_setownertrust_start @214
gpgme_op_random_bytes @215
+ gpgme_op_random_value @216
; END
diff --git a/src/gpgme.h.in b/src/gpgme.h.in
index 9c0c7977..705d9741 100644
--- a/src/gpgme.h.in
+++ b/src/gpgme.h.in
@@ -2481,10 +2481,15 @@ typedef enum
}
gpgme_random_mode_t;
-/* Fill BUFFER with BUFSIZE random bytes from gpg. */
+/* Fill BUFFER with BUFSIZE random bytes from gpg or return an error. */
gpgme_error_t gpgme_op_random_bytes (gpgme_ctx_t ctx, gpgme_random_mode_t mode,
char *buffer, size_t bufsize);
+/* Store an unbiased random value in the range [0,LIMIT) at RETVAL or
+ * return an error. */
+gpgme_error_t gpgme_op_random_value (gpgme_ctx_t ctx, size_t limit,
+ size_t *retval);
+
/*
diff --git a/src/libgpgme.vers b/src/libgpgme.vers
index 83f8c87b..555f9fc0 100644
--- a/src/libgpgme.vers
+++ b/src/libgpgme.vers
@@ -274,6 +274,7 @@ GPGME_1.0 {
gpgme_op_setownertrust_start;
gpgme_op_random_bytes;
+ gpgme_op_random_value;
local:
*;
diff --git a/tests/run-genrandom.c b/tests/run-genrandom.c
index 18b8c7ab..f28ef299 100644
--- a/tests/run-genrandom.c
+++ b/tests/run-genrandom.c
@@ -28,6 +28,8 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
#include <gpgme.h>
@@ -42,10 +44,13 @@ static int verbose;
static int
show_usage (int ex)
{
- fputs ("usage: " PGM " [options]\n\n"
+ fputs ("usage: " PGM " [options] [LIMIT]\n\n"
"Options:\n"
" --verbose run in verbose mode\n"
" --zbase32 generate 30 zbase32 characters\n"
+ " --hex return a hex value in LIMIT mode\n"
+ "\n"
+ "With LIMIT return a decimal value in the range [0,LIMIT)\n"
, stderr);
exit (ex);
}
@@ -60,6 +65,8 @@ main (int argc, char **argv)
gpgme_protocol_t protocol = GPGME_PROTOCOL_OPENPGP;
gpgme_random_mode_t mode = 0;
char buffer[128];
+ int hexmode = 0;
+ int valuemode = 0;
if (argc)
@@ -85,11 +92,21 @@ main (int argc, char **argv)
mode = GPGME_RANDOM_MODE_ZBASE32;
argc--; argv++;
}
+ else if (!strcmp (*argv, "--hex"))
+ {
+ hexmode = 1;
+ argc--; argv++;
+ }
else if (!strncmp (*argv, "--", 2))
show_usage (1);
}
- if (argc)
+ if (argc == 1)
+ valuemode = 1;
+ else if (argc)
+ show_usage (1);
+
+ if ((valuemode && mode) || (!valuemode && hexmode))
show_usage (1);
init_gpgme (protocol);
@@ -98,26 +115,62 @@ main (int argc, char **argv)
fail_if_err (err);
gpgme_set_protocol (ctx, protocol);
- err = gpgme_op_random_bytes (ctx, mode, buffer, sizeof buffer);
- if (err)
+ if (valuemode)
{
- fprintf (stderr, PGM ": error getting random: %s\n", gpg_strerror (err));
- exit (1);
- }
+ size_t limit, value;
+
+ errno = 0;
+ limit = strtoul (*argv, NULL, 0);
+ if (errno)
+ {
+ fprintf (stderr, PGM ": error parsing LIMIT arg: %s\n",
+ strerror (errno));
+ exit (1);
+ }
+ if (limit > SIZE_MAX)
+ {
+ fprintf (stderr, PGM ": error parsing LIMIT arg: %s\n",
+ "too large for size_t");
+ exit (1);
+ }
- if (mode == GPGME_RANDOM_MODE_ZBASE32)
- puts (buffer);
+ err = gpgme_op_random_value (ctx, limit, &value);
+ if (err)
+ {
+ fprintf (stderr, PGM ": error getting random: %s\n",
+ gpg_strerror (err));
+ exit (1);
+ }
+
+ if (hexmode)
+ printf ("%zx\n", value);
+ else
+ printf ("%zu\n", value);
+ }
else
{
- int i;
+ err = gpgme_op_random_bytes (ctx, mode, buffer, sizeof buffer);
+ if (err)
+ {
+ fprintf (stderr, PGM ": error getting random: %s\n",
+ gpg_strerror (err));
+ exit (1);
+ }
- for (i=0; i < sizeof buffer; i++)
+ if (mode == GPGME_RANDOM_MODE_ZBASE32)
+ puts (buffer);
+ else
{
- if (i && !(i%32))
- putchar ('\n');
- printf ("%02x", ((unsigned char *)buffer)[i]);
+ int i;
+
+ for (i=0; i < sizeof buffer; i++)
+ {
+ if (i && !(i%32))
+ putchar ('\n');
+ printf ("%02x", ((unsigned char *)buffer)[i]);
+ }
+ putchar ('\n');
}
- putchar ('\n');
}
gpgme_release (ctx);