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 <wk@gnupg.org>
This commit is contained in:
Werner Koch 2016-09-14 09:51:16 +02:00
parent 51f9acbca9
commit cc353701b0
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
7 changed files with 196 additions and 33 deletions

2
NEWS
View File

@ -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.

View File

@ -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}.

View File

@ -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;
}

View File

@ -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);
}

View File

@ -231,6 +231,8 @@ EXPORTS
gpgme_op_createkey_start @172
gpgme_op_createkey @173
gpgme_op_createsubkey_start @174
gpgme_op_createsubkey @175
; END

View File

@ -105,6 +105,8 @@ GPGME_1.1 {
gpgme_op_createkey_start;
gpgme_op_createkey;
gpgme_op_createsubkey_start;
gpgme_op_createsubkey;
};

View File

@ -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);