gpg: Allow direct key generation from card with --full-gen-key.

* g10/call-agent.c (agent_scd_readkey): New.
* g10/keygen.c (ask_key_flags): Factor code out to ..
(ask_key_flags_with_mask): new.
(ask_algo): New mode 14.
--

Note that this new menu 14 is always displayed.  The usage flags can
be changed only in --expert mode, though.  Creating and using signing
keys works but decryption does not yet work; we will need to tweak a
couple of other places for that.  Tested with a Yubikey's PIV app.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2019-04-02 18:57:09 +02:00
parent f952226043
commit a480182f9d
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 213 additions and 28 deletions

View File

@ -1572,6 +1572,7 @@ Description of some debug flags:
| ecc/* | 11 | ECC (set your own capabilities) |
| ecc/e | 12 | ECC (encrypt only) |
| keygrip | 13 | Existing key |
| cardkey | 14 | Existing key from card |
If one of the "foo/*" names are used a "keygen.flags" prompt needs
to be answered as well. Instead of toggling the predefined flags,

View File

@ -210,7 +210,7 @@ Key management ...: [none]
keyref .....: PIV.9D
@end example
Note that the ``Displayed s/sn'' is printed on the token and also
Note that the ``Displayed s/n'' is printed on the token and also
shown in Pinentry prompts asking for the PIN. The four standard key
slots are always shown, if other key slots are initialized they are
shown as well. The @emph{PIV authentication} key (internal reference
@ -231,11 +231,11 @@ which needs to be provided only once so that decryption operations can
then be done until the card is reset or removed from the reader or USB
port.
We now generate tree of the four keys. Note that GnuPG does currently
not use the the @emph{Card authentication} key but because it is
mandatory by the specs we create it anyway. Key generation requires
that we authenticate to the card. This can be done either on the
command line (which would reveal the key):
We now generate three of the four keys. Note that GnuPG does
currently not use the the @emph{Card authentication} key; however,
that key is mandatory by the PIV standard and thus we create it too.
Key generation requires that we authenticate to the card. This can be
done either on the command line (which would reveal the key):
@example
gpg/card> auth 010203040506070801020304050607080102030405060708
@ -360,7 +360,7 @@ gpgsm: total number processed: 1
gpgsm: imported: 1
@end example
Note the last steps which imported the created certificate. If you
Note the last step which imported the created certificate. If you
you instead created a certificate signing request (CSR) instead of a
self-signed certificate and sent this off to a CA you would do the
same import step with the certificate received from the CA. Take note

View File

@ -1251,6 +1251,49 @@ agent_scd_readcert (const char *certidstr,
}
/* 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. */
gpg_error_t
agent_scd_readkey (const char *keyrefstr, gcry_sexp_t *r_result)
{
gpg_error_t err;
char line[ASSUAN_LINELENGTH];
membuf_t data;
unsigned char *buf;
size_t len, buflen;
struct default_inq_parm_s dfltparm;
memset (&dfltparm, 0, sizeof dfltparm);
dfltparm.ctx = agent_ctx;
*r_result = NULL;
err = start_agent (NULL, 1);
if (err)
return err;
init_membuf (&data, 1024);
snprintf (line, DIM(line), "SCD READKEY %s", keyrefstr);
err = assuan_transact (agent_ctx, line,
put_membuf_cb, &data,
default_inq_cb, &dfltparm,
NULL, NULL);
if (err)
{
xfree (get_membuf (&data, &len));
return err;
}
buf = get_membuf (&data, &buflen);
if (!buf)
return gpg_error_from_syserror ();
err = gcry_sexp_new (r_result, buf, buflen, 0);
xfree (buf);
return err;
}
struct card_cardlist_parm_s {
int error;

View File

@ -114,10 +114,13 @@ int agent_scd_writecert (const char *certidstr,
/* Send a GENKEY command to the SCdaemon. */
int agent_scd_genkey (int keyno, int force, u32 *createtime);
/* Send a READKEY command to the SCdaemon. */
/* Send a READCERT command to the SCdaemon. */
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);
/* Change the PIN of an OpenPGP card or reset the retry counter. */
int agent_scd_change_pin (int chvno, const char *serialno);

View File

@ -1881,24 +1881,26 @@ print_key_flags(int flags)
/* Ask for the key flags and return them. CURRENT gives the current
* usage which should normally be given as 0. */
* usage which should normally be given as 0. MASK gives the allowed
* flags. */
unsigned int
ask_key_flags (int algo, int subkey, unsigned int current)
ask_key_flags_with_mask (int algo, int subkey, unsigned int current,
unsigned int mask)
{
/* TRANSLATORS: Please use only plain ASCII characters for the
translation. If this is not possible use single digits. The
string needs to 8 bytes long. Here is a description of the
functions:
s = Toggle signing capability
e = Toggle encryption capability
a = Toggle authentication capability
q = Finish
*/
* translation. If this is not possible use single digits. The
* string needs to 8 bytes long. Here is a description of the
* functions:
*
* s = Toggle signing capability
* e = Toggle encryption capability
* a = Toggle authentication capability
* q = Finish
*/
const char *togglers = _("SsEeAaQq");
char *answer = NULL;
const char *s;
unsigned int possible = openpgp_pk_algo_usage(algo);
unsigned int possible;
if ( strlen(togglers) != 8 )
{
@ -1907,22 +1909,26 @@ ask_key_flags (int algo, int subkey, unsigned int current)
togglers = "11223300";
}
/* Only primary keys may certify. */
if(subkey)
possible&=~PUBKEY_USAGE_CERT;
/* Mask the possible usage flags. This is for example used for a
* card based key. */
possible = (openpgp_pk_algo_usage (algo) & mask);
/* Preload the current set with the possible set, minus
authentication if CURRENT has been given as 0. If CURRENT has
been has non-zero we mask with all possible usages. */
/* However, only primary keys may certify. */
if (subkey)
possible &= ~PUBKEY_USAGE_CERT;
/* Preload the current set with the possible set, without
* authentication if CURRENT is 0. If CURRENT is non-zero we mask
* with all possible usages. */
if (current)
current &= possible;
else
current = (possible&~PUBKEY_USAGE_AUTH);
for(;;)
for (;;)
{
tty_printf("\n");
tty_printf(_("Possible actions for a %s key: "),
tty_printf(_("Possible actions for this %s key: "),
(algo == PUBKEY_ALGO_ECDSA
|| algo == PUBKEY_ALGO_EDDSA)
? "ECDSA/EdDSA" : openpgp_pk_algo_name (algo));
@ -2009,6 +2015,13 @@ ask_key_flags (int algo, int subkey, unsigned int current)
}
unsigned int
ask_key_flags (int algo, int subkey, unsigned int current)
{
return ask_key_flags_with_mask (algo, subkey, current, ~0);
}
/* 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
@ -2047,10 +2060,12 @@ static int
ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
char **r_keygrip)
{
gpg_error_t err;
char *keygrip = NULL;
char *answer = NULL;
int algo;
int dummy_algo;
char *p;
if (!r_subkey_algo)
r_subkey_algo = &dummy_algo;
@ -2101,6 +2116,8 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
if (opt.expert && r_keygrip)
tty_printf (_(" (%d) Existing key\n"), 13 );
if (r_keygrip)
tty_printf (_(" (%d) Existing key from card\n"), 14 );
for (;;)
{
@ -2221,9 +2238,130 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
*r_usage = ask_key_flags (algo, addmode, 0);
break;
}
else if ((algo == 14 || !strcmp (answer, "cardkey")) && r_keygrip)
{
char *serialno;
strlist_t keypairlist, sl;
int count, selection;
err = agent_scd_serialno (&serialno, NULL);
if (err)
{
tty_printf (_("error reading the card: %s\n"),
gpg_strerror (err));
goto ask_again;
}
tty_printf (_("Serial number of the card: %s\n"), serialno);
xfree (serialno);
err = agent_scd_keypairinfo (ctrl, &keypairlist);
if (err)
{
tty_printf (_("error reading the card: %s\n"),
gpg_strerror (err));
goto ask_again;
}
do
{
tty_printf (_("Available keys:\n"));
for (count=1,sl=keypairlist; sl; sl = sl->next, count++)
{
gcry_sexp_t s_pkey;
char *algostr = NULL;
enum gcry_pk_algos algoid = 0;
const char *keyref;
int any = 0;
keyref = strchr (sl->d, ' ');
if (keyref)
{
keyref++;
if (!agent_scd_readkey (keyref, &s_pkey))
{
algostr = pubkey_algo_string (s_pkey, &algoid);
gcry_sexp_release (s_pkey);
}
}
/* 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
* is not aware of the OpenPGP algo mapping.
* FIXME: This is an ugly hack. */
sl->flags &= 0xff;
if (algoid == GCRY_PK_ECC
&& algostr && !strncmp (algostr, "nistp", 5)
&& !(sl->flags & GCRY_PK_USAGE_ENCR))
sl->flags |= (PUBKEY_ALGO_ECDSA << 8);
else
sl->flags |= (map_pk_gcry_to_openpgp (algoid) << 8);
tty_printf (" (%d) %s %s", count, sl->d, algostr);
if ((sl->flags & GCRY_PK_USAGE_CERT))
{
tty_printf ("%scert", any?",":" (");
any = 1;
}
if ((sl->flags & GCRY_PK_USAGE_SIGN))
{
tty_printf ("%ssign", any?",":" (");
any = 1;
}
if ((sl->flags & GCRY_PK_USAGE_AUTH))
{
tty_printf ("%sauth", any?",":" (");
any = 1;
}
if ((sl->flags & GCRY_PK_USAGE_ENCR))
{
tty_printf ("%sencr", any?",":" (");
any = 1;
}
tty_printf ("%s\n", any?")":"");
xfree (algostr);
}
xfree (answer);
answer = cpr_get ("keygen.cardkey", _("Your selection? "));
cpr_kill_prompt ();
trim_spaces (answer);
selection = atoi (answer);
}
while (!(selection > 0 && selection < count));
for (count=1,sl=keypairlist; sl; sl = sl->next, count++)
if (count == selection)
break;
if (!sl)
{
/* Just in case COUNT is zero (no keys). */
free_strlist (keypairlist);
goto ask_again;
}
xfree (keygrip);
keygrip = xstrdup (sl->d);
if ((p = strchr (keygrip, ' ')))
*p = 0;
algo = (sl->flags >>8);
if (opt.expert)
*r_usage = ask_key_flags_with_mask (algo, addmode,
(sl->flags & 0xff),
(sl->flags & 0xff));
else
{
*r_usage = (sl->flags & 0xff);
if (addmode)
*r_usage &= ~GCRY_PK_USAGE_CERT;
}
free_strlist (keypairlist);
break;
}
else
tty_printf (_("Invalid selection.\n"));
ask_again:
;
}
xfree(answer);