core: New function gpgme_op_createsubkey.
authorWerner Koch <wk@gnupg.org>
Wed, 14 Sep 2016 07:51:16 +0000 (09:51 +0200)
committerWerner Koch <wk@gnupg.org>
Wed, 14 Sep 2016 07:51:16 +0000 (09:51 +0200)
* 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>
NEWS
doc/gpgme.texi
src/engine-gpg.c
src/genkey.c
src/gpgme.def
src/libgpgme.vers
tests/run-genkey.c

diff --git a/NEWS b/NEWS
index d3639c8..9445f7f 100644 (file)
--- 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.
index dfc9548..ef39d81 100644 (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}.
index f22d8b4..5a16f80 100644 (file)
@@ -1969,6 +1969,47 @@ gpg_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
 }
 
 
+\f
+/* 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;
 }
 
 
index 0b795f4..26bcca6 100644 (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);
 }
+
+
+\f
+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);
+}
index a56b9ef..7b7b1f2 100644 (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
 
index b06c9c6..2a3e9fc 100644 (file)
@@ -105,6 +105,8 @@ GPGME_1.1 {
 
     gpgme_op_createkey_start;
     gpgme_op_createkey;
+    gpgme_op_createsubkey_start;
+    gpgme_op_createsubkey;
 };
 
 
index 74d4038..3b64502 100644 (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);