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 + + * pksign.c (do_encode_dsa): Fix sign problem. + * findkey.c (agent_is_dsa_key): Adjust to actual usage. + +2010-08-30 Werner Koch + + * protect.c (s2k_hash_passphrase): New public function. + +2010-08-27 Werner Koch + + * 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 * 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 #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 . + */ + +#include +#include +#include +#include +#include + +#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 . + */ +#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 + + * 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 + + * 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 + + * misc.c (openpgp_pk_algo_name): New. + (openpgp_md_algo_name): New. + +2010-08-24 Werner Koch + + * 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 * 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; - while((secnode=walk_kbnode(sec_keyblock,&ctx,0))) + /* Get the current KEK. */ + err = agent_keywrap_key (ctrl, 0, &kek, &keklen); + if (err) { - KBNODE pubnode; + log_error ("error getting the KEK: %s\n", gpg_strerror (err)); + goto leave; + } - if(secnode->pkt->pkttype==PKT_SECRET_KEY || - secnode->pkt->pkttype==PKT_SECRET_SUBKEY) + /* 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))) + { + 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; + + 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;ipkey[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 ); + clear_kbnode_flags( keyblock ); + + have_seckey = have_secret_key_with_kid (keyid); - /* 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*/ + if (!have_seckey && !(opt.import_options&IMPORT_MERGE_ONLY) ) + { + /* We don't have this key, insert as a new key. */ + kbnode_t pub_keyblock; - /* 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); + stats->secret_imported++; + if (is_status_enabled ()) + print_import_ok (NULL, sk, 1|16); - if(options&IMPORT_SK2PK) - { - /* Try and make a public key out of this. */ + /* 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)); - 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); - } - } - - /* 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)); - - 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) -{ - static char keyid_str[19]; - switch(opt.keyid_format) +const char * +keystr (u32 *keyid) +{ + static char keyid_str[KEYID_STR_SIZE]; + + 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; -}