xref: /openbsd/usr.bin/openssl/pkeyutl.c (revision 440d1414)
1 /* $OpenBSD: pkeyutl.c,v 1.20 2023/07/23 11:39:29 tb Exp $ */
2 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
3  * project 2006.
4  */
5 /* ====================================================================
6  * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. All advertising materials mentioning features or use of this
21  *    software must display the following acknowledgment:
22  *    "This product includes software developed by the OpenSSL Project
23  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24  *
25  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26  *    endorse or promote products derived from this software without
27  *    prior written permission. For written permission, please contact
28  *    licensing@OpenSSL.org.
29  *
30  * 5. Products derived from this software may not be called "OpenSSL"
31  *    nor may "OpenSSL" appear in their names without prior written
32  *    permission of the OpenSSL Project.
33  *
34  * 6. Redistributions of any form whatsoever must retain the following
35  *    acknowledgment:
36  *    "This product includes software developed by the OpenSSL Project
37  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38  *
39  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50  * OF THE POSSIBILITY OF SUCH DAMAGE.
51  * ====================================================================
52  *
53  * This product includes cryptographic software written by Eric Young
54  * (eay@cryptsoft.com).  This product includes software written by Tim
55  * Hudson (tjh@cryptsoft.com).
56  *
57  */
58 
59 #include <string.h>
60 
61 #include "apps.h"
62 
63 #include <openssl/err.h>
64 #include <openssl/evp.h>
65 #include <openssl/pem.h>
66 
67 #define KEY_PRIVKEY	1
68 #define KEY_PUBKEY	2
69 #define KEY_CERT	3
70 
71 static struct {
72 	int asn1parse;
73 	EVP_PKEY_CTX *ctx;
74 	int hexdump;
75 	char *infile;
76 	int key_type;
77 	int keyform;
78 	int keysize;
79 	char *outfile;
80 	char *passargin;
81 	int peerform;
82 	int pkey_op;
83 	int rev;
84 	char *sigfile;
85 } cfg;
86 
87 static void pkeyutl_usage(void);
88 
89 static int init_ctx(char *keyfile);
90 
91 static int setup_peer(char *file);
92 
93 static int pkeyutl_pkeyopt(char *pkeyopt);
94 
95 static int do_keyop(EVP_PKEY_CTX * ctx, int pkey_op,
96     unsigned char *out, size_t * poutlen,
97     unsigned char *in, size_t inlen);
98 
99 static const struct option pkeyutl_options[] = {
100 	{
101 		.name = "asn1parse",
102 		.desc = "ASN.1 parse the output data",
103 		.type = OPTION_FLAG,
104 		.opt.flag = &cfg.asn1parse,
105 	},
106 	{
107 		.name = "certin",
108 		.desc = "Input is a certificate containing a public key",
109 		.type = OPTION_VALUE,
110 		.value = KEY_CERT,
111 		.opt.value = &cfg.key_type,
112 	},
113 	{
114 		.name = "decrypt",
115 		.desc = "Decrypt the input data using a private key",
116 		.type = OPTION_VALUE,
117 		.value = EVP_PKEY_OP_DECRYPT,
118 		.opt.value = &cfg.pkey_op,
119 	},
120 	{
121 		.name = "derive",
122 		.desc = "Derive a shared secret using the peer key",
123 		.type = OPTION_VALUE,
124 		.value = EVP_PKEY_OP_DERIVE,
125 		.opt.value = &cfg.pkey_op,
126 	},
127 	{
128 		.name = "encrypt",
129 		.desc = "Encrypt the input data using a public key",
130 		.type = OPTION_VALUE,
131 		.value = EVP_PKEY_OP_ENCRYPT,
132 		.opt.value = &cfg.pkey_op,
133 	},
134 	{
135 		.name = "hexdump",
136 		.desc = "Hex dump the output data",
137 		.type = OPTION_FLAG,
138 		.opt.flag = &cfg.hexdump,
139 	},
140 	{
141 		.name = "in",
142 		.argname = "file",
143 		.desc = "Input file (default stdin)",
144 		.type = OPTION_ARG,
145 		.opt.arg = &cfg.infile,
146 	},
147 	{
148 		.name = "inkey",
149 		.argname = "file",
150 		.desc = "Input key file",
151 		.type = OPTION_ARG_FUNC,
152 		.opt.argfunc = init_ctx,
153 	},
154 	{
155 		.name = "keyform",
156 		.argname = "fmt",
157 		.desc = "Input key format (DER or PEM (default))",
158 		.type = OPTION_ARG_FORMAT,
159 		.opt.value = &cfg.keyform,
160 	},
161 	{
162 		.name = "out",
163 		.argname = "file",
164 		.desc = "Output file (default stdout)",
165 		.type = OPTION_ARG,
166 		.opt.arg = &cfg.outfile,
167 	},
168 	{
169 		.name = "passin",
170 		.argname = "arg",
171 		.desc = "Key password source",
172 		.type = OPTION_ARG,
173 		.opt.arg = &cfg.passargin,
174 	},
175 	{
176 		.name = "peerform",
177 		.argname = "fmt",
178 		.desc = "Input key format (DER or PEM (default))",
179 		.type = OPTION_ARG_FORMAT,
180 		.opt.value = &cfg.peerform,
181 	},
182 	{
183 		.name = "peerkey",
184 		.argname = "file",
185 		.desc = "Peer key file",
186 		.type = OPTION_ARG_FUNC,
187 		.opt.argfunc = setup_peer,
188 	},
189 	{
190 		.name = "pkeyopt",
191 		.argname = "opt:value",
192 		.desc = "Public key options",
193 		.type = OPTION_ARG_FUNC,
194 		.opt.argfunc = pkeyutl_pkeyopt,
195 	},
196 	{
197 		.name = "pubin",
198 		.desc = "Input is a public key",
199 		.type = OPTION_VALUE,
200 		.value = KEY_PUBKEY,
201 		.opt.value = &cfg.key_type,
202 	},
203 	{
204 		.name = "rev",
205 		.desc = "Reverse the input data",
206 		.type = OPTION_FLAG,
207 		.opt.flag = &cfg.rev,
208 	},
209 	{
210 		.name = "sigfile",
211 		.argname = "file",
212 		.desc = "Signature file (verify operation only)",
213 		.type = OPTION_ARG,
214 		.opt.arg = &cfg.sigfile,
215 	},
216 	{
217 		.name = "sign",
218 		.desc = "Sign the input data using private key",
219 		.type = OPTION_VALUE,
220 		.value = EVP_PKEY_OP_SIGN,
221 		.opt.value = &cfg.pkey_op,
222 	},
223 	{
224 		.name = "verify",
225 		.desc = "Verify the input data using public key",
226 		.type = OPTION_VALUE,
227 		.value = EVP_PKEY_OP_VERIFY,
228 		.opt.value = &cfg.pkey_op,
229 	},
230 	{
231 		.name = "verifyrecover",
232 		.desc = "Verify with public key, recover original data",
233 		.type = OPTION_VALUE,
234 		.value = EVP_PKEY_OP_VERIFYRECOVER,
235 		.opt.value = &cfg.pkey_op,
236 	},
237 
238 	{NULL},
239 };
240 
241 static void
pkeyutl_usage(void)242 pkeyutl_usage(void)
243 {
244 	fprintf(stderr,
245 	    "usage: pkeyutl [-asn1parse] [-certin] [-decrypt] [-derive] "
246 	    "[-encrypt]\n"
247 	    "    [-hexdump] [-in file] [-inkey file] [-keyform fmt]\n"
248 	    "    [-out file] [-passin arg] [-peerform fmt]\n"
249 	    "    [-peerkey file] [-pkeyopt opt:value] [-pubin] [-rev]\n"
250 	    "    [-sigfile file] [-sign] [-verify] [-verifyrecover]\n\n");
251 	options_usage(pkeyutl_options);
252         fprintf(stderr, "\n");
253 }
254 
255 int
pkeyutl_main(int argc,char ** argv)256 pkeyutl_main(int argc, char **argv)
257 {
258 	BIO *in = NULL, *out = NULL;
259 
260 	unsigned char *buf_in = NULL, *buf_out = NULL, *sig = NULL;
261 	size_t buf_outlen = 0;
262 	int buf_inlen = 0, siglen = -1;
263 
264 	int ret = 1, rv = -1;
265 
266 	if (pledge("stdio cpath wpath rpath tty", NULL) == -1) {
267 		perror("pledge");
268 		exit(1);
269 	}
270 
271 	memset(&cfg, 0, sizeof(cfg));
272 	cfg.pkey_op = EVP_PKEY_OP_SIGN;
273 	cfg.key_type = KEY_PRIVKEY;
274 	cfg.keyform = FORMAT_PEM;
275 	cfg.peerform = FORMAT_PEM;
276 	cfg.keysize = -1;
277 
278 	if (options_parse(argc, argv, pkeyutl_options, NULL, NULL) != 0) {
279 		pkeyutl_usage();
280 		goto end;
281 	}
282 
283 	if (!cfg.ctx) {
284 		pkeyutl_usage();
285 		goto end;
286 	}
287 	if (cfg.sigfile &&
288 	    (cfg.pkey_op != EVP_PKEY_OP_VERIFY)) {
289 		BIO_puts(bio_err, "Signature file specified for non verify\n");
290 		goto end;
291 	}
292 	if (!cfg.sigfile &&
293 	    (cfg.pkey_op == EVP_PKEY_OP_VERIFY)) {
294 		BIO_puts(bio_err, "No signature file specified for verify\n");
295 		goto end;
296 	}
297 
298 	if (cfg.pkey_op != EVP_PKEY_OP_DERIVE) {
299 		if (cfg.infile) {
300 			if (!(in = BIO_new_file(cfg.infile, "rb"))) {
301 				BIO_puts(bio_err,
302 				    "Error Opening Input File\n");
303 				ERR_print_errors(bio_err);
304 				goto end;
305 			}
306 		} else
307 			in = BIO_new_fp(stdin, BIO_NOCLOSE);
308 	}
309 	if (cfg.outfile) {
310 		if (!(out = BIO_new_file(cfg.outfile, "wb"))) {
311 			BIO_printf(bio_err, "Error Creating Output File\n");
312 			ERR_print_errors(bio_err);
313 			goto end;
314 		}
315 	} else {
316 		out = BIO_new_fp(stdout, BIO_NOCLOSE);
317 	}
318 
319 	if (cfg.sigfile) {
320 		BIO *sigbio = BIO_new_file(cfg.sigfile, "rb");
321 		if (!sigbio) {
322 			BIO_printf(bio_err, "Can't open signature file %s\n",
323 			    cfg.sigfile);
324 			goto end;
325 		}
326 		siglen = bio_to_mem(&sig, cfg.keysize * 10, sigbio);
327 		BIO_free(sigbio);
328 		if (siglen <= 0) {
329 			BIO_printf(bio_err, "Error reading signature data\n");
330 			goto end;
331 		}
332 	}
333 	if (in) {
334 		/* Read the input data */
335 		buf_inlen = bio_to_mem(&buf_in, cfg.keysize * 10, in);
336 		if (buf_inlen <= 0) {
337 			BIO_printf(bio_err, "Error reading input Data\n");
338 			exit(1);
339 		}
340 		if (cfg.rev) {
341 			size_t i;
342 			unsigned char ctmp;
343 			size_t l = (size_t) buf_inlen;
344 			for (i = 0; i < l / 2; i++) {
345 				ctmp = buf_in[i];
346 				buf_in[i] = buf_in[l - 1 - i];
347 				buf_in[l - 1 - i] = ctmp;
348 			}
349 		}
350 	}
351 	if (cfg.pkey_op == EVP_PKEY_OP_VERIFY) {
352 		rv = EVP_PKEY_verify(cfg.ctx, sig, (size_t) siglen,
353 		    buf_in, (size_t) buf_inlen);
354 		if (rv == 1) {
355 			BIO_puts(out, "Signature Verified Successfully\n");
356 			ret = 0;
357 		} else
358 			BIO_puts(out, "Signature Verification Failure\n");
359 		if (rv >= 0)
360 			goto end;
361 	} else {
362 		rv = do_keyop(cfg.ctx, cfg.pkey_op, NULL,
363 		    (size_t *)&buf_outlen, buf_in, (size_t) buf_inlen);
364 		if (rv > 0) {
365 			buf_out = malloc(buf_outlen);
366 			if (!buf_out)
367 				rv = -1;
368 			else
369 				rv = do_keyop(cfg.ctx,
370 				    cfg.pkey_op,
371 				    buf_out, (size_t *) & buf_outlen,
372 				    buf_in, (size_t) buf_inlen);
373 		}
374 	}
375 
376 	if (rv <= 0) {
377 		BIO_printf(bio_err, "Public Key operation error\n");
378 		ERR_print_errors(bio_err);
379 		goto end;
380 	}
381 	ret = 0;
382 	if (cfg.asn1parse) {
383 		if (!ASN1_parse_dump(out, buf_out, buf_outlen, 1, -1))
384 			ERR_print_errors(bio_err);
385 	} else if (cfg.hexdump)
386 		BIO_dump(out, (char *) buf_out, buf_outlen);
387 	else
388 		BIO_write(out, buf_out, buf_outlen);
389 
390  end:
391 	EVP_PKEY_CTX_free(cfg.ctx);
392 	BIO_free(in);
393 	BIO_free_all(out);
394 	free(buf_in);
395 	free(buf_out);
396 	free(sig);
397 
398 	return ret;
399 }
400 
401 static int
init_ctx(char * keyfile)402 init_ctx(char *keyfile)
403 {
404 	EVP_PKEY *pkey = NULL;
405 	char *passin = NULL;
406 	int rv = -1;
407 	X509 *x;
408 
409 	if (((cfg.pkey_op == EVP_PKEY_OP_SIGN)
410 		|| (cfg.pkey_op == EVP_PKEY_OP_DECRYPT)
411 		|| (cfg.pkey_op == EVP_PKEY_OP_DERIVE))
412 	    && (cfg.key_type != KEY_PRIVKEY)) {
413 		BIO_printf(bio_err,
414 		    "A private key is needed for this operation\n");
415 		goto end;
416 	}
417 	if (!app_passwd(bio_err, cfg.passargin, NULL, &passin,
418 	    NULL)) {
419 		BIO_printf(bio_err, "Error getting password\n");
420 		goto end;
421 	}
422 	switch (cfg.key_type) {
423 	case KEY_PRIVKEY:
424 		pkey = load_key(bio_err, keyfile, cfg.keyform, 0,
425 		    passin, "Private Key");
426 		break;
427 
428 	case KEY_PUBKEY:
429 		pkey = load_pubkey(bio_err, keyfile, cfg.keyform, 0,
430 		    NULL, "Public Key");
431 		break;
432 
433 	case KEY_CERT:
434 		x = load_cert(bio_err, keyfile, cfg.keyform,
435 		    NULL, "Certificate");
436 		if (x) {
437 			pkey = X509_get_pubkey(x);
438 			X509_free(x);
439 		}
440 		break;
441 	}
442 
443 	cfg.keysize = EVP_PKEY_size(pkey);
444 
445 	if (!pkey)
446 		goto end;
447 
448 	cfg.ctx = EVP_PKEY_CTX_new(pkey, NULL);
449 
450 	EVP_PKEY_free(pkey);
451 
452 	if (!cfg.ctx)
453 		goto end;
454 
455 	switch (cfg.pkey_op) {
456 	case EVP_PKEY_OP_SIGN:
457 		rv = EVP_PKEY_sign_init(cfg.ctx);
458 		break;
459 
460 	case EVP_PKEY_OP_VERIFY:
461 		rv = EVP_PKEY_verify_init(cfg.ctx);
462 		break;
463 
464 	case EVP_PKEY_OP_VERIFYRECOVER:
465 		rv = EVP_PKEY_verify_recover_init(cfg.ctx);
466 		break;
467 
468 	case EVP_PKEY_OP_ENCRYPT:
469 		rv = EVP_PKEY_encrypt_init(cfg.ctx);
470 		break;
471 
472 	case EVP_PKEY_OP_DECRYPT:
473 		rv = EVP_PKEY_decrypt_init(cfg.ctx);
474 		break;
475 
476 	case EVP_PKEY_OP_DERIVE:
477 		rv = EVP_PKEY_derive_init(cfg.ctx);
478 		break;
479 	}
480 
481 	if (rv <= 0) {
482 		EVP_PKEY_CTX_free(cfg.ctx);
483 		cfg.ctx = NULL;
484 	}
485 
486  end:
487 	free(passin);
488 
489 	if (!cfg.ctx) {
490 		BIO_puts(bio_err, "Error initializing context\n");
491 		ERR_print_errors(bio_err);
492 		return (1);
493 	}
494 
495 	return (0);
496 }
497 
498 static int
setup_peer(char * file)499 setup_peer(char *file)
500 {
501 	EVP_PKEY *peer = NULL;
502 	int ret;
503 
504 	if (!cfg.ctx) {
505 		BIO_puts(bio_err, "-peerkey command before -inkey\n");
506 		return (1);
507 	}
508 	peer = load_pubkey(bio_err, file, cfg.peerform, 0, NULL,
509 	    "Peer Key");
510 
511 	if (!peer) {
512 		BIO_printf(bio_err, "Error reading peer key %s\n", file);
513 		ERR_print_errors(bio_err);
514 		return (1);
515 	}
516 	ret = EVP_PKEY_derive_set_peer(cfg.ctx, peer);
517 
518 	EVP_PKEY_free(peer);
519 	if (ret <= 0) {
520 		ERR_print_errors(bio_err);
521 		return (1);
522 	}
523 
524 	return (0);
525 }
526 
527 static int
pkeyutl_pkeyopt(char * pkeyopt)528 pkeyutl_pkeyopt(char *pkeyopt)
529 {
530 	if (!cfg.ctx) {
531 		BIO_puts(bio_err, "-pkeyopt command before -inkey\n");
532 		return (1);
533 	} else if (pkey_ctrl_string(cfg.ctx, pkeyopt) <= 0) {
534 		BIO_puts(bio_err, "parameter setting error\n");
535 		ERR_print_errors(bio_err);
536 		return (1);
537 	}
538 
539 	return (0);
540 }
541 
542 static int
do_keyop(EVP_PKEY_CTX * ctx,int pkey_op,unsigned char * out,size_t * poutlen,unsigned char * in,size_t inlen)543 do_keyop(EVP_PKEY_CTX * ctx, int pkey_op,
544     unsigned char *out, size_t * poutlen,
545     unsigned char *in, size_t inlen)
546 {
547 	int rv = 0;
548 	switch (pkey_op) {
549 	case EVP_PKEY_OP_VERIFYRECOVER:
550 		rv = EVP_PKEY_verify_recover(ctx, out, poutlen, in, inlen);
551 		break;
552 
553 	case EVP_PKEY_OP_SIGN:
554 		rv = EVP_PKEY_sign(ctx, out, poutlen, in, inlen);
555 		break;
556 
557 	case EVP_PKEY_OP_ENCRYPT:
558 		rv = EVP_PKEY_encrypt(ctx, out, poutlen, in, inlen);
559 		break;
560 
561 	case EVP_PKEY_OP_DECRYPT:
562 		rv = EVP_PKEY_decrypt(ctx, out, poutlen, in, inlen);
563 		break;
564 
565 	case EVP_PKEY_OP_DERIVE:
566 		rv = EVP_PKEY_derive(ctx, out, poutlen);
567 		break;
568 
569 	}
570 	return rv;
571 }
572