1/* $OpenBSD: pkey.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 <stdio.h>
60#include <string.h>
61
62#include "apps.h"
63
64#include <openssl/err.h>
65#include <openssl/evp.h>
66#include <openssl/pem.h>
67
68static struct {
69	int check;
70	const EVP_CIPHER *cipher;
71	char *infile;
72	int informat;
73	int noout;
74	char *outfile;
75	int outformat;
76	char *passargin;
77	char *passargout;
78	int pubcheck;
79	int pubin;
80	int pubout;
81	int pubtext;
82	int text;
83} cfg;
84
85static int
86pkey_opt_cipher(int argc, char **argv, int *argsused)
87{
88	char *name = argv[0];
89
90	if (*name++ != '-')
91		return (1);
92
93	if ((cfg.cipher = EVP_get_cipherbyname(name)) == NULL) {
94		BIO_printf(bio_err, "Unknown cipher %s\n", name);
95		return (1);
96	}
97
98	*argsused = 1;
99	return (0);
100}
101
102static const struct option pkey_options[] = {
103	{
104		.name = "check",
105		.desc = "Check validity of key",
106		.type = OPTION_FLAG,
107		.opt.flag = &cfg.check,
108	},
109	{
110		.name = "in",
111		.argname = "file",
112		.desc = "Input file (default stdin)",
113		.type = OPTION_ARG,
114		.opt.arg = &cfg.infile,
115	},
116	{
117		.name = "inform",
118		.argname = "format",
119		.desc = "Input format (DER or PEM (default))",
120		.type = OPTION_ARG_FORMAT,
121		.opt.value = &cfg.informat,
122	},
123	{
124		.name = "noout",
125		.desc = "Do not print encoded version of the key",
126		.type = OPTION_FLAG,
127		.opt.flag = &cfg.noout,
128	},
129	{
130		.name = "out",
131		.argname = "file",
132		.desc = "Output file (default stdout)",
133		.type = OPTION_ARG,
134		.opt.arg = &cfg.outfile,
135	},
136	{
137		.name = "outform",
138		.argname = "format",
139		.desc = "Output format (DER or PEM (default))",
140		.type = OPTION_ARG_FORMAT,
141		.opt.value = &cfg.outformat,
142	},
143	{
144		.name = "passin",
145		.argname = "src",
146		.desc = "Input file passphrase source",
147		.type = OPTION_ARG,
148		.opt.arg = &cfg.passargin,
149	},
150	{
151		.name = "passout",
152		.argname = "src",
153		.desc = "Output file passphrase source",
154		.type = OPTION_ARG,
155		.opt.arg = &cfg.passargout,
156	},
157	{
158		.name = "pubcheck",
159		.desc = "Check validity of public key",
160		.type = OPTION_FLAG,
161		.opt.flag = &cfg.pubcheck,
162	},
163	{
164		.name = "pubin",
165		.desc = "Expect a public key (default private key)",
166		.type = OPTION_VALUE,
167		.value = 1,
168		.opt.value = &cfg.pubin,
169	},
170	{
171		.name = "pubout",
172		.desc = "Output a public key (default private key)",
173		.type = OPTION_VALUE,
174		.value = 1,
175		.opt.value = &cfg.pubout,
176	},
177	{
178		.name = "text",
179		.desc = "Print the public/private key in plain text",
180		.type = OPTION_FLAG,
181		.opt.flag = &cfg.text,
182	},
183	{
184		.name = "text_pub",
185		.desc = "Print out only public key in plain text",
186		.type = OPTION_FLAG,
187		.opt.flag = &cfg.pubtext,
188	},
189	{
190		.name = NULL,
191		.type = OPTION_ARGV_FUNC,
192		.opt.argvfunc = pkey_opt_cipher,
193	},
194	{ NULL }
195};
196
197static void
198pkey_usage(void)
199{
200	int n = 0;
201
202	fprintf(stderr,
203	    "usage: pkey [-check] [-ciphername] [-in file] [-inform fmt] "
204	    "[-noout] [-out file]\n"
205	    "    [-outform fmt] [-passin src] [-passout src] [-pubcheck] "
206	    "[-pubin] [-pubout]\n"
207	    "    [-text] [-text_pub]\n\n");
208	options_usage(pkey_options);
209	fprintf(stderr, "\n");
210
211	fprintf(stderr, "Valid ciphername values:\n\n");
212	OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH, show_cipher, &n);
213	fprintf(stderr, "\n");
214}
215
216int
217pkey_main(int argc, char **argv)
218{
219	BIO *in = NULL, *out = NULL;
220	EVP_PKEY *pkey = NULL;
221	char *passin = NULL, *passout = NULL;
222	int ret = 1;
223
224	if (pledge("stdio cpath wpath rpath tty", NULL) == -1) {
225		perror("pledge");
226		exit(1);
227	}
228
229	memset(&cfg, 0, sizeof(cfg));
230	cfg.informat = FORMAT_PEM;
231	cfg.outformat = FORMAT_PEM;
232
233	if (options_parse(argc, argv, pkey_options, NULL, NULL) != 0) {
234		pkey_usage();
235		goto end;
236	}
237
238	if (cfg.pubtext)
239		cfg.text = 1;
240	if (cfg.pubin)
241		cfg.pubout = cfg.pubtext = 1;
242
243	if (!app_passwd(bio_err, cfg.passargin, cfg.passargout,
244	    &passin, &passout)) {
245		BIO_printf(bio_err, "Error getting passwords\n");
246		goto end;
247	}
248	if (cfg.outfile) {
249		if (!(out = BIO_new_file(cfg.outfile, "wb"))) {
250			BIO_printf(bio_err,
251			    "Can't open output file %s\n", cfg.outfile);
252			goto end;
253		}
254	} else {
255		out = BIO_new_fp(stdout, BIO_NOCLOSE);
256	}
257
258	if (cfg.pubin)
259		pkey = load_pubkey(bio_err, cfg.infile,
260		    cfg.informat, 1, passin, "Public Key");
261	else
262		pkey = load_key(bio_err, cfg.infile,
263		    cfg.informat, 1, passin, "key");
264	if (!pkey)
265		goto end;
266
267	if (cfg.check) {
268		if (!pkey_check(out, pkey, EVP_PKEY_check, "Key pair"))
269			goto end;
270	} else if (cfg.pubcheck) {
271		if (!pkey_check(out, pkey, EVP_PKEY_public_check, "Public key"))
272			goto end;
273	}
274
275	if (!cfg.noout) {
276		if (cfg.outformat == FORMAT_PEM) {
277			if (cfg.pubout)
278				PEM_write_bio_PUBKEY(out, pkey);
279			else
280				PEM_write_bio_PrivateKey(out, pkey,
281				    cfg.cipher, NULL, 0, NULL, passout);
282		} else if (cfg.outformat == FORMAT_ASN1) {
283			if (cfg.pubout)
284				i2d_PUBKEY_bio(out, pkey);
285			else
286				i2d_PrivateKey_bio(out, pkey);
287		} else {
288			BIO_printf(bio_err, "Bad format specified for key\n");
289			goto end;
290		}
291
292	}
293	if (cfg.text) {
294		if (cfg.pubtext)
295			EVP_PKEY_print_public(out, pkey, 0, NULL);
296		else
297			EVP_PKEY_print_private(out, pkey, 0, NULL);
298	}
299	ret = 0;
300
301 end:
302	EVP_PKEY_free(pkey);
303	BIO_free_all(out);
304	BIO_free(in);
305	free(passin);
306	free(passout);
307
308	return ret;
309}
310