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