diff --git a/agent/command.c b/agent/command.c index 145c9595a..b1f89e0c8 100644 --- a/agent/command.c +++ b/agent/command.c @@ -978,8 +978,8 @@ cmd_genkey (assuan_context_t ctx, char *line) static const char hlp_readkey[] = - "READKEY \n" - " --card \n" + "READKEY [--no-data] \n" + " --card \n" "\n" "Return the public key for the given keygrip or keyid.\n" "With --card, private key file with card information will be created."; @@ -992,18 +992,20 @@ cmd_readkey (assuan_context_t ctx, char *line) gcry_sexp_t s_pkey = NULL; unsigned char *pkbuf = NULL; char *serialno = NULL; + char *keyidbuf = NULL; size_t pkbuflen; - const char *opt_card; + int opt_card, opt_no_data; if (ctrl->restricted) return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); - opt_card = has_option_name (line, "--card"); + opt_no_data = has_option (line, "--no-data"); + opt_card = has_option (line, "--card"); line = skip_options (line); if (opt_card) { - const char *keyid = opt_card; + const char *keyid = line; rc = agent_card_getattr (ctrl, "SERIALNO", &serialno); if (rc) @@ -1013,6 +1015,11 @@ cmd_readkey (assuan_context_t ctx, char *line) goto leave; } + /* Hack to create the shadow key for the OpenPGP standard keys. */ + if ((!strcmp (keyid, "$SIGNKEYID") || !strcmp (keyid, "$ENCRKEYID")) + && !agent_card_getattr (ctrl, keyid, &keyidbuf)) + keyid = keyidbuf; + rc = agent_card_readkey (ctrl, keyid, &pkbuf); if (rc) goto leave; @@ -1038,7 +1045,7 @@ cmd_readkey (assuan_context_t ctx, char *line) goto leave; } - rc = assuan_send_data (ctx, pkbuf, pkbuflen); + rc = opt_no_data? 0 : assuan_send_data (ctx, pkbuf, pkbuflen); } else { @@ -1058,12 +1065,13 @@ cmd_readkey (assuan_context_t ctx, char *line) { pkbuflen = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, pkbuf, pkbuflen); - rc = assuan_send_data (ctx, pkbuf, pkbuflen); + rc = opt_no_data? 0 : assuan_send_data (ctx, pkbuf, pkbuflen); } } } leave: + xfree (keyidbuf); xfree (serialno); xfree (pkbuf); gcry_sexp_release (s_pkey); diff --git a/g10/call-agent.c b/g10/call-agent.c index c7f1c296a..a9d72719f 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1097,6 +1097,16 @@ agent_scd_getattr (const char *name, struct agent_card_info_s *info) parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, learn_status_cb, info); + if (!rc && !strcmp (name, "KEY-FPR")) + { + /* Let the agent create the shadow keys if not yet done. */ + if (info->fpr1valid) + assuan_transact (agent_ctx, "READKEY --card --no-data -- $SIGNKEYID", + NULL, NULL, NULL, NULL, NULL, NULL); + if (info->fpr2valid) + assuan_transact (agent_ctx, "READKEY --card --no-data -- $ENCRKEYID", + NULL, NULL, NULL, NULL, NULL, NULL); + } return rc; } diff --git a/g10/skclist.c b/g10/skclist.c index 5a32b6a17..d0511adea 100644 --- a/g10/skclist.c +++ b/g10/skclist.c @@ -31,6 +31,7 @@ #include "keydb.h" #include "../common/util.h" #include "../common/i18n.h" +#include "keyserver-internal.h" #include "call-agent.h" @@ -326,11 +327,14 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) { gpg_error_t err = 0; const char *name; + int cardkey; /* We got an encryption fingerprint from the card */ + /* in c->info.fpr2. */ kbnode_t keyblock; struct { int eof; int state; + int cardkey_done; strlist_t sl; strlist_t card_list; char *serialno; @@ -389,6 +393,7 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) char *serialno; name = NULL; + cardkey = 0; keyblock = NULL; switch (c->state) { @@ -444,6 +449,7 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) bin2hex (c->info.fpr2, sizeof c->info.fpr2, c->fpr2 + 2); name = c->fpr2; + cardkey = 1; } } else if (gpg_err_code (err) == GPG_ERR_INV_NAME) @@ -551,6 +557,39 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) it. */ release_kbnode (c->keyblock); c->keyblock = NULL; + /* If this was a card key we might not yet have the + * public key for it. Thus check whether the card + * can return the fingerprint of the encryption key + * and we can then find the public key via LDAP. */ + if (cardkey && !c->cardkey_done + && gpg_err_code (err) == GPG_ERR_NO_SECKEY) + { + /* Note that this code does not handle the case + * for two readers having both openpgp + * encryption keys. Only one will be tried. */ + c->cardkey_done = 1; + if (opt.debug) + log_debug ("using LDAP to find public key" + " for current card\n"); + if (!keyserver_import_fprint + (ctrl, c->info.fpr2, sizeof c->info.fpr2, + opt.keyserver, KEYSERVER_IMPORT_FLAG_LDAP)) + { + char fpr_string[MAX_FINGERPRINT_LEN * 2 + 1]; + + bin2hex (c->info.fpr2, sizeof c->info.fpr2, + fpr_string); + err = getkey_byname (ctrl, NULL, NULL, fpr_string, 1, + &c->keyblock); + if (err) + { + release_kbnode (c->keyblock); + c->keyblock = NULL; + } + else + c->node = c->keyblock; + } + } } else c->node = c->keyblock;