xref: /openbsd/usr.sbin/rpki-client/print.c (revision 7aabcda0)
1*7aabcda0Sclaudio /*	$OpenBSD: print.c,v 1.39 2023/05/30 12:02:22 claudio 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"
31*7aabcda0Sclaudio #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) {
95*7aabcda0Sclaudio 		json_do_string("type", "tal");
96*7aabcda0Sclaudio 		json_do_string("name", p->descr);
97*7aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(ski));
98*7aabcda0Sclaudio 		json_do_array("trust_anchor_locations");
99*7aabcda0Sclaudio 		for (i = 0; i < p->urisz; i++)
100*7aabcda0Sclaudio 			json_do_string("tal", p->uri[i]);
101*7aabcda0Sclaudio 		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) {
145*7aabcda0Sclaudio 		json_do_string("cert_issuer", issuer);
146*7aabcda0Sclaudio 		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 
157*7aabcda0Sclaudio static void
158*7aabcda0Sclaudio as_resources_print(struct cert_as *as, size_t asz)
159*7aabcda0Sclaudio {
160*7aabcda0Sclaudio 	size_t i;
161*7aabcda0Sclaudio 
162*7aabcda0Sclaudio 	for (i = 0; i < asz; i++) {
163*7aabcda0Sclaudio 		if (outformats & FORMAT_JSON)
164*7aabcda0Sclaudio 			json_do_object("resource");
165*7aabcda0Sclaudio 		switch (as[i].type) {
166*7aabcda0Sclaudio 		case CERT_AS_ID:
167*7aabcda0Sclaudio 			if (outformats & FORMAT_JSON) {
168*7aabcda0Sclaudio 				json_do_uint("asid", as[i].id);
169*7aabcda0Sclaudio 			} else {
170*7aabcda0Sclaudio 				if (i > 0)
171*7aabcda0Sclaudio 					printf("%26s", "");
172*7aabcda0Sclaudio 				printf("AS: %u", as[i].id);
173*7aabcda0Sclaudio 			}
174*7aabcda0Sclaudio 			break;
175*7aabcda0Sclaudio 		case CERT_AS_INHERIT:
176*7aabcda0Sclaudio 			if (outformats & FORMAT_JSON) {
177*7aabcda0Sclaudio 				json_do_bool("asid_inherit", 1);
178*7aabcda0Sclaudio 			} else {
179*7aabcda0Sclaudio 				if (i > 0)
180*7aabcda0Sclaudio 					printf("%26s", "");
181*7aabcda0Sclaudio 				printf("AS: inherit");
182*7aabcda0Sclaudio 			}
183*7aabcda0Sclaudio 			break;
184*7aabcda0Sclaudio 		case CERT_AS_RANGE:
185*7aabcda0Sclaudio 			if (outformats & FORMAT_JSON) {
186*7aabcda0Sclaudio 				json_do_object("asrange");
187*7aabcda0Sclaudio 				json_do_uint("min", as[i].range.min);
188*7aabcda0Sclaudio 				json_do_uint("max", as[i].range.max);
189*7aabcda0Sclaudio 				json_do_end();
190*7aabcda0Sclaudio 			} else {
191*7aabcda0Sclaudio 				if (i > 0)
192*7aabcda0Sclaudio 					printf("%26s", "");
193*7aabcda0Sclaudio 				printf("AS: %u -- %u", as[i].range.min,
194*7aabcda0Sclaudio 				    as[i].range.max);
195*7aabcda0Sclaudio 			}
196*7aabcda0Sclaudio 			break;
197*7aabcda0Sclaudio 		}
198*7aabcda0Sclaudio 		if (outformats & FORMAT_JSON)
199*7aabcda0Sclaudio 			json_do_end();
200*7aabcda0Sclaudio 		else
201*7aabcda0Sclaudio 			printf("\n");
202*7aabcda0Sclaudio 	}
203*7aabcda0Sclaudio }
204*7aabcda0Sclaudio 
205*7aabcda0Sclaudio static void
206*7aabcda0Sclaudio ip_resources_print(struct cert_ip *ips, size_t ipsz, size_t asz)
207*7aabcda0Sclaudio {
208*7aabcda0Sclaudio 	char buf1[64], buf2[64];
209*7aabcda0Sclaudio 	size_t i;
210*7aabcda0Sclaudio 	int sockt;
211*7aabcda0Sclaudio 
212*7aabcda0Sclaudio 
213*7aabcda0Sclaudio 	for (i = 0; i < ipsz; i++) {
214*7aabcda0Sclaudio 		if (outformats & FORMAT_JSON)
215*7aabcda0Sclaudio 			json_do_object("resource");
216*7aabcda0Sclaudio 		switch (ips[i].type) {
217*7aabcda0Sclaudio 		case CERT_IP_INHERIT:
218*7aabcda0Sclaudio 			if (outformats & FORMAT_JSON) {
219*7aabcda0Sclaudio 				json_do_bool("ip_inherit", 1);
220*7aabcda0Sclaudio 			} else {
221*7aabcda0Sclaudio 				if (i > 0 || asz > 0)
222*7aabcda0Sclaudio 					printf("%26s", "");
223*7aabcda0Sclaudio 				printf("IP: inherit");
224*7aabcda0Sclaudio 			}
225*7aabcda0Sclaudio 			break;
226*7aabcda0Sclaudio 		case CERT_IP_ADDR:
227*7aabcda0Sclaudio 			ip_addr_print(&ips[i].ip, ips[i].afi, buf1,
228*7aabcda0Sclaudio 			    sizeof(buf1));
229*7aabcda0Sclaudio 			if (outformats & FORMAT_JSON) {
230*7aabcda0Sclaudio 				json_do_string("ip_prefix", buf1);
231*7aabcda0Sclaudio 			} else {
232*7aabcda0Sclaudio 				if (i > 0 || asz > 0)
233*7aabcda0Sclaudio 					printf("%26s", "");
234*7aabcda0Sclaudio 				printf("IP: %s", buf1);
235*7aabcda0Sclaudio 			}
236*7aabcda0Sclaudio 			break;
237*7aabcda0Sclaudio 		case CERT_IP_RANGE:
238*7aabcda0Sclaudio 			sockt = (ips[i].afi == AFI_IPV4) ?
239*7aabcda0Sclaudio 			    AF_INET : AF_INET6;
240*7aabcda0Sclaudio 			inet_ntop(sockt, ips[i].min, buf1, sizeof(buf1));
241*7aabcda0Sclaudio 			inet_ntop(sockt, ips[i].max, buf2, sizeof(buf2));
242*7aabcda0Sclaudio 			if (outformats & FORMAT_JSON) {
243*7aabcda0Sclaudio 				json_do_object("ip_range");
244*7aabcda0Sclaudio 				json_do_string("min", buf1);
245*7aabcda0Sclaudio 				json_do_string("max", buf2);
246*7aabcda0Sclaudio 				json_do_end();
247*7aabcda0Sclaudio 			} else {
248*7aabcda0Sclaudio 				if (i > 0 || asz > 0)
249*7aabcda0Sclaudio 					printf("%26s", "");
250*7aabcda0Sclaudio 				printf("IP: %s -- %s", buf1, buf2);
251*7aabcda0Sclaudio 			}
252*7aabcda0Sclaudio 			break;
253*7aabcda0Sclaudio 		}
254*7aabcda0Sclaudio 		if (outformats & FORMAT_JSON)
255*7aabcda0Sclaudio 			json_do_end();
256*7aabcda0Sclaudio 		else
257*7aabcda0Sclaudio 			printf("\n");
258*7aabcda0Sclaudio 	}
259*7aabcda0Sclaudio }
260*7aabcda0Sclaudio 
261530399e8Sjob void
262714f4e3fSclaudio cert_print(const struct cert *p)
263714f4e3fSclaudio {
264530399e8Sjob 	if (outformats & FORMAT_JSON) {
265530399e8Sjob 		if (p->pubkey != NULL)
266*7aabcda0Sclaudio 			json_do_string("type", "router_key");
267530399e8Sjob 		else
268*7aabcda0Sclaudio 			json_do_string("type", "ca_cert");
269*7aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
270530399e8Sjob 		if (p->aki != NULL)
271*7aabcda0Sclaudio 			json_do_string("aki", pretty_key_id(p->aki));
272530399e8Sjob 		x509_print(p->x509);
273530399e8Sjob 		if (p->aia != NULL)
274*7aabcda0Sclaudio 			json_do_string("aia", p->aia);
275530399e8Sjob 		if (p->mft != NULL)
276*7aabcda0Sclaudio 			json_do_string("manifest", p->mft);
277530399e8Sjob 		if (p->repo != NULL)
278*7aabcda0Sclaudio 			json_do_string("carepository", p->repo);
279530399e8Sjob 		if (p->notify != NULL)
280*7aabcda0Sclaudio 			json_do_string("notify_url", p->notify);
281530399e8Sjob 		if (p->pubkey != NULL)
282*7aabcda0Sclaudio 			json_do_string("router_key", p->pubkey);
283*7aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
284*7aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
285894936b4Sjob 		if (p->expires)
286*7aabcda0Sclaudio 			json_do_int("expires", p->expires);
287*7aabcda0Sclaudio 		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 
318*7aabcda0Sclaudio 	as_resources_print(p->as, p->asz);
319*7aabcda0Sclaudio 	ip_resources_print(p->ips, p->ipsz, p->asz);
320530399e8Sjob 
321530399e8Sjob 	if (outformats & FORMAT_JSON)
322*7aabcda0Sclaudio 		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;
3307cdd491fSclaudio 	ASN1_INTEGER *crlnum;
331e0b87278Sjob 	X509_NAME *xissuer;
332220c707cSclaudio 	int i;
333e0b87278Sjob 	char *issuer, *serial;
334220c707cSclaudio 	time_t t;
335220c707cSclaudio 
336530399e8Sjob 	if (outformats & FORMAT_JSON) {
337*7aabcda0Sclaudio 		json_do_string("type", "crl");
338*7aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
339530399e8Sjob 	} else
340220c707cSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
3417cdd491fSclaudio 
342e0b87278Sjob 	xissuer = X509_CRL_get_issuer(p->x509_crl);
343e0b87278Sjob 	issuer = X509_NAME_oneline(xissuer, NULL, 0);
3447cdd491fSclaudio 	crlnum = X509_CRL_get_ext_d2i(p->x509_crl, NID_crl_number, NULL, NULL);
3457cdd491fSclaudio 	serial = x509_convert_seqnum(__func__, crlnum);
346e0b87278Sjob 	if (issuer != NULL && serial != NULL) {
347e0b87278Sjob 		if (outformats & FORMAT_JSON) {
348*7aabcda0Sclaudio 			json_do_string("crl_issuer", issuer);
349*7aabcda0Sclaudio 			json_do_string("crl_serial", serial);
350e0b87278Sjob 		} else {
351e0b87278Sjob 			printf("CRL issuer:               %s\n", issuer);
352e0b87278Sjob 			printf("CRL serial number:        %s\n", serial);
353530399e8Sjob 		}
354e0b87278Sjob 	}
355e0b87278Sjob 	free(issuer);
3567cdd491fSclaudio 	free(serial);
3577cdd491fSclaudio 	ASN1_INTEGER_free(crlnum);
3587cdd491fSclaudio 
359530399e8Sjob 	if (outformats & FORMAT_JSON) {
360*7aabcda0Sclaudio 		json_do_int("valid_since", p->lastupdate);
361*7aabcda0Sclaudio 		json_do_int("valid_until", p->nextupdate);
362*7aabcda0Sclaudio 		json_do_array("revoked_certs");
363530399e8Sjob 	} else {
3644dbb22b8Sjob 		printf("CRL last update:          %s\n",
3659f544822Sjob 		    time2str(p->lastupdate));
3664dbb22b8Sjob 		printf("CRL next update:          %s\n",
3679f544822Sjob 		    time2str(p->nextupdate));
368530399e8Sjob 		printf("Revoked Certificates:\n");
369530399e8Sjob 	}
370220c707cSclaudio 
371220c707cSclaudio 	revlist = X509_CRL_get_REVOKED(p->x509_crl);
372220c707cSclaudio 	for (i = 0; i < sk_X509_REVOKED_num(revlist); i++) {
373220c707cSclaudio 		rev = sk_X509_REVOKED_value(revlist, i);
3747cdd491fSclaudio 		serial = x509_convert_seqnum(__func__,
3757cdd491fSclaudio 		    X509_REVOKED_get0_serialNumber(rev));
376220c707cSclaudio 		x509_get_time(X509_REVOKED_get0_revocationDate(rev), &t);
377530399e8Sjob 		if (serial != NULL) {
378530399e8Sjob 			if (outformats & FORMAT_JSON) {
379*7aabcda0Sclaudio 				json_do_object("cert");
380*7aabcda0Sclaudio 				json_do_string("serial", serial);
381*7aabcda0Sclaudio 				json_do_string("date", time2str(t));
382*7aabcda0Sclaudio 				json_do_end();
383530399e8Sjob 			} else
3844486d057Sjob 				printf("%25s Serial: %8s   Revocation Date: %s"
3854486d057Sjob 				    "\n", "", serial, time2str(t));
386530399e8Sjob 		}
3877cdd491fSclaudio 		free(serial);
388220c707cSclaudio 	}
389530399e8Sjob 
390530399e8Sjob 	if (outformats & FORMAT_JSON)
391*7aabcda0Sclaudio 		json_do_end();
392530399e8Sjob 	else if (i == 0)
393220c707cSclaudio 		printf("No Revoked Certificates\n");
394220c707cSclaudio }
395220c707cSclaudio 
396220c707cSclaudio void
397530399e8Sjob mft_print(const X509 *x, const struct mft *p)
398714f4e3fSclaudio {
399714f4e3fSclaudio 	size_t i;
400714f4e3fSclaudio 	char *hash;
401714f4e3fSclaudio 
402530399e8Sjob 	if (outformats & FORMAT_JSON) {
403*7aabcda0Sclaudio 		json_do_string("type", "manifest");
404*7aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
405530399e8Sjob 		x509_print(x);
406*7aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
407*7aabcda0Sclaudio 		json_do_string("aia", p->aia);
408*7aabcda0Sclaudio 		json_do_string("sia", p->sia);
409*7aabcda0Sclaudio 		json_do_string("manifest_number", p->seqnum);
4101bb1e509Sjob 		if (p->signtime != 0)
411*7aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
412*7aabcda0Sclaudio 		json_do_int("valid_since", p->thisupdate);
413*7aabcda0Sclaudio 		json_do_int("valid_until", p->nextupdate);
414894936b4Sjob 		if (p->expires)
415*7aabcda0Sclaudio 			json_do_int("expires", p->expires);
416530399e8Sjob 	} else {
417714f4e3fSclaudio 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
418714f4e3fSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
419530399e8Sjob 		x509_print(x);
420714f4e3fSclaudio 		printf("Authority info access:    %s\n", p->aia);
4212cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
422489e308aSjob 		printf("Manifest number:          %s\n", p->seqnum);
4231bb1e509Sjob 		if (p->signtime != 0)
4241bb1e509Sjob 			printf("Signing time:             %s\n",
4251bb1e509Sjob 			    time2str(p->signtime));
4264dbb22b8Sjob 		printf("Manifest this update:     %s\n", time2str(p->thisupdate));
4274dbb22b8Sjob 		printf("Manifest next update:     %s\n", time2str(p->nextupdate));
4284486d057Sjob 		printf("Files and hashes:         ");
429530399e8Sjob 	}
430530399e8Sjob 
431*7aabcda0Sclaudio 	if (outformats & FORMAT_JSON)
432*7aabcda0Sclaudio 		json_do_array("filesandhashes");
433714f4e3fSclaudio 	for (i = 0; i < p->filesz; i++) {
434714f4e3fSclaudio 		if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash),
435714f4e3fSclaudio 		    &hash) == -1)
436714f4e3fSclaudio 			errx(1, "base64_encode failure");
437530399e8Sjob 
438530399e8Sjob 		if (outformats & FORMAT_JSON) {
439*7aabcda0Sclaudio 			json_do_object("filehash");
440*7aabcda0Sclaudio 			json_do_string("filename", p->files[i].file);
441*7aabcda0Sclaudio 			json_do_string("hash", hash);
442*7aabcda0Sclaudio 			json_do_end();
443530399e8Sjob 		} else {
4444486d057Sjob 			if (i > 0)
4454486d057Sjob 				printf("%26s", "");
4464486d057Sjob 			printf("%zu: %s (hash: %s)\n", i + 1, p->files[i].file,
4474486d057Sjob 			    hash);
448530399e8Sjob 		}
449530399e8Sjob 
450714f4e3fSclaudio 		free(hash);
451714f4e3fSclaudio 	}
452530399e8Sjob 	if (outformats & FORMAT_JSON)
453*7aabcda0Sclaudio 		json_do_end();
454714f4e3fSclaudio }
455714f4e3fSclaudio 
456714f4e3fSclaudio void
457530399e8Sjob roa_print(const X509 *x, const struct roa *p)
458714f4e3fSclaudio {
459714f4e3fSclaudio 	char	 buf[128];
460714f4e3fSclaudio 	size_t	 i;
461714f4e3fSclaudio 
462530399e8Sjob 	if (outformats & FORMAT_JSON) {
463*7aabcda0Sclaudio 		json_do_string("type", "roa");
464*7aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
465530399e8Sjob 		x509_print(x);
466*7aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
467*7aabcda0Sclaudio 		json_do_string("aia", p->aia);
468*7aabcda0Sclaudio 		json_do_string("sia", p->sia);
4691bb1e509Sjob 		if (p->signtime != 0)
470*7aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
471*7aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
472*7aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
4739c1f5d6bSjob 		if (p->expires)
474*7aabcda0Sclaudio 			json_do_int("expires", p->expires);
475530399e8Sjob 	} else {
476714f4e3fSclaudio 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
477530399e8Sjob 		x509_print(x);
478714f4e3fSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
479714f4e3fSclaudio 		printf("Authority info access:    %s\n", p->aia);
4802cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
4811bb1e509Sjob 		if (p->signtime != 0)
4821bb1e509Sjob 			printf("Signing time:             %s\n",
4831bb1e509Sjob 			    time2str(p->signtime));
4844dbb22b8Sjob 		printf("ROA not before:           %s\n",
485f5999ddfSjob 		    time2str(p->notbefore));
4864dbb22b8Sjob 		printf("ROA not after:            %s\n", time2str(p->notafter));
487714f4e3fSclaudio 		printf("asID:                     %u\n", p->asid);
4884486d057Sjob 		printf("IP address blocks:        ");
489530399e8Sjob 	}
490530399e8Sjob 
491*7aabcda0Sclaudio 	if (outformats & FORMAT_JSON)
492*7aabcda0Sclaudio 		json_do_array("vrps");
493714f4e3fSclaudio 	for (i = 0; i < p->ipsz; i++) {
494714f4e3fSclaudio 		ip_addr_print(&p->ips[i].addr,
495714f4e3fSclaudio 		    p->ips[i].afi, buf, sizeof(buf));
496530399e8Sjob 
497530399e8Sjob 		if (outformats & FORMAT_JSON) {
498*7aabcda0Sclaudio 			json_do_object("vrp");
499*7aabcda0Sclaudio 			json_do_string("prefix", buf);
500*7aabcda0Sclaudio 			json_do_uint("asid", p->asid);
501*7aabcda0Sclaudio 			json_do_uint("maxlen", p->ips[i].maxlength);
502*7aabcda0Sclaudio 			json_do_end();
5034486d057Sjob 		} else {
5044486d057Sjob 			if (i > 0)
5054486d057Sjob 				printf("%26s", "");
5064486d057Sjob 			printf("%s maxlen: %hhu\n", buf, p->ips[i].maxlength);
5074486d057Sjob 		}
508714f4e3fSclaudio 	}
509530399e8Sjob 	if (outformats & FORMAT_JSON)
510*7aabcda0Sclaudio 		json_do_end();
511714f4e3fSclaudio }
512714f4e3fSclaudio 
513714f4e3fSclaudio void
514530399e8Sjob gbr_print(const X509 *x, const struct gbr *p)
515714f4e3fSclaudio {
516530399e8Sjob 	if (outformats & FORMAT_JSON) {
517*7aabcda0Sclaudio 		json_do_string("type", "gbr");
518*7aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
519530399e8Sjob 		x509_print(x);
520*7aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
521*7aabcda0Sclaudio 		json_do_string("aia", p->aia);
522*7aabcda0Sclaudio 		json_do_string("sia", p->sia);
5231bb1e509Sjob 		if (p->signtime != 0)
524*7aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
525*7aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
526*7aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
527894936b4Sjob 		if (p->expires)
528*7aabcda0Sclaudio 			json_do_int("expires", p->expires);
529*7aabcda0Sclaudio 		json_do_string("vcard", p->vcard);
530530399e8Sjob 	} else {
531714f4e3fSclaudio 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
532530399e8Sjob 		x509_print(x);
533714f4e3fSclaudio 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
534714f4e3fSclaudio 		printf("Authority info access:    %s\n", p->aia);
5352cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
5361bb1e509Sjob 		if (p->signtime != 0)
5371bb1e509Sjob 			printf("Signing time:             %s\n",
5381bb1e509Sjob 			    time2str(p->signtime));
5394dbb22b8Sjob 		printf("GBR not before:           %s\n",
540f5999ddfSjob 		    time2str(p->notbefore));
5414dbb22b8Sjob 		printf("GBR not after:            %s\n", time2str(p->notafter));
542714f4e3fSclaudio 		printf("vcard:\n%s", p->vcard);
543714f4e3fSclaudio 	}
544530399e8Sjob }
54504834fbdSjob 
54604834fbdSjob void
54704834fbdSjob rsc_print(const X509 *x, const struct rsc *p)
54804834fbdSjob {
54904834fbdSjob 	char	*hash;
550*7aabcda0Sclaudio 	size_t	 i;
55104834fbdSjob 
55204834fbdSjob 	if (outformats & FORMAT_JSON) {
553*7aabcda0Sclaudio 		json_do_string("type", "rsc");
554*7aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
55504834fbdSjob 		x509_print(x);
556*7aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
557*7aabcda0Sclaudio 		json_do_string("aia", p->aia);
5581bb1e509Sjob 		if (p->signtime != 0)
559*7aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
560*7aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
561*7aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
562894936b4Sjob 		if (p->expires)
563*7aabcda0Sclaudio 			json_do_int("expires", p->expires);
564*7aabcda0Sclaudio 		json_do_array("signed_with_resources");
56504834fbdSjob 	} else {
56604834fbdSjob 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
56704834fbdSjob 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
56804834fbdSjob 		x509_print(x);
56904834fbdSjob 		printf("Authority info access:    %s\n", p->aia);
5701bb1e509Sjob 		if (p->signtime != 0)
5711bb1e509Sjob 			printf("Signing time:             %s\n",
5721bb1e509Sjob 			    time2str(p->signtime));
5734dbb22b8Sjob 		printf("RSC not before:           %s\n",
574f5999ddfSjob 		    time2str(p->notbefore));
5754dbb22b8Sjob 		printf("RSC not after:            %s\n", time2str(p->notafter));
5764486d057Sjob 		printf("Signed with resources:    ");
57704834fbdSjob 	}
57804834fbdSjob 
579*7aabcda0Sclaudio 	as_resources_print(p->as, p->asz);
580*7aabcda0Sclaudio 	ip_resources_print(p->ips, p->ipsz, p->asz);
58104834fbdSjob 
58204834fbdSjob 	if (outformats & FORMAT_JSON) {
583*7aabcda0Sclaudio 		json_do_end();
584*7aabcda0Sclaudio 		json_do_array("filenamesandhashes");
58504834fbdSjob 	} else
5864486d057Sjob 		printf("Filenames and hashes:     ");
58704834fbdSjob 
58804834fbdSjob 	for (i = 0; i < p->filesz; i++) {
58904834fbdSjob 		if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash),
59004834fbdSjob 		    &hash) == -1)
59104834fbdSjob 			errx(1, "base64_encode failure");
59204834fbdSjob 
59304834fbdSjob 		if (outformats & FORMAT_JSON) {
594*7aabcda0Sclaudio 			json_do_object("filehash");
595*7aabcda0Sclaudio 			if (p->files[i].filename)
596*7aabcda0Sclaudio 				json_do_string("filename",
597*7aabcda0Sclaudio 				    p->files[i].filename);
598*7aabcda0Sclaudio 			json_do_string("hash_digest", hash);
599*7aabcda0Sclaudio 			json_do_end();
60004834fbdSjob 		} else {
6014486d057Sjob 			if (i > 0)
6024486d057Sjob 				printf("%26s", "");
6034486d057Sjob 			printf("%zu: %s (hash: %s)\n", i + 1,
6044486d057Sjob 			    p->files[i].filename ? p->files[i].filename
6054486d057Sjob 			    : "no filename", hash);
60604834fbdSjob 		}
60704834fbdSjob 
60804834fbdSjob 		free(hash);
60904834fbdSjob 	}
61004834fbdSjob 
61104834fbdSjob 	if (outformats & FORMAT_JSON)
612*7aabcda0Sclaudio 		json_do_end();
61304834fbdSjob }
614a29ddfd5Sjob 
615ba54bc08Sjob static void
616*7aabcda0Sclaudio aspa_provider(uint32_t as, enum afi afi)
617*7aabcda0Sclaudio {
618*7aabcda0Sclaudio 	if (outformats & FORMAT_JSON) {
619*7aabcda0Sclaudio 		json_do_object("aspa");
620*7aabcda0Sclaudio 		json_do_uint("asid", as);
621*7aabcda0Sclaudio 		if (afi == AFI_IPV4)
622*7aabcda0Sclaudio 			json_do_string("afi_limit", "ipv4");
623*7aabcda0Sclaudio 		if (afi == AFI_IPV6)
624*7aabcda0Sclaudio 			json_do_string("afi_limit", "ipv6");
625*7aabcda0Sclaudio 		json_do_end();
626*7aabcda0Sclaudio 	} else {
627*7aabcda0Sclaudio 		printf("AS: %u", as);
628*7aabcda0Sclaudio 		if (afi == AFI_IPV4)
629*7aabcda0Sclaudio 			printf(" (IPv4 only)");
630*7aabcda0Sclaudio 		if (afi == AFI_IPV6)
631*7aabcda0Sclaudio 			printf(" (IPv6 only)");
632*7aabcda0Sclaudio 		printf("\n");
633*7aabcda0Sclaudio 	}
634*7aabcda0Sclaudio }
635*7aabcda0Sclaudio 
636*7aabcda0Sclaudio static void
637*7aabcda0Sclaudio aspa_providers(const struct aspa *a)
638ba54bc08Sjob {
639ba54bc08Sjob 	size_t	i;
640*7aabcda0Sclaudio 	int	hasv4 = 0, hasv6 = 0;
641ba54bc08Sjob 
642ba54bc08Sjob 	for (i = 0; i < a->providersz; i++) {
643*7aabcda0Sclaudio 		if ((outformats & FORMAT_JSON) == 0 && i > 0)
644*7aabcda0Sclaudio 			printf("%26s", "");
645*7aabcda0Sclaudio 		aspa_provider(a->providers[i].as, a->providers[i].afi);
646*7aabcda0Sclaudio 
647ba54bc08Sjob 		switch (a->providers[i].afi) {
648ba54bc08Sjob 		case AFI_IPV4:
649*7aabcda0Sclaudio 			hasv4 = 1;
650ba54bc08Sjob 			break;
651ba54bc08Sjob 		case AFI_IPV6:
652*7aabcda0Sclaudio 			hasv6 = 1;
653ba54bc08Sjob 			break;
654ba54bc08Sjob 		default:
655*7aabcda0Sclaudio 			hasv4 = hasv6 = 1;
656*7aabcda0Sclaudio 			break;
657ba54bc08Sjob 		}
658ba54bc08Sjob 	}
659ba54bc08Sjob 
660*7aabcda0Sclaudio 	if (!hasv4)
661*7aabcda0Sclaudio 		aspa_provider(0, AFI_IPV4);
662*7aabcda0Sclaudio 	if (!hasv6)
663*7aabcda0Sclaudio 		aspa_provider(0, AFI_IPV6);
664ba54bc08Sjob }
665ba54bc08Sjob 
666a29ddfd5Sjob void
667a29ddfd5Sjob aspa_print(const X509 *x, const struct aspa *p)
668a29ddfd5Sjob {
669a29ddfd5Sjob 	if (outformats & FORMAT_JSON) {
670*7aabcda0Sclaudio 		json_do_string("type", "aspa");
671*7aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
672a29ddfd5Sjob 		x509_print(x);
673*7aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
674*7aabcda0Sclaudio 		json_do_string("aia", p->aia);
675*7aabcda0Sclaudio 		json_do_string("sia", p->sia);
6761bb1e509Sjob 		if (p->signtime != 0)
677*7aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
678*7aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
679*7aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
6809c1f5d6bSjob 		if (p->expires)
681*7aabcda0Sclaudio 			json_do_int("expires", p->expires);
682*7aabcda0Sclaudio 		json_do_uint("customer_asid", p->custasid);
683*7aabcda0Sclaudio 		json_do_array("provider_set");
684a29ddfd5Sjob 	} else {
685a29ddfd5Sjob 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
686a29ddfd5Sjob 		x509_print(x);
687a29ddfd5Sjob 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
688a29ddfd5Sjob 		printf("Authority info access:    %s\n", p->aia);
6892cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
6901bb1e509Sjob 		if (p->signtime != 0)
6911bb1e509Sjob 			printf("Signing time:             %s\n",
6921bb1e509Sjob 			    time2str(p->signtime));
6934dbb22b8Sjob 		printf("ASPA not before:          %s\n",
694f5999ddfSjob 		    time2str(p->notbefore));
6954dbb22b8Sjob 		printf("ASPA not after:           %s\n", time2str(p->notafter));
696ba54bc08Sjob 		printf("Customer ASID:            %u\n", p->custasid);
697ba54bc08Sjob 		printf("Provider set:             ");
698a29ddfd5Sjob 	}
699*7aabcda0Sclaudio 
700*7aabcda0Sclaudio 	aspa_providers(p);
701*7aabcda0Sclaudio 
702*7aabcda0Sclaudio 	if (outformats & FORMAT_JSON)
703*7aabcda0Sclaudio 		json_do_end();
704a29ddfd5Sjob }
705ee2a33daSjob 
706ee2a33daSjob static void
707ee2a33daSjob takey_print(char *name, const struct takey *t)
708ee2a33daSjob {
709ee2a33daSjob 	char	*spki = NULL;
710ee2a33daSjob 	size_t	 i, j = 0;
711ee2a33daSjob 
712ee2a33daSjob 	if (base64_encode(t->pubkey, t->pubkeysz, &spki) != 0)
713ee2a33daSjob 		errx(1, "base64_encode failed in %s", __func__);
714ee2a33daSjob 
715ee2a33daSjob 	if (outformats & FORMAT_JSON) {
716*7aabcda0Sclaudio 		json_do_object("takey");
717*7aabcda0Sclaudio 		json_do_string("name", name);
718*7aabcda0Sclaudio 		json_do_array("comments");
719*7aabcda0Sclaudio 		for (i = 0; i < t->commentsz; i++)
720*7aabcda0Sclaudio 			json_do_string("comment", t->comments[i]);
721*7aabcda0Sclaudio 		json_do_end();
722*7aabcda0Sclaudio 		json_do_array("uris");
723*7aabcda0Sclaudio 		for (i = 0; i < t->urisz; i++)
724*7aabcda0Sclaudio 			json_do_string("uri", t->uris[i]);
725*7aabcda0Sclaudio 		json_do_end();
726*7aabcda0Sclaudio 		json_do_string("spki", spki);
727ee2a33daSjob 	} else {
728ee2a33daSjob 		printf("TAL derived from the '%s' Trust Anchor Key:\n\n", name);
729ee2a33daSjob 
730*7aabcda0Sclaudio 		for (i = 0; i < t->commentsz; i++)
731ee2a33daSjob 			printf("\t# %s\n", t->comments[i]);
732*7aabcda0Sclaudio 		printf("\n");
733*7aabcda0Sclaudio 		for (i = 0; i < t->urisz; i++)
734*7aabcda0Sclaudio 			printf("\t%s\n\t", t->uris[i]);
735ee2a33daSjob 		for (i = 0; i < strlen(spki); i++) {
736ee2a33daSjob 			printf("%c", spki[i]);
737*7aabcda0Sclaudio 			if ((++j % 64) == 0)
738ee2a33daSjob 				printf("\n\t");
739ee2a33daSjob 		}
740ee2a33daSjob 		printf("\n\n");
741ee2a33daSjob 	}
742ee2a33daSjob 
743ee2a33daSjob 	free(spki);
744ee2a33daSjob }
745ee2a33daSjob 
746ee2a33daSjob void
747ee2a33daSjob tak_print(const X509 *x, const struct tak *p)
748ee2a33daSjob {
749ee2a33daSjob 	if (outformats & FORMAT_JSON) {
750*7aabcda0Sclaudio 		json_do_string("type", "tak");
751*7aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
752ee2a33daSjob 		x509_print(x);
753*7aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
754*7aabcda0Sclaudio 		json_do_string("aia", p->aia);
755*7aabcda0Sclaudio 		json_do_string("sia", p->sia);
7561bb1e509Sjob 		if (p->signtime != 0)
757*7aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
758*7aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
759*7aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
760894936b4Sjob 		if (p->expires)
761*7aabcda0Sclaudio 			json_do_int("expires", p->expires);
762*7aabcda0Sclaudio 		json_do_array("takeys");
763ee2a33daSjob 	} else {
764ee2a33daSjob 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
765ee2a33daSjob 		x509_print(x);
766ee2a33daSjob 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
767ee2a33daSjob 		printf("Authority info access:    %s\n", p->aia);
7682cf0e122Sjob 		printf("Subject info access:      %s\n", p->sia);
7691bb1e509Sjob 		if (p->signtime != 0)
7701bb1e509Sjob 			printf("Signing time:             %s\n",
7711bb1e509Sjob 			    time2str(p->signtime));
7724dbb22b8Sjob 		printf("TAK not before:           %s\n",
773f5999ddfSjob 		    time2str(p->notbefore));
7744dbb22b8Sjob 		printf("TAK not after:            %s\n", time2str(p->notafter));
775ee2a33daSjob 	}
776ee2a33daSjob 
777ee2a33daSjob 	takey_print("current", p->current);
778*7aabcda0Sclaudio 	if (p->predecessor != NULL)
779ee2a33daSjob 		takey_print("predecessor", p->predecessor);
780*7aabcda0Sclaudio 	if (p->successor != NULL)
781ee2a33daSjob 		takey_print("successor", p->successor);
782ee2a33daSjob 
783ee2a33daSjob 	if (outformats & FORMAT_JSON)
784*7aabcda0Sclaudio 		json_do_end();
785ee2a33daSjob }
786ef3f6f56Sjob 
787ef3f6f56Sjob void
788ef3f6f56Sjob geofeed_print(const X509 *x, const struct geofeed *p)
789ef3f6f56Sjob {
790ef3f6f56Sjob 	char	 buf[128];
791ef3f6f56Sjob 	size_t	 i;
792ef3f6f56Sjob 
793ef3f6f56Sjob 	if (outformats & FORMAT_JSON) {
794*7aabcda0Sclaudio 		json_do_string("type", "geofeed");
795*7aabcda0Sclaudio 		json_do_string("ski", pretty_key_id(p->ski));
796ef3f6f56Sjob 		x509_print(x);
797*7aabcda0Sclaudio 		json_do_string("aki", pretty_key_id(p->aki));
798*7aabcda0Sclaudio 		json_do_string("aia", p->aia);
7991bb1e509Sjob 		if (p->signtime != 0)
800*7aabcda0Sclaudio 			json_do_int("signing_time", p->signtime);
801*7aabcda0Sclaudio 		json_do_int("valid_since", p->notbefore);
802*7aabcda0Sclaudio 		json_do_int("valid_until", p->notafter);
803*7aabcda0Sclaudio 		if (p->expires)
804*7aabcda0Sclaudio 			json_do_int("expires", p->expires);
805*7aabcda0Sclaudio 		json_do_array("records");
806ef3f6f56Sjob 	} else {
807ef3f6f56Sjob 		printf("Subject key identifier:   %s\n", pretty_key_id(p->ski));
808ef3f6f56Sjob 		x509_print(x);
809ef3f6f56Sjob 		printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
810ef3f6f56Sjob 		printf("Authority info access:    %s\n", p->aia);
8111bb1e509Sjob 		if (p->signtime != 0)
8121bb1e509Sjob 			printf("Signing time:             %s\n",
8131bb1e509Sjob 			    time2str(p->signtime));
8144dbb22b8Sjob 		printf("Geofeed not before:       %s\n",
815f5999ddfSjob 		    time2str(p->notbefore));
8164dbb22b8Sjob 		printf("Geofeed not after:        %s\n", time2str(p->notafter));
817bd19f13dSjob 		printf("Geofeed CSV records:      ");
818ef3f6f56Sjob 	}
819ef3f6f56Sjob 
820ef3f6f56Sjob 	for (i = 0; i < p->geoipsz; i++) {
821ef3f6f56Sjob 		if (p->geoips[i].ip->type != CERT_IP_ADDR)
822ef3f6f56Sjob 			continue;
823ef3f6f56Sjob 
824ef3f6f56Sjob 		ip_addr_print(&p->geoips[i].ip->ip, p->geoips[i].ip->afi, buf,
825ef3f6f56Sjob 		    sizeof(buf));
826*7aabcda0Sclaudio 		if (outformats & FORMAT_JSON) {
827*7aabcda0Sclaudio 			json_do_object("geoip");
828*7aabcda0Sclaudio 			json_do_string("prefix", buf);
829*7aabcda0Sclaudio 			json_do_string("location", p->geoips[i].loc);
830*7aabcda0Sclaudio 			json_do_end();
831*7aabcda0Sclaudio 		} else {
8324486d057Sjob 			if (i > 0)
8334486d057Sjob 				printf("%26s", "");
834*7aabcda0Sclaudio 			printf("IP: %s (%s)\n", buf, p->geoips[i].loc);
8354486d057Sjob 		}
836ef3f6f56Sjob 	}
837ef3f6f56Sjob 
838ef3f6f56Sjob 	if (outformats & FORMAT_JSON)
839*7aabcda0Sclaudio 		json_do_end();
840ef3f6f56Sjob }
841