diff --git a/NEWS b/NEWS
index da331b4b..d3639c89 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,9 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
GPGME_PK_EDDSA NEW.
gpgme_set_ctx_flag NEW.
gpgme_data_set_flag NEW.
+ gpgme_op_createkey NEW.
+ gpgme_op_createkey_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.
gpgme_subkey_t EXTENDED: New field keygrip.
@@ -31,6 +34,16 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
GPGME_DATA_TYPE_PGP_SIGNATURE NEW.
GPGME_DATA_ENCODING_MIME NEW.
GPGME_ENCRYPT_SYMMETRIC NEW.
+ GPGME_CREATE_SIGN NEW.
+ GPGME_CREATE_ENCR NEW.
+ GPGME_CREATE_CERT NEW.
+ GPGME_CREATE_AUTH NEW.
+ GPGME_CREATE_NOPASSWD NEW.
+ GPGME_CREATE_SELFSIGNED NEW.
+ GPGME_CREATE_NOSTORE NEW.
+ GPGME_CREATE_WANTPUB NEW.
+ GPGME_CREATE_WANTSEC NEW.
+ GPGME_CREATE_FORCE NEW.
Noteworthy changes in version 1.6.0 (2015-08-26) [C25/A14/R0]
diff --git a/src/engine-backend.h b/src/engine-backend.h
index a15194e6..842292dd 100644
--- a/src/engine-backend.h
+++ b/src/engine-backend.h
@@ -82,7 +82,11 @@ struct engine_ops
gpgme_error_t (*export_ext) (void *engine, const char *pattern[],
gpgme_export_mode_t mode, gpgme_data_t keydata,
int use_armor);
- gpgme_error_t (*genkey) (void *engine, gpgme_data_t help_data, int use_armor,
+ gpgme_error_t (*genkey) (void *engine,
+ const char *userid, const char *algo,
+ unsigned long reserved, unsigned long expires,
+ gpgme_key_t key, unsigned int flags,
+ gpgme_data_t help_data, int use_armor,
gpgme_data_t pubkey, gpgme_data_t seckey);
gpgme_error_t (*import) (void *engine, gpgme_data_t keydata,
gpgme_key_t *keyarray);
diff --git a/src/engine-gpg.c b/src/engine-gpg.c
index 3f77ba87..289578b0 100644
--- a/src/engine-gpg.c
+++ b/src/engine-gpg.c
@@ -1964,22 +1964,11 @@ gpg_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
static gpgme_error_t
-gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
- gpgme_data_t pubkey, gpgme_data_t seckey)
+gpg_createkey_from_param (engine_gpg_t gpg,
+ gpgme_data_t help_data, int use_armor)
{
- engine_gpg_t gpg = engine;
gpgme_error_t err;
- if (!gpg)
- return gpg_error (GPG_ERR_INV_VALUE);
-
- /* We need a special mechanism to get the fd of a pipe here, so that
- we can use this for the %pubring and %secring parameters. We
- don't have this yet, so we implement only the adding to the
- standard keyrings. */
- if (pubkey || seckey)
- return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-
err = add_arg (gpg, "--gen-key");
if (!err && use_armor)
err = add_arg (gpg, "--armor");
@@ -1987,9 +1976,156 @@ gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, help_data, -1, 0);
+ if (!err)
+ err = start (gpg);
+ return err;
+}
+
+
+/* This is used for gpg versions which do not support the quick-genkey
+ * command to emulate the gpgme_op_createkey API. */
+static gpgme_error_t
+gpg_createkey_legacy (engine_gpg_t gpg,
+ const char *userid, const char *algo,
+ unsigned long expires,
+ unsigned int flags,
+ int use_armor)
+{
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+}
+
+
+static gpgme_error_t
+gpg_createkey (engine_gpg_t gpg,
+ const char *userid, const char *algo,
+ unsigned long expires,
+ unsigned int flags,
+ int use_armor)
+{
+ gpgme_error_t err;
+
+ err = add_arg (gpg, "--quick-gen-key");
+ 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 && (flags & GPGME_CREATE_FORCE))
+ err = add_arg (gpg, "--yes");
+ if (!err)
+ err = add_arg (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 = start (gpg);
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_addkey (engine_gpg_t gpg,
+ const char *algo,
+ unsigned long expires,
+ gpgme_key_t key,
+ unsigned int flags,
+ int use_armor)
+{
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+}
+
+
+static gpgme_error_t
+gpg_adduid (engine_gpg_t gpg,
+ const char *userid,
+ unsigned int flags,
+ int use_armor)
+{
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+}
+
+
+static gpgme_error_t
+gpg_genkey (void *engine,
+ const char *userid, const char *algo,
+ unsigned long reserved, unsigned long expires,
+ gpgme_key_t key, unsigned int flags,
+ gpgme_data_t help_data, int use_armor,
+ gpgme_data_t pubkey, gpgme_data_t seckey)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ (void)reserved;
+
+ if (!gpg)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* If HELP_DATA is given the use of the old interface
+ * (gpgme_op_genkey) has been requested. The other modes are:
+ *
+ * USERID && !KEY - Create a new keyblock.
+ * !USERID && KEY - Add a new subkey to KEY (gpg >= 2.1.14)
+ * USERID && KEY && !ALGO - Add a new user id to KEY (gpg >= 2.1.14).
+ *
+ */
+ if (help_data)
+ {
+ /* We need a special mechanism to get the fd of a pipe here, so
+ that we can use this for the %pubring and %secring
+ parameters. We don't have this yet, so we implement only the
+ adding to the standard keyrings. */
+ if (pubkey || seckey)
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ else
+ err = gpg_createkey_from_param (gpg, help_data, use_armor);
+ }
+ else if (userid && !key)
+ {
+ if (!have_gpg_version (gpg, "2.1.13"))
+ err = gpg_createkey_legacy (gpg, userid, algo, expires, flags,
+ use_armor);
+ else
+ err = gpg_createkey (gpg, userid, algo, expires, flags, use_armor);
+ }
+ else if (!have_gpg_version (gpg, "2.1.13"))
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ else if (!userid && key)
+ err = gpg_addkey (gpg, algo, expires, key, flags, use_armor);
+ else if (userid && key && !algo)
+ err = gpg_adduid (gpg, userid, flags, use_armor);
+ else
+ err = gpg_error (GPG_ERR_INV_VALUE);
return err;
}
diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c
index d4a1cf77..3f3230bb 100644
--- a/src/engine-gpgsm.c
+++ b/src/engine-gpgsm.c
@@ -1433,29 +1433,49 @@ gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
static gpgme_error_t
-gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
+gpgsm_genkey (void *engine,
+ const char *userid, const char *algo,
+ unsigned long reserved, unsigned long expires,
+ gpgme_key_t key, unsigned int flags,
+ gpgme_data_t help_data, int use_armor,
gpgme_data_t pubkey, gpgme_data_t seckey)
{
engine_gpgsm_t gpgsm = engine;
gpgme_error_t err;
- if (!gpgsm || !pubkey || seckey)
+ (void)reserved;
+
+ if (!gpgsm)
return gpg_error (GPG_ERR_INV_VALUE);
- gpgsm->input_cb.data = help_data;
- err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
- if (err)
- return err;
- gpgsm->output_cb.data = pubkey;
- err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
- : map_data_enc (gpgsm->output_cb.data));
- if (err)
- return err;
- gpgsm_clear_fd (gpgsm, MESSAGE_FD);
- gpgsm->inline_data = NULL;
+ if (help_data)
+ {
+ if (!pubkey || seckey)
+ return gpg_error (GPG_ERR_INV_VALUE);
- err = start (gpgsm, "GENKEY");
- return err;
+ gpgsm->input_cb.data = help_data;
+ err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+ if (err)
+ return err;
+ gpgsm->output_cb.data = pubkey;
+ err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
+ : map_data_enc (gpgsm->output_cb.data));
+ if (err)
+ return err;
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ err = start (gpgsm, "GENKEY");
+ return err;
+ }
+
+ (void)userid;
+ (void)expires;
+ (void)key;
+ (void)flags;
+
+ /* The new interface has not yet been implemented, */
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
diff --git a/src/engine.c b/src/engine.c
index f428034d..1ff86981 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -773,7 +773,11 @@ _gpgme_engine_op_export_ext (engine_t engine, const char *pattern[],
gpgme_error_t
-_gpgme_engine_op_genkey (engine_t engine, gpgme_data_t help_data,
+_gpgme_engine_op_genkey (engine_t engine,
+ const char *userid, const char *algo,
+ unsigned long reserved, unsigned long expires,
+ gpgme_key_t key, unsigned int flags,
+ gpgme_data_t help_data,
int use_armor, gpgme_data_t pubkey,
gpgme_data_t seckey)
{
@@ -783,7 +787,9 @@ _gpgme_engine_op_genkey (engine_t engine, gpgme_data_t help_data,
if (!engine->ops->genkey)
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
- return (*engine->ops->genkey) (engine->engine, help_data, use_armor,
+ return (*engine->ops->genkey) (engine->engine,
+ userid, algo, reserved, expires, key, flags,
+ help_data, use_armor,
pubkey, seckey);
}
diff --git a/src/engine.h b/src/engine.h
index b713d961..857dff42 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -108,6 +108,10 @@ gpgme_error_t _gpgme_engine_op_export_ext (engine_t engine,
gpgme_data_t keydata,
int use_armor);
gpgme_error_t _gpgme_engine_op_genkey (engine_t engine,
+ const char *userid, const char *algo,
+ unsigned long reserved,
+ unsigned long expires,
+ gpgme_key_t key, unsigned int flags,
gpgme_data_t help_data,
int use_armor, gpgme_data_t pubkey,
gpgme_data_t seckey);
diff --git a/src/export.c b/src/export.c
index a29fbde8..41a9ebaf 100644
--- a/src/export.c
+++ b/src/export.c
@@ -34,7 +34,7 @@
/* Local operation data. */
typedef struct
{
- gpg_error_t err; /* Error encountred during the export. */
+ gpg_error_t err; /* Error encountered during the export. */
} *op_data_t;
diff --git a/src/genkey.c b/src/genkey.c
index 34cc5af4..0b795f43 100644
--- a/src/genkey.c
+++ b/src/genkey.c
@@ -1,23 +1,22 @@
/* genkey.c - Key generation.
- Copyright (C) 2000 Werner Koch (dd9jn)
- Copyright (C) 2001, 2002, 2003, 2004 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, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA. */
+ * Copyright (C) 2000 Werner Koch (dd9jn)
+ * Copyright (C) 2001, 2002, 2003, 2004, 2016 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 .
+ */
#if HAVE_CONFIG_H
#include
@@ -40,6 +39,9 @@ typedef struct
/* The error code from a FAILURE status line or 0. */
gpg_error_t failure_code;
+ /* The error code from certain ERROR status lines or 0. */
+ gpg_error_t error_code;
+
/* The key parameters passed to the crypto engine. */
gpgme_data_t key_parameter;
} *op_data_t;
@@ -82,7 +84,39 @@ gpgme_op_genkey_result (gpgme_ctx_t ctx)
return &opd->result;
}
+
+/* Parse an error status line. Return the error location and the
+ error code. The function may modify ARGS. */
+static char *
+parse_error (char *args, gpg_error_t *r_err)
+{
+ char *where = strchr (args, ' ');
+ char *which;
+
+ if (where)
+ {
+ *where = '\0';
+ which = where + 1;
+
+ where = strchr (which, ' ');
+ if (where)
+ *where = '\0';
+
+ where = args;
+ }
+ else
+ {
+ *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE);
+ return NULL;
+ }
+
+ *r_err = atoi (which);
+
+ return where;
+}
+
+
static gpgme_error_t
genkey_status_handler (void *priv, gpgme_status_code_t code, char *args)
{
@@ -90,6 +124,7 @@ genkey_status_handler (void *priv, gpgme_status_code_t code, char *args)
gpgme_error_t err;
void *hook;
op_data_t opd;
+ char *loc;
/* Pipe the status code through the progress status handler. */
err = _gpgme_progress_status_handler (ctx, code, args);
@@ -121,13 +156,22 @@ genkey_status_handler (void *priv, gpgme_status_code_t code, char *args)
}
break;
+ case GPGME_STATUS_ERROR:
+ loc = parse_error (args, &err);
+ if (!loc)
+ return err;
+ if (!opd->error_code)
+ opd->error_code = err;
+ break;
+
case GPGME_STATUS_FAILURE:
opd->failure_code = _gpgme_parse_failure (args);
break;
case GPGME_STATUS_EOF:
- /* FIXME: Should return some more useful error value. */
- if (!opd->result.primary && !opd->result.sub)
+ if (opd->error_code)
+ return opd->error_code;
+ else if (!opd->result.primary && !opd->result.sub)
return gpg_error (GPG_ERR_GENERAL);
else if (opd->failure_code)
return opd->failure_code;
@@ -212,7 +256,9 @@ genkey_start (gpgme_ctx_t ctx, int synchronous, const char *parms,
return err;
}
- return _gpgme_engine_op_genkey (ctx->engine, opd->key_parameter,
+ return _gpgme_engine_op_genkey (ctx->engine,
+ NULL, NULL, 0, 0, NULL, 0,
+ opd->key_parameter,
ctx->use_armor, pubkey, seckey);
}
@@ -259,3 +305,85 @@ gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey,
err = _gpgme_wait_one (ctx);
return TRACE_ERR (err);
}
+
+
+
+static gpgme_error_t
+createkey_start (gpgme_ctx_t ctx, int synchronous,
+ const char *userid, const char *algo,
+ unsigned long reserved, unsigned long expires,
+ gpgme_key_t anchorkey, unsigned int flags)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ if (reserved || anchorkey || !userid)
+ 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,
+ userid, algo, reserved, expires,
+ anchorkey, flags,
+ NULL, ctx->use_armor, NULL, NULL);
+
+}
+
+
+gpgme_error_t
+gpgme_op_createkey_start (gpgme_ctx_t ctx, const char *userid, const char *algo,
+ unsigned long reserved, unsigned long expires,
+ gpgme_key_t anchorkey, unsigned int flags)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createkey_start", ctx,
+ "userid='%s', algo='%s' flags=0x%x", userid, algo, flags);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = createkey_start (ctx, 0,
+ userid, algo, reserved, expires, anchorkey, flags);
+ return TRACE_ERR (err);
+}
+
+
+gpgme_error_t
+gpgme_op_createkey (gpgme_ctx_t ctx, const char *userid, const char *algo,
+ unsigned long reserved, unsigned long expires,
+ gpgme_key_t anchorkey, unsigned int flags)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createkey", ctx,
+ "userid='%s', algo='%s' flags=0x%x", userid, algo, flags);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = createkey_start (ctx, 1,
+ userid, algo, reserved, expires, anchorkey, flags);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
diff --git a/src/gpgme.def b/src/gpgme.def
index a15c35b5..a56b9efa 100644
--- a/src/gpgme.def
+++ b/src/gpgme.def
@@ -229,5 +229,8 @@ EXPORTS
gpgme_data_set_flag @171
+ gpgme_op_createkey_start @172
+ gpgme_op_createkey @173
+
; END
diff --git a/src/gpgme.h.in b/src/gpgme.h.in
index 00a4bed3..0fdc9276 100644
--- a/src/gpgme.h.in
+++ b/src/gpgme.h.in
@@ -1815,6 +1815,18 @@ gpgme_error_t gpgme_op_export_keys (gpgme_ctx_t ctx,
* Key generation.
*/
+/* Flags for the key creation functions. */
+#define GPGME_CREATE_SIGN (1 << 0) /* Allow usage: signing. */
+#define GPGME_CREATE_ENCR (1 << 1) /* Allow usage: encryption. */
+#define GPGME_CREATE_CERT (1 << 2) /* Allow usage: certification. */
+#define GPGME_CREATE_AUTH (1 << 3) /* Allow usage: authentication. */
+#define GPGME_CREATE_NOPASSWD (1 << 7) /* Create w/o passphrase. */
+#define GPGME_CREATE_SELFSIGNED (1 << 8) /* Create self-signed cert. */
+#define GPGME_CREATE_NOSTORE (1 << 9) /* Do not store the key. */
+#define GPGME_CREATE_WANTPUB (1 << 10) /* Return the public key. */
+#define GPGME_CREATE_WANTSEC (1 << 11) /* Return the secret key. */
+#define GPGME_CREATE_FORCE (1 << 12) /* Force creation. */
+
struct _gpgme_op_genkey_result
{
/* A primary key was generated. */
@@ -1828,6 +1840,14 @@ struct _gpgme_op_genkey_result
/* The fingerprint of the generated key. */
char *fpr;
+
+ /* A memory data object with the created public key. Only set when
+ * GPGME_CREATE_WANTPUB has been used. */
+ gpgme_data_t pubkey;
+
+ /* A memory data object with the created secret key. Only set when
+ * GPGME_CREATE_WANTSEC has been used. */
+ gpgme_data_t seckey;
};
typedef struct _gpgme_op_genkey_result *gpgme_genkey_result_t;
@@ -1839,7 +1859,39 @@ gpgme_error_t gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms,
gpgme_error_t gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms,
gpgme_data_t pubkey, gpgme_data_t seckey);
-/* Retrieve a pointer to the result of the genkey operation. */
+/* Generate a key pair using the modern interface. */
+gpgme_error_t gpgme_op_createkey_start (gpgme_ctx_t ctx,
+ const char *userid,
+ const char *algo,
+ unsigned long reserved,
+ unsigned long expires,
+ gpgme_key_t certkey,
+ unsigned int flags);
+gpgme_error_t gpgme_op_createkey (gpgme_ctx_t ctx,
+ const char *userid,
+ const char *algo,
+ unsigned long reserved,
+ unsigned long expires,
+ gpgme_key_t certkey,
+ unsigned int flags);
+/* Add a new subkey to 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 gpgme_op_createsubkey (gpgme_ctx_t ctx,
+ gpgme_key_t key,
+ const char *algo,
+ unsigned long reserved,
+ unsigned long expires,
+ unsigned int flags);
+
+
+
+/* Retrieve a pointer to the result of a genkey, createkey, or
+ * createsubkey operation. */
gpgme_genkey_result_t gpgme_op_genkey_result (gpgme_ctx_t ctx);
@@ -2177,7 +2229,7 @@ typedef struct gpgme_conf_arg
} *gpgme_conf_arg_t;
-/* The flags of a configuration option. See the gpg-conf
+/* The flags of a configuration option. See the gpgconf
documentation for details. */
#define GPGME_CONF_GROUP (1 << 0)
#define GPGME_CONF_OPTIONAL (1 << 1)
diff --git a/src/libgpgme.vers b/src/libgpgme.vers
index d29bc147..b06c9c65 100644
--- a/src/libgpgme.vers
+++ b/src/libgpgme.vers
@@ -101,6 +101,10 @@ GPGME_1.1 {
gpgme_pubkey_algo_string;
gpgme_set_ctx_flag;
+ gpgme_data_set_flag;
+
+ gpgme_op_createkey_start;
+ gpgme_op_createkey;
};
@@ -230,8 +234,6 @@ GPGME_1.0 {
gpgme_err_code_from_syserror;
gpgme_err_set_errno;
- gpgme_data_set_flag;
-
local:
*;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a450f2a8..f3a16045 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -32,7 +32,7 @@ LDADD = ../src/libgpgme.la @GPG_ERROR_LIBS@
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-verify run-encrypt run-identify run-decrypt run-genkey
if RUN_GPG_TESTS
diff --git a/tests/run-genkey.c b/tests/run-genkey.c
new file mode 100644
index 00000000..74d4038e
--- /dev/null
+++ b/tests/run-genkey.c
@@ -0,0 +1,348 @@
+/* run-genkey.c - Test tool to perform key generation
+ * Copyright (C) 2016 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 .
+ */
+
+/* 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
+#endif
+
+#include
+#include
+#include
+#include
+
+#include
+
+#define PGM "run-genkey"
+
+#include "run-support.h"
+
+
+static int verbose;
+
+
+/* Tokenize STRING using the set of delimiters in DELIM. Leading
+ * spaces and tabs are removed from all tokens. The caller must free
+ * the result.
+ *
+ * Returns: A malloced and NULL delimited array with the tokens. On
+ * memory error NULL is returned and ERRNO is set.
+ */
+static char **
+strtokenize (const char *string, const char *delim)
+{
+ const char *s;
+ size_t fields;
+ size_t bytes, n;
+ char *buffer;
+ char *p, *px, *pend;
+ char **result;
+
+ /* Count the number of fields. */
+ for (fields = 1, s = strpbrk (string, delim); s; s = strpbrk (s + 1, delim))
+ fields++;
+ fields++; /* Add one for the terminating NULL. */
+
+ /* Allocate an array for all fields, a terminating NULL, and space
+ for a copy of the string. */
+ bytes = fields * sizeof *result;
+ if (bytes / sizeof *result != fields)
+ {
+ gpg_err_set_errno (ENOMEM);
+ return NULL;
+ }
+ n = strlen (string) + 1;
+ bytes += n;
+ if (bytes < n)
+ {
+ gpg_err_set_errno (ENOMEM);
+ return NULL;
+ }
+ result = malloc (bytes);
+ if (!result)
+ return NULL;
+ buffer = (char*)(result + fields);
+
+ /* Copy and parse the string. */
+ strcpy (buffer, string);
+ for (n = 0, p = buffer; (pend = strpbrk (p, delim)); p = pend + 1)
+ {
+ *pend = 0;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ for (px = pend - 1; px >= p && (*px == ' ' || *px == '\t'); px--)
+ *px = 0;
+ result[n++] = p;
+ }
+ while (*p == ' ' || *p == '\t')
+ p++;
+ for (px = p + strlen (p) - 1; px >= p && (*px == ' ' || *px == '\t'); px--)
+ *px = 0;
+ result[n++] = p;
+ result[n] = NULL;
+
+ assert ((char*)(result + n + 1) == buffer);
+
+ return result;
+}
+
+
+static gpg_error_t
+status_cb (void *opaque, const char *keyword, const char *value)
+{
+ (void)opaque;
+ fprintf (stderr, "status_cb: %s %s\n", nonnull(keyword), nonnull(value));
+ return 0;
+}
+
+
+static void
+progress_cb (void *opaque, const char *what, int type, int current, int total)
+{
+ (void)opaque;
+ (void)type;
+
+ if (total)
+ fprintf (stderr, "progress for '%s' %u%% (%d of %d)\n",
+ nonnull (what),
+ (unsigned)(((double)current / total) * 100), current, total);
+ else
+ fprintf (stderr, "progress for '%s' %d\n", nonnull(what), current);
+ fflush (stderr);
+}
+
+
+static unsigned long
+parse_expire_string (const char *string)
+{
+ unsigned long seconds;
+
+ if (!string || !*string || !strcmp (string, "none")
+ || !strcmp (string, "never") || !strcmp (string, "-"))
+ seconds = 0;
+ else if (strspn (string, "01234567890") == strlen (string))
+ seconds = strtoul (string, NULL, 10);
+ else
+ {
+ fprintf (stderr, PGM ": invalid value '%s'\n", string);
+ exit (1);
+ }
+
+ return seconds;
+}
+
+
+/* Parse a usage string and return flags for gpgme_op_createkey. */
+static unsigned int
+parse_usage_string (const char *string)
+{
+ gpg_error_t err;
+ char **tokens = NULL;
+ const char *s;
+ int i;
+ unsigned int flags = 0;
+
+ tokens = strtokenize (string, " \t,");
+ if (!tokens)
+ {
+ err = gpg_error_from_syserror ();
+ fprintf (stderr, PGM": strtokenize failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ for (i=0; (s = tokens[i]); i++)
+ {
+ if (!*s)
+ ;
+ else if (!strcmp (s, "default"))
+ ;
+ else if (!strcmp (s, "sign"))
+ flags |= GPGME_CREATE_SIGN;
+ else if (!strcmp (s, "encr"))
+ flags |= GPGME_CREATE_ENCR;
+ else if (!strcmp (s, "cert"))
+ flags |= GPGME_CREATE_CERT;
+ else if (!strcmp (s, "auth"))
+ flags |= GPGME_CREATE_AUTH;
+ else
+ {
+ free (tokens);
+ fprintf (stderr, PGM": invalid value '%s': %s\n",
+ string, "bad usage");
+ exit (1);
+ }
+ }
+
+ free (tokens);
+ return flags;
+}
+
+
+
+static int
+show_usage (int ex)
+{
+ fputs ("usage: " PGM " [options] USERID [ALGO [USAGE [EXPIRESECONDS]]]\n\n"
+ "Options:\n"
+ " --verbose run in verbose mode\n"
+ " --status print status lines from the backend\n"
+ " --progress print progress info\n"
+ " --openpgp use the OpenPGP protocol (default)\n"
+ " --cms use the CMS protocol\n"
+ " --loopback use a loopback pinentry\n"
+ " --unprotected do not use a passphrase\n"
+ " --force do not check for a duplicated user id\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;
+ int print_status = 0;
+ int print_progress = 0;
+ int use_loopback = 0;
+ const char *userid;
+ const char *algo = NULL;
+ unsigned int flags = 0;
+ unsigned long expire = 0;
+ gpgme_genkey_result_t result;
+
+ 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, "--status"))
+ {
+ print_status = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--progress"))
+ {
+ print_progress = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--openpgp"))
+ {
+ protocol = GPGME_PROTOCOL_OpenPGP;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--cms"))
+ {
+ protocol = GPGME_PROTOCOL_CMS;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--loopback"))
+ {
+ use_loopback = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--unprotected"))
+ {
+ flags |= GPGME_CREATE_NOPASSWD;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--force"))
+ {
+ flags |= GPGME_CREATE_FORCE;
+ argc--; argv++;
+ }
+ else if (!strncmp (*argv, "--", 2))
+ show_usage (1);
+ }
+
+ if (!argc || argc > 4)
+ show_usage (1);
+ userid = argv[0];
+ if (argc > 1)
+ algo = argv[1];
+ if (argc > 2)
+ flags |= parse_usage_string (argv[2]);
+ if (argc > 3)
+ expire = parse_expire_string (argv[3]);
+
+ init_gpgme (protocol);
+
+ err = gpgme_new (&ctx);
+ fail_if_err (err);
+ gpgme_set_protocol (ctx, protocol);
+ gpgme_set_armor (ctx, 1);
+ if (print_status)
+ {
+ gpgme_set_status_cb (ctx, status_cb, NULL);
+ gpgme_set_ctx_flag (ctx, "full-status", "1");
+ }
+ if (print_progress)
+ gpgme_set_progress_cb (ctx, progress_cb, NULL);
+ if (use_loopback)
+ {
+ gpgme_set_pinentry_mode (ctx, GPGME_PINENTRY_MODE_LOOPBACK);
+ gpgme_set_passphrase_cb (ctx, passphrase_cb, NULL);
+ }
+
+ 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);
+ if (!result)
+ {
+ fprintf (stderr, PGM": gpgme_op_genkey_result returned NULL\n");
+ exit (1);
+ }
+
+ printf ("Generated key: %s (%s)\n",
+ result->fpr ? result->fpr : "none",
+ result->primary ? (result->sub ? "primary, sub" : "primary")
+ /**/ : (result->sub ? "sub" : "none"));
+
+ if (result->fpr && strlen (result->fpr) < 40)
+ fprintf (stderr, PGM": generated key has unexpected fingerprint\n");
+ if (!result->primary)
+ fprintf (stderr, PGM": primary key was not generated\n");
+ if (!result->sub)
+ fprintf (stderr, PGM": sub key was not generated\n");
+
+ gpgme_release (ctx);
+ return 0;
+}