xref: /openbsd/usr.sbin/rpki-client/print.c (revision 489e308a)
1*489e308aSjob /*	$OpenBSD: print.c,v 1.36 2023/04/20 15:12:19 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"
31714f4e3fSclaudio 
32714f4e3fSclaudio static const char *
33254fd372Sclaudio pretty_key_id(const char *hex)
34714f4e3fSclaudio {
35714f4e3fSclaudio 	static char buf[128];	/* bigger than SHA_DIGEST_LENGTH * 3 */
36714f4e3fSclaudio 	size_t i;
37714f4e3fSclaudio 
38714f4e3fSclaudio 	for (i = 0; i < sizeof(buf) && *hex != '\0'; i++) {
39254fd372Sclaudio 		if (i % 3 == 2)
40714f4e3fSclaudio 			buf[i] = ':';
41714f4e3fSclaudio 		else
42714f4e3fSclaudio 			buf[i] = *hex++;
43714f4e3fSclaudio 	}
44714f4e3fSclaudio 	if (i == sizeof(buf))
45714f4e3fSclaudio 		memcpy(buf + sizeof(buf) - 4, "...", 4);
46254fd372Sclaudio 	else
47254fd372Sclaudio 		buf[i] = '\0';
48714f4e3fSclaudio 	return buf;
49714f4e3fSclaudio }
50714f4e3fSclaudio 
51220c707cSclaudio char *
52220c707cSclaudio time2str(time_t t)
53220c707cSclaudio {
54220c707cSclaudio 	static char buf[64];
55220c707cSclaudio 	struct tm tm;
56220c707cSclaudio 
57220c707cSclaudio 	if (gmtime_r(&t, &tm) == NULL)
58220c707cSclaudio 		return "could not convert time";
59220c707cSclaudio 
60cc6f004eSjob 	strftime(buf, sizeof(buf), "%a %d %b %Y %T %z", &tm);
61cc6f004eSjob 
62220c707cSclaudio 	return buf;
63220c707cSclaudio }
64220c707cSclaudio 
65714f4e3fSclaudio void
66714f4e3fSclaudio tal_print(const struct tal *p)
67714f4e3fSclaudio {
68e911df76Sjob 	char			*ski;
69e911df76Sjob 	EVP_PKEY		*pk;
70e911df76Sjob 	RSA			*r;
71e911df76Sjob 	const unsigned char	*der;
72e911df76Sjob 	unsigned char		*rder = NULL;
73e911df76Sjob 	unsigned char		 md[SHA_DIGEST_LENGTH];
74e911df76Sjob 	int			 rder_len;
75714f4e3fSclaudio 	size_t			 i;
76714f4e3fSclaudio 
77e911df76Sjob 	der = p->pkey;
78e911df76Sjob 	pk = d2i_PUBKEY(NULL, &der, p->pkeysz);
79e911df76Sjob 	if (pk == NULL)
80e911df76Sjob 		errx(1, "d2i_PUBKEY failed in %s", __func__);
81e911df76Sjob 
82e911df76Sjob 	r = EVP_PKEY_get0_RSA(pk);
83e911df76Sjob 	if (r == NULL)
84e911df76Sjob 		errx(1, "EVP_PKEY_get0_RSA failed in %s", __func__);
85e911df76Sjob 	if ((rder_len = i2d_RSAPublicKey(r, &rder)) <= 0)
86e911df76Sjob 		errx(1, "i2d_RSAPublicKey failed in %s", __func__);
87e911df76Sjob 
88e911df76Sjob 	if (!EVP_Digest(rder, rder_len, md, NULL, EVP_sha1(), NULL))
89e911df76Sjob 		errx(1, "EVP_Digest failed in %s", __func__);
90e911df76Sjob 
91e911df76Sjob 	ski = hex_encode(md, SHA_DIGEST_LENGTH);
92e911df76Sjob 
93530399e8Sjob 	if (outformats & FORMAT_JSON) {
94530399e8Sjob 		printf("\t\"type\": \"tal\",\n");
953b3f075aSjob 		printf("\t\"name\": \"%s\",\n", p->descr);
963b3f075aSjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(ski));
97530399e8Sjob 		printf("\t\"trust_anchor_locations\": [");
98530399e8Sjob 		for (i = 0; i < p->urisz; i++) {
99530399e8Sjob 			printf("\"%s\"", p->uri[i]);
100530399e8Sjob 			if (i + 1 < p->urisz)
101530399e8Sjob 				printf(", ");
102530399e8Sjob 		}
1033b3f075aSjob 		printf("],\n");
104530399e8Sjob 	} else {
105530399e8Sjob 		printf("Trust anchor name:        %s\n", p->descr);
106530399e8Sjob 		printf("Subject key identifier:   %s\n", pretty_key_id(ski));
1074486d057Sjob 		printf("Trust anchor locations:   ");
1084486d057Sjob 		for (i = 0; i < p->urisz; i++) {
1094486d057Sjob 			if (i > 0)
1104486d057Sjob 				printf("%26s", "");
1114486d057Sjob 			printf("%s\n", p->uri[i]);
1124486d057Sjob 		}
113530399e8Sjob 	}
114e911df76Sjob 
115e911df76Sjob 	EVP_PKEY_free(pk);
116e911df76Sjob 	free(rder);
117e911df76Sjob 	free(ski);
118714f4e3fSclaudio }
119714f4e3fSclaudio 
120714f4e3fSclaudio void
121530399e8Sjob x509_print(const X509 *x)
122530399e8Sjob {
123530399e8Sjob 	const ASN1_INTEGER	*xserial;
124e0b87278Sjob 	const X509_NAME		*xissuer;
125e0b87278Sjob 	char			*issuer = NULL;
126530399e8Sjob 	char			*serial = NULL;
127530399e8Sjob 
128e0b87278Sjob 	if ((xissuer = X509_get_issuer_name(x)) == NULL) {
129e0b87278Sjob 		warnx("X509_get_issuer_name failed");
130530399e8Sjob 		goto out;
131530399e8Sjob 	}
132530399e8Sjob 
133e0b87278Sjob 	if ((issuer = X509_NAME_oneline(xissuer, NULL, 0)) == NULL) {
134e0b87278Sjob 		warnx("X509_NAME_oneline failed");
135530399e8Sjob 		goto out;
136530399e8Sjob 	}
137530399e8Sjob 
138e0b87278Sjob 	if ((xserial = X509_get0_serialNumber(x)) == NULL) {
139e0b87278Sjob 		warnx("X509_get0_serialNumber failed");
140e0b87278Sjob 		goto out;
141e0b87278Sjob 	}
142e0b87278Sjob 
143e0b87278Sjob 	if ((serial = x509_convert_seqnum(__func__, xserial)) == NULL)
144e0b87278Sjob 		goto out;
145e0b87278Sjob 
146530399e8Sjob 	if (outformats & FORMAT_JSON) {
147e0b87278Sjob 		printf("\t\"cert_issuer\": \"%s\",\n", issuer);
148530399e8Sjob 		printf("\t\"cert_serial\": \"%s\",\n", serial);
149530399e8Sjob 	} else {
150e0b87278Sjob 		printf("Certificate issuer:       %s\n", issuer);
151530399e8Sjob 		printf("Certificate serial:       %s\n", serial);
152530399e8Sjob 	}
153530399e8Sjob 
154530399e8Sjob  out:
155e0b87278Sjob 	free(issuer);
156530399e8Sjob 	free(serial);
157530399e8Sjob }
158530399e8Sjob 
159530399e8Sjob void
160714f4e3fSclaudio cert_print(const struct cert *p)
161714f4e3fSclaudio {
162530399e8Sjob 	size_t			 i, j;
163714f4e3fSclaudio 	char			 buf1[64], buf2[64];
164714f4e3fSclaudio 	int			 sockt;
165530399e8Sjob 
166530399e8Sjob 	if (outformats & FORMAT_JSON) {
167530399e8Sjob 		if (p->pubkey != NULL)
168530399e8Sjob 			printf("\t\"type\": \"router_key\",\n");
169530399e8Sjob 		else
170530399e8Sjob 			printf("\t\"type\": \"ca_cert\",\n");
171530399e8Sjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
172530399e8Sjob 		if (p->aki != NULL)
173530399e8Sjob 			printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
174530399e8Sjob 		x509_print(p->x509);
175530399e8Sjob 		if (p->aia != NULL)
176530399e8Sjob 			printf("\t\"aia\": \"%s\",\n", p->aia);
177530399e8Sjob 		if (p->mft != NULL)
178530399e8Sjob 			printf("\t\"manifest\": \"%s\",\n", p->mft);
179530399e8Sjob 		if (p->repo != NULL)
180530399e8Sjob 			printf("\t\"carepository\": \"%s\",\n", p->repo);
181530399e8Sjob 		if (p->notify != NULL)
182530399e8Sjob 			printf("\t\"notify_url\": \"%s\",\n", p->notify);
183530399e8Sjob 		if (p->pubkey != NULL)
184530399e8Sjob 			printf("\t\"router_key\": \"%s\",\n", p->pubkey);
185f5999ddfSjob 		printf("\t\"valid_since\": %lld,\n", (long long)p->notbefore);
1869f544822Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->notafter);
187894936b4Sjob 		if (p->expires)
188894936b4Sjob 			printf("\t\"expires\": %lld,\n", (long long)p->expires);
189530399e8Sjob 		printf("\t\"subordinate_resources\": [\n");
190530399e8Sjob 	} else {
191714f4e3fSclaudio 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
192714f4e3fSclaudio 		if (p->aki != NULL)
19367642cb5Stb 			printf("Authority key identifier: %s\n",
19467642cb5Stb 			    pretty_key_id(p->aki));
195530399e8Sjob 		x509_print(p->x509);
196714f4e3fSclaudio 		if (p->aia != NULL)
197714f4e3fSclaudio 			printf("Authority info access:    %s\n", p->aia);
198714f4e3fSclaudio 		if (p->mft != NULL)
199714f4e3fSclaudio 			printf("Manifest:                 %s\n", p->mft);
200714f4e3fSclaudio 		if (p->repo != NULL)
201714f4e3fSclaudio 			printf("caRepository:             %s\n", p->repo);
202714f4e3fSclaudio 		if (p->notify != NULL)
203714f4e3fSclaudio 			printf("Notify URL:               %s\n", p->notify);
20414d83341Sjob 		if (p->pubkey != NULL) {
2056530cf17Sjob 			printf("BGPsec ECDSA public key:  %s\n",
20667642cb5Stb 			    p->pubkey);
2074dbb22b8Sjob 			printf("Router key not before:    %s\n",
208f5999ddfSjob 			    time2str(p->notbefore));
2094dbb22b8Sjob 			printf("Router key not after:     %s\n",
2109f544822Sjob 			    time2str(p->notafter));
211f5999ddfSjob 		} else {
2124dbb22b8Sjob 			printf("Certificate not before:   %s\n",
213f5999ddfSjob 			    time2str(p->notbefore));
2144dbb22b8Sjob 			printf("Certificate not after:    %s\n",
2159f544822Sjob 			    time2str(p->notafter));
216f5999ddfSjob 		}
2174486d057Sjob 		printf("Subordinate resources:    ");
218530399e8Sjob 	}
219714f4e3fSclaudio 
220530399e8Sjob 	for (i = 0; i < p->asz; i++) {
221714f4e3fSclaudio 		switch (p->as[i].type) {
222714f4e3fSclaudio 		case CERT_AS_ID:
223530399e8Sjob 			if (outformats & FORMAT_JSON)
224530399e8Sjob 				printf("\t\t{ \"asid\": %u }", p->as[i].id);
2254486d057Sjob 			else {
2264486d057Sjob 				if (i > 0)
2274486d057Sjob 					printf("%26s", "");
2284486d057Sjob 				printf("AS: %u", p->as[i].id);
2294486d057Sjob 			}
230714f4e3fSclaudio 			break;
231714f4e3fSclaudio 		case CERT_AS_INHERIT:
232530399e8Sjob 			if (outformats & FORMAT_JSON)
233530399e8Sjob 				printf("\t\t{ \"asid_inherit\": \"true\" }");
2344486d057Sjob 			else {
2354486d057Sjob 				if (i > 0)
2364486d057Sjob 					printf("%26s", "");
2374486d057Sjob 				printf("AS: inherit");
2384486d057Sjob 			}
239714f4e3fSclaudio 			break;
240714f4e3fSclaudio 		case CERT_AS_RANGE:
241530399e8Sjob 			if (outformats & FORMAT_JSON)
242530399e8Sjob 				printf("\t\t{ \"asrange\": { \"min\": %u, "
243530399e8Sjob 				    "\"max\": %u }}", p->as[i].range.min,
244530399e8Sjob 				    p->as[i].range.max);
2454486d057Sjob 			else {
2464486d057Sjob 				if (i > 0)
2474486d057Sjob 					printf("%26s", "");
2484486d057Sjob 				printf("AS: %u -- %u", p->as[i].range.min,
2494486d057Sjob 				    p->as[i].range.max);
2504486d057Sjob 			}
251714f4e3fSclaudio 			break;
252714f4e3fSclaudio 		}
253530399e8Sjob 		if (outformats & FORMAT_JSON && i + 1 < p->asz + p->ipsz)
254530399e8Sjob 			printf(",\n");
255530399e8Sjob 		else
256530399e8Sjob 			printf("\n");
257714f4e3fSclaudio 	}
258714f4e3fSclaudio 
259530399e8Sjob 	for (j = 0; j < p->ipsz; j++) {
260530399e8Sjob 		switch (p->ips[j].type) {
261530399e8Sjob 		case CERT_IP_INHERIT:
262530399e8Sjob 			if (outformats & FORMAT_JSON)
263530399e8Sjob 				printf("\t\t{ \"ip_inherit\": \"true\" }");
2644486d057Sjob 			else {
2654486d057Sjob 				if (i > 0 || j > 0)
2664486d057Sjob 					printf("%26s", "");
2674486d057Sjob 				printf("IP: inherit");
2684486d057Sjob 			}
269530399e8Sjob 			break;
270530399e8Sjob 		case CERT_IP_ADDR:
271530399e8Sjob 			ip_addr_print(&p->ips[j].ip,
272530399e8Sjob 			    p->ips[j].afi, buf1, sizeof(buf1));
273530399e8Sjob 			if (outformats & FORMAT_JSON)
274530399e8Sjob 				printf("\t\t{ \"ip_prefix\": \"%s\" }", buf1);
2754486d057Sjob 			else {
2764486d057Sjob 				if (i > 0 || j > 0)
2774486d057Sjob 					printf("%26s", "");
2784486d057Sjob 				printf("IP: %s", buf1);
2794486d057Sjob 			}
280530399e8Sjob 			break;
281530399e8Sjob 		case CERT_IP_RANGE:
282530399e8Sjob 			sockt = (p->ips[j].afi == AFI_IPV4) ?
283530399e8Sjob 			    AF_INET : AF_INET6;
284530399e8Sjob 			inet_ntop(sockt, p->ips[j].min, buf1, sizeof(buf1));
285530399e8Sjob 			inet_ntop(sockt, p->ips[j].max, buf2, sizeof(buf2));
286530399e8Sjob 			if (outformats & FORMAT_JSON)
287530399e8Sjob 				printf("\t\t{ \"ip_range\": { \"min\": \"%s\""
288530399e8Sjob 				    ", \"max\": \"%s\" }}", buf1, buf2);
2894486d057Sjob 			else {
2904486d057Sjob 				if (i > 0 || j > 0)
2914486d057Sjob 					printf("%26s", "");
2924486d057Sjob 				printf("IP: %s -- %s", buf1, buf2);
2934486d057Sjob 			}
294530399e8Sjob 			break;
295530399e8Sjob 		}
296530399e8Sjob 		if (outformats & FORMAT_JSON && i + j + 1 < p->asz + p->ipsz)
297530399e8Sjob 			printf(",\n");
298530399e8Sjob 		else
299530399e8Sjob 			printf("\n");
300530399e8Sjob 	}
301530399e8Sjob 
302530399e8Sjob 	if (outformats & FORMAT_JSON)
303530399e8Sjob 		printf("\t],\n");
304714f4e3fSclaudio }
305714f4e3fSclaudio 
306714f4e3fSclaudio void
307220c707cSclaudio crl_print(const struct crl *p)
308220c707cSclaudio {
309220c707cSclaudio 	STACK_OF(X509_REVOKED)	*revlist;
310220c707cSclaudio 	X509_REVOKED *rev;
3117cdd491fSclaudio 	ASN1_INTEGER *crlnum;
312e0b87278Sjob 	X509_NAME *xissuer;
313220c707cSclaudio 	int i;
314e0b87278Sjob 	char *issuer, *serial;
315220c707cSclaudio 	time_t t;
316220c707cSclaudio 
317530399e8Sjob 	if (outformats & FORMAT_JSON) {
318530399e8Sjob 		printf("\t\"type\": \"crl\",\n");
319530399e8Sjob 		printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
320530399e8Sjob 	} else
321220c707cSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
3227cdd491fSclaudio 
323e0b87278Sjob 	xissuer = X509_CRL_get_issuer(p->x509_crl);
324e0b87278Sjob 	issuer = X509_NAME_oneline(xissuer, NULL, 0);
3257cdd491fSclaudio 	crlnum = X509_CRL_get_ext_d2i(p->x509_crl, NID_crl_number, NULL, NULL);
3267cdd491fSclaudio 	serial = x509_convert_seqnum(__func__, crlnum);
327e0b87278Sjob 	if (issuer != NULL && serial != NULL) {
328e0b87278Sjob 		if (outformats & FORMAT_JSON) {
329e0b87278Sjob 			printf("\t\"crl_issuer\": \"%s\",\n", issuer);
330530399e8Sjob 			printf("\t\"crl_serial\": \"%s\",\n", serial);
331e0b87278Sjob 		} else {
332e0b87278Sjob 			printf("CRL issuer:               %s\n", issuer);
333e0b87278Sjob 			printf("CRL serial number:        %s\n", serial);
334530399e8Sjob 		}
335e0b87278Sjob 	}
336e0b87278Sjob 	free(issuer);
3377cdd491fSclaudio 	free(serial);
3387cdd491fSclaudio 	ASN1_INTEGER_free(crlnum);
3397cdd491fSclaudio 
340530399e8Sjob 	if (outformats & FORMAT_JSON) {
3419f544822Sjob 		printf("\t\"valid_since\": %lld,\n", (long long)p->lastupdate);
3429f544822Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->nextupdate);
343530399e8Sjob 		printf("\t\"revoked_certs\": [\n");
344530399e8Sjob 	} else {
3454dbb22b8Sjob 		printf("CRL last update:          %s\n",
3469f544822Sjob 		    time2str(p->lastupdate));
3474dbb22b8Sjob 		printf("CRL next update:          %s\n",
3489f544822Sjob 		    time2str(p->nextupdate));
349530399e8Sjob 		printf("Revoked Certificates:\n");
350530399e8Sjob 	}
351220c707cSclaudio 
352220c707cSclaudio 	revlist = X509_CRL_get_REVOKED(p->x509_crl);
353220c707cSclaudio 	for (i = 0; i < sk_X509_REVOKED_num(revlist); i++) {
354220c707cSclaudio 		rev = sk_X509_REVOKED_value(revlist, i);
3557cdd491fSclaudio 		serial = x509_convert_seqnum(__func__,
3567cdd491fSclaudio 		    X509_REVOKED_get0_serialNumber(rev));
357220c707cSclaudio 		x509_get_time(X509_REVOKED_get0_revocationDate(rev), &t);
358530399e8Sjob 		if (serial != NULL) {
359530399e8Sjob 			if (outformats & FORMAT_JSON) {
360530399e8Sjob 				printf("\t\t{ \"serial\": \"%s\"", serial);
361530399e8Sjob 				printf(", \"date\": \"%s\" }", time2str(t));
362530399e8Sjob 				if (i + 1 < sk_X509_REVOKED_num(revlist))
363530399e8Sjob 					printf(",");
364530399e8Sjob 				printf("\n");
365530399e8Sjob 			} else
3664486d057Sjob 				printf("%25s Serial: %8s   Revocation Date: %s"
3674486d057Sjob 				    "\n", "", serial, time2str(t));
368530399e8Sjob 		}
3697cdd491fSclaudio 		free(serial);
370220c707cSclaudio 	}
371530399e8Sjob 
372530399e8Sjob 	if (outformats & FORMAT_JSON)
373530399e8Sjob 		printf("\t],\n");
374530399e8Sjob 	else if (i == 0)
375220c707cSclaudio 		printf("No Revoked Certificates\n");
376220c707cSclaudio }
377220c707cSclaudio 
378220c707cSclaudio void
379530399e8Sjob mft_print(const X509 *x, const struct mft *p)
380714f4e3fSclaudio {
381714f4e3fSclaudio 	size_t i;
382714f4e3fSclaudio 	char *hash;
383714f4e3fSclaudio 
384530399e8Sjob 	if (outformats & FORMAT_JSON) {
385530399e8Sjob 		printf("\t\"type\": \"manifest\",\n");
386530399e8Sjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
387530399e8Sjob 		x509_print(x);
388530399e8Sjob 		printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
389530399e8Sjob 		printf("\t\"aia\": \"%s\",\n", p->aia);
3902cf0e122Sjob 		printf("\t\"sia\": \"%s\",\n", p->sia);
391530399e8Sjob 		printf("\t\"manifest_number\": \"%s\",\n", p->seqnum);
3921bb1e509Sjob 		if (p->signtime != 0)
3931bb1e509Sjob 			printf("\t\"signing_time\": %lld,\n",
3941bb1e509Sjob 			    (long long)p->signtime);
3954dbb22b8Sjob 		printf("\t\"valid_since\": %lld,\n", (long long)p->thisupdate);
3964dbb22b8Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->nextupdate);
397894936b4Sjob 		if (p->expires)
398894936b4Sjob 			printf("\t\"expires\": %lld,\n", (long long)p->expires);
399530399e8Sjob 	} else {
400714f4e3fSclaudio 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
401714f4e3fSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
402530399e8Sjob 		x509_print(x);
403714f4e3fSclaudio 		printf("Authority info access:    %s\n", p->aia);
4042cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
405*489e308aSjob 		printf("Manifest number:          %s\n", p->seqnum);
4061bb1e509Sjob 		if (p->signtime != 0)
4071bb1e509Sjob 			printf("Signing time:             %s\n",
4081bb1e509Sjob 			    time2str(p->signtime));
4094dbb22b8Sjob 		printf("Manifest this update:     %s\n", time2str(p->thisupdate));
4104dbb22b8Sjob 		printf("Manifest next update:     %s\n", time2str(p->nextupdate));
4114486d057Sjob 		printf("Files and hashes:         ");
412530399e8Sjob 	}
413530399e8Sjob 
414714f4e3fSclaudio 	for (i = 0; i < p->filesz; i++) {
415530399e8Sjob 		if (i == 0 && outformats & FORMAT_JSON)
416530399e8Sjob 			printf("\t\"filesandhashes\": [\n");
417530399e8Sjob 
418714f4e3fSclaudio 		if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash),
419714f4e3fSclaudio 		    &hash) == -1)
420714f4e3fSclaudio 			errx(1, "base64_encode failure");
421530399e8Sjob 
422530399e8Sjob 		if (outformats & FORMAT_JSON) {
423530399e8Sjob 			printf("\t\t{ \"filename\": \"%s\",", p->files[i].file);
424530399e8Sjob 			printf(" \"hash\": \"%s\" }", hash);
425530399e8Sjob 			if (i + 1 < p->filesz)
426530399e8Sjob 				printf(",");
427530399e8Sjob 			printf("\n");
428530399e8Sjob 		} else {
4294486d057Sjob 			if (i > 0)
4304486d057Sjob 				printf("%26s", "");
4314486d057Sjob 			printf("%zu: %s (hash: %s)\n", i + 1, p->files[i].file,
4324486d057Sjob 			    hash);
433530399e8Sjob 		}
434530399e8Sjob 
435714f4e3fSclaudio 		free(hash);
436714f4e3fSclaudio 	}
437530399e8Sjob 
438530399e8Sjob 	if (outformats & FORMAT_JSON)
439530399e8Sjob 		printf("\t],\n");
440714f4e3fSclaudio }
441714f4e3fSclaudio 
442714f4e3fSclaudio void
443530399e8Sjob roa_print(const X509 *x, const struct roa *p)
444714f4e3fSclaudio {
445714f4e3fSclaudio 	char	 buf[128];
446714f4e3fSclaudio 	size_t	 i;
447714f4e3fSclaudio 
448530399e8Sjob 	if (outformats & FORMAT_JSON) {
449530399e8Sjob 		printf("\t\"type\": \"roa\",\n");
450530399e8Sjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
451530399e8Sjob 		x509_print(x);
452530399e8Sjob 		printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
453530399e8Sjob 		printf("\t\"aia\": \"%s\",\n", p->aia);
4542cf0e122Sjob 		printf("\t\"sia\": \"%s\",\n", p->sia);
4551bb1e509Sjob 		if (p->signtime != 0)
4561bb1e509Sjob 			printf("\t\"signing_time\": %lld,\n",
4571bb1e509Sjob 			    (long long)p->signtime);
458f5999ddfSjob 		printf("\t\"valid_since\": %lld,\n", (long long)p->notbefore);
4599f544822Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->notafter);
4609c1f5d6bSjob 		if (p->expires)
4619c1f5d6bSjob 			printf("\t\"expires\": %lld,\n", (long long)p->expires);
462530399e8Sjob 	} else {
463714f4e3fSclaudio 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
464530399e8Sjob 		x509_print(x);
465714f4e3fSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
466714f4e3fSclaudio 		printf("Authority info access:    %s\n", p->aia);
4672cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
4681bb1e509Sjob 		if (p->signtime != 0)
4691bb1e509Sjob 			printf("Signing time:             %s\n",
4701bb1e509Sjob 			    time2str(p->signtime));
4714dbb22b8Sjob 		printf("ROA not before:           %s\n",
472f5999ddfSjob 		    time2str(p->notbefore));
4734dbb22b8Sjob 		printf("ROA not after:            %s\n", time2str(p->notafter));
474714f4e3fSclaudio 		printf("asID:                     %u\n", p->asid);
4754486d057Sjob 		printf("IP address blocks:        ");
476530399e8Sjob 	}
477530399e8Sjob 
478714f4e3fSclaudio 	for (i = 0; i < p->ipsz; i++) {
479530399e8Sjob 		if (i == 0 && outformats & FORMAT_JSON)
480530399e8Sjob 			printf("\t\"vrps\": [\n");
481530399e8Sjob 
482714f4e3fSclaudio 		ip_addr_print(&p->ips[i].addr,
483714f4e3fSclaudio 		    p->ips[i].afi, buf, sizeof(buf));
484530399e8Sjob 
485530399e8Sjob 		if (outformats & FORMAT_JSON) {
486530399e8Sjob 			printf("\t\t{ \"prefix\": \"%s\",", buf);
487530399e8Sjob 			printf(" \"asid\": %u,", p->asid);
488530399e8Sjob 			printf(" \"maxlen\": %hhu }", p->ips[i].maxlength);
489530399e8Sjob 			if (i + 1 < p->ipsz)
490530399e8Sjob 				printf(",");
491530399e8Sjob 			printf("\n");
4924486d057Sjob 		} else {
4934486d057Sjob 			if (i > 0)
4944486d057Sjob 				printf("%26s", "");
4954486d057Sjob 			printf("%s maxlen: %hhu\n", buf, p->ips[i].maxlength);
4964486d057Sjob 		}
497714f4e3fSclaudio 	}
498530399e8Sjob 
499530399e8Sjob 	if (outformats & FORMAT_JSON)
500530399e8Sjob 		printf("\t],\n");
501714f4e3fSclaudio }
502714f4e3fSclaudio 
503714f4e3fSclaudio void
504530399e8Sjob gbr_print(const X509 *x, const struct gbr *p)
505714f4e3fSclaudio {
506530399e8Sjob 	size_t	i;
507530399e8Sjob 
508530399e8Sjob 	if (outformats & FORMAT_JSON) {
509530399e8Sjob 		printf("\t\"type\": \"gbr\",\n");
510530399e8Sjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
511530399e8Sjob 		x509_print(x);
512530399e8Sjob 		printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
513530399e8Sjob 		printf("\t\"aia\": \"%s\",\n", p->aia);
5142cf0e122Sjob 		printf("\t\"sia\": \"%s\",\n", p->sia);
5151bb1e509Sjob 		if (p->signtime != 0)
5161bb1e509Sjob 			printf("\t\"signing_time\": %lld,\n",
5171bb1e509Sjob 			    (long long)p->signtime);
518f5999ddfSjob 		printf("\t\"valid_since\": %lld,\n", (long long)p->notbefore);
5199f544822Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->notafter);
520894936b4Sjob 		if (p->expires)
521894936b4Sjob 			printf("\t\"expires\": %lld,\n", (long long)p->expires);
522530399e8Sjob 		printf("\t\"vcard\": \"");
523530399e8Sjob 		for (i = 0; i < strlen(p->vcard); i++) {
524530399e8Sjob 			if (p->vcard[i] == '"')
525530399e8Sjob 				printf("\\\"");
526530399e8Sjob 			if (p->vcard[i] == '\r')
527530399e8Sjob 				continue;
528530399e8Sjob 			if (p->vcard[i] == '\n')
529530399e8Sjob 				printf("\\r\\n");
530530399e8Sjob 			else
531530399e8Sjob 				putchar(p->vcard[i]);
532530399e8Sjob 		}
533530399e8Sjob 		printf("\",\n");
534530399e8Sjob 	} else {
535714f4e3fSclaudio 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
536530399e8Sjob 		x509_print(x);
537714f4e3fSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
538714f4e3fSclaudio 		printf("Authority info access:    %s\n", p->aia);
5392cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
5401bb1e509Sjob 		if (p->signtime != 0)
5411bb1e509Sjob 			printf("Signing time:             %s\n",
5421bb1e509Sjob 			    time2str(p->signtime));
5434dbb22b8Sjob 		printf("GBR not before:           %s\n",
544f5999ddfSjob 		    time2str(p->notbefore));
5454dbb22b8Sjob 		printf("GBR not after:            %s\n", time2str(p->notafter));
546714f4e3fSclaudio 		printf("vcard:\n%s", p->vcard);
547714f4e3fSclaudio 	}
548530399e8Sjob }
54904834fbdSjob 
55004834fbdSjob void
55104834fbdSjob rsc_print(const X509 *x, const struct rsc *p)
55204834fbdSjob {
55328439bbcSjob 	char	 buf1[64], buf2[64];
55404834fbdSjob 	char	*hash;
55504834fbdSjob 	int	 sockt;
55604834fbdSjob 	size_t	 i, j;
55704834fbdSjob 
55804834fbdSjob 	if (outformats & FORMAT_JSON) {
55904834fbdSjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
56004834fbdSjob 		printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
56104834fbdSjob 		x509_print(x);
56204834fbdSjob 		printf("\t\"aia\": \"%s\",\n", p->aia);
5631bb1e509Sjob 		if (p->signtime != 0)
5641bb1e509Sjob 			printf("\t\"signing_time\": %lld,\n",
5651bb1e509Sjob 			    (long long)p->signtime);
566f5999ddfSjob 		printf("\t\"valid_since\": %lld,\n", (long long)p->notbefore);
5679f544822Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->notafter);
568894936b4Sjob 		if (p->expires)
569894936b4Sjob 			printf("\t\"expires\": %lld,\n", (long long)p->expires);
57004834fbdSjob 		printf("\t\"signed_with_resources\": [\n");
57104834fbdSjob 	} else {
57204834fbdSjob 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
57304834fbdSjob 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
57404834fbdSjob 		x509_print(x);
57504834fbdSjob 		printf("Authority info access:    %s\n", p->aia);
5761bb1e509Sjob 		if (p->signtime != 0)
5771bb1e509Sjob 			printf("Signing time:             %s\n",
5781bb1e509Sjob 			    time2str(p->signtime));
5794dbb22b8Sjob 		printf("RSC not before:           %s\n",
580f5999ddfSjob 		    time2str(p->notbefore));
5814dbb22b8Sjob 		printf("RSC not after:            %s\n", time2str(p->notafter));
5824486d057Sjob 		printf("Signed with resources:    ");
58304834fbdSjob 	}
58404834fbdSjob 
58504834fbdSjob 	for (i = 0; i < p->asz; i++) {
58604834fbdSjob 		switch (p->as[i].type) {
58704834fbdSjob 		case CERT_AS_ID:
58804834fbdSjob 			if (outformats & FORMAT_JSON)
58904834fbdSjob 				printf("\t\t{ \"asid\": %u }", p->as[i].id);
5904486d057Sjob 			else {
5914486d057Sjob 				if (i > 0)
5924486d057Sjob 					printf("%26s", "");
5934486d057Sjob 				printf("AS: %u", p->as[i].id);
5944486d057Sjob 			}
59504834fbdSjob 			break;
59604834fbdSjob 		case CERT_AS_RANGE:
59704834fbdSjob 			if (outformats & FORMAT_JSON)
59804834fbdSjob 				printf("\t\t{ \"asrange\": { \"min\": %u, "
59904834fbdSjob 				    "\"max\": %u }}", p->as[i].range.min,
60004834fbdSjob 				    p->as[i].range.max);
6014486d057Sjob 			else {
6024486d057Sjob 				if (i > 0)
6034486d057Sjob 					printf("%26s", "");
6044486d057Sjob 				printf("AS: %u -- %u", p->as[i].range.min,
6054486d057Sjob 				    p->as[i].range.max);
6064486d057Sjob 			}
60704834fbdSjob 			break;
60804834fbdSjob 		case CERT_AS_INHERIT:
60904834fbdSjob 			/* inheritance isn't possible in RSC */
61004834fbdSjob 			break;
61104834fbdSjob 		}
61204834fbdSjob 		if (outformats & FORMAT_JSON && i + 1 < p->asz + p->ipsz)
61304834fbdSjob 			printf(",\n");
61404834fbdSjob 		else
61504834fbdSjob 			printf("\n");
61604834fbdSjob 	}
61704834fbdSjob 
61804834fbdSjob 	for (j = 0; j < p->ipsz; j++) {
61904834fbdSjob 		switch (p->ips[j].type) {
62004834fbdSjob 		case CERT_IP_ADDR:
62104834fbdSjob 			ip_addr_print(&p->ips[j].ip,
62204834fbdSjob 			    p->ips[j].afi, buf1, sizeof(buf1));
62304834fbdSjob 			if (outformats & FORMAT_JSON)
62404834fbdSjob 				printf("\t\t{ \"ip_prefix\": \"%s\" }", buf1);
6254486d057Sjob 			else {
6264486d057Sjob 				if (i > 0 || j > 0)
6274486d057Sjob 					printf("%26s", "");
6284486d057Sjob 				printf("IP: %s", buf1);
6294486d057Sjob 			}
63004834fbdSjob 			break;
63104834fbdSjob 		case CERT_IP_RANGE:
63204834fbdSjob 			sockt = (p->ips[j].afi == AFI_IPV4) ?
63304834fbdSjob 			    AF_INET : AF_INET6;
63404834fbdSjob 			inet_ntop(sockt, p->ips[j].min, buf1, sizeof(buf1));
63504834fbdSjob 			inet_ntop(sockt, p->ips[j].max, buf2, sizeof(buf2));
63604834fbdSjob 			if (outformats & FORMAT_JSON)
63704834fbdSjob 				printf("\t\t{ \"ip_range\": { \"min\": \"%s\""
63804834fbdSjob 				    ", \"max\": \"%s\" }}", buf1, buf2);
6394486d057Sjob 			else {
6404486d057Sjob 				if (i > 0 || j > 0)
6414486d057Sjob 					printf("%26s", "");
6424486d057Sjob 				printf("IP: %s -- %s", buf1, buf2);
6434486d057Sjob 			}
64404834fbdSjob 			break;
64504834fbdSjob 		case CERT_IP_INHERIT:
64604834fbdSjob 			/* inheritance isn't possible in RSC */
64704834fbdSjob 			break;
64804834fbdSjob 		}
64904834fbdSjob 		if (outformats & FORMAT_JSON && i + j + 1 < p->asz + p->ipsz)
65004834fbdSjob 			printf(",\n");
65104834fbdSjob 		else
65204834fbdSjob 			printf("\n");
65304834fbdSjob 	}
65404834fbdSjob 
65504834fbdSjob 	if (outformats & FORMAT_JSON) {
65604834fbdSjob 		printf("\t],\n");
65704834fbdSjob 		printf("\t\"filenamesandhashes\": [\n");
65804834fbdSjob 	} else
6594486d057Sjob 		printf("Filenames and hashes:     ");
66004834fbdSjob 
66104834fbdSjob 	for (i = 0; i < p->filesz; i++) {
66204834fbdSjob 		if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash),
66304834fbdSjob 		    &hash) == -1)
66404834fbdSjob 			errx(1, "base64_encode failure");
66504834fbdSjob 
66604834fbdSjob 		if (outformats & FORMAT_JSON) {
66704834fbdSjob 			printf("\t\t{ \"filename\": \"%s\",",
66804834fbdSjob 			    p->files[i].filename ? p->files[i].filename : "");
66904834fbdSjob 			printf(" \"hash_digest\": \"%s\" }", hash);
67004834fbdSjob 			if (i + 1 < p->filesz)
67104834fbdSjob 				printf(",");
67204834fbdSjob 			printf("\n");
67304834fbdSjob 		} else {
6744486d057Sjob 			if (i > 0)
6754486d057Sjob 				printf("%26s", "");
6764486d057Sjob 			printf("%zu: %s (hash: %s)\n", i + 1,
6774486d057Sjob 			    p->files[i].filename ? p->files[i].filename
6784486d057Sjob 			    : "no filename", hash);
67904834fbdSjob 		}
68004834fbdSjob 
68104834fbdSjob 		free(hash);
68204834fbdSjob 	}
68304834fbdSjob 
68404834fbdSjob 	if (outformats & FORMAT_JSON)
68504834fbdSjob 		printf("\t],\n");
68604834fbdSjob }
687a29ddfd5Sjob 
688a29ddfd5Sjob void
689a29ddfd5Sjob aspa_print(const X509 *x, const struct aspa *p)
690a29ddfd5Sjob {
691a29ddfd5Sjob 	size_t	i;
692a29ddfd5Sjob 
693a29ddfd5Sjob 	if (outformats & FORMAT_JSON) {
694a29ddfd5Sjob 		printf("\t\"type\": \"aspa\",\n");
695a29ddfd5Sjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
696a29ddfd5Sjob 		x509_print(x);
697a29ddfd5Sjob 		printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
698a29ddfd5Sjob 		printf("\t\"aia\": \"%s\",\n", p->aia);
6992cf0e122Sjob 		printf("\t\"sia\": \"%s\",\n", p->sia);
7001bb1e509Sjob 		if (p->signtime != 0)
7011bb1e509Sjob 			printf("\t\"signing_time\": %lld,\n",
7021bb1e509Sjob 			    (long long)p->signtime);
703f5999ddfSjob 		printf("\t\"valid_since\": %lld,\n", (long long)p->notbefore);
7049f544822Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->notafter);
7059c1f5d6bSjob 		if (p->expires)
7069c1f5d6bSjob 			printf("\t\"expires\": %lld,\n", (long long)p->expires);
707a29ddfd5Sjob 		printf("\t\"customer_asid\": %u,\n", p->custasid);
708a29ddfd5Sjob 		printf("\t\"provider_set\": [\n");
709a29ddfd5Sjob 		for (i = 0; i < p->providersz; i++) {
710a29ddfd5Sjob 			printf("\t\t{ \"asid\": %u", p->providers[i].as);
711a29ddfd5Sjob 			if (p->providers[i].afi == AFI_IPV4)
712a29ddfd5Sjob 				printf(", \"afi_limit\": \"ipv4\"");
713a29ddfd5Sjob 			if (p->providers[i].afi == AFI_IPV6)
714a29ddfd5Sjob 				printf(", \"afi_limit\": \"ipv6\"");
715a29ddfd5Sjob 			printf(" }");
716a29ddfd5Sjob 			if (i + 1 < p->providersz)
717a29ddfd5Sjob 				printf(",");
718a29ddfd5Sjob 			printf("\n");
719a29ddfd5Sjob 		}
720a29ddfd5Sjob 		printf("\t],\n");
721a29ddfd5Sjob 	} else {
722a29ddfd5Sjob 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
723a29ddfd5Sjob 		x509_print(x);
724a29ddfd5Sjob 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
725a29ddfd5Sjob 		printf("Authority info access:    %s\n", p->aia);
7262cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
7271bb1e509Sjob 		if (p->signtime != 0)
7281bb1e509Sjob 			printf("Signing time:             %s\n",
7291bb1e509Sjob 			    time2str(p->signtime));
7304dbb22b8Sjob 		printf("ASPA not before:          %s\n",
731f5999ddfSjob 		    time2str(p->notbefore));
7324dbb22b8Sjob 		printf("ASPA not after:           %s\n", time2str(p->notafter));
733a29ddfd5Sjob 		printf("Customer AS:              %u\n", p->custasid);
7344486d057Sjob 		printf("Provider Set:             ");
735a29ddfd5Sjob 		for (i = 0; i < p->providersz; i++) {
7364486d057Sjob 			if (i > 0)
7374486d057Sjob 				printf("%26s", "");
7384486d057Sjob 			printf("AS: %d", p->providers[i].as);
739a29ddfd5Sjob 			switch (p->providers[i].afi) {
740a29ddfd5Sjob 			case AFI_IPV4:
741a29ddfd5Sjob 				printf(" (IPv4 only)");
742a29ddfd5Sjob 				break;
743a29ddfd5Sjob 			case AFI_IPV6:
744a29ddfd5Sjob 				printf(" (IPv6 only)");
745a29ddfd5Sjob 				break;
746a29ddfd5Sjob 			default:
747a29ddfd5Sjob 				break;
748a29ddfd5Sjob 			}
749a29ddfd5Sjob 			printf("\n");
750a29ddfd5Sjob 		}
751a29ddfd5Sjob 	}
752a29ddfd5Sjob }
753ee2a33daSjob 
754ee2a33daSjob static void
755ee2a33daSjob takey_print(char *name, const struct takey *t)
756ee2a33daSjob {
757ee2a33daSjob 	char	*spki = NULL;
758ee2a33daSjob 	size_t	 i, j = 0;
759ee2a33daSjob 
760ee2a33daSjob 	if (base64_encode(t->pubkey, t->pubkeysz, &spki) != 0)
761ee2a33daSjob 		errx(1, "base64_encode failed in %s", __func__);
762ee2a33daSjob 
763ee2a33daSjob 	if (outformats & FORMAT_JSON) {
764ee2a33daSjob 		printf("\t\t{\n\t\t\t\"name\": \"%s\",\n", name);
765ee2a33daSjob 		printf("\t\t\t\"comments\": [");
766ee2a33daSjob 		for (i = 0; i < t->commentsz; i++) {
767ee2a33daSjob 			printf("\"%s\"", t->comments[i]);
768ee2a33daSjob 			if (i + 1 < t->commentsz)
769ee2a33daSjob 				printf(", ");
770ee2a33daSjob 		}
771ee2a33daSjob 		printf("],\n");
772ee2a33daSjob 		printf("\t\t\t\"uris\": [");
773ee2a33daSjob 		for (i = 0; i < t->urisz; i++) {
774ee2a33daSjob 			printf("\"%s\"", t->uris[i]);
775ee2a33daSjob 			if (i + 1 < t->urisz)
776ee2a33daSjob 				printf(", ");
777ee2a33daSjob 		}
778ee2a33daSjob 		printf("],\n");
779ee2a33daSjob 		printf("\t\t\t\"spki\": \"%s\"\n\t\t}", spki);
780ee2a33daSjob 	} else {
781ee2a33daSjob 		printf("TAL derived from the '%s' Trust Anchor Key:\n\n", name);
782ee2a33daSjob 
783ee2a33daSjob 		for (i = 0; i < t->commentsz; i++) {
784ee2a33daSjob 			printf("\t# %s\n", t->comments[i]);
785ee2a33daSjob 		}
786ee2a33daSjob 
787ee2a33daSjob 		for (i = 0; i < t->urisz; i++) {
788ee2a33daSjob 			printf("\t%s\n\n\t", t->uris[i]);
789ee2a33daSjob 		}
790ee2a33daSjob 
791ee2a33daSjob 		for (i = 0; i < strlen(spki); i++) {
792ee2a33daSjob 			printf("%c", spki[i]);
793ee2a33daSjob 			j++;
794ee2a33daSjob 			if (j == 64) {
795ee2a33daSjob 				printf("\n\t");
796ee2a33daSjob 				j = 0;
797ee2a33daSjob 			}
798ee2a33daSjob 		}
799ee2a33daSjob 
800ee2a33daSjob 		printf("\n\n");
801ee2a33daSjob 	}
802ee2a33daSjob 
803ee2a33daSjob 	free(spki);
804ee2a33daSjob }
805ee2a33daSjob 
806ee2a33daSjob void
807ee2a33daSjob tak_print(const X509 *x, const struct tak *p)
808ee2a33daSjob {
809ee2a33daSjob 	if (outformats & FORMAT_JSON) {
810ee2a33daSjob 		printf("\t\"type\": \"tak\",\n");
811ee2a33daSjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
812ee2a33daSjob 		x509_print(x);
813ee2a33daSjob 		printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
814ee2a33daSjob 		printf("\t\"aia\": \"%s\",\n", p->aia);
8152cf0e122Sjob 		printf("\t\"sia\": \"%s\",\n", p->sia);
8161bb1e509Sjob 		if (p->signtime != 0)
8171bb1e509Sjob 			printf("\t\"signing_time\": %lld,\n",
8181bb1e509Sjob 			    (long long)p->signtime);
819f5999ddfSjob 		printf("\t\"valid_since\": %lld,\n", (long long)p->notbefore);
8209f544822Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->notafter);
821894936b4Sjob 		if (p->expires)
822894936b4Sjob 			printf("\t\"expires\": %lld,\n", (long long)p->expires);
823ee2a33daSjob 		printf("\t\"takeys\": [\n");
824ee2a33daSjob 	} else {
825ee2a33daSjob 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
826ee2a33daSjob 		x509_print(x);
827ee2a33daSjob 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
828ee2a33daSjob 		printf("Authority info access:    %s\n", p->aia);
8292cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
8301bb1e509Sjob 		if (p->signtime != 0)
8311bb1e509Sjob 			printf("Signing time:             %s\n",
8321bb1e509Sjob 			    time2str(p->signtime));
8334dbb22b8Sjob 		printf("TAK not before:           %s\n",
834f5999ddfSjob 		    time2str(p->notbefore));
8354dbb22b8Sjob 		printf("TAK not after:            %s\n", time2str(p->notafter));
836ee2a33daSjob 	}
837ee2a33daSjob 
838ee2a33daSjob 	takey_print("current", p->current);
839ee2a33daSjob 
840ee2a33daSjob 	if (p->predecessor != NULL) {
841ee2a33daSjob 		if (outformats & FORMAT_JSON)
842ee2a33daSjob 			printf(",\n");
843ee2a33daSjob 		takey_print("predecessor", p->predecessor);
844ee2a33daSjob 	}
845ee2a33daSjob 
846ee2a33daSjob 	if (p->successor != NULL) {
847ee2a33daSjob 		if (outformats & FORMAT_JSON)
848ee2a33daSjob 			printf(",\n");
849ee2a33daSjob 		takey_print("successor", p->successor);
850ee2a33daSjob 	}
851ee2a33daSjob 
852ee2a33daSjob 	if (outformats & FORMAT_JSON)
853ee2a33daSjob 		printf("\n\t],\n");
854ee2a33daSjob }
855ef3f6f56Sjob 
856ef3f6f56Sjob void
857ef3f6f56Sjob geofeed_print(const X509 *x, const struct geofeed *p)
858ef3f6f56Sjob {
859ef3f6f56Sjob 	char	 buf[128];
860ef3f6f56Sjob 	size_t	 i;
861ef3f6f56Sjob 
862ef3f6f56Sjob 	if (outformats & FORMAT_JSON) {
863ef3f6f56Sjob 		printf("\t\"type\": \"geofeed\",\n");
864ef3f6f56Sjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
865ef3f6f56Sjob 		x509_print(x);
866ef3f6f56Sjob 		printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
867ef3f6f56Sjob 		printf("\t\"aia\": \"%s\",\n", p->aia);
8681bb1e509Sjob 		if (p->signtime != 0)
8691bb1e509Sjob 			printf("\t\"signing_time\": %lld,\n",
8701bb1e509Sjob 			    (long long)p->signtime);
871f5999ddfSjob 		printf("\t\"valid_since\": %lld,\n", (long long)p->notbefore);
8729f544822Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->notafter);
873ef3f6f56Sjob 		printf("\t\"records\": [\n");
874ef3f6f56Sjob 	} else {
875ef3f6f56Sjob 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
876ef3f6f56Sjob 		x509_print(x);
877ef3f6f56Sjob 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
878ef3f6f56Sjob 		printf("Authority info access:    %s\n", p->aia);
8791bb1e509Sjob 		if (p->signtime != 0)
8801bb1e509Sjob 			printf("Signing time:             %s\n",
8811bb1e509Sjob 			    time2str(p->signtime));
8824dbb22b8Sjob 		printf("Geofeed not before:       %s\n",
883f5999ddfSjob 		    time2str(p->notbefore));
8844dbb22b8Sjob 		printf("Geofeed not after:        %s\n", time2str(p->notafter));
885bd19f13dSjob 		printf("Geofeed CSV records:      ");
886ef3f6f56Sjob 	}
887ef3f6f56Sjob 
888ef3f6f56Sjob 	for (i = 0; i < p->geoipsz; i++) {
889ef3f6f56Sjob 		if (p->geoips[i].ip->type != CERT_IP_ADDR)
890ef3f6f56Sjob 			continue;
891ef3f6f56Sjob 
892ef3f6f56Sjob 		ip_addr_print(&p->geoips[i].ip->ip, p->geoips[i].ip->afi, buf,
893ef3f6f56Sjob 		    sizeof(buf));
894ef3f6f56Sjob 		if (outformats & FORMAT_JSON)
895ef3f6f56Sjob 			printf("\t\t{ \"prefix\": \"%s\", \"location\": \"%s\""
896ef3f6f56Sjob 			    "}", buf, p->geoips[i].loc);
8974486d057Sjob 		else {
8984486d057Sjob 			if (i > 0)
8994486d057Sjob 				printf("%26s", "");
9004486d057Sjob 			printf("IP: %s (%s)", buf, p->geoips[i].loc);
9014486d057Sjob 		}
902ef3f6f56Sjob 
903ef3f6f56Sjob 		if (outformats & FORMAT_JSON && i + 1 < p->geoipsz)
904ef3f6f56Sjob 			printf(",\n");
905ef3f6f56Sjob 		else
906ef3f6f56Sjob 			printf("\n");
907ef3f6f56Sjob 	}
908ef3f6f56Sjob 
909ef3f6f56Sjob 	if (outformats & FORMAT_JSON)
910ef3f6f56Sjob 		printf("\t],\n");
911ef3f6f56Sjob }
912