]> git.ipfire.org Git - thirdparty/openssl.git/blobdiff - crypto/ec/ec_lib.c
Rename OPENSSL_CTX prefix to OSSL_LIB_CTX
[thirdparty/openssl.git] / crypto / ec / ec_lib.c
index 1b2ddc2b4437632a49caec8ebb3ce365d751e801..d1d403e175ed02cb1122382ae59caad679a668ac 100644 (file)
 #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 */
 
-EC_GROUP *EC_GROUP_new_ex(OPENSSL_CTX *libctx, const EC_METHOD *meth)
+EC_GROUP *ec_group_new_ex(OSSL_LIB_CTX *libctx, const char *propq,
+                          const EC_METHOD *meth)
 {
     EC_GROUP *ret;
 
     if (meth == NULL) {
-        ECerr(EC_F_EC_GROUP_NEW_EX, EC_R_SLOT_FULL);
+        ECerr(0, EC_R_SLOT_FULL);
         return NULL;
     }
     if (meth->group_init == 0) {
-        ECerr(EC_F_EC_GROUP_NEW_EX, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        ECerr(0, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return NULL;
     }
 
     ret = OPENSSL_zalloc(sizeof(*ret));
     if (ret == NULL) {
-        ECerr(EC_F_EC_GROUP_NEW_EX, ERR_R_MALLOC_FAILURE);
+        ECerr(0, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
 
     ret->libctx = libctx;
+    if (propq != NULL) {
+        ret->propq = OPENSSL_strdup(propq);
+        if (ret->propq == NULL) {
+            ECerr(0, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
     ret->meth = meth;
     if ((ret->meth->flags & EC_FLAGS_CUSTOM_CURVE) == 0) {
         ret->order = BN_new();
@@ -61,15 +72,18 @@ EC_GROUP *EC_GROUP_new_ex(OPENSSL_CTX *libctx, const EC_METHOD *meth)
  err:
     BN_free(ret->order);
     BN_free(ret->cofactor);
+    OPENSSL_free(ret->propq);
     OPENSSL_free(ret);
     return NULL;
 }
 
-#ifndef FIPS_MODULE
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+# ifndef FIPS_MODULE
 EC_GROUP *EC_GROUP_new(const EC_METHOD *meth)
 {
-    return EC_GROUP_new_ex(NULL, meth);
+    return ec_group_new_ex(NULL, NULL, meth);
 }
+# endif
 #endif
 
 void EC_pre_comp_free(EC_GROUP *group)
@@ -119,6 +133,7 @@ void EC_GROUP_free(EC_GROUP *group)
     BN_free(group->order);
     BN_free(group->cofactor);
     OPENSSL_free(group->seed);
+    OPENSSL_free(group->propq);
     OPENSSL_free(group);
 }
 
@@ -228,6 +243,7 @@ int EC_GROUP_copy(EC_GROUP *dest, const EC_GROUP *src)
 
     dest->asn1_flag = src->asn1_flag;
     dest->asn1_form = src->asn1_form;
+    dest->decoded_from_explicit_params = src->decoded_from_explicit_params;
 
     if (src->seed) {
         OPENSSL_free(dest->seed);
@@ -255,7 +271,7 @@ EC_GROUP *EC_GROUP_dup(const EC_GROUP *a)
     if (a == NULL)
         return NULL;
 
-    if ((t = EC_GROUP_new_ex(a->libctx, a->meth)) == NULL)
+    if ((t = ec_group_new_ex(a->libctx, a->propq, a->meth)) == NULL)
         return NULL;
     if (!EC_GROUP_copy(t, a))
         goto err;
@@ -270,6 +286,7 @@ EC_GROUP *EC_GROUP_dup(const EC_GROUP *a)
         return t;
 }
 
+#ifndef OPENSSL_NO_DEPRECATED_3_0
 const EC_METHOD *EC_GROUP_method_of(const EC_GROUP *group)
 {
     return group->meth;
@@ -279,6 +296,7 @@ int EC_METHOD_get_field_type(const EC_METHOD *meth)
 {
     return meth->field_type;
 }
+#endif
 
 static int ec_precompute_mont_data(EC_GROUP *);
 
@@ -475,6 +493,11 @@ const BIGNUM *EC_GROUP_get0_field(const EC_GROUP *group)
     return group->field;
 }
 
+int EC_GROUP_get_field_type(const EC_GROUP *group)
+{
+    return group->meth->field_type;
+}
+
 void EC_GROUP_set_asn1_flag(EC_GROUP *group, int flag)
 {
     group->asn1_flag = flag;
@@ -602,8 +625,7 @@ int EC_GROUP_cmp(const EC_GROUP *a, const EC_GROUP *b, BN_CTX *ctx)
 #endif
 
     /* compare the field types */
-    if (EC_METHOD_get_field_type(EC_GROUP_method_of(a)) !=
-        EC_METHOD_get_field_type(EC_GROUP_method_of(b)))
+    if (EC_GROUP_get_field_type(a) != EC_GROUP_get_field_type(b))
         return 1;
     /* compare the curve name (if present in both) */
     if (EC_GROUP_get_curve_name(a) && EC_GROUP_get_curve_name(b) &&
@@ -777,10 +799,12 @@ EC_POINT *EC_POINT_dup(const EC_POINT *a, const EC_GROUP *group)
     return t;
 }
 
+#ifndef OPENSSL_NO_DEPRECATED_3_0
 const EC_METHOD *EC_POINT_method_of(const EC_POINT *point)
 {
     return point->meth;
 }
+#endif
 
 int EC_POINT_set_to_infinity(const EC_GROUP *group, EC_POINT *point)
 {
@@ -1297,3 +1321,404 @@ 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,
+                                            OSSL_LIB_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_ex(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,
+                                     OSSL_LIB_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_ex(libctx, propq, nid);
+        }
+    }
+    return NULL;
+}
+
+EC_GROUP *EC_GROUP_new_from_params(const OSSL_PARAM params[],
+                                   OSSL_LIB_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);
+        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;
+}