summaryrefslogtreecommitdiffstats
path: root/agent/cvt-openpgp.c
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2010-08-31 17:58:39 +0200
committerWerner Koch <wk@gnupg.org>2010-08-31 17:58:39 +0200
commit87fac9911241310a4b601e126fa2e26b10bd370f (patch)
tree49e09cc881b44a1dba0e9474040cda1d5f9ce581 /agent/cvt-openpgp.c
parentFix for W32. (diff)
downloadgnupg2-87fac9911241310a4b601e126fa2e26b10bd370f.tar.xz
gnupg2-87fac9911241310a4b601e126fa2e26b10bd370f.zip
Import OpenPGP keys into the agent.
Diffstat (limited to 'agent/cvt-openpgp.c')
-rw-r--r--agent/cvt-openpgp.c807
1 files changed, 807 insertions, 0 deletions
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;
+
+}
+
+
+