added code to validate EC named curve parameters

Reviewed-by: Nicola Tuveri <nic.tuv@gmail.com>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/8555)
This commit is contained in:
Shane Lontis 2019-03-21 20:09:02 +10:00 committed by Nicola Tuveri
parent 4660bdea07
commit 8402cd5f75
11 changed files with 398 additions and 30 deletions

View file

@ -30,7 +30,7 @@ typedef enum OPTION_choice {
OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_TEXT, OPT_C, 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_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 OPT_R_ENUM
} OPTION_CHOICE; } OPTION_CHOICE;
@ -43,6 +43,8 @@ const OPTIONS ecparam_options[] = {
{"text", OPT_TEXT, '-', "Print the ec parameters in text form"}, {"text", OPT_TEXT, '-', "Print the ec parameters in text form"},
{"C", OPT_C, '-', "Print a 'C' function creating the parameters"}, {"C", OPT_C, '-', "Print a 'C' function creating the parameters"},
{"check", OPT_CHECK, '-', "Validate the ec 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, '-', {"list_curves", OPT_LIST_CURVES, '-',
"Prints a list of all curve 'short names'"}, "Prints a list of all curve 'short names'"},
{"no_seed", OPT_NO_SEED, '-', {"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 informat = FORMAT_PEM, outformat = FORMAT_PEM, noout = 0, C = 0;
int ret = 1, private = 0; int ret = 1, private = 0;
int list_curves = 0, no_seed = 0, check = 0, new_form = 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); prog = opt_init(argc, argv, ecparam_options);
while ((o = opt_next()) != OPT_EOF) { while ((o = opt_next()) != OPT_EOF) {
@ -127,6 +129,9 @@ int ecparam_main(int argc, char **argv)
case OPT_CHECK: case OPT_CHECK:
check = 1; check = 1;
break; break;
case OPT_CHECK_NAMED:
check_named = 1;
break;
case OPT_LIST_CURVES: case OPT_LIST_CURVES:
list_curves = 1; list_curves = 1;
break; break;
@ -266,6 +271,16 @@ int ecparam_main(int argc, char **argv)
goto end; 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) { if (check) {
BIO_printf(bio_err, "checking elliptic curve parameters: "); BIO_printf(bio_err, "checking elliptic curve parameters: ");
if (!EC_GROUP_check(group, NULL)) { if (!EC_GROUP_check(group, NULL)) {

View 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 * 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 * this file except in compliance with the License. You can obtain a copy
@ -10,6 +10,16 @@
#include "ec_lcl.h" #include "ec_lcl.h"
#include <openssl/err.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 EC_GROUP_check(const EC_GROUP *group, BN_CTX *ctx)
{ {
int ret = 0; int ret = 0;
@ -17,6 +27,11 @@ int EC_GROUP_check(const EC_GROUP *group, BN_CTX *ctx)
BN_CTX *new_ctx = NULL; BN_CTX *new_ctx = NULL;
EC_POINT *point = 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 */ /* Custom curves assumed to be correct */
if ((group->meth->flags & EC_FLAGS_CUSTOM_CURVE) != 0) if ((group->meth->flags & EC_FLAGS_CUSTOM_CURVE) != 0)
return 1; return 1;

View file

@ -14,6 +14,7 @@
#include <openssl/obj_mac.h> #include <openssl/obj_mac.h>
#include <openssl/opensslconf.h> #include <openssl/opensslconf.h>
#include "internal/nelem.h" #include "internal/nelem.h"
#include "internal/o_str.h"
typedef struct { typedef struct {
int field_type, /* either NID_X9_62_prime_field or int field_type, /* either NID_X9_62_prime_field or
@ -1136,6 +1137,7 @@ static const struct {
}, },
{ {
/* no seed */ /* no seed */
/* p */
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
@ -1209,6 +1211,7 @@ static const struct {
}, },
{ {
/* no seed */ /* no seed */
/* p */
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
@ -1244,6 +1247,7 @@ static const struct {
}, },
{ {
/* no seed */ /* no seed */
/* p */
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xA1, 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 NID_X9_62_characteristic_two_field, 20, 36, 2
}, },
{ {
/* no seed */ /* seed */
0x77, 0xE2, 0xB0, 0x73, 0x70, 0xEB, 0x0F, 0x83, 0x2A, 0x6D, 0xD5, 0xB6, 0x77, 0xE2, 0xB0, 0x73, 0x70, 0xEB, 0x0F, 0x83, 0x2A, 0x6D, 0xD5, 0xB6,
0x2D, 0xFC, 0x88, 0xCD, 0x06, 0xBB, 0x84, 0xBE, 0x2D, 0xFC, 0x88, 0xCD, 0x06, 0xBB, 0x84, 0xBE,
/* p */ /* p */
@ -3197,3 +3201,120 @@ int EC_curve_nist2nid(const char *name)
} }
return NID_undef; 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;
}

View file

@ -309,13 +309,10 @@ struct ec_point_st {
static ossl_inline int ec_point_is_compat(const EC_POINT *point, static ossl_inline int ec_point_is_compat(const EC_POINT *point,
const EC_GROUP *group) const EC_GROUP *group)
{ {
if (group->meth != point->meth return group->meth == point->meth
|| (group->curve_name != 0 && (group->curve_name == 0
&& point->curve_name != 0 || point->curve_name == 0
&& group->curve_name != point->curve_name)) || group->curve_name == point->curve_name);
return 0;
return 1;
} }
NISTP224_PRE_COMP *EC_nistp224_pre_comp_dup(NISTP224_PRE_COMP *); 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_generate_public_key(EC_KEY *eckey);
int ec_key_simple_check_key(const 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 */ /* EC_METHOD definitions */
struct ec_key_method_st { struct ec_key_method_st {

View file

@ -284,15 +284,17 @@ int EC_GROUP_set_generator(EC_GROUP *group, const EC_POINT *generator,
if (order != NULL) { if (order != NULL) {
if (!BN_copy(group->order, order)) if (!BN_copy(group->order, order))
return 0; return 0;
} else } else {
BN_zero(group->order); BN_zero(group->order);
}
/* The cofactor is an optional field, so it should be able to be NULL. */
if (cofactor != NULL) { if (cofactor != NULL) {
if (!BN_copy(group->cofactor, cofactor)) if (!BN_copy(group->cofactor, cofactor))
return 0; return 0;
} else } else {
BN_zero(group->cofactor); BN_zero(group->cofactor);
}
/* /*
* Some groups have an order with * Some groups have an order with
* factors of two, which makes the Montgomery setup fail. * 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)) !b->meth->group_get_curve(b, b1, b2, b3, ctx))
r = 1; 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; 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), 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; r = 1;
if (!r) { if (!r) {
const BIGNUM *ao, *bo, *ac, *bc; const BIGNUM *ao, *bo, *ac, *bc;
/* compare the order and cofactor */ /* compare the order's */
ao = EC_GROUP_get0_order(a); ao = EC_GROUP_get0_order(a);
bo = EC_GROUP_get0_order(b); bo = EC_GROUP_get0_order(b);
if (ao == NULL || bo == NULL) {
/* 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;
}
/*
* 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); ac = EC_GROUP_get0_cofactor(a);
bc = EC_GROUP_get0_cofactor(b); bc = EC_GROUP_get0_cofactor(b);
if (ao == NULL || bo == NULL) { /* Returns 1 (mismatch) if both cofactors are specified and different */
BN_CTX_end(ctx); if (!BN_is_zero(ac) && !BN_is_zero(bc) && BN_cmp(ac, bc) != 0)
BN_CTX_free(ctx_new);
return -1;
}
if (BN_cmp(ao, bo) || BN_cmp(ac, bc))
r = 1; r = 1;
/* Returns 0 if the parameters matched */
} }
end:
BN_CTX_end(ctx); BN_CTX_end(ctx);
BN_CTX_free(ctx_new); BN_CTX_free(ctx_new);

View 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); ECerr(EC_F_EC_SCALAR_MUL_LADDER, EC_R_UNKNOWN_ORDER);
return 0; 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); ECerr(EC_F_EC_SCALAR_MUL_LADDER, EC_R_UNKNOWN_COFACTOR);
return 0; return 0;
} }

View 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_asn1_flag, EC_GROUP_set_point_conversion_form,
EC_GROUP_get_point_conversion_form, EC_GROUP_get0_seed, 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_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_basis_type, EC_GROUP_get_trinomial_basis,
EC_GROUP_get_pentanomial_basis, EC_GROUP_get0_field EC_GROUP_get_pentanomial_basis, EC_GROUP_get0_field
- Functions for manipulating EC_GROUP objects - 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_get_degree(const EC_GROUP *group);
int EC_GROUP_check(const EC_GROUP *group, BN_CTX *ctx); 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); 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 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 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. 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 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 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. 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_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_order_bits() returns the number of bits in the group order.
EC_GROUP_get0_cofactor() returns an internal pointer to the group cofactor. 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_POINT_new(3)>, L<EC_POINT_add(3)>, L<EC_KEY_new(3)>,
L<EC_GFp_simple_method(3)>, L<d2i_ECPKParameters(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 =head1 COPYRIGHT
Copyright 2013-2017 The OpenSSL Project Authors. All Rights Reserved. Copyright 2013-2017 The OpenSSL Project Authors. All Rights Reserved.

View 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); const char *EC_curve_nid2nist(int nid);
int EC_curve_nist2nid(const char *name); int EC_curve_nist2nid(const char *name);
int EC_GROUP_check_named_curve(const EC_GROUP *group, int nist_only);
/********************************************************************/ /********************************************************************/
/* EC_POINT functions */ /* EC_POINT functions */

View file

@ -8,6 +8,7 @@
* https://www.openssl.org/source/license.html * https://www.openssl.org/source/license.html
*/ */
#include <string.h>
#include "internal/nelem.h" #include "internal/nelem.h"
#include "testutil.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, 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) static int parameter_test(void)
{ {
EC_GROUP *group = NULL, *group2 = NULL; 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, crv_len);
ADD_ALL_TESTS(internal_curve_test_method, crv_len); ADD_ALL_TESTS(internal_curve_test_method, crv_len);
ADD_TEST(group_field_test); ADD_TEST(group_field_test);
ADD_ALL_TESTS(check_named_curve, crv_len);
#endif #endif
return 1; return 1;
} }

View file

@ -23,12 +23,20 @@ plan skip_all => "EC isn't supported in this build"
my @valid = glob(data_file("valid", "*.pem")); my @valid = glob(data_file("valid", "*.pem"));
my @invalid = glob(data_file("invalid", "*.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) { foreach (@valid) {
ok(run(app([qw{openssl ecparam -noout -check -in}, $_]))); ok(run(app([qw{openssl ecparam -noout -check -in}, $_])));
} }
foreach (@valid) {
ok(run(app([qw{openssl ecparam -noout -check_named -in}, $_])));
}
foreach (@invalid) { foreach (@invalid) {
ok(!run(app([qw{openssl ecparam -noout -check -in}, $_]))); ok(!run(app([qw{openssl ecparam -noout -check -in}, $_])));
} }
foreach (@invalid) {
ok(!run(app([qw{openssl ecparam -noout -check_named -in}, $_])));
}

View file

@ -4795,3 +4795,4 @@ EVP_MD_upref 4742 3_0_0 EXIST::FUNCTION:
EVP_MD_fetch 4743 3_0_0 EXIST::FUNCTION: EVP_MD_fetch 4743 3_0_0 EXIST::FUNCTION:
EVP_set_default_properties 4744 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: OSSL_PARAM_construct_end 4745 3_0_0 EXIST::FUNCTION:
EC_GROUP_check_named_curve 4746 3_0_0 EXIST::FUNCTION:EC