1/*
2 * Copyright 2006-2020 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the OpenSSL license (the "License").  You may not use
5 * this file except in compliance with the License.  You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10#include <stdio.h>
11#include <string.h>
12#include "apps.h"
13#include "progs.h"
14#include <openssl/pem.h>
15#include <openssl/err.h>
16#include <openssl/evp.h>
17#ifndef OPENSSL_NO_ENGINE
18# include <openssl/engine.h>
19#endif
20
21static int init_keygen_file(EVP_PKEY_CTX **pctx, const char *file, ENGINE *e);
22static int genpkey_cb(EVP_PKEY_CTX *ctx);
23
24typedef enum OPTION_choice {
25    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
26    OPT_ENGINE, OPT_OUTFORM, OPT_OUT, OPT_PASS, OPT_PARAMFILE,
27    OPT_ALGORITHM, OPT_PKEYOPT, OPT_GENPARAM, OPT_TEXT, OPT_CIPHER
28} OPTION_CHOICE;
29
30const OPTIONS genpkey_options[] = {
31    {"help", OPT_HELP, '-', "Display this summary"},
32    {"out", OPT_OUT, '>', "Output file"},
33    {"outform", OPT_OUTFORM, 'F', "output format (DER or PEM)"},
34    {"pass", OPT_PASS, 's', "Output file pass phrase source"},
35    {"paramfile", OPT_PARAMFILE, '<', "Parameters file"},
36    {"algorithm", OPT_ALGORITHM, 's', "The public key algorithm"},
37    {"pkeyopt", OPT_PKEYOPT, 's',
38     "Set the public key algorithm option as opt:value"},
39    {"genparam", OPT_GENPARAM, '-', "Generate parameters, not key"},
40    {"text", OPT_TEXT, '-', "Print the in text"},
41    {"", OPT_CIPHER, '-', "Cipher to use to encrypt the key"},
42#ifndef OPENSSL_NO_ENGINE
43    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
44#endif
45    /* This is deliberately last. */
46    {OPT_HELP_STR, 1, 1,
47     "Order of options may be important!  See the documentation.\n"},
48    {NULL}
49};
50
51int genpkey_main(int argc, char **argv)
52{
53    BIO *in = NULL, *out = NULL;
54    ENGINE *e = NULL;
55    EVP_PKEY *pkey = NULL;
56    EVP_PKEY_CTX *ctx = NULL;
57    char *outfile = NULL, *passarg = NULL, *pass = NULL, *prog;
58    const EVP_CIPHER *cipher = NULL;
59    OPTION_CHOICE o;
60    int outformat = FORMAT_PEM, text = 0, ret = 1, rv, do_param = 0;
61    int private = 0;
62
63    prog = opt_init(argc, argv, genpkey_options);
64    while ((o = opt_next()) != OPT_EOF) {
65        switch (o) {
66        case OPT_EOF:
67        case OPT_ERR:
68 opthelp:
69            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
70            goto end;
71        case OPT_HELP:
72            ret = 0;
73            opt_help(genpkey_options);
74            goto end;
75        case OPT_OUTFORM:
76            if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat))
77                goto opthelp;
78            break;
79        case OPT_OUT:
80            outfile = opt_arg();
81            break;
82        case OPT_PASS:
83            passarg = opt_arg();
84            break;
85        case OPT_ENGINE:
86            e = setup_engine(opt_arg(), 0);
87            break;
88        case OPT_PARAMFILE:
89            if (do_param == 1)
90                goto opthelp;
91            if (!init_keygen_file(&ctx, opt_arg(), e))
92                goto end;
93            break;
94        case OPT_ALGORITHM:
95            if (!init_gen_str(&ctx, opt_arg(), e, do_param))
96                goto end;
97            break;
98        case OPT_PKEYOPT:
99            if (ctx == NULL) {
100                BIO_printf(bio_err, "%s: No keytype specified.\n", prog);
101                goto opthelp;
102            }
103            if (pkey_ctrl_string(ctx, opt_arg()) <= 0) {
104                BIO_printf(bio_err,
105                           "%s: Error setting %s parameter:\n",
106                           prog, opt_arg());
107                ERR_print_errors(bio_err);
108                goto end;
109            }
110            break;
111        case OPT_GENPARAM:
112            if (ctx != NULL)
113                goto opthelp;
114            do_param = 1;
115            break;
116        case OPT_TEXT:
117            text = 1;
118            break;
119        case OPT_CIPHER:
120            if (!opt_cipher(opt_unknown(), &cipher)
121                || do_param == 1)
122                goto opthelp;
123            if (EVP_CIPHER_mode(cipher) == EVP_CIPH_GCM_MODE ||
124                EVP_CIPHER_mode(cipher) == EVP_CIPH_CCM_MODE ||
125                EVP_CIPHER_mode(cipher) == EVP_CIPH_XTS_MODE ||
126                EVP_CIPHER_mode(cipher) == EVP_CIPH_OCB_MODE) {
127                BIO_printf(bio_err, "%s: cipher mode not supported\n", prog);
128                goto end;
129            }
130        }
131    }
132    argc = opt_num_rest();
133    if (argc != 0)
134        goto opthelp;
135
136    private = do_param ? 0 : 1;
137
138    if (ctx == NULL)
139        goto opthelp;
140
141    if (!app_passwd(passarg, NULL, &pass, NULL)) {
142        BIO_puts(bio_err, "Error getting password\n");
143        goto end;
144    }
145
146    out = bio_open_owner(outfile, outformat, private);
147    if (out == NULL)
148        goto end;
149
150    EVP_PKEY_CTX_set_cb(ctx, genpkey_cb);
151    EVP_PKEY_CTX_set_app_data(ctx, bio_err);
152
153    if (do_param) {
154        if (EVP_PKEY_paramgen(ctx, &pkey) <= 0) {
155            BIO_puts(bio_err, "Error generating parameters\n");
156            ERR_print_errors(bio_err);
157            goto end;
158        }
159    } else {
160        if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
161            BIO_puts(bio_err, "Error generating key\n");
162            ERR_print_errors(bio_err);
163            goto end;
164        }
165    }
166
167    if (do_param) {
168        rv = PEM_write_bio_Parameters(out, pkey);
169    } else if (outformat == FORMAT_PEM) {
170        assert(private);
171        rv = PEM_write_bio_PrivateKey(out, pkey, cipher, NULL, 0, NULL, pass);
172    } else if (outformat == FORMAT_ASN1) {
173        assert(private);
174        rv = i2d_PrivateKey_bio(out, pkey);
175    } else {
176        BIO_printf(bio_err, "Bad format specified for key\n");
177        goto end;
178    }
179
180    ret = 0;
181
182    if (rv <= 0) {
183        BIO_puts(bio_err, "Error writing key\n");
184        ERR_print_errors(bio_err);
185        ret = 1;
186    }
187
188    if (text) {
189        if (do_param)
190            rv = EVP_PKEY_print_params(out, pkey, 0, NULL);
191        else
192            rv = EVP_PKEY_print_private(out, pkey, 0, NULL);
193
194        if (rv <= 0) {
195            BIO_puts(bio_err, "Error printing key\n");
196            ERR_print_errors(bio_err);
197            ret = 1;
198        }
199    }
200
201 end:
202    EVP_PKEY_free(pkey);
203    EVP_PKEY_CTX_free(ctx);
204    BIO_free_all(out);
205    BIO_free(in);
206    release_engine(e);
207    OPENSSL_free(pass);
208    return ret;
209}
210
211static int init_keygen_file(EVP_PKEY_CTX **pctx, const char *file, ENGINE *e)
212{
213    BIO *pbio;
214    EVP_PKEY *pkey = NULL;
215    EVP_PKEY_CTX *ctx = NULL;
216    if (*pctx) {
217        BIO_puts(bio_err, "Parameters already set!\n");
218        return 0;
219    }
220
221    pbio = BIO_new_file(file, "r");
222    if (!pbio) {
223        BIO_printf(bio_err, "Can't open parameter file %s\n", file);
224        return 0;
225    }
226
227    pkey = PEM_read_bio_Parameters(pbio, NULL);
228    BIO_free(pbio);
229
230    if (!pkey) {
231        BIO_printf(bio_err, "Error reading parameter file %s\n", file);
232        return 0;
233    }
234
235    ctx = EVP_PKEY_CTX_new(pkey, e);
236    if (ctx == NULL)
237        goto err;
238    if (EVP_PKEY_keygen_init(ctx) <= 0)
239        goto err;
240    EVP_PKEY_free(pkey);
241    *pctx = ctx;
242    return 1;
243
244 err:
245    BIO_puts(bio_err, "Error initializing context\n");
246    ERR_print_errors(bio_err);
247    EVP_PKEY_CTX_free(ctx);
248    EVP_PKEY_free(pkey);
249    return 0;
250
251}
252
253int init_gen_str(EVP_PKEY_CTX **pctx,
254                 const char *algname, ENGINE *e, int do_param)
255{
256    EVP_PKEY_CTX *ctx = NULL;
257    const EVP_PKEY_ASN1_METHOD *ameth;
258    ENGINE *tmpeng = NULL;
259    int pkey_id;
260
261    if (*pctx) {
262        BIO_puts(bio_err, "Algorithm already set!\n");
263        return 0;
264    }
265
266    ameth = EVP_PKEY_asn1_find_str(&tmpeng, algname, -1);
267
268#ifndef OPENSSL_NO_ENGINE
269    if (!ameth && e)
270        ameth = ENGINE_get_pkey_asn1_meth_str(e, algname, -1);
271#endif
272
273    if (!ameth) {
274        BIO_printf(bio_err, "Algorithm %s not found\n", algname);
275        return 0;
276    }
277
278    ERR_clear_error();
279
280    EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
281#ifndef OPENSSL_NO_ENGINE
282    ENGINE_finish(tmpeng);
283#endif
284    ctx = EVP_PKEY_CTX_new_id(pkey_id, e);
285
286    if (!ctx)
287        goto err;
288    if (do_param) {
289        if (EVP_PKEY_paramgen_init(ctx) <= 0)
290            goto err;
291    } else {
292        if (EVP_PKEY_keygen_init(ctx) <= 0)
293            goto err;
294    }
295
296    *pctx = ctx;
297    return 1;
298
299 err:
300    BIO_printf(bio_err, "Error initializing %s context\n", algname);
301    ERR_print_errors(bio_err);
302    EVP_PKEY_CTX_free(ctx);
303    return 0;
304
305}
306
307static int genpkey_cb(EVP_PKEY_CTX *ctx)
308{
309    char c = '*';
310    BIO *b = EVP_PKEY_CTX_get_app_data(ctx);
311    int p;
312    p = EVP_PKEY_CTX_get_keygen_info(ctx, 0);
313    if (p == 0)
314        c = '.';
315    if (p == 1)
316        c = '+';
317    if (p == 2)
318        c = '*';
319    if (p == 3)
320        c = '\n';
321    BIO_write(b, &c, 1);
322    (void)BIO_flush(b);
323    return 1;
324}
325