1 /* $OpenBSD: dgst.c,v 1.19 2022/01/14 09:28:07 tb 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 (single_execution) { 342 if (pledge("stdio cpath wpath rpath tty", NULL) == -1) { 343 perror("pledge"); 344 exit(1); 345 } 346 } 347 348 if ((buf = malloc(BUFSIZE)) == NULL) { 349 BIO_printf(bio_err, "out of memory\n"); 350 goto end; 351 } 352 353 memset(&dgst_config, 0, sizeof(dgst_config)); 354 dgst_config.keyform = FORMAT_PEM; 355 dgst_config.out_bin = -1; 356 357 /* first check the program name */ 358 program_name(argv[0], pname, sizeof pname); 359 360 dgst_config.md = EVP_get_digestbyname(pname); 361 362 if (options_parse(argc, argv, dgst_options, NULL, 363 &dgst_config.argsused) != 0) { 364 dgst_usage(); 365 goto end; 366 } 367 argc -= dgst_config.argsused; 368 argv += dgst_config.argsused; 369 370 if (dgst_config.do_verify && !dgst_config.sigfile) { 371 BIO_printf(bio_err, 372 "No signature to verify: use the -signature option\n"); 373 goto end; 374 } 375 376 in = BIO_new(BIO_s_file()); 377 bmd = BIO_new(BIO_f_md()); 378 if (in == NULL || bmd == NULL) { 379 ERR_print_errors(bio_err); 380 goto end; 381 } 382 383 if (dgst_config.debug) { 384 BIO_set_callback(in, BIO_debug_callback); 385 /* needed for windows 3.1 */ 386 BIO_set_callback_arg(in, (char *) bio_err); 387 } 388 if (!app_passwd(bio_err, dgst_config.passargin, NULL, &passin, NULL)) { 389 BIO_printf(bio_err, "Error getting password\n"); 390 goto end; 391 } 392 if (dgst_config.out_bin == -1) { 393 if (dgst_config.keyfile) 394 dgst_config.out_bin = 1; 395 else 396 dgst_config.out_bin = 0; 397 } 398 399 if (dgst_config.outfile) { 400 if (dgst_config.out_bin) 401 out = BIO_new_file(dgst_config.outfile, "wb"); 402 else 403 out = BIO_new_file(dgst_config.outfile, "w"); 404 } else { 405 out = BIO_new_fp(stdout, BIO_NOCLOSE); 406 } 407 408 if (!out) { 409 BIO_printf(bio_err, "Error opening output file %s\n", 410 dgst_config.outfile ? dgst_config.outfile : "(stdout)"); 411 ERR_print_errors(bio_err); 412 goto end; 413 } 414 if ((!!dgst_config.mac_name + !!dgst_config.keyfile + 415 !!dgst_config.hmac_key) > 1) { 416 BIO_printf(bio_err, 417 "MAC and Signing key cannot both be specified\n"); 418 goto end; 419 } 420 if (dgst_config.keyfile) { 421 if (dgst_config.want_pub) 422 sigkey = load_pubkey(bio_err, dgst_config.keyfile, 423 dgst_config.keyform, 0, NULL, "key file"); 424 else 425 sigkey = load_key(bio_err, dgst_config.keyfile, 426 dgst_config.keyform, 0, passin, "key file"); 427 if (!sigkey) { 428 /* 429 * load_[pub]key() has already printed an appropriate 430 * message 431 */ 432 goto end; 433 } 434 } 435 if (dgst_config.mac_name) { 436 EVP_PKEY_CTX *mac_ctx = NULL; 437 int r = 0; 438 if (!init_gen_str(bio_err, &mac_ctx, dgst_config.mac_name, 0)) 439 goto mac_end; 440 if (dgst_config.macopts) { 441 char *macopt; 442 for (i = 0; i < sk_OPENSSL_STRING_num( 443 dgst_config.macopts); i++) { 444 macopt = sk_OPENSSL_STRING_value( 445 dgst_config.macopts, i); 446 if (pkey_ctrl_string(mac_ctx, macopt) <= 0) { 447 BIO_printf(bio_err, 448 "MAC parameter error \"%s\"\n", 449 macopt); 450 ERR_print_errors(bio_err); 451 goto mac_end; 452 } 453 } 454 } 455 if (EVP_PKEY_keygen(mac_ctx, &sigkey) <= 0) { 456 BIO_puts(bio_err, "Error generating key\n"); 457 ERR_print_errors(bio_err); 458 goto mac_end; 459 } 460 r = 1; 461 mac_end: 462 EVP_PKEY_CTX_free(mac_ctx); 463 if (r == 0) 464 goto end; 465 } 466 if (dgst_config.hmac_key) { 467 sigkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, 468 (unsigned char *) dgst_config.hmac_key, -1); 469 if (!sigkey) 470 goto end; 471 } 472 if (sigkey) { 473 EVP_MD_CTX *mctx = NULL; 474 EVP_PKEY_CTX *pctx = NULL; 475 int r; 476 if (!BIO_get_md_ctx(bmd, &mctx)) { 477 BIO_printf(bio_err, "Error getting context\n"); 478 ERR_print_errors(bio_err); 479 goto end; 480 } 481 if (dgst_config.do_verify) 482 r = EVP_DigestVerifyInit(mctx, &pctx, dgst_config.md, 483 NULL, sigkey); 484 else 485 r = EVP_DigestSignInit(mctx, &pctx, dgst_config.md, 486 NULL, sigkey); 487 if (!r) { 488 BIO_printf(bio_err, "Error setting context\n"); 489 ERR_print_errors(bio_err); 490 goto end; 491 } 492 if (dgst_config.sigopts) { 493 char *sigopt; 494 for (i = 0; i < sk_OPENSSL_STRING_num( 495 dgst_config.sigopts); i++) { 496 sigopt = sk_OPENSSL_STRING_value( 497 dgst_config.sigopts, i); 498 if (pkey_ctrl_string(pctx, sigopt) <= 0) { 499 BIO_printf(bio_err, 500 "parameter error \"%s\"\n", 501 sigopt); 502 ERR_print_errors(bio_err); 503 goto end; 504 } 505 } 506 } 507 } 508 /* we use md as a filter, reading from 'in' */ 509 else { 510 if (dgst_config.md == NULL) 511 dgst_config.md = EVP_sha256(); 512 if (!BIO_set_md(bmd, dgst_config.md)) { 513 BIO_printf(bio_err, "Error setting digest %s\n", pname); 514 ERR_print_errors(bio_err); 515 goto end; 516 } 517 } 518 519 if (dgst_config.sigfile && sigkey) { 520 BIO *sigbio; 521 siglen = EVP_PKEY_size(sigkey); 522 sigbuf = malloc(siglen); 523 if (sigbuf == NULL) { 524 BIO_printf(bio_err, "out of memory\n"); 525 ERR_print_errors(bio_err); 526 goto end; 527 } 528 sigbio = BIO_new_file(dgst_config.sigfile, "rb"); 529 if (!sigbio) { 530 BIO_printf(bio_err, "Error opening signature file %s\n", 531 dgst_config.sigfile); 532 ERR_print_errors(bio_err); 533 goto end; 534 } 535 siglen = BIO_read(sigbio, sigbuf, siglen); 536 BIO_free(sigbio); 537 if (siglen <= 0) { 538 BIO_printf(bio_err, "Error reading signature file %s\n", 539 dgst_config.sigfile); 540 ERR_print_errors(bio_err); 541 goto end; 542 } 543 } 544 inp = BIO_push(bmd, in); 545 546 if (dgst_config.md == NULL) { 547 EVP_MD_CTX *tctx; 548 BIO_get_md_ctx(bmd, &tctx); 549 dgst_config.md = EVP_MD_CTX_md(tctx); 550 } 551 if (argc == 0) { 552 BIO_set_fp(in, stdin, BIO_NOCLOSE); 553 err = do_fp(out, buf, inp, dgst_config.separator, 554 dgst_config.out_bin, sigkey, sigbuf, siglen, NULL, NULL, 555 "stdin", bmd); 556 } else { 557 const char *md_name = NULL, *sig_name = NULL; 558 if (!dgst_config.out_bin) { 559 if (sigkey) { 560 const EVP_PKEY_ASN1_METHOD *ameth; 561 ameth = EVP_PKEY_get0_asn1(sigkey); 562 if (ameth) 563 EVP_PKEY_asn1_get0_info(NULL, NULL, 564 NULL, NULL, &sig_name, ameth); 565 } 566 md_name = EVP_MD_name(dgst_config.md); 567 } 568 err = 0; 569 for (i = 0; i < argc; i++) { 570 int r; 571 if (BIO_read_filename(in, argv[i]) <= 0) { 572 perror(argv[i]); 573 err++; 574 continue; 575 } else { 576 r = do_fp(out, buf, inp, dgst_config.separator, 577 dgst_config.out_bin, sigkey, sigbuf, siglen, 578 sig_name, md_name, argv[i], bmd); 579 } 580 if (r) 581 err = r; 582 (void) BIO_reset(bmd); 583 } 584 } 585 586 end: 587 freezero(buf, BUFSIZE); 588 BIO_free(in); 589 free(passin); 590 BIO_free_all(out); 591 EVP_PKEY_free(sigkey); 592 sk_OPENSSL_STRING_free(dgst_config.sigopts); 593 sk_OPENSSL_STRING_free(dgst_config.macopts); 594 free(sigbuf); 595 BIO_free(bmd); 596 597 return (err); 598 } 599 600 int 601 do_fp(BIO * out, unsigned char *buf, BIO * bp, int sep, int binout, 602 EVP_PKEY * key, unsigned char *sigin, int siglen, 603 const char *sig_name, const char *md_name, 604 const char *file, BIO * bmd) 605 { 606 size_t len; 607 int i; 608 609 for (;;) { 610 i = BIO_read(bp, (char *) buf, BUFSIZE); 611 if (i < 0) { 612 BIO_printf(bio_err, "Read Error in %s\n", file); 613 ERR_print_errors(bio_err); 614 return 1; 615 } 616 if (i == 0) 617 break; 618 } 619 if (sigin) { 620 EVP_MD_CTX *ctx; 621 BIO_get_md_ctx(bp, &ctx); 622 i = EVP_DigestVerifyFinal(ctx, sigin, (unsigned int) siglen); 623 if (i > 0) 624 BIO_printf(out, "Verified OK\n"); 625 else if (i == 0) { 626 BIO_printf(out, "Verification Failure\n"); 627 return 1; 628 } else { 629 BIO_printf(bio_err, "Error Verifying Data\n"); 630 ERR_print_errors(bio_err); 631 return 1; 632 } 633 return 0; 634 } 635 if (key) { 636 EVP_MD_CTX *ctx; 637 BIO_get_md_ctx(bp, &ctx); 638 len = BUFSIZE; 639 if (!EVP_DigestSignFinal(ctx, buf, &len)) { 640 BIO_printf(bio_err, "Error Signing Data\n"); 641 ERR_print_errors(bio_err); 642 return 1; 643 } 644 } else { 645 len = BIO_gets(bp, (char *) buf, BUFSIZE); 646 if ((int) len < 0) { 647 ERR_print_errors(bio_err); 648 return 1; 649 } 650 } 651 652 if (binout) 653 BIO_write(out, buf, len); 654 else if (sep == 2) { 655 for (i = 0; i < (int) len; i++) 656 BIO_printf(out, "%02x", buf[i]); 657 BIO_printf(out, " *%s\n", file); 658 } else { 659 if (sig_name) 660 BIO_printf(out, "%s-%s(%s)= ", sig_name, md_name, file); 661 else if (md_name) 662 BIO_printf(out, "%s(%s)= ", md_name, file); 663 else 664 BIO_printf(out, "(%s)= ", file); 665 for (i = 0; i < (int) len; i++) { 666 if (sep && (i != 0)) 667 BIO_printf(out, ":"); 668 BIO_printf(out, "%02x", buf[i]); 669 } 670 BIO_printf(out, "\n"); 671 } 672 return 0; 673 } 674