scd:p15: Support attribute KEY-FPR.

* scd/app-p15.c: Include openpgpdefs.h.
(struct prkdf_object_s): Add fields have_keytime and ecdh_kdf.
(read_p15_info): Set ecdh_kdf.
(keygrip_from_prkdf): Flag that we have the keytime.
(send_keypairinfo): Send the key time only if valid.
(send_key_fpr_line): New.
(send_key_fpr): New.
(do_getattr): Add KEY-FPR.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2021-04-16 12:46:05 +02:00
parent 2f2bdd9c08
commit 30f90fc857
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
2 changed files with 173 additions and 5 deletions

View File

@ -1599,6 +1599,7 @@ Status codes are:
1.3.6.1.4.1.11591.2.2 X.509 extensions 1.3.6.1.4.1.11591.2.2 X.509 extensions
1.3.6.1.4.1.11591.2.2.1 standaloneCertificate 1.3.6.1.4.1.11591.2.2.1 standaloneCertificate
1.3.6.1.4.1.11591.2.2.2 wellKnownPrivateKey 1.3.6.1.4.1.11591.2.2.2 wellKnownPrivateKey
1.3.6.1.4.1.11591.2.2.10 OpenPGP KDF/KEK parameter
1.3.6.1.4.1.11591.2.3 CMS contentType 1.3.6.1.4.1.11591.2.3 CMS contentType
1.3.6.1.4.1.11591.2.3.1 OpenPGP keyblock (as octet string) 1.3.6.1.4.1.11591.2.3.1 OpenPGP keyblock (as octet string)
1.3.6.1.4.1.11591.2.4 LDAP stuff 1.3.6.1.4.1.11591.2.4 LDAP stuff
@ -1617,6 +1618,8 @@ Status codes are:
1.3.6.1.4.1.11591.2.12242973 invalid encoded OID 1.3.6.1.4.1.11591.2.12242973 invalid encoded OID
#+end_example #+end_example
The OpenPGP KDF/KEK parameter extension is used to convey additional
info for OpenPGP keys as an X.509 extensions.
* Debug flags * Debug flags

View File

@ -42,6 +42,7 @@
#include "../common/i18n.h" #include "../common/i18n.h"
#include "../common/tlv.h" #include "../common/tlv.h"
#include "../common/host2net.h" #include "../common/host2net.h"
#include "../common/openpgpdefs.h"
#include "apdu.h" /* fixme: we should move the card detection to a #include "apdu.h" /* fixme: we should move the card detection to a
separate file */ separate file */
@ -254,6 +255,7 @@ struct prkdf_object_s
unsigned int keygrip_valid:1; unsigned int keygrip_valid:1;
unsigned int key_reference_valid:1; unsigned int key_reference_valid:1;
unsigned int have_off:1; unsigned int have_off:1;
unsigned int have_keytime:1;
/* Flag indicating that the corresponding PIN has already been /* Flag indicating that the corresponding PIN has already been
* verified. Note that for cards which are able to return the * verified. Note that for cards which are able to return the
@ -310,6 +312,10 @@ struct prkdf_object_s
* certificate or NULL if not known. */ * certificate or NULL if not known. */
char *serial_number; char *serial_number;
/* KDF/KEK parameter for OpenPGP's ECDH. First byte is zero if not
* availabale. .*/
unsigned char ecdh_kdf[4];
/* Length and allocated buffer with the Id of this object. */ /* Length and allocated buffer with the Id of this object. */
size_t objidlen; size_t objidlen;
unsigned char *objid; unsigned char *objid;
@ -635,7 +641,6 @@ do_deinit (app_t app)
} }
/* Do a select and a read for the file with EFID. EFID_DESC is a /* Do a select and a read for the file with EFID. EFID_DESC is a
desctription of the EF to be used with error messages. On success desctription of the EF to be used with error messages. On success
BUFFER and BUFLEN contain the entire content of the EF. The caller BUFFER and BUFLEN contain the entire content of the EF. The caller
@ -3557,8 +3562,7 @@ read_p15_info (app_t app)
cdf_object_t cdf; cdf_object_t cdf;
char *extusage; char *extusage;
char *p, *pend; char *p, *pend;
int seen; int seen, i;
gpg_error_t errx;
if (opt.debug) if (opt.debug)
log_printhex (prkdf->objid, prkdf->objidlen, "p15: prkdf id="); log_printhex (prkdf->objid, prkdf->objidlen, "p15: prkdf id=");
@ -3570,7 +3574,38 @@ read_p15_info (app_t app)
if (!cdf->cert) if (!cdf->cert)
continue; /* Unsupported or broken certificate. */ continue; /* Unsupported or broken certificate. */
if ((errx=ksba_cert_get_ext_key_usages (cdf->cert, &extusage))) if (prkdf->is_ecc)
{
const char *oid;
const unsigned char *der;
size_t off, derlen, objlen, hdrlen;
int class, tag, constructed, ndef;
for (i=0; !(err = ksba_cert_get_extension
(cdf->cert, i, &oid, NULL, &off, &derlen)); i++)
if (!strcmp (oid, "1.3.6.1.4.1.11591.2.2.10") )
break;
if (!err && (der = ksba_cert_get_image (cdf->cert, NULL)))
{
der += off;
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_OCTET_STRING || ndef))
err = gpg_error (GPG_ERR_INV_OBJ);
if (!err)
{
derlen = objlen;
if (opt.debug)
log_printhex (der, derlen, "p15: OpenPGP KDF parms:");
/* Store them if they match the known OpenPGP format. */
if (derlen == 4 && der[0] == 3 && der[1] == 1)
memcpy (prkdf->ecdh_kdf, der, 4);
}
}
err = 0;
}
if (ksba_cert_get_ext_key_usages (cdf->cert, &extusage))
continue; /* No extended key usage attribute. */ continue; /* No extended key usage attribute. */
if (opt.debug) if (opt.debug)
@ -3933,6 +3968,7 @@ keygrip_from_prkdf (app_t app, prkdf_object_t prkdf)
ksba_cert_get_validity (cert, 0, isot); ksba_cert_get_validity (cert, 0, isot);
t = isotime2epoch (isot); t = isotime2epoch (isot);
prkdf->keytime = (t == (time_t)(-1))? 0 : (u32)t; prkdf->keytime = (t == (time_t)(-1))? 0 : (u32)t;
prkdf->have_keytime = 1;
} }
if (!err && !prkdf->keyalgostr) if (!err && !prkdf->keyalgostr)
@ -4053,7 +4089,7 @@ send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t prkdf)
} }
log_assert (strlen (prkdf->keygrip) == 40); log_assert (strlen (prkdf->keygrip) == 40);
if (prkdf->keytime) if (prkdf->keytime && prkdf->have_keytime)
snprintf (keytime, sizeof keytime, "%lu", snprintf (keytime, sizeof keytime, "%lu",
(unsigned long)prkdf->keytime); (unsigned long)prkdf->keytime);
else else
@ -4320,6 +4356,98 @@ compare_aodf_objid (const void *arg_a, const void *arg_b)
} }
static void
send_key_fpr_line (ctrl_t ctrl, int number, const unsigned char *fpr)
{
char buf[41];
char numbuf[25];
bin2hex (fpr, 20, buf);
if (number == -1)
*numbuf = 0; /* Don't print the key number */
else
snprintf (numbuf, sizeof numbuf, "%d", number);
send_status_info (ctrl, "KEY-FPR",
numbuf, (size_t)strlen(numbuf),
buf, (size_t)strlen (buf),
NULL, 0);
}
/* If possible Emit a FPR-KEY status line for the private key object
* PRKDF using NUMBER as index. */
static void
send_key_fpr (app_t app, ctrl_t ctrl, prkdf_object_t prkdf, int number)
{
gpg_error_t err;
cdf_object_t cdf;
unsigned char *pk, *fixed_pk;
size_t pklen, fixed_pklen;
const unsigned char *m, *e, *q;
size_t mlen, elen, qlen;
unsigned char fpr20[20];
if (cdf_object_from_objid (app, prkdf->objidlen, prkdf->objid, &cdf)
&& cdf_object_from_label (app, prkdf->label, &cdf))
return;
if (!cdf->cert)
readcert_by_cdf (app, cdf, NULL, NULL);
if (!cdf->cert)
return;
if (!prkdf->have_keytime)
return;
pk = ksba_cert_get_public_key (cdf->cert);
if (!pk)
return;
pklen = gcry_sexp_canon_len (pk, 0, NULL, &err);
if (uncompress_ecc_q_in_canon_sexp (pk, pklen, &fixed_pk, &fixed_pklen))
{
xfree (pk);
return;
}
if (fixed_pk)
{
xfree (pk); pk = NULL;
pk = fixed_pk;
pklen = fixed_pklen;
}
switch (prkdf->keyalgo)
{
case GCRY_PK_RSA:
if (!get_rsa_pk_from_canon_sexp (pk, pklen,
&m, &mlen, &e, &elen)
&& !compute_openpgp_fpr_rsa (4,
prkdf->keytime,
m, mlen, e, elen,
fpr20, NULL))
send_key_fpr_line (ctrl, number, fpr20);
break;
case GCRY_PK_ECC:
case GCRY_PK_ECDSA:
case GCRY_PK_ECDH:
case GCRY_PK_EDDSA:
/* Note that NUMBER 2 indicates the encryption key. */
if (!get_ecc_q_from_canon_sexp (pk, pklen, &q, &qlen)
&& !compute_openpgp_fpr_ecc (4,
prkdf->keytime,
prkdf->keyalgostr,
number == 2,
q, qlen,
prkdf->ecdh_kdf, 4,
fpr20, NULL))
send_key_fpr_line (ctrl, number, fpr20);
break;
default: /* No Fingerprint for an unknown algo. */
break;
}
xfree (pk);
}
/* Implement the GETATTR command. This is similar to the LEARN /* Implement the GETATTR command. This is similar to the LEARN
command but returns just one value via the status interface. */ command but returns just one value via the status interface. */
@ -4530,6 +4658,43 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
} }
return 0; return 0;
} }
else if (!strcmp (name, "KEY-FPR"))
{
/* Send KEY-FPR for the two openpgp keys. */
for (prkdf = app->app_local->private_key_info; prkdf;
prkdf = prkdf->next)
{
if (app->app_local->any_gpgusage)
{
if (prkdf->gpgusage.sign)
break;
}
else
{
if (prkdf->usageflags.sign || prkdf->usageflags.sign_recover)
break;
}
}
if (prkdf)
send_key_fpr (app, ctrl, prkdf, 1);
for (prkdf = app->app_local->private_key_info; prkdf;
prkdf = prkdf->next)
{
if (app->app_local->any_gpgusage)
{
if (prkdf->gpgusage.encr)
break;
}
else
{
if (prkdf->usageflags.decrypt || prkdf->usageflags.unwrap)
break;
}
}
if (prkdf)
send_key_fpr (app, ctrl, prkdf, 2);
return 0;
}
return gpg_error (GPG_ERR_INV_NAME); return gpg_error (GPG_ERR_INV_NAME);
} }