summaryrefslogtreecommitdiffstats
path: root/crypto/ec/ec_lib.c
diff options
context:
space:
mode:
authorShane Lontis <shane.lontis@oracle.com>2020-08-22 14:55:41 +1000
committerShane Lontis <shane.lontis@oracle.com>2020-08-22 14:55:41 +1000
commitc0f39ded68ba0929698a8773e63e9806ec9e5c74 (patch)
treedce39d009fceac112a1d320b5e072d94e22ca27d /crypto/ec/ec_lib.c
parenta02c715c183382aa3038fc4d7d463b17e62a24ff (diff)
Add Explicit EC parameter support to providers.
This was added for backward compatability. Added EC_GROUP_new_from_params() that supports explicit curve parameters. This fixes the 15-test_genec.t TODO. Reviewed-by: Richard Levitte <levitte@openssl.org> (Merged from https://github.com/openssl/openssl/pull/12604)
Diffstat (limited to 'crypto/ec/ec_lib.c')
-rw-r--r--crypto/ec/ec_lib.c411
1 files changed, 409 insertions, 2 deletions
diff --git a/crypto/ec/ec_lib.c b/crypto/ec/ec_lib.c
index a0c007a13e..d9298f62d0 100644
--- a/crypto/ec/ec_lib.c
+++ b/crypto/ec/ec_lib.c
@@ -15,11 +15,14 @@
#include "internal/deprecated.h"
#include <string.h>
-
+#include <openssl/params.h>
+#include <openssl/core_names.h>
#include <openssl/err.h>
#include <openssl/opensslv.h>
-
+#include "crypto/ec.h"
+#include "internal/nelem.h"
#include "ec_local.h"
+#include "e_os.h" /* strcasecmp */
/* functions for EC_GROUP objects */
@@ -1317,3 +1320,407 @@ int ec_point_blind_coordinates(const EC_GROUP *group, EC_POINT *p, BN_CTX *ctx)
return group->meth->blind_coordinates(group, p, ctx);
}
+
+int EC_GROUP_get_basis_type(const EC_GROUP *group)
+{
+ int i;
+
+ if (EC_GROUP_get_field_type(group) != NID_X9_62_characteristic_two_field)
+ /* everything else is currently not supported */
+ return 0;
+
+ /* Find the last non-zero element of group->poly[] */
+ for (i = 0;
+ i < (int)OSSL_NELEM(group->poly) && group->poly[i] != 0;
+ i++)
+ continue;
+
+ if (i == 4)
+ return NID_X9_62_ppBasis;
+ else if (i == 2)
+ return NID_X9_62_tpBasis;
+ else
+ /* everything else is currently not supported */
+ return 0;
+}
+
+#ifndef OPENSSL_NO_EC2M
+int EC_GROUP_get_trinomial_basis(const EC_GROUP *group, unsigned int *k)
+{
+ if (group == NULL)
+ return 0;
+
+ if (EC_GROUP_get_field_type(group) != NID_X9_62_characteristic_two_field
+ || !((group->poly[0] != 0) && (group->poly[1] != 0)
+ && (group->poly[2] == 0))) {
+ ECerr(EC_F_EC_GROUP_GET_TRINOMIAL_BASIS,
+ ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+ if (k)
+ *k = group->poly[1];
+
+ return 1;
+}
+
+int EC_GROUP_get_pentanomial_basis(const EC_GROUP *group, unsigned int *k1,
+ unsigned int *k2, unsigned int *k3)
+{
+ if (group == NULL)
+ return 0;
+
+ if (EC_GROUP_get_field_type(group) != NID_X9_62_characteristic_two_field
+ || !((group->poly[0] != 0) && (group->poly[1] != 0)
+ && (group->poly[2] != 0) && (group->poly[3] != 0)
+ && (group->poly[4] == 0))) {
+ ECerr(EC_F_EC_GROUP_GET_PENTANOMIAL_BASIS,
+ ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+ if (k1)
+ *k1 = group->poly[3];
+ if (k2)
+ *k2 = group->poly[2];
+ if (k3)
+ *k3 = group->poly[1];
+
+ return 1;
+}
+#endif
+
+/*
+ * Check if the explicit parameters group matches any built-in curves.
+ *
+ * We create a copy of the group just built, so that we can remove optional
+ * fields for the lookup: we do this to avoid the possibility that one of
+ * the optional parameters is used to force the library into using a less
+ * performant and less secure EC_METHOD instead of the specialized one.
+ * In any case, `seed` is not really used in any computation, while a
+ * cofactor different from the one in the built-in table is just
+ * mathematically wrong anyway and should not be used.
+ */
+static EC_GROUP *ec_group_explicit_to_named(const EC_GROUP *group,
+ OPENSSL_CTX *libctx,
+ const char *propq,
+ BN_CTX *ctx)
+{
+ EC_GROUP *ret_group = NULL, *dup = NULL;
+ int curve_name_nid;
+
+ const EC_POINT *point = EC_GROUP_get0_generator(group);
+ const BIGNUM *order = EC_GROUP_get0_order(group);
+ int no_seed = (EC_GROUP_get0_seed(group) == NULL);
+
+ if ((dup = EC_GROUP_dup(group)) == NULL
+ || EC_GROUP_set_seed(dup, NULL, 0) != 1
+ || !EC_GROUP_set_generator(dup, point, order, NULL))
+ goto err;
+ if ((curve_name_nid = ec_curve_nid_from_params(dup, ctx)) != NID_undef) {
+ /*
+ * The input explicit parameters successfully matched one of the
+ * built-in curves: often for built-in curves we have specialized
+ * methods with better performance and hardening.
+ *
+ * In this case we replace the `EC_GROUP` created through explicit
+ * parameters with one created from a named group.
+ */
+
+#ifndef OPENSSL_NO_EC_NISTP_64_GCC_128
+ /*
+ * NID_wap_wsg_idm_ecid_wtls12 and NID_secp224r1 are both aliases for
+ * the same curve, we prefer the SECP nid when matching explicit
+ * parameters as that is associated with a specialized EC_METHOD.
+ */
+ if (curve_name_nid == NID_wap_wsg_idm_ecid_wtls12)
+ curve_name_nid = NID_secp224r1;
+#endif /* !def(OPENSSL_NO_EC_NISTP_64_GCC_128) */
+
+ ret_group = EC_GROUP_new_by_curve_name_with_libctx(libctx, propq,
+ curve_name_nid);
+ if (ret_group == NULL)
+ goto err;
+
+ /*
+ * Set the flag so that EC_GROUPs created from explicit parameters are
+ * serialized using explicit parameters by default.
+ */
+ EC_GROUP_set_asn1_flag(ret_group, OPENSSL_EC_EXPLICIT_CURVE);
+
+ /*
+ * If the input params do not contain the optional seed field we make
+ * sure it is not added to the returned group.
+ *
+ * The seed field is not really used inside libcrypto anyway, and
+ * adding it to parsed explicit parameter keys would alter their DER
+ * encoding output (because of the extra field) which could impact
+ * applications fingerprinting keys by their DER encoding.
+ */
+ if (no_seed) {
+ if (EC_GROUP_set_seed(ret_group, NULL, 0) != 1)
+ goto err;
+ }
+ } else {
+ ret_group = (EC_GROUP *)group;
+ }
+ EC_GROUP_free(dup);
+ return ret_group;
+err:
+ EC_GROUP_free(dup);
+ EC_GROUP_free(ret_group);
+ return NULL;
+}
+
+static int ec_encoding_param2id(const OSSL_PARAM *p, int *id)
+{
+ const char *name = NULL;
+ int status = 0;
+
+ switch (p->data_type) {
+ case OSSL_PARAM_UTF8_STRING:
+ /* The OSSL_PARAM functions have no support for this */
+ name = p->data;
+ status = (name != NULL);
+ break;
+ case OSSL_PARAM_UTF8_PTR:
+ status = OSSL_PARAM_get_utf8_ptr(p, &name);
+ break;
+ }
+ if (status) {
+ int i = ec_encoding_name2id(name);
+
+ if (i >= 0) {
+ *id = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static EC_GROUP *group_new_from_name(const OSSL_PARAM *p,
+ OPENSSL_CTX *libctx, const char *propq)
+{
+ int ok = 0, nid;
+ const char *curve_name = NULL;
+
+ switch (p->data_type) {
+ case OSSL_PARAM_UTF8_STRING:
+ /* The OSSL_PARAM functions have no support for this */
+ curve_name = p->data;
+ ok = (curve_name != NULL);
+ break;
+ case OSSL_PARAM_UTF8_PTR:
+ ok = OSSL_PARAM_get_utf8_ptr(p, &curve_name);
+ break;
+ }
+
+ if (ok) {
+ nid = ec_curve_name2nid(curve_name);
+ if (nid == NID_undef) {
+ ECerr(0, EC_R_INVALID_CURVE);
+ return NULL;
+ } else {
+ return EC_GROUP_new_by_curve_name_with_libctx(libctx, propq, nid);
+ }
+ }
+ return NULL;
+}
+
+EC_GROUP *EC_GROUP_new_from_params(const OSSL_PARAM params[],
+ OPENSSL_CTX *libctx, const char *propq)
+{
+ const OSSL_PARAM *ptmp, *pa, *pb;
+ int ok = 0;
+ EC_GROUP *group = NULL, *named_group = NULL;
+ BIGNUM *p = NULL, *a = NULL, *b = NULL, *order = NULL, *cofactor = NULL;
+ EC_POINT *point = NULL;
+ int field_bits = 0;
+ int is_prime_field = 1;
+ BN_CTX *bnctx = NULL;
+ const unsigned char *buf = NULL;
+ int encoding_flag = -1;
+
+ ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_ENCODING);
+ if (ptmp != NULL && !ec_encoding_param2id(ptmp, &encoding_flag)) {
+ ECerr(0, EC_R_INVALID_ENCODING);
+ return 0;
+ }
+
+ ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME);
+ if (ptmp != NULL) {
+ group = group_new_from_name(ptmp, libctx, propq);
+ if (group != NULL)
+ EC_GROUP_set_asn1_flag(group, encoding_flag);
+ else
+ ECerr(0, ERR_R_EC_LIB);
+ return group;
+ }
+ bnctx = BN_CTX_new_ex(libctx);
+ if (bnctx == NULL) {
+ ECerr(0, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ BN_CTX_start(bnctx);
+
+ p = BN_CTX_get(bnctx);
+ a = BN_CTX_get(bnctx);
+ b = BN_CTX_get(bnctx);
+ order = BN_CTX_get(bnctx);
+ if (order == NULL) {
+ ECerr(0, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_FIELD_TYPE);
+ if (ptmp == NULL || ptmp->data_type != OSSL_PARAM_UTF8_STRING) {
+ ECerr(0, EC_R_INVALID_FIELD);
+ goto err;
+ }
+ if (strcasecmp(ptmp->data, SN_X9_62_prime_field) == 0) {
+ is_prime_field = 1;
+ } else if (strcasecmp(ptmp->data, SN_X9_62_characteristic_two_field) == 0) {
+ is_prime_field = 0;
+ } else {
+ /* Invalid field */
+ ECerr(0, EC_R_UNSUPPORTED_FIELD);
+ goto err;
+ }
+
+ pa = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_A);
+ if (!OSSL_PARAM_get_BN(pa, &a)) {
+ ECerr(0, EC_R_INVALID_A);
+ goto err;
+ }
+ pb = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_B);
+ if (!OSSL_PARAM_get_BN(pb, &b)) {
+ ECerr(0, EC_R_INVALID_B);
+ goto err;
+ }
+
+ /* extract the prime number or irreducible polynomial */
+ ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_P);
+ if (!OSSL_PARAM_get_BN(ptmp, &p)) {
+ ECerr(0, EC_R_INVALID_P);
+ goto err;
+ }
+
+ if (is_prime_field) {
+ if (BN_is_negative(p) || BN_is_zero(p)) {
+ ECerr(0, EC_R_INVALID_P);
+ goto err;
+ }
+ field_bits = BN_num_bits(p);
+ if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS) {
+ ECerr(0, EC_R_FIELD_TOO_LARGE);
+ goto err;
+ }
+
+ /* create the EC_GROUP structure */
+ group = EC_GROUP_new_curve_GFp(p, a, b, bnctx);
+ } else {
+#ifdef OPENSSL_NO_EC2M
+ ECerr(0, EC_R_GF2M_NOT_SUPPORTED);
+ goto err;
+#else
+ /* create the EC_GROUP structure */
+ group = EC_GROUP_new_curve_GF2m(p, a, b, NULL);
+ if (group != NULL) {
+ field_bits = EC_GROUP_get_degree(group);
+ if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS) {
+ ECerr(0, EC_R_FIELD_TOO_LARGE);
+ goto err;
+ }
+ }
+#endif /* OPENSSL_NO_EC2M */
+ }
+
+ if (group == NULL) {
+ ECerr(0, ERR_R_EC_LIB);
+ goto err;
+ }
+
+ /* Optional seed */
+ ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_SEED);
+ if (ptmp != NULL) {
+ if (ptmp->data_type != OSSL_PARAM_OCTET_STRING) {
+ ECerr(0, EC_R_INVALID_SEED);
+ goto err;
+ }
+ if (!EC_GROUP_set_seed(group, ptmp->data, ptmp->data_size))
+ goto err;
+ }
+
+ /* generator base point */
+ ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_GENERATOR);
+ if (ptmp == NULL
+ || ptmp->data_type != OSSL_PARAM_OCTET_STRING) {
+ ECerr(0, EC_R_INVALID_GENERATOR);
+ goto err;
+ }
+ buf = (const unsigned char *)(ptmp->data);
+ if ((point = EC_POINT_new(group)) == NULL)
+ goto err;
+ EC_GROUP_set_point_conversion_form(group,
+ (point_conversion_form_t)buf[0] & ~0x01);
+ if (!EC_POINT_oct2point(group, point, buf, ptmp->data_size, bnctx)) {
+ ECerr(0, EC_R_INVALID_GENERATOR);
+ goto err;
+ }
+
+ /* order */
+ ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_ORDER);
+ if (!OSSL_PARAM_get_BN(ptmp, &order)
+ || (BN_is_negative(order) || BN_is_zero(order))
+ || (BN_num_bits(order) > (int)field_bits + 1)) { /* Hasse bound */
+ ECerr(0, EC_R_INVALID_GROUP_ORDER);
+ goto err;
+ }
+
+ /* Optional cofactor */
+ ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_COFACTOR);
+ if (ptmp != NULL) {
+ cofactor = BN_CTX_get(bnctx);
+ if (cofactor == NULL || !OSSL_PARAM_get_BN(ptmp, &cofactor)) {
+ ECerr(0, EC_R_INVALID_COFACTOR);
+ goto err;
+ }
+ }
+
+ /* set the generator, order and cofactor (if present) */
+ if (!EC_GROUP_set_generator(group, point, order, cofactor)) {
+ ECerr(0, EC_R_INVALID_GENERATOR);
+ goto err;
+ }
+
+ named_group = ec_group_explicit_to_named(group, libctx, propq, bnctx);
+ if (named_group == NULL) {
+ ECerr(0, EC_R_INVALID_NAMED_GROUP_CONVERSION);
+ goto err;
+ }
+ if (named_group == group) {
+ /*
+ * If we did not find a named group then the encoding should be explicit
+ * if it was specified
+ */
+ if (encoding_flag == OPENSSL_EC_NAMED_CURVE) {
+ ECerr(0, EC_R_INVALID_ENCODING);
+ goto err;
+ }
+ EC_GROUP_set_asn1_flag(group, OPENSSL_EC_EXPLICIT_CURVE);
+ } else {
+ EC_GROUP_free(group);
+ group = named_group;
+ }
+ ok = 1;
+ err:
+ if (!ok) {
+ EC_GROUP_free(group);
+ group = NULL;
+ }
+ EC_POINT_free(point);
+ BN_CTX_end(bnctx);
+ BN_CTX_free(bnctx);
+
+ return group;
+}