xref: /netbsd-src/external/mpl/bind/dist/bin/dnssec/dnssec-keygen.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: dnssec-keygen.c,v 1.13 2025/01/26 16:24:32 christos Exp $	*/
2 
3 /*
4  * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  *
15  * Portions Copyright (C) Network Associates, Inc.
16  *
17  * Permission to use, copy, modify, and/or distribute this software for any
18  * purpose with or without fee is hereby granted, provided that the above
19  * copyright notice and this permission notice appear in all copies.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
22  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
24  * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
25  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
26  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
27  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28  */
29 
30 /*! \file */
31 
32 #include <ctype.h>
33 #include <inttypes.h>
34 #include <stdbool.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 
38 #include <openssl/opensslv.h>
39 
40 #include <isc/attributes.h>
41 #include <isc/buffer.h>
42 #include <isc/commandline.h>
43 #include <isc/fips.h>
44 #include <isc/mem.h>
45 #include <isc/region.h>
46 #include <isc/result.h>
47 #include <isc/string.h>
48 #include <isc/util.h>
49 
50 #include <dns/dnssec.h>
51 #include <dns/fixedname.h>
52 #include <dns/kasp.h>
53 #include <dns/keyvalues.h>
54 #include <dns/log.h>
55 #include <dns/name.h>
56 #include <dns/rdataclass.h>
57 #include <dns/secalg.h>
58 
59 #include <dst/dst.h>
60 
61 #if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000
62 #include <openssl/err.h>
63 #include <openssl/provider.h>
64 #endif
65 
66 #include "dnssectool.h"
67 
68 const char *program = "dnssec-keygen";
69 
70 /*
71  * These are are set here for backwards compatibility.  They are
72  * raised to 2048 in FIPS mode.
73  */
74 static int min_rsa = 1024;
75 static int min_dh = 128;
76 
77 isc_log_t *lctx = NULL;
78 
79 noreturn static void
80 usage(void);
81 
82 static void
83 progress(int p);
84 
85 struct keygen_ctx {
86 	const char *predecessor;
87 	const char *policy;
88 	const char *configfile;
89 	const char *directory;
90 	dns_keystore_t *keystore;
91 	char *algname;
92 	char *nametype;
93 	char *type;
94 	int protocol;
95 	int size;
96 	uint16_t tag_min;
97 	uint16_t tag_max;
98 	int signatory;
99 	dns_rdataclass_t rdclass;
100 	int options;
101 	int dbits;
102 	dns_ttl_t ttl;
103 	bool wantzsk;
104 	bool wantksk;
105 	bool wantrev;
106 	dns_secalg_t alg;
107 	/* timing data */
108 	int prepub;
109 	isc_stdtime_t now;
110 	isc_stdtime_t publish;
111 	isc_stdtime_t activate;
112 	isc_stdtime_t inactive;
113 	isc_stdtime_t revokekey;
114 	isc_stdtime_t deltime;
115 	isc_stdtime_t syncadd;
116 	isc_stdtime_t syncdel;
117 	bool setpub;
118 	bool setact;
119 	bool setinact;
120 	bool setrev;
121 	bool setdel;
122 	bool setsyncadd;
123 	bool setsyncdel;
124 	bool unsetpub;
125 	bool unsetact;
126 	bool unsetinact;
127 	bool unsetrev;
128 	bool unsetdel;
129 	/* how to generate the key */
130 	bool setttl;
131 	bool use_nsec3;
132 	bool genonly;
133 	bool showprogress;
134 	bool quiet;
135 	bool oldstyle;
136 	/* state */
137 	time_t lifetime;
138 	bool ksk;
139 	bool zsk;
140 };
141 
142 typedef struct keygen_ctx keygen_ctx_t;
143 
144 static void
145 usage(void) {
146 	fprintf(stderr, "Usage:\n");
147 	fprintf(stderr, "    %s [options] name\n\n", program);
148 	fprintf(stderr, "Version: %s\n", PACKAGE_VERSION);
149 	fprintf(stderr, "    name: owner of the key\n");
150 	fprintf(stderr, "Options:\n");
151 	fprintf(stderr, "    -K <directory>: write keys into directory\n");
152 	fprintf(stderr, "    -k <policy>: generate keys for dnssec-policy\n");
153 	fprintf(stderr, "    -l <file>: configuration file with dnssec-policy "
154 			"statement\n");
155 	fprintf(stderr, "    -a <algorithm>:\n");
156 	if (!isc_fips_mode()) {
157 		fprintf(stderr, "        RSASHA1 | NSEC3RSASHA1 |\n");
158 	}
159 	fprintf(stderr, "        RSASHA256 | RSASHA512 |\n");
160 	fprintf(stderr, "        ECDSAP256SHA256 | ECDSAP384SHA384 |\n");
161 	fprintf(stderr, "        ED25519 | ED448\n");
162 	fprintf(stderr, "    -3: use NSEC3-capable algorithm\n");
163 	fprintf(stderr, "    -b <key size in bits>:\n");
164 	if (!isc_fips_mode()) {
165 		fprintf(stderr, "        RSASHA1:\t[%d..%d]\n", min_rsa,
166 			MAX_RSA);
167 		fprintf(stderr, "        NSEC3RSASHA1:\t[%d..%d]\n", min_rsa,
168 			MAX_RSA);
169 	}
170 	fprintf(stderr, "        RSASHA256:\t[%d..%d]\n", min_rsa, MAX_RSA);
171 	fprintf(stderr, "        RSASHA512:\t[%d..%d]\n", min_rsa, MAX_RSA);
172 	fprintf(stderr, "        ECDSAP256SHA256:\tignored\n");
173 	fprintf(stderr, "        ECDSAP384SHA384:\tignored\n");
174 	fprintf(stderr, "        ED25519:\tignored\n");
175 	fprintf(stderr, "        ED448:\tignored\n");
176 	fprintf(stderr, "        (key size defaults are set according to\n"
177 			"        algorithm and usage (ZSK or KSK)\n");
178 	fprintf(stderr, "    -n <nametype>: ZONE | HOST | ENTITY | "
179 			"USER | OTHER\n");
180 	fprintf(stderr, "        (DNSKEY generation defaults to ZONE)\n");
181 	fprintf(stderr, "    -c <class>: (default: IN)\n");
182 	fprintf(stderr, "    -d <digest bits> (0 => max, default)\n");
183 	fprintf(stderr, "    -E <engine>:\n");
184 	fprintf(stderr, "        name of an OpenSSL engine to use\n");
185 	fprintf(stderr, "    -f <keyflag>: ZSK | KSK | REVOKE\n");
186 	fprintf(stderr, "    -F: FIPS mode\n");
187 	fprintf(stderr, "    -L <ttl>: default key TTL\n");
188 	fprintf(stderr, "    -M <min>:<max>: allowed Key ID range\n");
189 	fprintf(stderr, "    -p <protocol>: (default: 3 [dnssec])\n");
190 	fprintf(stderr, "    -s <strength>: strength value this key signs DNS "
191 			"records with (default: 0)\n");
192 	fprintf(stderr, "    -T <rrtype>: DNSKEY | KEY (default: DNSKEY; "
193 			"use KEY for SIG(0))\n");
194 	fprintf(stderr, "    -t <type>: "
195 			"AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF "
196 			"(default: AUTHCONF)\n");
197 	fprintf(stderr, "    -h: print usage and exit\n");
198 	fprintf(stderr, "    -m <memory debugging mode>:\n");
199 	fprintf(stderr, "       usage | trace | record | size | mctx\n");
200 	fprintf(stderr, "    -v <level>: set verbosity level (0 - 10)\n");
201 	fprintf(stderr, "    -V: print version information\n");
202 	fprintf(stderr, "Timing options:\n");
203 	fprintf(stderr, "    -P date/[+-]offset/none: set key publication date "
204 			"(default: now)\n");
205 	fprintf(stderr, "    -P sync date/[+-]offset/none: set CDS and CDNSKEY "
206 			"publication date\n");
207 	fprintf(stderr, "    -A date/[+-]offset/none: set key activation date "
208 			"(default: now)\n");
209 	fprintf(stderr, "    -R date/[+-]offset/none: set key "
210 			"revocation date\n");
211 	fprintf(stderr, "    -I date/[+-]offset/none: set key "
212 			"inactivation date\n");
213 	fprintf(stderr, "    -D date/[+-]offset/none: set key deletion date\n");
214 	fprintf(stderr, "    -D sync date/[+-]offset/none: set CDS and CDNSKEY "
215 			"deletion date\n");
216 
217 	fprintf(stderr, "    -G: generate key only; do not set -P or -A\n");
218 	fprintf(stderr, "    -C: generate a backward-compatible key, omitting "
219 			"all dates\n");
220 	fprintf(stderr, "    -S <key>: generate a successor to an existing "
221 			"key\n");
222 	fprintf(stderr, "    -i <interval>: prepublication interval for "
223 			"successor key "
224 			"(default: 30 days)\n");
225 	fprintf(stderr, "Output:\n");
226 	fprintf(stderr, "     K<name>+<alg>+<id>.key, "
227 			"K<name>+<alg>+<id>.private\n");
228 
229 	exit(EXIT_FAILURE);
230 }
231 
232 static void
233 progress(int p) {
234 	char c = '*';
235 
236 	switch (p) {
237 	case 0:
238 		c = '.';
239 		break;
240 	case 1:
241 		c = '+';
242 		break;
243 	case 2:
244 		c = '*';
245 		break;
246 	case 3:
247 		c = ' ';
248 		break;
249 	default:
250 		break;
251 	}
252 	(void)putc(c, stderr);
253 	(void)fflush(stderr);
254 }
255 
256 static void
257 keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) {
258 	char filename[255];
259 	char algstr[DNS_SECALG_FORMATSIZE];
260 	uint16_t flags = 0;
261 	int param = 0;
262 	bool null_key = false;
263 	bool conflict = false;
264 	bool show_progress = false;
265 	isc_buffer_t buf;
266 	dns_name_t *name;
267 	dns_fixedname_t fname;
268 	isc_result_t ret;
269 	dst_key_t *key = NULL;
270 	dst_key_t *prevkey = NULL;
271 
272 	UNUSED(argc);
273 
274 	dns_secalg_format(ctx->alg, algstr, sizeof(algstr));
275 
276 	if (ctx->predecessor == NULL) {
277 		if (ctx->prepub == -1) {
278 			ctx->prepub = 0;
279 		}
280 
281 		name = dns_fixedname_initname(&fname);
282 		isc_buffer_init(&buf, argv[isc_commandline_index],
283 				strlen(argv[isc_commandline_index]));
284 		isc_buffer_add(&buf, strlen(argv[isc_commandline_index]));
285 		ret = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
286 		if (ret != ISC_R_SUCCESS) {
287 			fatal("invalid key name %s: %s",
288 			      argv[isc_commandline_index],
289 			      isc_result_totext(ret));
290 		}
291 
292 		if (!dst_algorithm_supported(ctx->alg)) {
293 			fatal("unsupported algorithm: %s", algstr);
294 		}
295 
296 		if (isc_fips_mode()) {
297 			/* verify only in FIPS mode */
298 			switch (ctx->alg) {
299 			case DST_ALG_RSASHA1:
300 			case DST_ALG_NSEC3RSASHA1:
301 				fatal("unsupported algorithm: %s", algstr);
302 			default:
303 				break;
304 			}
305 		}
306 
307 		if (ctx->use_nsec3) {
308 			switch (ctx->alg) {
309 			case DST_ALG_RSASHA1:
310 				ctx->alg = DST_ALG_NSEC3RSASHA1;
311 				break;
312 			case DST_ALG_NSEC3RSASHA1:
313 			case DST_ALG_RSASHA256:
314 			case DST_ALG_RSASHA512:
315 			case DST_ALG_ECDSA256:
316 			case DST_ALG_ECDSA384:
317 			case DST_ALG_ED25519:
318 			case DST_ALG_ED448:
319 				break;
320 			default:
321 				fatal("algorithm %s is incompatible with NSEC3"
322 				      ", do not use the -3 option",
323 				      algstr);
324 			}
325 		}
326 
327 		if (ctx->type != NULL && (ctx->options & DST_TYPE_KEY) != 0) {
328 			if (strcasecmp(ctx->type, "NOAUTH") == 0) {
329 				flags |= DNS_KEYTYPE_NOAUTH;
330 			} else if (strcasecmp(ctx->type, "NOCONF") == 0) {
331 				flags |= DNS_KEYTYPE_NOCONF;
332 			} else if (strcasecmp(ctx->type, "NOAUTHCONF") == 0) {
333 				flags |= (DNS_KEYTYPE_NOAUTH |
334 					  DNS_KEYTYPE_NOCONF);
335 				if (ctx->size < 0) {
336 					ctx->size = 0;
337 				}
338 			} else if (strcasecmp(ctx->type, "AUTHCONF") == 0) {
339 				/* nothing */
340 			} else {
341 				fatal("invalid type %s", ctx->type);
342 			}
343 		}
344 
345 		if (ctx->size < 0) {
346 			switch (ctx->alg) {
347 			case DST_ALG_RSASHA1:
348 			case DST_ALG_NSEC3RSASHA1:
349 				if (isc_fips_mode()) {
350 					fatal("key size not specified (-b "
351 					      "option)");
352 				}
353 				FALLTHROUGH;
354 			case DST_ALG_RSASHA256:
355 			case DST_ALG_RSASHA512:
356 				ctx->size = 2048;
357 				if (verbose > 0) {
358 					fprintf(stderr,
359 						"key size not "
360 						"specified; defaulting"
361 						" to %d\n",
362 						ctx->size);
363 				}
364 				break;
365 			case DST_ALG_ECDSA256:
366 			case DST_ALG_ECDSA384:
367 			case DST_ALG_ED25519:
368 			case DST_ALG_ED448:
369 				break;
370 			default:
371 				fatal("key size not specified (-b option)");
372 			}
373 		}
374 
375 		if (!ctx->oldstyle && ctx->prepub > 0) {
376 			if (ctx->setpub && ctx->setact &&
377 			    (ctx->activate - ctx->prepub) < ctx->publish)
378 			{
379 				fatal("Activation and publication dates "
380 				      "are closer together than the\n\t"
381 				      "prepublication interval.");
382 			}
383 
384 			if (!ctx->setpub && !ctx->setact) {
385 				ctx->setpub = ctx->setact = true;
386 				ctx->publish = ctx->now;
387 				ctx->activate = ctx->now + ctx->prepub;
388 			} else if (ctx->setpub && !ctx->setact) {
389 				ctx->setact = true;
390 				ctx->activate = ctx->publish + ctx->prepub;
391 			} else if (ctx->setact && !ctx->setpub) {
392 				ctx->setpub = true;
393 				ctx->publish = ctx->activate - ctx->prepub;
394 			}
395 
396 			if ((ctx->activate - ctx->prepub) < ctx->now) {
397 				fatal("Time until activation is shorter "
398 				      "than the\n\tprepublication interval.");
399 			}
400 		}
401 	} else {
402 		char keystr[DST_KEY_FORMATSIZE];
403 		isc_stdtime_t when;
404 		int major, minor;
405 
406 		if (ctx->prepub == -1) {
407 			ctx->prepub = (30 * 86400);
408 		}
409 
410 		if (ctx->alg != 0) {
411 			fatal("-S and -a cannot be used together");
412 		}
413 		if (ctx->size >= 0) {
414 			fatal("-S and -b cannot be used together");
415 		}
416 		if (ctx->nametype != NULL) {
417 			fatal("-S and -n cannot be used together");
418 		}
419 		if (ctx->type != NULL) {
420 			fatal("-S and -t cannot be used together");
421 		}
422 		if (ctx->setpub || ctx->unsetpub) {
423 			fatal("-S and -P cannot be used together");
424 		}
425 		if (ctx->setact || ctx->unsetact) {
426 			fatal("-S and -A cannot be used together");
427 		}
428 		if (ctx->use_nsec3) {
429 			fatal("-S and -3 cannot be used together");
430 		}
431 		if (ctx->oldstyle) {
432 			fatal("-S and -C cannot be used together");
433 		}
434 		if (ctx->genonly) {
435 			fatal("-S and -G cannot be used together");
436 		}
437 
438 		ret = dst_key_fromnamedfile(
439 			ctx->predecessor, ctx->directory,
440 			(DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE),
441 			mctx, &prevkey);
442 		if (ret != ISC_R_SUCCESS) {
443 			fatal("Invalid keyfile %s: %s", ctx->predecessor,
444 			      isc_result_totext(ret));
445 		}
446 		if (!dst_key_isprivate(prevkey)) {
447 			fatal("%s is not a private key", ctx->predecessor);
448 		}
449 
450 		name = dst_key_name(prevkey);
451 		ctx->alg = dst_key_alg(prevkey);
452 		ctx->size = dst_key_size(prevkey);
453 		flags = dst_key_flags(prevkey);
454 
455 		dst_key_format(prevkey, keystr, sizeof(keystr));
456 		dst_key_getprivateformat(prevkey, &major, &minor);
457 		if (major != DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) {
458 			fatal("Key %s has incompatible format version %d.%d\n\t"
459 			      "It is not possible to generate a successor key.",
460 			      keystr, major, minor);
461 		}
462 
463 		ret = dst_key_gettime(prevkey, DST_TIME_ACTIVATE, &when);
464 		if (ret != ISC_R_SUCCESS) {
465 			fatal("Key %s has no activation date.\n\t"
466 			      "You must use dnssec-settime -A to set one "
467 			      "before generating a successor.",
468 			      keystr);
469 		}
470 
471 		ret = dst_key_gettime(prevkey, DST_TIME_INACTIVE,
472 				      &ctx->activate);
473 		if (ret != ISC_R_SUCCESS) {
474 			fatal("Key %s has no inactivation date.\n\t"
475 			      "You must use dnssec-settime -I to set one "
476 			      "before generating a successor.",
477 			      keystr);
478 		}
479 
480 		ctx->publish = ctx->activate - ctx->prepub;
481 		if (ctx->publish < ctx->now) {
482 			fatal("Key %s becomes inactive\n\t"
483 			      "sooner than the prepublication period "
484 			      "for the new key ends.\n\t"
485 			      "Either change the inactivation date with "
486 			      "dnssec-settime -I,\n\t"
487 			      "or use the -i option to set a shorter "
488 			      "prepublication interval.",
489 			      keystr);
490 		}
491 
492 		ret = dst_key_gettime(prevkey, DST_TIME_DELETE, &when);
493 		if (ret != ISC_R_SUCCESS) {
494 			fprintf(stderr,
495 				"%s: WARNING: Key %s has no removal "
496 				"date;\n\t it will remain in the zone "
497 				"indefinitely after rollover.\n\t "
498 				"You can use dnssec-settime -D to "
499 				"change this.\n",
500 				program, keystr);
501 		}
502 
503 		ctx->setpub = ctx->setact = true;
504 	}
505 
506 	switch (ctx->alg) {
507 	case DNS_KEYALG_RSASHA1:
508 	case DNS_KEYALG_NSEC3RSASHA1:
509 		if (isc_fips_mode()) {
510 			fatal("SHA1 based keys not supported in FIPS mode");
511 		}
512 		FALLTHROUGH;
513 	case DNS_KEYALG_RSASHA256:
514 	case DNS_KEYALG_RSASHA512:
515 		if (ctx->size != 0 &&
516 		    (ctx->size < min_rsa || ctx->size > MAX_RSA))
517 		{
518 			fatal("RSA key size %d out of range", ctx->size);
519 		}
520 		break;
521 	case DST_ALG_ECDSA256:
522 		ctx->size = 256;
523 		break;
524 	case DST_ALG_ECDSA384:
525 		ctx->size = 384;
526 		break;
527 	case DST_ALG_ED25519:
528 		ctx->size = 256;
529 		break;
530 	case DST_ALG_ED448:
531 		ctx->size = 456;
532 		break;
533 	}
534 
535 	if (ctx->nametype == NULL) {
536 		if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */
537 			fatal("no nametype specified");
538 		}
539 		flags |= DNS_KEYOWNER_ZONE; /* DNSKEY */
540 	} else if (strcasecmp(ctx->nametype, "zone") == 0) {
541 		flags |= DNS_KEYOWNER_ZONE;
542 	} else if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */
543 		if (strcasecmp(ctx->nametype, "host") == 0 ||
544 		    strcasecmp(ctx->nametype, "entity") == 0)
545 		{
546 			flags |= DNS_KEYOWNER_ENTITY;
547 		} else if (strcasecmp(ctx->nametype, "user") == 0) {
548 			flags |= DNS_KEYOWNER_USER;
549 		} else {
550 			fatal("invalid KEY nametype %s", ctx->nametype);
551 		}
552 	} else if (strcasecmp(ctx->nametype, "other") != 0) { /* DNSKEY */
553 		fatal("invalid DNSKEY nametype %s", ctx->nametype);
554 	}
555 
556 	if (ctx->directory == NULL) {
557 		ctx->directory = ".";
558 	}
559 
560 	if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */
561 		flags |= ctx->signatory;
562 	} else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */
563 		if (ctx->ksk || ctx->wantksk) {
564 			flags |= DNS_KEYFLAG_KSK;
565 		}
566 		if (ctx->wantrev) {
567 			flags |= DNS_KEYFLAG_REVOKE;
568 		}
569 	}
570 
571 	if (ctx->protocol == -1) {
572 		ctx->protocol = DNS_KEYPROTO_DNSSEC;
573 	} else if ((ctx->options & DST_TYPE_KEY) == 0 &&
574 		   ctx->protocol != DNS_KEYPROTO_DNSSEC)
575 	{
576 		fatal("invalid DNSKEY protocol: %d", ctx->protocol);
577 	}
578 
579 	if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
580 		if (ctx->size > 0) {
581 			fatal("specified null key with non-zero size");
582 		}
583 		if ((flags & DNS_KEYFLAG_SIGNATORYMASK) != 0) {
584 			fatal("specified null key with signing authority");
585 		}
586 	}
587 
588 	switch (ctx->alg) {
589 	case DNS_KEYALG_RSASHA1:
590 	case DNS_KEYALG_NSEC3RSASHA1:
591 	case DNS_KEYALG_RSASHA256:
592 	case DNS_KEYALG_RSASHA512:
593 		show_progress = true;
594 		break;
595 
596 	case DST_ALG_ECDSA256:
597 	case DST_ALG_ECDSA384:
598 	case DST_ALG_ED25519:
599 	case DST_ALG_ED448:
600 		show_progress = true;
601 		break;
602 	}
603 
604 	if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
605 		null_key = true;
606 	}
607 
608 	isc_buffer_init(&buf, filename, sizeof(filename) - 1);
609 
610 	do {
611 		conflict = false;
612 
613 		if (!ctx->quiet && show_progress) {
614 			fprintf(stderr, "Generating key pair.");
615 		}
616 
617 		if (ctx->keystore != NULL && ctx->policy != NULL) {
618 			ret = dns_keystore_keygen(
619 				ctx->keystore, name, ctx->policy, ctx->rdclass,
620 				mctx, ctx->alg, ctx->size, flags, &key);
621 		} else if (!ctx->quiet && show_progress) {
622 			ret = dst_key_generate(name, ctx->alg, ctx->size, param,
623 					       flags, ctx->protocol,
624 					       ctx->rdclass, NULL, mctx, &key,
625 					       &progress);
626 		} else {
627 			ret = dst_key_generate(name, ctx->alg, ctx->size, param,
628 					       flags, ctx->protocol,
629 					       ctx->rdclass, NULL, mctx, &key,
630 					       NULL);
631 		}
632 
633 		if (!ctx->quiet && show_progress) {
634 			putc('\n', stderr);
635 			fflush(stderr);
636 		}
637 
638 		if (ret != ISC_R_SUCCESS) {
639 			char namestr[DNS_NAME_FORMATSIZE];
640 			dns_name_format(name, namestr, sizeof(namestr));
641 			fatal("failed to generate key %s/%s: %s\n", namestr,
642 			      algstr, isc_result_totext(ret));
643 		}
644 
645 		dst_key_setbits(key, ctx->dbits);
646 
647 		/*
648 		 * Set key timing metadata (unless using -C)
649 		 *
650 		 * Creation date is always set to "now".
651 		 *
652 		 * For a new key without an explicit predecessor, publish
653 		 * and activation dates are set to "now" by default, but
654 		 * can both be overridden.
655 		 *
656 		 * For a successor key, activation is set to match the
657 		 * predecessor's inactivation date.  Publish is set to 30
658 		 * days earlier than that (XXX: this should be configurable).
659 		 * If either of the resulting dates are in the past, that's
660 		 * an error; the inactivation date of the predecessor key
661 		 * must be updated before a successor key can be created.
662 		 */
663 		if (!ctx->oldstyle) {
664 			dst_key_settime(key, DST_TIME_CREATED, ctx->now);
665 
666 			if (ctx->genonly && (ctx->setpub || ctx->setact)) {
667 				fatal("cannot use -G together with "
668 				      "-P or -A options");
669 			}
670 
671 			if (ctx->setpub) {
672 				dst_key_settime(key, DST_TIME_PUBLISH,
673 						ctx->publish);
674 			} else if (ctx->setact && !ctx->unsetpub) {
675 				dst_key_settime(key, DST_TIME_PUBLISH,
676 						ctx->activate - ctx->prepub);
677 			} else if (!ctx->genonly && !ctx->unsetpub) {
678 				dst_key_settime(key, DST_TIME_PUBLISH,
679 						ctx->now);
680 			}
681 
682 			if (ctx->setact) {
683 				dst_key_settime(key, DST_TIME_ACTIVATE,
684 						ctx->activate);
685 			} else if (!ctx->genonly && !ctx->unsetact) {
686 				dst_key_settime(key, DST_TIME_ACTIVATE,
687 						ctx->now);
688 			}
689 
690 			if (ctx->setrev) {
691 				if (!ctx->wantksk) {
692 					fprintf(stderr,
693 						"%s: warning: Key is "
694 						"not flagged as a KSK, but -R "
695 						"was used. Revoking a ZSK is "
696 						"legal, but undefined.\n",
697 						program);
698 				}
699 				dst_key_settime(key, DST_TIME_REVOKE,
700 						ctx->revokekey);
701 			}
702 
703 			if (ctx->setinact) {
704 				dst_key_settime(key, DST_TIME_INACTIVE,
705 						ctx->inactive);
706 			}
707 
708 			if (ctx->setdel) {
709 				if (ctx->setinact &&
710 				    ctx->deltime < ctx->inactive)
711 				{
712 					fprintf(stderr,
713 						"%s: warning: Key is "
714 						"scheduled to be deleted "
715 						"before it is scheduled to be "
716 						"made inactive.\n",
717 						program);
718 				}
719 				dst_key_settime(key, DST_TIME_DELETE,
720 						ctx->deltime);
721 			}
722 
723 			if (ctx->setsyncadd) {
724 				dst_key_settime(key, DST_TIME_SYNCPUBLISH,
725 						ctx->syncadd);
726 			}
727 
728 			if (ctx->setsyncdel) {
729 				dst_key_settime(key, DST_TIME_SYNCDELETE,
730 						ctx->syncdel);
731 			}
732 		} else {
733 			if (ctx->setpub || ctx->setact || ctx->setrev ||
734 			    ctx->setinact || ctx->setdel || ctx->unsetpub ||
735 			    ctx->unsetact || ctx->unsetrev || ctx->unsetinact ||
736 			    ctx->unsetdel || ctx->genonly || ctx->setsyncadd ||
737 			    ctx->setsyncdel)
738 			{
739 				fatal("cannot use -C together with "
740 				      "-P, -A, -R, -I, -D, or -G options");
741 			}
742 			/*
743 			 * Compatibility mode: Private-key-format
744 			 * should be set to 1.2.
745 			 */
746 			dst_key_setprivateformat(key, 1, 2);
747 		}
748 
749 		/* Set the default key TTL */
750 		if (ctx->setttl) {
751 			dst_key_setttl(key, ctx->ttl);
752 		}
753 
754 		/* Set dnssec-policy related metadata */
755 		if (ctx->policy != NULL) {
756 			dst_key_setnum(key, DST_NUM_LIFETIME, ctx->lifetime);
757 			dst_key_setbool(key, DST_BOOL_KSK, ctx->ksk);
758 			dst_key_setbool(key, DST_BOOL_ZSK, ctx->zsk);
759 		}
760 
761 		/*
762 		 * Do not overwrite an existing key, or create a key
763 		 * if there is a risk of ID collision due to this key
764 		 * or another key being revoked.
765 		 */
766 		if (key_collision(key, name, ctx->directory, mctx, ctx->tag_min,
767 				  ctx->tag_max, NULL))
768 		{
769 			conflict = true;
770 			if (null_key) {
771 				dst_key_free(&key);
772 				break;
773 			}
774 
775 			if (verbose > 0) {
776 				isc_buffer_clear(&buf);
777 				ret = dst_key_buildfilename(
778 					key, 0, ctx->directory, &buf);
779 				if (ret == ISC_R_SUCCESS) {
780 					fprintf(stderr,
781 						"%s: %s already exists, or "
782 						"might collide with another "
783 						"key upon revokation.  "
784 						"Generating a new key\n",
785 						program, filename);
786 				}
787 			}
788 
789 			dst_key_free(&key);
790 		}
791 	} while (conflict);
792 
793 	if (conflict) {
794 		fatal("cannot generate a null key due to possible key ID "
795 		      "collision");
796 	}
797 
798 	if (ctx->predecessor != NULL && prevkey != NULL) {
799 		dst_key_setnum(prevkey, DST_NUM_SUCCESSOR, dst_key_id(key));
800 		dst_key_setnum(key, DST_NUM_PREDECESSOR, dst_key_id(prevkey));
801 
802 		ret = dst_key_tofile(prevkey, ctx->options, ctx->directory);
803 		if (ret != ISC_R_SUCCESS) {
804 			char keystr[DST_KEY_FORMATSIZE];
805 			dst_key_format(prevkey, keystr, sizeof(keystr));
806 			fatal("failed to update predecessor %s: %s\n", keystr,
807 			      isc_result_totext(ret));
808 		}
809 	}
810 
811 	ret = dst_key_tofile(key, ctx->options, ctx->directory);
812 	if (ret != ISC_R_SUCCESS) {
813 		char keystr[DST_KEY_FORMATSIZE];
814 		dst_key_format(key, keystr, sizeof(keystr));
815 		fatal("failed to write key %s: %s\n", keystr,
816 		      isc_result_totext(ret));
817 	}
818 
819 	isc_buffer_clear(&buf);
820 	ret = dst_key_buildfilename(key, 0, NULL, &buf);
821 	if (ret != ISC_R_SUCCESS) {
822 		fatal("dst_key_buildfilename returned: %s\n",
823 		      isc_result_totext(ret));
824 	}
825 	printf("%s\n", filename);
826 
827 	dst_key_free(&key);
828 	if (prevkey != NULL) {
829 		dst_key_free(&prevkey);
830 	}
831 }
832 
833 static void
834 check_keystore_options(keygen_ctx_t *ctx) {
835 	ctx->directory = dns_keystore_directory(ctx->keystore, NULL);
836 	if (ctx->directory != NULL) {
837 		isc_result_t ret = try_dir(ctx->directory);
838 		if (ret != ISC_R_SUCCESS) {
839 			fatal("cannot open directory %s: %s", ctx->directory,
840 			      isc_result_totext(ret));
841 		}
842 	}
843 }
844 
845 int
846 main(int argc, char **argv) {
847 	char *algname = NULL, *freeit = NULL;
848 	char *classname = NULL;
849 	char *endp;
850 	isc_mem_t *mctx = NULL;
851 	isc_result_t ret;
852 	isc_textregion_t r;
853 	const char *engine = NULL;
854 	unsigned char c;
855 	int ch;
856 	bool set_fips_mode = false;
857 #if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000
858 	OSSL_PROVIDER *fips = NULL, *base = NULL;
859 #endif
860 
861 	keygen_ctx_t ctx = {
862 		.options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC,
863 		.prepub = -1,
864 		.protocol = -1,
865 		.size = -1,
866 		.now = isc_stdtime_now(),
867 	};
868 
869 	if (argc == 1) {
870 		usage();
871 	}
872 
873 	isc_commandline_errprint = false;
874 
875 	/*
876 	 * Process memory debugging argument first.
877 	 */
878 #define CMDLINE_FLAGS                                          \
879 	"3A:a:b:Cc:D:d:E:Ff:GhI:i:K:k:L:l:M:m:n:P:p:qR:r:S:s:" \
880 	"T:t:v:V"
881 	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
882 		switch (ch) {
883 		case 'm':
884 			if (strcasecmp(isc_commandline_argument, "record") == 0)
885 			{
886 				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
887 			}
888 			if (strcasecmp(isc_commandline_argument, "trace") == 0)
889 			{
890 				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
891 			}
892 			if (strcasecmp(isc_commandline_argument, "usage") == 0)
893 			{
894 				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
895 			}
896 			break;
897 		default:
898 			break;
899 		}
900 	}
901 	isc_commandline_reset = true;
902 
903 	isc_mem_create(&mctx);
904 
905 	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
906 		switch (ch) {
907 		case '3':
908 			ctx.use_nsec3 = true;
909 			break;
910 		case 'a':
911 			algname = isc_commandline_argument;
912 			break;
913 		case 'b':
914 			ctx.size = strtol(isc_commandline_argument, &endp, 10);
915 			if (*endp != '\0' || ctx.size < 0) {
916 				fatal("-b requires a non-negative number");
917 			}
918 			break;
919 		case 'C':
920 			ctx.oldstyle = true;
921 			break;
922 		case 'c':
923 			classname = isc_commandline_argument;
924 			break;
925 		case 'd':
926 			ctx.dbits = strtol(isc_commandline_argument, &endp, 10);
927 			if (*endp != '\0' || ctx.dbits < 0) {
928 				fatal("-d requires a non-negative number");
929 			}
930 			break;
931 		case 'E':
932 			engine = isc_commandline_argument;
933 			break;
934 		case 'f':
935 			c = (unsigned char)(isc_commandline_argument[0]);
936 			if (toupper(c) == 'K') {
937 				ctx.wantksk = true;
938 			} else if (toupper(c) == 'Z') {
939 				ctx.wantzsk = true;
940 			} else if (toupper(c) == 'R') {
941 				ctx.wantrev = true;
942 			} else {
943 				fatal("unknown flag '%s'",
944 				      isc_commandline_argument);
945 			}
946 			break;
947 		case 'K':
948 			ctx.directory = isc_commandline_argument;
949 			ret = try_dir(ctx.directory);
950 			if (ret != ISC_R_SUCCESS) {
951 				fatal("cannot open directory %s: %s",
952 				      ctx.directory, isc_result_totext(ret));
953 			}
954 			break;
955 		case 'k':
956 			ctx.policy = isc_commandline_argument;
957 			break;
958 		case 'L':
959 			ctx.ttl = strtottl(isc_commandline_argument);
960 			ctx.setttl = true;
961 			break;
962 		case 'l':
963 			ctx.configfile = isc_commandline_argument;
964 			break;
965 		case 'n':
966 			ctx.nametype = isc_commandline_argument;
967 			break;
968 		case 'M': {
969 			unsigned long ul;
970 			ctx.tag_min = ul = strtoul(isc_commandline_argument,
971 						   &endp, 10);
972 			if (*endp != ':' || ul > 0xffff) {
973 				fatal("-M range invalid");
974 			}
975 			ctx.tag_max = ul = strtoul(endp + 1, &endp, 10);
976 			if (*endp != '\0' || ul > 0xffff ||
977 			    ctx.tag_max <= ctx.tag_min)
978 			{
979 				fatal("-M range invalid");
980 			}
981 			break;
982 		}
983 		case 'm':
984 			break;
985 		case 'p':
986 			ctx.protocol = strtol(isc_commandline_argument, &endp,
987 					      10);
988 			if (*endp != '\0' || ctx.protocol < 0 ||
989 			    ctx.protocol > 255)
990 			{
991 				fatal("-p must be followed by a number "
992 				      "[0..255]");
993 			}
994 			break;
995 		case 'q':
996 			ctx.quiet = true;
997 			break;
998 		case 'r':
999 			fatal("The -r option has been deprecated.\n"
1000 			      "System random data is always used.\n");
1001 			break;
1002 		case 's':
1003 			ctx.signatory = strtol(isc_commandline_argument, &endp,
1004 					       10);
1005 			if (*endp != '\0' || ctx.signatory < 0 ||
1006 			    ctx.signatory > 15)
1007 			{
1008 				fatal("-s must be followed by a number "
1009 				      "[0..15]");
1010 			}
1011 			break;
1012 		case 'T':
1013 			if (strcasecmp(isc_commandline_argument, "KEY") == 0) {
1014 				ctx.options |= DST_TYPE_KEY;
1015 			} else if (strcasecmp(isc_commandline_argument,
1016 					      "DNSKE"
1017 					      "Y") == 0)
1018 			{
1019 				/* default behavior */
1020 			} else {
1021 				fatal("unknown type '%s'",
1022 				      isc_commandline_argument);
1023 			}
1024 			break;
1025 		case 't':
1026 			ctx.type = isc_commandline_argument;
1027 			break;
1028 		case 'v':
1029 			endp = NULL;
1030 			verbose = strtol(isc_commandline_argument, &endp, 0);
1031 			if (*endp != '\0') {
1032 				fatal("-v must be followed by a number");
1033 			}
1034 			break;
1035 		case 'G':
1036 			ctx.genonly = true;
1037 			break;
1038 		case 'P':
1039 			/* -Psync ? */
1040 			if (isoptarg("sync", argv, usage)) {
1041 				if (ctx.setsyncadd) {
1042 					fatal("-P sync specified more than "
1043 					      "once");
1044 				}
1045 
1046 				ctx.syncadd = strtotime(
1047 					isc_commandline_argument, ctx.now,
1048 					ctx.now, &ctx.setsyncadd);
1049 				break;
1050 			}
1051 			(void)isoptarg("dnskey", argv, usage);
1052 			if (ctx.setpub || ctx.unsetpub) {
1053 				fatal("-P specified more than once");
1054 			}
1055 
1056 			ctx.publish = strtotime(isc_commandline_argument,
1057 						ctx.now, ctx.now, &ctx.setpub);
1058 			ctx.unsetpub = !ctx.setpub;
1059 			break;
1060 		case 'A':
1061 			if (ctx.setact || ctx.unsetact) {
1062 				fatal("-A specified more than once");
1063 			}
1064 
1065 			ctx.activate = strtotime(isc_commandline_argument,
1066 						 ctx.now, ctx.now, &ctx.setact);
1067 			ctx.unsetact = !ctx.setact;
1068 			break;
1069 		case 'R':
1070 			if (ctx.setrev || ctx.unsetrev) {
1071 				fatal("-R specified more than once");
1072 			}
1073 
1074 			ctx.revokekey = strtotime(isc_commandline_argument,
1075 						  ctx.now, ctx.now,
1076 						  &ctx.setrev);
1077 			ctx.unsetrev = !ctx.setrev;
1078 			break;
1079 		case 'I':
1080 			if (ctx.setinact || ctx.unsetinact) {
1081 				fatal("-I specified more than once");
1082 			}
1083 
1084 			ctx.inactive = strtotime(isc_commandline_argument,
1085 						 ctx.now, ctx.now,
1086 						 &ctx.setinact);
1087 			ctx.unsetinact = !ctx.setinact;
1088 			break;
1089 		case 'D':
1090 			/* -Dsync ? */
1091 			if (isoptarg("sync", argv, usage)) {
1092 				if (ctx.setsyncdel) {
1093 					fatal("-D sync specified more than "
1094 					      "once");
1095 				}
1096 
1097 				ctx.syncdel = strtotime(
1098 					isc_commandline_argument, ctx.now,
1099 					ctx.now, &ctx.setsyncdel);
1100 				break;
1101 			}
1102 			(void)isoptarg("dnskey", argv, usage);
1103 			if (ctx.setdel || ctx.unsetdel) {
1104 				fatal("-D specified more than once");
1105 			}
1106 
1107 			ctx.deltime = strtotime(isc_commandline_argument,
1108 						ctx.now, ctx.now, &ctx.setdel);
1109 			ctx.unsetdel = !ctx.setdel;
1110 			break;
1111 		case 'S':
1112 			ctx.predecessor = isc_commandline_argument;
1113 			break;
1114 		case 'i':
1115 			ctx.prepub = strtottl(isc_commandline_argument);
1116 			break;
1117 		case 'F':
1118 			set_fips_mode = true;
1119 			break;
1120 		case '?':
1121 			if (isc_commandline_option != '?') {
1122 				fprintf(stderr, "%s: invalid argument -%c\n",
1123 					program, isc_commandline_option);
1124 			}
1125 			FALLTHROUGH;
1126 		case 'h':
1127 			/* Does not return. */
1128 			usage();
1129 
1130 		case 'V':
1131 			/* Does not return. */
1132 			version(program);
1133 
1134 		default:
1135 			fprintf(stderr, "%s: unhandled option -%c\n", program,
1136 				isc_commandline_option);
1137 			exit(EXIT_FAILURE);
1138 		}
1139 	}
1140 
1141 	if (!isatty(0)) {
1142 		ctx.quiet = true;
1143 	}
1144 
1145 	if (set_fips_mode) {
1146 #if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000
1147 		fips = OSSL_PROVIDER_load(NULL, "fips");
1148 		if (fips == NULL) {
1149 			ERR_clear_error();
1150 			fatal("Failed to load FIPS provider");
1151 		}
1152 		base = OSSL_PROVIDER_load(NULL, "base");
1153 		if (base == NULL) {
1154 			OSSL_PROVIDER_unload(fips);
1155 			ERR_clear_error();
1156 			fatal("Failed to load base provider");
1157 		}
1158 #endif
1159 		if (!isc_fips_mode()) {
1160 			if (isc_fips_set_mode(1) != ISC_R_SUCCESS) {
1161 				fatal("setting FIPS mode failed");
1162 			}
1163 		}
1164 	}
1165 
1166 	ret = dst_lib_init(mctx, engine);
1167 	if (ret != ISC_R_SUCCESS) {
1168 		fatal("could not initialize dst: %s", isc_result_totext(ret));
1169 	}
1170 
1171 	/*
1172 	 * After dst_lib_init which will set FIPS mode if requested
1173 	 * at build time.  The minumums are both raised to 2048.
1174 	 */
1175 	if (isc_fips_mode()) {
1176 		min_rsa = min_dh = 2048;
1177 	}
1178 
1179 	setup_logging(mctx, &lctx);
1180 
1181 	ctx.rdclass = strtoclass(classname);
1182 
1183 	if (ctx.configfile == NULL || ctx.configfile[0] == '\0') {
1184 		ctx.configfile = NAMED_CONFFILE;
1185 	}
1186 
1187 	if (ctx.predecessor == NULL) {
1188 		if (argc < isc_commandline_index + 1) {
1189 			fatal("the key name was not specified");
1190 		}
1191 		if (argc > isc_commandline_index + 1) {
1192 			fatal("extraneous arguments");
1193 		}
1194 	}
1195 
1196 	if (ctx.predecessor == NULL && ctx.policy == NULL) {
1197 		if (algname == NULL) {
1198 			fatal("no algorithm specified");
1199 		}
1200 		r.base = algname;
1201 		r.length = strlen(algname);
1202 		ret = dns_secalg_fromtext(&ctx.alg, &r);
1203 		if (ret != ISC_R_SUCCESS) {
1204 			fatal("unknown algorithm %s", algname);
1205 		}
1206 		if (!dst_algorithm_supported(ctx.alg)) {
1207 			fatal("unsupported algorithm: %s", algname);
1208 		}
1209 	}
1210 
1211 	if (ctx.policy != NULL) {
1212 		if (ctx.nametype != NULL) {
1213 			fatal("-k and -n cannot be used together");
1214 		}
1215 		if (ctx.predecessor != NULL) {
1216 			fatal("-k and -S cannot be used together");
1217 		}
1218 		if (ctx.oldstyle) {
1219 			fatal("-k and -C cannot be used together");
1220 		}
1221 		if (ctx.setttl) {
1222 			fatal("-k and -L cannot be used together");
1223 		}
1224 		if (ctx.prepub > 0) {
1225 			fatal("-k and -i cannot be used together");
1226 		}
1227 		if (ctx.size != -1) {
1228 			fatal("-k and -b cannot be used together");
1229 		}
1230 		if (ctx.wantrev) {
1231 			fatal("-k and -fR cannot be used together");
1232 		}
1233 		if (ctx.options & DST_TYPE_KEY) {
1234 			fatal("-k and -T KEY cannot be used together");
1235 		}
1236 		if (ctx.use_nsec3) {
1237 			fatal("-k and -3 cannot be used together");
1238 		}
1239 
1240 		ctx.options |= DST_TYPE_STATE;
1241 
1242 		if (strcmp(ctx.policy, "default") == 0) {
1243 			ctx.use_nsec3 = false;
1244 			ctx.alg = DST_ALG_ECDSA256;
1245 			ctx.size = 0;
1246 			ctx.ttl = 3600;
1247 			ctx.setttl = true;
1248 			ctx.ksk = true;
1249 			ctx.zsk = true;
1250 			ctx.lifetime = 0;
1251 			ctx.tag_min = 0;
1252 			ctx.tag_max = 0xffff;
1253 
1254 			keygen(&ctx, mctx, argc, argv);
1255 		} else {
1256 			cfg_parser_t *parser = NULL;
1257 			cfg_obj_t *config = NULL;
1258 			dns_kasp_t *kasp = NULL;
1259 			dns_kasp_key_t *kaspkey = NULL;
1260 
1261 			RUNTIME_CHECK(cfg_parser_create(mctx, lctx, &parser) ==
1262 				      ISC_R_SUCCESS);
1263 			if (cfg_parse_file(parser, ctx.configfile,
1264 					   &cfg_type_namedconf,
1265 					   &config) != ISC_R_SUCCESS)
1266 			{
1267 				fatal("unable to load dnssec-policy '%s' from "
1268 				      "'%s'",
1269 				      ctx.policy, ctx.configfile);
1270 			}
1271 
1272 			kasp_from_conf(config, mctx, lctx, ctx.policy,
1273 				       ctx.directory, engine, &kasp);
1274 			if (kasp == NULL) {
1275 				fatal("failed to load dnssec-policy '%s'",
1276 				      ctx.policy);
1277 			}
1278 			if (ISC_LIST_EMPTY(dns_kasp_keys(kasp))) {
1279 				fatal("dnssec-policy '%s' has no keys "
1280 				      "configured",
1281 				      ctx.policy);
1282 			}
1283 
1284 			ctx.ttl = dns_kasp_dnskeyttl(kasp);
1285 			ctx.setttl = true;
1286 
1287 			for (kaspkey = ISC_LIST_HEAD(dns_kasp_keys(kasp));
1288 			     kaspkey != NULL;
1289 			     kaspkey = ISC_LIST_NEXT(kaspkey, link))
1290 			{
1291 				ctx.use_nsec3 = false;
1292 				ctx.alg = dns_kasp_key_algorithm(kaspkey);
1293 				ctx.size = dns_kasp_key_size(kaspkey);
1294 				ctx.ksk = dns_kasp_key_ksk(kaspkey);
1295 				ctx.zsk = dns_kasp_key_zsk(kaspkey);
1296 				ctx.lifetime = dns_kasp_key_lifetime(kaspkey);
1297 				ctx.keystore = dns_kasp_key_keystore(kaspkey);
1298 				if (ctx.keystore != NULL) {
1299 					check_keystore_options(&ctx);
1300 				}
1301 				ctx.tag_min = dns_kasp_key_tagmin(kaspkey);
1302 				ctx.tag_max = dns_kasp_key_tagmax(kaspkey);
1303 				if ((ctx.ksk && !ctx.wantksk && ctx.wantzsk) ||
1304 				    (ctx.zsk && !ctx.wantzsk && ctx.wantksk))
1305 				{
1306 					continue;
1307 				}
1308 				keygen(&ctx, mctx, argc, argv);
1309 			}
1310 
1311 			dns_kasp_detach(&kasp);
1312 			cfg_obj_destroy(parser, &config);
1313 			cfg_parser_destroy(&parser);
1314 		}
1315 	} else {
1316 		keygen(&ctx, mctx, argc, argv);
1317 	}
1318 
1319 	cleanup_logging(&lctx);
1320 	dst_lib_destroy();
1321 	if (verbose > 10) {
1322 		isc_mem_stats(mctx, stdout);
1323 	}
1324 	isc_mem_destroy(&mctx);
1325 
1326 #if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000
1327 	if (base != NULL) {
1328 		OSSL_PROVIDER_unload(base);
1329 	}
1330 	if (fips != NULL) {
1331 		OSSL_PROVIDER_unload(fips);
1332 	}
1333 #endif
1334 	if (freeit != NULL) {
1335 		free(freeit);
1336 	}
1337 
1338 	return 0;
1339 }
1340