/*
- * Copyright 2001-2018 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2001-2020 The OpenSSL Project Authors. All Rights Reserved.
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved
*
- * Licensed under the OpenSSL license (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
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
+/*
+ * ECDSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
#include <string.h>
#include <openssl/err.h>
#include "internal/cryptlib.h"
-#include "internal/bn_int.h"
-#include "ec_lcl.h"
+#include "crypto/bn.h"
+#include "ec_local.h"
#include "internal/refcount.h"
/*
} while(0)
/*-
- * This functions computes (in constant time) a point multiplication over the
- * EC group.
- *
- * At a high level, it is Montgomery ladder with conditional swaps.
+ * This functions computes a single point multiplication over the EC group,
+ * using, at a high level, a Montgomery ladder with conditional swaps, with
+ * various timing attack defenses.
*
* It performs either a fixed point multiplication
* (scalar * generator)
* (scalar * point)
* when point is not NULL.
*
- * scalar should be in the range [0,n) otherwise all constant time bets are off.
+ * `scalar` cannot be NULL and should be in the range [0,n) otherwise all
+ * constant time bets are off (where n is the cardinality of the EC group).
+ *
+ * This function expects `group->order` and `group->cardinality` to be well
+ * defined and non-zero: it fails with an error code otherwise.
*
- * NB: This says nothing about EC_POINT_add and EC_POINT_dbl,
- * which of course are not constant time themselves.
+ * NB: This says nothing about the constant-timeness of the ladder step
+ * implementation (i.e., the default implementation is based on EC_POINT_add and
+ * EC_POINT_dbl, which of course are not constant time themselves) or the
+ * underlying multiprecision arithmetic.
*
- * The product is stored in r.
+ * The product is stored in `r`.
+ *
+ * This is an internal function: callers are in charge of ensuring that the
+ * input parameters `group`, `r`, `scalar` and `ctx` are not NULL.
*
* Returns 1 on success, 0 otherwise.
*/
-static int ec_mul_consttime(const EC_GROUP *group, EC_POINT *r,
- const BIGNUM *scalar, const EC_POINT *point,
- BN_CTX *ctx)
+int ec_scalar_mul_ladder(const EC_GROUP *group, EC_POINT *r,
+ const BIGNUM *scalar, const EC_POINT *point,
+ BN_CTX *ctx)
{
- int i, order_bits, group_top, kbit, pbit, Z_is_one;
+ int i, cardinality_bits, group_top, kbit, pbit, Z_is_one;
+ EC_POINT *p = NULL;
EC_POINT *s = NULL;
BIGNUM *k = NULL;
BIGNUM *lambda = NULL;
- BN_CTX *new_ctx = NULL;
+ BIGNUM *cardinality = NULL;
int ret = 0;
- if (ctx == NULL && (ctx = new_ctx = BN_CTX_secure_new()) == NULL)
+ /* early exit if the input point is the point at infinity */
+ if (point != NULL && EC_POINT_is_at_infinity(group, point))
+ return EC_POINT_set_to_infinity(group, r);
+
+ if (BN_is_zero(group->order)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, EC_R_UNKNOWN_ORDER);
return 0;
+ }
+ if (BN_is_zero(group->cofactor)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, EC_R_UNKNOWN_COFACTOR);
+ return 0;
+ }
BN_CTX_start(ctx);
- order_bits = BN_num_bits(group->order);
-
- s = EC_POINT_new(group);
- if (s == NULL)
+ if (((p = EC_POINT_new(group)) == NULL)
+ || ((s = EC_POINT_new(group)) == NULL)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_MALLOC_FAILURE);
goto err;
+ }
if (point == NULL) {
- if (!EC_POINT_copy(s, group->generator))
+ if (!EC_POINT_copy(p, group->generator)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_EC_LIB);
goto err;
+ }
} else {
- if (!EC_POINT_copy(s, point))
+ if (!EC_POINT_copy(p, point)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_EC_LIB);
goto err;
+ }
}
+ EC_POINT_BN_set_flags(p, BN_FLG_CONSTTIME);
+ EC_POINT_BN_set_flags(r, BN_FLG_CONSTTIME);
EC_POINT_BN_set_flags(s, BN_FLG_CONSTTIME);
+ cardinality = BN_CTX_get(ctx);
lambda = BN_CTX_get(ctx);
k = BN_CTX_get(ctx);
- if (k == NULL)
+ if (k == NULL) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ if (!BN_mul(cardinality, group->order, group->cofactor, ctx)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_BN_LIB);
goto err;
+ }
/*
- * Group orders are often on a word boundary.
+ * Group cardinalities are often on a word boundary.
* So when we pad the scalar, some timing diff might
* pop if it needs to be expanded due to carries.
* So expand ahead of time.
*/
- group_top = bn_get_top(group->order);
- if ((bn_wexpand(k, group_top + 1) == NULL)
- || (bn_wexpand(lambda, group_top + 1) == NULL))
+ cardinality_bits = BN_num_bits(cardinality);
+ group_top = bn_get_top(cardinality);
+ if ((bn_wexpand(k, group_top + 2) == NULL)
+ || (bn_wexpand(lambda, group_top + 2) == NULL)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_BN_LIB);
goto err;
+ }
- if (!BN_copy(k, scalar))
+ if (!BN_copy(k, scalar)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_BN_LIB);
goto err;
+ }
BN_set_flags(k, BN_FLG_CONSTTIME);
- if ((BN_num_bits(k) > order_bits) || (BN_is_negative(k))) {
+ if ((BN_num_bits(k) > cardinality_bits) || (BN_is_negative(k))) {
/*-
* this is an unusual input, and we don't guarantee
* constant-timeness
*/
- if (!BN_nnmod(k, k, group->order, ctx))
+ if (!BN_nnmod(k, k, cardinality, ctx)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_BN_LIB);
goto err;
+ }
}
- if (!BN_add(lambda, k, group->order))
+ if (!BN_add(lambda, k, cardinality)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_BN_LIB);
goto err;
+ }
BN_set_flags(lambda, BN_FLG_CONSTTIME);
- if (!BN_add(k, lambda, group->order))
+ if (!BN_add(k, lambda, cardinality)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_BN_LIB);
goto err;
+ }
/*
- * lambda := scalar + order
- * k := scalar + 2*order
+ * lambda := scalar + cardinality
+ * k := scalar + 2*cardinality
*/
- kbit = BN_is_bit_set(lambda, order_bits);
- BN_consttime_swap(kbit, k, lambda, group_top + 1);
+ kbit = BN_is_bit_set(lambda, cardinality_bits);
+ BN_consttime_swap(kbit, k, lambda, group_top + 2);
group_top = bn_get_top(group->field);
if ((bn_wexpand(s->X, group_top) == NULL)
|| (bn_wexpand(s->Z, group_top) == NULL)
|| (bn_wexpand(r->X, group_top) == NULL)
|| (bn_wexpand(r->Y, group_top) == NULL)
- || (bn_wexpand(r->Z, group_top) == NULL))
- goto err;
-
- /* top bit is a 1, in a fixed pos */
- if (!EC_POINT_copy(r, s))
+ || (bn_wexpand(r->Z, group_top) == NULL)
+ || (bn_wexpand(p->X, group_top) == NULL)
+ || (bn_wexpand(p->Y, group_top) == NULL)
+ || (bn_wexpand(p->Z, group_top) == NULL)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_BN_LIB);
goto err;
+ }
- EC_POINT_BN_set_flags(r, BN_FLG_CONSTTIME);
+ /* ensure input point is in affine coords for ladder step efficiency */
+ if (!p->Z_is_one && (group->meth->make_affine == NULL
+ || !group->meth->make_affine(group, p, ctx))) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_EC_LIB);
+ goto err;
+ }
- if (!EC_POINT_dbl(group, s, s, ctx))
+ /* Initialize the Montgomery ladder */
+ if (!ec_point_ladder_pre(group, r, s, p, ctx)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, EC_R_LADDER_PRE_FAILURE);
goto err;
+ }
- pbit = 0;
+ /* top bit is a 1, in a fixed pos */
+ pbit = 1;
#define EC_POINT_CSWAP(c, a, b, w, t) do { \
BN_consttime_swap(c, (a)->X, (b)->X, w); \
* This is XOR. pbit tracks the previous bit of k.
*/
- for (i = order_bits - 1; i >= 0; i--) {
+ for (i = cardinality_bits - 1; i >= 0; i--) {
kbit = BN_is_bit_set(k, i) ^ pbit;
EC_POINT_CSWAP(kbit, r, s, group_top, Z_is_one);
- if (!EC_POINT_add(group, s, r, s, ctx))
- goto err;
- if (!EC_POINT_dbl(group, r, r, ctx))
+
+ /* Perform a single step of the Montgomery ladder */
+ if (!ec_point_ladder_step(group, r, s, p, ctx)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, EC_R_LADDER_STEP_FAILURE);
goto err;
+ }
/*
* pbit logic merges this cswap with that of the
* next iteration
EC_POINT_CSWAP(pbit, r, s, group_top, Z_is_one);
#undef EC_POINT_CSWAP
+ /* Finalize ladder (and recover full point coordinates) */
+ if (!ec_point_ladder_post(group, r, s, p, ctx)) {
+ ECerr(EC_F_EC_SCALAR_MUL_LADDER, EC_R_LADDER_POST_FAILURE);
+ goto err;
+ }
+
ret = 1;
err:
- EC_POINT_free(s);
+ EC_POINT_free(p);
+ EC_POINT_clear_free(s);
BN_CTX_end(ctx);
- BN_CTX_free(new_ctx);
return ret;
}
size_t num, const EC_POINT *points[], const BIGNUM *scalars[],
BN_CTX *ctx)
{
- BN_CTX *new_ctx = NULL;
const EC_POINT *generator = NULL;
EC_POINT *tmp = NULL;
size_t totalnum;
* precomputation is not available */
int ret = 0;
- if (!ec_point_is_compat(r, group)) {
- ECerr(EC_F_EC_WNAF_MUL, EC_R_INCOMPATIBLE_OBJECTS);
- return 0;
- }
-
- if ((scalar == NULL) && (num == 0)) {
- return EC_POINT_set_to_infinity(group, r);
- }
-
- /*-
- * Handle the common cases where the scalar is secret, enforcing a constant
- * time scalar multiplication algorithm.
- */
- if ((scalar != NULL) && (num == 0)) {
+ if (!BN_is_zero(group->order) && !BN_is_zero(group->cofactor)) {
/*-
- * In this case we want to compute scalar * GeneratorPoint: this
- * codepath is reached most prominently by (ephemeral) key generation
- * of EC cryptosystems (i.e. ECDSA keygen and sign setup, ECDH
- * keygen/first half), where the scalar is always secret. This is why
- * we ignore if BN_FLG_CONSTTIME is actually set and we always call the
- * constant time version.
+ * Handle the common cases where the scalar is secret, enforcing a
+ * scalar multiplication implementation based on a Montgomery ladder,
+ * with various timing attack defenses.
*/
- return ec_mul_consttime(group, r, scalar, NULL, ctx);
- }
- if ((scalar == NULL) && (num == 1)) {
- /*-
- * In this case we want to compute scalar * GenericPoint: this codepath
- * is reached most prominently by the second half of ECDH, where the
- * secret scalar is multiplied by the peer's public point. To protect
- * the secret scalar, we ignore if BN_FLG_CONSTTIME is actually set and
- * we always call the constant time version.
- */
- return ec_mul_consttime(group, r, scalars[0], points[0], ctx);
- }
-
- for (i = 0; i < num; i++) {
- if (!ec_point_is_compat(points[i], group)) {
- ECerr(EC_F_EC_WNAF_MUL, EC_R_INCOMPATIBLE_OBJECTS);
- return 0;
+ if ((scalar != group->order) && (scalar != NULL) && (num == 0)) {
+ /*-
+ * In this case we want to compute scalar * GeneratorPoint: this
+ * codepath is reached most prominently by (ephemeral) key
+ * generation of EC cryptosystems (i.e. ECDSA keygen and sign setup,
+ * ECDH keygen/first half), where the scalar is always secret. This
+ * is why we ignore if BN_FLG_CONSTTIME is actually set and we
+ * always call the ladder version.
+ */
+ return ec_scalar_mul_ladder(group, r, scalar, NULL, ctx);
+ }
+ if ((scalar == NULL) && (num == 1) && (scalars[0] != group->order)) {
+ /*-
+ * In this case we want to compute scalar * VariablePoint: this
+ * codepath is reached most prominently by the second half of ECDH,
+ * where the secret scalar is multiplied by the peer's public point.
+ * To protect the secret scalar, we ignore if BN_FLG_CONSTTIME is
+ * actually set and we always call the ladder version.
+ */
+ return ec_scalar_mul_ladder(group, r, scalars[0], points[0], ctx);
}
- }
-
- if (ctx == NULL) {
- ctx = new_ctx = BN_CTX_new();
- if (ctx == NULL)
- goto err;
}
if (scalar != NULL) {
}
}
- if (!EC_POINTs_make_affine(group, num_val, val, ctx))
+ if (group->meth->points_make_affine == NULL
+ || !group->meth->points_make_affine(group, num_val, val, ctx))
goto err;
r_is_at_infinity = 1;
if (r_is_at_infinity) {
if (!EC_POINT_copy(r, val_sub[i][digit >> 1]))
goto err;
+
+ /*-
+ * Apply coordinate blinding for EC_POINT.
+ *
+ * The underlying EC_METHOD can optionally implement this function:
+ * ec_point_blind_coordinates() returns 0 in case of errors or 1 on
+ * success or if coordinate blinding is not implemented for this
+ * group.
+ */
+ if (!ec_point_blind_coordinates(group, r, ctx)) {
+ ECerr(EC_F_EC_WNAF_MUL, EC_R_POINT_COORDINATES_BLIND_FAILURE);
+ goto err;
+ }
+
r_is_at_infinity = 0;
} else {
if (!EC_POINT_add
ret = 1;
err:
- BN_CTX_free(new_ctx);
EC_POINT_free(tmp);
OPENSSL_free(wsize);
OPENSSL_free(wNAF_len);
{
const EC_POINT *generator;
EC_POINT *tmp_point = NULL, *base = NULL, **var;
- BN_CTX *new_ctx = NULL;
const BIGNUM *order;
size_t i, bits, w, pre_points_per_block, blocksize, numblocks, num;
EC_POINT **points = NULL;
EC_PRE_COMP *pre_comp;
int ret = 0;
+#ifndef FIPS_MODULE
+ BN_CTX *new_ctx = NULL;
+#endif
/* if there is an old EC_PRE_COMP object, throw it away */
EC_pre_comp_free(group);
goto err;
}
- if (ctx == NULL) {
+#ifndef FIPS_MODULE
+ if (ctx == NULL)
ctx = new_ctx = BN_CTX_new();
- if (ctx == NULL)
- goto err;
- }
+#endif
+ if (ctx == NULL)
+ goto err;
BN_CTX_start(ctx);
}
}
- if (!EC_POINTs_make_affine(group, num, points, ctx))
+ if (group->meth->points_make_affine == NULL
+ || !group->meth->points_make_affine(group, num, points, ctx))
goto err;
pre_comp->group = group;
ret = 1;
err:
- if (ctx != NULL)
- BN_CTX_end(ctx);
+ BN_CTX_end(ctx);
+#ifndef FIPS_MODULE
BN_CTX_free(new_ctx);
+#endif
EC_ec_pre_comp_free(pre_comp);
if (points) {
EC_POINT **p;