1/*
2 * Copyright 2002-2022 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (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 <string.h>
11#include <openssl/opensslconf.h>
12#include <openssl/evp.h>
13#include <openssl/encoder.h>
14#include <openssl/decoder.h>
15#include <openssl/core_names.h>
16#include <openssl/core_dispatch.h>
17#include <openssl/params.h>
18#include <openssl/err.h>
19
20#include "apps.h"
21#include "progs.h"
22#include "ec_common.h"
23
24typedef enum OPTION_choice {
25    OPT_COMMON,
26    OPT_INFORM, OPT_OUTFORM, OPT_ENGINE, OPT_IN, OPT_OUT,
27    OPT_NOOUT, OPT_TEXT, OPT_PARAM_OUT, OPT_PUBIN, OPT_PUBOUT,
28    OPT_PASSIN, OPT_PASSOUT, OPT_PARAM_ENC, OPT_CONV_FORM, OPT_CIPHER,
29    OPT_NO_PUBLIC, OPT_CHECK, OPT_PROV_ENUM
30} OPTION_CHOICE;
31
32const OPTIONS ec_options[] = {
33    OPT_SECTION("General"),
34    {"help", OPT_HELP, '-', "Display this summary"},
35#ifndef OPENSSL_NO_ENGINE
36    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
37#endif
38
39    OPT_SECTION("Input"),
40    {"in", OPT_IN, 's', "Input file"},
41    {"inform", OPT_INFORM, 'f', "Input format (DER/PEM/P12/ENGINE)"},
42    {"pubin", OPT_PUBIN, '-', "Expect a public key in input file"},
43    {"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
44    {"check", OPT_CHECK, '-', "check key consistency"},
45    {"", OPT_CIPHER, '-', "Any supported cipher"},
46    {"param_enc", OPT_PARAM_ENC, 's',
47     "Specifies the way the ec parameters are encoded"},
48    {"conv_form", OPT_CONV_FORM, 's', "Specifies the point conversion form "},
49
50    OPT_SECTION("Output"),
51    {"out", OPT_OUT, '>', "Output file"},
52    {"outform", OPT_OUTFORM, 'F', "Output format - DER or PEM"},
53    {"noout", OPT_NOOUT, '-', "Don't print key out"},
54    {"text", OPT_TEXT, '-', "Print the key"},
55    {"param_out", OPT_PARAM_OUT, '-', "Print the elliptic curve parameters"},
56    {"pubout", OPT_PUBOUT, '-', "Output public key, not private"},
57    {"no_public", OPT_NO_PUBLIC, '-', "exclude public key from private key"},
58    {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"},
59
60    OPT_PROV_OPTIONS,
61    {NULL}
62};
63
64int ec_main(int argc, char **argv)
65{
66    OSSL_ENCODER_CTX *ectx = NULL;
67    OSSL_DECODER_CTX *dctx = NULL;
68    EVP_PKEY_CTX *pctx = NULL;
69    EVP_PKEY *eckey = NULL;
70    BIO *out = NULL;
71    ENGINE *e = NULL;
72    EVP_CIPHER *enc = NULL;
73    char *infile = NULL, *outfile = NULL, *ciphername = NULL, *prog;
74    char *passin = NULL, *passout = NULL, *passinarg = NULL, *passoutarg = NULL;
75    OPTION_CHOICE o;
76    int informat = FORMAT_UNDEF, outformat = FORMAT_PEM, text = 0, noout = 0;
77    int pubin = 0, pubout = 0, param_out = 0, ret = 1, private = 0;
78    int check = 0;
79    char *asn1_encoding = NULL;
80    char *point_format = NULL;
81    int no_public = 0;
82
83    prog = opt_init(argc, argv, ec_options);
84    while ((o = opt_next()) != OPT_EOF) {
85        switch (o) {
86        case OPT_EOF:
87        case OPT_ERR:
88 opthelp:
89            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
90            goto end;
91        case OPT_HELP:
92            opt_help(ec_options);
93            ret = 0;
94            goto end;
95        case OPT_INFORM:
96            if (!opt_format(opt_arg(), OPT_FMT_ANY, &informat))
97                goto opthelp;
98            break;
99        case OPT_IN:
100            infile = opt_arg();
101            break;
102        case OPT_OUTFORM:
103            if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat))
104                goto opthelp;
105            break;
106        case OPT_OUT:
107            outfile = opt_arg();
108            break;
109        case OPT_NOOUT:
110            noout = 1;
111            break;
112        case OPT_TEXT:
113            text = 1;
114            break;
115        case OPT_PARAM_OUT:
116            param_out = 1;
117            break;
118        case OPT_PUBIN:
119            pubin = 1;
120            break;
121        case OPT_PUBOUT:
122            pubout = 1;
123            break;
124        case OPT_PASSIN:
125            passinarg = opt_arg();
126            break;
127        case OPT_PASSOUT:
128            passoutarg = opt_arg();
129            break;
130        case OPT_ENGINE:
131            e = setup_engine(opt_arg(), 0);
132            break;
133        case OPT_CIPHER:
134            ciphername = opt_unknown();
135            break;
136        case OPT_CONV_FORM:
137            point_format = opt_arg();
138            if (!opt_string(point_format, point_format_options))
139                goto opthelp;
140            break;
141        case OPT_PARAM_ENC:
142            asn1_encoding = opt_arg();
143            if (!opt_string(asn1_encoding, asn1_encoding_options))
144                goto opthelp;
145            break;
146        case OPT_NO_PUBLIC:
147            no_public = 1;
148            break;
149        case OPT_CHECK:
150            check = 1;
151            break;
152        case OPT_PROV_CASES:
153            if (!opt_provider(o))
154                goto end;
155            break;
156        }
157    }
158
159    /* No extra arguments. */
160    argc = opt_num_rest();
161    if (argc != 0)
162        goto opthelp;
163
164    if (ciphername != NULL) {
165        if (!opt_cipher(ciphername, &enc))
166            goto opthelp;
167    }
168    private = param_out || pubin || pubout ? 0 : 1;
169    if (text && !pubin)
170        private = 1;
171
172    if (!app_passwd(passinarg, passoutarg, &passin, &passout)) {
173        BIO_printf(bio_err, "Error getting passwords\n");
174        goto end;
175    }
176
177    BIO_printf(bio_err, "read EC key\n");
178
179    if (pubin)
180        eckey = load_pubkey(infile, informat, 1, passin, e, "public key");
181    else
182        eckey = load_key(infile, informat, 1, passin, e, "private key");
183
184    if (eckey == NULL) {
185        BIO_printf(bio_err, "unable to load Key\n");
186        goto end;
187    }
188
189    out = bio_open_owner(outfile, outformat, private);
190    if (out == NULL)
191        goto end;
192
193    if (point_format
194        && !EVP_PKEY_set_utf8_string_param(
195                eckey, OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
196                point_format)) {
197        BIO_printf(bio_err, "unable to set point conversion format\n");
198        goto end;
199    }
200
201    if (asn1_encoding != NULL
202        && !EVP_PKEY_set_utf8_string_param(
203                eckey, OSSL_PKEY_PARAM_EC_ENCODING, asn1_encoding)) {
204        BIO_printf(bio_err, "unable to set asn1 encoding format\n");
205        goto end;
206    }
207
208    if (no_public) {
209        if (!EVP_PKEY_set_int_param(eckey, OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, 0)) {
210            BIO_printf(bio_err, "unable to disable public key encoding\n");
211            goto end;
212        }
213    } else {
214        if (!EVP_PKEY_set_int_param(eckey, OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, 1)) {
215            BIO_printf(bio_err, "unable to enable public key encoding\n");
216            goto end;
217        }
218    }
219
220    if (text) {
221        assert(pubin || private);
222        if ((pubin && EVP_PKEY_print_public(out, eckey, 0, NULL) <= 0)
223            || (!pubin && EVP_PKEY_print_private(out, eckey, 0, NULL) <= 0)) {
224            BIO_printf(bio_err, "unable to print EC key\n");
225            goto end;
226        }
227    }
228
229    if (check) {
230        pctx = EVP_PKEY_CTX_new_from_pkey(NULL, eckey, NULL);
231        if (pctx == NULL) {
232            BIO_printf(bio_err, "unable to check EC key\n");
233            goto end;
234        }
235        if (EVP_PKEY_check(pctx) <= 0)
236            BIO_printf(bio_err, "EC Key Invalid!\n");
237        else
238            BIO_printf(bio_err, "EC Key valid.\n");
239        ERR_print_errors(bio_err);
240    }
241
242    if (!noout) {
243        int selection;
244        const char *output_type = outformat == FORMAT_ASN1 ? "DER" : "PEM";
245        const char *output_structure = "type-specific";
246
247        BIO_printf(bio_err, "writing EC key\n");
248        if (param_out) {
249            selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
250        } else if (pubin || pubout) {
251            selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
252                | OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
253            output_structure = "SubjectPublicKeyInfo";
254        } else {
255            selection = OSSL_KEYMGMT_SELECT_ALL;
256            assert(private);
257        }
258
259        ectx = OSSL_ENCODER_CTX_new_for_pkey(eckey, selection,
260                                             output_type, output_structure,
261                                             NULL);
262        if (enc != NULL) {
263            OSSL_ENCODER_CTX_set_cipher(ectx, EVP_CIPHER_get0_name(enc), NULL);
264            /* Default passphrase prompter */
265            OSSL_ENCODER_CTX_set_passphrase_ui(ectx, get_ui_method(), NULL);
266            if (passout != NULL)
267                /* When passout given, override the passphrase prompter */
268                OSSL_ENCODER_CTX_set_passphrase(ectx,
269                                                (const unsigned char *)passout,
270                                                strlen(passout));
271        }
272        if (!OSSL_ENCODER_to_bio(ectx, out)) {
273            BIO_printf(bio_err, "unable to write EC key\n");
274            goto end;
275        }
276    }
277
278    ret = 0;
279end:
280    if (ret != 0)
281        ERR_print_errors(bio_err);
282    BIO_free_all(out);
283    EVP_PKEY_free(eckey);
284    EVP_CIPHER_free(enc);
285    OSSL_ENCODER_CTX_free(ectx);
286    OSSL_DECODER_CTX_free(dctx);
287    EVP_PKEY_CTX_free(pctx);
288    release_engine(e);
289    if (passin != NULL)
290        OPENSSL_clear_free(passin, strlen(passin));
291    if (passout != NULL)
292        OPENSSL_clear_free(passout, strlen(passout));
293    return ret;
294}
295