From cc353701b0fde4c811ddc1e9a91b852dfe9f4e06 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 14 Sep 2016 09:51:16 +0200 Subject: [PATCH] core: New function gpgme_op_createsubkey. * src/genkey.c (createsubkey_start): New. (gpgme_op_createsubkey_start, gpgme_op_createsubkey): New. * src/gpgme.def, src/libgpgme.vers: Add them. * src/engine-gpg.c (gpg_createkey): Factor some code out to ... (gpg_add_algo_usage_expire): new. (gpg_addkey): Implement. * tests/run-genkey.c: Add option --addkey. Signed-off-by: Werner Koch --- NEWS | 2 + doc/gpgme.texi | 2 +- src/engine-gpg.c | 95 +++++++++++++++++++++++++++++++++------------- src/genkey.c | 85 +++++++++++++++++++++++++++++++++++++++++ src/gpgme.def | 2 + src/libgpgme.vers | 2 + tests/run-genkey.c | 41 +++++++++++++++++--- 7 files changed, 196 insertions(+), 33 deletions(-) diff --git a/NEWS b/NEWS index d3639c89..9445f7f9 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,8 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_] gpgme_data_set_flag NEW. gpgme_op_createkey NEW. gpgme_op_createkey_start NEW. + gpgme_op_createsubkey NEW. + gpgme_op_createsubkey_start NEW. gpgme_genkey_result_t EXTENDED: New fields pubkey and seckey. gpgme_signature_t EXTENDED: New field key. gpgme_key_t EXTENDED: New field fpr. diff --git a/doc/gpgme.texi b/doc/gpgme.texi index dfc9548b..ef39d81b 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -3792,7 +3792,7 @@ The function @code{gpgme_op_genkey} generates a new key pair in the context @var{ctx}. The meaning of @var{public} and @var{secret} depends on the crypto backend. -GnuPG does not support @var{public} and @var{secret}, they should be +GPG does not support @var{public} and @var{secret}, they should be @code{NULL}. GnuPG will generate a key pair and add it to the standard key ring. The fingerprint of the generated key is available with @code{gpgme_op_genkey_result}. diff --git a/src/engine-gpg.c b/src/engine-gpg.c index f22d8b44..5a16f805 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -1969,6 +1969,47 @@ gpg_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode, } + +/* Helper to add algo, usage, and expire to the list of args. */ +static gpgme_error_t +gpg_add_algo_usage_expire (engine_gpg_t gpg, + const char *algo, + unsigned long expires, + unsigned int flags) +{ + gpg_error_t err; + + /* This condition is only required to allow the use of gpg < 2.1.16 */ + if (algo + || (flags & (GPGME_CREATE_SIGN | GPGME_CREATE_ENCR + | GPGME_CREATE_CERT | GPGME_CREATE_AUTH)) + || expires) + { + err = add_arg (gpg, algo? algo : "default"); + if (!err) + { + char tmpbuf[5*4+1]; + snprintf (tmpbuf, sizeof tmpbuf, "%s%s%s%s", + (flags & GPGME_CREATE_SIGN)? " sign":"", + (flags & GPGME_CREATE_ENCR)? " encr":"", + (flags & GPGME_CREATE_CERT)? " cert":"", + (flags & GPGME_CREATE_AUTH)? " auth":""); + err = add_arg (gpg, *tmpbuf? tmpbuf : "default"); + } + if (!err && expires) + { + char tmpbuf[8+20]; + snprintf (tmpbuf, sizeof tmpbuf, "seconds=%lu", expires); + err = add_arg (gpg, tmpbuf); + } + } + else + err = 0; + + return err; +} + + static gpgme_error_t gpg_createkey_from_param (engine_gpg_t gpg, gpgme_data_t help_data, int use_armor) @@ -2026,32 +2067,8 @@ gpg_createkey (engine_gpg_t gpg, if (!err) err = add_arg (gpg, userid); - /* This condition is only required to allow the use of gpg < 2.1.16 */ - if (algo - || (flags & (GPGME_CREATE_SIGN | GPGME_CREATE_ENCR - | GPGME_CREATE_CERT | GPGME_CREATE_AUTH)) - || expires) - { - - if (!err) - err = add_arg (gpg, algo? algo : "default"); - if (!err) - { - char tmpbuf[5*4+1]; - snprintf (tmpbuf, sizeof tmpbuf, "%s%s%s%s", - (flags & GPGME_CREATE_SIGN)? " sign":"", - (flags & GPGME_CREATE_ENCR)? " encr":"", - (flags & GPGME_CREATE_CERT)? " cert":"", - (flags & GPGME_CREATE_AUTH)? " auth":""); - err = add_arg (gpg, *tmpbuf? tmpbuf : "default"); - } - if (!err && expires) - { - char tmpbuf[8+20]; - snprintf (tmpbuf, sizeof tmpbuf, "seconds=%lu", expires); - err = add_arg (gpg, tmpbuf); - } - } + if (!err) + err = gpg_add_algo_usage_expire (gpg, algo, expires, flags); if (!err) err = start (gpg); @@ -2067,7 +2084,31 @@ gpg_addkey (engine_gpg_t gpg, unsigned int flags, int use_armor) { - return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + gpgme_error_t err; + + if (!key || !key->fpr) + return gpg_error (GPG_ERR_INV_ARG); + + err = add_arg (gpg, "--quick-addkey"); + if (!err && use_armor) + err = add_arg (gpg, "--armor"); + if (!err && (flags & GPGME_CREATE_NOPASSWD)) + { + err = add_arg (gpg, "--passphrase"); + if (!err) + err = add_arg (gpg, ""); + } + if (!err) + err = add_arg (gpg, "--"); + if (!err) + err = add_arg (gpg, key->fpr); + + if (!err) + err = gpg_add_algo_usage_expire (gpg, algo, expires, flags); + + if (!err) + err = start (gpg); + return err; } diff --git a/src/genkey.c b/src/genkey.c index 0b795f43..26bcca6e 100644 --- a/src/genkey.c +++ b/src/genkey.c @@ -387,3 +387,88 @@ gpgme_op_createkey (gpgme_ctx_t ctx, const char *userid, const char *algo, err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } + + + +static gpgme_error_t +createsubkey_start (gpgme_ctx_t ctx, int synchronous, + gpgme_key_t key, + const char *algo, + unsigned long reserved, unsigned long expires, + unsigned int flags) +{ + gpgme_error_t err; + void *hook; + op_data_t opd; + + if (ctx->protocol != GPGME_PROTOCOL_OPENPGP) + return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + + err = _gpgme_op_reset (ctx, synchronous); + if (err) + return err; + + if (reserved || !key) + return gpg_error (GPG_ERR_INV_VALUE); + + err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, + sizeof (*opd), release_op_data); + opd = hook; + if (err) + return err; + + _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx); + + if (ctx->passphrase_cb) + { + err = _gpgme_engine_set_command_handler + (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); + if (err) + return err; + } + + return _gpgme_engine_op_genkey (ctx->engine, + NULL, algo, reserved, expires, + key, flags, + NULL, ctx->use_armor, NULL, NULL); + +} + + +/* Add a subkey to an existing KEY. */ +gpgme_error_t +gpgme_op_createsubkey_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo, + unsigned long reserved, unsigned long expires, + unsigned int flags) +{ + gpgme_error_t err; + + TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createsubkey_start", ctx, + "key=%p, algo='%s' flags=0x%x", key, algo, flags); + + if (!ctx) + return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); + + err = createsubkey_start (ctx, 0, key, algo, reserved, expires, flags); + return TRACE_ERR (err); +} + + +gpgme_error_t +gpgme_op_createsubkey (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo, + unsigned long reserved, unsigned long expires, + unsigned int flags) +{ + gpgme_error_t err; + + TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createsubkey", ctx, + "key=%p, algo='%s' flags=0x%x", key, algo, flags); + + if (!ctx) + return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); + + err = createsubkey_start (ctx, 1, key, algo, reserved, expires, flags); + if (!err) + err = _gpgme_wait_one (ctx); + return TRACE_ERR (err); +} diff --git a/src/gpgme.def b/src/gpgme.def index a56b9efa..7b7b1f20 100644 --- a/src/gpgme.def +++ b/src/gpgme.def @@ -231,6 +231,8 @@ EXPORTS gpgme_op_createkey_start @172 gpgme_op_createkey @173 + gpgme_op_createsubkey_start @174 + gpgme_op_createsubkey @175 ; END diff --git a/src/libgpgme.vers b/src/libgpgme.vers index b06c9c65..2a3e9fc3 100644 --- a/src/libgpgme.vers +++ b/src/libgpgme.vers @@ -105,6 +105,8 @@ GPGME_1.1 { gpgme_op_createkey_start; gpgme_op_createkey; + gpgme_op_createsubkey_start; + gpgme_op_createsubkey; }; diff --git a/tests/run-genkey.c b/tests/run-genkey.c index 74d4038e..3b645025 100644 --- a/tests/run-genkey.c +++ b/tests/run-genkey.c @@ -201,6 +201,7 @@ show_usage (int ex) { fputs ("usage: " PGM " [options] USERID [ALGO [USAGE [EXPIRESECONDS]]]\n\n" "Options:\n" + " --addkey add a subkey to the key with USERID\n" " --verbose run in verbose mode\n" " --status print status lines from the backend\n" " --progress print progress info\n" @@ -224,6 +225,7 @@ main (int argc, char **argv) int print_status = 0; int print_progress = 0; int use_loopback = 0; + int addkey = 0; const char *userid; const char *algo = NULL; unsigned int flags = 0; @@ -243,6 +245,11 @@ main (int argc, char **argv) } else if (!strcmp (*argv, "--help")) show_usage (0); + else if (!strcmp (*argv, "--addkey")) + { + addkey = 1; + argc--; argv++; + } else if (!strcmp (*argv, "--verbose")) { verbose = 1; @@ -316,12 +323,36 @@ main (int argc, char **argv) gpgme_set_passphrase_cb (ctx, passphrase_cb, NULL); } - err = gpgme_op_createkey (ctx, userid, algo, 0, expire, NULL, flags); - if (err) + if (addkey) { - fprintf (stderr, PGM ": gpgme_op_createkey failed: %s\n", - gpg_strerror (err)); - exit (1); + gpgme_key_t akey; + + err = gpgme_get_key (ctx, userid, &akey, 1); + if (err) + { + fprintf (stderr, PGM ": error getting secret key for '%s': %s\n", + userid, gpg_strerror (err)); + exit (1); + } + + err = gpgme_op_createsubkey (ctx, akey, algo, 0, expire, flags); + if (err) + { + fprintf (stderr, PGM ": gpgme_op_createsubkey failed: %s\n", + gpg_strerror (err)); + exit (1); + } + gpgme_key_unref (akey); + } + else + { + err = gpgme_op_createkey (ctx, userid, algo, 0, expire, NULL, flags); + if (err) + { + fprintf (stderr, PGM ": gpgme_op_createkey failed: %s\n", + gpg_strerror (err)); + exit (1); + } } result = gpgme_op_genkey_result (ctx);