1 /* $NetBSD: dst_parse.c,v 1.1 2024/02/18 20:57:31 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 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 * 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 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 164 find_timedata(const char *s) { 165 return (find_metadata(s, timetags, TIMING_NTAGS)); 166 } 167 168 static int 169 find_numericdata(const char *s) { 170 return (find_metadata(s, numerictags, NUMERIC_NTAGS)); 171 } 172 173 static int 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 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 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 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 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 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 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 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 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 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