Import OpenPGP keys into the agent.
authorWerner Koch <wk@gnupg.org>
Tue, 31 Aug 2010 15:58:39 +0000 (15:58 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 31 Aug 2010 15:58:39 +0000 (15:58 +0000)
23 files changed:
NEWS
agent/ChangeLog
agent/Makefile.am
agent/agent.h
agent/command.c
agent/cvt-openpgp.c [new file with mode: 0644]
agent/cvt-openpgp.h [new file with mode: 0644]
agent/findkey.c
agent/keyformat.txt
agent/pksign.c
agent/protect.c
g10/ChangeLog
g10/call-agent.c
g10/call-agent.h
g10/gpg.c
g10/import.c
g10/keydb.h
g10/keyid.c
g10/keyserver.c
g10/main.h
g10/misc.c
g10/options.h
g10/pkglue.c

diff --git a/NEWS b/NEWS
index 886bebb..11c5d5e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -37,6 +37,10 @@ Noteworthy changes in version 2.1.x (under development)
 
  * Fixed output of "gpgconf --check-options".
 
+ * GPG does not anymore use secring.gpg but delegates all secret key
+   operations to gpg-agent.  The import command moves secret keys to
+   the agent.
+
 
 Noteworthy changes in version 2.0.13 (2009-09-04)
 -------------------------------------------------
index 0c31fc5..e584005 100644 (file)
@@ -1,3 +1,18 @@
+2010-08-31  Werner Koch  <wk@g10code.com>
+
+       * pksign.c (do_encode_dsa): Fix sign problem.
+       * findkey.c (agent_is_dsa_key): Adjust to actual usage.
+
+2010-08-30  Werner Koch  <wk@g10code.com>
+
+       * protect.c (s2k_hash_passphrase): New public function.
+
+2010-08-27  Werner Koch  <wk@g10code.com>
+
+       * command.c (cmd_import_key): Support OpenPGP keys.
+       * cvt-openpgp.h, cvt-openpgp.c: New.  Some of the code is based on
+       code taken from g10/seckey-cert.c.
+
 2010-08-26  Werner Koch  <wk@g10code.com>
 
        * command-ssh.c (open_control_file): Use estream to create the file.
index abd39be..9c58627 100644 (file)
@@ -46,6 +46,7 @@ gpg_agent_SOURCES = \
        protect.c \
        trustlist.c \
        divert-scd.c \
+       cvt-openpgp.c cvt-openpgp.h \
        call-scd.c \
        learncard.c
 
index b39f232..57078ab 100644 (file)
@@ -171,8 +171,8 @@ struct pin_entry_info_s
   int with_qualitybar; /* Set if the quality bar should be displayed.  */
   int (*check_cb)(struct pin_entry_info_s *); /* CB used to check the PIN */
   void *check_cb_arg;  /* optional argument which might be of use in the CB */
-  const char *cb_errtext; /* used by the cb to displaye a specific error */
-  size_t max_length; /* allocated length of the buffer */
+  const char *cb_errtext; /* used by the cb to display a specific error */
+  size_t max_length;      /* allocated length of the buffer */
   char pin[1];
 };
 
@@ -306,6 +306,11 @@ int agent_get_shadow_info (const unsigned char *shadowkey,
                            unsigned char const **shadow_info);
 gpg_error_t parse_shadow_info (const unsigned char *shadow_info, 
                                char **r_hexsn, char **r_idstr);
+gpg_error_t s2k_hash_passphrase (const char *passphrase, int hashalgo,
+                                 int s2kmode,
+                                 const unsigned char *s2ksalt,
+                                 unsigned int s2kcount,
+                                 unsigned char *key, size_t keylen);
 
 
 /*-- trustlist.c --*/
index 3a3b61b..4b847e5 100644 (file)
@@ -37,7 +37,7 @@
 #include "agent.h"
 #include <assuan.h>
 #include "i18n.h"
-
+#include "cvt-openpgp.h"
 
 
 /* Maximum allowed size of the inquired ciphertext.  */
@@ -357,6 +357,12 @@ leave_cmd (assuan_context_t ctx, gpg_error_t err)
       const char *name = assuan_get_command_name (ctx);
       if (!name)
         name = "?";
+
+      /* Most code from common/ does not know the error source, thus
+         we fix this here.  */
+      if (gpg_err_source (err) == GPG_ERR_SOURCE_UNKNOWN)
+        err = gpg_err_make (GPG_ERR_SOURCE_DEFAULT, gpg_err_code (err));
+
       if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
         log_error ("command '%s' failed: %s\n", name,
                    gpg_strerror (err));
@@ -566,8 +572,8 @@ cmd_sigkey (assuan_context_t ctx, char *line)
 static const char hlp_setkeydesc[] = 
   "SETKEYDESC plus_percent_escaped_string\n"
   "\n"
-  "Set a description to be used for the next PKSIGN, PKDECRYPT or EXPORT_KEY\n"
-  "operation if this operation requires the entry of a passphrase.  If\n"
+  "Set a description to be used for the next PKSIGN, PKDECRYPT, IMPORT_KEY\n"
+  "or EXPORT_KEY operation if this operation requires a passphrase.  If\n"
   "this command is not used a default text will be used.  Note, that\n"
   "this description implictly selects the label used for the entry\n"
   "box; if the string contains the string PIN (which in general will\n"
@@ -575,8 +581,8 @@ static const char hlp_setkeydesc[] =
   "\"passphrase\" is used.  The description string should not contain\n"
   "blanks unless they are percent or '+' escaped.\n"
   "\n"
-  "The description is only valid for the next PKSIGN, PKDECRYPT or\n"
-  "EXPORT_KEY operation.";
+  "The description is only valid for the next PKSIGN, PKDECRYPT,\n"
+  "IMPORT_KEY or EXPORT_KEY operation.";
 static gpg_error_t
 cmd_setkeydesc (assuan_context_t ctx, char *line)
 {
@@ -1478,6 +1484,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
   unsigned char *finalkey = NULL;
   size_t finalkeylen;
   unsigned char grip[20];
+  gcry_sexp_t openpgp_sexp = NULL;
   
   (void)line;
 
@@ -1528,20 +1535,58 @@ cmd_import_key (assuan_context_t ctx, char *line)
   
   err = keygrip_from_canon_sexp (key, realkeylen, grip);
   if (err)
-    goto leave;
-
-  if (!agent_key_available (grip))
     {
-      err = gpg_error (GPG_ERR_EEXIST);
-      goto leave;
+      /* This might be due to an unsupported S-expression format.
+         Check whether this is openpgp-private-key and trigger that
+         import code.  */
+      if (!gcry_sexp_sscan (&openpgp_sexp, NULL, key, realkeylen))
+        {
+          const char *tag;
+          size_t taglen;
+          
+          tag = gcry_sexp_nth_data (openpgp_sexp, 0, &taglen);
+          if (tag && taglen == 19 && !memcmp (tag, "openpgp-private-key", 19))
+            ;
+          else
+            {
+              gcry_sexp_release (openpgp_sexp);
+              openpgp_sexp = NULL;
+            }
+        }
+      if (!openpgp_sexp)
+        goto leave; /* Note that ERR is still set.  */
     }
 
-  err = agent_ask_new_passphrase 
-    (ctrl, _("Please enter the passphrase to protect the "
-             "imported object within the GnuPG system."),
-     &passphrase);
-  if (err)
-    goto leave;
+
+  if (openpgp_sexp)
+    {
+      /* In most cases the key is encrypted and thus the conversion
+         function from the OpenPGP format to our internal format will
+         ask for a passphrase.  That passphrase will be returned and
+         used to protect the key using the same code as for regular
+         key import. */
+      
+      err = convert_openpgp (ctrl, openpgp_sexp, grip,
+                             ctrl->server_local->keydesc,
+                             &key, &passphrase);
+      if (err)
+        goto leave;
+      realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err);
+      if (!realkeylen)
+        goto leave; /* Invalid canonical encoded S-expression.  */
+    }
+  else
+    {
+      if (!agent_key_available (grip))
+        err = gpg_error (GPG_ERR_EEXIST);
+      else
+        err = agent_ask_new_passphrase 
+          (ctrl, _("Please enter the passphrase to protect the "
+                   "imported object within the GnuPG system."),
+           &passphrase);
+      if (err)
+        goto leave;
+    }
 
   if (passphrase)
     {
@@ -1553,11 +1598,14 @@ cmd_import_key (assuan_context_t ctx, char *line)
     err = agent_write_private_key (grip, key, realkeylen, 0);
 
  leave:
+  gcry_sexp_release (openpgp_sexp);
   xfree (finalkey);
   xfree (passphrase);
   xfree (key);
   gcry_cipher_close (cipherhd);
   xfree (wrappedkey);
+  xfree (ctrl->server_local->keydesc);
+  ctrl->server_local->keydesc = NULL;
   return leave_cmd (ctx, err);
 }
 
diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c
new file mode 100644 (file)
index 0000000..a392518
--- /dev/null
@@ -0,0 +1,807 @@
+/* cvt-openpgp.c - Convert an OpenPGP key to our internal format.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 2009,
+ *               2010 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "agent.h"
+#include "i18n.h"
+#include "cvt-openpgp.h"
+
+
+/* Helper to pass data via the callback to do_unprotect. */
+struct try_do_unprotect_arg_s 
+{
+  int  is_v4;
+  int  is_protected;
+  int  pubkey_algo;
+  int  protect_algo;
+  char *iv;
+  int  ivlen;
+  int  s2k_mode;
+  int  s2k_algo;
+  byte *s2k_salt;
+  u32  s2k_count;
+  u16 desired_csum;
+  gcry_mpi_t *skey;
+  size_t skeysize;
+  int skeyidx;
+  gcry_sexp_t *r_key;
+};
+
+
+
+/* Compute the keygrip from the public key and store it at GRIP.  */
+static gpg_error_t
+get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip)
+{
+  gpg_error_t err;
+  gcry_sexp_t s_pkey = NULL;
+
+  switch (pubkey_algo)
+    {
+    case GCRY_PK_DSA:
+      err = gcry_sexp_build (&s_pkey, NULL,
+                             "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))",
+                             pkey[0], pkey[1], pkey[2], pkey[3]);
+      break;
+
+    case GCRY_PK_ELG:
+    case GCRY_PK_ELG_E:
+      err = gcry_sexp_build (&s_pkey, NULL,
+                             "(public-key(elg(p%m)(g%m)(y%m)))",
+                             pkey[0], pkey[1], pkey[2]);
+      break;
+
+    case GCRY_PK_RSA:
+    case GCRY_PK_RSA_E:
+    case GCRY_PK_RSA_S:
+      err = gcry_sexp_build (&s_pkey, NULL,
+                             "(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]);
+      break;
+
+    default:
+      err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+      break;
+    }
+
+  if (!err && !gcry_pk_get_keygrip (s_pkey, grip))
+    err = gpg_error (GPG_ERR_INTERNAL);
+
+  gcry_sexp_release (s_pkey);
+  return err;
+}
+
+
+/* Convert a secret key given as algorithm id and an array of key
+   parameters into our s-expression based format.  */
+static gpg_error_t
+convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
+{
+  gpg_error_t err;
+  gcry_sexp_t s_skey = NULL;
+
+  *r_key = NULL;
+
+  switch (pubkey_algo)
+    {
+    case GCRY_PK_DSA:
+      err = gcry_sexp_build (&s_skey, NULL,
+                             "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))",
+                             skey[0], skey[1], skey[2], skey[3], skey[4]);
+      break;
+
+    case GCRY_PK_ELG:
+    case GCRY_PK_ELG_E:
+      err = gcry_sexp_build (&s_skey, NULL,
+                             "(private-key(elg(p%m)(g%m)(y%m)(x%m)))",
+                             skey[0], skey[1], skey[2], skey[3]);
+      break;
+
+
+    case GCRY_PK_RSA:
+    case GCRY_PK_RSA_E:
+    case GCRY_PK_RSA_S:
+      err = gcry_sexp_build (&s_skey, NULL,
+                             "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
+                             skey[0], skey[1], skey[2], skey[3], skey[4],
+                             skey[5]);
+
+    default:
+      err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+      break;
+    }
+
+  if (!err)
+    *r_key = s_skey;
+  return err;
+}
+
+
+
+/* Hash the passphrase and set the key. */
+static gpg_error_t
+hash_passphrase_and_set_key (const char *passphrase,
+                             gcry_cipher_hd_t hd, int protect_algo,
+                             int s2k_mode, int s2k_algo,
+                             byte *s2k_salt, u32 s2k_count)
+{
+  gpg_error_t err;
+  unsigned char *key;
+  size_t keylen;
+
+  keylen = gcry_cipher_get_algo_keylen (protect_algo);
+  if (!keylen)
+    return gpg_error (GPG_ERR_INTERNAL);
+  
+  key = xtrymalloc_secure (keylen);
+  if (!key)
+    return gpg_error_from_syserror ();
+
+  err = s2k_hash_passphrase (passphrase,
+                             s2k_algo, s2k_mode, s2k_salt, s2k_count,
+                             key, keylen);
+  if (!err)
+    err = gcry_cipher_setkey (hd, key, keylen);
+
+  xfree (key);
+  return err;
+}
+
+
+static u16
+checksum (const unsigned char *p, unsigned int n)
+{
+  u16 a;
+  
+  for (a=0; n; n-- )
+    a += *p++;
+  return a;
+}
+
+
+/* Note that this function modified SKEY.  SKEYSIZE is the allocated
+   size of the array including the NULL item; this is used for a
+   bounds check.  On success a converted key is stored at R_KEY.  */
+static int
+do_unprotect (const char *passphrase,
+              int pkt_version, int pubkey_algo, int is_protected,
+              gcry_mpi_t *skey, size_t skeysize,
+              int protect_algo, void *protect_iv, size_t protect_ivlen,
+              int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count,
+              u16 desired_csum, gcry_sexp_t *r_key)
+{
+  gpg_error_t err;
+  size_t npkey, nskey, skeylen;
+  gcry_cipher_hd_t cipher_hd = NULL;
+  u16 actual_csum;
+  size_t nbytes;
+  int i;
+  gcry_mpi_t tmpmpi;
+
+  *r_key = NULL;
+
+  /* Count the actual number of MPIs is in the array and set the
+     remainder to NULL for easier processing later on.  */
+  for (skeylen = 0; skey[skeylen]; skeylen++)
+    ;
+  for (i=skeylen; i < skeysize; i++)
+    skey[i] = NULL;
+
+  /* Check some args.  */
+  if (s2k_mode == 1001)
+    {
+      /* Stub key.  */
+      log_info (_("secret key parts are not available\n"));
+      return gpg_error (GPG_ERR_UNUSABLE_SECKEY);
+    }
+
+  if (gcry_pk_test_algo (pubkey_algo))
+    {
+      /* The algorithm numbers are Libgcrypt numbers but fortunately
+         the OpenPGP algorithm numbers map one-to-one to the Libgcrypt
+         numbers.  */
+      log_info (_("public key algorithm %d (%s) is not supported\n"),
+                pubkey_algo, gcry_pk_algo_name (pubkey_algo));
+      return gpg_error (GPG_ERR_PUBKEY_ALGO);
+    }
+
+  /* Get properties of the public key algorithm and do some
+     consistency checks.  Note that we need at least NPKEY+1 elements
+     in the SKEY array. */
+  if ( (err = gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY,
+                                 NULL, &npkey))
+       || (err = gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY,
+                                    NULL, &nskey)))
+    return err;
+  if (!npkey || npkey >= nskey)
+    return gpg_error (GPG_ERR_INTERNAL);
+  if (skeylen <= npkey)
+    return gpg_error (GPG_ERR_MISSING_VALUE);
+  if (nskey+1 >= skeysize)
+    return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
+  
+  /* Check whether SKEY is at all protected.  If it is not protected
+     merely verify the checksum.  */
+  if (!is_protected)
+    {
+      unsigned char *buffer;
+
+      actual_csum = 0;
+      for (i=npkey; i < nskey; i++)
+        {
+          if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE))
+            return gpg_error (GPG_ERR_BAD_SECKEY);
+          
+          err = gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, skey[i]);
+          if (!err)
+            {
+              buffer = (gcry_is_secure (skey[i])?
+                        xtrymalloc_secure (nbytes) : xtrymalloc (nbytes));
+              if (!buffer)
+                return gpg_error_from_syserror ();
+              err = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes,
+                                    NULL, skey[i]);
+              if (!err)
+                actual_csum += checksum (buffer, nbytes);
+              xfree (buffer);
+            }
+          if (err)
+            return err;
+        }
+      
+      if (actual_csum != desired_csum)
+        return gpg_error (GPG_ERR_CHECKSUM);
+      return 0;
+    }
+
+
+  if (gcry_cipher_test_algo (protect_algo))
+    {
+      /* The algorithm numbers are Libgcrypt numbers but fortunately
+         the OpenPGP algorithm numbers map one-to-one to the Libgcrypt
+         numbers.  */
+      log_info (_("protection algorithm %d (%s) is not supported\n"),
+                protect_algo, gcry_cipher_algo_name (protect_algo));
+      return gpg_error (GPG_ERR_CIPHER_ALGO);
+    }
+
+  if (gcry_md_test_algo (s2k_algo))
+    {
+      log_info (_("protection hash algorithm %d (%s) is not supported\n"),
+                s2k_algo, gcry_md_algo_name (s2k_algo));
+      return gpg_error (GPG_ERR_DIGEST_ALGO);
+    }
+  
+  err = gcry_cipher_open (&cipher_hd, protect_algo,
+                          GCRY_CIPHER_MODE_CFB,
+                          (GCRY_CIPHER_SECURE
+                           | (protect_algo >= 100 ?
+                              0 : GCRY_CIPHER_ENABLE_SYNC)));
+  if (err)
+    {
+      log_error ("failed to open cipher_algo %d: %s\n",
+                 protect_algo, gpg_strerror (err));
+      return err;
+    }
+
+  err = hash_passphrase_and_set_key (passphrase, cipher_hd, protect_algo,
+                                     s2k_mode, s2k_algo, s2k_salt, s2k_count);
+  if (err)
+    {
+      gcry_cipher_close (cipher_hd);
+      return err;
+    }  
+
+  gcry_cipher_setiv (cipher_hd, protect_iv, protect_ivlen);
+  
+  actual_csum = 0;
+  if (pkt_version >= 4)
+    {
+      int ndata;
+      unsigned int ndatabits;
+      unsigned char *p, *data;
+      u16 csum_pgp7 = 0;
+
+      if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE ))
+        {
+          gcry_cipher_close (cipher_hd);
+          return gpg_error (GPG_ERR_BAD_SECKEY);
+        }
+      p = gcry_mpi_get_opaque (skey[npkey], &ndatabits);
+      ndata = (ndatabits+7)/8;
+
+      if (ndata > 1)
+        csum_pgp7 = p[ndata-2] << 8 | p[ndata-1];
+      data = xtrymalloc_secure (ndata);
+      if (!data)
+        {
+          err = gpg_error_from_syserror ();
+          gcry_cipher_close (cipher_hd);
+          return err;
+        }
+      gcry_cipher_decrypt (cipher_hd, data, ndata, p, ndata);
+
+      p = data;
+      if (is_protected == 2)
+        {
+          /* This is the new SHA1 checksum method to detect tampering
+             with the key as used by the Klima/Rosa attack.  */
+          desired_csum = 0; 
+          actual_csum = 1;  /* Default to bad checksum.  */
+
+          if (ndata < 20) 
+            log_error ("not enough bytes for SHA-1 checksum\n");
+          else 
+            {
+              gcry_md_hd_t h;
+              
+              if (gcry_md_open (&h, GCRY_MD_SHA1, 1))
+                BUG(); /* Algo not available. */
+              gcry_md_write (h, data, ndata - 20);
+              gcry_md_final (h);
+              if (!memcmp (gcry_md_read (h, GCRY_MD_SHA1), data+ndata-20, 20))
+                actual_csum = 0; /* Digest does match.  */
+              gcry_md_close (h);
+            }
+        }
+      else 
+        {
+          /* Old 16 bit checksum method.  */
+          if (ndata < 2)
+            {
+              log_error ("not enough bytes for checksum\n");
+              desired_csum = 0; 
+              actual_csum = 1;  /* Mark checksum bad.  */
+            }
+          else
+            {
+              desired_csum = (data[ndata-2] << 8 | data[ndata-1]);
+              actual_csum = checksum (data, ndata-2);
+              if (desired_csum != actual_csum)
+                {
+                  /* This is a PGP 7.0.0 workaround */
+                  desired_csum = csum_pgp7; /* Take the encrypted one.  */
+                }
+            }
+        }
+      
+      /* Better check it here.  Otherwise the gcry_mpi_scan would fail
+         because the length may have an arbitrary value.  */
+      if (desired_csum == actual_csum)
+        {
+          for (i=npkey; i < nskey; i++ )
+            {
+              if (gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_PGP, p, ndata, &nbytes))
+                {
+                  /* Checksum was okay, but not correctly decrypted.  */
+                  desired_csum = 0;
+                  actual_csum = 1;   /* Mark checksum bad.  */
+                  break;
+                }
+              gcry_mpi_release (skey[i]);
+              skey[i] = tmpmpi;
+              ndata -= nbytes;
+              p += nbytes;
+            }
+          skey[i] = NULL;
+          skeylen = i;
+          assert (skeylen <= skeysize);
+
+          /* Note: at this point NDATA should be 2 for a simple
+             checksum or 20 for the sha1 digest.  */
+        }
+      xfree(data);
+    }
+  else /* Packet version <= 3.  */
+    {
+      unsigned char *buffer;
+
+      for (i = npkey; i < nskey; i++)
+        {
+          unsigned char *p;
+          size_t ndata;
+          unsigned int ndatabits;
+
+          if (!skey[i] || !gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE))
+            {
+              gcry_cipher_close (cipher_hd);
+              return gpg_error (GPG_ERR_BAD_SECKEY);
+            }
+          p = gcry_mpi_get_opaque (skey[i], &ndatabits);
+          ndata = (ndatabits+7)/8;
+
+          if (!(ndata >= 2) || !(ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2))
+            {
+              gcry_cipher_close (cipher_hd);
+              return gpg_error (GPG_ERR_BAD_SECKEY);
+            }
+          
+          buffer = xtrymalloc_secure (ndata);
+          if (!buffer)
+            {
+              err = gpg_error_from_syserror ();
+              gcry_cipher_close (cipher_hd);
+              return err;
+            }
+              
+          gcry_cipher_sync (cipher_hd);
+          buffer[0] = p[0];
+          buffer[1] = p[1];
+          gcry_cipher_decrypt (cipher_hd, buffer+2, ndata-2, p+2, ndata-2);
+          actual_csum += checksum (buffer, ndata);
+          err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_PGP, buffer, ndata, &ndata);
+          xfree (buffer);
+          if (err)
+            {
+              /* Checksum was okay, but not correctly decrypted.  */
+              desired_csum = 0;
+              actual_csum = 1;   /* Mark checksum bad.  */
+              break;
+            }
+          gcry_mpi_release (skey[i]);
+          skey[i] = tmpmpi;
+        }
+    }
+  gcry_cipher_close (cipher_hd);
+
+  /* Now let's see whether we have used the correct passphrase. */
+  if (actual_csum != desired_csum)
+    return gpg_error (GPG_ERR_BAD_PASSPHRASE);
+
+  if (nskey != skeylen)
+    err = gpg_error (GPG_ERR_BAD_SECKEY);
+  else
+    err = convert_secret_key (r_key, pubkey_algo, skey);
+  if (err)
+    return err;
+
+  /* The checksum may fail, thus we also check the key itself.  */
+  err = gcry_pk_testkey (*r_key);
+  if (err)
+    {
+      gcry_sexp_release (*r_key);
+      *r_key = NULL;
+      return gpg_error (GPG_ERR_BAD_PASSPHRASE);
+    }
+
+  return 0;
+}
+
+
+/* Callback function to try the unprotection from the passpharse query
+   code.  */
+static int
+try_do_unprotect_cb (struct pin_entry_info_s *pi)
+{
+  gpg_error_t err;
+  struct try_do_unprotect_arg_s *arg = pi->check_cb_arg;
+
+  err = do_unprotect (pi->pin, arg->is_v4? 4:3,
+                      arg->pubkey_algo, arg->is_protected,
+                      arg->skey, arg->skeysize,
+                      arg->protect_algo, arg->iv, arg->ivlen,
+                      arg->s2k_mode, arg->s2k_algo,
+                      arg->s2k_salt, arg->s2k_count,
+                      arg->desired_csum, arg->r_key);
+  /* SKEY may be modified now, thus we need to re-compute SKEYIDX.  */
+  for (arg->skeyidx = 0; (arg->skeyidx < arg->skeysize
+                          && arg->skey[arg->skeyidx]); arg->skeyidx++)
+         ;
+  return err;
+}
+
+
+/* Convert an OpenPGP transfer key into our internal format.  Before
+   asking for a passphrase we check whether the key already exists in
+   our key storage.  S_PGP is the OpenPGP key in transfer format.  On
+   success R_KEY will receive a canonical encoded S-expression with
+   the unprotected key in our internal format; the caller needs to
+   release that memory.  The passphrase used to decrypt the OpenPGP
+   key will be returned at R_PASSPHRASE; the caller must release this
+   passphrase.  The keygrip will be stored at the 20 byte buffer
+   pointed to by GRIP.  On error NULL is stored at all return
+   arguments.  */
+gpg_error_t
+convert_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, 
+                 unsigned char *grip, const char *prompt,
+                 unsigned char **r_key, char **r_passphrase)
+{
+  gpg_error_t err;
+  gcry_sexp_t top_list;
+  gcry_sexp_t list = NULL;
+  const char *value;
+  size_t valuelen;
+  char *string;
+  int  idx;
+  int  is_v4, is_protected;
+  int  pubkey_algo;
+  int  protect_algo = 0;
+  char iv[16];
+  int  ivlen = 0;
+  int  s2k_mode = 0;
+  int  s2k_algo = 0;
+  byte s2k_salt[8];
+  u32  s2k_count = 0;
+  size_t npkey, nskey;
+  gcry_mpi_t skey[10];  /* We support up to 9 parameters.  */
+  u16 desired_csum;
+  int skeyidx = 0;
+  gcry_sexp_t s_skey;
+  struct pin_entry_info_s *pi;
+  struct try_do_unprotect_arg_s pi_arg;
+
+  *r_key = NULL;
+  *r_passphrase = NULL;
+
+  top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0);
+  if (!top_list)
+    goto bad_seckey;
+
+  list = gcry_sexp_find_token (top_list, "version", 0);
+  if (!list)
+    goto bad_seckey;
+  value = gcry_sexp_nth_data (list, 1, &valuelen);
+  if (!value || valuelen != 1 || !(value[0] == '3' || value[0] == '4'))
+    goto bad_seckey;
+  is_v4 = (value[0] == '4');
+
+  gcry_sexp_release (list);
+  list = gcry_sexp_find_token (top_list, "protection", 0);
+  if (!list)
+    goto bad_seckey;
+  value = gcry_sexp_nth_data (list, 1, &valuelen);
+  if (!value)
+    goto bad_seckey;
+  if (valuelen == 4 && !memcmp (value, "sha1", 4))
+    is_protected = 2;
+  else if (valuelen == 3 && !memcmp (value, "sum", 3))
+    is_protected = 1;
+  else if (valuelen == 4 && !memcmp (value, "none", 4))
+    is_protected = 0;
+  else
+    goto bad_seckey;
+  if (is_protected)
+    {
+      string = gcry_sexp_nth_string (list, 2);
+      if (!string)
+        goto bad_seckey;
+      protect_algo = gcry_cipher_map_name (string);
+      if (!protect_algo && !!strcmp (string, "IDEA"))
+        protect_algo = GCRY_CIPHER_IDEA;
+      xfree (string);
+      
+      value = gcry_sexp_nth_data (list, 3, &valuelen);
+      if (!value || !valuelen || valuelen > sizeof iv)
+        goto bad_seckey;
+      memcpy (iv, value, valuelen);
+      ivlen = valuelen;
+
+      string = gcry_sexp_nth_string (list, 4);
+      if (!string)
+        goto bad_seckey;
+      s2k_mode = strtol (string, NULL, 10);
+      xfree (string);
+
+      string = gcry_sexp_nth_string (list, 5);
+      if (!string)
+        goto bad_seckey;
+      s2k_algo = gcry_md_map_name (string);
+      xfree (string);
+
+      value = gcry_sexp_nth_data (list, 6, &valuelen);
+      if (!value || !valuelen || valuelen > sizeof s2k_salt)
+        goto bad_seckey;
+      memcpy (s2k_salt, value, valuelen);
+
+      string = gcry_sexp_nth_string (list, 7);
+      if (!string)
+        goto bad_seckey;
+      s2k_count = strtoul (string, NULL, 10);
+      xfree (string);
+    }
+
+  gcry_sexp_release (list);
+  list = gcry_sexp_find_token (top_list, "algo", 0);
+  if (!list)
+    goto bad_seckey;
+  string = gcry_sexp_nth_string (list, 1);
+  if (!string)
+    goto bad_seckey;
+  pubkey_algo = gcry_pk_map_name (string);
+  xfree (string);
+
+  if (gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey)
+      || gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey)
+      || !npkey || npkey >= nskey)
+    goto bad_seckey;
+
+  gcry_sexp_release (list);
+  list = gcry_sexp_find_token (top_list, "skey", 0);
+  if (!list)
+    goto bad_seckey;
+  for (idx=0;;)
+    {
+      int is_enc;
+
+      value = gcry_sexp_nth_data (list, ++idx, &valuelen);
+      if (!value && skeyidx >= npkey)
+        break;  /* Ready.  */
+
+      /* Check for too many parameters.  Note that depending on the
+         protection mode and version number we may see less than NSKEY
+         (but at least NPKEY+1) parameters.  */
+      if (idx >= 2*nskey)
+        goto bad_seckey;
+      if (skeyidx >= DIM (skey)-1)
+        goto bad_seckey;
+
+      if (!value || valuelen != 1 || !(value[0] == '_' || value[0] == 'e'))
+        goto bad_seckey;
+      is_enc = (value[0] == 'e');
+      value = gcry_sexp_nth_data (list, ++idx, &valuelen);
+      if (!value || !valuelen)
+        goto bad_seckey;
+      if (is_enc)
+        {
+          void *p = xtrymalloc (valuelen);
+          if (!p)
+            goto outofmem;
+          memcpy (p, value, valuelen);
+          skey[skeyidx] = gcry_mpi_set_opaque (NULL, p, valuelen*8);
+          if (!skey[skeyidx])
+            goto outofmem;
+        }
+      else
+        {
+          if (gcry_mpi_scan (skey + skeyidx, GCRYMPI_FMT_STD,
+                             value, valuelen, NULL))
+            goto bad_seckey;
+        }
+      skeyidx++;
+    }
+  skey[skeyidx++] = NULL;
+
+  gcry_sexp_release (list);
+  list = gcry_sexp_find_token (top_list, "csum", 0);
+  if (list)
+    {
+      string = gcry_sexp_nth_string (list, 1);
+      if (!string)
+        goto bad_seckey;
+      desired_csum = strtoul (string, NULL, 10);
+      xfree (string);
+    }
+  else
+    desired_csum = 0;
+
+
+  gcry_sexp_release (list); list = NULL;
+  gcry_sexp_release (top_list); top_list = NULL;
+
+  /* log_debug ("XXX is_v4=%d\n", is_v4); */
+  /* log_debug ("XXX pubkey_algo=%d\n", pubkey_algo); */
+  /* log_debug ("XXX is_protected=%d\n", is_protected); */
+  /* log_debug ("XXX protect_algo=%d\n", protect_algo); */
+  /* log_printhex ("XXX iv", iv, ivlen); */
+  /* log_debug ("XXX ivlen=%d\n", ivlen); */
+  /* log_debug ("XXX s2k_mode=%d\n", s2k_mode); */
+  /* log_debug ("XXX s2k_algo=%d\n", s2k_algo); */
+  /* log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt); */
+  /* log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count); */
+  /* for (idx=0; skey[idx]; idx++) */
+  /*   { */
+  /*     int is_enc = gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE); */
+  /*     log_info ("XXX skey[%d]%s:", idx, is_enc? " (enc)":""); */
+  /*     if (is_enc) */
+  /*       { */
+  /*         void *p; */
+  /*         unsigned int nbits; */
+  /*         p = gcry_mpi_get_opaque (skey[idx], &nbits); */
+  /*         log_printhex (NULL, p, (nbits+7)/8); */
+  /*       } */
+  /*     else */
+  /*       gcry_mpi_dump (skey[idx]); */
+  /*     log_printf ("\n"); */
+  /*   } */
+
+  err = get_keygrip (pubkey_algo, skey, grip);
+  if (err)
+    goto leave;
+
+  if (!agent_key_available (grip))
+    {
+      err = gpg_error (GPG_ERR_EEXIST);
+      goto leave;
+    }
+
+  pi = xtrycalloc_secure (1, sizeof (*pi) + 100);
+  if (!pi)
+    return gpg_error_from_syserror ();
+  pi->max_length = 100;
+  pi->min_digits = 0;  /* We want a real passphrase.  */
+  pi->max_digits = 16;
+  pi->max_tries = 3;
+  pi->check_cb = try_do_unprotect_cb;
+  pi->check_cb_arg = &pi_arg;
+  pi_arg.is_v4 = is_v4;
+  pi_arg.is_protected = is_protected;
+  pi_arg.pubkey_algo = pubkey_algo;
+  pi_arg.protect_algo = protect_algo;
+  pi_arg.iv = iv;
+  pi_arg.ivlen = ivlen;
+  pi_arg.s2k_mode = s2k_mode;
+  pi_arg.s2k_algo = s2k_algo;
+  pi_arg.s2k_salt = s2k_salt;
+  pi_arg.s2k_count = s2k_count;
+  pi_arg.desired_csum = desired_csum;
+  pi_arg.skey = skey;
+  pi_arg.skeysize = DIM (skey);
+  pi_arg.skeyidx = skeyidx;
+  pi_arg.r_key = &s_skey;
+  err = agent_askpin (ctrl, prompt, NULL, NULL, pi);
+  skeyidx = pi_arg.skeyidx;
+  if (!err)
+    {
+      *r_passphrase = xtrystrdup (pi->pin);
+      if (!*r_passphrase)
+        err = gpg_error_from_syserror ();
+    }
+  xfree (pi);
+  if (err)
+    goto leave;
+
+  /* Save some memory and get rid of the SKEY array now.  */
+  for (idx=0; idx < skeyidx; idx++)
+    gcry_mpi_release (skey[idx]);
+  skeyidx = 0;
+
+  /* Note that the padding is not required - we use it only because
+     that function allows us to created the result in secure memory.  */
+  err = make_canon_sexp_pad (s_skey, 1, r_key, NULL);
+  gcry_sexp_release (s_skey);
+
+ leave:
+  gcry_sexp_release (list);
+  gcry_sexp_release (top_list);
+  for (idx=0; idx < skeyidx; idx++)
+    gcry_mpi_release (skey[idx]);
+  if (err)
+    {
+      xfree (*r_passphrase);
+      *r_passphrase = NULL;
+    }
+  return err;
+
+ bad_seckey:
+  err = gpg_error (GPG_ERR_BAD_SECKEY);
+  goto leave;
+  
+ outofmem:
+  err = gpg_error (GPG_ERR_ENOMEM);
+  goto leave;
+
+}
+
+
+
diff --git a/agent/cvt-openpgp.h b/agent/cvt-openpgp.h
new file mode 100644 (file)
index 0000000..17b1a6e
--- /dev/null
@@ -0,0 +1,27 @@
+/* cvt-openpgp.h - Convert an OpenPGP key to our internal format.
+ * Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef GNUPG_AGENT_CVT_OPENPGP_H
+#define GNUPG_AGENT_CVT_OPENPGP_H
+
+gpg_error_t convert_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, 
+                             unsigned char *grip, const char *prompt,
+                             unsigned char **r_key, char **r_passphrase);
+
+
+#endif /*GNUPG_AGENT_CVT_OPENPGP_H*/
index 5668aaf..7622111 100644 (file)
@@ -702,7 +702,8 @@ key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
 }
 
 
-/* Return true if S_KEY is a DSA style key.  */
+/* Return the public key algorithm number if S_KEY is a DSA style key.
+   If it is not a DSA style key, return 0.  */
 int 
 agent_is_dsa_key (gcry_sexp_t s_key)
 {
@@ -714,7 +715,12 @@ agent_is_dsa_key (gcry_sexp_t s_key)
   if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0))
     return 0; /* Error - assume it is not an DSA key.  */
 
-  return (!strcmp (algoname, "dsa") || !strcmp (algoname, "ecdsa"));
+  if (!strcmp (algoname, "dsa"))
+    return GCRY_PK_DSA;
+  else if (!strcmp (algoname, "ecdsa"))
+    return GCRY_PK_ECDSA;
+  else
+    return 0;
 }
 
 
index e246e88..841e584 100644 (file)
@@ -159,7 +159,34 @@ second list with the information has this layout:
 More items may be added to the list.
 
 
-
+OpenPGP Private Key Transfer Format
+===================================
+
+This format is used to transfer keys between gpg and gpg-agent.
+
+(openpgp-private-key
+  (version V)
+  (protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT)
+  (algo PUBKEYALGO)
+  (skey CSUM c P1 c P2 c P3 ... e PN))
+
+
+* V is the packet version number (3 or 4).
+* PUBKEYALGO is a Libgcrypt algo name 
+* CSUM is the 16 bit checksum as defined by OpenPGP.
+* P1 .. PN are the parameters; the public parameters are never encrypted
+  the secrect key parameters are encrypted if the "protection" list is
+  given.  To make this more explicit each parameter is preceded by a
+  flag "_" for cleartext or "e" for encrypted text.
+* If PROTTYPE is "sha1" the new style SHA1 checksum is used if it is "sum"
+  the old 16 bit checksum is used and if it is "none" no protection at
+  all is used.
+* PROTALGO is a Libgcrypt style cipher algorithm name
+* IV is the initialization verctor.
+* S2KMODE is the value from RFC-4880.
+* S2KHASH is a a libgcrypt style hash algorithm identifier.
+* S2KSALT is the 8 byte salt
+* S2KCOUNT is the count value from RFC-4880.
 
 
 
index 7ae50a9..28e208e 100644 (file)
@@ -164,10 +164,22 @@ do_encode_dsa (const byte * md, size_t mdlen, int dsaalgo, gcry_sexp_t pkey,
   if (mdlen > qbits/8)
     mdlen = qbits/8;
             
-  /* Create the S-expression.  */
-  err = gcry_sexp_build (&hash, NULL,
-                        "(data (flags raw) (value %b))",
-                        (int)mdlen, md);
+  /* Create the S-expression.  We need to convert to an MPI first
+     because we want an unsigned integer.  Using %b directly is not
+     possible because libgcrypt assumes an mpi and uses
+     GCRYMPI_FMT_STD for parsing and thus possible yielding a negative
+     value.  */
+  {
+    gcry_mpi_t mpi;
+      
+    err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_USG, md, mdlen, NULL);
+    if (!err)
+      {
+        err = gcry_sexp_build (&hash, NULL,
+                               "(data (flags raw) (value %m))", mpi);
+        gcry_mpi_release (mpi);
+      }
+  }
   if (!err)
     *r_hash = hash;
   return err;   
@@ -304,8 +316,10 @@ agent_pksign_do (ctrl_t ctrl, const char *desc_text,
 
       if (DBG_CRYPTO)
         {
-          log_debug ("skey: ");
+          log_debug ("skey:\n");
           gcry_sexp_dump (s_skey);
+          log_debug ("hash:\n");
+          gcry_sexp_dump (s_hash);
         }
 
       /* sign */
@@ -319,7 +333,7 @@ agent_pksign_do (ctrl_t ctrl, const char *desc_text,
 
       if (DBG_CRYPTO)
         {
-          log_debug ("result: ");
+          log_debug ("result:\n");
           gcry_sexp_dump (s_sig);
         }
     }
index db6caa4..3a983e2 100644 (file)
@@ -73,6 +73,8 @@ hash_passphrase (const char *passphrase, int hashalgo,
                  const unsigned char *s2ksalt, unsigned long s2kcount,
                  unsigned char *key, size_t keylen);
 
+
+\f
 /* Get the process time and store it in DATA.  */
 static void
 calibrate_get_time (struct calibrate_time_s *data)
@@ -1076,6 +1078,19 @@ hash_passphrase (const char *passphrase, int hashalgo,
 }
 
 
+gpg_error_t
+s2k_hash_passphrase (const char *passphrase, int hashalgo,
+                     int s2kmode,
+                     const unsigned char *s2ksalt,
+                     unsigned int s2kcount,
+                     unsigned char *key, size_t keylen)
+{
+  return hash_passphrase (passphrase, hashalgo, s2kmode, s2ksalt, 
+                          (16ul + (s2kcount & 15)) << ((s2kcount >> 4) + 6),
+                          key, keylen);
+}
+
+
 \f
 
 /* Create an canonical encoded S-expression with the shadow info from
index eb19158..abfa6f7 100644 (file)
@@ -1,3 +1,32 @@
+2010-08-30  Werner Koch  <wk@g10code.com>
+
+       * keyid.c (KEYID_STR_SIZE): New
+       (keystr): Use snprintf and new macro.
+       (keystr_with_sub): New.
+       (keystr_from_sk_with_sub): New.
+       (keystr_from_pk_with_sub): New.
+
+2010-08-27  Werner Koch  <wk@g10code.com>
+
+       * gpg.c (main): Change scope of CTRL to the entire function.
+
+       * import.c (import_secret_one, import, import_keys_internal)
+       (import_keys, import_keys_stream): Add arg CTRL.
+       * call-agent.c (agent_keywrap_key): New.
+       (agent_import_key, inq_import_key_parms): New.
+
+2010-08-26  Werner Koch  <wk@g10code.com>
+
+       * misc.c (openpgp_pk_algo_name): New.
+       (openpgp_md_algo_name): New.
+
+2010-08-24  Werner Koch  <wk@g10code.com>
+
+       * options.h (IMPORT_SK2PK): Remove.
+       * import.c (parse_import_options): Turn convert-sk-to-pk into a
+       dummy option.
+       (sec_to_pub_keyblock): Use modern functions.
+
 2010-08-16  Werner Koch  <wk@g10code.com>
 
        * gpg.c (list_config, gpgconf_list): Use es_printf.
index ea81c6b..7f98cfb 100644 (file)
@@ -77,6 +77,13 @@ struct genkey_parm_s
   const char *keyparms;
 };
 
+struct import_key_parm_s
+{
+  ctrl_t ctrl;
+  assuan_context_t ctx;
+  const void *key;
+  size_t keylen;
+};
 
 
 static gpg_error_t learn_status_cb (void *opaque, const char *line);
@@ -1706,3 +1713,97 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
   *r_buf = buf;
   return 0;
 }
+
+
+\f
+/* Retrieve a key encryption key from the agent.  With FOREXPORT true
+   the key shall be used for export, with false for import.  On success
+   the new key is stored at R_KEY and its length at R_KEKLEN.  */
+gpg_error_t
+agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen)
+{
+  gpg_error_t err;
+  membuf_t data;
+  size_t len;
+  unsigned char *buf;
+  char line[ASSUAN_LINELENGTH];
+
+  *r_kek = NULL;
+  err = start_agent (ctrl, 0);
+  if (err)
+    return err;
+
+  snprintf (line, DIM(line)-1, "KEYWRAP_KEY %s",
+            forexport? "--export":"--import");
+
+  init_membuf_secure (&data, 64);
+  err = assuan_transact (agent_ctx, line,
+                         membuf_data_cb, &data, 
+                         default_inq_cb, ctrl, NULL, NULL);
+  if (err)
+    {
+      xfree (get_membuf (&data, &len));
+      return err;
+    }
+  buf = get_membuf (&data, &len);
+  if (!buf)
+    return gpg_error_from_syserror ();
+  *r_kek = buf;
+  *r_keklen = len;
+  return 0;
+}
+
+
+\f
+/* Handle the inquiry for an IMPORT_KEY command.  */
+static gpg_error_t
+inq_import_key_parms (void *opaque, const char *line)
+{
+  struct import_key_parm_s *parm = opaque; 
+  gpg_error_t err;
+
+  if (!strncmp (line, "KEYDATA", 7) && (line[7]==' '||!line[7]))
+    {
+      err = assuan_send_data (parm->ctx, parm->key, parm->keylen);
+    }
+  else
+    err = default_inq_cb (parm->ctrl, line);
+
+  return err; 
+}
+
+
+/* Call the agent to import a key into the agent.  */
+gpg_error_t
+agent_import_key (ctrl_t ctrl, const char *desc, const void *key, size_t keylen)
+{
+  gpg_error_t err;
+  struct import_key_parm_s parm;
+
+  err = start_agent (ctrl, 0);
+  if (err)
+    return err;
+
+  if (desc)
+    {
+      char line[ASSUAN_LINELENGTH];
+
+      snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+      line[DIM(line)-1] = 0;
+      err = assuan_transact (agent_ctx, line,
+                            NULL, NULL, NULL, NULL, NULL, NULL);
+      if (err)
+        return err;
+    }
+
+  parm.ctrl   = ctrl;
+  parm.ctx    = agent_ctx;
+  parm.key    = key;
+  parm.keylen = keylen;
+
+  err = assuan_transact (agent_ctx, "IMPORT_KEY",
+                         NULL, NULL, inq_import_key_parms, &parm, NULL, NULL);
+  return err;
+}
+
+
index c8e9208..7495b2a 100644 (file)
@@ -163,6 +163,14 @@ gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
                              gcry_sexp_t s_ciphertext,
                              unsigned char **r_buf, size_t *r_buflen);
 
+/* Retrieve a key encryption key.  */
+gpg_error_t agent_keywrap_key (ctrl_t ctrl, int forexport,
+                               void **r_kek, size_t *r_keklen);
+
+/* Send a key to the agent.  */
+gpg_error_t agent_import_key (ctrl_t ctrl, const char *desc,
+                              const void *key, size_t keylen);
+
 
 #endif /*GNUPG_G10_CALL_AGENT_H*/
 
index 01c307b..b038362 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -1925,6 +1925,7 @@ main (int argc, char **argv)
     int any_explicit_recipient = 0;
     int require_secmem=0,got_secmem=0;
     struct assuan_malloc_hooks malloc_hooks;
+    ctrl_t ctrl;
 
 #ifdef __riscos__
     opt.lock_once = 1;
@@ -1984,23 +1985,24 @@ main (int argc, char **argv)
     opt.pgp2_workarounds = 1;
     opt.escape_from = 1;
     opt.flags.require_cross_cert = 1;
-    opt.import_options=IMPORT_SK2PK;
-    opt.export_options=EXPORT_ATTRIBUTES;
-    opt.keyserver_options.import_options=IMPORT_REPAIR_PKS_SUBKEY_BUG;
-    opt.keyserver_options.export_options=EXPORT_ATTRIBUTES;
-    opt.keyserver_options.options=
-      KEYSERVER_HONOR_KEYSERVER_URL|KEYSERVER_HONOR_PKA_RECORD;
-    opt.verify_options=
-      VERIFY_SHOW_POLICY_URLS|VERIFY_SHOW_STD_NOTATIONS|VERIFY_SHOW_KEYSERVER_URLS;
-    opt.trust_model=TM_AUTO;
-    opt.mangle_dos_filenames=0;
-    opt.min_cert_level=2;
-    set_screen_dimensions();
-    opt.keyid_format=KF_SHORT;
-    opt.def_sig_expire="0";
-    opt.def_cert_expire="0";
-    set_homedir ( default_homedir () );
-    opt.passphrase_repeat=1;
+    opt.import_options = 0;
+    opt.export_options = EXPORT_ATTRIBUTES;
+    opt.keyserver_options.import_options = IMPORT_REPAIR_PKS_SUBKEY_BUG;
+    opt.keyserver_options.export_options = EXPORT_ATTRIBUTES;
+    opt.keyserver_options.options = (KEYSERVER_HONOR_KEYSERVER_URL
+                                     | KEYSERVER_HONOR_PKA_RECORD );
+    opt.verify_options = (VERIFY_SHOW_POLICY_URLS
+                          | VERIFY_SHOW_STD_NOTATIONS
+                          | VERIFY_SHOW_KEYSERVER_URLS);
+    opt.trust_model = TM_AUTO;
+    opt.mangle_dos_filenames = 0;
+    opt.min_cert_level = 2;
+    set_screen_dimensions ();
+    opt.keyid_format = KF_SHORT;
+    opt.def_sig_expire = "0";
+    opt.def_cert_expire = "0";
+    set_homedir (default_homedir ());
+    opt.passphrase_repeat = 1;
 
     /* Check whether we have a config file on the command line.  */
     orig_argc = argc;
@@ -3403,6 +3405,9 @@ main (int argc, char **argv)
     if(fname && utf8_strings)
       opt.flags.utf8_filename=1;
 
+    ctrl = xtrycalloc (1, sizeof *ctrl);
+    gpg_init_default_ctrl (ctrl);
+
     switch( cmd ) {
       case aPrimegen:
       case aPrintMD:
@@ -3438,13 +3443,7 @@ main (int argc, char **argv)
     switch( cmd )
       {
       case aServer:
-        {
-          ctrl_t ctrl = xtrycalloc (1, sizeof *ctrl);
-          gpg_init_default_ctrl (ctrl);
-          gpg_server (ctrl);
-          gpg_deinit_default_ctrl (ctrl);
-          xfree (ctrl);
-        }
+        gpg_server (ctrl);
         break;
 
       case aStore: /* only store the file */
@@ -3704,7 +3703,7 @@ main (int argc, char **argv)
       case aFastImport:
         opt.import_options |= IMPORT_FAST;
       case aImport:
-       import_keys( argc? argv:NULL, argc, NULL, opt.import_options );
+       import_keys (ctrl, argc? argv:NULL, argc, NULL, opt.import_options);
        break;
 
        /* TODO: There are a number of command that use this same
@@ -4055,6 +4054,8 @@ main (int argc, char **argv)
       }
 
     /* cleanup */
+    gpg_deinit_default_ctrl (ctrl);
+    xfree (ctrl);
     release_armor_context (afx);
     FREE_STRLIST(remusr);
     FREE_STRLIST(locusr);
index 53349ee..13773da 100644 (file)
@@ -1,6 +1,6 @@
 /* import.c - import a key into our key storage.
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
- *               2007 Free Software Foundation, Inc.
+ *               2007, 2010 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -37,6 +37,8 @@
 #include "ttyio.h"
 #include "status.h"
 #include "keyserver-internal.h"
+#include "call-agent.h"
+#include "../common/membuf.h"
 
 struct stats_s {
     ulong count;
@@ -58,14 +60,15 @@ struct stats_s {
 };
 
 
-static int import( IOBUF inp, const char* fname,struct stats_s *stats,
-                  unsigned char **fpr,size_t *fpr_len,unsigned int options );
+static int import (ctrl_t ctrl,
+                   IOBUF inp, const char* fname, struct stats_s *stats,
+                  unsigned char **fpr, size_t *fpr_len, unsigned int options);
 static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root );
 static void revocation_present(KBNODE keyblock);
 static int import_one(const char *fname, KBNODE keyblock,struct stats_s *stats,
                      unsigned char **fpr,size_t *fpr_len,
                      unsigned int options,int from_sk);
-static int import_secret_one( const char *fname, KBNODE keyblock,
+static int import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock,
                               struct stats_s *stats, unsigned int options);
 static int import_revoke_cert( const char *fname, KBNODE node,
                                struct stats_s *stats);
@@ -96,8 +99,6 @@ parse_import_options(char *str,unsigned int *options,int noisy)
        N_("repair damage from the pks keyserver during import")},
       {"fast-import",IMPORT_FAST,NULL,
        N_("do not update the trustdb after import")},
-      {"convert-sk-to-pk",IMPORT_SK2PK,NULL,
-       N_("create a public key when importing a secret key")},
       {"merge-only",IMPORT_MERGE_ONLY,NULL,
        N_("only accept updates to existing keys")},
       {"import-clean",IMPORT_CLEAN,NULL,
@@ -111,6 +112,7 @@ parse_import_options(char *str,unsigned int *options,int noisy)
       {"import-unusable-sigs",0,NULL,NULL},
       {"import-clean-sigs",0,NULL,NULL},
       {"import-clean-uids",0,NULL,NULL},
+      {"convert-sk-to-pk",0, NULL,NULL},
       {NULL,0,NULL,NULL}
     };
 
@@ -161,7 +163,7 @@ import_release_stats_handle (void *p)
  *
  */
 static int
-import_keys_internal( IOBUF inp, char **fnames, int nnames,
+import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames,
                      void *stats_handle, unsigned char **fpr, size_t *fpr_len,
                      unsigned int options )
 {
@@ -172,7 +174,7 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames,
         stats = import_new_stats_handle ();
 
     if (inp) {
-        rc = import( inp, "[stream]", stats, fpr, fpr_len, options);
+      rc = import (ctrl, inp, "[stream]", stats, fpr, fpr_len, options);
     }
     else {
         if( !fnames && !nnames )
@@ -193,7 +195,7 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames,
                log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
            else
              {
-               rc = import( inp2, fname, stats, fpr, fpr_len, options );
+               rc = import (ctrl, inp2, fname, stats, fpr, fpr_len, options);
                iobuf_close(inp2);
                 /* Must invalidate that ugly cache to actually close it. */
                 iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 
@@ -224,21 +226,23 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames,
 }
 
 void
-import_keys( char **fnames, int nnames,
+import_keys (ctrl_t ctrl, char **fnames, int nnames,
             void *stats_handle, unsigned int options )
 {
-  import_keys_internal(NULL,fnames,nnames,stats_handle,NULL,NULL,options);
+  import_keys_internal (ctrl, NULL, fnames, nnames, stats_handle,
+                        NULL, NULL, options);
 }
 
 int
-import_keys_stream( IOBUF inp, void *stats_handle,
-                   unsigned char **fpr, size_t *fpr_len,unsigned int options )
+import_keys_stream (ctrl_t ctrl, IOBUF inp, void *stats_handle,
+                   unsigned char **fpr, size_t *fpr_len,unsigned int options)
 {
-  return import_keys_internal(inp,NULL,0,stats_handle,fpr,fpr_len,options);
+  return import_keys_internal (ctrl, inp, NULL, 0, stats_handle,
+                               fpr, fpr_len, options);
 }
 
 static int
-import( IOBUF inp, const char* fname,struct stats_s *stats,
+import (ctrl_t ctrl, IOBUF inp, const char* fname,struct stats_s *stats,
        unsigned char **fpr,size_t *fpr_len,unsigned int options )
 {
     PACKET *pending_pkt = NULL;
@@ -262,7 +266,7 @@ import( IOBUF inp, const char* fname,struct stats_s *stats,
        if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY )
            rc = import_one( fname, keyblock, stats, fpr, fpr_len, options, 0);
        else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) 
-                rc = import_secret_one( fname, keyblock, stats, options );
+          rc = import_secret_one (ctrl, fname, keyblock, stats, options);
        else if( keyblock->pkt->pkttype == PKT_SIGNATURE
                 && keyblock->pkt->pkt.signature->sig_class == 0x20 )
            rc = import_revoke_cert( fname, keyblock, stats );
@@ -528,7 +532,7 @@ fix_pks_corruption(KBNODE keyblock)
    equal.  Although direct key signatures are now checked during
    import, there might still be bogus signatures sitting in a keyring.
    We need to detect and delete them before doing a merge.  This
-   fucntion returns the number of removed sigs.  */
+   function returns the number of removed sigs.  */
 static int
 fix_bad_direct_key_sigs (kbnode_t keyblock, u32 *keyid)
 {
@@ -1076,66 +1080,298 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
     return rc;
 }
 
-/* Walk a secret keyblock and produce a public keyblock out of it. */
-static KBNODE
-sec_to_pub_keyblock(KBNODE sec_keyblock)
+
+/* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent.  The
+   function prints diagnostics and returns an error code. */
+static gpg_error_t
+transfer_secret_keys (ctrl_t ctrl, kbnode_t sec_keyblock)
 {
-  KBNODE secnode,pub_keyblock=NULL,ctx=NULL;
+  gpg_error_t err = 0;
+  void *kek = NULL;
+  size_t keklen;
+  kbnode_t ctx = NULL;
+  kbnode_t node;
+  PKT_secret_key *main_sk, *sk;
+  int nskey;
+  membuf_t mbuf;
+  int i, j;
+  size_t n;
+  void *format_args_buf_ptr[PUBKEY_MAX_NSKEY];
+  int   format_args_buf_int[PUBKEY_MAX_NSKEY];
+  void *format_args[2*PUBKEY_MAX_NSKEY];
+  gcry_sexp_t skey, prot, tmpsexp;
+  unsigned char *transferkey = NULL;
+  size_t transferkeylen;
+  gcry_cipher_hd_t cipherhd = NULL;
+  unsigned char *wrappedkey = NULL;
+  size_t wrappedkeylen;
+
+  /* Get the current KEK.  */
+  err = agent_keywrap_key (ctrl, 0, &kek, &keklen);
+  if (err)
+    {
+      log_error ("error getting the KEK: %s\n", gpg_strerror (err));
+      goto leave;
+    }
 
-  while((secnode=walk_kbnode(sec_keyblock,&ctx,0)))
+  /* Prepare a cipher context.  */
+  err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
+                          GCRY_CIPHER_MODE_AESWRAP, 0);
+  if (!err)
+    err = gcry_cipher_setkey (cipherhd, kek, keklen);
+  if (err)
+    goto leave;
+  xfree (kek);
+  kek = NULL;
+
+  main_sk = NULL;
+  while ((node = walk_kbnode (sec_keyblock, &ctx, 0)))
     {
-      KBNODE pubnode;
+      if (node->pkt->pkttype != PKT_SECRET_KEY
+          && node->pkt->pkttype != PKT_SECRET_SUBKEY)
+        continue;
+      sk = node->pkt->pkt.secret_key;
+      if (!main_sk)
+        main_sk = sk;
+
+      /* Convert our internal secret key object into an S-expression.  */
+      nskey = pubkey_get_nskey (sk->pubkey_algo);
+      if (!nskey || nskey > PUBKEY_MAX_NSKEY)
+        {
+          err = gpg_error (GPG_ERR_BAD_SECKEY);
+          log_error ("internal error: %s\n", gpg_strerror (err));
+          goto leave;
+        }
+
+      init_membuf (&mbuf, 50);
+      put_membuf_str (&mbuf, "(skey");
+      for (i=j=0; i < nskey; i++)
+        {
+          if (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE))
+            {
+              put_membuf_str (&mbuf, " e %b");
+              format_args_buf_ptr[i] = gcry_mpi_get_opaque (sk->skey[i], &n);
+              format_args_buf_int[i] = (n+7)/8;
+              format_args[j++] = format_args_buf_int + i;
+              format_args[j++] = format_args_buf_ptr + i;
+            }
+          else
+            {
+              put_membuf_str (&mbuf, " _ %m");
+              format_args[j++] = sk->skey + i;
+            }
+        }
+      put_membuf_str (&mbuf, ")\n");
+      put_membuf (&mbuf, "", 1);
+      {
+        char *format = get_membuf (&mbuf, NULL);
+        if (!format)
+          err = gpg_error_from_syserror ();
+        else
+          err = gcry_sexp_build_array (&skey, NULL, format, format_args);
+        xfree (format);
+      }
+      if (err)
+        {
+          log_error ("error building skey array: %s\n", gpg_strerror (err));
+          goto leave;
+        }
+
+      if (sk->is_protected)
+        {
+          char countbuf[35];
+
+          snprintf (countbuf, sizeof countbuf, "%lu",
+                    (unsigned long)sk->protect.s2k.count);
+          err = gcry_sexp_build
+            (&prot, NULL,
+             " (protection %s %s %b %d %s %b %s)\n",
+             sk->protect.sha1chk? "sha1":"sum",
+             openpgp_cipher_algo_name (sk->protect.algo),
+             (int)sk->protect.ivlen, sk->protect.iv,
+             sk->protect.s2k.mode,
+             openpgp_md_algo_name (sk->protect.s2k.hash_algo),
+             (int)sizeof (sk->protect.s2k.salt), sk->protect.s2k.salt,
+             countbuf);
+        }
+      else
+        err = gcry_sexp_build (&prot, NULL, " (protection none)\n");
+
+      tmpsexp = NULL;
+      xfree (transferkey);
+      transferkey = NULL;
+      if (!err)
+        err = gcry_sexp_build (&tmpsexp, NULL,
+                               "(openpgp-private-key\n"
+                               " (version %d)\n"
+                               " (algo %s)\n"
+                               " %S\n"
+                               " (csum %d)\n"
+                               " %S)\n",
+                               sk->version,
+                               openpgp_pk_algo_name (sk->pubkey_algo),
+                               skey, (int)(unsigned long)sk->csum, prot);
+      gcry_sexp_release (skey);
+      gcry_sexp_release (prot);
+      if (!err)
+        err = make_canon_sexp_pad (tmpsexp, 1, &transferkey, &transferkeylen);
+      gcry_sexp_release (tmpsexp);
+      if (err)
+        {
+          log_error ("error building transfer key: %s\n", gpg_strerror (err));
+          goto leave;
+        }
+
+      /* Wrap the key.  */
+      wrappedkeylen = transferkeylen + 8;
+      xfree (wrappedkey);
+      wrappedkey = xtrymalloc (wrappedkeylen);
+      if (!wrappedkey)
+        err = gpg_error_from_syserror ();
+      else
+        err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen,
+                                   transferkey, transferkeylen);
+      if (err)
+        goto leave;
+      xfree (transferkey);
+      transferkey = NULL;
+
+      /* Send the wrapped key to the agent.  */
+      { 
+        char *uid, *desc;
+        size_t uidlen;
+        u32 keyid[2];
+        char *orig_codeset;
+        
+        keyid_from_sk (sk, keyid);
+        uid = get_user_id (keyid, &uidlen); 
+        orig_codeset = i18n_switchto_utf8 ();
+        desc = xtryasprintf (_("Please enter the passphrase to import the"
+                             " secret key for the OpenPGP certificate:\n"
+                               "\"%.*s\"\n"             \
+                               "%u-bit %s key, ID %s,\n"       
+                               "created %s.\n"),
+                             (int)uidlen, uid,
+                             nbits_from_sk (sk),
+                             openpgp_pk_algo_name (sk->pubkey_algo),
+                             (main_sk == sk
+                              ? keystr_from_sk (sk)
+                              : keystr_from_sk_with_sub (main_sk, sk)),
+                             strtimestamp (sk->timestamp));
+        i18n_switchback (orig_codeset);
+        xfree (uid);
+        if (desc)
+          {
+            uid = percent_plus_escape (desc);
+            xfree (desc);
+            desc = uid;
+          }
+        err = agent_import_key (ctrl, desc, wrappedkey, wrappedkeylen);
+        xfree (desc);
+      }
+      if (!err)
+        {
+          if (opt.verbose)
+            log_info (_("key %s: secret key imported\n"),
+                      keystr_from_sk_with_sub (main_sk, sk));
+          /* stats->count++; */
+          /* stats->secret_read++; */
+          /* stats->secret_imported++; */
+        }
+      else if ( gpg_err_code (err) == GPG_ERR_EEXIST )
+        {
+          if (opt.verbose)
+            log_info (_("key %s: secret key already exists\n"),
+                      keystr_from_sk_with_sub (main_sk, sk));
+          err = 0;
+          /* stats->count++; */
+          /* stats->secret_read++; */
+          /* stats->secret_dups++; */
+        }
+      else
+        {
+          log_error (_("key %s: error sending to agent: %s\n"),
+                     keystr_from_sk_with_sub (main_sk, sk),
+                     gpg_strerror (err));
+          if (sk->protect.algo == GCRY_CIPHER_IDEA
+              && gpg_err_code (err) == GPG_ERR_CIPHER_ALGO)
+            {
+              write_status (STATUS_RSA_OR_IDEA);
+              idea_cipher_warn (0);
+            }
+          if (gpg_err_code (err) == GPG_ERR_CANCELED)
+            break; /* Don't try the other subkeys.  */
+        }
+    }
+
+ leave:
+  xfree (wrappedkey);
+  xfree (transferkey);
+  gcry_cipher_close (cipherhd);
+  xfree (kek);
+  return err;
+}
+
+
+/* Walk a secret keyblock and produce a public keyblock out of it.
+   Returns a new node or NULL on error. */
+static kbnode_t
+sec_to_pub_keyblock (kbnode_t sec_keyblock)
+{
+  kbnode_t pub_keyblock = NULL;
+  kbnode_t ctx = NULL;
+  kbnode_t secnode, pubnode;
 
-      if(secnode->pkt->pkttype==PKT_SECRET_KEY ||
-        secnode->pkt->pkttype==PKT_SECRET_SUBKEY)
+  while ((secnode = walk_kbnode (sec_keyblock, &ctx, 0)))
+    {
+      if (secnode->pkt->pkttype == PKT_SECRET_KEY
+          || secnode->pkt->pkttype == PKT_SECRET_SUBKEY)
        {
          /* Make a public key.  We only need to convert enough to
             write the keyblock out. */
+         PACKET *pkt;
+         PKT_secret_key *sk;
+         PKT_public_key *pk;
+         int n, i;
 
-         PKT_secret_key *sk=secnode->pkt->pkt.secret_key;
-         PACKET *pkt=xmalloc_clear(sizeof(PACKET));
-         PKT_public_key *pk=xmalloc_clear(sizeof(PKT_public_key));
-         int n;
+         pkt = xcalloc (1, sizeof *pkt);
+         sk = secnode->pkt->pkt.secret_key;
+         pk = xcalloc (1, sizeof *pk);
 
-         if(secnode->pkt->pkttype==PKT_SECRET_KEY)
-           pkt->pkttype=PKT_PUBLIC_KEY;
+         if (secnode->pkt->pkttype == PKT_SECRET_KEY)
+           pkt->pkttype = PKT_PUBLIC_KEY;
          else
-           pkt->pkttype=PKT_PUBLIC_SUBKEY;
+           pkt->pkttype = PKT_PUBLIC_SUBKEY;
 
-         pkt->pkt.public_key=pk;
+         pkt->pkt.public_key = pk;
 
-         pk->version=sk->version;
-         pk->timestamp=sk->timestamp;
-         pk->expiredate=sk->expiredate;
-         pk->pubkey_algo=sk->pubkey_algo;
+         pk->version     = sk->version;
+         pk->timestamp   = sk->timestamp;
+         pk->expiredate  = sk->expiredate;
+         pk->pubkey_algo = sk->pubkey_algo;
 
-         n=pubkey_get_npkey(pk->pubkey_algo);
-         if(n==0)
+         n = pubkey_get_npkey (pk->pubkey_algo);
+         if (!n)
            {
-             /* we can't properly extract the pubkey without knowing
+             /* We can't properly extract the pubkey without knowing
                 the number of MPIs */
-             release_kbnode(pub_keyblock);
+             release_kbnode (pub_keyblock);
              return NULL;
            }
-         else
-           {
-             int i;
-
-             for(i=0;i<n;i++)
-               pk->pkey[i]=mpi_copy(sk->skey[i]);
-           }
 
-         pubnode=new_kbnode(pkt);
+          for (i=0; i < n; i++)
+            pk->pkey[i] = mpi_copy (sk->skey[i]);
+         pubnode = new_kbnode (pkt);
        }
       else
        {
-         pubnode=clone_kbnode(secnode);
+         pubnode = clone_kbnode (secnode);
        }
 
-      if(pub_keyblock==NULL)
-       pub_keyblock=pubnode;
+      if (!pub_keyblock)
+       pub_keyblock = pubnode;
       else
-       add_kbnode(pub_keyblock,pubnode);
+       add_kbnode (pub_keyblock, pubnode);
     }
 
   return pub_keyblock;
@@ -1148,132 +1384,126 @@ sec_to_pub_keyblock(KBNODE sec_keyblock)
  * with the trust calculation.
  */
 static int
-import_secret_one( const char *fname, KBNODE keyblock, 
+import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, 
                    struct stats_s *stats, unsigned int options)
 {
-    PKT_secret_key *sk;
-    KBNODE node, uidnode;
-    u32 keyid[2];
-    int rc = 0;
-
-    /* get the key and print some info about it */
-    node = find_kbnode( keyblock, PKT_SECRET_KEY );
-    if( !node )
-       BUG();
-
-    sk = node->pkt->pkt.secret_key;
-    keyid_from_sk( sk, keyid );
-    uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
-
-    if( opt.verbose )
-      {
-       log_info( "sec  %4u%c/%s %s   ",
-                 nbits_from_sk( sk ),
-                 pubkey_letter( sk->pubkey_algo ),
-                 keystr_from_sk(sk), datestr_from_sk(sk) );
-       if( uidnode )
-         print_utf8_buffer (es_stderr, uidnode->pkt->pkt.user_id->name,
-                            uidnode->pkt->pkt.user_id->len );
-       log_printf ("\n");
-      }
-    stats->secret_read++;
-
-    if( !uidnode )
-      {
-       log_error( _("key %s: no user ID\n"), keystr_from_sk(sk));
-       return 0;
-      }
-
-    if(sk->protect.algo>110)
-      {
-       log_error(_("key %s: secret key with invalid cipher %d"
-                   " - skipped\n"),keystr_from_sk(sk),sk->protect.algo);
-       return 0;
-      }
+  PKT_secret_key *sk;
+  KBNODE node, uidnode;
+  u32 keyid[2];
+  int have_seckey;
+  int rc = 0;
+    
+  /* Get the key and print some info about it */
+  node = find_kbnode (keyblock, PKT_SECRET_KEY);
+  if (!node)
+    BUG ();
+  
+  sk = node->pkt->pkt.secret_key;
+  keyid_from_sk (sk, keyid);
+  uidnode = find_next_kbnode (keyblock, PKT_USER_ID);
+  
+  if (opt.verbose)
+    {
+      log_info ("sec  %4u%c/%s %s   ",
+                nbits_from_sk (sk),
+                pubkey_letter (sk->pubkey_algo),
+                keystr_from_sk (sk), datestr_from_sk (sk));
+      if (uidnode)
+        print_utf8_buffer (es_stderr, uidnode->pkt->pkt.user_id->name,
+                           uidnode->pkt->pkt.user_id->len);
+      log_printf ("\n");
+    }
+  stats->secret_read++;
+  
+  if (!uidnode)
+    {
+      log_error( _("key %s: no user ID\n"), keystr_from_sk(sk));
+      return 0;
+    }
+    
+  /* A quick check to not import keys with an invalid protection
+     cipher algorithm (only checks the primary key, though).  */
+  if (sk->protect.algo > 110)
+    {
+      log_error (_("key %s: secret key with invalid cipher %d"
+                   " - skipped\n"),keystr_from_sk(sk),sk->protect.algo);
+      return 0;
+    }
 
 #ifdef ENABLE_SELINUX_HACKS
-    if (1)
-      {
-        /* We don't allow to import secret keys because that may be used
-           to put a secret key into the keyring and the user might later
-           be tricked into signing stuff with that key.  */
-        log_error (_("importing secret keys not allowed\n"));
-        return 0;
-      }
+  if (1)
+    {
+      /* We don't allow to import secret keys because that may be used
+         to put a secret key into the keyring and the user might later
+         be tricked into signing stuff with that key.  */
+      log_error (_("importing secret keys not allowed\n"));
+      return 0;
+    }
 #endif 
     
-    clear_kbnode_flags( keyblock );
-
-    /* do we have this key already in one of our secrings ? */
-    rc = -1 /* fixme seckey_available( keyid ) is not anymore
-               available and has been replaced by
-               have_secret_key_with_kid.  We need to rework the entire
-               secret key import code.  The solution I am currently
-               thinking about is to move that code into a helper
-               program.  */;
-    if( rc == G10ERR_NO_SECKEY && !(opt.import_options&IMPORT_MERGE_ONLY) )
-      {
-       /* simply insert this key */
-        KEYDB_HANDLE hd = keydb_new (); /* FIXME*/
-
-       /* get default resource */
-        rc = keydb_locate_writable (hd, NULL);
-       if (rc) {
-         log_error (_("no default secret keyring: %s\n"), g10_errstr (rc));
-         keydb_release (hd);
-         return G10ERR_GENERAL;
-       }
-       rc = keydb_insert_keyblock (hd, keyblock );
-        if (rc)
-         log_error (_("error writing keyring `%s': %s\n"),
-                    keydb_get_resource_name (hd), g10_errstr(rc) );
-        keydb_release (hd);
-       /* we are ready */
-       if( !opt.quiet )
-         log_info( _("key %s: secret key imported\n"), keystr_from_sk(sk));
-       stats->secret_imported++;
-        if (is_status_enabled ()) 
-         print_import_ok (NULL, sk, 1|16);
+  clear_kbnode_flags( keyblock );
+  
+  have_seckey = have_secret_key_with_kid (keyid);
 
-       if(options&IMPORT_SK2PK)
-         {
-           /* Try and make a public key out of this. */
+  if (!have_seckey && !(opt.import_options&IMPORT_MERGE_ONLY) )
+    {
+      /* We don't have this key, insert as a new key.  */
+      kbnode_t pub_keyblock;
 
-           KBNODE pub_keyblock=sec_to_pub_keyblock(keyblock);
-           if(pub_keyblock)
-             {
-               import_one(fname,pub_keyblock,stats,
-                          NULL,NULL,opt.import_options,1);
-               release_kbnode(pub_keyblock);
-             }
-         }
+      stats->secret_imported++;
+      if (is_status_enabled ()) 
+        print_import_ok (NULL, sk, 1|16);
 
-       /* Now that the key is definitely incorporated into the keydb,
-          if we have the public part of this key, we need to check if
-          the prefs are rational. */
-       node=get_pubkeyblock(keyid);
-       if(node)
-         {
-           check_prefs(node);
-           release_kbnode(node);
-         }
-      }
-    else if( !rc )
-      { /* we can't merge secret keys */
-       log_error( _("key %s: already in secret keyring\n"),
-                  keystr_from_sk(sk));
-       stats->secret_dups++;
-        if (is_status_enabled ()) 
-         print_import_ok (NULL, sk, 16);
-
-       /* TODO: if we ever do merge secret keys, make sure to handle
-          the sec_to_pub_keyblock feature as well. */
-      }
-    else
-      log_error( _("key %s: secret key not found: %s\n"),
-                keystr_from_sk(sk), g10_errstr(rc));
+      /* Make a public key out of this. */
+      pub_keyblock = sec_to_pub_keyblock (keyblock);
+      if (!pub_keyblock)
+        log_error ("oops: FIXME (bad error message)\n");
+      else
+        {
+          import_one (fname, pub_keyblock, stats,
+                      NULL, NULL, opt.import_options, 1);
+          /* Fixme: We should check for an invalid keyblock and
+             cancel the secret key import in this case.  */
+          release_kbnode (pub_keyblock);
+            
+          /* Read the keyblock again to get the effects of a merge.  */
+          /* Fixme: we should do this based on the fingerprint or
+             even better let import_one return the merged
+             keyblock.  */
+          node = get_pubkeyblock (keyid);
+          if (!node)
+            log_error ("oops: error getting public keyblock again\n");
+          else
+            {
+              if (!transfer_secret_keys (ctrl, keyblock))
+                {
+                  if (!opt.quiet)
+                    log_info (_("key %s: secret key imported\n"),
+                              keystr_from_sk (sk));
+                  check_prefs (node);
+                }
+              release_kbnode (node);
+            }
+        }
+    }
+  else if (have_seckey)
+    { 
+      /* We can't yet merge secret keys. - Well, with the new system
+         we can => FIXME  */
+      log_error( _("key %s: secret key part already available\n"),
+                 keystr_from_sk(sk));
+      stats->secret_dups++;
+      if (is_status_enabled ()) 
+        print_import_ok (NULL, sk, 16);
+        
+      /* TODO: if we ever do merge secret keys, make sure to handle
+         the sec_to_pub_keyblock feature as well. */
+    }
+  else
+    log_error( _("key %s: secret key not found: %s\n"),
+               keystr_from_sk(sk), g10_errstr(rc));
 
-    return rc;
+  return rc;
 }
 
 
index e860a9f..ca6b901 100644 (file)
@@ -259,8 +259,13 @@ u32 v3_keyid (gcry_mpi_t a, u32 *ki);
 void hash_public_key( gcry_md_hd_t md, PKT_public_key *pk );
 size_t keystrlen(void);
 const char *keystr(u32 *keyid);
+const char *keystr_with_sub (u32 *main_kid, u32 *sub_kid);
 const char *keystr_from_pk(PKT_public_key *pk);
+const char *keystr_from_pk_with_sub (PKT_public_key *main_pk,
+                                     PKT_public_key *sub_pk);
 const char *keystr_from_sk(PKT_secret_key *sk);
+const char *keystr_from_sk_with_sub (PKT_secret_key *main_sk,
+                                     PKT_secret_key *sub_sk);
 const char *keystr_from_desc(KEYDB_SEARCH_DESC *desc);
 u32 keyid_from_sk( PKT_secret_key *sk, u32 *keyid );
 u32 keyid_from_pk( PKT_public_key *pk, u32 *keyid );
index 8f2d8f7..a6e8b37 100644 (file)
@@ -35,6 +35,9 @@
 #include "i18n.h"
 #include "rmd160.h"
 
+#define KEYID_STR_SIZE 19
+
+
 int
 pubkey_letter( int algo )
 {
@@ -204,35 +207,38 @@ keystrlen(void)
     }
 }
 
+
 const char *
-keystr(u32 *keyid)
+keystr (u32 *keyid)
 {  
-  static char keyid_str[19];
+  static char keyid_str[KEYID_STR_SIZE];
 
-  switch(opt.keyid_format)
+  switch (opt.keyid_format)
     {
     case KF_SHORT:
-      sprintf(keyid_str,"%08lX",(ulong)keyid[1]);
+      snprintf (keyid_str, sizeof keyid_str, "%08lX", (ulong)keyid[1]);
       break;
 
     case KF_LONG:
-      if(keyid[0])
-       sprintf(keyid_str,"%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]);
+      if (keyid[0])
+       snprintf (keyid_str, sizeof keyid_str, "%08lX%08lX", 
+                  (ulong)keyid[0], (ulong)keyid[1]);
       else
-       sprintf(keyid_str,"%08lX",(ulong)keyid[1]);
+       snprintf (keyid_str, sizeof keyid_str, "%08lX", (ulong)keyid[1]);
       break;
 
     case KF_0xSHORT:
-      sprintf(keyid_str,"0x%08lX",(ulong)keyid[1]);
+      snprintf (keyid_str, sizeof keyid_str, "0x%08lX", (ulong)keyid[1]);
       break;
 
     case KF_0xLONG:
       if(keyid[0])
-       sprintf(keyid_str,"0x%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]);
+       snprintf (keyid_str, sizeof keyid_str, "0x%08lX%08lX", 
+                  (ulong)keyid[0],(ulong)keyid[1]);
       else
-       sprintf(keyid_str,"0x%08lX",(ulong)keyid[1]);
+       snprintf (keyid_str, sizeof keyid_str, "0x%08lX", (ulong)keyid[1]);
       break;
-
+      
     default:
       BUG();
     }
@@ -240,6 +246,21 @@ keystr(u32 *keyid)
   return keyid_str;
 }
 
+
+const char *
+keystr_with_sub (u32 *main_kid, u32 *sub_kid)
+{  
+  static char buffer[KEYID_STR_SIZE+1+KEYID_STR_SIZE];
+  char *p;
+
+  mem2str (buffer, keystr (main_kid), KEYID_STR_SIZE);
+  p = buffer + strlen (buffer);
+  *p++ = '/';
+  mem2str (p, keystr (sub_kid), KEYID_STR_SIZE);
+  return buffer;
+}
+
+
 const char *
 keystr_from_pk(PKT_public_key *pk)
 {
@@ -248,14 +269,36 @@ keystr_from_pk(PKT_public_key *pk)
   return keystr(pk->keyid);
 }
 
+
+const char *
+keystr_from_pk_with_sub (PKT_public_key *main_pk, PKT_public_key *sub_pk)
+{
+  keyid_from_pk (main_pk, NULL);
+  keyid_from_pk (sub_pk, NULL);
+
+  return keystr_with_sub (main_pk->keyid, sub_pk->keyid);
+}
+
+
 const char *
 keystr_from_sk(PKT_secret_key *sk)
 {
-  keyid_from_sk(sk,NULL);
+  keyid_from_sk (sk,NULL);
 
   return keystr(sk->keyid);
 }
 
+
+const char *
+keystr_from_sk_with_sub (PKT_secret_key *main_sk, PKT_secret_key *sub_sk)
+{
+  keyid_from_sk (main_sk, NULL);
+  keyid_from_sk (sub_sk, NULL);
+
+  return keystr_with_sub (main_sk->keyid, sub_sk->keyid);
+}
+
+
 const char *
 keystr_from_desc(KEYDB_SEARCH_DESC *desc)
 {
index 27f5271..39c3d69 100644 (file)
@@ -1494,9 +1494,10 @@ keyserver_spawn(enum ks_action action,strlist_t list,KEYDB_SEARCH_DESC *desc,
             gpg complain about "no valid OpenPGP data found".  One
             way to do this could be to continue parsing this
             line-by-line and make a temp iobuf for each key. */
-
-         import_keys_stream(spawn->fromchild,stats_handle,fpr,fpr_len,
-                            opt.keyserver_options.import_options);
+          
+          /* FIXME: Pass CTRL.  */
+         import_keys_stream (NULL, spawn->fromchild,stats_handle,fpr,fpr_len,
+                             opt.keyserver_options.import_options);
 
          import_print_stats(stats_handle);
          import_release_stats_handle(stats_handle);
@@ -2037,8 +2038,9 @@ keyserver_import_cert(const char *name,unsigned char **fpr,size_t *fpr_len)
       /* CERTs are always in binary format */
       opt.no_armor=1;
 
-      rc=import_keys_stream(key,NULL,fpr,fpr_len,
-                           opt.keyserver_options.import_options);
+      /* FIXME: Pass CTRL.  */
+      rc = import_keys_stream (NULL, key, NULL, fpr, fpr_len,
+                               opt.keyserver_options.import_options);
 
       opt.no_armor=armor_status;
 
index 83723ed..d5b8702 100644 (file)
@@ -96,7 +96,9 @@ const char *openpgp_cipher_algo_name (int algo);
 int openpgp_pk_test_algo( int algo );
 int openpgp_pk_test_algo2 ( int algo, unsigned int use );
 int openpgp_pk_algo_usage ( int algo );
+const char *openpgp_pk_algo_name (int algo);
 int openpgp_md_test_algo( int algo );
+const char *openpgp_md_algo_name (int algo);
 
 #ifdef USE_IDEA
 void idea_cipher_warn( int show );
@@ -263,10 +265,11 @@ gcry_mpi_t encode_md_value (PKT_public_key *pk,
 
 /*-- import.c --*/
 int parse_import_options(char *str,unsigned int *options,int noisy);
-void import_keys( char **fnames, int nnames,
-                 void *stats_hd, unsigned int options );
-int import_keys_stream( iobuf_t inp,void *stats_hd,unsigned char **fpr,
-                       size_t *fpr_len,unsigned int options );
+void import_keys (ctrl_t ctrl, char **fnames, int nnames,
+                 void *stats_hd, unsigned int options);
+int import_keys_stream (ctrl_t ctrl, iobuf_t inp, void *stats_hd, 
+                        unsigned char **fpr,
+                       size_t *fpr_len, unsigned int options);
 void *import_new_stats_handle (void);
 void import_release_stats_handle (void *p);
 void import_print_stats (void *hd);
index eb3ecee..91d1c31 100644 (file)
@@ -478,6 +478,28 @@ openpgp_pk_algo_usage ( int algo )
     return use;
 }
 
+/* Map the OpenPGP pubkey algorithm whose ID is contained in ALGO to a
+   string representation of the algorithm name.  For unknown algorithm
+   IDs this function returns "?".  */
+const char *
+openpgp_pk_algo_name (int algo) 
+{
+  switch (algo)
+    {    
+    case PUBKEY_ALGO_RSA:
+    case PUBKEY_ALGO_RSA_E:
+    case PUBKEY_ALGO_RSA_S: return "rsa";
+
+    case PUBKEY_ALGO_ELGAMAL:
+    case PUBKEY_ALGO_ELGAMAL_E: return "elg";
+
+    case PUBKEY_ALGO_DSA:  return "dsa";
+
+    default: return "?";
+    }
+}
+
+
 int
 openpgp_md_test_algo( int algo )
 {
@@ -491,6 +513,19 @@ openpgp_md_test_algo( int algo )
   return gcry_md_test_algo (algo);
 }
 
+
+/* Map the OpenPGP digest algorithm whose ID is contained in ALGO to a
+   string representation of the algorithm name.  For unknown algorithm
+   IDs this function returns "?".  */
+const char *
+openpgp_md_algo_name (int algo) 
+{
+  if (algo < 0 || algo > 110)
+    return "?";
+  return gcry_md_algo_name (algo);
+}
+
+
 #ifdef USE_IDEA
 /* Special warning for the IDEA cipher */
 void
index 221d040..cee248f 100644 (file)
@@ -320,7 +320,6 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode;
 #define IMPORT_LOCAL_SIGS                (1<<0)
 #define IMPORT_REPAIR_PKS_SUBKEY_BUG     (1<<1)
 #define IMPORT_FAST                      (1<<2)
-#define IMPORT_SK2PK                     (1<<3)
 #define IMPORT_MERGE_ONLY                (1<<4)
 #define IMPORT_MINIMAL                   (1<<5)
 #define IMPORT_CLEAN                     (1<<6)
index f3001f5..14a2753 100644 (file)
@@ -244,41 +244,3 @@ pk_decrypt (int algo, gcry_mpi_t * result, gcry_mpi_t * data,
   return 0;
 }
 
-
-/* Check whether SKEY is a suitable secret key. */
-int
-REMOVE_ME_pk_check_secret_key (int algo, gcry_mpi_t *skey)
-{
-  gcry_sexp_t s_skey;
-  int rc;
-
-  if (algo == GCRY_PK_DSA)
-    {
-      rc = gcry_sexp_build (&s_skey, NULL,
-                           "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))",
-                           skey[0], skey[1], skey[2], skey[3], skey[4]);
-    }
-  else if (algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E)
-    {
-      rc = gcry_sexp_build (&s_skey, NULL,
-                           "(private-key(elg(p%m)(g%m)(y%m)(x%m)))",
-                           skey[0], skey[1], skey[2], skey[3]);
-    }
-  else if (algo == GCRY_PK_RSA
-           || algo == GCRY_PK_RSA_S || algo == GCRY_PK_RSA_E)
-    {
-      rc = gcry_sexp_build (&s_skey, NULL,
-                           "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
-                           skey[0], skey[1], skey[2], skey[3], skey[4],
-                           skey[5]);
-    }
-  else
-    return GPG_ERR_PUBKEY_ALGO;
-
-  if (!rc)
-    {
-      rc = gcry_pk_testkey (s_skey);
-      gcry_sexp_release (s_skey);
-    }
-  return rc;
-}