1/*	$OpenBSD: print.c,v 1.55 2024/06/08 13:30:35 tb Exp $ */
2/*
3 * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/socket.h>
21#include <arpa/inet.h>
22
23#include <err.h>
24#include <stdio.h>
25#include <string.h>
26#include <time.h>
27
28#include <openssl/evp.h>
29
30#include "extern.h"
31#include "json.h"
32
33static const char *
34pretty_key_id(const char *hex)
35{
36	static char buf[128];	/* bigger than SHA_DIGEST_LENGTH * 3 */
37	size_t i;
38
39	for (i = 0; i < sizeof(buf) && *hex != '\0'; i++) {
40		if (i % 3 == 2)
41			buf[i] = ':';
42		else
43			buf[i] = *hex++;
44	}
45	if (i == sizeof(buf))
46		memcpy(buf + sizeof(buf) - 4, "...", 4);
47	else
48		buf[i] = '\0';
49	return buf;
50}
51
52char *
53nid2str(int nid)
54{
55	static char buf[128];
56	const char *name;
57
58	if ((name = OBJ_nid2ln(nid)) == NULL)
59		name = OBJ_nid2sn(nid);
60	if (name == NULL)
61		name = "unknown";
62
63	snprintf(buf, sizeof(buf), "nid %d (%s)", nid, name);
64
65	return buf;
66}
67
68const char *
69purpose2str(enum cert_purpose purpose)
70{
71	switch (purpose) {
72	case CERT_PURPOSE_INVALID:
73		return "invalid cert";
74	case CERT_PURPOSE_TA:
75		return "TA cert";
76	case CERT_PURPOSE_CA:
77		return "CA cert";
78	case CERT_PURPOSE_EE:
79		return "EE cert";
80	case CERT_PURPOSE_BGPSEC_ROUTER:
81		return "BGPsec Router cert";
82	default:
83		return "unknown certificate purpose";
84	}
85}
86
87char *
88time2str(time_t t)
89{
90	static char buf[64];
91	struct tm tm;
92
93	if (gmtime_r(&t, &tm) == NULL)
94		return "could not convert time";
95
96	strftime(buf, sizeof(buf), "%a %d %b %Y %T %z", &tm);
97
98	return buf;
99}
100
101void
102tal_print(const struct tal *p)
103{
104	char			*ski;
105	const unsigned char	*der;
106	X509_PUBKEY		*pubkey;
107	size_t			 i;
108
109	der = p->pkey;
110	if ((pubkey = d2i_X509_PUBKEY(NULL, &der, p->pkeysz)) == NULL)
111		errx(1, "d2i_X509_PUBKEY failed");
112
113	if ((ski = x509_pubkey_get_ski(pubkey, p->descr)) == NULL)
114		errx(1, "x509_pubkey_get_ski failed");
115
116	if (outformats & FORMAT_JSON) {
117		json_do_string("type", "tal");
118		json_do_string("name", p->descr);
119		json_do_string("ski", pretty_key_id(ski));
120		json_do_array("trust_anchor_locations");
121		for (i = 0; i < p->urisz; i++)
122			json_do_string("tal", p->uri[i]);
123		json_do_end();
124	} else {
125		printf("Trust anchor name:        %s\n", p->descr);
126		printf("Subject key identifier:   %s\n", pretty_key_id(ski));
127		printf("Trust anchor locations:   ");
128		for (i = 0; i < p->urisz; i++) {
129			if (i > 0)
130				printf("%26s", "");
131			printf("%s\n", p->uri[i]);
132		}
133	}
134
135	X509_PUBKEY_free(pubkey);
136	free(ski);
137}
138
139void
140x509_print(const X509 *x)
141{
142	const ASN1_INTEGER	*xserial;
143	const X509_NAME		*xissuer;
144	char			*issuer = NULL;
145	char			*serial = NULL;
146
147	if ((xissuer = X509_get_issuer_name(x)) == NULL) {
148		warnx("X509_get_issuer_name failed");
149		goto out;
150	}
151
152	if ((issuer = X509_NAME_oneline(xissuer, NULL, 0)) == NULL) {
153		warnx("X509_NAME_oneline failed");
154		goto out;
155	}
156
157	if ((xserial = X509_get0_serialNumber(x)) == NULL) {
158		warnx("X509_get0_serialNumber failed");
159		goto out;
160	}
161
162	if ((serial = x509_convert_seqnum(__func__, xserial)) == NULL)
163		goto out;
164
165	if (outformats & FORMAT_JSON) {
166		json_do_string("cert_issuer", issuer);
167		json_do_string("cert_serial", serial);
168	} else {
169		printf("Certificate issuer:       %s\n", issuer);
170		printf("Certificate serial:       %s\n", serial);
171	}
172
173 out:
174	free(issuer);
175	free(serial);
176}
177
178static void
179as_resources_print(struct cert_as *as, size_t asz)
180{
181	size_t i;
182
183	for (i = 0; i < asz; i++) {
184		if (outformats & FORMAT_JSON)
185			json_do_object("resource", 1);
186		switch (as[i].type) {
187		case CERT_AS_ID:
188			if (outformats & FORMAT_JSON) {
189				json_do_uint("asid", as[i].id);
190			} else {
191				if (i > 0)
192					printf("%26s", "");
193				printf("AS: %u", as[i].id);
194			}
195			break;
196		case CERT_AS_INHERIT:
197			if (outformats & FORMAT_JSON) {
198				json_do_bool("asid_inherit", 1);
199			} else {
200				if (i > 0)
201					printf("%26s", "");
202				printf("AS: inherit");
203			}
204			break;
205		case CERT_AS_RANGE:
206			if (outformats & FORMAT_JSON) {
207				json_do_object("asrange", 1);
208				json_do_uint("min", as[i].range.min);
209				json_do_uint("max", as[i].range.max);
210				json_do_end();
211			} else {
212				if (i > 0)
213					printf("%26s", "");
214				printf("AS: %u -- %u", as[i].range.min,
215				    as[i].range.max);
216			}
217			break;
218		}
219		if (outformats & FORMAT_JSON)
220			json_do_end();
221		else
222			printf("\n");
223	}
224}
225
226static void
227ip_resources_print(struct cert_ip *ips, size_t ipsz, size_t asz)
228{
229	char buf1[64], buf2[64];
230	size_t i;
231	int sockt;
232
233	for (i = 0; i < ipsz; i++) {
234		if (outformats & FORMAT_JSON)
235			json_do_object("resource", 1);
236		switch (ips[i].type) {
237		case CERT_IP_INHERIT:
238			if (outformats & FORMAT_JSON) {
239				json_do_bool("ip_inherit", 1);
240			} else {
241				if (i > 0 || asz > 0)
242					printf("%26s", "");
243				printf("IP: inherit");
244			}
245			break;
246		case CERT_IP_ADDR:
247			ip_addr_print(&ips[i].ip, ips[i].afi, buf1,
248			    sizeof(buf1));
249			if (outformats & FORMAT_JSON) {
250				json_do_string("ip_prefix", buf1);
251			} else {
252				if (i > 0 || asz > 0)
253					printf("%26s", "");
254				printf("IP: %s", buf1);
255			}
256			break;
257		case CERT_IP_RANGE:
258			sockt = (ips[i].afi == AFI_IPV4) ?
259			    AF_INET : AF_INET6;
260			inet_ntop(sockt, ips[i].min, buf1, sizeof(buf1));
261			inet_ntop(sockt, ips[i].max, buf2, sizeof(buf2));
262			if (outformats & FORMAT_JSON) {
263				json_do_object("ip_range", 1);
264				json_do_string("min", buf1);
265				json_do_string("max", buf2);
266				json_do_end();
267			} else {
268				if (i > 0 || asz > 0)
269					printf("%26s", "");
270				printf("IP: %s -- %s", buf1, buf2);
271			}
272			break;
273		}
274		if (outformats & FORMAT_JSON)
275			json_do_end();
276		else
277			printf("\n");
278	}
279}
280
281void
282cert_print(const struct cert *p)
283{
284	if (outformats & FORMAT_JSON) {
285		if (p->pubkey != NULL)
286			json_do_string("type", "router_key");
287		else
288			json_do_string("type", "ca_cert");
289		json_do_string("ski", pretty_key_id(p->ski));
290		if (p->aki != NULL)
291			json_do_string("aki", pretty_key_id(p->aki));
292		x509_print(p->x509);
293		if (p->aia != NULL)
294			json_do_string("aia", p->aia);
295		if (p->mft != NULL)
296			json_do_string("manifest", p->mft);
297		if (p->repo != NULL)
298			json_do_string("carepository", p->repo);
299		if (p->notify != NULL)
300			json_do_string("notify_url", p->notify);
301		if (p->pubkey != NULL)
302			json_do_string("router_key", p->pubkey);
303		json_do_int("valid_since", p->notbefore);
304		json_do_int("valid_until", p->notafter);
305		if (p->expires)
306			json_do_int("expires", p->expires);
307		json_do_array("subordinate_resources");
308	} else {
309		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
310		if (p->aki != NULL)
311			printf("Authority key identifier: %s\n",
312			    pretty_key_id(p->aki));
313		x509_print(p->x509);
314		if (p->aia != NULL)
315			printf("Authority info access:    %s\n", p->aia);
316		if (p->mft != NULL)
317			printf("Manifest:                 %s\n", p->mft);
318		if (p->repo != NULL)
319			printf("caRepository:             %s\n", p->repo);
320		if (p->notify != NULL)
321			printf("Notify URL:               %s\n", p->notify);
322		if (p->pubkey != NULL) {
323			printf("BGPsec ECDSA public key:  %s\n",
324			    p->pubkey);
325			printf("Router key not before:    %s\n",
326			    time2str(p->notbefore));
327			printf("Router key not after:     %s\n",
328			    time2str(p->notafter));
329		} else {
330			printf("Certificate not before:   %s\n",
331			    time2str(p->notbefore));
332			printf("Certificate not after:    %s\n",
333			    time2str(p->notafter));
334		}
335		printf("Subordinate resources:    ");
336	}
337
338	as_resources_print(p->as, p->asz);
339	ip_resources_print(p->ips, p->ipsz, p->asz);
340
341	if (outformats & FORMAT_JSON)
342		json_do_end();
343}
344
345/*
346 * XXX - dedup with x509_convert_seqnum()?
347 */
348static char *
349crl_parse_number(const X509_CRL *x509_crl)
350{
351	ASN1_INTEGER	*aint = NULL;
352	int		 crit;
353	BIGNUM		*seqnum = NULL;
354	char		*s = NULL;
355
356	aint = X509_CRL_get_ext_d2i(x509_crl, NID_crl_number, &crit, NULL);
357	if (aint == NULL) {
358		if (crit != -1)
359			warnx("failed to parse CRL Number");
360		else
361			warnx("CRL Number missing");
362		goto out;
363	}
364
365	if (ASN1_STRING_length(aint) > 20)
366		warnx("CRL Number should fit in 20 octets");
367
368	seqnum = ASN1_INTEGER_to_BN(aint, NULL);
369	if (seqnum == NULL) {
370		warnx("CRL Number: ASN1_INTEGER_to_BN error");
371		goto out;
372	}
373
374	if (BN_is_negative(seqnum))
375		warnx("CRL Number should be positive");
376
377	s = BN_bn2hex(seqnum);
378	if (s == NULL)
379		warnx("CRL Number: BN_bn2hex error");
380
381 out:
382	ASN1_INTEGER_free(aint);
383	BN_free(seqnum);
384	return s;
385}
386
387void
388crl_print(const struct crl *p)
389{
390	STACK_OF(X509_REVOKED)	*revlist;
391	X509_REVOKED *rev;
392	X509_NAME *xissuer;
393	int i;
394	char *issuer, *serial;
395	time_t t;
396
397	if (outformats & FORMAT_JSON) {
398		json_do_string("type", "crl");
399		json_do_string("aki", pretty_key_id(p->aki));
400	} else
401		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
402
403	xissuer = X509_CRL_get_issuer(p->x509_crl);
404	issuer = X509_NAME_oneline(xissuer, NULL, 0);
405	if (issuer != NULL) {
406		char *number;
407
408		if ((number = crl_parse_number(p->x509_crl)) != NULL) {
409			if (outformats & FORMAT_JSON) {
410				json_do_string("crl_issuer", issuer);
411				json_do_string("crl_serial", number);
412			} else {
413				printf("CRL issuer:               %s\n",
414				    issuer);
415				printf("CRL serial number:        %s\n",
416				    number);
417			}
418			free(number);
419		}
420	}
421	free(issuer);
422
423	if (outformats & FORMAT_JSON) {
424		json_do_int("valid_since", p->thisupdate);
425		json_do_int("valid_until", p->nextupdate);
426		json_do_array("revoked_certs");
427	} else {
428		printf("CRL this update:          %s\n",
429		    time2str(p->thisupdate));
430		printf("CRL next update:          %s\n",
431		    time2str(p->nextupdate));
432		printf("Revoked Certificates:\n");
433	}
434
435	revlist = X509_CRL_get_REVOKED(p->x509_crl);
436	for (i = 0; i < sk_X509_REVOKED_num(revlist); i++) {
437		rev = sk_X509_REVOKED_value(revlist, i);
438		serial = x509_convert_seqnum(__func__,
439		    X509_REVOKED_get0_serialNumber(rev));
440		x509_get_time(X509_REVOKED_get0_revocationDate(rev), &t);
441		if (serial != NULL) {
442			if (outformats & FORMAT_JSON) {
443				json_do_object("cert", 1);
444				json_do_string("serial", serial);
445				json_do_string("date", time2str(t));
446				json_do_end();
447			} else
448				printf("%25s Serial: %8s   Revocation Date: %s"
449				    "\n", "", serial, time2str(t));
450		}
451		free(serial);
452	}
453
454	if (outformats & FORMAT_JSON)
455		json_do_end();
456	else if (i == 0)
457		printf("No Revoked Certificates\n");
458}
459
460void
461mft_print(const X509 *x, const struct mft *p)
462{
463	size_t i;
464	char *hash;
465
466	if (outformats & FORMAT_JSON) {
467		json_do_string("type", "manifest");
468		json_do_string("ski", pretty_key_id(p->ski));
469		x509_print(x);
470		json_do_string("aki", pretty_key_id(p->aki));
471		json_do_string("aia", p->aia);
472		json_do_string("sia", p->sia);
473		json_do_string("manifest_number", p->seqnum);
474		if (p->signtime != 0)
475			json_do_int("signing_time", p->signtime);
476		json_do_int("valid_since", p->thisupdate);
477		json_do_int("valid_until", p->nextupdate);
478		if (p->expires)
479			json_do_int("expires", p->expires);
480	} else {
481		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
482		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
483		x509_print(x);
484		printf("Authority info access:    %s\n", p->aia);
485		printf("Subject info access:      %s\n", p->sia);
486		printf("Manifest number:          %s\n", p->seqnum);
487		if (p->signtime != 0)
488			printf("Signing time:             %s\n",
489			    time2str(p->signtime));
490		printf("Manifest this update:     %s\n", time2str(p->thisupdate));
491		printf("Manifest next update:     %s\n", time2str(p->nextupdate));
492		printf("Files and hashes:         ");
493	}
494
495	if (outformats & FORMAT_JSON)
496		json_do_array("filesandhashes");
497	for (i = 0; i < p->filesz; i++) {
498		if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash),
499		    &hash) == -1)
500			errx(1, "base64_encode failure");
501
502		if (outformats & FORMAT_JSON) {
503			json_do_object("filehash", 1);
504			json_do_string("filename", p->files[i].file);
505			json_do_string("hash", hash);
506			json_do_end();
507		} else {
508			if (i > 0)
509				printf("%26s", "");
510			printf("%zu: %s (hash: %s)\n", i + 1, p->files[i].file,
511			    hash);
512		}
513
514		free(hash);
515	}
516	if (outformats & FORMAT_JSON)
517		json_do_end();
518}
519
520void
521roa_print(const X509 *x, const struct roa *p)
522{
523	char	 buf[128];
524	size_t	 i;
525
526	if (outformats & FORMAT_JSON) {
527		json_do_string("type", "roa");
528		json_do_string("ski", pretty_key_id(p->ski));
529		x509_print(x);
530		json_do_string("aki", pretty_key_id(p->aki));
531		json_do_string("aia", p->aia);
532		json_do_string("sia", p->sia);
533		if (p->signtime != 0)
534			json_do_int("signing_time", p->signtime);
535		json_do_int("valid_since", p->notbefore);
536		json_do_int("valid_until", p->notafter);
537		if (p->expires)
538			json_do_int("expires", p->expires);
539	} else {
540		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
541		x509_print(x);
542		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
543		printf("Authority info access:    %s\n", p->aia);
544		printf("Subject info access:      %s\n", p->sia);
545		if (p->signtime != 0)
546			printf("Signing time:             %s\n",
547			    time2str(p->signtime));
548		printf("ROA not before:           %s\n",
549		    time2str(p->notbefore));
550		printf("ROA not after:            %s\n", time2str(p->notafter));
551		printf("asID:                     %u\n", p->asid);
552		printf("IP address blocks:        ");
553	}
554
555	if (outformats & FORMAT_JSON)
556		json_do_array("vrps");
557	for (i = 0; i < p->ipsz; i++) {
558		ip_addr_print(&p->ips[i].addr,
559		    p->ips[i].afi, buf, sizeof(buf));
560
561		if (outformats & FORMAT_JSON) {
562			json_do_object("vrp", 1);
563			json_do_string("prefix", buf);
564			json_do_uint("asid", p->asid);
565			json_do_uint("maxlen", p->ips[i].maxlength);
566			json_do_end();
567		} else {
568			if (i > 0)
569				printf("%26s", "");
570			printf("%s maxlen: %hhu\n", buf, p->ips[i].maxlength);
571		}
572	}
573	if (outformats & FORMAT_JSON)
574		json_do_end();
575}
576
577void
578spl_print(const X509 *x, const struct spl *s)
579{
580	char	 buf[128];
581	size_t	 i;
582
583	if (outformats & FORMAT_JSON) {
584		json_do_string("type", "spl");
585		json_do_string("ski", pretty_key_id(s->ski));
586		x509_print(x);
587		json_do_string("aki", pretty_key_id(s->aki));
588		json_do_string("aia", s->aia);
589		json_do_string("sia", s->sia);
590		if (s->signtime != 0)
591			json_do_int("signing_time", s->signtime);
592		json_do_int("valid_since", s->notbefore);
593		json_do_int("valid_until", s->notafter);
594		if (s->expires)
595			json_do_int("expires", s->expires);
596		json_do_int("asid", s->asid);
597	} else {
598		printf("Subject key identifier:   %s\n", pretty_key_id(s->ski));
599		x509_print(x);
600		printf("Authority key identifier: %s\n", pretty_key_id(s->aki));
601		printf("Authority info access:    %s\n", s->aia);
602		printf("Subject info access:      %s\n", s->sia);
603		if (s->signtime != 0)
604			printf("Signing time:             %s\n",
605			    time2str(s->signtime));
606		printf("SPL not before:           %s\n",
607		    time2str(s->notbefore));
608		printf("SPL not after:            %s\n", time2str(s->notafter));
609		printf("asID:                     %u\n", s->asid);
610		printf("Originated IP Prefixes:   ");
611	}
612
613	if (outformats & FORMAT_JSON)
614		json_do_array("prefixes");
615	for (i = 0; i < s->pfxsz; i++) {
616		ip_addr_print(&s->pfxs[i].prefix, s->pfxs[i].afi, buf,
617		    sizeof(buf));
618
619		if (outformats & FORMAT_JSON) {
620			json_do_string("prefix", buf);
621		} else {
622			if (i > 0)
623				printf("%26s", "");
624			printf("%s\n", buf);
625		}
626	}
627	if (outformats & FORMAT_JSON)
628		json_do_end();
629}
630
631void
632gbr_print(const X509 *x, const struct gbr *p)
633{
634	if (outformats & FORMAT_JSON) {
635		json_do_string("type", "gbr");
636		json_do_string("ski", pretty_key_id(p->ski));
637		x509_print(x);
638		json_do_string("aki", pretty_key_id(p->aki));
639		json_do_string("aia", p->aia);
640		json_do_string("sia", p->sia);
641		if (p->signtime != 0)
642			json_do_int("signing_time", p->signtime);
643		json_do_int("valid_since", p->notbefore);
644		json_do_int("valid_until", p->notafter);
645		if (p->expires)
646			json_do_int("expires", p->expires);
647		json_do_string("vcard", p->vcard);
648	} else {
649		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
650		x509_print(x);
651		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
652		printf("Authority info access:    %s\n", p->aia);
653		printf("Subject info access:      %s\n", p->sia);
654		if (p->signtime != 0)
655			printf("Signing time:             %s\n",
656			    time2str(p->signtime));
657		printf("GBR not before:           %s\n",
658		    time2str(p->notbefore));
659		printf("GBR not after:            %s\n", time2str(p->notafter));
660		printf("vcard:\n%s", p->vcard);
661	}
662}
663
664void
665rsc_print(const X509 *x, const struct rsc *p)
666{
667	char	*hash;
668	size_t	 i;
669
670	if (outformats & FORMAT_JSON) {
671		json_do_string("type", "rsc");
672		json_do_string("ski", pretty_key_id(p->ski));
673		x509_print(x);
674		json_do_string("aki", pretty_key_id(p->aki));
675		json_do_string("aia", p->aia);
676		if (p->signtime != 0)
677			json_do_int("signing_time", p->signtime);
678		json_do_int("valid_since", p->notbefore);
679		json_do_int("valid_until", p->notafter);
680		if (p->expires)
681			json_do_int("expires", p->expires);
682		json_do_array("signed_with_resources");
683	} else {
684		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
685		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
686		x509_print(x);
687		printf("Authority info access:    %s\n", p->aia);
688		if (p->signtime != 0)
689			printf("Signing time:             %s\n",
690			    time2str(p->signtime));
691		printf("RSC not before:           %s\n",
692		    time2str(p->notbefore));
693		printf("RSC not after:            %s\n", time2str(p->notafter));
694		printf("Signed with resources:    ");
695	}
696
697	as_resources_print(p->as, p->asz);
698	ip_resources_print(p->ips, p->ipsz, p->asz);
699
700	if (outformats & FORMAT_JSON) {
701		json_do_end();
702		json_do_array("filenamesandhashes");
703	} else
704		printf("Filenames and hashes:     ");
705
706	for (i = 0; i < p->filesz; i++) {
707		if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash),
708		    &hash) == -1)
709			errx(1, "base64_encode failure");
710
711		if (outformats & FORMAT_JSON) {
712			json_do_object("filehash", 1);
713			if (p->files[i].filename)
714				json_do_string("filename",
715				    p->files[i].filename);
716			json_do_string("hash_digest", hash);
717			json_do_end();
718		} else {
719			if (i > 0)
720				printf("%26s", "");
721			printf("%zu: %s (hash: %s)\n", i + 1,
722			    p->files[i].filename ? p->files[i].filename
723			    : "no filename", hash);
724		}
725
726		free(hash);
727	}
728
729	if (outformats & FORMAT_JSON)
730		json_do_end();
731}
732
733void
734aspa_print(const X509 *x, const struct aspa *p)
735{
736	size_t	i;
737
738	if (outformats & FORMAT_JSON) {
739		json_do_string("type", "aspa");
740		json_do_string("ski", pretty_key_id(p->ski));
741		x509_print(x);
742		json_do_string("aki", pretty_key_id(p->aki));
743		json_do_string("aia", p->aia);
744		json_do_string("sia", p->sia);
745		if (p->signtime != 0)
746			json_do_int("signing_time", p->signtime);
747		json_do_int("valid_since", p->notbefore);
748		json_do_int("valid_until", p->notafter);
749		if (p->expires)
750			json_do_int("expires", p->expires);
751		json_do_uint("customer_asid", p->custasid);
752		json_do_array("providers");
753	} else {
754		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
755		x509_print(x);
756		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
757		printf("Authority info access:    %s\n", p->aia);
758		printf("Subject info access:      %s\n", p->sia);
759		if (p->signtime != 0)
760			printf("Signing time:             %s\n",
761			    time2str(p->signtime));
762		printf("ASPA not before:          %s\n",
763		    time2str(p->notbefore));
764		printf("ASPA not after:           %s\n", time2str(p->notafter));
765		printf("Customer ASID:            %u\n", p->custasid);
766		printf("Providers:                ");
767	}
768
769	for (i = 0; i < p->providersz; i++) {
770		if (outformats & FORMAT_JSON)
771			json_do_uint("asid", p->providers[i]);
772		else {
773			if (i > 0)
774				printf("%26s", "");
775			printf("AS: %u\n", p->providers[i]);
776		}
777	}
778
779	if (outformats & FORMAT_JSON)
780		json_do_end();
781}
782
783static void
784takey_print(char *name, const struct takey *t)
785{
786	char	*spki = NULL;
787	size_t	 i, j = 0;
788
789	if (base64_encode(t->pubkey, t->pubkeysz, &spki) != 0)
790		errx(1, "base64_encode failed in %s", __func__);
791
792	if (outformats & FORMAT_JSON) {
793		json_do_object("takey", 0);
794		json_do_string("name", name);
795		json_do_array("comments");
796		for (i = 0; i < t->commentsz; i++)
797			json_do_string("comment", t->comments[i]);
798		json_do_end();
799		json_do_array("uris");
800		for (i = 0; i < t->urisz; i++)
801			json_do_string("uri", t->uris[i]);
802		json_do_end();
803		json_do_string("spki", spki);
804		json_do_end();
805	} else {
806		printf("TAL derived from the '%s' Trust Anchor Key:\n\n", name);
807
808		for (i = 0; i < t->commentsz; i++)
809			printf("\t# %s\n", t->comments[i]);
810		if (t->commentsz > 0)
811			printf("\n");
812		for (i = 0; i < t->urisz; i++)
813			printf("\t%s\n", t->uris[i]);
814		printf("\n\t");
815		for (i = 0; i < strlen(spki); i++) {
816			printf("%c", spki[i]);
817			if ((++j % 64) == 0)
818				printf("\n\t");
819		}
820		printf("\n\n");
821	}
822
823	free(spki);
824}
825
826void
827tak_print(const X509 *x, const struct tak *p)
828{
829	if (outformats & FORMAT_JSON) {
830		json_do_string("type", "tak");
831		json_do_string("ski", pretty_key_id(p->ski));
832		x509_print(x);
833		json_do_string("aki", pretty_key_id(p->aki));
834		json_do_string("aia", p->aia);
835		json_do_string("sia", p->sia);
836		if (p->signtime != 0)
837			json_do_int("signing_time", p->signtime);
838		json_do_int("valid_since", p->notbefore);
839		json_do_int("valid_until", p->notafter);
840		if (p->expires)
841			json_do_int("expires", p->expires);
842		json_do_array("takeys");
843	} else {
844		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
845		x509_print(x);
846		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
847		printf("Authority info access:    %s\n", p->aia);
848		printf("Subject info access:      %s\n", p->sia);
849		if (p->signtime != 0)
850			printf("Signing time:             %s\n",
851			    time2str(p->signtime));
852		printf("TAK not before:           %s\n",
853		    time2str(p->notbefore));
854		printf("TAK not after:            %s\n", time2str(p->notafter));
855	}
856
857	takey_print("current", p->current);
858	if (p->predecessor != NULL)
859		takey_print("predecessor", p->predecessor);
860	if (p->successor != NULL)
861		takey_print("successor", p->successor);
862
863	if (outformats & FORMAT_JSON)
864		json_do_end();
865}
866
867void
868geofeed_print(const X509 *x, const struct geofeed *p)
869{
870	char	 buf[128];
871	size_t	 i;
872
873	if (outformats & FORMAT_JSON) {
874		json_do_string("type", "geofeed");
875		json_do_string("ski", pretty_key_id(p->ski));
876		x509_print(x);
877		json_do_string("aki", pretty_key_id(p->aki));
878		json_do_string("aia", p->aia);
879		if (p->signtime != 0)
880			json_do_int("signing_time", p->signtime);
881		json_do_int("valid_since", p->notbefore);
882		json_do_int("valid_until", p->notafter);
883		if (p->expires)
884			json_do_int("expires", p->expires);
885		json_do_array("records");
886	} else {
887		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
888		x509_print(x);
889		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
890		printf("Authority info access:    %s\n", p->aia);
891		if (p->signtime != 0)
892			printf("Signing time:             %s\n",
893			    time2str(p->signtime));
894		printf("Geofeed not before:       %s\n",
895		    time2str(p->notbefore));
896		printf("Geofeed not after:        %s\n", time2str(p->notafter));
897		printf("Geofeed CSV records:      ");
898	}
899
900	for (i = 0; i < p->geoipsz; i++) {
901		if (p->geoips[i].ip->type != CERT_IP_ADDR)
902			continue;
903
904		ip_addr_print(&p->geoips[i].ip->ip, p->geoips[i].ip->afi, buf,
905		    sizeof(buf));
906		if (outformats & FORMAT_JSON) {
907			json_do_object("geoip", 1);
908			json_do_string("prefix", buf);
909			json_do_string("location", p->geoips[i].loc);
910			json_do_end();
911		} else {
912			if (i > 0)
913				printf("%26s", "");
914			printf("IP: %s (%s)\n", buf, p->geoips[i].loc);
915		}
916	}
917
918	if (outformats & FORMAT_JSON)
919		json_do_end();
920}
921