gpg: Implement searching keys via keygrip.

* kbx/keybox-defs.h (struct _keybox_openpgp_key_info): Add field grip.
* kbx/keybox-openpgp.c (struct keyparm_s): New.
(keygrip_from_keyparm): New.
(parse_key): Compute keygrip.
* kbx/keybox-search.c (blob_openpgp_has_grip): New.
(has_keygrip): Call it.
--

This has been marked for too long as not yet working.  However, it is
a pretty useful feature and will come pretty handy when looking for
all keys matching one keygrip.

Can be optimized a lot by storing the keygrip in the meta data.  This
will be done along with the upgrade of KBX for v5 fingerprints.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2019-01-29 19:52:08 +01:00
parent f382984966
commit c128667b3c
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 204 additions and 24 deletions

View File

@ -135,7 +135,7 @@ RFC-2253 encoded DN of the issuer. See note above.
@item By keygrip.
This is indicated by an ampersand followed by the 40 hex digits of a
keygrip. @command{gpgsm} prints the keygrip when using the command
@option{--dump-cert}. It does not yet work for OpenPGP keys.
@option{--dump-cert}.
@cartouche
@example
@ -171,6 +171,3 @@ Using the RFC-2253 format of DNs has the drawback that it is not
possible to map them back to the original encoding, however we don't
have to do this because our key database stores this encoding as meta
data.

View File

@ -329,6 +329,18 @@ dump_fpr (const unsigned char *buffer, size_t len)
}
static void
dump_grip (const unsigned char *buffer, size_t len)
{
int i;
for (i=0; i < len; i++, buffer++)
{
printf ("%02X", buffer[0]);
}
}
static void
dump_openpgp_key (keybox_openpgp_info_t info, const unsigned char *image)
{
@ -338,6 +350,9 @@ dump_openpgp_key (keybox_openpgp_info_t info, const unsigned char *image)
info->primary.keyid[6], info->primary.keyid[7] );
dump_fpr (info->primary.fpr, info->primary.fprlen);
putchar ('\n');
fputs ("grp ", stdout);
dump_grip (info->primary.grip, 20);
putchar ('\n');
if (info->nsubkeys)
{
struct _keybox_openpgp_key_info *k;
@ -351,6 +366,9 @@ dump_openpgp_key (keybox_openpgp_info_t info, const unsigned char *image)
k->keyid[6], k->keyid[7] );
dump_fpr (k->fpr, k->fprlen);
putchar ('\n');
fputs ("grp ", stdout);
dump_grip (k->grip, 20);
putchar ('\n');
k = k->next;
}
while (k);

View File

@ -94,11 +94,12 @@ struct keybox_handle {
};
/* Openpgp helper structures. */
/* OpenPGP helper structures. */
struct _keybox_openpgp_key_info
{
struct _keybox_openpgp_key_info *next;
int algo;
unsigned char grip[20];
unsigned char keyid[8];
int fprlen; /* Either 16 or 20 */
unsigned char fpr[20];

View File

@ -38,6 +38,13 @@
#include "../common/openpgpdefs.h"
#include "../common/host2net.h"
struct keyparm_s
{
const char *mpi;
int len; /* int to avoid a cast in gcry_sexp_build. */
};
/* Assume a valid OpenPGP packet at the address pointed to by BUFBTR
which has a maximum length as stored at BUFLEN. Return the header
information of that packet and advance the pointer stored at BUFPTR
@ -165,6 +172,86 @@ next_packet (unsigned char const **bufptr, size_t *buflen,
}
/* Take a list of key parameters KP for the OpenPGP ALGO and compute
* the keygrip which will be stored at GRIP. GRIP needs to be a
* buffer of 20 bytes. */
static gpg_error_t
keygrip_from_keyparm (int algo, struct keyparm_s *kp, unsigned char *grip)
{
gpg_error_t err;
gcry_sexp_t s_pkey = NULL;
switch (algo)
{
case PUBKEY_ALGO_DSA:
err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(dsa(p%b)(q%b)(g%b)(y%b)))",
kp[0].len, kp[0].mpi,
kp[1].len, kp[1].mpi,
kp[2].len, kp[2].mpi,
kp[3].len, kp[3].mpi);
break;
case PUBKEY_ALGO_ELGAMAL:
case PUBKEY_ALGO_ELGAMAL_E:
err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(elg(p%b)(g%b)(y%b)))",
kp[0].len, kp[0].mpi,
kp[1].len, kp[1].mpi,
kp[2].len, kp[2].mpi);
break;
case PUBKEY_ALGO_RSA:
case PUBKEY_ALGO_RSA_S:
case PUBKEY_ALGO_RSA_E:
err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(rsa(n%b)(e%b)))",
kp[0].len, kp[0].mpi,
kp[1].len, kp[1].mpi);
break;
case PUBKEY_ALGO_EDDSA:
case PUBKEY_ALGO_ECDSA:
case PUBKEY_ALGO_ECDH:
{
char *curve = openpgp_oidbuf_to_str (kp[0].mpi, kp[0].len);
if (!curve)
err = gpg_error_from_syserror ();
else
{
err = gcry_sexp_build
(&s_pkey, NULL,
(algo == PUBKEY_ALGO_EDDSA)?
"(public-key(ecc(curve%s)(flags eddsa)(q%b)))":
(algo == PUBKEY_ALGO_ECDH
&& openpgp_oidbuf_is_cv25519 (kp[0].mpi, kp[0].len))?
"(public-key(ecc(curve%s)(flags djb-tweak)(q%b)))":
"(public-key(ecc(curve%s)(q%b)))",
curve, kp[1].len, kp[1].mpi);
xfree (curve);
}
}
break;
default:
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
break;
}
if (!err && !gcry_pk_get_keygrip (s_pkey, grip))
{
log_info ("kbx: error computing keygrip\n");
err = gpg_error (GPG_ERR_GENERAL);
}
gcry_sexp_release (s_pkey);
if (err)
memset (grip, 0, 20);
return err;
}
/* Parse a key packet and store the information in KI. */
static gpg_error_t
parse_key (const unsigned char *data, size_t datalen,
@ -176,10 +263,10 @@ parse_key (const unsigned char *data, size_t datalen,
size_t n;
int npkey;
unsigned char hashbuffer[768];
const unsigned char *mpi_n = NULL;
size_t mpi_n_len = 0, mpi_e_len = 0;
gcry_md_hd_t md;
int is_ecc = 0;
struct keyparm_s keyparm[OPENPGP_MAX_NPKEY];
unsigned char *helpmpibuf[OPENPGP_MAX_NPKEY] = { NULL };
if (datalen < 5)
return gpg_error (GPG_ERR_INV_PACKET);
@ -245,6 +332,9 @@ parse_key (const unsigned char *data, size_t datalen,
nbytes++; /* The size byte itself. */
if (datalen < nbytes)
return gpg_error (GPG_ERR_INV_PACKET);
keyparm[i].mpi = data;
keyparm[i].len = nbytes;
}
else
{
@ -254,21 +344,40 @@ parse_key (const unsigned char *data, size_t datalen,
nbytes = (nbits+7) / 8;
if (datalen < nbytes)
return gpg_error (GPG_ERR_INV_PACKET);
/* For use by v3 fingerprint calculation we need to know the RSA
modulus and exponent. */
if (i==0)
{
mpi_n = data;
mpi_n_len = nbytes;
}
else if (i==1)
mpi_e_len = nbytes;
keyparm[i].mpi = data;
keyparm[i].len = nbytes;
}
data += nbytes; datalen -= nbytes;
}
n = data - data_start;
/* Note: Starting here we need to jump to leave on error. */
/* Make sure the MPIs are unsigned. */
for (i=0; i < npkey; i++)
{
if (!keyparm[i].len || (keyparm[i].mpi[0] & 0x80))
{
helpmpibuf[i] = xtrymalloc (1+keyparm[i].len);
if (!helpmpibuf[i])
{
err = gpg_error_from_syserror ();
goto leave;
}
helpmpibuf[i][0] = 0;
memcpy (helpmpibuf[i]+1, keyparm[i].mpi, keyparm[i].len);
keyparm[i].mpi = helpmpibuf[i];
keyparm[i].len++;
}
}
err = keygrip_from_keyparm (algorithm, keyparm, ki->grip);
if (err)
goto leave;
if (version < 4)
{
/* We do not support any other algorithm than RSA in v3
@ -279,20 +388,20 @@ parse_key (const unsigned char *data, size_t datalen,
err = gcry_md_open (&md, GCRY_MD_MD5, 0);
if (err)
return err; /* Oops */
gcry_md_write (md, mpi_n, mpi_n_len);
gcry_md_write (md, mpi_n+mpi_n_len+2, mpi_e_len);
gcry_md_write (md, keyparm[0].mpi, keyparm[0].len);
gcry_md_write (md, keyparm[1].mpi, keyparm[1].len);
memcpy (ki->fpr, gcry_md_read (md, 0), 16);
gcry_md_close (md);
ki->fprlen = 16;
if (mpi_n_len < 8)
if (keyparm[0].len < 8)
{
/* Moduli less than 64 bit are out of the specs scope. Zero
them out because this is what gpg does too. */
memset (ki->keyid, 0, 8);
}
else
memcpy (ki->keyid, mpi_n + mpi_n_len - 8, 8);
memcpy (ki->keyid, keyparm[0].mpi + keyparm[0].len - 8, 8);
}
else
{
@ -327,7 +436,11 @@ parse_key (const unsigned char *data, size_t datalen,
memcpy (ki->keyid, ki->fpr+12, 8);
}
return 0;
leave:
for (i=0; i < npkey; i++)
xfree (helpmpibuf[i]);
return err;
}

View File

@ -497,6 +497,58 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr,
}
/* Return true if the key in BLOB matches the 20 bytes keygrip GRIP.
* We don't have the keygrips as meta data, thus we need to parse the
* certificate. Fixme: We might want to return proper error codes
* instead of failing a search for invalid certificates etc. */
static int
blob_openpgp_has_grip (KEYBOXBLOB blob, const unsigned char *grip)
{
int rc = 0;
const unsigned char *buffer;
size_t length;
size_t cert_off, cert_len;
struct _keybox_openpgp_info info;
struct _keybox_openpgp_key_info *k;
buffer = _keybox_get_blob_image (blob, &length);
if (length < 40)
return 0; /* Too short. */
cert_off = get32 (buffer+8);
cert_len = get32 (buffer+12);
if ((uint64_t)cert_off+(uint64_t)cert_len > (uint64_t)length)
return 0; /* Too short. */
if (_keybox_parse_openpgp (buffer + cert_off, cert_len, NULL, &info))
return 0; /* Parse error. */
if (!memcmp (info.primary.grip, grip, 20))
{
rc = 1;
goto leave;
}
if (info.nsubkeys)
{
k = &info.subkeys;
do
{
if (!memcmp (k->grip, grip, 20))
{
rc = 1;
goto leave;
}
k = k->next;
}
while (k);
}
leave:
_keybox_destroy_openpgp_info (&info);
return rc;
}
#ifdef KEYBOX_WITH_X509
/* Return true if the key in BLOB matches the 20 bytes keygrip GRIP.
We don't have the keygrips as meta data, thus we need to parse the
@ -606,12 +658,11 @@ has_fingerprint (KEYBOXBLOB blob, const unsigned char *fpr)
static inline int
has_keygrip (KEYBOXBLOB blob, const unsigned char *grip)
{
if (blob_get_type (blob) == KEYBOX_BLOBTYPE_PGP)
return blob_openpgp_has_grip (blob, grip);
#ifdef KEYBOX_WITH_X509
if (blob_get_type (blob) == KEYBOX_BLOBTYPE_X509)
return blob_x509_has_grip (blob, grip);
#else
(void)blob;
(void)grip;
#endif
return 0;
}