aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2010-08-31 15:58:39 +0000
committerWerner Koch <[email protected]>2010-08-31 15:58:39 +0000
commit87fac9911241310a4b601e126fa2e26b10bd370f (patch)
tree49e09cc881b44a1dba0e9474040cda1d5f9ce581
parentFix for W32. (diff)
downloadgnupg-87fac9911241310a4b601e126fa2e26b10bd370f.tar.gz
gnupg-87fac9911241310a4b601e126fa2e26b10bd370f.zip
Import OpenPGP keys into the agent.
-rw-r--r--NEWS4
-rw-r--r--agent/ChangeLog15
-rw-r--r--agent/Makefile.am1
-rw-r--r--agent/agent.h9
-rw-r--r--agent/command.c80
-rw-r--r--agent/cvt-openpgp.c807
-rw-r--r--agent/cvt-openpgp.h27
-rw-r--r--agent/findkey.c10
-rw-r--r--agent/keyformat.txt29
-rw-r--r--agent/pksign.c26
-rw-r--r--agent/protect.c15
-rw-r--r--g10/ChangeLog29
-rw-r--r--g10/call-agent.c101
-rw-r--r--g10/call-agent.h8
-rw-r--r--g10/gpg.c51
-rw-r--r--g10/import.c568
-rw-r--r--g10/keydb.h5
-rw-r--r--g10/keyid.c67
-rw-r--r--g10/keyserver.c12
-rw-r--r--g10/main.h11
-rw-r--r--g10/misc.c35
-rw-r--r--g10/options.h1
-rw-r--r--g10/pkglue.c38
23 files changed, 1668 insertions, 281 deletions
diff --git a/NEWS b/NEWS
index 886bebbdf..11c5d5e80 100644
--- 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)
-------------------------------------------------
diff --git a/agent/ChangeLog b/agent/ChangeLog
index 0c31fc5b7..e584005ff 100644
--- a/agent/ChangeLog
+++ b/agent/ChangeLog
@@ -1,3 +1,18 @@
+2010-08-31 Werner Koch <[email protected]>
+
+ * pksign.c (do_encode_dsa): Fix sign problem.
+ * findkey.c (agent_is_dsa_key): Adjust to actual usage.
+
+2010-08-30 Werner Koch <[email protected]>
+
+ * protect.c (s2k_hash_passphrase): New public function.
+
+2010-08-27 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
* command-ssh.c (open_control_file): Use estream to create the file.
diff --git a/agent/Makefile.am b/agent/Makefile.am
index abd39bed8..9c58627e6 100644
--- a/agent/Makefile.am
+++ b/agent/Makefile.am
@@ -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
diff --git a/agent/agent.h b/agent/agent.h
index b39f2325c..57078ab40 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -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 --*/
diff --git a/agent/command.c b/agent/command.c
index 3a3b61ba8..4b847e503 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -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
index 000000000..a392518ba
--- /dev/null
+++ b/agent/cvt-openpgp.c
@@ -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
index 000000000..17b1a6ead
--- /dev/null
+++ b/agent/cvt-openpgp.h
@@ -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*/
diff --git a/agent/findkey.c b/agent/findkey.c
index 5668aafbc..76221119e 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -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;
}
diff --git a/agent/keyformat.txt b/agent/keyformat.txt
index e246e888c..841e5840f 100644
--- a/agent/keyformat.txt
+++ b/agent/keyformat.txt
@@ -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.
diff --git a/agent/pksign.c b/agent/pksign.c
index 7ae50a931..28e208e55 100644
--- a/agent/pksign.c
+++ b/agent/pksign.c
@@ -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);
}
}
diff --git a/agent/protect.c b/agent/protect.c
index db6caa48c..3a983e2bd 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -73,6 +73,8 @@ hash_passphrase (const char *passphrase, int hashalgo,
const unsigned char *s2ksalt, unsigned long s2kcount,
unsigned char *key, size_t keylen);
+
+
/* 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);
+}
+
+
/* Create an canonical encoded S-expression with the shadow info from
diff --git a/g10/ChangeLog b/g10/ChangeLog
index eb19158b0..abfa6f7af 100644
--- a/g10/ChangeLog
+++ b/g10/ChangeLog
@@ -1,3 +1,32 @@
+2010-08-30 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * misc.c (openpgp_pk_algo_name): New.
+ (openpgp_md_algo_name): New.
+
+2010-08-24 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
* gpg.c (list_config, gpgconf_list): Use es_printf.
diff --git a/g10/call-agent.c b/g10/call-agent.c
index ea81c6b9e..7f98cfba9 100644
--- a/g10/call-agent.c
+++ b/g10/call-agent.c
@@ -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;
}
+
+
+
+/* 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;
+}
+
+
+
+/* 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;
+}
+
+
diff --git a/g10/call-agent.h b/g10/call-agent.h
index c8e920855..7495b2ac6 100644
--- a/g10/call-agent.h
+++ b/g10/call-agent.h
@@ -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*/
diff --git a/g10/gpg.c b/g10/gpg.c
index 01c307b7b..b0383627e 100644
--- 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);
diff --git a/g10/import.c b/g10/import.c
index 53349ee8d..13773da25 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -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;
}
diff --git a/g10/keydb.h b/g10/keydb.h
index e860a9fbf..ca6b901c2 100644
--- a/g10/keydb.h
+++ b/g10/keydb.h
@@ -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 );
diff --git a/g10/keyid.c b/g10/keyid.c
index 8f2d8f7fd..a6e8b3728 100644
--- a/g10/keyid.c
+++ b/g10/keyid.c
@@ -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)
{
diff --git a/g10/keyserver.c b/g10/keyserver.c
index 27f52719d..39c3d69d9 100644
--- a/g10/keyserver.c
+++ b/g10/keyserver.c
@@ -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;
diff --git a/g10/main.h b/g10/main.h
index 83723edc1..d5b8702f2 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -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);
diff --git a/g10/misc.c b/g10/misc.c
index eb3eceee9..91d1c310a 100644
--- a/g10/misc.c
+++ b/g10/misc.c
@@ -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
diff --git a/g10/options.h b/g10/options.h
index 221d04021..cee248f25 100644
--- a/g10/options.h
+++ b/g10/options.h
@@ -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)
diff --git a/g10/pkglue.c b/g10/pkglue.c
index f3001f549..14a27535f 100644
--- a/g10/pkglue.c
+++ b/g10/pkglue.c
@@ -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;
-}