From ec6a6779236a89d4784a6bb7de0def9cc0f9e8a4 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 3 Apr 2019 15:30:10 +0200 Subject: [PATCH] gpg: Allow decryption using PIV cards. * g10/call-agent.c (struct getattr_one_parm_s): New. (getattr_one_status_cb): New. (agent_scd_getattr_one): New. * g10/pubkey-enc.c (get_it): Allow the standard leading zero byte from pkcs#1. * g10/skclist.c (enum_secret_keys): Handle non-OpenPGP cards. Signed-off-by: Werner Koch --- g10/call-agent.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ g10/call-agent.h | 3 ++ g10/pubkey-enc.c | 10 ++++++ g10/skclist.c | 61 +++++++++++++++++++++++++++++++----- 4 files changed, 146 insertions(+), 8 deletions(-) diff --git a/g10/call-agent.c b/g10/call-agent.c index 3b4882b53..f603d491a 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -947,6 +947,86 @@ agent_keytocard (const char *hexgrip, int keyno, int force, } + +/* Object used with the agent_scd_getattr_one. */ +struct getattr_one_parm_s { + const char *keyword; /* Keyword to look for. */ + char *data; /* Malloced and unescaped data. */ + gpg_error_t err; /* Error code or 0 on success. */ +}; + + +/* Callback for agent_scd_getattr_one. */ +static gpg_error_t +getattr_one_status_cb (void *opaque, const char *line) +{ + struct getattr_one_parm_s *parm = opaque; + const char *s; + + if (parm->data) + return 0; /* We want only the first occurrence. */ + + if ((s=has_leading_keyword (line, parm->keyword))) + { + parm->data = percent_plus_unescape (s, 0xff); + if (!parm->data) + parm->err = gpg_error_from_syserror (); + } + + return 0; +} + + +/* Simplified version of agent_scd_getattr. This function returns + * only the first occurance of the attribute NAME and stores it at + * R_VALUE. A nul in the result is silennly replaced by 0xff. On + * error NULL is stored at R_VALUE. */ +gpg_error_t +agent_scd_getattr_one (const char *name, char **r_value) +{ + gpg_error_t err; + char line[ASSUAN_LINELENGTH]; + struct default_inq_parm_s inqparm; + struct getattr_one_parm_s parm; + + *r_value = NULL; + + if (!*name) + return gpg_error (GPG_ERR_INV_VALUE); + + memset (&inqparm, 0, sizeof inqparm); + inqparm.ctx = agent_ctx; + + memset (&parm, 0, sizeof parm); + parm.keyword = name; + + /* We assume that NAME does not need escaping. */ + if (12 + strlen (name) > DIM(line)-1) + return gpg_error (GPG_ERR_TOO_LARGE); + stpcpy (stpcpy (line, "SCD GETATTR "), name); + + err = start_agent (NULL, 1); + if (err) + return err; + + err = assuan_transact (agent_ctx, line, + NULL, NULL, + default_inq_cb, &inqparm, + getattr_one_status_cb, &parm); + if (!err && parm.err) + err = parm.err; + else if (!err && !parm.data) + err = gpg_error (GPG_ERR_NO_DATA); + + if (!err) + *r_value = parm.data; + else + xfree (parm.data); + + return err; +} + + /* Call the agent to retrieve a data object. This function returns * the data in the same structure as used by the learn command. It is diff --git a/g10/call-agent.h b/g10/call-agent.h index cb874fdad..c0018a595 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -96,6 +96,9 @@ int agent_scd_serialno (char **r_serialno, const char *demand); /* Send an APDU to the card. */ gpg_error_t agent_scd_apdu (const char *hexapdu, unsigned int *r_sw); +/* Get attribute NAME from the card and store at R_VALUE. */ +gpg_error_t agent_scd_getattr_one (const char *name, char **r_value); + /* Update INFO with the attribute NAME. */ int agent_scd_getattr (const char *name, struct agent_card_info_s *info); diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 055c39b8f..f61fa7abe 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -319,6 +319,16 @@ get_it (ctrl_t ctrl, err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } + + /* FIXME: Actually the leading zero is required but due to + * the way we encode the output in libgcrypt as an MPI we + * are not able to encode that leading zero. However, when + * using a Smartcard we are doing it the right way and + * therefore we have to skip the zero. This should be fixed + * in gpg-agent of course. */ + if (!frame[n]) + n++; + if (frame[n] == 1 && frame[nframe - 1] == 2) { log_info (_("old encoding of the DEK is not supported\n")); diff --git a/g10/skclist.c b/g10/skclist.c index ebbaba254..b4f83ea1a 100644 --- a/g10/skclist.c +++ b/g10/skclist.c @@ -340,6 +340,10 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) SK_LIST results; } *c = *context; +#if MAX_FINGERPRINT_LEN < KEYGRIP_LEN +# error buffer too short for this configuration +#endif + if (!c) { /* Make a new context. */ @@ -430,17 +434,58 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) xfree (serialno); c->info.fpr2len = 0; err = agent_scd_getattr ("KEY-FPR", &c->info); + if (!err) + { + if (c->info.fpr2len) + { + c->fpr2[0] = '0'; + c->fpr2[1] = 'x'; + bin2hex (c->info.fpr2, sizeof c->info.fpr2, + c->fpr2 + 2); + name = c->fpr2; + } + } + else if (gpg_err_code (err) == GPG_ERR_INV_NAME) + { + /* KEY-FPR not supported by the card - get + * the key using the keygrip. */ + char *keyref; + strlist_t kplist, sl; + const char *s; + int i; + + err = agent_scd_getattr_one ("$ENCRKEYID", &keyref); + if (!err) + { + err = agent_scd_keypairinfo (ctrl, &kplist); + if (!err) + { + for (sl = kplist; sl; sl = sl->next) + if ((s = strchr (sl->d, ' ')) + && !strcmp (s+1, keyref)) + break; + if (sl) + { + c->fpr2[0] = '&'; + for (i=1, s=sl->d; + (*s && *s != ' ' + && i < sizeof c->fpr2 - 3); + s++, i++) + c->fpr2[i] = *s; + c->fpr2[i] = 0; + name = c->fpr2; + } + else /* Restore error. */ + err = gpg_error (GPG_ERR_INV_NAME); + free_strlist (kplist); + } + } + xfree (keyref); + } if (err) - log_error ("error retrieving key fingerprint from card: %s\n", + log_error ("error retrieving key from card: %s\n", gpg_strerror (err)); - if (c->info.fpr2len) - { - c->fpr2[0] = '0'; - c->fpr2[1] = 'x'; - bin2hex (c->info.fpr2, sizeof c->info.fpr2,c->fpr2+2); - name = c->fpr2; - } c->sl = c->sl->next; } else