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