diff options
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | doc/gpgme.texi | 56 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/engine-assuan.c | 1 | ||||
-rw-r--r-- | src/engine-backend.h | 3 | ||||
-rw-r--r-- | src/engine-g13.c | 1 | ||||
-rw-r--r-- | src/engine-gpg.c | 24 | ||||
-rw-r--r-- | src/engine-gpgconf.c | 1 | ||||
-rw-r--r-- | src/engine-gpgsm.c | 1 | ||||
-rw-r--r-- | src/engine-spawn.c | 1 | ||||
-rw-r--r-- | src/engine-uiserver.c | 1 | ||||
-rw-r--r-- | src/engine.c | 20 | ||||
-rw-r--r-- | src/engine.h | 4 | ||||
-rw-r--r-- | src/genrandom.c | 149 | ||||
-rw-r--r-- | src/gpgme.def | 2 | ||||
-rw-r--r-- | src/gpgme.h.in | 12 | ||||
-rw-r--r-- | src/libgpgme.vers | 2 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/run-genrandom.c | 125 |
19 files changed, 411 insertions, 2 deletions
@@ -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; +} |