Add API gpgme_op_random_value.
* src/genrandom.c (getrandom_size_t): New. (gpgme_op_random_value): New. * src/gpgme.def: Add new function. * src/libgpgme.vers: Ditto. * src/gpgme.h.in: Add prototype. * tests/run-genrandom.c: Add an option to use the new function. -- The implementation is not optimized but sufficient for our use case. Possible improvements for this and gpgme_op_random_bytes are a cache for random bytes in the context so that we do not need to get out to gpgme for just a few random bytes. GnuPG-bug-id: 6694
This commit is contained in:
parent
7568566ef3
commit
926b1f1f1e
8
NEWS
8
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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -276,4 +276,5 @@ EXPORTS
|
||||
gpgme_op_setownertrust_start @214
|
||||
|
||||
gpgme_op_random_bytes @215
|
||||
gpgme_op_random_value @216
|
||||
; END
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
@ -274,6 +274,7 @@ GPGME_1.0 {
|
||||
gpgme_op_setownertrust_start;
|
||||
|
||||
gpgme_op_random_bytes;
|
||||
gpgme_op_random_value;
|
||||
|
||||
local:
|
||||
*;
|
||||
|
@ -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;
|
||||
|
||||
if (mode == GPGME_RANDOM_MODE_ZBASE32)
|
||||
puts (buffer);
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
for (i=0; i < sizeof buffer; i++)
|
||||
err = gpgme_op_random_bytes (ctx, mode, buffer, sizeof buffer);
|
||||
if (err)
|
||||
{
|
||||
if (i && !(i%32))
|
||||
putchar ('\n');
|
||||
printf ("%02x", ((unsigned char *)buffer)[i]);
|
||||
fprintf (stderr, PGM ": error getting random: %s\n",
|
||||
gpg_strerror (err));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (mode == GPGME_RANDOM_MODE_ZBASE32)
|
||||
puts (buffer);
|
||||
else
|
||||
{
|
||||
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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user