gpg: Add experimental AKL method "wkd" and option --with-wkd-hash.

* g10/getkey.c (parse_auto_key_locate): Add method "wkd".
(get_pubkey_byname): Implement that method.  Also rename a variable.
* g10/call-dirmngr.c (gpg_dirmngr_wkd_get): New.
* g10/keyserver.c (keyserver_import_wkd): New.
* g10/test-stubs.c (keyserver_import_wkd): Add stub.
* g10/gpgv.c (keyserver_import_wkd): Ditto.
* g10/options.h (opt):  Add field 'with_wkd_hash'.
(AKL_WKD): New.

* g10/gpg.c (oWithWKDHash): New.
(opts): Add option --with-wkd-hash.
(main): Set that option.
* g10/keylist.c (list_keyblock_print): Implement that option.
--

The Web Key Directory is an experimental feature to retrieve a key via
https.  It is similar to OpenPGP DANE but also uses an encryption to
reveal less information about a key lookup.

For example the URI to lookup the key for Joe.Doe@Example.ORG is:

    https://example.org/.well-known/openpgpkey/
    hu/example.org/iy9q119eutrkn8s1mk4r39qejnbu3n5q

(line has been wrapped for rendering purposes).  The hash is a
z-Base-32 encoded SHA-1 hash of the mail address' local-part.  The
address wk@gnupg.org can be used for testing.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2016-04-27 08:34:29 +02:00
parent c83c6f212e
commit 87de9e19ed
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
12 changed files with 187 additions and 22 deletions

View File

@ -1600,6 +1600,10 @@ mechanisms, in the order they are to be tried:
Locate a key using DANE, as specified Locate a key using DANE, as specified
in draft-ietf-dane-openpgpkey-05.txt. in draft-ietf-dane-openpgpkey-05.txt.
@item wkd
Locate a key using the Web Key Directory protocol.
This is an experimental method and semantics may change.
@item ldap @item ldap
Using DNS Service Discovery, check the domain in question for any LDAP Using DNS Service Discovery, check the domain in question for any LDAP
keyservers to use. If this fails, attempt to locate the key using the keyservers to use. If this fails, attempt to locate the key using the
@ -2235,6 +2239,11 @@ Print the ICAO spelling of the fingerprint in addition to the hex digits.
@opindex with-keygrip @opindex with-keygrip
Include the keygrip in the key listings. Include the keygrip in the key listings.
@item --with-wkd-hash
@opindex with-wkd-hash
Print a Web Key Directory indentifier along with each user ID in key
listings. This is an experimental feature and semantics may change.
@item --with-secret @item --with-secret
@opindex with-secret @opindex with-secret
Include info about the presence of a secret key in public key listings Include info about the presence of a secret key in public key listings

View File

@ -1064,7 +1064,7 @@ gpg_dirmngr_ks_put (ctrl_t ctrl, void *data, size_t datalen, kbnode_t keyblock)
/* Data callback for the DNS_CERT command. */ /* Data callback for the DNS_CERT and WKD_GET commands. */
static gpg_error_t static gpg_error_t
dns_cert_data_cb (void *opaque, const void *data, size_t datalen) dns_cert_data_cb (void *opaque, const void *data, size_t datalen)
{ {
@ -1287,3 +1287,62 @@ gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid,
close_context (ctrl, ctx); close_context (ctrl, ctx);
return err; return err;
} }
/* Ask the dirmngr to retrieve a key via the Web Key Directory
* protocol. On success a new estream with the key is stored at
* R_KEY.
*/
gpg_error_t
gpg_dirmngr_wkd_get (ctrl_t ctrl, const char *name, estream_t *r_key)
{
gpg_error_t err;
assuan_context_t ctx;
struct dns_cert_parm_s parm;
char *line = NULL;
memset (&parm, 0, sizeof parm);
err = open_context (ctrl, &ctx);
if (err)
return err;
line = es_bsprintf ("WKD_GET -- %s", name);
if (!line)
{
err = gpg_error_from_syserror ();
goto leave;
}
if (strlen (line) + 2 >= ASSUAN_LINELENGTH)
{
err = gpg_error (GPG_ERR_TOO_LARGE);
goto leave;
}
parm.memfp = es_fopenmem (0, "rwb");
if (!parm.memfp)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = assuan_transact (ctx, line, dns_cert_data_cb, &parm,
NULL, NULL, NULL, &parm);
if (err)
goto leave;
if (r_key)
{
es_rewind (parm.memfp);
*r_key = parm.memfp;
parm.memfp = NULL;
}
leave:
xfree (parm.fpr);
xfree (parm.url);
es_fclose (parm.memfp);
xfree (line);
close_context (ctrl, ctx);
return err;
}

View File

@ -40,6 +40,8 @@ gpg_error_t gpg_dirmngr_dns_cert (ctrl_t ctrl,
gpg_error_t gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid, gpg_error_t gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid,
unsigned char **r_fpr, size_t *r_fprlen, unsigned char **r_fpr, size_t *r_fprlen,
char **r_url); char **r_url);
gpg_error_t gpg_dirmngr_wkd_get (ctrl_t ctrl, const char *name,
estream_t *r_key);
#endif /*GNUPG_G10_CALL_DIRMNGR_H*/ #endif /*GNUPG_G10_CALL_DIRMNGR_H*/

View File

@ -1274,7 +1274,7 @@ get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk,
{ {
unsigned char *fpr = NULL; unsigned char *fpr = NULL;
size_t fpr_len; size_t fpr_len;
int did_key_byname = 0; int did_akl_local = 0;
int no_fingerprint = 0; int no_fingerprint = 0;
const char *mechanism = "?"; const char *mechanism = "?";
@ -1288,7 +1288,7 @@ get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk,
case AKL_LOCAL: case AKL_LOCAL:
mechanism = "Local"; mechanism = "Local";
did_key_byname = 1; did_akl_local = 1;
if (retctx) if (retctx)
{ {
getkey_end (*retctx); getkey_end (*retctx);
@ -1321,6 +1321,13 @@ get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk,
glo_ctrl.in_auto_key_retrieve--; glo_ctrl.in_auto_key_retrieve--;
break; break;
case AKL_WKD:
mechanism = "WKD";
glo_ctrl.in_auto_key_retrieve++;
rc = keyserver_import_wkd (ctrl, name, &fpr, &fpr_len);
glo_ctrl.in_auto_key_retrieve--;
break;
case AKL_LDAP: case AKL_LDAP:
mechanism = "LDAP"; mechanism = "LDAP";
glo_ctrl.in_auto_key_retrieve++; glo_ctrl.in_auto_key_retrieve++;
@ -1386,22 +1393,20 @@ get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk,
add_to_strlist (&namelist, fpr_string); add_to_strlist (&namelist, fpr_string);
} }
else if (!rc && !fpr && !did_key_byname) else if (!rc && !fpr && !did_akl_local)
/* The acquisition method said no failure occurred, but it { /* The acquisition method said no failure occurred, but
didn't return a fingerprint. That's a failure. */ it didn't return a fingerprint. That's a failure. */
{ no_fingerprint = 1;
no_fingerprint = 1;
rc = GPG_ERR_NO_PUBKEY; rc = GPG_ERR_NO_PUBKEY;
} }
xfree (fpr); xfree (fpr);
fpr = NULL; fpr = NULL;
if (!rc && !did_key_byname) if (!rc && !did_akl_local)
/* There was no error and we didn't do a local lookup. { /* There was no error and we didn't do a local lookup.
This means that we imported a key into the local This means that we imported a key into the local
keyring. Try to read the imported key from the keyring. Try to read the imported key from the
keyring. */ keyring. */
{
if (retctx) if (retctx)
{ {
getkey_end (*retctx); getkey_end (*retctx);
@ -3195,6 +3200,7 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock)
if (DBG_LOOKUP) if (DBG_LOOKUP)
log_debug ("\tsubkey has expired\n"); log_debug ("\tsubkey has expired\n");
continue; continue;
} }
if (pk->timestamp > curtime && !opt.ignore_valid_from) if (pk->timestamp > curtime && !opt.ignore_valid_from)
{ {
@ -3769,6 +3775,8 @@ parse_auto_key_locate (char *options)
akl->type = AKL_PKA; akl->type = AKL_PKA;
else if (ascii_strcasecmp (tok, "dane") == 0) else if (ascii_strcasecmp (tok, "dane") == 0)
akl->type = AKL_DANE; akl->type = AKL_DANE;
else if (ascii_strcasecmp (tok, "wkd") == 0)
akl->type = AKL_WKD;
else if ((akl->spec = parse_keyserver_uri (tok, 1))) else if ((akl->spec = parse_keyserver_uri (tok, 1)))
akl->type = AKL_SPEC; akl->type = AKL_SPEC;
else else

View File

@ -185,6 +185,7 @@ enum cmd_and_opt_values
oWithICAOSpelling, oWithICAOSpelling,
oWithKeygrip, oWithKeygrip,
oWithSecret, oWithSecret,
oWithWKDHash,
oAnswerYes, oAnswerYes,
oAnswerNo, oAnswerNo,
oKeyring, oKeyring,
@ -721,6 +722,7 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_s_n (oWithICAOSpelling, "with-icao-spelling", "@"), ARGPARSE_s_n (oWithICAOSpelling, "with-icao-spelling", "@"),
ARGPARSE_s_n (oWithKeygrip, "with-keygrip", "@"), ARGPARSE_s_n (oWithKeygrip, "with-keygrip", "@"),
ARGPARSE_s_n (oWithSecret, "with-secret", "@"), ARGPARSE_s_n (oWithSecret, "with-secret", "@"),
ARGPARSE_s_n (oWithWKDHash, "with-wkd-hash", "@"),
ARGPARSE_s_s (oDisableCipherAlgo, "disable-cipher-algo", "@"), ARGPARSE_s_s (oDisableCipherAlgo, "disable-cipher-algo", "@"),
ARGPARSE_s_s (oDisablePubkeyAlgo, "disable-pubkey-algo", "@"), ARGPARSE_s_s (oDisablePubkeyAlgo, "disable-pubkey-algo", "@"),
ARGPARSE_s_n (oAllowNonSelfsignedUID, "allow-non-selfsigned-uid", "@"), ARGPARSE_s_n (oAllowNonSelfsignedUID, "allow-non-selfsigned-uid", "@"),
@ -2575,6 +2577,10 @@ main (int argc, char **argv)
opt.with_secret = 1; opt.with_secret = 1;
break; break;
case oWithWKDHash:
opt.with_wkd_hash = 1;
break;
case oSecretKeyring: case oSecretKeyring:
/* Ignore this old option. */ /* Ignore this old option. */
break; break;

View File

@ -374,6 +374,17 @@ keyserver_import_pka (const char *name,unsigned char *fpr)
return -1; return -1;
} }
gpg_error_t
keyserver_import_wkd (ctrl_t ctrl, const char *name,
unsigned char **fpr, size_t *fpr_len)
{
(void)ctrl;
(void)name;
(void)fpr;
(void)fpr_len;
return GPG_ERR_BUG;
}
int int
keyserver_import_name (const char *name,struct keyserver_spec *spec) keyserver_import_name (const char *name,struct keyserver_spec *spec)
{ {

View File

@ -1279,7 +1279,7 @@ import_one (ctrl_t ctrl,
{ {
xfree (*fpr); xfree (*fpr);
/* Note that we need to compare against 0 here because /* Note that we need to compare against 0 here because
COUNT gets only incremented after returning form this COUNT gets only incremented after returning from this
function. */ function. */
if (!stats->count) if (!stats->count)
*fpr = fingerprint_from_pk (pk, NULL, fpr_len); *fpr = fingerprint_from_pk (pk, NULL, fpr_len);

View File

@ -1116,6 +1116,7 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
if (node->pkt->pkttype == PKT_USER_ID) if (node->pkt->pkttype == PKT_USER_ID)
{ {
PKT_user_id *uid = node->pkt->pkt.user_id; PKT_user_id *uid = node->pkt->pkt.user_id;
int indent;
if ((uid->is_expired || uid->is_revoked) if ((uid->is_expired || uid->is_revoked)
&& !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS)) && !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS))
@ -1133,25 +1134,46 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
|| (opt.list_options & LIST_SHOW_UID_VALIDITY)) || (opt.list_options & LIST_SHOW_UID_VALIDITY))
{ {
const char *validity; const char *validity;
int indent;
validity = uid_trust_string_fixed (pk, uid); validity = uid_trust_string_fixed (pk, uid);
indent = indent = ((keystrlen () + (opt.legacy_list_mode? 9:11))
(keystrlen () + (opt.legacy_list_mode? 9:11)) - - atoi (uid_trust_string_fixed (NULL, NULL)));
atoi (uid_trust_string_fixed (NULL, NULL));
if (indent < 0 || indent > 40) if (indent < 0 || indent > 40)
indent = 0; indent = 0;
es_fprintf (es_stdout, "uid%*s%s ", indent, "", validity); es_fprintf (es_stdout, "uid%*s%s ", indent, "", validity);
} }
else else
es_fprintf (es_stdout, "uid%*s", {
(int) keystrlen () + (opt.legacy_list_mode? 10:12), ""); indent = keystrlen () + (opt.legacy_list_mode? 10:12);
es_fprintf (es_stdout, "uid%*s", indent, "");
}
print_utf8_buffer (es_stdout, uid->name, uid->len); print_utf8_buffer (es_stdout, uid->name, uid->len);
es_putc ('\n', es_stdout); es_putc ('\n', es_stdout);
if (opt.with_wkd_hash)
{
char *mbox, *hash, *p;
char hashbuf[32];
mbox = mailbox_from_userid (uid->name);
if (mbox && (p = strchr (mbox, '@')))
{
*p++ = 0;
gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf,
mbox, strlen (mbox));
hash = zb32_encode (hashbuf, 8*20);
if (hash)
{
es_fprintf (es_stdout, " %*s%s@%s\n",
indent, "", hash, p);
xfree (hash);
}
}
xfree (mbox);
}
if ((opt.list_options & LIST_SHOW_PHOTOS) && uid->attribs != NULL) if ((opt.list_options & LIST_SHOW_PHOTOS) && uid->attribs != NULL)
show_photos (uid->attribs, uid->numattribs, pk, uid); show_photos (uid->attribs, uid->numattribs, pk, uid);
} }

View File

@ -45,6 +45,8 @@ int keyserver_import_cert (ctrl_t ctrl, const char *name, int dane_mode,
unsigned char **fpr,size_t *fpr_len); unsigned char **fpr,size_t *fpr_len);
gpg_error_t keyserver_import_pka (ctrl_t ctrl, const char *name, gpg_error_t keyserver_import_pka (ctrl_t ctrl, const char *name,
unsigned char **fpr,size_t *fpr_len); unsigned char **fpr,size_t *fpr_len);
gpg_error_t keyserver_import_wkd (ctrl_t ctrl, const char *name,
unsigned char **fpr, size_t *fpr_len);
int keyserver_import_name (ctrl_t ctrl, int keyserver_import_name (ctrl_t ctrl,
const char *name,unsigned char **fpr,size_t *fpr_len, const char *name,unsigned char **fpr,size_t *fpr_len,
struct keyserver_spec *keyserver); struct keyserver_spec *keyserver);

View File

@ -2004,6 +2004,39 @@ keyserver_import_pka (ctrl_t ctrl, const char *name,
} }
/* Import a key using the Web Key Directory protocol. */
gpg_error_t
keyserver_import_wkd (ctrl_t ctrl, const char *name,
unsigned char **fpr, size_t *fpr_len)
{
gpg_error_t err;
estream_t key;
err = gpg_dirmngr_wkd_get (ctrl, name, &key);
if (err)
;
else if (key)
{
int armor_status = opt.no_armor;
/* Keys returned via WKD are in binary format. */
opt.no_armor = 1;
err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len,
(opt.keyserver_options.import_options
| IMPORT_NO_SECKEY),
NULL, NULL);
opt.no_armor = armor_status;
es_fclose (key);
key = NULL;
}
return err;
}
/* Import a key by name using LDAP */ /* Import a key by name using LDAP */
int int
keyserver_import_ldap (ctrl_t ctrl, keyserver_import_ldap (ctrl_t ctrl,

View File

@ -73,6 +73,7 @@ struct
int with_fingerprint; /* Option --with-fingerprint active. */ int with_fingerprint; /* Option --with-fingerprint active. */
int with_keygrip; /* Option --with-keygrip active. */ int with_keygrip; /* Option --with-keygrip active. */
int with_secret; /* Option --with-secret active. */ int with_secret; /* Option --with-secret active. */
int with_wkd_hash; /* Option --with-wkd-hash. */
int fingerprint; /* list fingerprints */ int fingerprint; /* list fingerprints */
int list_sigs; /* list signatures */ int list_sigs; /* list signatures */
int print_pka_records; int print_pka_records;
@ -245,6 +246,7 @@ struct
AKL_CERT, AKL_CERT,
AKL_PKA, AKL_PKA,
AKL_DANE, AKL_DANE,
AKL_WKD,
AKL_LDAP, AKL_LDAP,
AKL_KEYSERVER, AKL_KEYSERVER,
AKL_SPEC AKL_SPEC

View File

@ -186,6 +186,17 @@ keyserver_import_pka (const char *name,unsigned char *fpr)
return -1; return -1;
} }
gpg_error_t
keyserver_import_wkd (ctrl_t ctrl, const char *name,
unsigned char **fpr, size_t *fpr_len)
{
(void)ctrl;
(void)name;
(void)fpr;
(void)fpr_len;
return GPG_ERR_BUG;
}
int int
keyserver_import_name (const char *name,struct keyserver_spec *spec) keyserver_import_name (const char *name,struct keyserver_spec *spec)
{ {