lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame^] | 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 | |
| 21 | static int init_keygen_file(EVP_PKEY_CTX **pctx, const char *file, ENGINE *e); |
| 22 | static int genpkey_cb(EVP_PKEY_CTX *ctx); |
| 23 | |
| 24 | typedef 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 | |
| 30 | const 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 | |
| 51 | int 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 | |
| 211 | static 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 | |
| 253 | int 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 | |
| 307 | static 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 | } |