summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2011-11-06 17:01:31 +0100
committerWerner Koch <wk@gnupg.org>2011-11-06 17:01:31 +0100
commit958f29d2251a96d09439e591ea3523133930e5e9 (patch)
tree21b939412dc91322f35b85524df4804049fa1141
parenttypo fixes (diff)
downloadgnupg2-958f29d2251a96d09439e591ea3523133930e5e9.tar.xz
gnupg2-958f29d2251a96d09439e591ea3523133930e5e9.zip
Allow creating subkeys using an existing key
This works by specifying the keygrip instead of an algorithm (section number 13) and requires that the option -expert has been used. It will be easy to extend this to the primary key.
Diffstat (limited to '')
-rw-r--r--g10/ChangeLog12
-rw-r--r--g10/call-agent.c89
-rw-r--r--g10/call-agent.h4
-rw-r--r--g10/card-util.c6
-rw-r--r--g10/gpg.c4
-rw-r--r--g10/keyedit.c2
-rw-r--r--g10/keygen.c186
-rw-r--r--g10/main.h6
8 files changed, 246 insertions, 63 deletions
diff --git a/g10/ChangeLog b/g10/ChangeLog
index 4b5f2f141..892878030 100644
--- a/g10/ChangeLog
+++ b/g10/ChangeLog
@@ -1,3 +1,15 @@
+2011-11-06 Werner Koch <wk@g10code.com>
+
+ * card-util.c (generate_card_keys): Add arg CTRL.
+
+ * call-agent.c (agent_readkey): New.
+ * keygen.c (do_create_from_keygrip): New.
+ (ask_algo): Add arg R_KEYGRIP and a prompt to enter it.
+ (generate_subkeypair): Call do_create_from_keygrip if required.
+ (generate_subkeypair): Add arg CTRL. Change caller.
+ (ask_algo): Add arg CTRL.
+ (generate_keypair): Ditto.
+
2011-09-23 Werner Koch <wk@g10code.com>
* gpgv.c (disable_dotlock): Rename to dotlock_disable.
diff --git a/g10/call-agent.c b/g10/call-agent.c
index 5a10dbdb9..445102951 100644
--- a/g10/call-agent.c
+++ b/g10/call-agent.c
@@ -1,6 +1,6 @@
/* call-agent.c - Divert GPG operations to the agent.
* Copyright (C) 2001, 2002, 2003, 2006, 2007, 2008, 2009,
- * 2010 Free Software Foundation, Inc.
+ * 2010, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -1506,55 +1506,52 @@ agent_genkey (ctrl_t ctrl, char **cache_nonce_addr,
}
-
-/* FIXME: Call the agent to read the public key part for a given keygrip. If
+/* Call the agent to read the public key part for a given keygrip. If
FROMCARD is true, the key is directly read from the current
smartcard. In this case HEXKEYGRIP should be the keyID
(e.g. OPENPGP.3). */
-/* int */
-/* agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, */
-/* ksba_sexp_t *r_pubkey) */
-/* { */
-/* int rc; */
-/* membuf_t data; */
-/* size_t len; */
-/* unsigned char *buf; */
-/* char line[ASSUAN_LINELENGTH]; */
-
-/* *r_pubkey = NULL; */
-/* rc = start_agent (ctrl); */
-/* if (rc) */
-/* return rc; */
-
-/* rc = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL); */
-/* if (rc) */
-/* return rc; */
-
-/* snprintf (line, DIM(line)-1, "%sREADKEY %s", */
-/* fromcard? "SCD ":"", hexkeygrip); */
-/* line[DIM(line)-1] = 0; */
-
-/* init_membuf (&data, 1024); */
-/* rc = assuan_transact (agent_ctx, line, */
-/* membuf_data_cb, &data, */
-/* default_inq_cb, ctrl, NULL, NULL); */
-/* if (rc) */
-/* { */
-/* xfree (get_membuf (&data, &len)); */
-/* return rc; */
-/* } */
-/* buf = get_membuf (&data, &len); */
-/* if (!buf) */
-/* return gpg_error (GPG_ERR_ENOMEM); */
-/* if (!gcry_sexp_canon_len (buf, len, NULL, NULL)) */
-/* { */
-/* xfree (buf); */
-/* return gpg_error (GPG_ERR_INV_SEXP); */
-/* } */
-/* *r_pubkey = buf; */
-/* return 0; */
-/* } */
+gpg_error_t
+agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
+ unsigned char **r_pubkey)
+{
+ gpg_error_t err;
+ membuf_t data;
+ size_t len;
+ unsigned char *buf;
+ char line[ASSUAN_LINELENGTH];
+
+ *r_pubkey = NULL;
+ err = start_agent (ctrl, 0);
+ if (err)
+ return err;
+
+ err = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+
+ snprintf (line, DIM(line)-1, "%sREADKEY %s", fromcard? "SCD ":"", hexkeygrip);
+
+ init_membuf (&data, 1024);
+ err = assuan_transact (agent_ctx, line,
+ membuf_data_cb, &data,
+ default_inq_cb, NULL, NULL, NULL);
+ if (err)
+ {
+ xfree (get_membuf (&data, &len));
+ return err;
+ }
+ buf = get_membuf (&data, &len);
+ if (!buf)
+ return gpg_error_from_syserror ();
+ if (!gcry_sexp_canon_len (buf, len, NULL, NULL))
+ {
+ xfree (buf);
+ return gpg_error (GPG_ERR_INV_SEXP);
+ }
+ *r_pubkey = buf;
+ return 0;
+}
diff --git a/g10/call-agent.h b/g10/call-agent.h
index 1e7e15abc..43de14fe3 100644
--- a/g10/call-agent.h
+++ b/g10/call-agent.h
@@ -148,6 +148,10 @@ gpg_error_t agent_genkey (ctrl_t ctrl, char **cache_nonce_addr,
const char *keyparms, int no_protection,
gcry_sexp_t *r_pubkey);
+/* Read a public key. */
+gpg_error_t agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
+ unsigned char **r_pubkey);
+
/* Create a signature. */
gpg_error_t agent_pksign (ctrl_t ctrl, const char *cache_nonce,
const char *hexkeygrip, const char *desc,
diff --git a/g10/card-util.c b/g10/card-util.c
index 9c124bbfc..14268dfeb 100644
--- a/g10/card-util.c
+++ b/g10/card-util.c
@@ -1356,7 +1356,7 @@ do_change_keysize (int keyno, unsigned int nbits)
static void
-generate_card_keys (void)
+generate_card_keys (ctrl_t ctrl)
{
struct agent_card_info_s info;
int forced_chv1;
@@ -1435,7 +1435,7 @@ generate_card_keys (void)
the serialnumber and thus it won't harm. */
}
- generate_keypair (NULL, info.serialno, want_backup);
+ generate_keypair (ctrl, NULL, info.serialno, want_backup);
leave:
agent_release_card_info (&info);
@@ -1986,7 +1986,7 @@ card_edit (ctrl_t ctrl, strlist_t commands)
break;
case cmdGENERATE:
- generate_card_keys ();
+ generate_card_keys (ctrl);
break;
case cmdPASSWD:
diff --git a/g10/gpg.c b/g10/gpg.c
index c31a55863..aa37a88b6 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -3709,12 +3709,12 @@ main (int argc, char **argv)
if( opt.batch ) {
if( argc > 1 )
wrong_args("--gen-key [parameterfile]");
- generate_keypair (argc? *argv : NULL, NULL, 0);
+ generate_keypair (ctrl, argc? *argv : NULL, NULL, 0);
}
else {
if( argc )
wrong_args("--gen-key");
- generate_keypair (NULL, NULL, 0);
+ generate_keypair (ctrl, NULL, NULL, 0);
}
break;
diff --git a/g10/keyedit.c b/g10/keyedit.c
index fd42439a8..26e05a02d 100644
--- a/g10/keyedit.c
+++ b/g10/keyedit.c
@@ -1794,7 +1794,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
break;
case cmdADDKEY:
- if (!generate_subkeypair (keyblock))
+ if (!generate_subkeypair (ctrl, keyblock))
{
redisplay = 1;
modified = 1;
diff --git a/g10/keygen.c b/g10/keygen.c
index a5650a8bf..55048b163 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -1254,6 +1254,91 @@ key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp,
}
+/* Create a keyblock using the given KEYGRIP. ALGO is the OpenPGP
+ algorithm of that keygrip. */
+static int
+do_create_from_keygrip (ctrl_t ctrl, int algo, const char *hexkeygrip,
+ kbnode_t pub_root, u32 timestamp, u32 expireval,
+ int is_subkey)
+{
+ int err;
+ PACKET *pkt;
+ PKT_public_key *pk;
+ gcry_sexp_t s_key;
+ const char *algoelem;
+
+ if (hexkeygrip[0] == '&')
+ hexkeygrip++;
+
+ switch (algo)
+ {
+ case PUBKEY_ALGO_RSA: algoelem = "ne"; break;
+ case PUBKEY_ALGO_DSA: algoelem = "pqgy"; break;
+ case PUBKEY_ALGO_ELGAMAL_E: algoelem = "pgy"; break;
+ case PUBKEY_ALGO_ECDH:
+ case PUBKEY_ALGO_ECDSA: algoelem = ""; break;
+ default: return gpg_error (GPG_ERR_INTERNAL);
+ }
+
+
+ /* Ask the agent for the public key matching HEXKEYGRIP. */
+ {
+ unsigned char *public;
+
+ err = agent_readkey (ctrl, 0, hexkeygrip, &public);
+ if (err)
+ return err;
+ err = gcry_sexp_sscan (&s_key, NULL,
+ public, gcry_sexp_canon_len (public, 0, NULL, NULL));
+ xfree (public);
+ if (err)
+ return err;
+ }
+
+ /* Build a public key packet. */
+ pk = xtrycalloc (1, sizeof *pk);
+ if (!pk)
+ {
+ err = gpg_error_from_syserror ();
+ gcry_sexp_release (s_key);
+ return err;
+ }
+
+ pk->timestamp = timestamp;
+ pk->version = 4;
+ if (expireval)
+ pk->expiredate = pk->timestamp + expireval;
+ pk->pubkey_algo = algo;
+
+ if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH)
+ err = ecckey_from_sexp (pk->pkey, s_key, algo);
+ else
+ err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem);
+ if (err)
+ {
+ log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) );
+ gcry_sexp_release (s_key);
+ free_public_key (pk);
+ return err;
+ }
+ gcry_sexp_release (s_key);
+
+ pkt = xtrycalloc (1, sizeof *pkt);
+ if (!pkt)
+ {
+ err = gpg_error_from_syserror ();
+ free_public_key (pk);
+ return err;
+ }
+
+ pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY;
+ pkt->pkt.public_key = pk;
+ add_kbnode (pub_root, new_kbnode (pkt));
+
+ return 0;
+}
+
+
/* Common code for the key generation fucntion gen_xxx. */
static int
common_gen (const char *keyparms, int algo, const char *algoelem,
@@ -1691,14 +1776,53 @@ ask_key_flags(int algo,int subkey)
}
+/* Check whether we have a key for the key with HEXGRIP. Returns 0 if
+ there is no such key or the OpenPGP algo number for the key. */
+static int
+check_keygrip (ctrl_t ctrl, const char *hexgrip)
+{
+ gpg_error_t err;
+ unsigned char *public;
+ size_t publiclen;
+ int algo;
+
+ if (hexgrip[0] == '&')
+ hexgrip++;
+
+ err = agent_readkey (ctrl, 0, hexgrip, &public);
+ if (err)
+ return 0;
+ publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL);
+
+ get_pk_algo_from_canon_sexp (public, publiclen, &algo);
+ xfree (public);
+
+ switch (algo)
+ {
+ case GCRY_PK_RSA: return PUBKEY_ALGO_RSA;
+ case GCRY_PK_DSA: return PUBKEY_ALGO_DSA;
+ case GCRY_PK_ELG_E: return PUBKEY_ALGO_ELGAMAL_E;
+ case GCRY_PK_ECDH: return PUBKEY_ALGO_ECDH;
+ case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA;
+ default: return 0;
+ }
+}
+
+
+
/* Ask for an algorithm. The function returns the algorithm id to
* create. If ADDMODE is false the function won't show an option to
* create the primary and subkey combined and won't set R_USAGE
* either. If a combined algorithm has been selected, the subkey
- * algorithm is stored at R_SUBKEY_ALGO. */
+ * algorithm is stored at R_SUBKEY_ALGO. If R_KEYGRIP is given, the
+ * user has the choice to enter the keygrip of an existing key. That
+ * keygrip is then stored at this address. The caller needs to free
+ * it. */
static int
-ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
+ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
+ char **r_keygrip)
{
+ char *keygrip = NULL;
char *answer;
int algo;
int dummy_algo;
@@ -1736,6 +1860,9 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
if (opt.expert && addmode)
tty_printf (_(" (%d) ECDH (encrypt only)\n"), 12 );
+ if (opt.expert && r_keygrip)
+ tty_printf (_(" (%d) Existing key\n"), 13 );
+
for (;;)
{
*r_usage = 0;
@@ -1744,6 +1871,7 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
cpr_kill_prompt ();
algo = *answer? atoi (answer) : 1;
xfree(answer);
+ answer = NULL;
if (algo == 1 && !addmode)
{
algo = PUBKEY_ALGO_RSA;
@@ -1816,10 +1944,42 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
*r_usage = PUBKEY_USAGE_ENC;
break;
}
+ else if (algo == 13 && opt.expert && r_keygrip)
+ {
+ for (;;)
+ {
+ xfree (answer);
+ answer = tty_get (_("Enter the keygrip: "));
+ tty_kill_prompt ();
+ trim_spaces (answer);
+ if (!*answer)
+ {
+ xfree (answer);
+ answer = NULL;
+ continue;
+ }
+
+ if (strlen (answer) != 40 &&
+ !(answer[0] == '&' && strlen (answer+1) == 40))
+ tty_printf
+ (_("Not a valid keygrip (expecting 40 hex digits)\n"));
+ else if (!(algo = check_keygrip (ctrl, answer)) )
+ tty_printf (_("No key with this keygrip\n"));
+ else
+ break; /* Okay. */
+ }
+ xfree (keygrip);
+ keygrip = answer;
+ answer = NULL;
+ *r_usage = ask_key_flags (algo, addmode);
+ break;
+ }
else
tty_printf (_("Invalid selection.\n"));
}
+ if (r_keygrip)
+ *r_keygrip = keygrip;
return algo;
}
@@ -3099,7 +3259,7 @@ read_parameter_file( const char *fname )
* imported to the card and a backup file created by gpg-agent.
*/
void
-generate_keypair (const char *fname, const char *card_serialno,
+generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
int card_backup_key)
{
unsigned int nbits;
@@ -3180,7 +3340,11 @@ generate_keypair (const char *fname, const char *card_serialno,
{
int subkey_algo;
- algo = ask_algo (0, &subkey_algo, &use);
+ /* Fixme: To support creating a primary key by keygrip we better
+ also define the keyword for the parameter file. Note that
+ the subkey case will never be asserted if a keygrip has been
+ given. */
+ algo = ask_algo (ctrl, 0, &subkey_algo, &use, NULL);
if (subkey_algo)
{
/* Create primary and subkey at once. */
@@ -3653,7 +3817,7 @@ do_generate_keypair (struct para_data_s *para,
/* Add a new subkey to an existing key. Returns 0 if a new key has
been generated and put into the keyblocks. */
gpg_error_t
-generate_subkeypair (KBNODE keyblock)
+generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
{
gpg_error_t err = 0;
kbnode_t node;
@@ -3712,9 +3876,11 @@ generate_subkeypair (KBNODE keyblock)
if (serialno)
tty_printf (_("Secret parts of primary key are stored on-card.\n"));
- algo = ask_algo (1, NULL, &use);
+ xfree (hexgrip);
+ hexgrip = NULL;
+ algo = ask_algo (ctrl, 1, NULL, &use, &hexgrip);
assert (algo);
- nbits = ask_keysize (algo, 0);
+ nbits = hexgrip? 0 : ask_keysize (algo, 0);
expire = ask_expire_interval (0, NULL);
if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay",
_("Really create? (y/N) ")))
@@ -3723,7 +3889,11 @@ generate_subkeypair (KBNODE keyblock)
goto leave;
}
- err = do_create (algo, nbits, keyblock, cur_time, expire, 1, 0, NULL);
+ if (hexgrip)
+ err = do_create_from_keygrip (ctrl, algo, hexgrip,
+ keyblock, cur_time, expire, 1);
+ else
+ err = do_create (algo, nbits, keyblock, cur_time, expire, 1, 0, NULL);
if (err)
goto leave;
diff --git a/g10/main.h b/g10/main.h
index 9548731fc..7088abec9 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -240,8 +240,8 @@ const char *gpg_curve_to_oid (const char *name, unsigned int *r_nbits);
u32 parse_expire_string(const char *string);
u32 ask_expire_interval(int object,const char *def_expire);
u32 ask_expiredate(void);
-void generate_keypair (const char *fname, const char *card_serialno,
- int card_backup_key);
+void generate_keypair (ctrl_t ctrl, const char *fname,
+ const char *card_serialno, int card_backup_key);
int keygen_set_std_prefs (const char *string,int personal);
PKT_user_id *keygen_get_std_prefs (void);
int keygen_add_key_expire( PKT_signature *sig, void *opaque );
@@ -253,7 +253,7 @@ int keygen_add_revkey(PKT_signature *sig, void *opaque);
gpg_error_t make_backsig (PKT_signature *sig, PKT_public_key *pk,
PKT_public_key *sub_pk, PKT_public_key *sub_psk,
u32 timestamp, const char *cache_nonce);
-gpg_error_t generate_subkeypair (kbnode_t pub_keyblock);
+gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock);
#ifdef ENABLE_CARD_SUPPORT
gpg_error_t generate_card_subkeypair (kbnode_t pub_keyblock,
int keyno, const char *serialno);