1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

gpg: Improve key creation direct from the card.

* g10/call-agent.c (readkey_status_cb): New.
(agent_scd_readkey): Add new arg r_keytime and allow NULL for
r_result.  Change all callers.
(agent_readkey): Minor code reformatting.
* g10/keygen.c (pCARDKEY): New.
(struct para_data_s): Add u.bool.
(get_parameter_bool): New.
(do_create_from_keygrip): Add arg cardkey and make use of it.
(ask_algo): Add args r_cardkey and r_keytime.  Read the keytime of the
selected card key and return it.
(generate_keypair): Store CARDKEY and KEYTIME.
(do_generate_keypair): Pass CARDKEY to do_create_from_keygrip.
(generate_subkeypair): Ditto.
--

This allows to first create keys on the card (e.g. using gpg-card)
even without having any public key for OpenPGP.  Then the key
generation option 14 (cardkey) can be used to create a primary OpenPGP
key from the key on the card.

There are still a couple of problems related to the agent which
creates the stub key and may run into problems if creating a second
key from the card.  This will be fixed in a future patch.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-02-11 20:51:33 +01:00
parent 77ea916533
commit 9c719c9c1f
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 122 additions and 26 deletions

View File

@ -1,6 +1,7 @@
/* call-agent.c - Divert GPG operations to the agent.
* Copyright (C) 2001-2003, 2006-2011, 2013 Free Software Foundation, Inc.
* Copyright (C) 2013-2015 Werner Koch
* Copyright (C) 2020 g10 Code GmbH
*
* This file is part of GnuPG.
*
@ -1379,11 +1380,35 @@ agent_scd_readcert (const char *certidstr,
}
/* Callback for the agent_scd_readkey fucntion. */
static gpg_error_t
readkey_status_cb (void *opaque, const char *line)
{
u32 *keytimep = opaque;
const char *args;
if ((args = has_leading_keyword (line, "KEY-TIME")))
{
/* Skip the keyref - READKEY returns just one. */
while (*args && !spacep (args))
args++;
while (spacep (args))
args++;
/* We are at the keytime. */
*keytimep = strtoul (args, NULL, 10);
}
return 0;
}
/* This is a variant of agent_readkey which sends a READKEY command
* directly Scdaemon. On success a new s-expression is stored at
* R_RESULT. */
* R_RESULT. If R_KEYTIME is not NULL the key cresation time of an
* OpenPGP card is stored there - if that is not known 0 is stored.
* In the latter case it is allowed to pass NULL for R_RESULT. */
gpg_error_t
agent_scd_readkey (const char *keyrefstr, gcry_sexp_t *r_result)
agent_scd_readkey (const char *keyrefstr,
gcry_sexp_t *r_result, u32 *r_keytime)
{
gpg_error_t err;
char line[ASSUAN_LINELENGTH];
@ -1391,21 +1416,28 @@ agent_scd_readkey (const char *keyrefstr, gcry_sexp_t *r_result)
unsigned char *buf;
size_t len, buflen;
struct default_inq_parm_s dfltparm;
u32 keytime;
memset (&dfltparm, 0, sizeof dfltparm);
dfltparm.ctx = agent_ctx;
if (r_result)
*r_result = NULL;
if (r_keytime)
*r_keytime = 0;
err = start_agent (NULL, 1);
if (err)
return err;
init_membuf (&data, 1024);
snprintf (line, DIM(line), "SCD READKEY %s", keyrefstr);
snprintf (line, DIM(line),
"SCD READKEY --info%s -- %s",
r_result? "":"-only", keyrefstr);
keytime = 0;
err = assuan_transact (agent_ctx, line,
put_membuf_cb, &data,
default_inq_cb, &dfltparm,
NULL, NULL);
readkey_status_cb, &keytime);
if (err)
{
xfree (get_membuf (&data, &len));
@ -1415,9 +1447,14 @@ agent_scd_readkey (const char *keyrefstr, gcry_sexp_t *r_result)
if (!buf)
return gpg_error_from_syserror ();
if (r_result)
err = gcry_sexp_new (r_result, buf, buflen, 0);
else
err = 0;
xfree (buf);
if (!err && r_keytime)
*r_keytime = keytime;
return err;
}
@ -2250,10 +2287,12 @@ agent_genkey (ctrl_t ctrl, char **cache_nonce_addr, char **passwd_nonce_addr,
/* 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). */
/* Call the agent to read the public key part for a given keygrip.
* Values from FROMCARD:
* 0 - Standard
* 1 - The key is read from the current card
* via the agent and a stub file is created.
*/
gpg_error_t
agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
unsigned char **r_pubkey)
@ -2278,8 +2317,10 @@ agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
if (err)
return err;
snprintf (line, DIM(line), "READKEY %s%s", fromcard? "--card ":"",
hexkeygrip);
if (fromcard)
snprintf (line, DIM(line), "READKEY --card -- %s", hexkeygrip);
else
snprintf (line, DIM(line), "READKEY -- %s", hexkeygrip);
init_membuf (&data, 1024);
err = assuan_transact (agent_ctx, line,

View File

@ -137,7 +137,8 @@ int agent_scd_readcert (const char *certidstr,
void **r_buf, size_t *r_buflen);
/* Send a READKEY command to the SCdaemon. */
gpg_error_t agent_scd_readkey (const char *keyrefstr, gcry_sexp_t *r_result);
gpg_error_t agent_scd_readkey (const char *keyrefstr,
gcry_sexp_t *r_result, u32 *r_keytime);
/* Change the PIN of an OpenPGP card or reset the retry counter. */
int agent_scd_change_pin (int chvno, const char *serialno);
@ -183,7 +184,7 @@ gpg_error_t agent_genkey (ctrl_t ctrl,
const char *passphrase,
gcry_sexp_t *r_pubkey);
/* Read a public key. */
/* Read a public key. FROMCARD may be 0, 1, or 2. */
gpg_error_t agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
unsigned char **r_pubkey);

View File

@ -94,6 +94,7 @@ enum para_name {
pSUBKEYGRIP,
pVERSION, /* Desired version of the key packet. */
pSUBVERSION, /* Ditto for the subpacket. */
pCARDKEY /* The keygrips have been taken from active card (bool). */
};
struct para_data_s {
@ -103,6 +104,7 @@ struct para_data_s {
union {
u32 expire;
u32 creation;
int abool;
unsigned int usage;
struct revocation_key revkey;
char value[1];
@ -1419,7 +1421,8 @@ 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,
do_create_from_keygrip (ctrl_t ctrl, int algo,
const char *hexkeygrip, int cardkey,
kbnode_t pub_root, u32 timestamp, u32 expireval,
int is_subkey, int keygen_flags)
{
@ -1448,7 +1451,7 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, const char *hexkeygrip,
{
unsigned char *public;
err = agent_readkey (ctrl, 0, hexkeygrip, &public);
err = agent_readkey (ctrl, cardkey? 1:0, hexkeygrip, &public);
if (err)
return err;
err = gcry_sexp_sscan (&s_key, NULL,
@ -2060,14 +2063,18 @@ check_keygrip (ctrl_t ctrl, const char *hexgrip)
* 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. */
* it. If R_CARDKEY is not NULL and the keygrip has been taken from
* an active card, true is stored there; if R_KEYTIME is not NULL the
* cretion time of that key is then stored there. */
static int
ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
char **r_keygrip)
char **r_keygrip, int *r_cardkey, u32 *r_keytime)
{
gpg_error_t err;
char *keygrip = NULL;
u32 keytime = 0;
char *answer = NULL;
int cardkey = 0;
int algo;
int dummy_algo;
char *p;
@ -2288,7 +2295,7 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
if (keyref)
{
keyref++;
if (!agent_scd_readkey (keyref, &s_pkey))
if (!agent_scd_readkey (keyref, &s_pkey, NULL))
{
algostr = pubkey_algo_string (s_pkey, &algoid);
gcry_sexp_release (s_pkey);
@ -2296,7 +2303,7 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
}
/* We use the flags also encode the algo for use
* below. We need to tweak the algo in case
* GCRY_PK_ECC is returned becuase pubkey_algo_string
* GCRY_PK_ECC is returned because pubkey_algo_string
* is not aware of the OpenPGP algo mapping.
* FIXME: This is an ugly hack. */
sl->flags &= 0xff;
@ -2366,9 +2373,22 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
xfree (keygrip);
keygrip = xstrdup (sl->d);
cardkey = 1;
if ((p = strchr (keygrip, ' ')))
*p = 0;
algo = (sl->flags >>8);
err = agent_scd_readkey (keygrip, NULL, &keytime);
if (err)
{
tty_printf (_("error reading the card: %s\n"),
gpg_strerror (err));
free_strlist (keypairlist);
xfree (keygrip);
keygrip = NULL;
goto ask_again;
}
if (opt.expert)
*r_usage = ask_key_flags_with_mask (algo, addmode,
(sl->flags & 0xff),
@ -2392,6 +2412,10 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
xfree(answer);
if (r_keygrip)
*r_keygrip = keygrip;
if (r_cardkey)
*r_cardkey = cardkey;
if (r_keytime)
*r_keytime = keytime;
return algo;
}
@ -3431,7 +3455,7 @@ parse_key_parameter_part (ctrl_t ctrl,
else
continue; /* Not usable for us. */
if (agent_scd_readkey (keyref, &s_pkey))
if (agent_scd_readkey (keyref, &s_pkey, NULL))
continue; /* Could not read the key. */
algostr = pubkey_algo_string (s_pkey, &algoid);
@ -3961,6 +3985,14 @@ get_parameter_revkey( struct para_data_s *para, enum para_name key )
return r? &r->u.revkey : NULL;
}
static int
get_parameter_bool (struct para_data_s *para, enum para_name key)
{
struct para_data_s *r = get_parameter (para, key);
return (r && r->u.abool);
}
static int
proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname,
struct output_control_s *outctrl, int card )
@ -4757,8 +4789,11 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
{
int subkey_algo;
char *key_from_hexgrip = NULL;
int cardkey;
u32 keytime;
algo = ask_algo (ctrl, 0, &subkey_algo, &use, &key_from_hexgrip);
algo = ask_algo (ctrl, 0, &subkey_algo, &use,
&key_from_hexgrip, &cardkey, &keytime);
if (key_from_hexgrip)
{
r = xmalloc_clear( sizeof *r + 20 );
@ -4785,6 +4820,21 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
r->next = para;
para = r;
r = xmalloc_clear (sizeof *r);
r->key = pCARDKEY;
r->u.abool = cardkey;
r->next = para;
para = r;
if (cardkey)
{
r = xmalloc_clear (sizeof *r);
r->key = pKEYCREATIONDATE;
r->u.creation = keytime;
r->next = para;
para = r;
}
xfree (key_from_hexgrip);
}
else
@ -5169,6 +5219,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
int algo;
u32 expire;
const char *key_from_hexgrip = NULL;
int cardkey;
unsigned int keygen_flags;
if (outctrl->dryrun)
@ -5238,13 +5289,14 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
algo = get_parameter_algo (ctrl, para, pKEYTYPE, NULL );
expire = get_parameter_u32( para, pKEYEXPIRE );
key_from_hexgrip = get_parameter_value (para, pKEYGRIP);
cardkey = get_parameter_bool (para, pCARDKEY);
keygen_flags = outctrl->keygen_flags;
if (get_parameter_uint (para, pVERSION) == 5)
keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY;
if (key_from_hexgrip)
err = do_create_from_keygrip (ctrl, algo, key_from_hexgrip,
err = do_create_from_keygrip (ctrl, algo, key_from_hexgrip, cardkey,
pub_root, timestamp, expire, 0, keygen_flags);
else if (!card)
err = do_create (algo,
@ -5318,7 +5370,8 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY;
if (key_from_hexgrip)
err = do_create_from_keygrip (ctrl, subkey_algo, key_from_hexgrip,
err = do_create_from_keygrip (ctrl, subkey_algo,
key_from_hexgrip, cardkey,
pub_root, timestamp,
get_parameter_u32 (para, pSUBKEYEXPIRE),
1, keygen_flags);
@ -5596,6 +5649,7 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr,
const char *curve = NULL;
u32 cur_time;
char *key_from_hexgrip = NULL;
int cardkey = 0;
char *hexgrip = NULL;
char *serialno = NULL;
char *cache_nonce = NULL;
@ -5660,7 +5714,7 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr,
if (interactive)
{
algo = ask_algo (ctrl, 1, NULL, &use, &key_from_hexgrip);
algo = ask_algo (ctrl, 1, NULL, &use, &key_from_hexgrip, &cardkey, NULL);
log_assert (algo);
if (key_from_hexgrip)
@ -5713,7 +5767,7 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr,
/* Start creation. */
if (key_from_hexgrip)
{
err = do_create_from_keygrip (ctrl, algo, key_from_hexgrip,
err = do_create_from_keygrip (ctrl, algo, key_from_hexgrip, cardkey,
keyblock, cur_time, expire, 1,
keygen_flags);
}