aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS6
-rw-r--r--doc/gpgme.texi56
-rw-r--r--src/Makefile.am2
-rw-r--r--src/engine-assuan.c1
-rw-r--r--src/engine-backend.h3
-rw-r--r--src/engine-g13.c1
-rw-r--r--src/engine-gpg.c24
-rw-r--r--src/engine-gpgconf.c1
-rw-r--r--src/engine-gpgsm.c1
-rw-r--r--src/engine-spawn.c1
-rw-r--r--src/engine-uiserver.c1
-rw-r--r--src/engine.c20
-rw-r--r--src/engine.h4
-rw-r--r--src/genrandom.c149
-rw-r--r--src/gpgme.def2
-rw-r--r--src/gpgme.h.in12
-rw-r--r--src/libgpgme.vers2
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/run-genrandom.c125
19 files changed, 411 insertions, 2 deletions
diff --git a/NEWS b/NEWS
index 4505b57c..481da3a0 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,9 @@
Noteworthy changes in version 2.0.0 (unreleased)
------------------------------------------------
+ * New function gpgme_op_random_bytes to get cryptographically strng
+ random bytes from gpg.
+
* Removed the gpgme_attr_t enums and their functions which were
deprecated since 2003. [rMd54d6eaa64]
@@ -12,6 +15,9 @@ Noteworthy changes in version 2.0.0 (unreleased)
* Interface changes relative to the 1.24 branch:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ gpgme_op_random_bytes NEW.
+ GPGME_RANDOM_MODE_NORMAL NEW.
+ GPGME_RANDOM_MODE_ZBASE32 NEW.
gpgme_attr_t REMOVED.
gpgme_get_sig_ulong_attr REMOVED.
gpgme_get_sig_string_attr REMOVED.
diff --git a/doc/gpgme.texi b/doc/gpgme.texi
index 9add4ff4..a586631e 100644
--- a/doc/gpgme.texi
+++ b/doc/gpgme.texi
@@ -223,6 +223,7 @@ Crypto Operations
* Decrypt and Verify:: Decrypting a signed ciphertext.
* Sign:: Creating a signature.
* Encrypt:: Encrypting a plaintext.
+* Random:: Getting strong random bytes.
Sign
@@ -5747,6 +5748,7 @@ An error code describing the reason why the key was found invalid.
* Decrypt and Verify:: Decrypting a signed ciphertext.
* Sign:: Creating a signature.
* Encrypt:: Encrypting a plaintext.
+* Random:: Getting strong random bytes.
@end menu
@@ -7092,6 +7094,60 @@ backend engine.
@end deftypefun
+@node Random
+@subsection Random
+@cindex random bytes
+@cindex cryptographic operation, random
+
+GPGME provides a simple interface to get cryptographic strong random
+numbers from Libgcrypt via the GPG engine.
+
+@menu
+* Getting Random:: How to get random bytes.
+@end menu
+
+
+@node Getting Random
+@subsubsection How to get random bytes
+
+@deftp {Data type} {enum gpgme_random_mode_t}
+@tindex gpgme_random_mode_t
+
+This enum ist used to select special modes of the random generator.
+
+@table @code
+@item GPGME_RANDOM_MODE_NORMAL
+This is the standard mode, you may also use 0 instead of this enum
+value.
+
+@item GPGME_RANDOM_MODE_ZBASE32
+This mode is used to tell the random function to return a 30 character
+string with random characters from the zBase32 set of characters. The
+returned string will be terminated by a Nul.
+@end table
+@end deftp
+
+
+@deftypefun {gpgme_error_t} gpgme_op_random_bytes ( @
+ @w{gpgme_ctx_t @var{ctx}}, @
+ @w{gpgme_random_mode_t @var{mode}}, @
+ @w{char *@var{buffer}}, @
+ @w{size_t @var{bufsize}})
+
+@since{2.0.0}
+
+The function @code{gpgme_op_random_bytes} returns random bytes.
+@var{buffer} must be provided by the caller with a size of
+@var{BUFSIZE} and will on return be filled with random bytes retrieved
+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
+
+@end deftypefun
+
+
@node Miscellaneous
@section Miscellaneous operations
diff --git a/src/Makefile.am b/src/Makefile.am
index 93de55b9..50f4bb4f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -85,7 +85,7 @@ main_sources = \
key.c keylist.c keysign.c tofupolicy.c \
revsig.c \
import.c export.c genkey.c delete.c edit.c getauditlog.c \
- setexpire.c setownertrust.c \
+ setexpire.c setownertrust.c genrandom.c \
opassuan.c passwd.c spawn.c assuan-support.c \
engine.h engine-backend.h engine.c engine-gpg.c status-table.c \
engine-gpgsm.c engine-assuan.c engine-gpgconf.c \
diff --git a/src/engine-assuan.c b/src/engine-assuan.c
index ee28740d..e96313ff 100644
--- a/src/engine-assuan.c
+++ b/src/engine-assuan.c
@@ -841,6 +841,7 @@ struct engine_ops _gpgme_engine_ops_assuan =
NULL, /* setexpire */
NULL, /* setownertrust */
llass_transact, /* opassuan_transact */
+ NULL, /* getdirect */
NULL, /* conf_load */
NULL, /* conf_save */
NULL, /* conf_dir */
diff --git a/src/engine-backend.h b/src/engine-backend.h
index d889d1a7..b0533f77 100644
--- a/src/engine-backend.h
+++ b/src/engine-backend.h
@@ -140,6 +140,9 @@ struct engine_ops
gpgme_assuan_status_cb_t status_cb,
void *status_cb_value);
+ gpgme_error_t (*getdirect) (void *engine, const char *argv[],
+ gpgme_data_t dataout, unsigned int flags);
+
gpgme_error_t (*conf_load) (void *engine, gpgme_conf_comp_t *conf_p);
gpgme_error_t (*conf_save) (void *engine, gpgme_conf_comp_t conf);
gpgme_error_t (*conf_dir) (void *engine, const char *what, char **result);
diff --git a/src/engine-g13.c b/src/engine-g13.c
index 9852be88..e30c1834 100644
--- a/src/engine-g13.c
+++ b/src/engine-g13.c
@@ -812,6 +812,7 @@ struct engine_ops _gpgme_engine_ops_g13 =
NULL, /* setexpire */
NULL, /* setownertrust */
g13_transact,
+ NULL, /* getdirect */
NULL, /* conf_load */
NULL, /* conf_save */
NULL, /* conf_dir */
diff --git a/src/engine-gpg.c b/src/engine-gpg.c
index 0768ea74..66303263 100644
--- a/src/engine-gpg.c
+++ b/src/engine-gpg.c
@@ -4173,6 +4173,29 @@ gpg_setownertrust (void *engine, gpgme_key_t key, const char *value)
}
+static gpgme_error_t
+gpg_getdirect (void *engine, const char *argv[],
+ gpgme_data_t dataout, unsigned int flags)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+ int i;
+
+ if (!engine || !argv || !dataout || flags)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ for (i=0; !err && argv[i]; i++)
+ if ((err = add_arg (gpg, argv[i])))
+ return err;
+
+ err = add_data (gpg, dataout, 1, 1);
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
struct engine_ops _gpgme_engine_ops_gpg =
{
@@ -4214,6 +4237,7 @@ struct engine_ops _gpgme_engine_ops_gpg =
gpg_setexpire,
gpg_setownertrust,
NULL, /* opassuan_transact */
+ gpg_getdirect,
NULL, /* conf_load */
NULL, /* conf_save */
NULL, /* conf_dir */
diff --git a/src/engine-gpgconf.c b/src/engine-gpgconf.c
index 03999ddd..d1b977ad 100644
--- a/src/engine-gpgconf.c
+++ b/src/engine-gpgconf.c
@@ -1312,6 +1312,7 @@ struct engine_ops _gpgme_engine_ops_gpgconf =
NULL, /* setexpire */
NULL, /* setownertrust */
NULL, /* opassuan_transact */
+ NULL, /* getdirect */
gpgconf_conf_load,
gpgconf_conf_save,
gpgconf_conf_dir,
diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c
index dfc51128..e9d69d7a 100644
--- a/src/engine-gpgsm.c
+++ b/src/engine-gpgsm.c
@@ -2446,6 +2446,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
NULL, /* setexpire */
NULL, /* setownertrust */
NULL, /* opassuan_transact */
+ NULL, /* getdirect */
NULL, /* conf_load */
NULL, /* conf_save */
NULL, /* conf_dir */
diff --git a/src/engine-spawn.c b/src/engine-spawn.c
index a6280cde..7fdac9ca 100644
--- a/src/engine-spawn.c
+++ b/src/engine-spawn.c
@@ -472,6 +472,7 @@ struct engine_ops _gpgme_engine_ops_spawn =
NULL, /* setexpire */
NULL, /* setownertrust */
NULL, /* opassuan_transact */
+ NULL, /* getdirect */
NULL, /* conf_load */
NULL, /* conf_save */
NULL, /* conf_dir */
diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c
index cdde41fd..6c89082e 100644
--- a/src/engine-uiserver.c
+++ b/src/engine-uiserver.c
@@ -1455,6 +1455,7 @@ struct engine_ops _gpgme_engine_ops_uiserver =
NULL, /* setexpire */
NULL, /* setownertrust */
NULL, /* opassuan_transact */
+ NULL, /* getdirect */
NULL, /* conf_load */
NULL, /* conf_save */
NULL, /* conf_dir */
diff --git a/src/engine.c b/src/engine.c
index 516bc3d1..d341488e 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -987,6 +987,26 @@ _gpgme_engine_op_assuan_transact (engine_t engine,
}
+/* Direct invocation of the engine's tool.
+ *
+ * For example with argv[0] = "--gen-random" and argv[1] = "30" the
+ * gpg engine puts 30 bytes zbase32 encoded random into DATAOUT.
+ * FLAGS must be passed as 0 for now.
+ */
+gpgme_error_t
+_gpgme_engine_op_getdirect (engine_t engine, const char *argv[],
+ gpgme_data_t dataout, unsigned int flags)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->getdirect)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->getdirect) (engine->engine, argv, dataout, flags);
+}
+
+
gpgme_error_t
_gpgme_engine_op_conf_load (engine_t engine, gpgme_conf_comp_t *conf_p)
{
diff --git a/src/engine.h b/src/engine.h
index 8c2d03ca..7befec90 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -185,6 +185,10 @@ gpgme_error_t _gpgme_engine_op_assuan_transact
gpgme_assuan_status_cb_t status_cb,
void *status_cb_value);
+gpgme_error_t _gpgme_engine_op_getdirect (engine_t engine, const char *argv[],
+ gpgme_data_t dataout,
+ unsigned int flags);
+
gpgme_error_t _gpgme_engine_op_conf_load (engine_t engine,
gpgme_conf_comp_t *conf_p);
gpgme_error_t _gpgme_engine_op_conf_save (engine_t engine,
diff --git a/src/genrandom.c b/src/genrandom.c
new file mode 100644
index 00000000..a4baa0b2
--- /dev/null
+++ b/src/genrandom.c
@@ -0,0 +1,149 @@
+/* genrandom.c - Wrapper around gpg --gen-random
+ * Copyright (C) 2025 g10 Code GmbH
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <https://gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <assert.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+
+
+static gpgme_error_t
+do_genrandom (gpgme_ctx_t ctx, gpgme_data_t dataout, size_t length, int zbase)
+{
+ gpgme_error_t err;
+ const char *argv[4];
+ char countbuf[35];
+
+ if (ctx->protocol != GPGME_PROTOCOL_OPENPGP)
+ return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+
+ err = _gpgme_op_reset (ctx, 1/*synchronous*/);
+ if (err)
+ return err;
+
+ snprintf (countbuf, sizeof countbuf, "%zu", length);
+ argv[0] = "--gen-random";
+ argv[1] = zbase? "30" : "2";
+ argv[2] = countbuf;
+ argv[3] = NULL;
+
+ err = _gpgme_engine_op_getdirect (ctx->engine, argv, dataout, 0);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+
+ 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
+ * by a Nul; the remainder of the buffer is not changed. In all other
+ * modes the entire buffer will be filled with binary data. The
+ * function has a limit of 1024 bytes to avoid accidental overuse of
+ * the random generator. */
+gpgme_error_t
+gpgme_op_random_bytes (gpgme_ctx_t ctx, gpgme_random_mode_t mode,
+ char *buffer, size_t bufsize)
+{
+ gpgme_error_t err = 0;
+ gpgme_data_t data = NULL;
+ char *datap = NULL;
+ size_t datalen;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_random_bytes", ctx, "mode=%d size=%zu",
+ mode, bufsize);
+
+ if (!ctx || !buffer || !bufsize)
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ else if (mode == GPGME_RANDOM_MODE_ZBASE32)
+ {
+ /* The output is expected to be 30 ascii characters followed by
+ * a trailing Nul. */
+ if (bufsize < 31)
+ err = gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
+ }
+ else if (mode)
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ else if (bufsize > 1024) /* More or an less arbitrary limit. */
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+
+ if (err)
+ goto leave;
+
+ err = gpgme_data_new (&data);
+ if (err)
+ goto leave;
+
+ err = do_genrandom (ctx, data, bufsize, (mode == GPGME_RANDOM_MODE_ZBASE32));
+ 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 > bufsize)
+ {
+ err = gpg_error (GPG_ERR_INTERNAL);
+ goto leave;
+ }
+ if (mode == GPGME_RANDOM_MODE_ZBASE32)
+ {
+ /* Strip trailing LF. */
+ while (datalen
+ && (datap[datalen-1] == '\n' || datap[datalen-1] == '\r'))
+ datalen--;
+
+ if (datalen != 30)
+ {
+ /* 30 is the holy count, not 29, not 31 and never 32. */
+ err = gpg_error (GPG_ERR_INTERNAL);
+ goto leave;
+ }
+ memcpy (buffer, datap, datalen);
+ buffer[datalen] = 0;
+ }
+ else
+ {
+ if (datalen != bufsize)
+ {
+ err = gpg_error (GPG_ERR_INTERNAL);
+ goto leave;
+ }
+ memcpy (buffer, datap, datalen);
+ }
+
+ leave:
+ free (datap);
+ gpgme_data_release (data);
+ return TRACE_ERR (err);
+}
diff --git a/src/gpgme.def b/src/gpgme.def
index 5a7f430a..d203901f 100644
--- a/src/gpgme.def
+++ b/src/gpgme.def
@@ -274,4 +274,6 @@ EXPORTS
gpgme_op_setownertrust @213
gpgme_op_setownertrust_start @214
+
+ gpgme_op_random_bytes @215
; END
diff --git a/src/gpgme.h.in b/src/gpgme.h.in
index 52b15971..9c0c7977 100644
--- a/src/gpgme.h.in
+++ b/src/gpgme.h.in
@@ -2473,6 +2473,18 @@ gpgme_error_t gpgme_op_query_swdb (gpgme_ctx_t ctx,
gpgme_query_swdb_result_t gpgme_op_query_swdb_result (gpgme_ctx_t ctx);
+/* Mode values for gpgme_op_get_random_bytes. */
+typedef enum
+ {
+ GPGME_RANDOM_MODE_NORMAL = 0,
+ GPGME_RANDOM_MODE_ZBASE32 = 1
+ }
+gpgme_random_mode_t;
+
+/* Fill BUFFER with BUFSIZE random bytes from gpg. */
+gpgme_error_t gpgme_op_random_bytes (gpgme_ctx_t ctx, gpgme_random_mode_t mode,
+ char *buffer, size_t bufsize);
+
/*
diff --git a/src/libgpgme.vers b/src/libgpgme.vers
index 3b7849bb..83f8c87b 100644
--- a/src/libgpgme.vers
+++ b/src/libgpgme.vers
@@ -273,6 +273,8 @@ GPGME_1.0 {
gpgme_op_setownertrust;
gpgme_op_setownertrust_start;
+ gpgme_op_random_bytes;
+
local:
*;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0147b4b7..d2e01dc0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -42,7 +42,7 @@ noinst_HEADERS = run-support.h
noinst_PROGRAMS = $(TESTS) run-keylist run-export run-import run-sign \
run-verify run-encrypt run-identify run-decrypt run-genkey \
run-keysign run-tofu run-swdb run-threaded \
- run-receive-keys run-setownertrust
+ run-receive-keys run-setownertrust run-genrandom
run_threaded_CPPFLAGS = -I$(top_builddir)/src @GPG_ERROR_MT_CFLAGS@
run_threaded_LDADD = ../src/libgpgme.la \
diff --git a/tests/run-genrandom.c b/tests/run-genrandom.c
new file mode 100644
index 00000000..18b8c7ab
--- /dev/null
+++ b/tests/run-genrandom.c
@@ -0,0 +1,125 @@
+/* run-genrandom.c - Test tool for the genrandom function
+ * Copyright (C) 2025 g10 Code GmbH
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <https://gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+/* We need to include config.h so that we know whether we are building
+ with large file system (LFS) support. */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <gpgme.h>
+
+#define PGM "run-genrandom"
+
+#include "run-support.h"
+
+
+static int verbose;
+
+
+static int
+show_usage (int ex)
+{
+ fputs ("usage: " PGM " [options]\n\n"
+ "Options:\n"
+ " --verbose run in verbose mode\n"
+ " --zbase32 generate 30 zbase32 characters\n"
+ , stderr);
+ exit (ex);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int last_argc = -1;
+ gpgme_error_t err;
+ gpgme_ctx_t ctx;
+ gpgme_protocol_t protocol = GPGME_PROTOCOL_OPENPGP;
+ gpgme_random_mode_t mode = 0;
+ char buffer[128];
+
+
+ if (argc)
+ { argc--; argv++; }
+
+ while (argc && last_argc != argc )
+ {
+ last_argc = argc;
+ if (!strcmp (*argv, "--"))
+ {
+ argc--; argv++;
+ break;
+ }
+ else if (!strcmp (*argv, "--help"))
+ show_usage (0);
+ else if (!strcmp (*argv, "--verbose"))
+ {
+ verbose = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--zbase32"))
+ {
+ mode = GPGME_RANDOM_MODE_ZBASE32;
+ argc--; argv++;
+ }
+ else if (!strncmp (*argv, "--", 2))
+ show_usage (1);
+ }
+
+ if (argc)
+ show_usage (1);
+
+ init_gpgme (protocol);
+
+ err = gpgme_new (&ctx);
+ fail_if_err (err);
+ gpgme_set_protocol (ctx, protocol);
+
+ 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);
+ }
+
+ 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');
+ }
+
+ gpgme_release (ctx);
+ return 0;
+}