core: New function gpgme_op_create_key.
authorWerner Koch <wk@gnupg.org>
Tue, 13 Sep 2016 16:57:38 +0000 (18:57 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 13 Sep 2016 16:59:10 +0000 (18:59 +0200)
* src/engine-backend.h (engine_ops): Change prototype of genkey.
* src/engine-gpgsm.c (gpgsm_genkey): Change accordingly.
* src/engine-gpg.c (gpg_genkey): Change it to a dispatcher.
(gpg_createkey_from_param): New for the old functionality.
(gpg_createkey_legacy): New.  Stub for now.
(gpg_createkey): New.
(gpg_addkey): New.  Stub for now.
(gpg_adduid): New.  Stub for now.
* src/engine.c (_gpgme_engine_op_genkey): Add new args.
* src/genkey.c (op_data_t): Add field ERROR_CODE.
(parse_error): New.
(genkey_status_handler): Parse ERROR status line.
(genkey_start): Use NULL/0 for the new args.
(createkey_start): New.
(gpgme_op_createkey_start, gpgme_op_createkey): New.
* src/gpgme.def, src/libgpgme.vers: Add gpgme_op_createkey_start and
gpgme_op_createkey.
* src/gpgme.h.in (_gpgme_op_genkey_result): Add fields PUBKEY and
SECKEY.
(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.

* tests/run-genkey.c: New.
* tests/Makefile.am (noinst_PROGRAMS): Add it.
--

This function uses the new --quick-gen-key API of gpg.  A limited
compatibility mode to use older gpg versions and gpgsm will eventually
be provided.  Not all flags are currently implemented.

 ./run-genkey --unprotected --force test@example.com

Create a new standard key with the given user id.  --force is used to
allow creating more than one key with that user id in the keyring.

 ./run-genkey --unprotected --force \
      test@example.com default default 2145826800

Creates a new standard key with an expiration date of 2037-12-31.

 ./run-genkey --unprotected --force \
      test@example.com future-default default 2145826800

Create a standard key using the fugure default algorithms.

Signed-off-by: Werner Koch <wk@gnupg.org>
13 files changed:
NEWS
src/engine-backend.h
src/engine-gpg.c
src/engine-gpgsm.c
src/engine.c
src/engine.h
src/export.c
src/genkey.c
src/gpgme.def
src/gpgme.h.in
src/libgpgme.vers
tests/Makefile.am
tests/run-genkey.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index da331b4..d3639c8 100644 (file)
--- 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]
index a15194e..842292d 100644 (file)
@@ -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);
index 3f77ba8..289578b 100644 (file)
@@ -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;
 }
index d4a1cf7..3f3230b 100644 (file)
@@ -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);
 }
 
 
index f428034..1ff8698 100644 (file)
@@ -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);
 }
 
index b713d96..857dff4 100644 (file)
@@ -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);
index a29fbde..41a9eba 100644 (file)
@@ -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;
 
 
index 34cc5af..0b795f4 100644 (file)
@@ -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 <http://www.gnu.org/licenses/>.
+ */
 
 #if HAVE_CONFIG_H
 #include <config.h>
@@ -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;
 }
 
+
 \f
+/* 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);
 }
+
+
+\f
+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);
+}
index a15c35b..a56b9ef 100644 (file)
@@ -229,5 +229,8 @@ EXPORTS
 
     gpgme_data_set_flag                   @171
 
+    gpgme_op_createkey_start              @172
+    gpgme_op_createkey                    @173
+
 ; END
 
index 00a4bed..0fdc927 100644 (file)
@@ -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)
index d29bc14..b06c9c6 100644 (file)
@@ -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:
     *;
 
index a450f2a..f3a1604 100644 (file)
@@ -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 (file)
index 0000000..74d4038
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+/* 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-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;
+}