1 /* $NetBSD: dst_parse.c,v 1.10 2023/01/25 21:43:30 christos Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0 AND ISC
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
16 /*
17 * Copyright (C) Network Associates, Inc.
18 *
19 * Permission to use, copy, modify, and/or distribute this software for any
20 * purpose with or without fee is hereby granted, provided that the above
21 * copyright notice and this permission notice appear in all copies.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
24 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE
26 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
29 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30 */
31
32 #include "dst_parse.h"
33 #include <inttypes.h>
34 #include <stdbool.h>
35
36 #include <isc/base64.h>
37 #include <isc/dir.h>
38 #include <isc/file.h>
39 #include <isc/fsaccess.h>
40 #include <isc/lex.h>
41 #include <isc/mem.h>
42 #include <isc/print.h>
43 #include <isc/stdtime.h>
44 #include <isc/string.h>
45 #include <isc/util.h>
46
47 #include <pk11/site.h>
48
49 #include <dns/log.h>
50 #include <dns/time.h>
51
52 #include "dst/result.h"
53 #include "dst_internal.h"
54
55 #define DST_AS_STR(t) ((t).value.as_textregion.base)
56
57 #define PRIVATE_KEY_STR "Private-key-format:"
58 #define ALGORITHM_STR "Algorithm:"
59
60 #define TIMING_NTAGS (DST_MAX_TIMES + 1)
61 static const char *timetags[TIMING_NTAGS] = {
62 "Created:", "Publish:", "Activate:", "Revoke:",
63 "Inactive:", "Delete:", "DSPublish:", "SyncPublish:",
64 "SyncDelete:", NULL, NULL, NULL,
65 NULL
66 };
67
68 #define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1)
69 static const char *numerictags[NUMERIC_NTAGS] = {
70 "Predecessor:", "Successor:", "MaxTTL:", "RollPeriod:", NULL, NULL, NULL
71 };
72
73 struct parse_map {
74 const int value;
75 const char *tag;
76 };
77
78 static struct parse_map map[] = { { TAG_RSA_MODULUS, "Modulus:" },
79 { TAG_RSA_PUBLICEXPONENT, "PublicExponent:" },
80 { TAG_RSA_PRIVATEEXPONENT, "PrivateExponent"
81 ":" },
82 { TAG_RSA_PRIME1, "Prime1:" },
83 { TAG_RSA_PRIME2, "Prime2:" },
84 { TAG_RSA_EXPONENT1, "Exponent1:" },
85 { TAG_RSA_EXPONENT2, "Exponent2:" },
86 { TAG_RSA_COEFFICIENT, "Coefficient:" },
87 { TAG_RSA_ENGINE, "Engine:" },
88 { TAG_RSA_LABEL, "Label:" },
89
90 { TAG_DH_PRIME, "Prime(p):" },
91 { TAG_DH_GENERATOR, "Generator(g):" },
92 { TAG_DH_PRIVATE, "Private_value(x):" },
93 { TAG_DH_PUBLIC, "Public_value(y):" },
94
95 { TAG_ECDSA_PRIVATEKEY, "PrivateKey:" },
96 { TAG_ECDSA_ENGINE, "Engine:" },
97 { TAG_ECDSA_LABEL, "Label:" },
98
99 { TAG_EDDSA_PRIVATEKEY, "PrivateKey:" },
100 { TAG_EDDSA_ENGINE, "Engine:" },
101 { TAG_EDDSA_LABEL, "Label:" },
102
103 { TAG_HMACMD5_KEY, "Key:" },
104 { TAG_HMACMD5_BITS, "Bits:" },
105
106 { TAG_HMACSHA1_KEY, "Key:" },
107 { TAG_HMACSHA1_BITS, "Bits:" },
108
109 { TAG_HMACSHA224_KEY, "Key:" },
110 { TAG_HMACSHA224_BITS, "Bits:" },
111
112 { TAG_HMACSHA256_KEY, "Key:" },
113 { TAG_HMACSHA256_BITS, "Bits:" },
114
115 { TAG_HMACSHA384_KEY, "Key:" },
116 { TAG_HMACSHA384_BITS, "Bits:" },
117
118 { TAG_HMACSHA512_KEY, "Key:" },
119 { TAG_HMACSHA512_BITS, "Bits:" },
120
121 { 0, NULL } };
122
123 static int
find_value(const char * s,const unsigned int alg)124 find_value(const char *s, const unsigned int alg) {
125 int i;
126
127 for (i = 0; map[i].tag != NULL; i++) {
128 if (strcasecmp(s, map[i].tag) == 0 &&
129 (TAG_ALG(map[i].value) == alg))
130 {
131 return (map[i].value);
132 }
133 }
134 return (-1);
135 }
136
137 static const char *
find_tag(const int value)138 find_tag(const int value) {
139 int i;
140
141 for (i = 0;; i++) {
142 if (map[i].tag == NULL) {
143 return (NULL);
144 } else if (value == map[i].value) {
145 return (map[i].tag);
146 }
147 }
148 }
149
150 static int
find_metadata(const char * s,const char * tags[],int ntags)151 find_metadata(const char *s, const char *tags[], int ntags) {
152 int i;
153
154 for (i = 0; i < ntags; i++) {
155 if (tags[i] != NULL && strcasecmp(s, tags[i]) == 0) {
156 return (i);
157 }
158 }
159
160 return (-1);
161 }
162
163 static int
find_timedata(const char * s)164 find_timedata(const char *s) {
165 return (find_metadata(s, timetags, TIMING_NTAGS));
166 }
167
168 static int
find_numericdata(const char * s)169 find_numericdata(const char *s) {
170 return (find_metadata(s, numerictags, NUMERIC_NTAGS));
171 }
172
173 static int
check_rsa(const dst_private_t * priv,bool external)174 check_rsa(const dst_private_t *priv, bool external) {
175 int i, j;
176 bool have[RSA_NTAGS];
177 bool ok;
178 unsigned int mask;
179
180 if (external) {
181 return ((priv->nelements == 0) ? 0 : -1);
182 }
183
184 for (i = 0; i < RSA_NTAGS; i++) {
185 have[i] = false;
186 }
187
188 for (j = 0; j < priv->nelements; j++) {
189 for (i = 0; i < RSA_NTAGS; i++) {
190 if (priv->elements[j].tag == TAG(DST_ALG_RSA, i)) {
191 break;
192 }
193 }
194 if (i == RSA_NTAGS) {
195 return (-1);
196 }
197 have[i] = true;
198 }
199
200 mask = (1ULL << TAG_SHIFT) - 1;
201
202 if (have[TAG_RSA_ENGINE & mask]) {
203 ok = have[TAG_RSA_MODULUS & mask] &&
204 have[TAG_RSA_PUBLICEXPONENT & mask] &&
205 have[TAG_RSA_LABEL & mask];
206 } else {
207 ok = have[TAG_RSA_MODULUS & mask] &&
208 have[TAG_RSA_PUBLICEXPONENT & mask] &&
209 have[TAG_RSA_PRIVATEEXPONENT & mask] &&
210 have[TAG_RSA_PRIME1 & mask] &&
211 have[TAG_RSA_PRIME2 & mask] &&
212 have[TAG_RSA_EXPONENT1 & mask] &&
213 have[TAG_RSA_EXPONENT2 & mask] &&
214 have[TAG_RSA_COEFFICIENT & mask];
215 }
216 return (ok ? 0 : -1);
217 }
218
219 static int
check_dh(const dst_private_t * priv)220 check_dh(const dst_private_t *priv) {
221 int i, j;
222 if (priv->nelements != DH_NTAGS) {
223 return (-1);
224 }
225 for (i = 0; i < DH_NTAGS; i++) {
226 for (j = 0; j < priv->nelements; j++) {
227 if (priv->elements[j].tag == TAG(DST_ALG_DH, i)) {
228 break;
229 }
230 }
231 if (j == priv->nelements) {
232 return (-1);
233 }
234 }
235 return (0);
236 }
237
238 static int
check_ecdsa(const dst_private_t * priv,bool external)239 check_ecdsa(const dst_private_t *priv, bool external) {
240 int i, j;
241 bool have[ECDSA_NTAGS];
242 bool ok;
243 unsigned int mask;
244
245 if (external) {
246 return ((priv->nelements == 0) ? 0 : -1);
247 }
248
249 for (i = 0; i < ECDSA_NTAGS; i++) {
250 have[i] = false;
251 }
252 for (j = 0; j < priv->nelements; j++) {
253 for (i = 0; i < ECDSA_NTAGS; i++) {
254 if (priv->elements[j].tag == TAG(DST_ALG_ECDSA256, i)) {
255 break;
256 }
257 }
258 if (i == ECDSA_NTAGS) {
259 return (-1);
260 }
261 have[i] = true;
262 }
263
264 mask = (1ULL << TAG_SHIFT) - 1;
265
266 if (have[TAG_ECDSA_ENGINE & mask]) {
267 ok = have[TAG_ECDSA_LABEL & mask];
268 } else {
269 ok = have[TAG_ECDSA_PRIVATEKEY & mask];
270 }
271 return (ok ? 0 : -1);
272 }
273
274 static int
check_eddsa(const dst_private_t * priv,bool external)275 check_eddsa(const dst_private_t *priv, bool external) {
276 int i, j;
277 bool have[EDDSA_NTAGS];
278 bool ok;
279 unsigned int mask;
280
281 if (external) {
282 return ((priv->nelements == 0) ? 0 : -1);
283 }
284
285 for (i = 0; i < EDDSA_NTAGS; i++) {
286 have[i] = false;
287 }
288 for (j = 0; j < priv->nelements; j++) {
289 for (i = 0; i < EDDSA_NTAGS; i++) {
290 if (priv->elements[j].tag == TAG(DST_ALG_ED25519, i)) {
291 break;
292 }
293 }
294 if (i == EDDSA_NTAGS) {
295 return (-1);
296 }
297 have[i] = true;
298 }
299
300 mask = (1ULL << TAG_SHIFT) - 1;
301
302 if (have[TAG_EDDSA_ENGINE & mask]) {
303 ok = have[TAG_EDDSA_LABEL & mask];
304 } else {
305 ok = have[TAG_EDDSA_PRIVATEKEY & mask];
306 }
307 return (ok ? 0 : -1);
308 }
309
310 static int
check_hmac_md5(const dst_private_t * priv,bool old)311 check_hmac_md5(const dst_private_t *priv, bool old) {
312 int i, j;
313
314 if (priv->nelements != HMACMD5_NTAGS) {
315 /*
316 * If this is a good old format and we are accepting
317 * the old format return success.
318 */
319 if (old && priv->nelements == OLD_HMACMD5_NTAGS &&
320 priv->elements[0].tag == TAG_HMACMD5_KEY)
321 {
322 return (0);
323 }
324 return (-1);
325 }
326 /*
327 * We must be new format at this point.
328 */
329 for (i = 0; i < HMACMD5_NTAGS; i++) {
330 for (j = 0; j < priv->nelements; j++) {
331 if (priv->elements[j].tag == TAG(DST_ALG_HMACMD5, i)) {
332 break;
333 }
334 }
335 if (j == priv->nelements) {
336 return (-1);
337 }
338 }
339 return (0);
340 }
341
342 static int
check_hmac_sha(const dst_private_t * priv,unsigned int ntags,unsigned int alg)343 check_hmac_sha(const dst_private_t *priv, unsigned int ntags,
344 unsigned int alg) {
345 unsigned int i, j;
346 if (priv->nelements != ntags) {
347 return (-1);
348 }
349 for (i = 0; i < ntags; i++) {
350 for (j = 0; j < priv->nelements; j++) {
351 if (priv->elements[j].tag == TAG(alg, i)) {
352 break;
353 }
354 }
355 if (j == priv->nelements) {
356 return (-1);
357 }
358 }
359 return (0);
360 }
361
362 static int
check_data(const dst_private_t * priv,const unsigned int alg,bool old,bool external)363 check_data(const dst_private_t *priv, const unsigned int alg, bool old,
364 bool external) {
365 /* XXXVIX this switch statement is too sparse to gen a jump table. */
366 switch (alg) {
367 case DST_ALG_RSA:
368 case DST_ALG_RSASHA1:
369 case DST_ALG_NSEC3RSASHA1:
370 case DST_ALG_RSASHA256:
371 case DST_ALG_RSASHA512:
372 return (check_rsa(priv, external));
373 case DST_ALG_DH:
374 return (check_dh(priv));
375 case DST_ALG_ECDSA256:
376 case DST_ALG_ECDSA384:
377 return (check_ecdsa(priv, external));
378 case DST_ALG_ED25519:
379 case DST_ALG_ED448:
380 return (check_eddsa(priv, external));
381 case DST_ALG_HMACMD5:
382 return (check_hmac_md5(priv, old));
383 case DST_ALG_HMACSHA1:
384 return (check_hmac_sha(priv, HMACSHA1_NTAGS, alg));
385 case DST_ALG_HMACSHA224:
386 return (check_hmac_sha(priv, HMACSHA224_NTAGS, alg));
387 case DST_ALG_HMACSHA256:
388 return (check_hmac_sha(priv, HMACSHA256_NTAGS, alg));
389 case DST_ALG_HMACSHA384:
390 return (check_hmac_sha(priv, HMACSHA384_NTAGS, alg));
391 case DST_ALG_HMACSHA512:
392 return (check_hmac_sha(priv, HMACSHA512_NTAGS, alg));
393 default:
394 return (DST_R_UNSUPPORTEDALG);
395 }
396 }
397
398 void
dst__privstruct_free(dst_private_t * priv,isc_mem_t * mctx)399 dst__privstruct_free(dst_private_t *priv, isc_mem_t *mctx) {
400 int i;
401
402 if (priv == NULL) {
403 return;
404 }
405 for (i = 0; i < priv->nelements; i++) {
406 if (priv->elements[i].data == NULL) {
407 continue;
408 }
409 memset(priv->elements[i].data, 0, MAXFIELDSIZE);
410 isc_mem_put(mctx, priv->elements[i].data, MAXFIELDSIZE);
411 }
412 priv->nelements = 0;
413 }
414
415 isc_result_t
dst__privstruct_parse(dst_key_t * key,unsigned int alg,isc_lex_t * lex,isc_mem_t * mctx,dst_private_t * priv)416 dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex,
417 isc_mem_t *mctx, dst_private_t *priv) {
418 int n = 0, major, minor, check;
419 isc_buffer_t b;
420 isc_token_t token;
421 unsigned char *data = NULL;
422 unsigned int opt = ISC_LEXOPT_EOL;
423 isc_stdtime_t when;
424 isc_result_t ret;
425 bool external = false;
426
427 REQUIRE(priv != NULL);
428
429 priv->nelements = 0;
430 memset(priv->elements, 0, sizeof(priv->elements));
431
432 #define NEXTTOKEN(lex, opt, token) \
433 do { \
434 ret = isc_lex_gettoken(lex, opt, token); \
435 if (ret != ISC_R_SUCCESS) \
436 goto fail; \
437 } while (0)
438
439 #define READLINE(lex, opt, token) \
440 do { \
441 ret = isc_lex_gettoken(lex, opt, token); \
442 if (ret == ISC_R_EOF) \
443 break; \
444 else if (ret != ISC_R_SUCCESS) \
445 goto fail; \
446 } while ((*token).type != isc_tokentype_eol)
447
448 /*
449 * Read the description line.
450 */
451 NEXTTOKEN(lex, opt, &token);
452 if (token.type != isc_tokentype_string ||
453 strcmp(DST_AS_STR(token), PRIVATE_KEY_STR) != 0)
454 {
455 ret = DST_R_INVALIDPRIVATEKEY;
456 goto fail;
457 }
458
459 NEXTTOKEN(lex, opt, &token);
460 if (token.type != isc_tokentype_string || (DST_AS_STR(token))[0] != 'v')
461 {
462 ret = DST_R_INVALIDPRIVATEKEY;
463 goto fail;
464 }
465 if (sscanf(DST_AS_STR(token), "v%d.%d", &major, &minor) != 2) {
466 ret = DST_R_INVALIDPRIVATEKEY;
467 goto fail;
468 }
469
470 if (major > DST_MAJOR_VERSION) {
471 ret = DST_R_INVALIDPRIVATEKEY;
472 goto fail;
473 }
474
475 /*
476 * Store the private key format version number
477 */
478 dst_key_setprivateformat(key, major, minor);
479
480 READLINE(lex, opt, &token);
481
482 /*
483 * Read the algorithm line.
484 */
485 NEXTTOKEN(lex, opt, &token);
486 if (token.type != isc_tokentype_string ||
487 strcmp(DST_AS_STR(token), ALGORITHM_STR) != 0)
488 {
489 ret = DST_R_INVALIDPRIVATEKEY;
490 goto fail;
491 }
492
493 NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
494 if (token.type != isc_tokentype_number ||
495 token.value.as_ulong != (unsigned long)dst_key_alg(key))
496 {
497 ret = DST_R_INVALIDPRIVATEKEY;
498 goto fail;
499 }
500
501 READLINE(lex, opt, &token);
502
503 /*
504 * Read the key data.
505 */
506 for (n = 0; n < MAXFIELDS; n++) {
507 int tag;
508 isc_region_t r;
509 do {
510 ret = isc_lex_gettoken(lex, opt, &token);
511 if (ret == ISC_R_EOF) {
512 goto done;
513 }
514 if (ret != ISC_R_SUCCESS) {
515 goto fail;
516 }
517 } while (token.type == isc_tokentype_eol);
518
519 if (token.type != isc_tokentype_string) {
520 ret = DST_R_INVALIDPRIVATEKEY;
521 goto fail;
522 }
523
524 if (strcmp(DST_AS_STR(token), "External:") == 0) {
525 external = true;
526 goto next;
527 }
528
529 /* Numeric metadata */
530 tag = find_numericdata(DST_AS_STR(token));
531 if (tag >= 0) {
532 INSIST(tag < NUMERIC_NTAGS);
533
534 NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
535 if (token.type != isc_tokentype_number) {
536 ret = DST_R_INVALIDPRIVATEKEY;
537 goto fail;
538 }
539
540 dst_key_setnum(key, tag, token.value.as_ulong);
541 goto next;
542 }
543
544 /* Timing metadata */
545 tag = find_timedata(DST_AS_STR(token));
546 if (tag >= 0) {
547 INSIST(tag < TIMING_NTAGS);
548
549 NEXTTOKEN(lex, opt, &token);
550 if (token.type != isc_tokentype_string) {
551 ret = DST_R_INVALIDPRIVATEKEY;
552 goto fail;
553 }
554
555 ret = dns_time32_fromtext(DST_AS_STR(token), &when);
556 if (ret != ISC_R_SUCCESS) {
557 goto fail;
558 }
559
560 dst_key_settime(key, tag, when);
561
562 goto next;
563 }
564
565 /* Key data */
566 tag = find_value(DST_AS_STR(token), alg);
567 if (tag < 0 && minor > DST_MINOR_VERSION) {
568 goto next;
569 } else if (tag < 0) {
570 ret = DST_R_INVALIDPRIVATEKEY;
571 goto fail;
572 }
573
574 priv->elements[n].tag = tag;
575
576 data = isc_mem_get(mctx, MAXFIELDSIZE);
577
578 isc_buffer_init(&b, data, MAXFIELDSIZE);
579 ret = isc_base64_tobuffer(lex, &b, -1);
580 if (ret != ISC_R_SUCCESS) {
581 goto fail;
582 }
583
584 isc_buffer_usedregion(&b, &r);
585 priv->elements[n].length = r.length;
586 priv->elements[n].data = r.base;
587 priv->nelements++;
588
589 next:
590 READLINE(lex, opt, &token);
591 data = NULL;
592 }
593
594 done:
595 if (external && priv->nelements != 0) {
596 ret = DST_R_INVALIDPRIVATEKEY;
597 goto fail;
598 }
599
600 check = check_data(priv, alg, true, external);
601 if (check < 0) {
602 ret = DST_R_INVALIDPRIVATEKEY;
603 goto fail;
604 } else if (check != ISC_R_SUCCESS) {
605 ret = check;
606 goto fail;
607 }
608
609 key->external = external;
610
611 return (ISC_R_SUCCESS);
612
613 fail:
614 dst__privstruct_free(priv, mctx);
615 if (data != NULL) {
616 isc_mem_put(mctx, data, MAXFIELDSIZE);
617 }
618
619 return (ret);
620 }
621
622 isc_result_t
dst__privstruct_writefile(const dst_key_t * key,const dst_private_t * priv,const char * directory)623 dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
624 const char *directory) {
625 FILE *fp;
626 isc_result_t result;
627 char filename[NAME_MAX];
628 char buffer[MAXFIELDSIZE * 2];
629 isc_fsaccess_t access;
630 isc_stdtime_t when;
631 uint32_t value;
632 isc_buffer_t b;
633 isc_region_t r;
634 int major, minor;
635 mode_t mode;
636 int i, ret;
637
638 REQUIRE(priv != NULL);
639
640 ret = check_data(priv, dst_key_alg(key), false, key->external);
641 if (ret < 0) {
642 return (DST_R_INVALIDPRIVATEKEY);
643 } else if (ret != ISC_R_SUCCESS) {
644 return (ret);
645 }
646
647 isc_buffer_init(&b, filename, sizeof(filename));
648 result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, &b);
649 if (result != ISC_R_SUCCESS) {
650 return (result);
651 }
652
653 result = isc_file_mode(filename, &mode);
654 if (result == ISC_R_SUCCESS && mode != 0600) {
655 /* File exists; warn that we are changing its permissions */
656 int level;
657
658 #ifdef _WIN32
659 /* Windows security model is pretty different,
660 * e.g., there is no umask... */
661 level = ISC_LOG_NOTICE;
662 #else /* ifdef _WIN32 */
663 level = ISC_LOG_WARNING;
664 #endif /* ifdef _WIN32 */
665 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
666 DNS_LOGMODULE_DNSSEC, level,
667 "Permissions on the file %s "
668 "have changed from 0%o to 0600 as "
669 "a result of this operation.",
670 filename, (unsigned int)mode);
671 }
672
673 if ((fp = fopen(filename, "w")) == NULL) {
674 return (DST_R_WRITEERROR);
675 }
676
677 access = 0;
678 isc_fsaccess_add(ISC_FSACCESS_OWNER,
679 ISC_FSACCESS_READ | ISC_FSACCESS_WRITE, &access);
680 (void)isc_fsaccess_set(filename, access);
681
682 dst_key_getprivateformat(key, &major, &minor);
683 if (major == 0 && minor == 0) {
684 major = DST_MAJOR_VERSION;
685 minor = DST_MINOR_VERSION;
686 }
687
688 /* XXXDCL return value should be checked for full filesystem */
689 fprintf(fp, "%s v%d.%d\n", PRIVATE_KEY_STR, major, minor);
690
691 fprintf(fp, "%s %u ", ALGORITHM_STR, dst_key_alg(key));
692
693 /* XXXVIX this switch statement is too sparse to gen a jump table. */
694 switch (dst_key_alg(key)) {
695 case DST_ALG_DH:
696 fprintf(fp, "(DH)\n");
697 break;
698 case DST_ALG_RSASHA1:
699 fprintf(fp, "(RSASHA1)\n");
700 break;
701 case DST_ALG_NSEC3RSASHA1:
702 fprintf(fp, "(NSEC3RSASHA1)\n");
703 break;
704 case DST_ALG_RSASHA256:
705 fprintf(fp, "(RSASHA256)\n");
706 break;
707 case DST_ALG_RSASHA512:
708 fprintf(fp, "(RSASHA512)\n");
709 break;
710 case DST_ALG_ECDSA256:
711 fprintf(fp, "(ECDSAP256SHA256)\n");
712 break;
713 case DST_ALG_ECDSA384:
714 fprintf(fp, "(ECDSAP384SHA384)\n");
715 break;
716 case DST_ALG_ED25519:
717 fprintf(fp, "(ED25519)\n");
718 break;
719 case DST_ALG_ED448:
720 fprintf(fp, "(ED448)\n");
721 break;
722 case DST_ALG_HMACMD5:
723 fprintf(fp, "(HMAC_MD5)\n");
724 break;
725 case DST_ALG_HMACSHA1:
726 fprintf(fp, "(HMAC_SHA1)\n");
727 break;
728 case DST_ALG_HMACSHA224:
729 fprintf(fp, "(HMAC_SHA224)\n");
730 break;
731 case DST_ALG_HMACSHA256:
732 fprintf(fp, "(HMAC_SHA256)\n");
733 break;
734 case DST_ALG_HMACSHA384:
735 fprintf(fp, "(HMAC_SHA384)\n");
736 break;
737 case DST_ALG_HMACSHA512:
738 fprintf(fp, "(HMAC_SHA512)\n");
739 break;
740 default:
741 fprintf(fp, "(?)\n");
742 break;
743 }
744
745 for (i = 0; i < priv->nelements; i++) {
746 const char *s;
747
748 s = find_tag(priv->elements[i].tag);
749
750 r.base = priv->elements[i].data;
751 r.length = priv->elements[i].length;
752 isc_buffer_init(&b, buffer, sizeof(buffer));
753 result = isc_base64_totext(&r, sizeof(buffer), "", &b);
754 if (result != ISC_R_SUCCESS) {
755 fclose(fp);
756 return (DST_R_INVALIDPRIVATEKEY);
757 }
758 isc_buffer_usedregion(&b, &r);
759
760 fprintf(fp, "%s %.*s\n", s, (int)r.length, r.base);
761 }
762
763 if (key->external) {
764 fprintf(fp, "External:\n");
765 }
766
767 /* Add the metadata tags */
768 if (major > 1 || (major == 1 && minor >= 3)) {
769 for (i = 0; i < NUMERIC_NTAGS; i++) {
770 result = dst_key_getnum(key, i, &value);
771 if (result != ISC_R_SUCCESS) {
772 continue;
773 }
774 if (numerictags[i] != NULL) {
775 fprintf(fp, "%s %u\n", numerictags[i], value);
776 }
777 }
778 for (i = 0; i < TIMING_NTAGS; i++) {
779 result = dst_key_gettime(key, i, &when);
780 if (result != ISC_R_SUCCESS) {
781 continue;
782 }
783
784 isc_buffer_init(&b, buffer, sizeof(buffer));
785 result = dns_time32_totext(when, &b);
786 if (result != ISC_R_SUCCESS) {
787 fclose(fp);
788 return (DST_R_INVALIDPRIVATEKEY);
789 }
790
791 isc_buffer_usedregion(&b, &r);
792
793 if (timetags[i] != NULL) {
794 fprintf(fp, "%s %.*s\n", timetags[i],
795 (int)r.length, r.base);
796 }
797 }
798 }
799
800 fflush(fp);
801 result = ferror(fp) ? DST_R_WRITEERROR : ISC_R_SUCCESS;
802 fclose(fp);
803 return (result);
804 }
805
806 /*! \file */
807