Skip to content

Commit

Permalink
[crypto/ec] for ECC parameters with NULL or zero cofactor, compute it
Browse files Browse the repository at this point in the history
The cofactor argument to EC_GROUP_set_generator is optional, and SCA
mitigations for ECC currently use it. So the library currently falls
back to very old SCA-vulnerable code if the cofactor is not present.

This PR allows EC_GROUP_set_generator to compute the cofactor for all
curves of cryptographic interest. Steering scalar multiplication to more
SCA-robust code.

This issue affects persisted private keys in explicit parameter form,
where the (optional) cofactor field is zero or absent.

It also affects curves not built-in to the library, but constructed
programatically with explicit parameters, then calling
EC_GROUP_set_generator with a nonsensical value (NULL, zero).

The very old scalar multiplication code is known to be vulnerable to
local uarch attacks, outside of the OpenSSL threat model. New results
suggest the code path is also vulnerable to traditional wall clock
timing attacks.

CVE-2019-1547

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org>
Reviewed-by: Nicola Tuveri <nic.tuv@gmail.com>
(Merged from #9781)
  • Loading branch information
bbbrumley authored and romen committed Sep 7, 2019
1 parent ed0ac11 commit 30c22fa
Showing 1 changed file with 96 additions and 7 deletions.
103 changes: 96 additions & 7 deletions crypto/ec/ec_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,67 @@ int EC_METHOD_get_field_type(const EC_METHOD *meth)

static int ec_precompute_mont_data(EC_GROUP *);

/*-
* Try computing cofactor from the generator order (n) and field cardinality (q).
* This works for all curves of cryptographic interest.
*
* Hasse thm: q + 1 - 2*sqrt(q) <= n*h <= q + 1 + 2*sqrt(q)
* h_min = (q + 1 - 2*sqrt(q))/n
* h_max = (q + 1 + 2*sqrt(q))/n
* h_max - h_min = 4*sqrt(q)/n
* So if n > 4*sqrt(q) holds, there is only one possible value for h:
* h = \lfloor (h_min + h_max)/2 \rceil = \lfloor (q + 1)/n \rceil
*
* Otherwise, zero cofactor and return success.
*/
static int ec_guess_cofactor(EC_GROUP *group) {
int ret = 0;
BN_CTX *ctx = NULL;
BIGNUM *q = NULL;

/*-
* If the cofactor is too large, we cannot guess it.
* The RHS of below is a strict overestimate of lg(4 * sqrt(q))
*/
if (BN_num_bits(group->order) <= (BN_num_bits(group->field) + 1) / 2 + 3) {
/* default to 0 */
BN_zero(group->cofactor);
/* return success */
return 1;
}

if ((ctx = BN_CTX_new()) == NULL)
return 0;

BN_CTX_start(ctx);
if ((q = BN_CTX_get(ctx)) == NULL)
goto err;

/* set q = 2**m for binary fields; q = p otherwise */
if (group->meth->field_type == NID_X9_62_characteristic_two_field) {
BN_zero(q);
if (!BN_set_bit(q, BN_num_bits(group->field) - 1))
goto err;
} else {
if (!BN_copy(q, group->field))
goto err;
}

/* compute h = \lfloor (q + 1)/n \rceil = \lfloor (q + 1 + n/2)/n \rfloor */
if (!BN_rshift1(group->cofactor, group->order) /* n/2 */
|| !BN_add(group->cofactor, group->cofactor, q) /* q + n/2 */
/* q + 1 + n/2 */
|| !BN_add(group->cofactor, group->cofactor, BN_value_one())
/* (q + 1 + n/2)/n */
|| !BN_div(group->cofactor, NULL, group->cofactor, group->order, ctx))
goto err;
ret = 1;
err:
BN_CTX_end(ctx);
BN_CTX_free(ctx);
return ret;
}

int EC_GROUP_set_generator(EC_GROUP *group, const EC_POINT *generator,
const BIGNUM *order, const BIGNUM *cofactor)
{
Expand All @@ -273,6 +334,34 @@ int EC_GROUP_set_generator(EC_GROUP *group, const EC_POINT *generator,
return 0;
}

/* require group->field >= 1 */
if (group->field == NULL || BN_is_zero(group->field)
|| BN_is_negative(group->field)) {
ECerr(EC_F_EC_GROUP_SET_GENERATOR, EC_R_INVALID_FIELD);
return 0;
}

/*-
* - require order >= 1
* - enforce upper bound due to Hasse thm: order can be no more than one bit
* longer than field cardinality
*/
if (order == NULL || BN_is_zero(order) || BN_is_negative(order)
|| BN_num_bits(order) > BN_num_bits(group->field) + 1) {
ECerr(EC_F_EC_GROUP_SET_GENERATOR, EC_R_INVALID_GROUP_ORDER);
return 0;
}

/*-
* Unfortunately the cofactor is an optional field in many standards.
* Internally, the lib uses 0 cofactor as a marker for "unknown cofactor".
* So accept cofactor == NULL or cofactor >= 0.
*/
if (cofactor != NULL && BN_is_negative(cofactor)) {
ECerr(EC_F_EC_GROUP_SET_GENERATOR, EC_R_UNKNOWN_COFACTOR);
return 0;
}

if (group->generator == NULL) {
group->generator = EC_POINT_new(group);
if (group->generator == NULL)
Expand All @@ -281,17 +370,17 @@ int EC_GROUP_set_generator(EC_GROUP *group, const EC_POINT *generator,
if (!EC_POINT_copy(group->generator, generator))
return 0;

if (order != NULL) {
if (!BN_copy(group->order, order))
return 0;
} else
BN_zero(group->order);
if (!BN_copy(group->order, order))
return 0;

if (cofactor != NULL) {
/* Either take the provided positive cofactor, or try to compute it */
if (cofactor != NULL && !BN_is_zero(cofactor)) {
if (!BN_copy(group->cofactor, cofactor))
return 0;
} else
} else if (!ec_guess_cofactor(group)) {
BN_zero(group->cofactor);
return 0;
}

/*
* Some groups have an order with
Expand Down

1 comment on commit 30c22fa

@etherx-dev
Copy link

@etherx-dev etherx-dev commented on 30c22fa Apr 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing up my sleeve

  • done

Please sign in to comment.