From a480182f9d7ec316648cb64248f7a0cc8f681bc3 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 2 Apr 2019 18:57:09 +0200 Subject: [PATCH] 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 --- doc/DETAILS | 1 + doc/gpg-card.texi | 14 ++-- g10/call-agent.c | 43 +++++++++++ g10/call-agent.h | 5 +- g10/keygen.c | 178 ++++++++++++++++++++++++++++++++++++++++------ 5 files changed, 213 insertions(+), 28 deletions(-) diff --git a/doc/DETAILS b/doc/DETAILS index 74a63ef00..3046523da 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -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, diff --git a/doc/gpg-card.texi b/doc/gpg-card.texi index aa49f81e7..92379aa19 100644 --- a/doc/gpg-card.texi +++ b/doc/gpg-card.texi @@ -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 diff --git a/g10/call-agent.c b/g10/call-agent.c index 83ca921a7..a0c5f811f 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -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; diff --git a/g10/call-agent.h b/g10/call-agent.h index 0a545b22e..cb874fdad 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -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); diff --git a/g10/keygen.c b/g10/keygen.c index 943b40110..6ea4e72c6 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -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);