xref: /openbsd/usr.sbin/rpki-client/print.c (revision 530399e8)
1*530399e8Sjob /*	$OpenBSD: print.c,v 1.8 2022/04/20 10:46:20 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 
60220c707cSclaudio 	strftime(buf, sizeof(buf), "%h %d %T %Y %Z", &tm);
61220c707cSclaudio 	return buf;
62220c707cSclaudio }
63220c707cSclaudio 
64714f4e3fSclaudio void
65714f4e3fSclaudio tal_print(const struct tal *p)
66714f4e3fSclaudio {
67e911df76Sjob 	char			*ski;
68e911df76Sjob 	EVP_PKEY		*pk;
69e911df76Sjob 	RSA			*r;
70e911df76Sjob 	const unsigned char	*der;
71e911df76Sjob 	unsigned char		*rder = NULL;
72e911df76Sjob 	unsigned char		 md[SHA_DIGEST_LENGTH];
73e911df76Sjob 	int			 rder_len;
74714f4e3fSclaudio 	size_t			 i;
75714f4e3fSclaudio 
76e911df76Sjob 
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 
93*530399e8Sjob 	if (outformats & FORMAT_JSON) {
94*530399e8Sjob 		printf("\t\"type\": \"tal\",\n");
95*530399e8Sjob 		printf("\t\"name\": %s\n", p->descr);
96*530399e8Sjob 		printf("\t\"ski\": %s\n", pretty_key_id(ski));
97*530399e8Sjob 		printf("\t\"trust_anchor_locations\": [");
98*530399e8Sjob 		for (i = 0; i < p->urisz; i++) {
99*530399e8Sjob 			printf("\"%s\"", p->uri[i]);
100*530399e8Sjob 			if (i + 1 < p->urisz)
101*530399e8Sjob 				printf(", ");
102*530399e8Sjob 		}
103*530399e8Sjob 		printf("]\n");
104*530399e8Sjob 
105*530399e8Sjob 	} else {
106*530399e8Sjob 		printf("Trust anchor name: %s\n", p->descr);
107*530399e8Sjob 		printf("Subject key identifier: %s\n", pretty_key_id(ski));
108e911df76Sjob 		printf("Trust anchor locations:\n");
109714f4e3fSclaudio 		for (i = 0; i < p->urisz; i++)
110e911df76Sjob 			printf("%5zu: %s\n", i + 1, p->uri[i]);
111*530399e8Sjob 	}
112e911df76Sjob 
113e911df76Sjob 	EVP_PKEY_free(pk);
114e911df76Sjob 	free(rder);
115e911df76Sjob 	free(ski);
116714f4e3fSclaudio }
117714f4e3fSclaudio 
118714f4e3fSclaudio void
119*530399e8Sjob x509_print(const X509 *x)
120*530399e8Sjob {
121*530399e8Sjob 	const ASN1_INTEGER	*xserial;
122*530399e8Sjob 	char			*serial = NULL;
123*530399e8Sjob 
124*530399e8Sjob 	xserial = X509_get0_serialNumber(x);
125*530399e8Sjob 	if (xserial == NULL) {
126*530399e8Sjob 		warnx("X509_get0_serialNumber failed in %s", __func__);
127*530399e8Sjob 		goto out;
128*530399e8Sjob 	}
129*530399e8Sjob 
130*530399e8Sjob 	serial = x509_convert_seqnum(__func__, xserial);
131*530399e8Sjob 	if (serial == NULL) {
132*530399e8Sjob 		warnx("x509_convert_seqnum failed in %s", __func__);
133*530399e8Sjob 		goto out;
134*530399e8Sjob 	}
135*530399e8Sjob 
136*530399e8Sjob 	if (outformats & FORMAT_JSON) {
137*530399e8Sjob 		printf("\t\"cert_serial\": \"%s\",\n", serial);
138*530399e8Sjob 	} else {
139*530399e8Sjob 		printf("Certificate serial: %s\n", serial);
140*530399e8Sjob 	}
141*530399e8Sjob 
142*530399e8Sjob  out:
143*530399e8Sjob 	free(serial);
144*530399e8Sjob }
145*530399e8Sjob 
146*530399e8Sjob void
147714f4e3fSclaudio cert_print(const struct cert *p)
148714f4e3fSclaudio {
149*530399e8Sjob 	size_t			 i, j;
150714f4e3fSclaudio 	char			 buf1[64], buf2[64];
151714f4e3fSclaudio 	int			 sockt;
152714f4e3fSclaudio 	char			 tbuf[21];
153714f4e3fSclaudio 
154*530399e8Sjob 	strftime(tbuf, sizeof(tbuf), "%FT%TZ", gmtime(&p->expires));
155*530399e8Sjob 
156*530399e8Sjob 	if (outformats & FORMAT_JSON) {
157*530399e8Sjob 		if (p->pubkey != NULL)
158*530399e8Sjob 			printf("\t\"type\": \"router_key\",\n");
159*530399e8Sjob 		else
160*530399e8Sjob 			printf("\t\"type\": \"ca_cert\",\n");
161*530399e8Sjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
162*530399e8Sjob 		if (p->aki != NULL)
163*530399e8Sjob 			printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
164*530399e8Sjob 		x509_print(p->x509);
165*530399e8Sjob 		if (p->aia != NULL)
166*530399e8Sjob 			printf("\t\"aia\": \"%s\",\n", p->aia);
167*530399e8Sjob 		if (p->mft != NULL)
168*530399e8Sjob 			printf("\t\"manifest\": \"%s\",\n", p->mft);
169*530399e8Sjob 		if (p->repo != NULL)
170*530399e8Sjob 			printf("\t\"carepository\": \"%s\",\n", p->repo);
171*530399e8Sjob 		if (p->notify != NULL)
172*530399e8Sjob 			printf("\t\"notify_url\": \"%s\",\n", p->notify);
173*530399e8Sjob 		if (p->pubkey != NULL)
174*530399e8Sjob 			printf("\t\"router_key\": \"%s\",\n", p->pubkey);
175*530399e8Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->expires);
176*530399e8Sjob 		printf("\t\"subordinate_resources\": [\n");
177*530399e8Sjob 	} else {
178714f4e3fSclaudio 		printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
179714f4e3fSclaudio 		if (p->aki != NULL)
180714f4e3fSclaudio 			printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
181*530399e8Sjob 		x509_print(p->x509);
182714f4e3fSclaudio 		if (p->aia != NULL)
183714f4e3fSclaudio 			printf("Authority info access: %s\n", p->aia);
184714f4e3fSclaudio 		if (p->mft != NULL)
185714f4e3fSclaudio 			printf("Manifest: %s\n", p->mft);
186714f4e3fSclaudio 		if (p->repo != NULL)
187714f4e3fSclaudio 			printf("caRepository: %s\n", p->repo);
188714f4e3fSclaudio 		if (p->notify != NULL)
189714f4e3fSclaudio 			printf("Notify URL: %s\n", p->notify);
190714f4e3fSclaudio 		if (p->pubkey != NULL)
191714f4e3fSclaudio 			printf("BGPsec P-256 ECDSA public key: %s\n", p->pubkey);
192714f4e3fSclaudio 		printf("Valid until: %s\n", tbuf);
193714f4e3fSclaudio 		printf("Subordinate Resources:\n");
194*530399e8Sjob 	}
195714f4e3fSclaudio 
196*530399e8Sjob 	for (i = 0; i < p->asz; i++) {
197714f4e3fSclaudio 		switch (p->as[i].type) {
198714f4e3fSclaudio 		case CERT_AS_ID:
199*530399e8Sjob 			if (outformats & FORMAT_JSON)
200*530399e8Sjob 				printf("\t\t{ \"asid\": %u }", p->as[i].id);
201*530399e8Sjob 			else
202*530399e8Sjob 				printf("%5zu: AS: %u", i + 1, p->as[i].id);
203714f4e3fSclaudio 			break;
204714f4e3fSclaudio 		case CERT_AS_INHERIT:
205*530399e8Sjob 			if (outformats & FORMAT_JSON)
206*530399e8Sjob 				printf("\t\t{ \"asid_inherit\": \"true\" }");
207*530399e8Sjob 			else
208*530399e8Sjob 				printf("%5zu: AS: inherit", i + 1);
209714f4e3fSclaudio 			break;
210714f4e3fSclaudio 		case CERT_AS_RANGE:
211*530399e8Sjob 			if (outformats & FORMAT_JSON)
212*530399e8Sjob 				printf("\t\t{ \"asrange\": { \"min\": %u, "
213*530399e8Sjob 				    "\"max\": %u }}", p->as[i].range.min,
214*530399e8Sjob 				    p->as[i].range.max);
215*530399e8Sjob 			else
216*530399e8Sjob 				printf("%5zu: AS: %u -- %u", i + 1,
217714f4e3fSclaudio 				    p->as[i].range.min, p->as[i].range.max);
218714f4e3fSclaudio 			break;
219714f4e3fSclaudio 		}
220*530399e8Sjob 		if (outformats & FORMAT_JSON && i + 1 < p->asz + p->ipsz)
221*530399e8Sjob 			printf(",\n");
222*530399e8Sjob 		else
223*530399e8Sjob 			printf("\n");
224714f4e3fSclaudio 	}
225714f4e3fSclaudio 
226*530399e8Sjob 	for (j = 0; j < p->ipsz; j++) {
227*530399e8Sjob 		switch (p->ips[j].type) {
228*530399e8Sjob 		case CERT_IP_INHERIT:
229*530399e8Sjob 			if (outformats & FORMAT_JSON)
230*530399e8Sjob 				printf("\t\t{ \"ip_inherit\": \"true\" }");
231*530399e8Sjob 			else
232*530399e8Sjob 				printf("%5zu: IP: inherit", i + j + 1);
233*530399e8Sjob 			break;
234*530399e8Sjob 		case CERT_IP_ADDR:
235*530399e8Sjob 			ip_addr_print(&p->ips[j].ip,
236*530399e8Sjob 			    p->ips[j].afi, buf1, sizeof(buf1));
237*530399e8Sjob 			if (outformats & FORMAT_JSON)
238*530399e8Sjob 				printf("\t\t{ \"ip_prefix\": \"%s\" }", buf1);
239*530399e8Sjob 			else
240*530399e8Sjob 				printf("%5zu: IP: %s", i + j + 1, buf1);
241*530399e8Sjob 			break;
242*530399e8Sjob 		case CERT_IP_RANGE:
243*530399e8Sjob 			sockt = (p->ips[j].afi == AFI_IPV4) ?
244*530399e8Sjob 				AF_INET : AF_INET6;
245*530399e8Sjob 			inet_ntop(sockt, p->ips[j].min, buf1, sizeof(buf1));
246*530399e8Sjob 			inet_ntop(sockt, p->ips[j].max, buf2, sizeof(buf2));
247*530399e8Sjob 			if (outformats & FORMAT_JSON)
248*530399e8Sjob 				printf("\t\t{ \"ip_range\": { \"min\": \"%s\""
249*530399e8Sjob 				    ", \"max\": \"%s\" }}", buf1, buf2);
250*530399e8Sjob 			else
251*530399e8Sjob 				printf("%5zu: IP: %s -- %s", i + j + 1, buf1,
252*530399e8Sjob 				    buf2);
253*530399e8Sjob 			break;
254*530399e8Sjob 		}
255*530399e8Sjob 		if (outformats & FORMAT_JSON && i + j + 1 < p->asz + p->ipsz)
256*530399e8Sjob 			printf(",\n");
257*530399e8Sjob 		else
258*530399e8Sjob 			printf("\n");
259*530399e8Sjob 	}
260*530399e8Sjob 
261*530399e8Sjob 	if (outformats & FORMAT_JSON)
262*530399e8Sjob 		printf("\t],\n");
263714f4e3fSclaudio }
264714f4e3fSclaudio 
265714f4e3fSclaudio void
266220c707cSclaudio crl_print(const struct crl *p)
267220c707cSclaudio {
268220c707cSclaudio 	STACK_OF(X509_REVOKED)	*revlist;
269220c707cSclaudio 	X509_REVOKED *rev;
2707cdd491fSclaudio 	ASN1_INTEGER *crlnum;
271220c707cSclaudio 	int i;
2727cdd491fSclaudio 	char *serial;
273220c707cSclaudio 	time_t t;
274220c707cSclaudio 
275*530399e8Sjob 	if (outformats & FORMAT_JSON) {
276*530399e8Sjob 		printf("\t\"type\": \"crl\",\n");
277*530399e8Sjob 		printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
278*530399e8Sjob 	} else
279220c707cSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
2807cdd491fSclaudio 
2817cdd491fSclaudio 	crlnum = X509_CRL_get_ext_d2i(p->x509_crl, NID_crl_number, NULL, NULL);
2827cdd491fSclaudio 	serial = x509_convert_seqnum(__func__, crlnum);
283*530399e8Sjob 	if (serial != NULL) {
284*530399e8Sjob 		if (outformats & FORMAT_JSON)
285*530399e8Sjob 			printf("\t\"crl_serial\": \"%s\",\n", serial);
286*530399e8Sjob 		else
2877cdd491fSclaudio 			printf("CRL Serial Number: %s\n", serial);
288*530399e8Sjob 	}
2897cdd491fSclaudio 	free(serial);
2907cdd491fSclaudio 	ASN1_INTEGER_free(crlnum);
2917cdd491fSclaudio 
292*530399e8Sjob 	if (outformats & FORMAT_JSON) {
293*530399e8Sjob 		printf("\t\"valid_since\": %lld,\n", (long long)p->issued);
294*530399e8Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->expires);
295*530399e8Sjob 		printf("\t\"revoked_certs\": [\n");
296*530399e8Sjob 	} else {
297220c707cSclaudio 		printf("CRL valid since: %s\n", time2str(p->issued));
298220c707cSclaudio 		printf("CRL valid until: %s\n", time2str(p->expires));
299*530399e8Sjob 		printf("Revoked Certificates:\n");
300*530399e8Sjob 	}
301220c707cSclaudio 
302220c707cSclaudio 	revlist = X509_CRL_get_REVOKED(p->x509_crl);
303220c707cSclaudio 	for (i = 0; i < sk_X509_REVOKED_num(revlist); i++) {
304220c707cSclaudio 		rev = sk_X509_REVOKED_value(revlist, i);
3057cdd491fSclaudio 		serial = x509_convert_seqnum(__func__,
3067cdd491fSclaudio 		    X509_REVOKED_get0_serialNumber(rev));
307220c707cSclaudio 		x509_get_time(X509_REVOKED_get0_revocationDate(rev), &t);
308*530399e8Sjob 		if (serial != NULL) {
309*530399e8Sjob 			if (outformats & FORMAT_JSON) {
310*530399e8Sjob 				printf("\t\t{ \"serial\": \"%s\"", serial);
311*530399e8Sjob 				printf(", \"date\": \"%s\" }", time2str(t));
312*530399e8Sjob 				if (i + 1 < sk_X509_REVOKED_num(revlist))
313*530399e8Sjob 					printf(",");
314*530399e8Sjob 				printf("\n");
315*530399e8Sjob 			} else
316*530399e8Sjob 				printf("    Serial: %8s   Revocation Date: %s"
317*530399e8Sjob 				    "\n", serial, time2str(t));
318*530399e8Sjob 		}
3197cdd491fSclaudio 		free(serial);
320220c707cSclaudio 	}
321*530399e8Sjob 
322*530399e8Sjob 	if (outformats & FORMAT_JSON)
323*530399e8Sjob 		printf("\t],\n");
324*530399e8Sjob 	else if (i == 0)
325220c707cSclaudio 		printf("No Revoked Certificates\n");
326220c707cSclaudio }
327220c707cSclaudio 
328220c707cSclaudio void
329*530399e8Sjob mft_print(const X509 *x, const struct mft *p)
330714f4e3fSclaudio {
331714f4e3fSclaudio 	size_t i;
332714f4e3fSclaudio 	char *hash;
333714f4e3fSclaudio 
334*530399e8Sjob 	if (outformats & FORMAT_JSON) {
335*530399e8Sjob 		printf("\t\"type\": \"manifest\",\n");
336*530399e8Sjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
337*530399e8Sjob 		x509_print(x);
338*530399e8Sjob 		printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
339*530399e8Sjob 		printf("\t\"aia\": \"%s\",\n", p->aia);
340*530399e8Sjob 		printf("\t\"manifest_number\": \"%s\",\n", p->seqnum);
341*530399e8Sjob 		printf("\t\"valid_since\": %lld,\n", (long long)p->valid_since);
342*530399e8Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->valid_until);
343*530399e8Sjob 	} else {
344714f4e3fSclaudio 		printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
345714f4e3fSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
346*530399e8Sjob 		x509_print(x);
347714f4e3fSclaudio 		printf("Authority info access: %s\n", p->aia);
348714f4e3fSclaudio 		printf("Manifest Number: %s\n", p->seqnum);
349*530399e8Sjob 		printf("Manifest valid since: %s\n", time2str(p->valid_since));
350*530399e8Sjob 		printf("Manifest valid until: %s\n", time2str(p->valid_until));
351*530399e8Sjob 	}
352*530399e8Sjob 
353714f4e3fSclaudio 	for (i = 0; i < p->filesz; i++) {
354*530399e8Sjob 		if (i == 0 && outformats & FORMAT_JSON)
355*530399e8Sjob 			printf("\t\"filesandhashes\": [\n");
356*530399e8Sjob 
357714f4e3fSclaudio 		if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash),
358714f4e3fSclaudio 		    &hash) == -1)
359714f4e3fSclaudio 			errx(1, "base64_encode failure");
360*530399e8Sjob 
361*530399e8Sjob 		if (outformats & FORMAT_JSON) {
362*530399e8Sjob 			printf("\t\t{ \"filename\": \"%s\",", p->files[i].file);
363*530399e8Sjob 			printf(" \"hash\": \"%s\" }", hash);
364*530399e8Sjob 			if (i + 1 < p->filesz)
365*530399e8Sjob 				printf(",");
366*530399e8Sjob 			printf("\n");
367*530399e8Sjob 		} else {
368714f4e3fSclaudio 			printf("%5zu: %s\n", i + 1, p->files[i].file);
369714f4e3fSclaudio 			printf("\thash %s\n", hash);
370*530399e8Sjob 		}
371*530399e8Sjob 
372714f4e3fSclaudio 		free(hash);
373714f4e3fSclaudio 	}
374*530399e8Sjob 
375*530399e8Sjob 	if (outformats & FORMAT_JSON)
376*530399e8Sjob 		printf("\t],\n");
377*530399e8Sjob 
378*530399e8Sjob 
379714f4e3fSclaudio }
380714f4e3fSclaudio 
381714f4e3fSclaudio void
382*530399e8Sjob roa_print(const X509 *x, const struct roa *p)
383714f4e3fSclaudio {
384714f4e3fSclaudio 	char	 buf[128];
385714f4e3fSclaudio 	size_t	 i;
386714f4e3fSclaudio 
387*530399e8Sjob 	if (outformats & FORMAT_JSON) {
388*530399e8Sjob 		printf("\t\"type\": \"roa\",\n");
389*530399e8Sjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
390*530399e8Sjob 		x509_print(x);
391*530399e8Sjob 		printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
392*530399e8Sjob 		printf("\t\"aia\": \"%s\",\n", p->aia);
393*530399e8Sjob 		printf("\t\"valid_until\": %lld,\n", (long long)p->expires);
394*530399e8Sjob 	} else {
395714f4e3fSclaudio 		printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
396*530399e8Sjob 		x509_print(x);
397714f4e3fSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
398714f4e3fSclaudio 		printf("Authority info access: %s\n", p->aia);
399220c707cSclaudio 		printf("ROA valid until: %s\n", time2str(p->expires));
400714f4e3fSclaudio 		printf("asID: %u\n", p->asid);
401*530399e8Sjob 	}
402*530399e8Sjob 
403714f4e3fSclaudio 	for (i = 0; i < p->ipsz; i++) {
404*530399e8Sjob 		if (i == 0 && outformats & FORMAT_JSON)
405*530399e8Sjob 			printf("\t\"vrps\": [\n");
406*530399e8Sjob 
407714f4e3fSclaudio 		ip_addr_print(&p->ips[i].addr,
408714f4e3fSclaudio 			p->ips[i].afi, buf, sizeof(buf));
409*530399e8Sjob 
410*530399e8Sjob 		if (outformats & FORMAT_JSON) {
411*530399e8Sjob 			printf("\t\t{ \"prefix\": \"%s\",", buf);
412*530399e8Sjob 			printf(" \"asid\": %u,", p->asid);
413*530399e8Sjob 			printf(" \"maxlen\": %hhu }", p->ips[i].maxlength);
414*530399e8Sjob 			if (i + 1 < p->ipsz)
415*530399e8Sjob 				printf(",");
416*530399e8Sjob 			printf("\n");
417*530399e8Sjob 		} else
418*530399e8Sjob 			printf("%5zu: %s maxlen: %hhu\n", i + 1, buf,
419*530399e8Sjob 			    p->ips[i].maxlength);
420714f4e3fSclaudio 	}
421*530399e8Sjob 
422*530399e8Sjob 	if (outformats & FORMAT_JSON)
423*530399e8Sjob 		printf("\t],\n");
424714f4e3fSclaudio }
425714f4e3fSclaudio 
426714f4e3fSclaudio void
427*530399e8Sjob gbr_print(const X509 *x, const struct gbr *p)
428714f4e3fSclaudio {
429*530399e8Sjob 	size_t	i;
430*530399e8Sjob 
431*530399e8Sjob 	if (outformats & FORMAT_JSON) {
432*530399e8Sjob 		printf("\t\"type\": \"gbr\",\n");
433*530399e8Sjob 		printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
434*530399e8Sjob 		x509_print(x);
435*530399e8Sjob 		printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
436*530399e8Sjob 		printf("\t\"aia\": \"%s\",\n", p->aia);
437*530399e8Sjob 		printf("\t\"vcard\": \"");
438*530399e8Sjob 		for (i = 0; i < strlen(p->vcard); i++) {
439*530399e8Sjob 			if (p->vcard[i] == '"')
440*530399e8Sjob 				printf("\\\"");
441*530399e8Sjob 			if (p->vcard[i] == '\r')
442*530399e8Sjob 				continue;
443*530399e8Sjob 			if (p->vcard[i] == '\n')
444*530399e8Sjob 				printf("\\r\\n");
445*530399e8Sjob 			else
446*530399e8Sjob 				putchar(p->vcard[i]);
447*530399e8Sjob 		}
448*530399e8Sjob 		printf("\",\n");
449*530399e8Sjob 	} else {
450714f4e3fSclaudio 		printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
451*530399e8Sjob 		x509_print(x);
452714f4e3fSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
453714f4e3fSclaudio 		printf("Authority info access: %s\n", p->aia);
454714f4e3fSclaudio 		printf("vcard:\n%s", p->vcard);
455714f4e3fSclaudio 	}
456*530399e8Sjob }
457