aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2025-03-14 12:03:46 +0000
committerWerner Koch <[email protected]>2025-03-14 12:08:37 +0000
commit926b1f1f1e3e382f08cc57fc86d5892649514007 (patch)
treeddc1bab10152c7de64a9c4167620f0683eac4a7f /src
parentm4: Update libassuan.m4. (diff)
downloadgpgme-926b1f1f1e3e382f08cc57fc86d5892649514007.tar.gz
gpgme-926b1f1f1e3e382f08cc57fc86d5892649514007.zip
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
Diffstat (limited to 'src')
-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
4 files changed, 91 insertions, 1 deletions
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:
*;