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.
This commit is contained in:
Werner Koch 2011-11-06 17:01:31 +01:00
parent 32118628a0
commit 958f29d225
8 changed files with 242 additions and 59 deletions

View File

@ -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> 2011-09-23 Werner Koch <wk@g10code.com>
* gpgv.c (disable_dotlock): Rename to dotlock_disable. * gpgv.c (disable_dotlock): Rename to dotlock_disable.

View File

@ -1,6 +1,6 @@
/* call-agent.c - Divert GPG operations to the agent. /* call-agent.c - Divert GPG operations to the agent.
* Copyright (C) 2001, 2002, 2003, 2006, 2007, 2008, 2009, * 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. * 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 FROMCARD is true, the key is directly read from the current
smartcard. In this case HEXKEYGRIP should be the keyID smartcard. In this case HEXKEYGRIP should be the keyID
(e.g. OPENPGP.3). */ (e.g. OPENPGP.3). */
/* int */ gpg_error_t
/* agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, */ agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
/* ksba_sexp_t *r_pubkey) */ unsigned char **r_pubkey)
/* { */ {
/* int rc; */ gpg_error_t err;
/* membuf_t data; */ membuf_t data;
/* size_t len; */ size_t len;
/* unsigned char *buf; */ unsigned char *buf;
/* char line[ASSUAN_LINELENGTH]; */ char line[ASSUAN_LINELENGTH];
/* *r_pubkey = NULL; */ *r_pubkey = NULL;
/* rc = start_agent (ctrl); */ err = start_agent (ctrl, 0);
/* if (rc) */ if (err)
/* return rc; */ return err;
/* rc = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL); */ err = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL);
/* if (rc) */ if (err)
/* return rc; */ return err;
/* snprintf (line, DIM(line)-1, "%sREADKEY %s", */ snprintf (line, DIM(line)-1, "%sREADKEY %s", fromcard? "SCD ":"", hexkeygrip);
/* fromcard? "SCD ":"", hexkeygrip); */
/* line[DIM(line)-1] = 0; */
/* init_membuf (&data, 1024); */ init_membuf (&data, 1024);
/* rc = assuan_transact (agent_ctx, line, */ err = assuan_transact (agent_ctx, line,
/* membuf_data_cb, &data, */ membuf_data_cb, &data,
/* default_inq_cb, ctrl, NULL, NULL); */ default_inq_cb, NULL, NULL, NULL);
/* if (rc) */ if (err)
/* { */ {
/* xfree (get_membuf (&data, &len)); */ xfree (get_membuf (&data, &len));
/* return rc; */ return err;
/* } */ }
/* buf = get_membuf (&data, &len); */ buf = get_membuf (&data, &len);
/* if (!buf) */ if (!buf)
/* return gpg_error (GPG_ERR_ENOMEM); */ return gpg_error_from_syserror ();
/* if (!gcry_sexp_canon_len (buf, len, NULL, NULL)) */ if (!gcry_sexp_canon_len (buf, len, NULL, NULL))
/* { */ {
/* xfree (buf); */ xfree (buf);
/* return gpg_error (GPG_ERR_INV_SEXP); */ return gpg_error (GPG_ERR_INV_SEXP);
/* } */ }
/* *r_pubkey = buf; */ *r_pubkey = buf;
/* return 0; */ return 0;
/* } */ }

View File

@ -148,6 +148,10 @@ gpg_error_t agent_genkey (ctrl_t ctrl, char **cache_nonce_addr,
const char *keyparms, int no_protection, const char *keyparms, int no_protection,
gcry_sexp_t *r_pubkey); 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. */ /* Create a signature. */
gpg_error_t agent_pksign (ctrl_t ctrl, const char *cache_nonce, gpg_error_t agent_pksign (ctrl_t ctrl, const char *cache_nonce,
const char *hexkeygrip, const char *desc, const char *hexkeygrip, const char *desc,

View File

@ -1356,7 +1356,7 @@ do_change_keysize (int keyno, unsigned int nbits)
static void static void
generate_card_keys (void) generate_card_keys (ctrl_t ctrl)
{ {
struct agent_card_info_s info; struct agent_card_info_s info;
int forced_chv1; int forced_chv1;
@ -1435,7 +1435,7 @@ generate_card_keys (void)
the serialnumber and thus it won't harm. */ the serialnumber and thus it won't harm. */
} }
generate_keypair (NULL, info.serialno, want_backup); generate_keypair (ctrl, NULL, info.serialno, want_backup);
leave: leave:
agent_release_card_info (&info); agent_release_card_info (&info);
@ -1986,7 +1986,7 @@ card_edit (ctrl_t ctrl, strlist_t commands)
break; break;
case cmdGENERATE: case cmdGENERATE:
generate_card_keys (); generate_card_keys (ctrl);
break; break;
case cmdPASSWD: case cmdPASSWD:

View File

@ -3709,12 +3709,12 @@ main (int argc, char **argv)
if( opt.batch ) { if( opt.batch ) {
if( argc > 1 ) if( argc > 1 )
wrong_args("--gen-key [parameterfile]"); wrong_args("--gen-key [parameterfile]");
generate_keypair (argc? *argv : NULL, NULL, 0); generate_keypair (ctrl, argc? *argv : NULL, NULL, 0);
} }
else { else {
if( argc ) if( argc )
wrong_args("--gen-key"); wrong_args("--gen-key");
generate_keypair (NULL, NULL, 0); generate_keypair (ctrl, NULL, NULL, 0);
} }
break; break;

View File

@ -1794,7 +1794,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
break; break;
case cmdADDKEY: case cmdADDKEY:
if (!generate_subkeypair (keyblock)) if (!generate_subkeypair (ctrl, keyblock))
{ {
redisplay = 1; redisplay = 1;
modified = 1; modified = 1;

View File

@ -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. */ /* Common code for the key generation fucntion gen_xxx. */
static int static int
common_gen (const char *keyparms, int algo, const char *algoelem, 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 /* 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. If ADDMODE is false the function won't show an option to
* create the primary and subkey combined and won't set R_USAGE * create the primary and subkey combined and won't set R_USAGE
* either. If a combined algorithm has been selected, the subkey * 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 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; char *answer;
int algo; int algo;
int dummy_algo; int dummy_algo;
@ -1736,6 +1860,9 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
if (opt.expert && addmode) if (opt.expert && addmode)
tty_printf (_(" (%d) ECDH (encrypt only)\n"), 12 ); tty_printf (_(" (%d) ECDH (encrypt only)\n"), 12 );
if (opt.expert && r_keygrip)
tty_printf (_(" (%d) Existing key\n"), 13 );
for (;;) for (;;)
{ {
*r_usage = 0; *r_usage = 0;
@ -1744,6 +1871,7 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
cpr_kill_prompt (); cpr_kill_prompt ();
algo = *answer? atoi (answer) : 1; algo = *answer? atoi (answer) : 1;
xfree(answer); xfree(answer);
answer = NULL;
if (algo == 1 && !addmode) if (algo == 1 && !addmode)
{ {
algo = PUBKEY_ALGO_RSA; 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; *r_usage = PUBKEY_USAGE_ENC;
break; 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 else
tty_printf (_("Invalid selection.\n")); tty_printf (_("Invalid selection.\n"));
} }
if (r_keygrip)
*r_keygrip = keygrip;
return algo; return algo;
} }
@ -3099,7 +3259,7 @@ read_parameter_file( const char *fname )
* imported to the card and a backup file created by gpg-agent. * imported to the card and a backup file created by gpg-agent.
*/ */
void 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) int card_backup_key)
{ {
unsigned int nbits; unsigned int nbits;
@ -3180,7 +3340,11 @@ generate_keypair (const char *fname, const char *card_serialno,
{ {
int subkey_algo; 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) if (subkey_algo)
{ {
/* Create primary and subkey at once. */ /* 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 /* Add a new subkey to an existing key. Returns 0 if a new key has
been generated and put into the keyblocks. */ been generated and put into the keyblocks. */
gpg_error_t gpg_error_t
generate_subkeypair (KBNODE keyblock) generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
{ {
gpg_error_t err = 0; gpg_error_t err = 0;
kbnode_t node; kbnode_t node;
@ -3712,9 +3876,11 @@ generate_subkeypair (KBNODE keyblock)
if (serialno) if (serialno)
tty_printf (_("Secret parts of primary key are stored on-card.\n")); 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); assert (algo);
nbits = ask_keysize (algo, 0); nbits = hexgrip? 0 : ask_keysize (algo, 0);
expire = ask_expire_interval (0, NULL); expire = ask_expire_interval (0, NULL);
if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay", if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay",
_("Really create? (y/N) "))) _("Really create? (y/N) ")))
@ -3723,7 +3889,11 @@ generate_subkeypair (KBNODE keyblock)
goto leave; 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) if (err)
goto leave; goto leave;

View File

@ -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 parse_expire_string(const char *string);
u32 ask_expire_interval(int object,const char *def_expire); u32 ask_expire_interval(int object,const char *def_expire);
u32 ask_expiredate(void); u32 ask_expiredate(void);
void generate_keypair (const char *fname, const char *card_serialno, void generate_keypair (ctrl_t ctrl, const char *fname,
int card_backup_key); const char *card_serialno, int card_backup_key);
int keygen_set_std_prefs (const char *string,int personal); int keygen_set_std_prefs (const char *string,int personal);
PKT_user_id *keygen_get_std_prefs (void); PKT_user_id *keygen_get_std_prefs (void);
int keygen_add_key_expire( PKT_signature *sig, void *opaque ); 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, gpg_error_t make_backsig (PKT_signature *sig, PKT_public_key *pk,
PKT_public_key *sub_pk, PKT_public_key *sub_psk, PKT_public_key *sub_pk, PKT_public_key *sub_psk,
u32 timestamp, const char *cache_nonce); 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 #ifdef ENABLE_CARD_SUPPORT
gpg_error_t generate_card_subkeypair (kbnode_t pub_keyblock, gpg_error_t generate_card_subkeypair (kbnode_t pub_keyblock,
int keyno, const char *serialno); int keyno, const char *serialno);