xref: /openbsd/usr.sbin/rpki-client/print.c (revision c527cc7a)
1*c527cc7aSjob /*	$OpenBSD: print.c,v 1.45 2024/01/18 14:34:26 job Exp $ */
2714f4e3fSclaudio /*
3714f4e3fSclaudio  * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
4714f4e3fSclaudio  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5714f4e3fSclaudio  *
6714f4e3fSclaudio  * Permission to use, copy, modify, and distribute this software for any
7714f4e3fSclaudio  * purpose with or without fee is hereby granted, provided that the above
8714f4e3fSclaudio  * copyright notice and this permission notice appear in all copies.
9714f4e3fSclaudio  *
10714f4e3fSclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11714f4e3fSclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12714f4e3fSclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13714f4e3fSclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14714f4e3fSclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15714f4e3fSclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16714f4e3fSclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17714f4e3fSclaudio  */
18714f4e3fSclaudio 
19714f4e3fSclaudio #include <sys/types.h>
20714f4e3fSclaudio #include <sys/socket.h>
21714f4e3fSclaudio #include <arpa/inet.h>
22714f4e3fSclaudio 
23714f4e3fSclaudio #include <err.h>
24714f4e3fSclaudio #include <stdio.h>
25714f4e3fSclaudio #include <string.h>
26714f4e3fSclaudio #include <time.h>
27714f4e3fSclaudio 
28e911df76Sjob #include <openssl/evp.h>
29e911df76Sjob 
30714f4e3fSclaudio #include "extern.h"
317aabcda0Sclaudio #include "json.h"
32714f4e3fSclaudio 
33714f4e3fSclaudio static const char *
34254fd372Sclaudio pretty_key_id(const char *hex)
35714f4e3fSclaudio {
36714f4e3fSclaudio 	static char buf[128];	/* bigger than SHA_DIGEST_LENGTH * 3 */
37714f4e3fSclaudio 	size_t i;
38714f4e3fSclaudio 
39714f4e3fSclaudio 	for (i = 0; i < sizeof(buf) && *hex != '\0'; i++) {
40254fd372Sclaudio 		if (i % 3 == 2)
41714f4e3fSclaudio 			buf[i] = ':';
42714f4e3fSclaudio 		else
43714f4e3fSclaudio 			buf[i] = *hex++;
44714f4e3fSclaudio 	}
45714f4e3fSclaudio 	if (i == sizeof(buf))
46714f4e3fSclaudio 		memcpy(buf + sizeof(buf) - 4, "...", 4);
47254fd372Sclaudio 	else
48254fd372Sclaudio 		buf[i] = '\0';
49714f4e3fSclaudio 	return buf;
50714f4e3fSclaudio }
51714f4e3fSclaudio 
52220c707cSclaudio char *
53220c707cSclaudio time2str(time_t t)
54220c707cSclaudio {
55220c707cSclaudio 	static char buf[64];
56220c707cSclaudio 	struct tm tm;
57220c707cSclaudio 
58220c707cSclaudio 	if (gmtime_r(&t, &tm) == NULL)
59220c707cSclaudio 		return "could not convert time";
60220c707cSclaudio 
61cc6f004eSjob 	strftime(buf, sizeof(buf), "%a %d %b %Y %T %z", &tm);
62cc6f004eSjob 
63220c707cSclaudio 	return buf;
64220c707cSclaudio }
65220c707cSclaudio 
66714f4e3fSclaudio void
67714f4e3fSclaudio tal_print(const struct tal *p)
68714f4e3fSclaudio {
69e911df76Sjob 	char			*ski;
70e911df76Sjob 	EVP_PKEY		*pk;
71e911df76Sjob 	RSA			*r;
72e911df76Sjob 	const unsigned char	*der;
73e911df76Sjob 	unsigned char		*rder = NULL;
74e911df76Sjob 	unsigned char		 md[SHA_DIGEST_LENGTH];
75e911df76Sjob 	int			 rder_len;
76714f4e3fSclaudio 	size_t			 i;
77714f4e3fSclaudio 
78e911df76Sjob 	der = p->pkey;
79e911df76Sjob 	pk = d2i_PUBKEY(NULL, &der, p->pkeysz);
80e911df76Sjob 	if (pk == NULL)
81e911df76Sjob 		errx(1, "d2i_PUBKEY failed in %s", __func__);
82e911df76Sjob 
83e911df76Sjob 	r = EVP_PKEY_get0_RSA(pk);
84e911df76Sjob 	if (r == NULL)
85e911df76Sjob 		errx(1, "EVP_PKEY_get0_RSA failed in %s", __func__);
86e911df76Sjob 	if ((rder_len = i2d_RSAPublicKey(r, &rder)) <= 0)
87e911df76Sjob 		errx(1, "i2d_RSAPublicKey failed in %s", __func__);
88e911df76Sjob 
89e911df76Sjob 	if (!EVP_Digest(rder, rder_len, md, NULL, EVP_sha1(), NULL))
90e911df76Sjob 		errx(1, "EVP_Digest failed in %s", __func__);
91e911df76Sjob 
92e911df76Sjob 	ski = hex_encode(md, SHA_DIGEST_LENGTH);
93e911df76Sjob 
94530399e8Sjob 	if (outformats & FORMAT_JSON) {
957aabcda0Sclaudio 		json_do_string("type", "tal");
967aabcda0Sclaudio 		json_do_string("name", p->descr);
977aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(ski));
987aabcda0Sclaudio 		json_do_array("trust_anchor_locations");
997aabcda0Sclaudio 		for (i = 0; i < p->urisz; i++)
1007aabcda0Sclaudio 			json_do_string("tal", p->uri[i]);
1017aabcda0Sclaudio 		json_do_end();
102530399e8Sjob 	} else {
103530399e8Sjob 		printf("Trust anchor name:        %s\n", p->descr);
104530399e8Sjob 		printf("Subject key identifier:   %s\n", pretty_key_id(ski));
1054486d057Sjob 		printf("Trust anchor locations:   ");
1064486d057Sjob 		for (i = 0; i < p->urisz; i++) {
1074486d057Sjob 			if (i > 0)
1084486d057Sjob 				printf("%26s", "");
1094486d057Sjob 			printf("%s\n", p->uri[i]);
1104486d057Sjob 		}
111530399e8Sjob 	}
112e911df76Sjob 
113e911df76Sjob 	EVP_PKEY_free(pk);
114e911df76Sjob 	free(rder);
115e911df76Sjob 	free(ski);
116714f4e3fSclaudio }
117714f4e3fSclaudio 
118714f4e3fSclaudio void
119530399e8Sjob x509_print(const X509 *x)
120530399e8Sjob {
121530399e8Sjob 	const ASN1_INTEGER	*xserial;
122e0b87278Sjob 	const X509_NAME		*xissuer;
123e0b87278Sjob 	char			*issuer = NULL;
124530399e8Sjob 	char			*serial = NULL;
125530399e8Sjob 
126e0b87278Sjob 	if ((xissuer = X509_get_issuer_name(x)) == NULL) {
127e0b87278Sjob 		warnx("X509_get_issuer_name failed");
128530399e8Sjob 		goto out;
129530399e8Sjob 	}
130530399e8Sjob 
131e0b87278Sjob 	if ((issuer = X509_NAME_oneline(xissuer, NULL, 0)) == NULL) {
132e0b87278Sjob 		warnx("X509_NAME_oneline failed");
133530399e8Sjob 		goto out;
134530399e8Sjob 	}
135530399e8Sjob 
136e0b87278Sjob 	if ((xserial = X509_get0_serialNumber(x)) == NULL) {
137e0b87278Sjob 		warnx("X509_get0_serialNumber failed");
138e0b87278Sjob 		goto out;
139e0b87278Sjob 	}
140e0b87278Sjob 
141e0b87278Sjob 	if ((serial = x509_convert_seqnum(__func__, xserial)) == NULL)
142e0b87278Sjob 		goto out;
143e0b87278Sjob 
144530399e8Sjob 	if (outformats & FORMAT_JSON) {
1457aabcda0Sclaudio 		json_do_string("cert_issuer", issuer);
1467aabcda0Sclaudio 		json_do_string("cert_serial", serial);
147530399e8Sjob 	} else {
148e0b87278Sjob 		printf("Certificate issuer:       %s\n", issuer);
149530399e8Sjob 		printf("Certificate serial:       %s\n", serial);
150530399e8Sjob 	}
151530399e8Sjob 
152530399e8Sjob  out:
153e0b87278Sjob 	free(issuer);
154530399e8Sjob 	free(serial);
155530399e8Sjob }
156530399e8Sjob 
1577aabcda0Sclaudio static void
1587aabcda0Sclaudio as_resources_print(struct cert_as *as, size_t asz)
1597aabcda0Sclaudio {
1607aabcda0Sclaudio 	size_t i;
1617aabcda0Sclaudio 
1627aabcda0Sclaudio 	for (i = 0; i < asz; i++) {
1637aabcda0Sclaudio 		if (outformats & FORMAT_JSON)
164a09a3191Sclaudio 			json_do_object("resource", 1);
1657aabcda0Sclaudio 		switch (as[i].type) {
1667aabcda0Sclaudio 		case CERT_AS_ID:
1677aabcda0Sclaudio 			if (outformats & FORMAT_JSON) {
1687aabcda0Sclaudio 				json_do_uint("asid", as[i].id);
1697aabcda0Sclaudio 			} else {
1707aabcda0Sclaudio 				if (i > 0)
1717aabcda0Sclaudio 					printf("%26s", "");
1727aabcda0Sclaudio 				printf("AS: %u", as[i].id);
1737aabcda0Sclaudio 			}
1747aabcda0Sclaudio 			break;
1757aabcda0Sclaudio 		case CERT_AS_INHERIT:
1767aabcda0Sclaudio 			if (outformats & FORMAT_JSON) {
1777aabcda0Sclaudio 				json_do_bool("asid_inherit", 1);
1787aabcda0Sclaudio 			} else {
1797aabcda0Sclaudio 				if (i > 0)
1807aabcda0Sclaudio 					printf("%26s", "");
1817aabcda0Sclaudio 				printf("AS: inherit");
1827aabcda0Sclaudio 			}
1837aabcda0Sclaudio 			break;
1847aabcda0Sclaudio 		case CERT_AS_RANGE:
1857aabcda0Sclaudio 			if (outformats & FORMAT_JSON) {
186a09a3191Sclaudio 				json_do_object("asrange", 1);
1877aabcda0Sclaudio 				json_do_uint("min", as[i].range.min);
1887aabcda0Sclaudio 				json_do_uint("max", as[i].range.max);
1897aabcda0Sclaudio 				json_do_end();
1907aabcda0Sclaudio 			} else {
1917aabcda0Sclaudio 				if (i > 0)
1927aabcda0Sclaudio 					printf("%26s", "");
1937aabcda0Sclaudio 				printf("AS: %u -- %u", as[i].range.min,
1947aabcda0Sclaudio 				    as[i].range.max);
1957aabcda0Sclaudio 			}
1967aabcda0Sclaudio 			break;
1977aabcda0Sclaudio 		}
1987aabcda0Sclaudio 		if (outformats & FORMAT_JSON)
1997aabcda0Sclaudio 			json_do_end();
2007aabcda0Sclaudio 		else
2017aabcda0Sclaudio 			printf("\n");
2027aabcda0Sclaudio 	}
2037aabcda0Sclaudio }
2047aabcda0Sclaudio 
2057aabcda0Sclaudio static void
2067aabcda0Sclaudio ip_resources_print(struct cert_ip *ips, size_t ipsz, size_t asz)
2077aabcda0Sclaudio {
2087aabcda0Sclaudio 	char buf1[64], buf2[64];
2097aabcda0Sclaudio 	size_t i;
2107aabcda0Sclaudio 	int sockt;
2117aabcda0Sclaudio 
2127aabcda0Sclaudio 
2137aabcda0Sclaudio 	for (i = 0; i < ipsz; i++) {
2147aabcda0Sclaudio 		if (outformats & FORMAT_JSON)
215a09a3191Sclaudio 			json_do_object("resource", 1);
2167aabcda0Sclaudio 		switch (ips[i].type) {
2177aabcda0Sclaudio 		case CERT_IP_INHERIT:
2187aabcda0Sclaudio 			if (outformats & FORMAT_JSON) {
2197aabcda0Sclaudio 				json_do_bool("ip_inherit", 1);
2207aabcda0Sclaudio 			} else {
2217aabcda0Sclaudio 				if (i > 0 || asz > 0)
2227aabcda0Sclaudio 					printf("%26s", "");
2237aabcda0Sclaudio 				printf("IP: inherit");
2247aabcda0Sclaudio 			}
2257aabcda0Sclaudio 			break;
2267aabcda0Sclaudio 		case CERT_IP_ADDR:
2277aabcda0Sclaudio 			ip_addr_print(&ips[i].ip, ips[i].afi, buf1,
2287aabcda0Sclaudio 			    sizeof(buf1));
2297aabcda0Sclaudio 			if (outformats & FORMAT_JSON) {
2307aabcda0Sclaudio 				json_do_string("ip_prefix", buf1);
2317aabcda0Sclaudio 			} else {
2327aabcda0Sclaudio 				if (i > 0 || asz > 0)
2337aabcda0Sclaudio 					printf("%26s", "");
2347aabcda0Sclaudio 				printf("IP: %s", buf1);
2357aabcda0Sclaudio 			}
2367aabcda0Sclaudio 			break;
2377aabcda0Sclaudio 		case CERT_IP_RANGE:
2387aabcda0Sclaudio 			sockt = (ips[i].afi == AFI_IPV4) ?
2397aabcda0Sclaudio 			    AF_INET : AF_INET6;
2407aabcda0Sclaudio 			inet_ntop(sockt, ips[i].min, buf1, sizeof(buf1));
2417aabcda0Sclaudio 			inet_ntop(sockt, ips[i].max, buf2, sizeof(buf2));
2427aabcda0Sclaudio 			if (outformats & FORMAT_JSON) {
243a09a3191Sclaudio 				json_do_object("ip_range", 1);
2447aabcda0Sclaudio 				json_do_string("min", buf1);
2457aabcda0Sclaudio 				json_do_string("max", buf2);
2467aabcda0Sclaudio 				json_do_end();
2477aabcda0Sclaudio 			} else {
2487aabcda0Sclaudio 				if (i > 0 || asz > 0)
2497aabcda0Sclaudio 					printf("%26s", "");
2507aabcda0Sclaudio 				printf("IP: %s -- %s", buf1, buf2);
2517aabcda0Sclaudio 			}
2527aabcda0Sclaudio 			break;
2537aabcda0Sclaudio 		}
2547aabcda0Sclaudio 		if (outformats & FORMAT_JSON)
2557aabcda0Sclaudio 			json_do_end();
2567aabcda0Sclaudio 		else
2577aabcda0Sclaudio 			printf("\n");
2587aabcda0Sclaudio 	}
2597aabcda0Sclaudio }
2607aabcda0Sclaudio 
261530399e8Sjob void
262714f4e3fSclaudio cert_print(const struct cert *p)
263714f4e3fSclaudio {
264530399e8Sjob 	if (outformats & FORMAT_JSON) {
265530399e8Sjob 		if (p->pubkey != NULL)
2667aabcda0Sclaudio 			json_do_string("type", "router_key");
267530399e8Sjob 		else
2687aabcda0Sclaudio 			json_do_string("type", "ca_cert");
2697aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
270530399e8Sjob 		if (p->aki != NULL)
2717aabcda0Sclaudio 			json_do_string("aki", pretty_key_id(p->aki));
272530399e8Sjob 		x509_print(p->x509);
273530399e8Sjob 		if (p->aia != NULL)
2747aabcda0Sclaudio 			json_do_string("aia", p->aia);
275530399e8Sjob 		if (p->mft != NULL)
2767aabcda0Sclaudio 			json_do_string("manifest", p->mft);
277530399e8Sjob 		if (p->repo != NULL)
2787aabcda0Sclaudio 			json_do_string("carepository", p->repo);
279530399e8Sjob 		if (p->notify != NULL)
2807aabcda0Sclaudio 			json_do_string("notify_url", p->notify);
281530399e8Sjob 		if (p->pubkey != NULL)
2827aabcda0Sclaudio 			json_do_string("router_key", p->pubkey);
2837aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
2847aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
285894936b4Sjob 		if (p->expires)
2867aabcda0Sclaudio 			json_do_int("expires", p->expires);
2877aabcda0Sclaudio 		json_do_array("subordinate_resources");
288530399e8Sjob 	} else {
289714f4e3fSclaudio 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
290714f4e3fSclaudio 		if (p->aki != NULL)
29167642cb5Stb 			printf("Authority key identifier: %s\n",
29267642cb5Stb 			    pretty_key_id(p->aki));
293530399e8Sjob 		x509_print(p->x509);
294714f4e3fSclaudio 		if (p->aia != NULL)
295714f4e3fSclaudio 			printf("Authority info access:    %s\n", p->aia);
296714f4e3fSclaudio 		if (p->mft != NULL)
297714f4e3fSclaudio 			printf("Manifest:                 %s\n", p->mft);
298714f4e3fSclaudio 		if (p->repo != NULL)
299714f4e3fSclaudio 			printf("caRepository:             %s\n", p->repo);
300714f4e3fSclaudio 		if (p->notify != NULL)
301714f4e3fSclaudio 			printf("Notify URL:               %s\n", p->notify);
30214d83341Sjob 		if (p->pubkey != NULL) {
3036530cf17Sjob 			printf("BGPsec ECDSA public key:  %s\n",
30467642cb5Stb 			    p->pubkey);
3054dbb22b8Sjob 			printf("Router key not before:    %s\n",
306f5999ddfSjob 			    time2str(p->notbefore));
3074dbb22b8Sjob 			printf("Router key not after:     %s\n",
3089f544822Sjob 			    time2str(p->notafter));
309f5999ddfSjob 		} else {
3104dbb22b8Sjob 			printf("Certificate not before:   %s\n",
311f5999ddfSjob 			    time2str(p->notbefore));
3124dbb22b8Sjob 			printf("Certificate not after:    %s\n",
3139f544822Sjob 			    time2str(p->notafter));
314f5999ddfSjob 		}
3154486d057Sjob 		printf("Subordinate resources:    ");
316530399e8Sjob 	}
317714f4e3fSclaudio 
3187aabcda0Sclaudio 	as_resources_print(p->as, p->asz);
3197aabcda0Sclaudio 	ip_resources_print(p->ips, p->ipsz, p->asz);
320530399e8Sjob 
321530399e8Sjob 	if (outformats & FORMAT_JSON)
3227aabcda0Sclaudio 		json_do_end();
323714f4e3fSclaudio }
324714f4e3fSclaudio 
325714f4e3fSclaudio void
326220c707cSclaudio crl_print(const struct crl *p)
327220c707cSclaudio {
328220c707cSclaudio 	STACK_OF(X509_REVOKED)	*revlist;
329220c707cSclaudio 	X509_REVOKED *rev;
330e0b87278Sjob 	X509_NAME *xissuer;
331220c707cSclaudio 	int i;
332e0b87278Sjob 	char *issuer, *serial;
333220c707cSclaudio 	time_t t;
334220c707cSclaudio 
335530399e8Sjob 	if (outformats & FORMAT_JSON) {
3367aabcda0Sclaudio 		json_do_string("type", "crl");
3377aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
338530399e8Sjob 	} else
339220c707cSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
3407cdd491fSclaudio 
341e0b87278Sjob 	xissuer = X509_CRL_get_issuer(p->x509_crl);
342e0b87278Sjob 	issuer = X509_NAME_oneline(xissuer, NULL, 0);
3430a722356Stb 	if (issuer != NULL && p->number != NULL) {
344e0b87278Sjob 		if (outformats & FORMAT_JSON) {
3457aabcda0Sclaudio 			json_do_string("crl_issuer", issuer);
3460a722356Stb 			json_do_string("crl_serial", p->number);
347e0b87278Sjob 		} else {
348e0b87278Sjob 			printf("CRL issuer:               %s\n", issuer);
3490a722356Stb 			printf("CRL serial number:        %s\n", p->number);
350530399e8Sjob 		}
351e0b87278Sjob 	}
352e0b87278Sjob 	free(issuer);
3537cdd491fSclaudio 
354530399e8Sjob 	if (outformats & FORMAT_JSON) {
355*c527cc7aSjob 		json_do_int("valid_since", p->thisupdate);
3567aabcda0Sclaudio 		json_do_int("valid_until", p->nextupdate);
3577aabcda0Sclaudio 		json_do_array("revoked_certs");
358530399e8Sjob 	} else {
359*c527cc7aSjob 		printf("CRL this update:          %s\n",
360*c527cc7aSjob 		    time2str(p->thisupdate));
3614dbb22b8Sjob 		printf("CRL next update:          %s\n",
3629f544822Sjob 		    time2str(p->nextupdate));
363530399e8Sjob 		printf("Revoked Certificates:\n");
364530399e8Sjob 	}
365220c707cSclaudio 
366220c707cSclaudio 	revlist = X509_CRL_get_REVOKED(p->x509_crl);
367220c707cSclaudio 	for (i = 0; i < sk_X509_REVOKED_num(revlist); i++) {
368220c707cSclaudio 		rev = sk_X509_REVOKED_value(revlist, i);
3697cdd491fSclaudio 		serial = x509_convert_seqnum(__func__,
3707cdd491fSclaudio 		    X509_REVOKED_get0_serialNumber(rev));
371220c707cSclaudio 		x509_get_time(X509_REVOKED_get0_revocationDate(rev), &t);
372530399e8Sjob 		if (serial != NULL) {
373530399e8Sjob 			if (outformats & FORMAT_JSON) {
374a09a3191Sclaudio 				json_do_object("cert", 1);
3757aabcda0Sclaudio 				json_do_string("serial", serial);
3767aabcda0Sclaudio 				json_do_string("date", time2str(t));
3777aabcda0Sclaudio 				json_do_end();
378530399e8Sjob 			} else
3794486d057Sjob 				printf("%25s Serial: %8s   Revocation Date: %s"
3804486d057Sjob 				    "\n", "", serial, time2str(t));
381530399e8Sjob 		}
3827cdd491fSclaudio 		free(serial);
383220c707cSclaudio 	}
384530399e8Sjob 
385530399e8Sjob 	if (outformats & FORMAT_JSON)
3867aabcda0Sclaudio 		json_do_end();
387530399e8Sjob 	else if (i == 0)
388220c707cSclaudio 		printf("No Revoked Certificates\n");
389220c707cSclaudio }
390220c707cSclaudio 
391220c707cSclaudio void
392530399e8Sjob mft_print(const X509 *x, const struct mft *p)
393714f4e3fSclaudio {
394714f4e3fSclaudio 	size_t i;
395714f4e3fSclaudio 	char *hash;
396714f4e3fSclaudio 
397530399e8Sjob 	if (outformats & FORMAT_JSON) {
3987aabcda0Sclaudio 		json_do_string("type", "manifest");
3997aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
400530399e8Sjob 		x509_print(x);
4017aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
4027aabcda0Sclaudio 		json_do_string("aia", p->aia);
4037aabcda0Sclaudio 		json_do_string("sia", p->sia);
4047aabcda0Sclaudio 		json_do_string("manifest_number", p->seqnum);
4051bb1e509Sjob 		if (p->signtime != 0)
4067aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
4077aabcda0Sclaudio 		json_do_int("valid_since", p->thisupdate);
4087aabcda0Sclaudio 		json_do_int("valid_until", p->nextupdate);
409894936b4Sjob 		if (p->expires)
4107aabcda0Sclaudio 			json_do_int("expires", p->expires);
411530399e8Sjob 	} else {
412714f4e3fSclaudio 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
413714f4e3fSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
414530399e8Sjob 		x509_print(x);
415714f4e3fSclaudio 		printf("Authority info access:    %s\n", p->aia);
4162cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
417489e308aSjob 		printf("Manifest number:          %s\n", p->seqnum);
4181bb1e509Sjob 		if (p->signtime != 0)
4191bb1e509Sjob 			printf("Signing time:             %s\n",
4201bb1e509Sjob 			    time2str(p->signtime));
4214dbb22b8Sjob 		printf("Manifest this update:     %s\n", time2str(p->thisupdate));
4224dbb22b8Sjob 		printf("Manifest next update:     %s\n", time2str(p->nextupdate));
4234486d057Sjob 		printf("Files and hashes:         ");
424530399e8Sjob 	}
425530399e8Sjob 
4267aabcda0Sclaudio 	if (outformats & FORMAT_JSON)
4277aabcda0Sclaudio 		json_do_array("filesandhashes");
428714f4e3fSclaudio 	for (i = 0; i < p->filesz; i++) {
429714f4e3fSclaudio 		if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash),
430714f4e3fSclaudio 		    &hash) == -1)
431714f4e3fSclaudio 			errx(1, "base64_encode failure");
432530399e8Sjob 
433530399e8Sjob 		if (outformats & FORMAT_JSON) {
434a09a3191Sclaudio 			json_do_object("filehash", 1);
4357aabcda0Sclaudio 			json_do_string("filename", p->files[i].file);
4367aabcda0Sclaudio 			json_do_string("hash", hash);
4377aabcda0Sclaudio 			json_do_end();
438530399e8Sjob 		} else {
4394486d057Sjob 			if (i > 0)
4404486d057Sjob 				printf("%26s", "");
4414486d057Sjob 			printf("%zu: %s (hash: %s)\n", i + 1, p->files[i].file,
4424486d057Sjob 			    hash);
443530399e8Sjob 		}
444530399e8Sjob 
445714f4e3fSclaudio 		free(hash);
446714f4e3fSclaudio 	}
447530399e8Sjob 	if (outformats & FORMAT_JSON)
4487aabcda0Sclaudio 		json_do_end();
449714f4e3fSclaudio }
450714f4e3fSclaudio 
451714f4e3fSclaudio void
452530399e8Sjob roa_print(const X509 *x, const struct roa *p)
453714f4e3fSclaudio {
454714f4e3fSclaudio 	char	 buf[128];
455714f4e3fSclaudio 	size_t	 i;
456714f4e3fSclaudio 
457530399e8Sjob 	if (outformats & FORMAT_JSON) {
4587aabcda0Sclaudio 		json_do_string("type", "roa");
4597aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
460530399e8Sjob 		x509_print(x);
4617aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
4627aabcda0Sclaudio 		json_do_string("aia", p->aia);
4637aabcda0Sclaudio 		json_do_string("sia", p->sia);
4641bb1e509Sjob 		if (p->signtime != 0)
4657aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
4667aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
4677aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
4689c1f5d6bSjob 		if (p->expires)
4697aabcda0Sclaudio 			json_do_int("expires", p->expires);
470530399e8Sjob 	} else {
471714f4e3fSclaudio 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
472530399e8Sjob 		x509_print(x);
473714f4e3fSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
474714f4e3fSclaudio 		printf("Authority info access:    %s\n", p->aia);
4752cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
4761bb1e509Sjob 		if (p->signtime != 0)
4771bb1e509Sjob 			printf("Signing time:             %s\n",
4781bb1e509Sjob 			    time2str(p->signtime));
4794dbb22b8Sjob 		printf("ROA not before:           %s\n",
480f5999ddfSjob 		    time2str(p->notbefore));
4814dbb22b8Sjob 		printf("ROA not after:            %s\n", time2str(p->notafter));
482714f4e3fSclaudio 		printf("asID:                     %u\n", p->asid);
4834486d057Sjob 		printf("IP address blocks:        ");
484530399e8Sjob 	}
485530399e8Sjob 
4867aabcda0Sclaudio 	if (outformats & FORMAT_JSON)
4877aabcda0Sclaudio 		json_do_array("vrps");
488714f4e3fSclaudio 	for (i = 0; i < p->ipsz; i++) {
489714f4e3fSclaudio 		ip_addr_print(&p->ips[i].addr,
490714f4e3fSclaudio 		    p->ips[i].afi, buf, sizeof(buf));
491530399e8Sjob 
492530399e8Sjob 		if (outformats & FORMAT_JSON) {
493a09a3191Sclaudio 			json_do_object("vrp", 1);
4947aabcda0Sclaudio 			json_do_string("prefix", buf);
4957aabcda0Sclaudio 			json_do_uint("asid", p->asid);
4967aabcda0Sclaudio 			json_do_uint("maxlen", p->ips[i].maxlength);
4977aabcda0Sclaudio 			json_do_end();
4984486d057Sjob 		} else {
4994486d057Sjob 			if (i > 0)
5004486d057Sjob 				printf("%26s", "");
5014486d057Sjob 			printf("%s maxlen: %hhu\n", buf, p->ips[i].maxlength);
5024486d057Sjob 		}
503714f4e3fSclaudio 	}
504530399e8Sjob 	if (outformats & FORMAT_JSON)
5057aabcda0Sclaudio 		json_do_end();
506714f4e3fSclaudio }
507714f4e3fSclaudio 
508714f4e3fSclaudio void
509530399e8Sjob gbr_print(const X509 *x, const struct gbr *p)
510714f4e3fSclaudio {
511530399e8Sjob 	if (outformats & FORMAT_JSON) {
5127aabcda0Sclaudio 		json_do_string("type", "gbr");
5137aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
514530399e8Sjob 		x509_print(x);
5157aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
5167aabcda0Sclaudio 		json_do_string("aia", p->aia);
5177aabcda0Sclaudio 		json_do_string("sia", p->sia);
5181bb1e509Sjob 		if (p->signtime != 0)
5197aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
5207aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
5217aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
522894936b4Sjob 		if (p->expires)
5237aabcda0Sclaudio 			json_do_int("expires", p->expires);
5247aabcda0Sclaudio 		json_do_string("vcard", p->vcard);
525530399e8Sjob 	} else {
526714f4e3fSclaudio 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
527530399e8Sjob 		x509_print(x);
528714f4e3fSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
529714f4e3fSclaudio 		printf("Authority info access:    %s\n", p->aia);
5302cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
5311bb1e509Sjob 		if (p->signtime != 0)
5321bb1e509Sjob 			printf("Signing time:             %s\n",
5331bb1e509Sjob 			    time2str(p->signtime));
5344dbb22b8Sjob 		printf("GBR not before:           %s\n",
535f5999ddfSjob 		    time2str(p->notbefore));
5364dbb22b8Sjob 		printf("GBR not after:            %s\n", time2str(p->notafter));
537714f4e3fSclaudio 		printf("vcard:\n%s", p->vcard);
538714f4e3fSclaudio 	}
539530399e8Sjob }
54004834fbdSjob 
54104834fbdSjob void
54204834fbdSjob rsc_print(const X509 *x, const struct rsc *p)
54304834fbdSjob {
54404834fbdSjob 	char	*hash;
5457aabcda0Sclaudio 	size_t	 i;
54604834fbdSjob 
54704834fbdSjob 	if (outformats & FORMAT_JSON) {
5487aabcda0Sclaudio 		json_do_string("type", "rsc");
5497aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
55004834fbdSjob 		x509_print(x);
5517aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
5527aabcda0Sclaudio 		json_do_string("aia", p->aia);
5531bb1e509Sjob 		if (p->signtime != 0)
5547aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
5557aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
5567aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
557894936b4Sjob 		if (p->expires)
5587aabcda0Sclaudio 			json_do_int("expires", p->expires);
5597aabcda0Sclaudio 		json_do_array("signed_with_resources");
56004834fbdSjob 	} else {
56104834fbdSjob 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
56204834fbdSjob 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
56304834fbdSjob 		x509_print(x);
56404834fbdSjob 		printf("Authority info access:    %s\n", p->aia);
5651bb1e509Sjob 		if (p->signtime != 0)
5661bb1e509Sjob 			printf("Signing time:             %s\n",
5671bb1e509Sjob 			    time2str(p->signtime));
5684dbb22b8Sjob 		printf("RSC not before:           %s\n",
569f5999ddfSjob 		    time2str(p->notbefore));
5704dbb22b8Sjob 		printf("RSC not after:            %s\n", time2str(p->notafter));
5714486d057Sjob 		printf("Signed with resources:    ");
57204834fbdSjob 	}
57304834fbdSjob 
5747aabcda0Sclaudio 	as_resources_print(p->as, p->asz);
5757aabcda0Sclaudio 	ip_resources_print(p->ips, p->ipsz, p->asz);
57604834fbdSjob 
57704834fbdSjob 	if (outformats & FORMAT_JSON) {
5787aabcda0Sclaudio 		json_do_end();
5797aabcda0Sclaudio 		json_do_array("filenamesandhashes");
58004834fbdSjob 	} else
5814486d057Sjob 		printf("Filenames and hashes:     ");
58204834fbdSjob 
58304834fbdSjob 	for (i = 0; i < p->filesz; i++) {
58404834fbdSjob 		if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash),
58504834fbdSjob 		    &hash) == -1)
58604834fbdSjob 			errx(1, "base64_encode failure");
58704834fbdSjob 
58804834fbdSjob 		if (outformats & FORMAT_JSON) {
589a09a3191Sclaudio 			json_do_object("filehash", 1);
5907aabcda0Sclaudio 			if (p->files[i].filename)
5917aabcda0Sclaudio 				json_do_string("filename",
5927aabcda0Sclaudio 				    p->files[i].filename);
5937aabcda0Sclaudio 			json_do_string("hash_digest", hash);
5947aabcda0Sclaudio 			json_do_end();
59504834fbdSjob 		} else {
5964486d057Sjob 			if (i > 0)
5974486d057Sjob 				printf("%26s", "");
5984486d057Sjob 			printf("%zu: %s (hash: %s)\n", i + 1,
5994486d057Sjob 			    p->files[i].filename ? p->files[i].filename
6004486d057Sjob 			    : "no filename", hash);
60104834fbdSjob 		}
60204834fbdSjob 
60304834fbdSjob 		free(hash);
60404834fbdSjob 	}
60504834fbdSjob 
60604834fbdSjob 	if (outformats & FORMAT_JSON)
6077aabcda0Sclaudio 		json_do_end();
60804834fbdSjob }
609a29ddfd5Sjob 
610a29ddfd5Sjob void
611a29ddfd5Sjob aspa_print(const X509 *x, const struct aspa *p)
612a29ddfd5Sjob {
6134b5fc138Sjob 	size_t	i;
6144b5fc138Sjob 
615a29ddfd5Sjob 	if (outformats & FORMAT_JSON) {
6167aabcda0Sclaudio 		json_do_string("type", "aspa");
6177aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
618a29ddfd5Sjob 		x509_print(x);
6197aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
6207aabcda0Sclaudio 		json_do_string("aia", p->aia);
6217aabcda0Sclaudio 		json_do_string("sia", p->sia);
6221bb1e509Sjob 		if (p->signtime != 0)
6237aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
6247aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
6257aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
6269c1f5d6bSjob 		if (p->expires)
6277aabcda0Sclaudio 			json_do_int("expires", p->expires);
6287aabcda0Sclaudio 		json_do_uint("customer_asid", p->custasid);
6294a0be80eSjob 		json_do_array("providers");
630a29ddfd5Sjob 	} else {
631a29ddfd5Sjob 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
632a29ddfd5Sjob 		x509_print(x);
633a29ddfd5Sjob 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
634a29ddfd5Sjob 		printf("Authority info access:    %s\n", p->aia);
6352cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
6361bb1e509Sjob 		if (p->signtime != 0)
6371bb1e509Sjob 			printf("Signing time:             %s\n",
6381bb1e509Sjob 			    time2str(p->signtime));
6394dbb22b8Sjob 		printf("ASPA not before:          %s\n",
640f5999ddfSjob 		    time2str(p->notbefore));
6414dbb22b8Sjob 		printf("ASPA not after:           %s\n", time2str(p->notafter));
642ba54bc08Sjob 		printf("Customer ASID:            %u\n", p->custasid);
6434a0be80eSjob 		printf("Providers:                ");
644a29ddfd5Sjob 	}
6457aabcda0Sclaudio 
6464b5fc138Sjob 	for (i = 0; i < p->providersz; i++) {
647b179db0eSjob 		if (outformats & FORMAT_JSON)
648b179db0eSjob 			json_do_uint("asid", p->providers[i]);
649b179db0eSjob 		else {
650b179db0eSjob 			if (i > 0)
6514b5fc138Sjob 				printf("%26s", "");
652b179db0eSjob 			printf("AS: %u\n", p->providers[i]);
653b179db0eSjob 		}
6544b5fc138Sjob 	}
6557aabcda0Sclaudio 
6567aabcda0Sclaudio 	if (outformats & FORMAT_JSON)
6577aabcda0Sclaudio 		json_do_end();
658a29ddfd5Sjob }
659ee2a33daSjob 
660ee2a33daSjob static void
661ee2a33daSjob takey_print(char *name, const struct takey *t)
662ee2a33daSjob {
663ee2a33daSjob 	char	*spki = NULL;
664ee2a33daSjob 	size_t	 i, j = 0;
665ee2a33daSjob 
666ee2a33daSjob 	if (base64_encode(t->pubkey, t->pubkeysz, &spki) != 0)
667ee2a33daSjob 		errx(1, "base64_encode failed in %s", __func__);
668ee2a33daSjob 
669ee2a33daSjob 	if (outformats & FORMAT_JSON) {
670a09a3191Sclaudio 		json_do_object("takey", 0);
6717aabcda0Sclaudio 		json_do_string("name", name);
6727aabcda0Sclaudio 		json_do_array("comments");
6737aabcda0Sclaudio 		for (i = 0; i < t->commentsz; i++)
6747aabcda0Sclaudio 			json_do_string("comment", t->comments[i]);
6757aabcda0Sclaudio 		json_do_end();
6767aabcda0Sclaudio 		json_do_array("uris");
6777aabcda0Sclaudio 		for (i = 0; i < t->urisz; i++)
6787aabcda0Sclaudio 			json_do_string("uri", t->uris[i]);
6797aabcda0Sclaudio 		json_do_end();
6807aabcda0Sclaudio 		json_do_string("spki", spki);
681ee2a33daSjob 	} else {
682ee2a33daSjob 		printf("TAL derived from the '%s' Trust Anchor Key:\n\n", name);
683ee2a33daSjob 
6847aabcda0Sclaudio 		for (i = 0; i < t->commentsz; i++)
685ee2a33daSjob 			printf("\t# %s\n", t->comments[i]);
6867aabcda0Sclaudio 		printf("\n");
6877aabcda0Sclaudio 		for (i = 0; i < t->urisz; i++)
6887aabcda0Sclaudio 			printf("\t%s\n\t", t->uris[i]);
689ee2a33daSjob 		for (i = 0; i < strlen(spki); i++) {
690ee2a33daSjob 			printf("%c", spki[i]);
6917aabcda0Sclaudio 			if ((++j % 64) == 0)
692ee2a33daSjob 				printf("\n\t");
693ee2a33daSjob 		}
694ee2a33daSjob 		printf("\n\n");
695ee2a33daSjob 	}
696ee2a33daSjob 
697ee2a33daSjob 	free(spki);
698ee2a33daSjob }
699ee2a33daSjob 
700ee2a33daSjob void
701ee2a33daSjob tak_print(const X509 *x, const struct tak *p)
702ee2a33daSjob {
703ee2a33daSjob 	if (outformats & FORMAT_JSON) {
7047aabcda0Sclaudio 		json_do_string("type", "tak");
7057aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
706ee2a33daSjob 		x509_print(x);
7077aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
7087aabcda0Sclaudio 		json_do_string("aia", p->aia);
7097aabcda0Sclaudio 		json_do_string("sia", p->sia);
7101bb1e509Sjob 		if (p->signtime != 0)
7117aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
7127aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
7137aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
714894936b4Sjob 		if (p->expires)
7157aabcda0Sclaudio 			json_do_int("expires", p->expires);
7167aabcda0Sclaudio 		json_do_array("takeys");
717ee2a33daSjob 	} else {
718ee2a33daSjob 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
719ee2a33daSjob 		x509_print(x);
720ee2a33daSjob 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
721ee2a33daSjob 		printf("Authority info access:    %s\n", p->aia);
7222cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
7231bb1e509Sjob 		if (p->signtime != 0)
7241bb1e509Sjob 			printf("Signing time:             %s\n",
7251bb1e509Sjob 			    time2str(p->signtime));
7264dbb22b8Sjob 		printf("TAK not before:           %s\n",
727f5999ddfSjob 		    time2str(p->notbefore));
7284dbb22b8Sjob 		printf("TAK not after:            %s\n", time2str(p->notafter));
729ee2a33daSjob 	}
730ee2a33daSjob 
731ee2a33daSjob 	takey_print("current", p->current);
7327aabcda0Sclaudio 	if (p->predecessor != NULL)
733ee2a33daSjob 		takey_print("predecessor", p->predecessor);
7347aabcda0Sclaudio 	if (p->successor != NULL)
735ee2a33daSjob 		takey_print("successor", p->successor);
736ee2a33daSjob 
737ee2a33daSjob 	if (outformats & FORMAT_JSON)
7387aabcda0Sclaudio 		json_do_end();
739ee2a33daSjob }
740ef3f6f56Sjob 
741ef3f6f56Sjob void
742ef3f6f56Sjob geofeed_print(const X509 *x, const struct geofeed *p)
743ef3f6f56Sjob {
744ef3f6f56Sjob 	char	 buf[128];
745ef3f6f56Sjob 	size_t	 i;
746ef3f6f56Sjob 
747ef3f6f56Sjob 	if (outformats & FORMAT_JSON) {
7487aabcda0Sclaudio 		json_do_string("type", "geofeed");
7497aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
750ef3f6f56Sjob 		x509_print(x);
7517aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
7527aabcda0Sclaudio 		json_do_string("aia", p->aia);
7531bb1e509Sjob 		if (p->signtime != 0)
7547aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
7557aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
7567aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
7577aabcda0Sclaudio 		if (p->expires)
7587aabcda0Sclaudio 			json_do_int("expires", p->expires);
7597aabcda0Sclaudio 		json_do_array("records");
760ef3f6f56Sjob 	} else {
761ef3f6f56Sjob 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
762ef3f6f56Sjob 		x509_print(x);
763ef3f6f56Sjob 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
764ef3f6f56Sjob 		printf("Authority info access:    %s\n", p->aia);
7651bb1e509Sjob 		if (p->signtime != 0)
7661bb1e509Sjob 			printf("Signing time:             %s\n",
7671bb1e509Sjob 			    time2str(p->signtime));
7684dbb22b8Sjob 		printf("Geofeed not before:       %s\n",
769f5999ddfSjob 		    time2str(p->notbefore));
7704dbb22b8Sjob 		printf("Geofeed not after:        %s\n", time2str(p->notafter));
771bd19f13dSjob 		printf("Geofeed CSV records:      ");
772ef3f6f56Sjob 	}
773ef3f6f56Sjob 
774ef3f6f56Sjob 	for (i = 0; i < p->geoipsz; i++) {
775ef3f6f56Sjob 		if (p->geoips[i].ip->type != CERT_IP_ADDR)
776ef3f6f56Sjob 			continue;
777ef3f6f56Sjob 
778ef3f6f56Sjob 		ip_addr_print(&p->geoips[i].ip->ip, p->geoips[i].ip->afi, buf,
779ef3f6f56Sjob 		    sizeof(buf));
7807aabcda0Sclaudio 		if (outformats & FORMAT_JSON) {
781a09a3191Sclaudio 			json_do_object("geoip", 1);
7827aabcda0Sclaudio 			json_do_string("prefix", buf);
7837aabcda0Sclaudio 			json_do_string("location", p->geoips[i].loc);
7847aabcda0Sclaudio 			json_do_end();
7857aabcda0Sclaudio 		} else {
7864486d057Sjob 			if (i > 0)
7874486d057Sjob 				printf("%26s", "");
7887aabcda0Sclaudio 			printf("IP: %s (%s)\n", buf, p->geoips[i].loc);
7894486d057Sjob 		}
790ef3f6f56Sjob 	}
791ef3f6f56Sjob 
792ef3f6f56Sjob 	if (outformats & FORMAT_JSON)
7937aabcda0Sclaudio 		json_do_end();
794ef3f6f56Sjob }
795