added code to validate EC named curve parameters
authorShane Lontis <shane.lontis@oracle.com>
Thu, 21 Mar 2019 10:09:02 +0000 (20:09 +1000)
committerNicola Tuveri <nic.tuv@gmail.com>
Thu, 11 Apr 2019 09:05:38 +0000 (12:05 +0300)
Reviewed-by: Nicola Tuveri <nic.tuv@gmail.com>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/8555)

apps/ecparam.c
crypto/ec/ec_check.c
crypto/ec/ec_curve.c
crypto/ec/ec_lcl.h
crypto/ec/ec_lib.c
crypto/ec/ec_mult.c
doc/man3/EC_GROUP_copy.pod
include/openssl/ec.h
test/ectest.c
test/recipes/15-test_ecparam.t
util/libcrypto.num

index 24fda04..0c893a3 100644 (file)
@@ -30,7 +30,7 @@ typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_TEXT, OPT_C,
     OPT_CHECK, OPT_LIST_CURVES, OPT_NO_SEED, OPT_NOOUT, OPT_NAME,
-    OPT_CONV_FORM, OPT_PARAM_ENC, OPT_GENKEY, OPT_ENGINE,
+    OPT_CONV_FORM, OPT_PARAM_ENC, OPT_GENKEY, OPT_ENGINE, OPT_CHECK_NAMED,
     OPT_R_ENUM
 } OPTION_CHOICE;
 
@@ -43,6 +43,8 @@ const OPTIONS ecparam_options[] = {
     {"text", OPT_TEXT, '-', "Print the ec parameters in text form"},
     {"C", OPT_C, '-', "Print a 'C' function creating the parameters"},
     {"check", OPT_CHECK, '-', "Validate the ec parameters"},
+    {"check_named", OPT_CHECK_NAMED, '-',
+     "Check that named EC curve parameters have not been modified"},
     {"list_curves", OPT_LIST_CURVES, '-',
      "Prints a list of all curve 'short names'"},
     {"no_seed", OPT_NO_SEED, '-',
@@ -90,7 +92,7 @@ int ecparam_main(int argc, char **argv)
     int informat = FORMAT_PEM, outformat = FORMAT_PEM, noout = 0, C = 0;
     int ret = 1, private = 0;
     int list_curves = 0, no_seed = 0, check = 0, new_form = 0;
-    int text = 0, i, genkey = 0;
+    int text = 0, i, genkey = 0, check_named = 0;
 
     prog = opt_init(argc, argv, ecparam_options);
     while ((o = opt_next()) != OPT_EOF) {
@@ -127,6 +129,9 @@ int ecparam_main(int argc, char **argv)
         case OPT_CHECK:
             check = 1;
             break;
+        case OPT_CHECK_NAMED:
+            check_named = 1;
+            break;
         case OPT_LIST_CURVES:
             list_curves = 1;
             break;
@@ -266,6 +271,16 @@ int ecparam_main(int argc, char **argv)
             goto end;
     }
 
+    if (check_named) {
+        BIO_printf(bio_err, "validating named elliptic curve parameters: ");
+        if (EC_GROUP_check_named_curve(group, 0) <= 0) {
+            BIO_printf(bio_err, "failed\n");
+            ERR_print_errors(bio_err);
+            goto end;
+        }
+        BIO_printf(bio_err, "ok\n");
+    }
+
     if (check) {
         BIO_printf(bio_err, "checking elliptic curve parameters: ");
         if (!EC_GROUP_check(group, NULL)) {
index 322d0fe..09e3deb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2002-2019 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
 #include "ec_lcl.h"
 #include <openssl/err.h>
 
+int EC_GROUP_check_named_curve(const EC_GROUP *group, int nist_only)
+{
+    int nid;
+
+    nid = ec_curve_nid_from_params(group);
+    if (nid > 0 && nist_only && EC_curve_nid2nist(nid) == NULL)
+        nid = 0;
+    return nid;
+}
+
 int EC_GROUP_check(const EC_GROUP *group, BN_CTX *ctx)
 {
     int ret = 0;
@@ -17,6 +27,11 @@ int EC_GROUP_check(const EC_GROUP *group, BN_CTX *ctx)
     BN_CTX *new_ctx = NULL;
     EC_POINT *point = NULL;
 
+    if (group == NULL || group->meth == NULL) {
+        ECerr(EC_F_EC_GROUP_CHECK, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
     /* Custom curves assumed to be correct */
     if ((group->meth->flags & EC_FLAGS_CUSTOM_CURVE) != 0)
         return 1;
index 641cf3f..e7a3bd8 100644 (file)
@@ -14,6 +14,7 @@
 #include <openssl/obj_mac.h>
 #include <openssl/opensslconf.h>
 #include "internal/nelem.h"
+#include "internal/o_str.h"
 
 typedef struct {
     int field_type,             /* either NID_X9_62_prime_field or
@@ -1136,6 +1137,7 @@ static const struct {
     },
     {
         /* no seed */
+        /* p */
         0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
@@ -1209,6 +1211,7 @@ static const struct {
     },
     {
         /* no seed */
+        /* p */
         0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
@@ -1244,6 +1247,7 @@ static const struct {
     },
     {
         /* no seed */
+        /* p */
         0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xA1,
@@ -1278,7 +1282,7 @@ static const struct {
         NID_X9_62_characteristic_two_field, 20, 36, 2
     },
     {
-        /* no seed */
+        /* seed */
         0x77, 0xE2, 0xB0, 0x73, 0x70, 0xEB, 0x0F, 0x83, 0x2A, 0x6D, 0xD5, 0xB6,
         0x2D, 0xFC, 0x88, 0xCD, 0x06, 0xBB, 0x84, 0xBE,
         /* p */
@@ -3197,3 +3201,120 @@ int EC_curve_nist2nid(const char *name)
     }
     return NID_undef;
 }
+
+#define NUM_BN_FIELDS 6
+/*
+ * Validates EC domain parameter data for known named curves.
+ * This can be used when a curve is loaded explicitly (without a curve
+ * name) or to validate that domain parameters have not been modified.
+ *
+ * Returns: The nid associated with the found named curve, or NID_undef
+ *          if not found. If there was an error it returns -1.
+ */
+int ec_curve_nid_from_params(const EC_GROUP *group)
+{
+    int ret = -1, nid, len, field_type, param_len;
+    size_t i, seed_len;
+    const unsigned char *seed, *params_seed, *params;
+    unsigned char *param_bytes = NULL;
+    const EC_CURVE_DATA *data;
+    const EC_POINT *generator = NULL;
+    const EC_METHOD *meth;
+    const BIGNUM *cofactor = NULL;
+    /* An array of BIGNUMs for (p, a, b, x, y, order) */
+    BIGNUM *bn[NUM_BN_FIELDS] = {NULL, NULL, NULL, NULL, NULL, NULL};
+    BN_CTX *ctx = NULL;
+
+    meth = EC_GROUP_method_of(group);
+    if (meth == NULL)
+        return -1;
+    /* Use the optional named curve nid as a search field */
+    nid = EC_GROUP_get_curve_name(group);
+    field_type = EC_METHOD_get_field_type(meth);
+    seed_len = EC_GROUP_get_seed_len(group);
+    seed = EC_GROUP_get0_seed(group);
+    cofactor = EC_GROUP_get0_cofactor(group);
+
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        return -1;
+    BN_CTX_start(ctx);
+
+    /*
+     * The in built curves contains data fields (p, a, b, x, y, order) that are
+     * all zero padded to be the same size. The size of the padding is
+     * determined by either the number of bytes in the field modulus (p) or the
+     * EC group order, whichever is larger.
+     */
+    param_len = BN_num_bytes(group->order);
+    len = BN_num_bytes(group->field);
+    if (len > param_len)
+        param_len = len;
+
+    /* Allocate space to store the padded data for (p, a, b, x, y, order)  */
+    param_bytes = OPENSSL_malloc(param_len * NUM_BN_FIELDS);
+    if (param_bytes == NULL)
+        goto end;
+
+    /* Create the bignums */
+    for (i = 0; i < NUM_BN_FIELDS; ++i) {
+        if ((bn[i] = BN_CTX_get(ctx)) == NULL)
+            goto end;
+    }
+    /*
+     * Fill in the bn array with the same values as the internal curves
+     * i.e. the values are p, a, b, x, y, order.
+     */
+    /* Get p, a & b */
+    if (!(EC_GROUP_get_curve(group, bn[0], bn[1], bn[2], ctx)
+        && ((generator = EC_GROUP_get0_generator(group)) != NULL)
+        /* Get x & y */
+        && EC_POINT_get_affine_coordinates(group, generator, bn[3], bn[4], ctx)
+        /* Get order */
+        && EC_GROUP_get_order(group, bn[5], ctx)))
+        goto end;
+
+   /*
+     * Convert the bignum array to bytes that are joined together to form
+     * a single buffer that contains data for all fields.
+     * (p, a, b, x, y, order) are all zero padded to be the same size.
+     */
+    for (i = 0; i < NUM_BN_FIELDS; ++i) {
+        if (BN_bn2binpad(bn[i], &param_bytes[i*param_len], param_len) <= 0)
+            goto end;
+    }
+
+    for (i = 0; i < curve_list_length; i++) {
+        const ec_list_element curve = curve_list[i];
+
+        data = curve.data;
+        /* Get the raw order byte data */
+        params_seed = (const unsigned char *)(data + 1); /* skip header */
+        params = params_seed + data->seed_len;
+
+        /* Look for unique fields in the fixed curve data */
+        if (data->field_type == field_type
+            && param_len == data->param_len
+            && (nid <= 0 || nid == curve.nid)
+            /* check the optional cofactor (ignore if its zero) */
+            && (BN_is_zero(cofactor)
+                || BN_is_word(cofactor, (const BN_ULONG)curve.data->cofactor))
+            /* Check the optional seed (ignore if its not set) */
+            && (data->seed_len == 0 || seed_len == 0
+                || ((size_t)data->seed_len == seed_len
+                     && OPENSSL_memcmp(params_seed, seed, seed_len) == 0))
+            /* Check that the groups params match the inbuilt curve params */
+            && OPENSSL_memcmp(param_bytes, params, param_len * NUM_BN_FIELDS)
+                              == 0) {
+            ret = curve.nid;
+            goto end;
+        }
+    }
+    /* Gets here if the group was not found */
+    ret = NID_undef;
+end:
+    OPENSSL_free(param_bytes);
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    return ret;
+}
index 6b90ef3..c54789b 100644 (file)
@@ -309,13 +309,10 @@ struct ec_point_st {
 static ossl_inline int ec_point_is_compat(const EC_POINT *point,
                                           const EC_GROUP *group)
 {
-    if (group->meth != point->meth
-        || (group->curve_name != 0
-            && point->curve_name != 0
-            && group->curve_name != point->curve_name))
-        return 0;
-
-    return 1;
+    return group->meth == point->meth
+           && (group->curve_name == 0
+               || point->curve_name == 0
+               || group->curve_name == point->curve_name);
 }
 
 NISTP224_PRE_COMP *EC_nistp224_pre_comp_dup(NISTP224_PRE_COMP *);
@@ -595,6 +592,8 @@ int ec_key_simple_generate_key(EC_KEY *eckey);
 int ec_key_simple_generate_public_key(EC_KEY *eckey);
 int ec_key_simple_check_key(const EC_KEY *eckey);
 
+int ec_curve_nid_from_params(const EC_GROUP *group);
+
 /* EC_METHOD definitions */
 
 struct ec_key_method_st {
index 798382a..b2ae11d 100644 (file)
@@ -284,15 +284,17 @@ int EC_GROUP_set_generator(EC_GROUP *group, const EC_POINT *generator,
     if (order != NULL) {
         if (!BN_copy(group->order, order))
             return 0;
-    } else
+    } else {
         BN_zero(group->order);
+    }
 
+    /* The cofactor is an optional field, so it should be able to be NULL. */
     if (cofactor != NULL) {
         if (!BN_copy(group->cofactor, cofactor))
             return 0;
-    } else
+    } else {
         BN_zero(group->cofactor);
-
+    }
     /*
      * Some groups have an order with
      * factors of two, which makes the Montgomery setup fail.
@@ -530,30 +532,42 @@ int EC_GROUP_cmp(const EC_GROUP *a, const EC_GROUP *b, BN_CTX *ctx)
         !b->meth->group_get_curve(b, b1, b2, b3, ctx))
         r = 1;
 
-    if (r || BN_cmp(a1, b1) || BN_cmp(a2, b2) || BN_cmp(a3, b3))
+    /* return 1 if the curve parameters are different */
+    if (r || BN_cmp(a1, b1) != 0 || BN_cmp(a2, b2) != 0 || BN_cmp(a3, b3) != 0)
         r = 1;
 
-    /* XXX EC_POINT_cmp() assumes that the methods are equal */
+    /* return 1 if the generators are different */
     if (r || EC_POINT_cmp(a, EC_GROUP_get0_generator(a),
-                          EC_GROUP_get0_generator(b), ctx))
+                          EC_GROUP_get0_generator(b), ctx) != 0)
         r = 1;
 
     if (!r) {
         const BIGNUM *ao, *bo, *ac, *bc;
-        /* compare the order and cofactor */
+        /* compare the order's */
         ao = EC_GROUP_get0_order(a);
         bo = EC_GROUP_get0_order(b);
-        ac = EC_GROUP_get0_cofactor(a);
-        bc = EC_GROUP_get0_cofactor(b);
         if (ao == NULL || bo == NULL) {
-            BN_CTX_end(ctx);
-            BN_CTX_free(ctx_new);
-            return -1;
+            /* return an error if either order is NULL */
+            r = -1;
+            goto end;
+        }
+        if (BN_cmp(ao, bo) != 0) {
+            /* return 1 if orders are different */
+            r = 1;
+            goto end;
         }
-        if (BN_cmp(ao, bo) || BN_cmp(ac, bc))
+        /*
+         * It gets here if the curve parameters and generator matched.
+         * Now check the optional cofactors (if both are present).
+         */
+        ac = EC_GROUP_get0_cofactor(a);
+        bc = EC_GROUP_get0_cofactor(b);
+        /* Returns 1 (mismatch) if both cofactors are specified and different */
+        if (!BN_is_zero(ac) && !BN_is_zero(bc) && BN_cmp(ac, bc) != 0)
             r = 1;
+        /* Returns 0 if the parameters matched */
     }
-
+end:
     BN_CTX_end(ctx);
     BN_CTX_free(ctx_new);
 
@@ -622,8 +636,8 @@ int EC_POINT_copy(EC_POINT *dest, const EC_POINT *src)
     }
     if (dest->meth != src->meth
             || (dest->curve_name != src->curve_name
-                && dest->curve_name != 0
-                && src->curve_name != 0)) {
+                 && dest->curve_name != 0
+                 && src->curve_name != 0)) {
         ECerr(EC_F_EC_POINT_COPY, EC_R_INCOMPATIBLE_OBJECTS);
         return 0;
     }
index 755d644..76dc524 100644 (file)
@@ -156,7 +156,7 @@ int ec_scalar_mul_ladder(const EC_GROUP *group, EC_POINT *r,
         ECerr(EC_F_EC_SCALAR_MUL_LADDER, EC_R_UNKNOWN_ORDER);
         return 0;
     }
-    if (BN_is_zero(group->cofactor)) {
+    if (BN_is_zero(group->cofactor) || BN_is_zero(group->cofactor)) {
         ECerr(EC_F_EC_SCALAR_MUL_LADDER, EC_R_UNKNOWN_COFACTOR);
         return 0;
     }
index 3f7108d..c25c8f0 100644 (file)
@@ -9,7 +9,8 @@ EC_GROUP_set_curve_name, EC_GROUP_get_curve_name, EC_GROUP_set_asn1_flag,
 EC_GROUP_get_asn1_flag, EC_GROUP_set_point_conversion_form,
 EC_GROUP_get_point_conversion_form, EC_GROUP_get0_seed,
 EC_GROUP_get_seed_len, EC_GROUP_set_seed, EC_GROUP_get_degree,
-EC_GROUP_check, EC_GROUP_check_discriminant, EC_GROUP_cmp,
+EC_GROUP_check, EC_GROUP_check_named_curve,
+EC_GROUP_check_discriminant, EC_GROUP_cmp,
 EC_GROUP_get_basis_type, EC_GROUP_get_trinomial_basis,
 EC_GROUP_get_pentanomial_basis, EC_GROUP_get0_field
 - Functions for manipulating EC_GROUP objects
@@ -50,6 +51,7 @@ EC_GROUP_get_pentanomial_basis, EC_GROUP_get0_field
  int EC_GROUP_get_degree(const EC_GROUP *group);
 
  int EC_GROUP_check(const EC_GROUP *group, BN_CTX *ctx);
+ int EC_GROUP_check_named_curve(const EC_GROUP *group, int nist_only);
 
  int EC_GROUP_check_discriminant(const EC_GROUP *group, BN_CTX *ctx);
 
@@ -143,6 +145,14 @@ The function EC_GROUP_check performs a number of checks on a curve to verify tha
 verifying that the discriminant is non zero; that a generator has been defined; that the generator is on the curve and has
 the correct order.
 
+EC_GROUP_check_named_curve determines if the group's domain parameters match one of the built in curves supported by the library.
+The curve name is returned as a B<NID> if it matches. If the group's domain parameters have been modified then no match will be found.
+If the curve name of the given group is B<NID_undef> (e.g. it has been created by using explicit parameters with no curve name),
+then this method can be used to lookup the name of the curve that matches the group domain parameters. The built in curves contain
+aliases, so that multiple NID's can map to the same domain parameters. For such curves it is unspecified which of the aliases will be
+returned if the curve name of the given group is NID_undef.
+If B<nist_only> is 1 it will only look for NIST approved curves, otherwise it searches all built in curves.
+
 EC_GROUP_cmp compares B<a> and B<b> to determine whether they represent the same curve or not.
 
 The functions EC_GROUP_get_basis_type, EC_GROUP_get_trinomial_basis and EC_GROUP_get_pentanomial_basis should only be called for curves
@@ -175,6 +185,8 @@ EC_GROUP_get_order, EC_GROUP_get_cofactor, EC_GROUP_get_curve_name, EC_GROUP_get
 and EC_GROUP_get_degree return the order, cofactor, curve name (NID), ASN1 flag, point_conversion_form and degree for the
 specified curve respectively. If there is no curve name associated with a curve then EC_GROUP_get_curve_name will return 0.
 
+EC_GROUP_check_named_curve returns the nid of the matching named curve, otherwise it returns 0 for no match, or -1 on error.
+
 EC_GROUP_get0_order() returns an internal pointer to the group order.
 EC_GROUP_order_bits() returns the number of bits in the group order.
 EC_GROUP_get0_cofactor() returns an internal pointer to the group cofactor.
@@ -198,6 +210,10 @@ L<crypto(7)>, L<EC_GROUP_new(3)>,
 L<EC_POINT_new(3)>, L<EC_POINT_add(3)>, L<EC_KEY_new(3)>,
 L<EC_GFp_simple_method(3)>, L<d2i_ECPKParameters(3)>
 
+=head1 HISTORY
+
+The EC_GROUP_check_named_curve() function was added in OpenSSL 3.0.
+
 =head1 COPYRIGHT
 
 Copyright 2013-2017 The OpenSSL Project Authors. All Rights Reserved.
index 8d4d1b1..af559cb 100644 (file)
@@ -422,6 +422,7 @@ size_t EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems);
 
 const char *EC_curve_nid2nist(int nid);
 int EC_curve_nist2nid(const char *name);
+int EC_GROUP_check_named_curve(const EC_GROUP *group, int nist_only);
 
 /********************************************************************/
 /*                    EC_POINT functions                            */
index 59c7e99..56c6b6f 100644 (file)
@@ -8,6 +8,7 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include <string.h>
 #include "internal/nelem.h"
 #include "testutil.h"
 
@@ -1556,6 +1557,182 @@ static const unsigned char p521_explicit[] = {
     0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38, 0x64, 0x09, 0x02, 0x01, 0x01,
 };
 
+static int check_named_curve(int id)
+{
+    int ret = 0, nid, field_nid, has_seed, rv = 0;
+    EC_GROUP *group = NULL, *gtest = NULL, *galias = NULL;
+    const EC_POINT *group_gen = NULL;
+    EC_POINT *other_gen = NULL;
+    BIGNUM *group_p = NULL, *group_a = NULL, *group_b = NULL;
+    BIGNUM *other_p = NULL, *other_a = NULL, *other_b = NULL;
+    BIGNUM *group_cofactor = NULL, *other_cofactor = NULL;
+    BIGNUM *other_order = NULL;
+    const BIGNUM *group_order = NULL;
+    BN_CTX *bn_ctx = NULL;
+    static const unsigned char invalid_seed[] = "THIS IS NOT A VALID SEED";
+    static size_t invalid_seed_len = sizeof(invalid_seed);
+
+    /* Do some setup */
+    nid = curves[id].nid;
+    if (!TEST_ptr(bn_ctx = BN_CTX_new())
+        || !TEST_ptr(group = EC_GROUP_new_by_curve_name(nid))
+        || !TEST_ptr(gtest = EC_GROUP_dup(group))
+        || !TEST_ptr(group_p = BN_new())
+        || !TEST_ptr(group_a = BN_new())
+        || !TEST_ptr(group_b = BN_new())
+        || !TEST_ptr(group_cofactor = BN_new())
+        || !TEST_ptr(group_gen = EC_GROUP_get0_generator(group))
+        || !TEST_ptr(group_order = EC_GROUP_get0_order(group))
+        || !TEST_true(EC_GROUP_get_cofactor(group, group_cofactor, NULL))
+        || !TEST_true(EC_GROUP_get_curve(group, group_p, group_a, group_b, NULL))
+        || !TEST_ptr(other_gen = EC_POINT_dup(group_gen, group))
+        || !TEST_true(EC_POINT_add(group, other_gen, group_gen, group_gen, NULL))
+        || !TEST_ptr(other_order = BN_dup(group_order))
+        || !TEST_true(BN_add_word(other_order, 1))
+        || !TEST_ptr(other_a = BN_dup(group_a))
+        || !TEST_true(BN_add_word(other_a, 1))
+        || !TEST_ptr(other_b = BN_dup(group_b))
+        || !TEST_true(BN_add_word(other_b, 1))
+        || !TEST_ptr(other_cofactor = BN_dup(group_cofactor))
+        || !TEST_true(BN_add_word(other_cofactor, 1)))
+        goto err;
+
+    /* Determine if the inbuilt curve has a seed field set */
+    has_seed = (EC_GROUP_get_seed_len(group) > 0);
+    field_nid = EC_METHOD_get_field_type(EC_GROUP_method_of(group));
+    if (field_nid == NID_X9_62_characteristic_two_field) {
+        if (!TEST_ptr(other_p = BN_dup(group_p))
+            || !TEST_true(BN_lshift1(other_p, other_p)))
+            goto err;
+    } else {
+        if (!TEST_ptr(other_p = BN_dup(group_p)))
+            goto err;
+        /*
+         * Just choosing any arbitrary prime does not work..
+         * Setting p via ec_GFp_nist_group_set_curve() needs the prime to be a
+         * nist prime. So only select one of these as an alternate prime.
+         */
+        if (!TEST_ptr(BN_copy(other_p,
+                              BN_ucmp(BN_get0_nist_prime_192(), other_p) == 0 ?
+                                      BN_get0_nist_prime_256() :
+                                      BN_get0_nist_prime_192())))
+            goto err;
+    }
+
+    /* Passes because this is a valid curve */
+    if (!TEST_int_eq(EC_GROUP_check_named_curve(group, 0), nid)
+        /* Only NIST curves pass */
+       || !TEST_int_eq(EC_GROUP_check_named_curve(group, 1),
+                        EC_curve_nid2nist(nid) != NULL ? nid : NID_undef))
+        goto err;
+    /*
+     * Pass if the named curve is not known but the parameters are correct.
+     * It is possible to find the wrong alias if there is no curve name.
+     */
+    EC_GROUP_set_curve_name(group, NID_undef);
+    if (!TEST_int_gt(rv = EC_GROUP_check_named_curve(group, 0), 0))
+        goto err;
+#if 0
+    /* This code does not currently work since aliases are not supported
+     * currently.
+     */
+    /* Found an alias */
+    if (rv != nid) {
+        /* Fail if the returned nid is not an alias of the original group */
+        if (!TEST_ptr(galias = EC_GROUP_new_by_curve_name(rv)))
+            goto err;
+        EC_GROUP_set_curve_name(galias, nid);
+        if (!TEST_int_eq(EC_GROUP_check_named_curve(galias, 0), nid))
+            goto err;
+    }
+#endif
+    /* Fail if the curve name doesnt match the parameters */
+    EC_GROUP_set_curve_name(group, nid + 1);
+    if (!TEST_int_le(EC_GROUP_check_named_curve(group, 0), 0))
+        goto err;
+    EC_GROUP_set_curve_name(group, nid);
+
+    if (!TEST_int_eq(EC_GROUP_set_seed(group, invalid_seed, invalid_seed_len),
+                     invalid_seed_len))
+        goto err;
+
+    if (has_seed) {
+        /*
+         * If the built in curve has a seed and we set the seed to another value
+         * then it will fail the check.
+         */
+        if (!TEST_int_eq(EC_GROUP_check_named_curve(group, 0), 0))
+            goto err;
+    } else {
+        /*
+         * If the built in curve does not have a seed then setting the seed will
+         * pass the check (as the seed is optional).
+         */
+        if (!TEST_int_eq(EC_GROUP_check_named_curve(group, 0), nid))
+            goto err;
+    }
+    /* Pass if the seed is unknown (as it is optional) */
+    if (!TEST_int_eq(EC_GROUP_set_seed(group, NULL, 0), 1)
+        || !TEST_int_gt(EC_GROUP_check_named_curve(group, 0), 0))
+        goto err;
+
+    /* Check that a duped group passes */
+    if (!TEST_int_eq(EC_GROUP_check_named_curve(gtest, 0), nid))
+        goto err;
+
+    /* check that changing any generator parameters fail */
+    if (!TEST_true(EC_GROUP_set_generator(gtest, other_gen, group_order,
+                                          group_cofactor))
+        || !TEST_int_eq(EC_GROUP_check_named_curve(gtest, 0), 0)
+        || !TEST_true(EC_GROUP_set_generator(gtest, group_gen, other_order,
+                                             group_cofactor))
+        || !TEST_int_eq(EC_GROUP_check_named_curve(gtest, 0), 0)
+        /* The order is not an optional field, so this should fail */
+        || !TEST_true(EC_GROUP_set_generator(gtest, group_gen, NULL,
+                                             group_cofactor))
+        || !TEST_int_le(EC_GROUP_check_named_curve(gtest, 0), 0)
+        || !TEST_true(EC_GROUP_set_generator(gtest, group_gen, group_order,
+                                             other_cofactor))
+        || !TEST_int_eq(EC_GROUP_check_named_curve(gtest, 0), 0)
+        /* Check that if the cofactor is not set then it still passes */
+        || !TEST_true(EC_GROUP_set_generator(gtest, group_gen, group_order,
+                                             NULL))
+        || !TEST_int_eq(EC_GROUP_check_named_curve(gtest, 0), nid)
+        /* check that restoring the generator passes */
+        || !TEST_true(EC_GROUP_set_generator(gtest, group_gen, group_order,
+                                             group_cofactor))
+        || !TEST_int_eq(EC_GROUP_check_named_curve(gtest, 0), nid)
+        /* check that changing any curve parameters fail */
+        || !TEST_true(EC_GROUP_set_curve(gtest, other_p, group_a, group_b, NULL))
+        || !TEST_int_le(EC_GROUP_check_named_curve(gtest, 0), 0)
+        || !TEST_true(EC_GROUP_set_curve(gtest, group_p, other_a, group_b, NULL))
+        || !TEST_int_eq(EC_GROUP_check_named_curve(gtest, 0), 0)
+        || !TEST_true(EC_GROUP_set_curve(gtest, group_p, group_a, other_b, NULL))
+        || !TEST_int_eq(EC_GROUP_check_named_curve(gtest, 0), 0)
+        /* Check that restoring the curve parameters pass */
+        || !TEST_true(EC_GROUP_set_curve(gtest, group_p, group_a, group_b, NULL))
+        || !TEST_int_eq(EC_GROUP_check_named_curve(gtest, 0), nid))
+        goto err;
+
+    ret = 1;
+err:
+    BN_free(group_p);
+    BN_free(other_p);
+    BN_free(group_a);
+    BN_free(other_a);
+    BN_free(group_b);
+    BN_free(other_b);
+    BN_free(group_cofactor);
+    BN_free(other_cofactor);
+    BN_free(other_order);
+    EC_POINT_free(other_gen);
+    EC_GROUP_free(galias);
+    EC_GROUP_free(gtest);
+    EC_GROUP_free(group);
+    BN_CTX_free(bn_ctx);
+    return ret;
+}
+
 static int parameter_test(void)
 {
     EC_GROUP *group = NULL, *group2 = NULL;
@@ -1621,6 +1798,7 @@ int setup_tests(void)
     ADD_ALL_TESTS(internal_curve_test, crv_len);
     ADD_ALL_TESTS(internal_curve_test_method, crv_len);
     ADD_TEST(group_field_test);
+    ADD_ALL_TESTS(check_named_curve, crv_len);
 #endif
     return 1;
 }
index 1d0b59c..ee14775 100644 (file)
@@ -23,12 +23,20 @@ plan skip_all => "EC isn't supported in this build"
 my @valid = glob(data_file("valid", "*.pem"));
 my @invalid = glob(data_file("invalid", "*.pem"));
 
-plan tests => scalar @valid + scalar @invalid;
+plan tests => scalar @valid + scalar @invalid + scalar @valid + scalar @invalid;
 
 foreach (@valid) {
     ok(run(app([qw{openssl ecparam -noout -check -in}, $_])));
 }
 
+foreach (@valid) {
+    ok(run(app([qw{openssl ecparam -noout -check_named -in}, $_])));
+}
+
 foreach (@invalid) {
     ok(!run(app([qw{openssl ecparam -noout -check -in}, $_])));
 }
+
+foreach (@invalid) {
+    ok(!run(app([qw{openssl ecparam -noout -check_named -in}, $_])));
+}
index 9569bf4..3704a63 100644 (file)
@@ -4795,3 +4795,4 @@ EVP_MD_upref                            4742      3_0_0   EXIST::FUNCTION:
 EVP_MD_fetch                            4743   3_0_0   EXIST::FUNCTION:
 EVP_set_default_properties              4744   3_0_0   EXIST::FUNCTION:
 OSSL_PARAM_construct_end                4745   3_0_0   EXIST::FUNCTION:
+EC_GROUP_check_named_curve              4746   3_0_0   EXIST::FUNCTION:EC