2 * keyindex.c - Routines to list an OpenPGP key.
4 * Copyright 2002-2008 Jonathan McDowell <noodles@earth.li>
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along with
16 * this program. If not, see <https://www.gnu.org/licenses/>.
26 #include "decodekey.h"
30 #include "keystructs.h"
36 * Convert a Public Key algorithm to its single character representation.
38 char pkalgo2char(uint8_t algo)
43 case OPENPGP_PKALGO_DSA:
46 case OPENPGP_PKALGO_ECDSA:
47 case OPENPGP_PKALGO_EDDSA:
50 case OPENPGP_PKALGO_EC:
53 case OPENPGP_PKALGO_ELGAMAL_SIGN:
56 case OPENPGP_PKALGO_ELGAMAL_ENC:
59 case OPENPGP_PKALGO_RSA:
62 case OPENPGP_PKALGO_RSA_ENC:
65 case OPENPGP_PKALGO_RSA_SIGN:
77 * html_escape - Takes a string and converts it to HTML.
78 * @src: The string to HTMLize.
79 * @src_len: The length of the source string
80 * @dst: A buffer to put the escaped string into
81 * @dst_len: Length of the destination buffer (including a trailing NULL)
83 * Takes a string and escapes any HTML entities (<, >, &, ", '). Returns
86 const char *html_escape(const char *src, size_t src_len,
87 char *dst, size_t dst_len)
89 size_t in_pos, out_pos;
93 for (in_pos = 0, out_pos = 0;
94 in_pos < src_len && out_pos < (dst_len - 1);
95 in_pos++, out_pos++) {
96 switch (src[in_pos]) {
98 if ((out_pos + 4) >= dst_len) {
101 dst[out_pos++] = '&';
102 dst[out_pos++] = 'l';
103 dst[out_pos++] = 't';
107 if ((out_pos + 4) >= dst_len) {
110 dst[out_pos++] = '&';
111 dst[out_pos++] = 'g';
112 dst[out_pos++] = 't';
116 if ((out_pos + 6) >= dst_len) {
119 dst[out_pos++] = '&';
120 dst[out_pos++] = 'q';
121 dst[out_pos++] = 'u';
122 dst[out_pos++] = 'o';
123 dst[out_pos++] = 't';
127 if ((out_pos + 5) >= dst_len) {
130 dst[out_pos++] = '&';
131 dst[out_pos++] = '#';
132 dst[out_pos++] = '3';
133 dst[out_pos++] = '9';
137 if ((out_pos + 5) >= dst_len) {
140 dst[out_pos++] = '&';
141 dst[out_pos++] = 'a';
142 dst[out_pos++] = 'm';
143 dst[out_pos++] = 'p';
147 dst[out_pos] = src[in_pos];
156 * Given a public key/subkey packet return the key length.
158 unsigned int keylength(struct openpgp_packet *keydata)
164 switch (keydata->data[0]) {
167 length = (keydata->data[8] << 8) +
172 /* v5 has an additional 4 bytes of key length data */
173 keyofs = (keydata->data[0] == 4) ? 6 : 10;
174 switch (keydata->data[5]) {
175 case OPENPGP_PKALGO_EC:
176 case OPENPGP_PKALGO_ECDSA:
177 case OPENPGP_PKALGO_EDDSA:
178 /* Elliptic curve key size is based on OID */
179 oid = onak_parse_oid(&keydata->data[keyofs],
180 keydata->length - keyofs);
181 if (oid == ONAK_OID_CURVE25519) {
183 } else if (oid == ONAK_OID_ED25519) {
185 } else if (oid == ONAK_OID_NISTP256) {
187 } else if (oid == ONAK_OID_NISTP384) {
189 } else if (oid == ONAK_OID_NISTP521) {
191 } else if (oid == ONAK_OID_BRAINPOOLP256R1) {
193 } else if (oid == ONAK_OID_BRAINPOOLP384R1) {
195 } else if (oid == ONAK_OID_BRAINPOOLP512R1) {
197 } else if (oid == ONAK_OID_SECP256K1) {
200 logthing(LOGTHING_ERROR,
201 "Unknown elliptic curve size");
206 length = (keydata->data[keyofs] << 8) +
207 keydata->data[keyofs + 1];
211 logthing(LOGTHING_ERROR, "Unknown key version: %d",
219 int list_sigs(struct onak_dbctx *dbctx,
220 struct openpgp_packet_list *sigs, bool html)
227 while (sigs != NULL) {
228 sigid = sig_keyid(sigs->packet);
230 uid = dbctx->keyid2uid(dbctx, sigid);
232 if (sigs->packet->data[0] == 4 &&
233 sigs->packet->data[1] == 0x30) {
234 /* It's a Type 4 sig revocation */
239 if (html && uid != NULL) {
240 printf("%s <a href=\"lookup?op=get&"
241 "search=0x%016" PRIX64 "\">0x%016" PRIX64
243 "<a href=\"lookup?op=vindex&search=0x%016"
244 PRIX64 "\">%s</a>\n",
249 html_escape(uid, strlen(uid), buf, sizeof(buf)));
250 } else if (html && uid == NULL) {
251 printf("%s 0x%016" PRIX64 " "
252 "[User id not found]\n",
256 printf("%s 0x%016" PRIX64
260 (uid != NULL) ? uid :
261 "[User id not found]");
273 int list_uids(struct onak_dbctx *dbctx,
274 uint64_t keyid, struct openpgp_signedpacket_list *uids,
275 bool verbose, bool html)
280 while (uids != NULL) {
281 if (uids->packet->tag == OPENPGP_PACKET_UID) {
282 snprintf(buf, 1023, "%.*s",
283 (int) uids->packet->length,
287 html_escape((char *) uids->packet->data,
288 uids->packet->length,
293 (int) uids->packet->length,
296 } else if (uids->packet->tag == OPENPGP_PACKET_UAT) {
299 printf("<img src=\"lookup?op=photo&search="
300 "0x%016" PRIX64 "&idx=%d\" alt=\""
306 printf("[photo id]\n");
310 list_sigs(dbctx, uids->sigs, html);
318 int list_subkeys(struct onak_dbctx *dbctx,
319 struct openpgp_signedpacket_list *subkeys, bool verbose,
323 time_t created_time = 0;
328 while (subkeys != NULL) {
329 if (subkeys->packet->tag == OPENPGP_PACKET_PUBLICSUBKEY) {
331 created_time = (subkeys->packet->data[1] << 24) +
332 (subkeys->packet->data[2] << 16) +
333 (subkeys->packet->data[3] << 8) +
334 subkeys->packet->data[4];
335 gmtime_r(&created_time, &created);
337 switch (subkeys->packet->data[0]) {
340 type = subkeys->packet->data[7];
344 type = subkeys->packet->data[5];
347 logthing(LOGTHING_ERROR,
348 "Unknown key version: %d",
349 subkeys->packet->data[0]);
351 length = keylength(subkeys->packet);
353 if (get_packetid(subkeys->packet,
354 &keyid) != ONAK_E_OK) {
355 logthing(LOGTHING_ERROR, "Couldn't get keyid.");
357 printf("sub %5d%c/0x%016" PRIX64 " %04d/%02d/%02d\n",
361 created.tm_year + 1900,
367 list_sigs(dbctx, subkeys->sigs, html);
369 subkeys = subkeys->next;
375 void display_fingerprint(struct openpgp_publickey *key)
378 struct openpgp_fingerprint fingerprint;
380 get_fingerprint(key->publickey, &fingerprint);
381 printf(" Key fingerprint =");
382 for (i = 0; i < fingerprint.length; i++) {
383 if ((fingerprint.length == 16) ||
387 if (fingerprint.length == 20 &&
388 (i * 2) == fingerprint.length) {
389 /* Extra space in the middle of a SHA1 fingerprint */
392 printf("%02X", fingerprint.fp[i]);
399 void display_skshash(struct openpgp_publickey *key, bool html)
404 get_skshash(key, &hash);
405 printf(" Key hash = ");
407 printf("<a href=\"lookup?op=hget&search=");
408 for (i = 0; i < sizeof(hash.hash); i++) {
409 printf("%02X", hash.hash[i]);
413 for (i = 0; i < sizeof(hash.hash); i++) {
414 printf("%02X", hash.hash[i]);
425 * key_index - List a set of OpenPGP keys.
426 * @keys: The keys to display.
427 * @verbose: Should we list sigs as well?
428 * @fingerprint: List the fingerprint?
429 * @html: Should the output be tailored for HTML?
431 * This function takes a list of OpenPGP public keys and displays an index
432 * of them. Useful for debugging or the keyserver Index function.
434 int key_index(struct onak_dbctx *dbctx,
435 struct openpgp_publickey *keys, bool verbose, bool fingerprint,
436 bool skshash, bool html)
438 struct openpgp_signedpacket_list *curuid = NULL;
440 time_t created_time = 0;
450 puts("Type bits/keyID Date User ID");
451 while (keys != NULL) {
452 created_time = (keys->publickey->data[1] << 24) +
453 (keys->publickey->data[2] << 16) +
454 (keys->publickey->data[3] << 8) +
455 keys->publickey->data[4];
456 gmtime_r(&created_time, &created);
458 switch (keys->publickey->data[0]) {
461 type = keys->publickey->data[7];
465 type = keys->publickey->data[5];
468 logthing(LOGTHING_ERROR, "Unknown key version: %d",
469 keys->publickey->data[0]);
471 length = keylength(keys->publickey);
473 if (get_keyid(keys, &keyid) != ONAK_E_OK) {
474 logthing(LOGTHING_ERROR, "Couldn't get keyid.");
478 printf("pub %5d%c/<a href=\"lookup?op=get&"
479 "search=0x%016" PRIX64 "\">0x%016" PRIX64
480 "</a> %04d/%02d/%02d ",
485 created.tm_year + 1900,
489 printf("pub %5d%c/0x%016" PRIX64 " %04d/%02d/%02d ",
493 created.tm_year + 1900,
499 if (curuid != NULL &&
500 curuid->packet->tag == OPENPGP_PACKET_UID) {
502 printf("<a href=\"lookup?op=vindex&"
503 "search=0x%016" PRIX64 "\">"
506 html_escape((char *) curuid->packet->data,
507 curuid->packet->length,
510 (keys->revoked) ? " *** REVOKED ***" : "");
513 (int) curuid->packet->length,
514 curuid->packet->data,
515 (keys->revoked) ? " *** REVOKED ***" : "");
518 display_skshash(keys, html);
521 display_fingerprint(keys);
524 list_sigs(dbctx, curuid->sigs, html);
526 curuid = curuid->next;
529 (keys->revoked) ? "*** REVOKED ***": "");
531 display_fingerprint(keys);
535 list_uids(dbctx, keyid, curuid, verbose, html);
537 list_subkeys(dbctx, keys->subkeys, verbose, html);
551 * mrkey_index - List a set of OpenPGP keys in the MRHKP format.
552 * @keys: The keys to display.
554 * This function takes a list of OpenPGP public keys and displays a
555 * machine readable list of them.
557 int mrkey_index(struct openpgp_publickey *keys)
559 struct openpgp_signedpacket_list *curuid = NULL;
560 time_t created_time = 0;
566 struct openpgp_fingerprint fingerprint;
568 while (keys != NULL) {
569 created_time = (keys->publickey->data[1] << 24) +
570 (keys->publickey->data[2] << 16) +
571 (keys->publickey->data[3] << 8) +
572 keys->publickey->data[4];
576 switch (keys->publickey->data[0]) {
579 if (get_keyid(keys, &keyid) != ONAK_E_OK) {
580 logthing(LOGTHING_ERROR, "Couldn't get keyid");
582 printf("%016" PRIX64, keyid);
583 type = keys->publickey->data[7];
587 (void) get_fingerprint(keys->publickey, &fingerprint);
589 for (i = 0; i < fingerprint.length; i++) {
590 printf("%02X", fingerprint.fp[i]);
593 type = keys->publickey->data[5];
596 logthing(LOGTHING_ERROR, "Unknown key version: %d",
597 keys->publickey->data[0]);
599 length = keylength(keys->publickey);
601 printf(":%d:%d:%ld::%s\n",
605 (keys->revoked) ? "r" : "");
607 for (curuid = keys->uids; curuid != NULL;
608 curuid = curuid->next) {
610 if (curuid->packet->tag == OPENPGP_PACKET_UID) {
612 for (i = 0; i < (int) curuid->packet->length;
614 c = curuid->packet->data[i];
618 } else if (c == ':' || c > 127) {