xref: /openbsd/usr.bin/openssl/rsautl.c (revision 440d1414)
1 /* $OpenBSD: rsautl.c,v 1.24 2023/07/23 11:39:29 tb Exp $ */
2 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
3  * project 2000.
4  */
5 /* ====================================================================
6  * Copyright (c) 2000 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 <openssl/opensslconf.h>
60 
61 #include <string.h>
62 
63 #include "apps.h"
64 
65 #include <openssl/err.h>
66 #include <openssl/pem.h>
67 #include <openssl/rsa.h>
68 
69 #define RSA_SIGN	1
70 #define RSA_VERIFY	2
71 #define RSA_ENCRYPT	3
72 #define RSA_DECRYPT	4
73 
74 #define KEY_PRIVKEY	1
75 #define KEY_PUBKEY	2
76 #define KEY_CERT	3
77 
78 static struct {
79 	int asn1parse;
80 	int hexdump;
81 	char *infile;
82 	char *keyfile;
83 	int keyform;
84 	int key_type;
85 	char *outfile;
86 	int pad;
87 	char *passargin;
88 	int rev;
89 	int rsa_mode;
90 } cfg;
91 
92 static const struct option rsautl_options[] = {
93 	{
94 		.name = "asn1parse",
95 		.desc = "ASN.1 parse the output data",
96 		.type = OPTION_FLAG,
97 		.opt.flag = &cfg.asn1parse,
98 	},
99 	{
100 		.name = "certin",
101 		.desc = "Input is a certificate containing an RSA public key",
102 		.type = OPTION_VALUE,
103 		.value = KEY_CERT,
104 		.opt.value = &cfg.key_type,
105 	},
106 	{
107 		.name = "decrypt",
108 		.desc = "Decrypt the input data using RSA private key",
109 		.type = OPTION_VALUE,
110 		.value = RSA_DECRYPT,
111 		.opt.value = &cfg.rsa_mode,
112 	},
113 	{
114 		.name = "encrypt",
115 		.desc = "Encrypt the input data using RSA public key",
116 		.type = OPTION_VALUE,
117 		.value = RSA_ENCRYPT,
118 		.opt.value = &cfg.rsa_mode,
119 	},
120 	{
121 		.name = "hexdump",
122 		.desc = "Hex dump the output data",
123 		.type = OPTION_FLAG,
124 		.opt.flag = &cfg.hexdump,
125 	},
126 	{
127 		.name = "in",
128 		.argname = "file",
129 		.desc = "Input file (default stdin)",
130 		.type = OPTION_ARG,
131 		.opt.arg = &cfg.infile,
132 	},
133 	{
134 		.name = "inkey",
135 		.argname = "file",
136 		.desc = "Input key file",
137 		.type = OPTION_ARG,
138 		.opt.arg = &cfg.keyfile,
139 	},
140 	{
141 		.name = "keyform",
142 		.argname = "fmt",
143 		.desc = "Input key format (DER, TXT or PEM (default))",
144 		.type = OPTION_ARG_FORMAT,
145 		.opt.value = &cfg.keyform,
146 	},
147 	{
148 		.name = "oaep",
149 		.desc = "Use PKCS#1 OAEP padding",
150 		.type = OPTION_VALUE,
151 		.value = RSA_PKCS1_OAEP_PADDING,
152 		.opt.value = &cfg.pad,
153 	},
154 	{
155 		.name = "out",
156 		.argname = "file",
157 		.desc = "Output file (default stdout)",
158 		.type = OPTION_ARG,
159 		.opt.arg = &cfg.outfile,
160 	},
161 	{
162 		.name = "passin",
163 		.argname = "arg",
164 		.desc = "Key password source",
165 		.type = OPTION_ARG,
166 		.opt.arg = &cfg.passargin,
167 	},
168 	{
169 		.name = "pkcs",
170 		.desc = "Use PKCS#1 v1.5 padding (default)",
171 		.type = OPTION_VALUE,
172 		.value = RSA_PKCS1_PADDING,
173 		.opt.value = &cfg.pad,
174 	},
175 	{
176 		.name = "pubin",
177 		.desc = "Input is an RSA public key",
178 		.type = OPTION_VALUE,
179 		.value = KEY_PUBKEY,
180 		.opt.value = &cfg.key_type,
181 	},
182 	{
183 		.name = "raw",
184 		.desc = "Use no padding",
185 		.type = OPTION_VALUE,
186 		.value = RSA_NO_PADDING,
187 		.opt.value = &cfg.pad,
188 	},
189 	{
190 		.name = "rev",
191 		.desc = "Reverse the input data",
192 		.type = OPTION_FLAG,
193 		.opt.flag = &cfg.rev,
194 	},
195 	{
196 		.name = "sign",
197 		.desc = "Sign the input data using RSA private key",
198 		.type = OPTION_VALUE,
199 		.value = RSA_SIGN,
200 		.opt.value = &cfg.rsa_mode,
201 	},
202 	{
203 		.name = "verify",
204 		.desc = "Verify the input data using RSA public key",
205 		.type = OPTION_VALUE,
206 		.value = RSA_VERIFY,
207 		.opt.value = &cfg.rsa_mode,
208 	},
209 	{
210 		.name = "x931",
211 		.desc = "Use ANSI X9.31 padding",
212 		.type = OPTION_VALUE,
213 		.value = RSA_X931_PADDING,
214 		.opt.value = &cfg.pad,
215 	},
216 
217 	{NULL},
218 };
219 
220 static void
rsautl_usage(void)221 rsautl_usage(void)
222 {
223 	fprintf(stderr,
224 	    "usage: rsautl [-asn1parse] [-certin] [-decrypt] [-encrypt] "
225 	    "[-hexdump]\n"
226 	    "    [-in file] [-inkey file] [-keyform der | pem]\n"
227 	    "    [-oaep | -pkcs | -raw | -x931] [-out file] [-passin arg]\n"
228 	    "    [-pubin] [-rev] [-sign] [-verify]\n\n");
229 
230 	options_usage(rsautl_options);
231 }
232 
233 int
rsautl_main(int argc,char ** argv)234 rsautl_main(int argc, char **argv)
235 {
236 	BIO *in = NULL, *out = NULL;
237 	X509 *x;
238 	EVP_PKEY *pkey = NULL;
239 	RSA *rsa = NULL;
240 	unsigned char *rsa_in = NULL, *rsa_out = NULL;
241 	char *passin = NULL;
242 	int rsa_inlen, rsa_outlen = 0;
243 	int need_priv = 0;
244 	int keysize;
245 	int ret = 1;
246 
247 	if (pledge("stdio cpath wpath rpath tty", NULL) == -1) {
248 		perror("pledge");
249 		exit(1);
250 	}
251 
252 	memset(&cfg, 0, sizeof(cfg));
253 	cfg.keyform = FORMAT_PEM;
254 	cfg.key_type = KEY_PRIVKEY;
255 	cfg.pad = RSA_PKCS1_PADDING;
256 	cfg.rsa_mode = RSA_VERIFY;
257 
258 	if (options_parse(argc, argv, rsautl_options, NULL, NULL) != 0) {
259 		rsautl_usage();
260 		return (1);
261 	}
262 
263 	if (cfg.rsa_mode == RSA_SIGN ||
264 	    cfg.rsa_mode == RSA_DECRYPT)
265 		need_priv = 1;
266 
267 	if (need_priv && cfg.key_type != KEY_PRIVKEY) {
268 		BIO_printf(bio_err, "A private key is needed for this operation\n");
269 		goto end;
270 	}
271 	if (!app_passwd(bio_err, cfg.passargin, NULL, &passin, NULL)) {
272 		BIO_printf(bio_err, "Error getting password\n");
273 		goto end;
274 	}
275 
276 	switch (cfg.key_type) {
277 	case KEY_PRIVKEY:
278 		pkey = load_key(bio_err, cfg.keyfile,
279 		    cfg.keyform, 0, passin, "Private Key");
280 		break;
281 
282 	case KEY_PUBKEY:
283 		pkey = load_pubkey(bio_err, cfg.keyfile,
284 		    cfg.keyform, 0, NULL, "Public Key");
285 		break;
286 
287 	case KEY_CERT:
288 		x = load_cert(bio_err, cfg.keyfile,
289 		    cfg.keyform, NULL, "Certificate");
290 		if (x) {
291 			pkey = X509_get_pubkey(x);
292 			X509_free(x);
293 		}
294 		break;
295 	}
296 
297 	if (!pkey)
298 		goto end;
299 
300 	rsa = EVP_PKEY_get1_RSA(pkey);
301 	EVP_PKEY_free(pkey);
302 
303 	if (!rsa) {
304 		BIO_printf(bio_err, "Error getting RSA key\n");
305 		ERR_print_errors(bio_err);
306 		goto end;
307 	}
308 	if (cfg.infile) {
309 		if (!(in = BIO_new_file(cfg.infile, "rb"))) {
310 			BIO_printf(bio_err, "Error Reading Input File\n");
311 			ERR_print_errors(bio_err);
312 			goto end;
313 		}
314 	} else
315 		in = BIO_new_fp(stdin, BIO_NOCLOSE);
316 
317 	if (cfg.outfile) {
318 		if (!(out = BIO_new_file(cfg.outfile, "wb"))) {
319 			BIO_printf(bio_err, "Error Reading Output File\n");
320 			ERR_print_errors(bio_err);
321 			goto end;
322 		}
323 	} else {
324 		out = BIO_new_fp(stdout, BIO_NOCLOSE);
325 	}
326 
327 	keysize = RSA_size(rsa);
328 
329 	rsa_in = reallocarray(NULL, keysize, 2);
330 	if (rsa_in == NULL) {
331 		BIO_printf(bio_err, "Error allocating memory for input data\n");
332 		exit(1);
333 	}
334 	rsa_out = malloc(keysize);
335 	if (rsa_out == NULL) {
336 		BIO_printf(bio_err, "Error allocating memory for output data\n");
337 		exit(1);
338 	}
339 
340 	/* Read the input data */
341 	rsa_inlen = BIO_read(in, rsa_in, keysize * 2);
342 	if (rsa_inlen <= 0) {
343 		BIO_printf(bio_err, "Error reading input Data\n");
344 		exit(1);
345 	}
346 	if (cfg.rev) {
347 		int i;
348 		unsigned char ctmp;
349 		for (i = 0; i < rsa_inlen / 2; i++) {
350 			ctmp = rsa_in[i];
351 			rsa_in[i] = rsa_in[rsa_inlen - 1 - i];
352 			rsa_in[rsa_inlen - 1 - i] = ctmp;
353 		}
354 	}
355 
356 	switch (cfg.rsa_mode) {
357 	case RSA_VERIFY:
358 		rsa_outlen = RSA_public_decrypt(rsa_inlen, rsa_in, rsa_out,
359 		    rsa, cfg.pad);
360 		break;
361 
362 	case RSA_SIGN:
363 		rsa_outlen = RSA_private_encrypt(rsa_inlen, rsa_in, rsa_out,
364 		    rsa, cfg.pad);
365 		break;
366 
367 	case RSA_ENCRYPT:
368 		rsa_outlen = RSA_public_encrypt(rsa_inlen, rsa_in, rsa_out,
369 		    rsa, cfg.pad);
370 		break;
371 
372 	case RSA_DECRYPT:
373 		rsa_outlen = RSA_private_decrypt(rsa_inlen, rsa_in, rsa_out,
374 		    rsa, cfg.pad);
375 		break;
376 	}
377 
378 	if (rsa_outlen <= 0) {
379 		BIO_printf(bio_err, "RSA operation error\n");
380 		ERR_print_errors(bio_err);
381 		goto end;
382 	}
383 	ret = 0;
384 	if (cfg.asn1parse) {
385 		if (!ASN1_parse_dump(out, rsa_out, rsa_outlen, 1, -1)) {
386 			ERR_print_errors(bio_err);
387 		}
388 	} else if (cfg.hexdump)
389 		BIO_dump(out, (char *) rsa_out, rsa_outlen);
390 	else
391 		BIO_write(out, rsa_out, rsa_outlen);
392 
393  end:
394 	RSA_free(rsa);
395 	BIO_free(in);
396 	BIO_free_all(out);
397 	free(rsa_in);
398 	free(rsa_out);
399 	free(passin);
400 
401 	return ret;
402 }
403