Files
Viktor Dukhovni d172868e53 EC: make the group the single source of the point conversion form
The point conversion form (compressed, uncompressed, or hybrid)
was kept both on the key and on the group, and the two could
disagree -- a key imported as compressed could re-encode as
uncompressed. The group is now the single source of truth:
encoding, parameter output, and the legacy lookup all read it from
the group, and decoding (PEM, DER, or raw parameters) records it
there, so the form round-trips faithfully.

Generated keys are always uncompressed; the point-format option at
key generation is now a documented no-op (it had had no effect for
several releases), and the unused form field on the keygen context
is dropped.  Imported keys still keep their form, and the
deprecated EC_KEY_get_conv_form()/EC_KEY_set_conv_form() still
work.

EVP_PKEY_fromdata() and openssl pkey -text now report the form a
loaded EC key actually has, and re-encoding via PEM or DER
preserves it.

Docs drop a stale note about a compile-time macro for compressed
points on binary curves, and the EC tests now exercise both the
affine and the compressed/hybrid binary formats unconditionally
(fixing a latent bug in the compressed/hybrid form tests that were
never exercised by CI).

Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.foundation>
MergeDate: Fri Jul 10 09:21:29 2026
(Merged from https://github.com/openssl/openssl/pull/30940)
2026-07-10 11:21:28 +02:00

293 lines
8.8 KiB
C

/*
* Copyright 2002-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <string.h>
#include <openssl/opensslconf.h>
#include <openssl/evp.h>
#include <openssl/encoder.h>
#include <openssl/decoder.h>
#include <openssl/core_names.h>
#include <openssl/core_dispatch.h>
#include <openssl/params.h>
#include <openssl/err.h>
#include "apps.h"
#include "progs.h"
#include "ec_common.h"
typedef enum OPTION_choice {
OPT_COMMON,
OPT_INFORM,
OPT_OUTFORM,
OPT_IN,
OPT_OUT,
OPT_NOOUT,
OPT_TEXT,
OPT_PARAM_OUT,
OPT_PUBIN,
OPT_PUBOUT,
OPT_PASSIN,
OPT_PASSOUT,
OPT_PARAM_ENC,
OPT_CONV_FORM,
OPT_CIPHER,
OPT_NO_PUBLIC,
OPT_CHECK,
OPT_PROV_ENUM
} OPTION_CHOICE;
const OPTIONS ec_options[] = {
OPT_SECTION("General"),
{ "help", OPT_HELP, '-', "Display this summary" },
OPT_SECTION("Input"),
{ "in", OPT_IN, 's', "Input file" },
{ "inform", OPT_INFORM, 'f', "Input format (DER/PEM/P12)" },
{ "pubin", OPT_PUBIN, '-', "Expect a public key in input file" },
{ "passin", OPT_PASSIN, 's', "Input file pass phrase source" },
{ "check", OPT_CHECK, '-', "check key consistency" },
{ "", OPT_CIPHER, '-', "Any supported cipher" },
{ "param_enc", OPT_PARAM_ENC, 's',
"Selects between named_curve and explicit EC parameter encoding" },
{ "conv_form", OPT_CONV_FORM, 's', "Specifies the point conversion form " },
OPT_SECTION("Output"),
{ "out", OPT_OUT, '>', "Output file" },
{ "outform", OPT_OUTFORM, 'F', "Output format - DER or PEM" },
{ "noout", OPT_NOOUT, '-', "Don't print key out" },
{ "text", OPT_TEXT, '-', "Print the key" },
{ "param_out", OPT_PARAM_OUT, '-', "Print the elliptic curve parameters" },
{ "pubout", OPT_PUBOUT, '-', "Output public key, not private" },
{ "no_public", OPT_NO_PUBLIC, '-', "exclude public key from private key" },
{ "passout", OPT_PASSOUT, 's', "Output file pass phrase source" },
OPT_PROV_OPTIONS,
{ NULL }
};
int ec_main(int argc, char **argv)
{
OSSL_ENCODER_CTX *ectx = NULL;
OSSL_DECODER_CTX *dctx = NULL;
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *eckey = NULL;
BIO *out = NULL;
EVP_CIPHER *enc = NULL;
char *infile = NULL, *outfile = NULL, *ciphername = NULL, *prog;
char *passin = NULL, *passout = NULL, *passinarg = NULL, *passoutarg = NULL;
OPTION_CHOICE o;
int informat = FORMAT_UNDEF, outformat = FORMAT_PEM, text = 0, noout = 0;
int pubin = 0, pubout = 0, param_out = 0, ret = 1, private = 0;
int check = 0;
char *asn1_encoding = NULL;
char *point_format = NULL;
int no_public = 0;
opt_set_unknown_name("cipher");
prog = opt_init(argc, argv, ec_options);
while ((o = opt_next()) != OPT_EOF) {
switch (o) {
case OPT_EOF:
case OPT_ERR:
opthelp:
BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
goto end;
case OPT_HELP:
opt_help(ec_options);
ret = 0;
goto end;
case OPT_INFORM:
if (!opt_format(opt_arg(), OPT_FMT_ANY, &informat))
goto opthelp;
break;
case OPT_IN:
infile = opt_arg();
break;
case OPT_OUTFORM:
if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat))
goto opthelp;
break;
case OPT_OUT:
outfile = opt_arg();
break;
case OPT_NOOUT:
noout = 1;
break;
case OPT_TEXT:
text = 1;
break;
case OPT_PARAM_OUT:
param_out = 1;
break;
case OPT_PUBIN:
pubin = 1;
break;
case OPT_PUBOUT:
pubout = 1;
break;
case OPT_PASSIN:
passinarg = opt_arg();
break;
case OPT_PASSOUT:
passoutarg = opt_arg();
break;
case OPT_CIPHER:
ciphername = opt_unknown();
break;
case OPT_CONV_FORM:
point_format = opt_arg();
if (!opt_string(point_format, point_format_options))
goto opthelp;
break;
case OPT_PARAM_ENC:
asn1_encoding = opt_arg();
if (!opt_string(asn1_encoding, asn1_encoding_options))
goto opthelp;
break;
case OPT_NO_PUBLIC:
no_public = 1;
break;
case OPT_CHECK:
check = 1;
break;
case OPT_PROV_CASES:
if (!opt_provider(o))
goto end;
break;
}
}
/* No extra arguments. */
if (!opt_check_rest_arg(NULL))
goto opthelp;
if (!opt_cipher(ciphername, &enc))
goto opthelp;
private = !pubin && (text || (!param_out && !pubout));
if (!app_passwd(passinarg, passoutarg, &passin, &passout)) {
BIO_puts(bio_err, "Error getting passwords\n");
goto end;
}
if (pubin)
eckey = load_pubkey(infile, informat, 1, passin, "public key");
else
eckey = load_key(infile, informat, 1, passin, "private key");
if (eckey == NULL) {
BIO_puts(bio_err, "unable to load Key\n");
goto end;
}
out = bio_open_owner(outfile, outformat, private);
if (out == NULL)
goto end;
if (point_format
&& !EVP_PKEY_set_utf8_string_param(
eckey, OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
point_format)) {
BIO_puts(bio_err, "unable to set point conversion format\n");
goto end;
}
if (asn1_encoding != NULL
&& !EVP_PKEY_set_utf8_string_param(
eckey, OSSL_PKEY_PARAM_EC_ENCODING, asn1_encoding)) {
BIO_puts(bio_err, "unable to set asn1 encoding format\n");
goto end;
}
if (no_public) {
if (!EVP_PKEY_set_int_param(eckey, OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, 0)) {
BIO_puts(bio_err, "unable to disable public key encoding\n");
goto end;
}
} else {
if (!EVP_PKEY_set_int_param(eckey, OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, 1)) {
BIO_puts(bio_err, "unable to enable public key encoding\n");
goto end;
}
}
if (text) {
assert(pubin || private);
if ((pubin && EVP_PKEY_print_public(out, eckey, 0, NULL) <= 0)
|| (!pubin && EVP_PKEY_print_private(out, eckey, 0, NULL) <= 0)) {
BIO_puts(bio_err, "unable to print EC key\n");
goto end;
}
}
if (check) {
pctx = EVP_PKEY_CTX_new_from_pkey(NULL, eckey, NULL);
if (pctx == NULL) {
BIO_puts(bio_err, "unable to check EC key\n");
goto end;
}
if (EVP_PKEY_check(pctx) <= 0)
BIO_puts(bio_err, "EC Key Invalid!\n");
else
BIO_puts(bio_err, "EC Key valid.\n");
ERR_print_errors(bio_err);
}
if (!noout) {
int selection;
const char *output_type = outformat == FORMAT_ASN1 ? "DER" : "PEM";
const char *output_structure = "type-specific";
if (param_out) {
selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
} else if (pubin || pubout) {
selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
| OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
output_structure = "SubjectPublicKeyInfo";
} else {
selection = OSSL_KEYMGMT_SELECT_ALL;
assert(private);
}
ectx = OSSL_ENCODER_CTX_new_for_pkey(eckey, selection,
output_type, output_structure,
NULL);
if (enc != NULL) {
OSSL_ENCODER_CTX_set_cipher(ectx, EVP_CIPHER_get0_name(enc), NULL);
/* Default passphrase prompter */
OSSL_ENCODER_CTX_set_passphrase_ui(ectx, get_ui_method(), NULL);
if (passout != NULL)
/* When passout given, override the passphrase prompter */
OSSL_ENCODER_CTX_set_passphrase(ectx,
(const unsigned char *)passout,
strlen(passout));
}
if (!OSSL_ENCODER_to_bio(ectx, out)) {
BIO_puts(bio_err, "unable to write EC key\n");
goto end;
}
}
ret = 0;
end:
if (ret != 0)
ERR_print_errors(bio_err);
BIO_free_all(out);
EVP_PKEY_free(eckey);
EVP_CIPHER_free(enc);
OSSL_ENCODER_CTX_free(ectx);
OSSL_DECODER_CTX_free(dctx);
EVP_PKEY_CTX_free(pctx);
if (passin != NULL)
OPENSSL_clear_free(passin, strlen(passin));
if (passout != NULL)
OPENSSL_clear_free(passout, strlen(passout));
return ret;
}