From 958f29d2251a96d09439e591ea3523133930e5e9 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Sun, 6 Nov 2011 17:01:31 +0100 Subject: [PATCH] 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. --- g10/ChangeLog | 12 +++ g10/call-agent.c | 81 ++++++++++----------- g10/call-agent.h | 4 + g10/card-util.c | 6 +- g10/gpg.c | 4 +- g10/keyedit.c | 2 +- g10/keygen.c | 186 +++++++++++++++++++++++++++++++++++++++++++++-- g10/main.h | 6 +- 8 files changed, 242 insertions(+), 59 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 + + * 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 * 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]; */ +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; */ -/* rc = start_agent (ctrl); */ -/* if (rc) */ -/* return rc; */ + *r_pubkey = NULL; + err = start_agent (ctrl, 0); + if (err) + return err; -/* rc = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL); */ -/* if (rc) */ -/* return rc; */ + 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); */ -/* line[DIM(line)-1] = 0; */ + snprintf (line, DIM(line)-1, "%sREADKEY %s", fromcard? "SCD ":"", hexkeygrip); -/* 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; */ -/* } */ + 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);