1 /* $OpenBSD: dgst.c,v 1.20 2022/11/11 17:07:38 joshua Exp $ */ 2 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) 3 * All rights reserved. 4 * 5 * This package is an SSL implementation written 6 * by Eric Young (eay@cryptsoft.com). 7 * The implementation was written so as to conform with Netscapes SSL. 8 * 9 * This library is free for commercial and non-commercial use as long as 10 * the following conditions are aheared to. The following conditions 11 * apply to all code found in this distribution, be it the RC4, RSA, 12 * lhash, DES, etc., code; not just the SSL code. The SSL documentation 13 * included with this distribution is covered by the same copyright terms 14 * except that the holder is Tim Hudson (tjh@cryptsoft.com). 15 * 16 * Copyright remains Eric Young's, and as such any Copyright notices in 17 * the code are not to be removed. 18 * If this package is used in a product, Eric Young should be given attribution 19 * as the author of the parts of the library used. 20 * This can be in the form of a textual message at program startup or 21 * in documentation (online or textual) provided with the package. 22 * 23 * Redistribution and use in source and binary forms, with or without 24 * modification, are permitted provided that the following conditions 25 * are met: 26 * 1. Redistributions of source code must retain the copyright 27 * notice, this list of conditions and the following disclaimer. 28 * 2. Redistributions in binary form must reproduce the above copyright 29 * notice, this list of conditions and the following disclaimer in the 30 * documentation and/or other materials provided with the distribution. 31 * 3. All advertising materials mentioning features or use of this software 32 * must display the following acknowledgement: 33 * "This product includes cryptographic software written by 34 * Eric Young (eay@cryptsoft.com)" 35 * The word 'cryptographic' can be left out if the rouines from the library 36 * being used are not cryptographic related :-). 37 * 4. If you include any Windows specific code (or a derivative thereof) from 38 * the apps directory (application code) you must include an acknowledgement: 39 * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" 40 * 41 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND 42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 51 * SUCH DAMAGE. 52 * 53 * The licence and distribution terms for any publically available version or 54 * derivative of this code cannot be changed. i.e. this code cannot simply be 55 * copied and put under another distribution licence 56 * [including the GNU Public Licence.] 57 */ 58 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 63 #include "apps.h" 64 65 #include <openssl/bio.h> 66 #include <openssl/err.h> 67 #include <openssl/evp.h> 68 #include <openssl/hmac.h> 69 #include <openssl/objects.h> 70 #include <openssl/pem.h> 71 #include <openssl/x509.h> 72 73 #define BUFSIZE 1024*8 74 75 int 76 do_fp(BIO * out, unsigned char *buf, BIO * bp, int sep, int binout, 77 EVP_PKEY * key, unsigned char *sigin, int siglen, 78 const char *sig_name, const char *md_name, 79 const char *file, BIO * bmd); 80 81 static struct { 82 int argsused; 83 int debug; 84 int do_verify; 85 char *hmac_key; 86 char *keyfile; 87 int keyform; 88 const EVP_MD *m; 89 char *mac_name; 90 STACK_OF(OPENSSL_STRING) *macopts; 91 const EVP_MD *md; 92 int out_bin; 93 char *outfile; 94 char *passargin; 95 int separator; 96 char *sigfile; 97 STACK_OF(OPENSSL_STRING) *sigopts; 98 int want_pub; 99 } dgst_config; 100 101 static int 102 dgst_opt_macopt(char *arg) 103 { 104 if (arg == NULL) 105 return (1); 106 107 if (dgst_config.macopts == NULL && 108 (dgst_config.macopts = sk_OPENSSL_STRING_new_null()) == NULL) 109 return (1); 110 111 if (!sk_OPENSSL_STRING_push(dgst_config.macopts, arg)) 112 return (1); 113 114 return (0); 115 } 116 117 static int 118 dgst_opt_md(int argc, char **argv, int *argsused) 119 { 120 char *name = argv[0]; 121 122 if (*name++ != '-') 123 return (1); 124 125 if ((dgst_config.m = EVP_get_digestbyname(name)) == NULL) 126 return (1); 127 128 dgst_config.md = dgst_config.m; 129 130 *argsused = 1; 131 return (0); 132 } 133 134 static int 135 dgst_opt_prverify(char *arg) 136 { 137 if (arg == NULL) 138 return (1); 139 140 dgst_config.keyfile = arg; 141 dgst_config.do_verify = 1; 142 return (0); 143 } 144 145 static int 146 dgst_opt_sigopt(char *arg) 147 { 148 if (arg == NULL) 149 return (1); 150 151 if (dgst_config.sigopts == NULL && 152 (dgst_config.sigopts = sk_OPENSSL_STRING_new_null()) == NULL) 153 return (1); 154 155 if (!sk_OPENSSL_STRING_push(dgst_config.sigopts, arg)) 156 return (1); 157 158 return (0); 159 } 160 161 static int 162 dgst_opt_verify(char *arg) 163 { 164 if (arg == NULL) 165 return (1); 166 167 dgst_config.keyfile = arg; 168 dgst_config.want_pub = 1; 169 dgst_config.do_verify = 1; 170 return (0); 171 } 172 173 static const struct option dgst_options[] = { 174 { 175 .name = "binary", 176 .desc = "Output the digest or signature in binary form", 177 .type = OPTION_VALUE, 178 .opt.value = &dgst_config.out_bin, 179 .value = 1, 180 }, 181 { 182 .name = "c", 183 .desc = "Print the digest in two-digit groups separated by colons", 184 .type = OPTION_VALUE, 185 .opt.value = &dgst_config.separator, 186 .value = 1, 187 }, 188 { 189 .name = "d", 190 .desc = "Print BIO debugging information", 191 .type = OPTION_FLAG, 192 .opt.flag = &dgst_config.debug, 193 }, 194 { 195 .name = "hex", 196 .desc = "Output as hex dump", 197 .type = OPTION_VALUE, 198 .opt.value = &dgst_config.out_bin, 199 .value = 0, 200 }, 201 { 202 .name = "hmac", 203 .argname = "key", 204 .desc = "Create hashed MAC with key", 205 .type = OPTION_ARG, 206 .opt.arg = &dgst_config.hmac_key, 207 }, 208 { 209 .name = "keyform", 210 .argname = "format", 211 .desc = "Key file format (PEM)", 212 .type = OPTION_ARG_FORMAT, 213 .opt.value = &dgst_config.keyform, 214 }, 215 { 216 .name = "mac", 217 .argname = "algorithm", 218 .desc = "Create MAC (not necessarily HMAC)", 219 .type = OPTION_ARG, 220 .opt.arg = &dgst_config.mac_name, 221 }, 222 { 223 .name = "macopt", 224 .argname = "nm:v", 225 .desc = "MAC algorithm parameters or key", 226 .type = OPTION_ARG_FUNC, 227 .opt.argfunc = dgst_opt_macopt, 228 }, 229 { 230 .name = "out", 231 .argname = "file", 232 .desc = "Output to file rather than stdout", 233 .type = OPTION_ARG, 234 .opt.arg = &dgst_config.outfile, 235 }, 236 { 237 .name = "passin", 238 .argname = "arg", 239 .desc = "Input file passphrase source", 240 .type = OPTION_ARG, 241 .opt.arg = &dgst_config.passargin, 242 }, 243 { 244 .name = "prverify", 245 .argname = "file", 246 .desc = "Verify a signature using private key in file", 247 .type = OPTION_ARG_FUNC, 248 .opt.argfunc = dgst_opt_prverify, 249 }, 250 { 251 .name = "r", 252 .desc = "Output the digest in coreutils format", 253 .type = OPTION_VALUE, 254 .opt.value = &dgst_config.separator, 255 .value = 2, 256 }, 257 { 258 .name = "sign", 259 .argname = "file", 260 .desc = "Sign digest using private key in file", 261 .type = OPTION_ARG, 262 .opt.arg = &dgst_config.keyfile, 263 }, 264 { 265 .name = "signature", 266 .argname = "file", 267 .desc = "Signature to verify", 268 .type = OPTION_ARG, 269 .opt.arg = &dgst_config.sigfile, 270 }, 271 { 272 .name = "sigopt", 273 .argname = "nm:v", 274 .desc = "Signature parameter", 275 .type = OPTION_ARG_FUNC, 276 .opt.argfunc = dgst_opt_sigopt, 277 }, 278 { 279 .name = "verify", 280 .argname = "file", 281 .desc = "Verify a signature using public key in file", 282 .type = OPTION_ARG_FUNC, 283 .opt.argfunc = dgst_opt_verify, 284 }, 285 { 286 .name = NULL, 287 .desc = "", 288 .type = OPTION_ARGV_FUNC, 289 .opt.argvfunc = dgst_opt_md, 290 }, 291 { NULL }, 292 }; 293 294 static void 295 list_md_fn(const EVP_MD * m, const char *from, const char *to, void *arg) 296 { 297 const char *mname; 298 /* Skip aliases */ 299 if (!m) 300 return; 301 mname = OBJ_nid2ln(EVP_MD_type(m)); 302 /* Skip shortnames */ 303 if (strcmp(from, mname)) 304 return; 305 if (strchr(mname, ' ')) 306 mname = EVP_MD_name(m); 307 BIO_printf(arg, " -%-17s To use the %s message digest algorithm\n", 308 mname, mname); 309 } 310 311 static void 312 dgst_usage(void) 313 { 314 fprintf(stderr, "usage: dgst [-cdr] [-binary] [-digest] [-hex]"); 315 fprintf(stderr, " [-hmac key] [-keyform fmt]\n"); 316 fprintf(stderr, " [-mac algorithm] [-macopt nm:v] [-out file]"); 317 fprintf(stderr, " [-passin arg]\n"); 318 fprintf(stderr, " [-prverify file] [-sign file]"); 319 fprintf(stderr, " [-signature file]\n"); 320 fprintf(stderr, " [-sigopt nm:v] [-verify file] [file ...]\n\n"); 321 options_usage(dgst_options); 322 EVP_MD_do_all_sorted(list_md_fn, bio_err); 323 fprintf(stderr, "\n"); 324 } 325 326 int 327 dgst_main(int argc, char **argv) 328 { 329 unsigned char *buf = NULL; 330 int i, err = 1; 331 BIO *in = NULL, *inp; 332 BIO *bmd = NULL; 333 BIO *out = NULL; 334 #define PROG_NAME_SIZE 39 335 char pname[PROG_NAME_SIZE + 1]; 336 EVP_PKEY *sigkey = NULL; 337 unsigned char *sigbuf = NULL; 338 int siglen = 0; 339 char *passin = NULL; 340 341 if (pledge("stdio cpath wpath rpath tty", NULL) == -1) { 342 perror("pledge"); 343 exit(1); 344 } 345 346 if ((buf = malloc(BUFSIZE)) == NULL) { 347 BIO_printf(bio_err, "out of memory\n"); 348 goto end; 349 } 350 351 memset(&dgst_config, 0, sizeof(dgst_config)); 352 dgst_config.keyform = FORMAT_PEM; 353 dgst_config.out_bin = -1; 354 355 /* first check the program name */ 356 program_name(argv[0], pname, sizeof pname); 357 358 dgst_config.md = EVP_get_digestbyname(pname); 359 360 if (options_parse(argc, argv, dgst_options, NULL, 361 &dgst_config.argsused) != 0) { 362 dgst_usage(); 363 goto end; 364 } 365 argc -= dgst_config.argsused; 366 argv += dgst_config.argsused; 367 368 if (dgst_config.do_verify && !dgst_config.sigfile) { 369 BIO_printf(bio_err, 370 "No signature to verify: use the -signature option\n"); 371 goto end; 372 } 373 374 in = BIO_new(BIO_s_file()); 375 bmd = BIO_new(BIO_f_md()); 376 if (in == NULL || bmd == NULL) { 377 ERR_print_errors(bio_err); 378 goto end; 379 } 380 381 if (dgst_config.debug) { 382 BIO_set_callback(in, BIO_debug_callback); 383 /* needed for windows 3.1 */ 384 BIO_set_callback_arg(in, (char *) bio_err); 385 } 386 if (!app_passwd(bio_err, dgst_config.passargin, NULL, &passin, NULL)) { 387 BIO_printf(bio_err, "Error getting password\n"); 388 goto end; 389 } 390 if (dgst_config.out_bin == -1) { 391 if (dgst_config.keyfile) 392 dgst_config.out_bin = 1; 393 else 394 dgst_config.out_bin = 0; 395 } 396 397 if (dgst_config.outfile) { 398 if (dgst_config.out_bin) 399 out = BIO_new_file(dgst_config.outfile, "wb"); 400 else 401 out = BIO_new_file(dgst_config.outfile, "w"); 402 } else { 403 out = BIO_new_fp(stdout, BIO_NOCLOSE); 404 } 405 406 if (!out) { 407 BIO_printf(bio_err, "Error opening output file %s\n", 408 dgst_config.outfile ? dgst_config.outfile : "(stdout)"); 409 ERR_print_errors(bio_err); 410 goto end; 411 } 412 if ((!!dgst_config.mac_name + !!dgst_config.keyfile + 413 !!dgst_config.hmac_key) > 1) { 414 BIO_printf(bio_err, 415 "MAC and Signing key cannot both be specified\n"); 416 goto end; 417 } 418 if (dgst_config.keyfile) { 419 if (dgst_config.want_pub) 420 sigkey = load_pubkey(bio_err, dgst_config.keyfile, 421 dgst_config.keyform, 0, NULL, "key file"); 422 else 423 sigkey = load_key(bio_err, dgst_config.keyfile, 424 dgst_config.keyform, 0, passin, "key file"); 425 if (!sigkey) { 426 /* 427 * load_[pub]key() has already printed an appropriate 428 * message 429 */ 430 goto end; 431 } 432 } 433 if (dgst_config.mac_name) { 434 EVP_PKEY_CTX *mac_ctx = NULL; 435 int r = 0; 436 if (!init_gen_str(bio_err, &mac_ctx, dgst_config.mac_name, 0)) 437 goto mac_end; 438 if (dgst_config.macopts) { 439 char *macopt; 440 for (i = 0; i < sk_OPENSSL_STRING_num( 441 dgst_config.macopts); i++) { 442 macopt = sk_OPENSSL_STRING_value( 443 dgst_config.macopts, i); 444 if (pkey_ctrl_string(mac_ctx, macopt) <= 0) { 445 BIO_printf(bio_err, 446 "MAC parameter error \"%s\"\n", 447 macopt); 448 ERR_print_errors(bio_err); 449 goto mac_end; 450 } 451 } 452 } 453 if (EVP_PKEY_keygen(mac_ctx, &sigkey) <= 0) { 454 BIO_puts(bio_err, "Error generating key\n"); 455 ERR_print_errors(bio_err); 456 goto mac_end; 457 } 458 r = 1; 459 mac_end: 460 EVP_PKEY_CTX_free(mac_ctx); 461 if (r == 0) 462 goto end; 463 } 464 if (dgst_config.hmac_key) { 465 sigkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, 466 (unsigned char *) dgst_config.hmac_key, -1); 467 if (!sigkey) 468 goto end; 469 } 470 if (sigkey) { 471 EVP_MD_CTX *mctx = NULL; 472 EVP_PKEY_CTX *pctx = NULL; 473 int r; 474 if (!BIO_get_md_ctx(bmd, &mctx)) { 475 BIO_printf(bio_err, "Error getting context\n"); 476 ERR_print_errors(bio_err); 477 goto end; 478 } 479 if (dgst_config.do_verify) 480 r = EVP_DigestVerifyInit(mctx, &pctx, dgst_config.md, 481 NULL, sigkey); 482 else 483 r = EVP_DigestSignInit(mctx, &pctx, dgst_config.md, 484 NULL, sigkey); 485 if (!r) { 486 BIO_printf(bio_err, "Error setting context\n"); 487 ERR_print_errors(bio_err); 488 goto end; 489 } 490 if (dgst_config.sigopts) { 491 char *sigopt; 492 for (i = 0; i < sk_OPENSSL_STRING_num( 493 dgst_config.sigopts); i++) { 494 sigopt = sk_OPENSSL_STRING_value( 495 dgst_config.sigopts, i); 496 if (pkey_ctrl_string(pctx, sigopt) <= 0) { 497 BIO_printf(bio_err, 498 "parameter error \"%s\"\n", 499 sigopt); 500 ERR_print_errors(bio_err); 501 goto end; 502 } 503 } 504 } 505 } 506 /* we use md as a filter, reading from 'in' */ 507 else { 508 if (dgst_config.md == NULL) 509 dgst_config.md = EVP_sha256(); 510 if (!BIO_set_md(bmd, dgst_config.md)) { 511 BIO_printf(bio_err, "Error setting digest %s\n", pname); 512 ERR_print_errors(bio_err); 513 goto end; 514 } 515 } 516 517 if (dgst_config.sigfile && sigkey) { 518 BIO *sigbio; 519 siglen = EVP_PKEY_size(sigkey); 520 sigbuf = malloc(siglen); 521 if (sigbuf == NULL) { 522 BIO_printf(bio_err, "out of memory\n"); 523 ERR_print_errors(bio_err); 524 goto end; 525 } 526 sigbio = BIO_new_file(dgst_config.sigfile, "rb"); 527 if (!sigbio) { 528 BIO_printf(bio_err, "Error opening signature file %s\n", 529 dgst_config.sigfile); 530 ERR_print_errors(bio_err); 531 goto end; 532 } 533 siglen = BIO_read(sigbio, sigbuf, siglen); 534 BIO_free(sigbio); 535 if (siglen <= 0) { 536 BIO_printf(bio_err, "Error reading signature file %s\n", 537 dgst_config.sigfile); 538 ERR_print_errors(bio_err); 539 goto end; 540 } 541 } 542 inp = BIO_push(bmd, in); 543 544 if (dgst_config.md == NULL) { 545 EVP_MD_CTX *tctx; 546 BIO_get_md_ctx(bmd, &tctx); 547 dgst_config.md = EVP_MD_CTX_md(tctx); 548 } 549 if (argc == 0) { 550 BIO_set_fp(in, stdin, BIO_NOCLOSE); 551 err = do_fp(out, buf, inp, dgst_config.separator, 552 dgst_config.out_bin, sigkey, sigbuf, siglen, NULL, NULL, 553 "stdin", bmd); 554 } else { 555 const char *md_name = NULL, *sig_name = NULL; 556 if (!dgst_config.out_bin) { 557 if (sigkey) { 558 const EVP_PKEY_ASN1_METHOD *ameth; 559 ameth = EVP_PKEY_get0_asn1(sigkey); 560 if (ameth) 561 EVP_PKEY_asn1_get0_info(NULL, NULL, 562 NULL, NULL, &sig_name, ameth); 563 } 564 md_name = EVP_MD_name(dgst_config.md); 565 } 566 err = 0; 567 for (i = 0; i < argc; i++) { 568 int r; 569 if (BIO_read_filename(in, argv[i]) <= 0) { 570 perror(argv[i]); 571 err++; 572 continue; 573 } else { 574 r = do_fp(out, buf, inp, dgst_config.separator, 575 dgst_config.out_bin, sigkey, sigbuf, siglen, 576 sig_name, md_name, argv[i], bmd); 577 } 578 if (r) 579 err = r; 580 (void) BIO_reset(bmd); 581 } 582 } 583 584 end: 585 freezero(buf, BUFSIZE); 586 BIO_free(in); 587 free(passin); 588 BIO_free_all(out); 589 EVP_PKEY_free(sigkey); 590 sk_OPENSSL_STRING_free(dgst_config.sigopts); 591 sk_OPENSSL_STRING_free(dgst_config.macopts); 592 free(sigbuf); 593 BIO_free(bmd); 594 595 return (err); 596 } 597 598 int 599 do_fp(BIO * out, unsigned char *buf, BIO * bp, int sep, int binout, 600 EVP_PKEY * key, unsigned char *sigin, int siglen, 601 const char *sig_name, const char *md_name, 602 const char *file, BIO * bmd) 603 { 604 size_t len; 605 int i; 606 607 for (;;) { 608 i = BIO_read(bp, (char *) buf, BUFSIZE); 609 if (i < 0) { 610 BIO_printf(bio_err, "Read Error in %s\n", file); 611 ERR_print_errors(bio_err); 612 return 1; 613 } 614 if (i == 0) 615 break; 616 } 617 if (sigin) { 618 EVP_MD_CTX *ctx; 619 BIO_get_md_ctx(bp, &ctx); 620 i = EVP_DigestVerifyFinal(ctx, sigin, (unsigned int) siglen); 621 if (i > 0) 622 BIO_printf(out, "Verified OK\n"); 623 else if (i == 0) { 624 BIO_printf(out, "Verification Failure\n"); 625 return 1; 626 } else { 627 BIO_printf(bio_err, "Error Verifying Data\n"); 628 ERR_print_errors(bio_err); 629 return 1; 630 } 631 return 0; 632 } 633 if (key) { 634 EVP_MD_CTX *ctx; 635 BIO_get_md_ctx(bp, &ctx); 636 len = BUFSIZE; 637 if (!EVP_DigestSignFinal(ctx, buf, &len)) { 638 BIO_printf(bio_err, "Error Signing Data\n"); 639 ERR_print_errors(bio_err); 640 return 1; 641 } 642 } else { 643 len = BIO_gets(bp, (char *) buf, BUFSIZE); 644 if ((int) len < 0) { 645 ERR_print_errors(bio_err); 646 return 1; 647 } 648 } 649 650 if (binout) 651 BIO_write(out, buf, len); 652 else if (sep == 2) { 653 for (i = 0; i < (int) len; i++) 654 BIO_printf(out, "%02x", buf[i]); 655 BIO_printf(out, " *%s\n", file); 656 } else { 657 if (sig_name) 658 BIO_printf(out, "%s-%s(%s)= ", sig_name, md_name, file); 659 else if (md_name) 660 BIO_printf(out, "%s(%s)= ", md_name, file); 661 else 662 BIO_printf(out, "(%s)= ", file); 663 for (i = 0; i < (int) len; i++) { 664 if (sep && (i != 0)) 665 BIO_printf(out, ":"); 666 BIO_printf(out, "%02x", buf[i]); 667 } 668 BIO_printf(out, "\n"); 669 } 670 return 0; 671 } 672