[openssl] master update

nic.tuv at gmail.com nic.tuv at gmail.com
Thu Apr 11 09:18:46 UTC 2019


The branch master has been updated
       via  37f03b9881a4ffa52b0059ae444be3c416cf2a5f (commit)
       via  ac2b52c6ad0cd40482b1c5c1c4ec68eb16020ae8 (commit)
       via  8d4f150f70d70d6c3e62661ed7cc16c2f751d8a1 (commit)
       via  8402cd5f75f8c2f60d8bd39775b24b03dd8b3b38 (commit)
      from  4660bdea07e185b96c3b91be3e3b0a38959626ac (commit)


- Log -----------------------------------------------------------------
commit 37f03b9881a4ffa52b0059ae444be3c416cf2a5f
Author: Shane Lontis <shane.lontis at oracle.com>
Date:   Tue Apr 2 10:55:00 2019 +1000

    doc fixups
    
    Reviewed-by: Nicola Tuveri <nic.tuv at gmail.com>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8555)

commit ac2b52c6ad0cd40482b1c5c1c4ec68eb16020ae8
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Sun Mar 31 18:46:53 2019 +0300

    Separate the lookup test
    
    This fixes the "verifying the alias" case.
    Actually, while working on it, I realized that conceptually we were
    testing the 2 different behaviours of `EC_GROUP_check_named_curve()` at
    the same time, and actually not in the proper way.
    
    I think it's fair to assume that overwriting the curve name for an
    existing group with `NID_undef` could lead to the unexpected behaviour
    we were observing and working around.
    Thus I decided to separate the lookup test in a dedicated simpler test
    that does what the documentation of `EC_GROUP_check_named_curve()`
    suggests: the lookup functionality is meant to find a name for a group
    generated with explicit parameters.
    
    In case an alternative alias is returned by the lookup instead of the
    expected nid, to avoid doing comparisons between `EC_GROUP`s with
    different `EC_METHOD`s, the workaround is to retrieve the `ECPARAMETERS`
    of the "alias group" and create a new explicit parameters group to use
    in `EC_GROUP_cmp()`.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8555)

commit 8d4f150f70d70d6c3e62661ed7cc16c2f751d8a1
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Sun Mar 31 16:26:33 2019 +0300

    EC_GROUP_set_curve() might fail for arbitrary params
    
    Setting arbitrary `p`, `a` or `b` with `EC_GROUP_set_curve()` might fail
    for some `EC_GROUP`s, depending on the internal `EC_METHOD`
    implementation, hence the block of tests verifying that
    `EC_GROUP_check_named_curve()` fails when any of the curve parameters is
    changed is modified to run only if the previous `EC_GROUP_set_curve()`
    call succeeds.
    
    `ERR_set_mark()` and `ERR_pop_to_mark()` are used to avoid littering the
    thread error stack with unrelated errors happened during
    `EC_GROUP_set_curve()`.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8555)

commit 8402cd5f75f8c2f60d8bd39775b24b03dd8b3b38
Author: Shane Lontis <shane.lontis at oracle.com>
Date:   Thu Mar 21 20:09:02 2019 +1000

    added code to validate EC named curve parameters
    
    Reviewed-by: Nicola Tuveri <nic.tuv at gmail.com>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8555)

-----------------------------------------------------------------------

Summary of changes:
 apps/ecparam.c                 |  19 ++-
 crypto/ec/ec_check.c           |  17 ++-
 crypto/ec/ec_curve.c           | 123 ++++++++++++++++++-
 crypto/ec/ec_lcl.h             |  13 +--
 crypto/ec/ec_lib.c             |  45 ++++---
 doc/man1/ecparam.pod           |   6 +
 doc/man3/EC_GROUP_copy.pod     |  22 +++-
 include/openssl/ec.h           |   1 +
 test/ectest.c                  | 259 +++++++++++++++++++++++++++++++++++++++++
 test/recipes/15-test_ecparam.t |  10 +-
 util/libcrypto.num             |   1 +
 11 files changed, 486 insertions(+), 30 deletions(-)

diff --git a/apps/ecparam.c b/apps/ecparam.c
index 24fda04..0c893a3 100644
--- a/apps/ecparam.c
+++ b/apps/ecparam.c
@@ -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)) {
diff --git a/crypto/ec/ec_check.c b/crypto/ec/ec_check.c
index 322d0fe..097d7e1 100644
--- a/crypto/ec/ec_check.c
+++ b/crypto/ec/ec_check.c
@@ -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
@@ -10,6 +10,16 @@
 #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 = NID_undef;
+    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;
diff --git a/crypto/ec/ec_curve.c b/crypto/ec/ec_curve.c
index 641cf3f..6c7c9dd 100644
--- a/crypto/ec/ec_curve.c
+++ b/crypto/ec/ec_curve.c
@@ -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 built-in 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 built-in 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;
+}
diff --git a/crypto/ec/ec_lcl.h b/crypto/ec/ec_lcl.h
index 6b90ef3..c54789b 100644
--- a/crypto/ec/ec_lcl.h
+++ b/crypto/ec/ec_lcl.h
@@ -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 {
diff --git a/crypto/ec/ec_lib.c b/crypto/ec/ec_lib.c
index 798382a..762cac4 100644
--- a/crypto/ec/ec_lib.c
+++ b/crypto/ec/ec_lib.c
@@ -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,43 @@ 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 orders */
         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 +637,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;
     }
diff --git a/doc/man1/ecparam.pod b/doc/man1/ecparam.pod
index ed39788..fac930e 100644
--- a/doc/man1/ecparam.pod
+++ b/doc/man1/ecparam.pod
@@ -17,6 +17,7 @@ B<openssl ecparam>
 [B<-text>]
 [B<-C>]
 [B<-check>]
+[B<-check_named>]
 [B<-name arg>]
 [B<-list_curves>]
 [B<-conv_form arg>]
@@ -79,6 +80,11 @@ be loaded by calling the get_ec_group_XXX() function.
 
 Validate the elliptic curve parameters.
 
+=item B<-check_named>
+
+Validate the elliptic name curve parameters by checking if the curve parameters
+match any built-in curves.
+
 =item B<-name arg>
 
 Use the EC parameters with the specified 'short' name. Use B<-list_curves>
diff --git a/doc/man3/EC_GROUP_copy.pod b/doc/man3/EC_GROUP_copy.pod
index 3f7108d..b62eaa9 100644
--- a/doc/man3/EC_GROUP_copy.pod
+++ b/doc/man3/EC_GROUP_copy.pod
@@ -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);
 
@@ -128,7 +130,7 @@ in that a parameter obtained in this way is highly unlikely to be susceptible to
 If the seed is present for a curve then the b parameter was generated in a verifiable fashion using that seed. The OpenSSL EC library
 does not use this seed value but does enable you to inspect it using EC_GROUP_get0_seed. This returns a pointer to a memory block
 containing the seed that was used. The length of the memory block can be obtained using EC_GROUP_get_seed_len. A number of the
-builtin curves within the library provide seed values that can be obtained. It is also possible to set a custom seed using
+built-in curves within the library provide seed values that can be obtained. It is also possible to set a custom seed using
 EC_GROUP_set_seed and passing a pointer to a memory block, along with the length of the seed. Again, the EC library will not use
 this seed value, although it will be preserved in any ASN1 based communications.
 
@@ -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.
 
+The function 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,9 +210,13 @@ 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.
+Copyright 2013-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
diff --git a/include/openssl/ec.h b/include/openssl/ec.h
index 8d4d1b1..af559cb 100644
--- a/include/openssl/ec.h
+++ b/include/openssl/ec.h
@@ -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                            */
diff --git a/test/ectest.c b/test/ectest.c
index 59c7e99..7236b43 100644
--- a/test/ectest.c
+++ b/test/ectest.c
@@ -8,6 +8,7 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include <string.h>
 #include "internal/nelem.h"
 #include "testutil.h"
 
@@ -1556,6 +1557,262 @@ static const unsigned char p521_explicit[] = {
     0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38, 0x64, 0x09, 0x02, 0x01, 0x01,
 };
 
+/*
+ * This test validates a named curve's group parameters using
+ * EC_GROUP_check_named_curve(). It also checks that modifying any of the
+ * group parameters results in the curve not being valid.
+ */
+static int check_named_curve_test(int id)
+{
+    int ret = 0, nid, field_nid, has_seed;
+    EC_GROUP *group = NULL, *gtest = 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 built-in 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;
+
+    /* Fail if the curve name doesn't match the parameters */
+    EC_GROUP_set_curve_name(group, nid + 1);
+    ERR_set_mark();
+    if (!TEST_int_le(EC_GROUP_check_named_curve(group, 0), 0))
+        goto err;
+    ERR_pop_to_mark();
+
+    /* Restore curve name and ensure it's passing */
+    EC_GROUP_set_curve_name(group, nid);
+    if (!TEST_int_eq(EC_GROUP_check_named_curve(group, 0), nid))
+        goto err;
+
+    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_eq(EC_GROUP_check_named_curve(group, 0), nid))
+        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 parameter fails */
+    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))
+        goto err;
+
+    /*
+     * check that changing any curve parameter fails
+     *
+     * Setting arbitrary p, a or b might fail for some EC_GROUPs
+     * depending on the internal EC_METHOD implementation, hence run
+     * these tests conditionally to the success of EC_GROUP_set_curve().
+     */
+    ERR_set_mark();
+    if (EC_GROUP_set_curve(gtest, other_p, group_a, group_b, NULL)) {
+        if (!TEST_int_le(EC_GROUP_check_named_curve(gtest, 0), 0))
+            goto err;
+    } else {
+        /* clear the error stack if EC_GROUP_set_curve() failed */
+        ERR_pop_to_mark();
+        ERR_set_mark();
+    }
+    if (EC_GROUP_set_curve(gtest, group_p, other_a, group_b, NULL)) {
+        if (!TEST_int_le(EC_GROUP_check_named_curve(gtest, 0), 0))
+            goto err;
+    } else {
+        /* clear the error stack if EC_GROUP_set_curve() failed */
+        ERR_pop_to_mark();
+        ERR_set_mark();
+    }
+    if (EC_GROUP_set_curve(gtest, group_p, group_a, other_b, NULL)) {
+        if (!TEST_int_le(EC_GROUP_check_named_curve(gtest, 0), 0))
+            goto err;
+    } else {
+        /* clear the error stack if EC_GROUP_set_curve() failed */
+        ERR_pop_to_mark();
+        ERR_set_mark();
+    }
+    ERR_pop_to_mark();
+
+    /* Check that restoring the curve parameters passes */
+    if (!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(gtest);
+    EC_GROUP_free(group);
+    BN_CTX_free(bn_ctx);
+    return ret;
+}
+
+/*
+ * This checks the lookup capability of EC_GROUP_check_named_curve()
+ * when the given group was created with explicit parameters.
+ *
+ * It is possible to retrieve an alternative alias that does not match
+ * the original nid in this case.
+ */
+static int check_named_curve_lookup_test(int id)
+{
+    int ret = 0, nid, rv = 0;
+    EC_GROUP *g = NULL , *ga = NULL;
+    ECPARAMETERS *p = NULL, *pa = NULL;
+    BN_CTX *ctx = NULL;
+
+    /* Do some setup */
+    nid = curves[id].nid;
+    if (!TEST_ptr(ctx = BN_CTX_new())
+        || !TEST_ptr(g = EC_GROUP_new_by_curve_name(nid))
+        || !TEST_ptr(p = EC_GROUP_get_ecparameters(g, NULL)))
+        goto err;
+
+    /* replace with group from explicit parameters */
+    EC_GROUP_free(g);
+    if (!TEST_ptr(g = EC_GROUP_new_from_ecparameters(p)))
+        goto err;
+
+    if (!TEST_int_gt(rv = EC_GROUP_check_named_curve(g, 0), 0))
+        goto err;
+    if (rv != nid) {
+        /*
+         * Found an alias:
+         * fail if the returned nid is not an alias of the original group.
+         *
+         * The comparison here is done by comparing two explicit
+         * parameter EC_GROUPs with EC_GROUP_cmp(), to ensure the
+         * comparison happens with unnamed EC_GROUPs using the same
+         * EC_METHODs.
+         */
+        if (!TEST_ptr(ga = EC_GROUP_new_by_curve_name(rv))
+                || !TEST_ptr(pa = EC_GROUP_get_ecparameters(ga, NULL)))
+            goto err;
+
+        /* replace with group from explicit parameters, then compare */
+        EC_GROUP_free(ga);
+        if (!TEST_ptr(ga = EC_GROUP_new_from_ecparameters(pa))
+                || !TEST_int_eq(EC_GROUP_cmp(g, ga, ctx), 0))
+            goto err;
+    }
+
+    ret = 1;
+
+ err:
+    EC_GROUP_free(g);
+    EC_GROUP_free(ga);
+    ECPARAMETERS_free(p);
+    ECPARAMETERS_free(pa);
+    BN_CTX_free(ctx);
+
+    return ret;
+}
+
 static int parameter_test(void)
 {
     EC_GROUP *group = NULL, *group2 = NULL;
@@ -1621,6 +1878,8 @@ 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_test, crv_len);
+    ADD_ALL_TESTS(check_named_curve_lookup_test, crv_len);
 #endif
     return 1;
 }
diff --git a/test/recipes/15-test_ecparam.t b/test/recipes/15-test_ecparam.t
index 1d0b59c..ee14775 100644
--- a/test/recipes/15-test_ecparam.t
+++ b/test/recipes/15-test_ecparam.t
@@ -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}, $_])));
+}
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 9569bf4..3704a63 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -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


More information about the openssl-commits mailing list