]> the.earth.li Git - onak.git/blob - keyindex.c
Add de-duplication of subkeys on a key
[onak.git] / keyindex.c
1 /*
2  * keyindex.c - Routines to list an OpenPGP key.
3  *
4  * Copyright 2002-2008 Jonathan McDowell <noodles@earth.li>
5  *
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.
9  *
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
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 51
17  * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #include <inttypes.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26
27 #include "decodekey.h"
28 #include "getcgi.h"
29 #include "hash.h"
30 #include "keydb.h"
31 #include "keyid.h"
32 #include "keyindex.h"
33 #include "keystructs.h"
34 #include "log.h"
35 #include "onak.h"
36 #include "onak-conf.h"
37 #include "openpgp.h"
38
39 /*
40  * Convert a Public Key algorithm to its single character representation.
41  */
42 char pkalgo2char(uint8_t algo)
43 {
44         char typech;
45
46         switch (algo) {
47         case OPENPGP_PKALGO_DSA:
48                 typech = 'D';
49                 break;
50         case OPENPGP_PKALGO_ECDSA:
51                 typech = 'E';
52                 break;
53         case OPENPGP_PKALGO_EC:
54                 typech = 'e';
55                 break;
56         case OPENPGP_PKALGO_ELGAMAL_SIGN:
57                 typech = 'G';
58                 break;
59         case OPENPGP_PKALGO_ELGAMAL_ENC:
60                 typech = 'g';
61                 break;
62         case OPENPGP_PKALGO_RSA:
63                 typech = 'R';
64                 break;
65         case OPENPGP_PKALGO_RSA_ENC:
66                 typech = 'r';
67                 break;
68         case OPENPGP_PKALGO_RSA_SIGN:
69                 typech = 's';
70                 break;
71         default:
72                 typech = '?';
73                 break;
74         }
75
76         return typech;
77 }
78
79 /*
80  * Given a public key/subkey packet return the key length.
81  */
82 unsigned int keylength(struct openpgp_packet *keydata)
83 {
84         unsigned int length;
85
86         switch (keydata->data[0]) {
87         case 2:
88         case 3:
89                 length = (keydata->data[8] << 8) +
90                                 keydata->data[9];
91                 break;
92         case 4:
93                 switch (keydata->data[5]) {
94                 case OPENPGP_PKALGO_EC:
95                 case OPENPGP_PKALGO_ECDSA:
96                         /* Elliptic curve key size is based on OID */
97                         /* Ed25519 / 1.3.6.1.4.1.11591.15.1 */
98                         if ((keydata->data[6] == 9) &&
99                                         (keydata->data[7] == 0x2B) &&
100                                         (keydata->data[8] == 0x06) &&
101                                         (keydata->data[9] == 0x01) &&
102                                         (keydata->data[10] == 0x04) &&
103                                         (keydata->data[11] == 0x01) &&
104                                         (keydata->data[12] == 0xDA) &&
105                                         (keydata->data[13] == 0x47) &&
106                                         (keydata->data[14] == 0x0F) &&
107                                         (keydata->data[15] == 0x01)) {
108                                 length = 256;
109                         /* nistp256 / 1.2.840.10045.3.1.7 */
110                         } else if ((keydata->data[6] == 8) &&
111                                         (keydata->data[7] == 0x2A) &&
112                                         (keydata->data[8] == 0x86) &&
113                                         (keydata->data[9] == 0x48) &&
114                                         (keydata->data[10] == 0xCE) &&
115                                         (keydata->data[11] == 0x3D) &&
116                                         (keydata->data[12] == 0x03) &&
117                                         (keydata->data[13] == 0x01) &&
118                                         (keydata->data[14] == 0x07)) {
119                                 length = 256;
120                         /* nistp384 / 1.3.132.0.34 */
121                         } else if ((keydata->data[6] == 5) &&
122                                         (keydata->data[7] == 0x2B) &&
123                                         (keydata->data[8] == 0x81) &&
124                                         (keydata->data[9] == 0x04) &&
125                                         (keydata->data[10] == 0x00) &&
126                                         (keydata->data[11] == 0x22)) {
127                                 length = 384;
128                         /* nistp521 / 1.3.132.0.35 */
129                         } else if ((keydata->data[6] == 5) &&
130                                         (keydata->data[7] == 0x2B) &&
131                                         (keydata->data[8] == 0x81) &&
132                                         (keydata->data[9] == 0x04) &&
133                                         (keydata->data[10] == 0x00) &&
134                                         (keydata->data[11] == 0x23)) {
135                                 length = 521;
136                         /* brainpoolP256r1 / 1.3.36.3.3.2.8.1.1.7 */
137                         } else if ((keydata->data[6] == 9) &&
138                                         (keydata->data[7] == 0x2B) &&
139                                         (keydata->data[8] == 0x24) &&
140                                         (keydata->data[9] == 0x03) &&
141                                         (keydata->data[10] == 0x03) &&
142                                         (keydata->data[11] == 0x02) &&
143                                         (keydata->data[12] == 0x08) &&
144                                         (keydata->data[13] == 0x01) &&
145                                         (keydata->data[14] == 0x01) &&
146                                         (keydata->data[15] == 0x07)) {
147                                 length = 256;
148                         /* brainpoolP384r1 / 1.3.36.3.3.2.8.1.1.11 */
149                         } else if ((keydata->data[6] == 9) &&
150                                         (keydata->data[7] == 0x2B) &&
151                                         (keydata->data[8] == 0x24) &&
152                                         (keydata->data[9] == 0x03) &&
153                                         (keydata->data[10] == 0x03) &&
154                                         (keydata->data[11] == 0x02) &&
155                                         (keydata->data[12] == 0x08) &&
156                                         (keydata->data[13] == 0x01) &&
157                                         (keydata->data[14] == 0x01) &&
158                                         (keydata->data[15] == 0x0B)) {
159                                 length = 384;
160                         /* brainpoolP512r1 / 1.3.36.3.3.2.8.1.1.13 */
161                         } else if ((keydata->data[6] == 9) &&
162                                         (keydata->data[7] == 0x2B) &&
163                                         (keydata->data[8] == 0x24) &&
164                                         (keydata->data[9] == 0x03) &&
165                                         (keydata->data[10] == 0x03) &&
166                                         (keydata->data[11] == 0x02) &&
167                                         (keydata->data[12] == 0x08) &&
168                                         (keydata->data[13] == 0x01) &&
169                                         (keydata->data[14] == 0x01) &&
170                                         (keydata->data[15] == 0x0D)) {
171                                 length = 512;
172                         /* secp256k1 / 1.3.132.0.10 */
173                         } else if ((keydata->data[6] == 5) &&
174                                         (keydata->data[7] == 0x2B) &&
175                                         (keydata->data[8] == 0x81) &&
176                                         (keydata->data[9] == 0x04) &&
177                                         (keydata->data[10] == 0x00) &&
178                                         (keydata->data[11] == 0x0A)) {
179                                 length = 256;
180                         } else {
181                                 logthing(LOGTHING_ERROR,
182                                         "Unknown elliptic curve size");
183                                 length = 0;
184                         }
185                         break;
186                 default:
187                         length = (keydata->data[6] << 8) +
188                                 keydata->data[7];
189                 }
190                 break;
191         default:
192                 logthing(LOGTHING_ERROR, "Unknown key version: %d",
193                         keydata->data[0]);
194                 length = 0;
195         }
196
197         return length;
198 }
199
200 int list_sigs(struct onak_dbctx *dbctx,
201                 struct openpgp_packet_list *sigs, bool html)
202 {
203         char *uid = NULL;
204         uint64_t sigid = 0;
205         char *sig = NULL;
206
207         while (sigs != NULL) {
208                 sigid = sig_keyid(sigs->packet);
209                 uid = dbctx->keyid2uid(dbctx, sigid);
210                 if (sigs->packet->data[0] == 4 &&
211                                 sigs->packet->data[1] == 0x30) {
212                         /* It's a Type 4 sig revocation */
213                         sig = "rev";
214                 } else {
215                         sig = "sig";
216                 }
217                 if (html && uid != NULL) {
218                         printf("%s         <a href=\"lookup?op=get&"
219                                 "search=0x%016" PRIX64 "\">%08" PRIX64
220                                 "</a>             "
221                                 "<a href=\"lookup?op=vindex&search=0x%016"
222                                 PRIX64 "\">%s</a>\n",
223                                 sig,
224                                 sigid,
225                                 sigid & 0xFFFFFFFF,
226                                 sigid,
227                                 txt2html(uid));
228                 } else if (html && uid == NULL) {
229                         printf("%s         %08" PRIX64 "             "
230                                 "[User id not found]\n",
231                                 sig,
232                                 sigid & 0xFFFFFFFF);
233                 } else {
234                         printf("%s         %08" PRIX64
235                                 "             %s\n",
236                                 sig,
237                                 sigid & 0xFFFFFFFF,
238                                 (uid != NULL) ? uid :
239                                 "[User id not found]");
240                 }
241                 if (uid != NULL) {
242                         free(uid);
243                         uid = NULL;
244                 }
245                 sigs = sigs->next;
246         }
247
248         return 0;
249 }
250
251 int list_uids(struct onak_dbctx *dbctx,
252                 uint64_t keyid, struct openpgp_signedpacket_list *uids,
253                 bool verbose, bool html)
254 {
255         char buf[1024];
256         int  imgindx = 0;
257
258         while (uids != NULL) {
259                 if (uids->packet->tag == OPENPGP_PACKET_UID) {
260                         snprintf(buf, 1023, "%.*s",
261                                 (int) uids->packet->length,
262                                 uids->packet->data);
263                         printf("                                %s\n",
264                                 (html) ? txt2html(buf) : buf);
265                 } else if (uids->packet->tag == OPENPGP_PACKET_UAT) {
266                         printf("                                ");
267                         if (html) {
268                                 printf("<img src=\"lookup?op=photo&search="
269                                         "0x%016" PRIX64 "&idx=%d\" alt=\""
270                                         "[photo id]\">\n",
271                                         keyid,
272                                         imgindx);
273                                 imgindx++;
274                         } else {
275                                 printf("[photo id]\n");
276                         }
277                 }
278                 if (verbose) {
279                         list_sigs(dbctx, uids->sigs, html);
280                 }
281                 uids = uids->next;
282         }
283
284         return 0;
285 }
286
287 int list_subkeys(struct onak_dbctx *dbctx,
288                 struct openpgp_signedpacket_list *subkeys, bool verbose,
289                 bool html)
290 {
291         struct tm       *created = NULL;
292         time_t          created_time = 0;
293         int             type = 0;
294         int             length = 0;
295         uint64_t        keyid = 0;
296
297         while (subkeys != NULL) {
298                 if (subkeys->packet->tag == OPENPGP_PACKET_PUBLICSUBKEY) {
299
300                         created_time = (subkeys->packet->data[1] << 24) +
301                                         (subkeys->packet->data[2] << 16) +
302                                         (subkeys->packet->data[3] << 8) +
303                                         subkeys->packet->data[4];
304                         created = gmtime(&created_time);
305
306                         switch (subkeys->packet->data[0]) {
307                         case 2:
308                         case 3:
309                                 type = subkeys->packet->data[7];
310                                 break;
311                         case 4:
312                                 type = subkeys->packet->data[5];
313                                 break;
314                         default:
315                                 logthing(LOGTHING_ERROR,
316                                         "Unknown key type: %d",
317                                         subkeys->packet->data[0]);
318                         }
319                         length = keylength(subkeys->packet);
320
321                         if (get_packetid(subkeys->packet,
322                                         &keyid) != ONAK_E_OK) {
323                                 logthing(LOGTHING_ERROR, "Couldn't get keyid.");
324                         }
325                         printf("sub  %5d%c/%08X %04d/%02d/%02d\n",
326                                 length,
327                                 pkalgo2char(type),
328                                 (uint32_t) (keyid & 0xFFFFFFFF),
329                                 created->tm_year + 1900,
330                                 created->tm_mon + 1,
331                                 created->tm_mday);
332
333                 }
334                 if (verbose) {
335                         list_sigs(dbctx, subkeys->sigs, html);
336                 }
337                 subkeys = subkeys->next;
338         }
339
340         return 0;
341 }
342
343 void display_fingerprint(struct openpgp_publickey *key)
344 {
345         int             i = 0;
346         struct openpgp_fingerprint fingerprint;
347
348         get_fingerprint(key->publickey, &fingerprint);
349         printf("      Key fingerprint =");
350         for (i = 0; i < fingerprint.length; i++) {
351                 if ((fingerprint.length == 16) ||
352                         (i % 2 == 0)) {
353                         printf(" ");
354                 }
355                 if (fingerprint.length == 20 &&
356                                 (i * 2) == fingerprint.length) {
357                         /* Extra space in the middle of a SHA1 fingerprint */
358                         printf(" ");
359                 }
360                 printf("%02X", fingerprint.fp[i]);
361         }
362         printf("\n");
363
364         return;
365 }
366
367 void display_skshash(struct openpgp_publickey *key, bool html)
368 {
369         int             i = 0;
370         struct skshash  hash;
371
372         get_skshash(key, &hash);
373         printf("      Key hash = ");
374         if (html) {
375                 printf("<a href=\"lookup?op=hget&search=");
376                 for (i = 0; i < sizeof(hash.hash); i++) {
377                         printf("%02X", hash.hash[i]);
378                 }
379                 printf("\">");
380         }
381         for (i = 0; i < sizeof(hash.hash); i++) {
382                 printf("%02X", hash.hash[i]);
383         }
384         if (html) {
385                 printf("</a>");
386         }
387         printf("\n");
388
389         return;
390 }
391
392 /**
393  *      key_index - List a set of OpenPGP keys.
394  *      @keys: The keys to display.
395  *      @verbose: Should we list sigs as well?
396  *      @fingerprint: List the fingerprint?
397  *      @html: Should the output be tailored for HTML?
398  *
399  *      This function takes a list of OpenPGP public keys and displays an index
400  *      of them. Useful for debugging or the keyserver Index function.
401  */
402 int key_index(struct onak_dbctx *dbctx,
403                 struct openpgp_publickey *keys, bool verbose, bool fingerprint,
404                         bool skshash, bool html)
405 {
406         struct openpgp_signedpacket_list        *curuid = NULL;
407         struct tm                               *created = NULL;
408         time_t                                   created_time = 0;
409         int                                      type = 0;
410         int                                      length = 0;
411         char                                     buf[1024];
412         uint64_t                                 keyid;
413
414         if (html) {
415                 puts("<pre>");
416         }
417         puts("Type   bits/keyID    Date       User ID");
418         while (keys != NULL) {
419                 created_time = (keys->publickey->data[1] << 24) +
420                                         (keys->publickey->data[2] << 16) +
421                                         (keys->publickey->data[3] << 8) +
422                                         keys->publickey->data[4];
423                 created = gmtime(&created_time);
424
425                 switch (keys->publickey->data[0]) {
426                 case 2:
427                 case 3:
428                         type = keys->publickey->data[7];
429                         break;
430                 case 4:
431                         type = keys->publickey->data[5];
432                         break;
433                 default:
434                         logthing(LOGTHING_ERROR, "Unknown key type: %d",
435                                 keys->publickey->data[0]);
436                 }
437                 length = keylength(keys->publickey);
438
439                 if (get_keyid(keys, &keyid) != ONAK_E_OK) {
440                         logthing(LOGTHING_ERROR, "Couldn't get keyid.");
441                 }
442
443                 if (html) {
444                         printf("pub  %5d%c/<a href=\"lookup?op=get&"
445                                 "search=0x%016" PRIX64 "\">%08" PRIX64
446                                 "</a> %04d/%02d/%02d ",
447                                 length,
448                                 pkalgo2char(type),
449                                 keyid,
450                                 keyid & 0xFFFFFFFF,
451                                 created->tm_year + 1900,
452                                 created->tm_mon + 1,
453                                 created->tm_mday);
454                 } else {
455                         printf("pub  %5d%c/%08" PRIX64 " %04d/%02d/%02d ",
456                                 length,
457                                 pkalgo2char(type),
458                                 keyid & 0xFFFFFFFF,
459                                 created->tm_year + 1900,
460                                 created->tm_mon + 1,
461                                 created->tm_mday);
462                 }
463
464                 curuid = keys->uids;
465                 if (curuid != NULL &&
466                                 curuid->packet->tag == OPENPGP_PACKET_UID) {
467                         snprintf(buf, 1023, "%.*s",
468                                 (int) curuid->packet->length,
469                                 curuid->packet->data);
470                         if (html) {
471                                 printf("<a href=\"lookup?op=vindex&"
472                                         "search=0x%016" PRIX64 "\">",
473                                         keyid);
474                         }
475                         printf("%s%s%s\n", 
476                                 (html) ? txt2html(buf) : buf,
477                                 (html) ? "</a>" : "",
478                                 (keys->revoked) ? " *** REVOKED ***" : "");
479                         if (skshash) {
480                                 display_skshash(keys, html);
481                         }
482                         if (fingerprint) {
483                                 display_fingerprint(keys);
484                         }
485                         if (verbose) {
486                                 list_sigs(dbctx, curuid->sigs, html);
487                         }
488                         curuid = curuid->next;
489                 } else {
490                         printf("%s\n", 
491                                 (keys->revoked) ? "*** REVOKED ***": "");
492                         if (fingerprint) {
493                                 display_fingerprint(keys);
494                         }
495                 }
496
497                 list_uids(dbctx, keyid, curuid, verbose, html);
498                 if (verbose) {
499                         list_subkeys(dbctx, keys->subkeys, verbose, html);
500                 }
501
502                 keys = keys->next;
503         }
504
505         if (html) {
506                 puts("</pre>");
507         }
508
509         return 0;
510 }
511
512 /**
513  *      mrkey_index - List a set of OpenPGP keys in the MRHKP format.
514  *      @keys: The keys to display.
515  *
516  *      This function takes a list of OpenPGP public keys and displays a
517  *      machine readable list of them.
518  */
519 int mrkey_index(struct openpgp_publickey *keys)
520 {
521         struct openpgp_signedpacket_list        *curuid = NULL;
522         time_t                                   created_time = 0;
523         int                                      type = 0;
524         int                                      length = 0;
525         int                                      i = 0;
526         int                                      c;
527         uint64_t                                 keyid;
528         struct openpgp_fingerprint fingerprint;
529
530         while (keys != NULL) {
531                 created_time = (keys->publickey->data[1] << 24) +
532                                         (keys->publickey->data[2] << 16) +
533                                         (keys->publickey->data[3] << 8) +
534                                         keys->publickey->data[4];
535
536                 printf("pub:");
537
538                 switch (keys->publickey->data[0]) {
539                 case 2:
540                 case 3:
541                         if (get_keyid(keys, &keyid) != ONAK_E_OK) {
542                                 logthing(LOGTHING_ERROR, "Couldn't get keyid");
543                         }
544                         printf("%016" PRIX64, keyid);
545                         type = keys->publickey->data[7];
546                         break;
547                 case 4:
548                         (void) get_fingerprint(keys->publickey, &fingerprint);
549
550                         for (i = 0; i < fingerprint.length; i++) {
551                                 printf("%02X", fingerprint.fp[i]);
552                         }
553
554                         type = keys->publickey->data[5];
555                         break;
556                 default:
557                         logthing(LOGTHING_ERROR, "Unknown key type: %d",
558                                 keys->publickey->data[0]);
559                 }
560                 length = keylength(keys->publickey);
561
562                 printf(":%d:%d:%ld::%s\n",
563                         type,
564                         length,
565                         created_time,
566                         (keys->revoked) ? "r" : "");
567         
568                 for (curuid = keys->uids; curuid != NULL;
569                          curuid = curuid->next) {
570                 
571                         if (curuid->packet->tag == OPENPGP_PACKET_UID) {
572                                 printf("uid:");
573                                 for (i = 0; i < (int) curuid->packet->length;
574                                                 i++) {
575                                         c = curuid->packet->data[i];
576                                         if (c == '%') {
577                                                 putchar('%');
578                                                 putchar(c);
579                                         } else if (c == ':' || c > 127) {
580                                                 printf("%%%X", c);
581                                         } else {
582                                                 putchar(c);
583                                         }
584                                 }
585                                 printf("\n");
586                         }
587                 }
588                 keys = keys->next;
589         }
590         return 0;
591 }