scd: New options --info and --info-only for READKEY.

* scd/command.c (cmd_readkey): New options --info and --info-only.
* scd/app.c (app_readkey): New arg 'flags'.
* scd/app-common.h (APP_READKEY_FLAG_INFO): New.
(struct app_ctx_s): New args 'ctrl' and 'flags' for member readkey.
Change all implementers.
* scd/app-nks.c (do_readkey): Stub implementation of
APP_READKEY_FLAG_INFO.
* scd/app-openpgp.c (do_readkey): Implement APP_READKEY_FLAG_INFO.
* scd/app-piv.c (do_readkey): Ditto.
--

This feature allows to quickly get the keygrip and in most cases also
the usage flags for one specific keyref.  Example:

 <- readkey --info-only  PIV.9D
 -> S KEYPAIRINFO FC6061FB457224370B85C6F34DD56CD29E669620 PIV.9D e
 -> OK

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2019-04-03 17:31:09 +02:00
parent ec6a677923
commit 679b8f1c04
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
7 changed files with 147 additions and 50 deletions

View File

@ -36,10 +36,14 @@
/* Flags used with app_writekey. */ /* Flags used with app_writekey. */
#define APP_WRITEKEY_FLAG_FORCE 1 /* Force overwriting existing key. */ #define APP_WRITEKEY_FLAG_FORCE 1 /* Force overwriting existing key. */
/* Flags used with app_readkey. */
#define APP_READKEY_FLAG_INFO 1 /* Send also a KEYPAIRINFO line. */
/* Bit flags set by the decipher function into R_INFO. */ /* Bit flags set by the decipher function into R_INFO. */
#define APP_DECIPHER_INFO_NOPAD 1 /* Padding has been removed. */ #define APP_DECIPHER_INFO_NOPAD 1 /* Padding has been removed. */
struct app_local_s; /* Defined by all app-*.c. */ struct app_local_s; /* Defined by all app-*.c. */
struct app_ctx_s { struct app_ctx_s {
@ -75,8 +79,9 @@ struct app_ctx_s {
gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl, unsigned int flags); gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl, unsigned int flags);
gpg_error_t (*readcert) (app_t app, const char *certid, gpg_error_t (*readcert) (app_t app, const char *certid,
unsigned char **cert, size_t *certlen); unsigned char **cert, size_t *certlen);
gpg_error_t (*readkey) (app_t app, const char *certid, gpg_error_t (*readkey) (app_t app, ctrl_t ctrl,
unsigned char **pk, size_t *pklen); const char *certid, unsigned int flags,
unsigned char **pk, size_t *pklen);
gpg_error_t (*getattr) (app_t app, ctrl_t ctrl, const char *name); gpg_error_t (*getattr) (app_t app, ctrl_t ctrl, const char *name);
gpg_error_t (*setattr) (app_t app, const char *name, gpg_error_t (*setattr) (app_t app, const char *name,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
@ -126,6 +131,8 @@ struct app_ctx_s {
/*-- app-help.c --*/ /*-- app-help.c --*/
unsigned int app_help_count_bits (const unsigned char *a, size_t len); unsigned int app_help_count_bits (const unsigned char *a, size_t len);
gpg_error_t app_help_get_keygrip_string_pk (const void *pk, size_t pklen,
char *hexkeygrip);
gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip); gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip);
gpg_error_t app_help_pubkey_from_cert (const void *cert, size_t certlen, gpg_error_t app_help_pubkey_from_cert (const void *cert, size_t certlen,
unsigned char **r_pk, size_t *r_pklen); unsigned char **r_pk, size_t *r_pklen);
@ -152,7 +159,8 @@ gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl,
gpg_error_t app_readcert (app_t app, ctrl_t ctrl, const char *certid, gpg_error_t app_readcert (app_t app, ctrl_t ctrl, const char *certid,
unsigned char **cert, size_t *certlen); unsigned char **cert, size_t *certlen);
gpg_error_t app_readkey (app_t app, ctrl_t ctrl, gpg_error_t app_readkey (app_t app, ctrl_t ctrl,
const char *keyid, unsigned char **pk, size_t *pklen); const char *keyid, unsigned int flags,
unsigned char **pk, size_t *pklen);
gpg_error_t app_getattr (app_t app, ctrl_t ctrl, const char *name); gpg_error_t app_getattr (app_t app, ctrl_t ctrl, const char *name);
gpg_error_t app_setattr (app_t app, ctrl_t ctrl, const char *name, gpg_error_t app_setattr (app_t app, ctrl_t ctrl, const char *name,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),

View File

@ -52,26 +52,17 @@ app_help_count_bits (const unsigned char *a, size_t len)
} }
/* Return the KEYGRIP for the certificate CERT as an hex encoded /* Return the KEYGRIP for the canonical encoded public key (PK,PKLEN)
string in the user provided buffer HEXKEYGRIP which must be of at * as an hex encoded string in the user provided buffer HEXKEYGRIP
least 41 bytes. */ * which must be of at least 41 bytes. */
gpg_error_t gpg_error_t
app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip) app_help_get_keygrip_string_pk (const void *pk, size_t pklen, char *hexkeygrip)
{ {
gpg_error_t err; gpg_error_t err;
gcry_sexp_t s_pkey; gcry_sexp_t s_pkey;
ksba_sexp_t p; unsigned char array[KEYGRIP_LEN];
size_t n;
unsigned char array[20];
p = ksba_cert_get_public_key (cert); err = gcry_sexp_sscan (&s_pkey, NULL, pk, pklen);
if (!p)
return gpg_error (GPG_ERR_BUG);
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
err = gcry_sexp_sscan (&s_pkey, NULL, (char*)p, n);
xfree (p);
if (err) if (err)
return err; /* Can't parse that S-expression. */ return err; /* Can't parse that S-expression. */
if (!gcry_pk_get_keygrip (s_pkey, array)) if (!gcry_pk_get_keygrip (s_pkey, array))
@ -81,12 +72,34 @@ app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip)
} }
gcry_sexp_release (s_pkey); gcry_sexp_release (s_pkey);
bin2hex (array, 20, hexkeygrip); bin2hex (array, KEYGRIP_LEN, hexkeygrip);
return 0; return 0;
} }
/* Return the KEYGRIP for the certificate CERT as an hex encoded
string in the user provided buffer HEXKEYGRIP which must be of at
least 41 bytes. */
gpg_error_t
app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip)
{
gpg_error_t err;
ksba_sexp_t p;
size_t n;
p = ksba_cert_get_public_key (cert);
if (!p)
return gpg_error (GPG_ERR_BUG);
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
err = app_help_get_keygrip_string_pk ((void*)p, n, hexkeygrip);
ksba_free (p);
return err;
}
gpg_error_t gpg_error_t
app_help_pubkey_from_cert (const void *cert, size_t certlen, app_help_pubkey_from_cert (const void *cert, size_t certlen,
unsigned char **r_pk, size_t *r_pklen) unsigned char **r_pk, size_t *r_pklen)

View File

@ -618,7 +618,8 @@ do_readcert (app_t app, const char *certid,
certificate parsing code in commands.c:cmd_readkey. For internal certificate parsing code in commands.c:cmd_readkey. For internal
use PK and PKLEN may be NULL to just check for an existing key. */ use PK and PKLEN may be NULL to just check for an existing key. */
static gpg_error_t static gpg_error_t
do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
unsigned char **pk, size_t *pklen)
{ {
gpg_error_t err; gpg_error_t err;
unsigned char *buffer[2]; unsigned char *buffer[2];
@ -653,6 +654,14 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
return err; return err;
} }
if ((flags & APP_READKEY_FLAG_INFO))
{
/* Not yet implemented but we won't get here for any regular
* keyrefs anyway, thus the top layer will provide the
* keypairinfo from the certificate. */
(void)ctrl;
}
if (pk && pklen) if (pk && pklen)
{ {
*pk = make_canon_sexp_from_rsa_pk (buffer[0], buflen[0], *pk = make_canon_sexp_from_rsa_pk (buffer[0], buflen[0],
@ -698,7 +707,7 @@ do_writekey (app_t app, ctrl_t ctrl,
else else
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
if (!force && !do_readkey (app, keyid, NULL, NULL)) if (!force && !do_readkey (app, ctrl, keyid, 0, NULL, NULL))
return gpg_error (GPG_ERR_EEXIST); return gpg_error (GPG_ERR_EEXIST);
/* Parse the S-expression. */ /* Parse the S-expression. */

View File

@ -1889,7 +1889,8 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
buffer. On error PK and PKLEN are not changed and an error code is buffer. On error PK and PKLEN are not changed and an error code is
returned. */ returned. */
static gpg_error_t static gpg_error_t
do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
unsigned char **pk, size_t *pklen)
{ {
gpg_error_t err; gpg_error_t err;
int keyno; int keyno;
@ -1912,15 +1913,25 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
if (!buf) if (!buf)
return gpg_error (GPG_ERR_NO_PUBKEY); return gpg_error (GPG_ERR_NO_PUBKEY);
*pklen = app->app_local->pk[keyno].keylen; if ((flags & APP_READKEY_FLAG_INFO))
*pk = xtrymalloc (*pklen);
if (!*pk)
{ {
err = gpg_error_from_syserror (); err = send_keypair_info (app, ctrl, keyno+1);
*pklen = 0; if (err)
return err; return err;
}
if (pk && pklen)
{
*pklen = app->app_local->pk[keyno].keylen;
*pk = xtrymalloc (*pklen);
if (!*pk)
{
err = gpg_error_from_syserror ();
*pklen = 0;
return err;
}
memcpy (*pk, buf, *pklen);
} }
memcpy (*pk, buf, *pklen);
return 0; return 0;
} }

View File

@ -1465,7 +1465,7 @@ do_readcert (app_t app, const char *certid,
* returned. * returned.
*/ */
static gpg_error_t static gpg_error_t
do_readkey (app_t app, const char *keyrefstr, do_readkey (app_t app, ctrl_t ctrl, const char *keyrefstr, unsigned int flags,
unsigned char **r_pk, size_t *r_pklen) unsigned char **r_pk, size_t *r_pklen)
{ {
gpg_error_t err; gpg_error_t err;
@ -1517,9 +1517,35 @@ do_readkey (app_t app, const char *keyrefstr,
goto leave; goto leave;
} }
*r_pk = pk; if ((flags & APP_READKEY_FLAG_INFO))
pk = NULL; {
*r_pklen = pklen; char keygripstr[KEYGRIP_LEN*2+1];
char idbuf[50];
const char *usage;
err = app_help_get_keygrip_string_pk (pk, pklen, keygripstr);
if (err)
{
log_error ("app_help_get_keygrip_string_pk failed: %s\n",
gpg_strerror (err));
goto leave;
}
usage = dobj->usage? dobj->usage : "";
snprintf (idbuf, sizeof idbuf, "PIV.%s", dobj->keyref);
send_status_info (ctrl, "KEYPAIRINFO",
keygripstr, strlen (keygripstr),
idbuf, strlen (idbuf),
usage, strlen (usage),
NULL, (size_t)0);
}
if (r_pk && r_pklen)
{
*r_pk = pk;
pk = NULL;
*r_pklen = pklen;
}
leave: leave:
gcry_sexp_release (s_pkey); gcry_sexp_release (s_pkey);
@ -3218,7 +3244,7 @@ do_writecert (app_t app, ctrl_t ctrl,
* GPG_ERR_NO_PUBKEY). We enforce this because otherwise the only * GPG_ERR_NO_PUBKEY). We enforce this because otherwise the only
* way to detect whether a key exists is by trying to use that * way to detect whether a key exists is by trying to use that
* key. */ * key. */
err = do_readkey (app, certrefstr, &orig_pk, &orig_pklen); err = do_readkey (app, ctrl, certrefstr, 0, &orig_pk, &orig_pklen);
if (err) if (err)
{ {
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)

View File

@ -772,14 +772,15 @@ app_readcert (app_t app, ctrl_t ctrl, const char *certid,
/* Read the key with ID KEYID. On success a canonical encoded /* Read the key with ID KEYID. On success a canonical encoded
S-expression with the public key will get stored at PK and its * S-expression with the public key will get stored at PK and its
length (for assertions) at PKLEN; the caller must release that * length (for assertions) at PKLEN; the caller must release that
buffer. On error NULL will be stored at PK and PKLEN and an error * buffer. On error NULL will be stored at PK and PKLEN and an error
code returned. * code returned. If the key is not required NULL may be passed for
* PK; this makse send if the APP_READKEY_FLAG_INFO has also been set.
This function might not be supported by all applications. */ *
* This function might not be supported by all applications. */
gpg_error_t gpg_error_t
app_readkey (app_t app, ctrl_t ctrl, const char *keyid, app_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
unsigned char **pk, size_t *pklen) unsigned char **pk, size_t *pklen)
{ {
gpg_error_t err; gpg_error_t err;
@ -789,7 +790,7 @@ app_readkey (app_t app, ctrl_t ctrl, const char *keyid,
if (pklen) if (pklen)
*pklen = 0; *pklen = 0;
if (!app || !keyid || !pk || !pklen) if (!app || !keyid)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
if (!app->ref_count) if (!app->ref_count)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
@ -798,7 +799,7 @@ app_readkey (app_t app, ctrl_t ctrl, const char *keyid,
err = lock_app (app, ctrl); err = lock_app (app, ctrl);
if (err) if (err)
return err; return err;
err= app->fnc.readkey (app, keyid, pk, pklen); err= app->fnc.readkey (app, ctrl, keyid, flags, pk, pklen);
unlock_app (app); unlock_app (app);
return err; return err;
} }

View File

@ -502,19 +502,20 @@ cmd_readcert (assuan_context_t ctx, char *line)
static const char hlp_readkey[] = static const char hlp_readkey[] =
"READKEY [--advanced] <keyid>|<oid>\n" "READKEY [--advanced] [--info[-only]] <keyid>|<oid>\n"
"\n" "\n"
"Return the public key for the given cert or key ID as a standard\n" "Return the public key for the given cert or key ID as a standard\n"
"S-expression.\n" "S-expression. With --advanced the S-expression is returned in\n"
"In --advanced mode it returns the S-expression in advanced format.\n" "advanced format. With --info a KEYPAIRINFO status line is also\n"
"\n" "emitted; with --info-only the regular output is suppressed.";
"Note that this function may even be used on a locked card.";
static gpg_error_t static gpg_error_t
cmd_readkey (assuan_context_t ctx, char *line) cmd_readkey (assuan_context_t ctx, char *line)
{ {
ctrl_t ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
int advanced = 0; int advanced = 0;
int opt_info = 0;
int opt_nokey = 0;
unsigned char *cert = NULL; unsigned char *cert = NULL;
unsigned char *pk = NULL; unsigned char *pk = NULL;
size_t ncert, pklen; size_t ncert, pklen;
@ -524,6 +525,10 @@ cmd_readkey (assuan_context_t ctx, char *line)
if (has_option (line, "--advanced")) if (has_option (line, "--advanced"))
advanced = 1; advanced = 1;
if (has_option (line, "--info"))
opt_info = 1;
if (has_option (line, "--info-only"))
opt_info = opt_nokey = 1;
line = skip_options (line); line = skip_options (line);
line = xstrdup (line); /* Need a copy of the line. */ line = xstrdup (line); /* Need a copy of the line. */
@ -531,7 +536,9 @@ cmd_readkey (assuan_context_t ctx, char *line)
/* If the application supports the READKEY function we use that. /* If the application supports the READKEY function we use that.
Otherwise we use the old way by extracting it from the Otherwise we use the old way by extracting it from the
certificate. */ certificate. */
rc = app_readkey (ctrl->app_ctx, ctrl, line, &pk, &pklen); rc = app_readkey (ctrl->app_ctx, ctrl, line,
opt_info? APP_READKEY_FLAG_INFO : 0,
opt_nokey? NULL : &pk, &pklen);
if (!rc) if (!rc)
; /* Okay, got that key. */ ; /* Okay, got that key. */
else if (gpg_err_code (rc) == GPG_ERR_UNSUPPORTED_OPERATION else if (gpg_err_code (rc) == GPG_ERR_UNSUPPORTED_OPERATION
@ -551,6 +558,26 @@ cmd_readkey (assuan_context_t ctx, char *line)
gpg_strerror (rc)); gpg_strerror (rc));
goto leave; goto leave;
} }
if (opt_info)
{
char keygripstr[KEYGRIP_LEN*2+1];
rc = app_help_get_keygrip_string_pk (pk, pklen, keygripstr);
if (rc)
{
log_error ("app_help_get_keygrip_string failed: %s\n",
gpg_strerror (rc));
goto leave;
}
/* FIXME: Using LINE is not correct because it might be an
* OID and has not been canonicalized (i.e. uppercased). */
send_status_info (ctrl, "KEYPAIRINFO",
keygripstr, strlen (keygripstr),
line, strlen (line),
NULL, (size_t)0);
}
} }
else else
{ {
@ -558,7 +585,9 @@ cmd_readkey (assuan_context_t ctx, char *line)
goto leave; goto leave;
} }
if (advanced) if (opt_nokey)
;
else if (advanced)
{ {
gcry_sexp_t s_key; gcry_sexp_t s_key;
unsigned char *pkadv; unsigned char *pkadv;