This commit is contained in:
purplerain 2023-06-25 21:25:02 +00:00
parent 82bafdd0b3
commit ae25582c29
Signed by: purplerain
GPG key ID: F42C07F07E2E35B7
37 changed files with 421 additions and 1406 deletions

View file

@ -1,4 +1,4 @@
/* $OpenBSD: ec_mult.c,v 1.29 2023/04/11 18:58:20 jsing Exp $ */
/* $OpenBSD: ec_mult.c,v 1.31 2023/06/24 17:49:44 jsing Exp $ */
/*
* Originally written by Bodo Moeller and Nils Larsch for the OpenSSL project.
*/
@ -67,7 +67,6 @@
#include "ec_local.h"
/*
* This file implements the wNAF-based interleaving multi-exponentation method
* (<URL:http://www.informatik.tu-darmstadt.de/TI/Mitarbeiter/moeller.html#multiexp>);
@ -75,114 +74,6 @@
* (<URL:http://www.informatik.tu-darmstadt.de/TI/Mitarbeiter/moeller.html#fastexp>).
*/
/* structure for precomputed multiples of the generator */
typedef struct ec_pre_comp_st {
const EC_GROUP *group; /* parent EC_GROUP object */
size_t blocksize; /* block size for wNAF splitting */
size_t numblocks; /* max. number of blocks for which we have
* precomputation */
size_t w; /* window size */
EC_POINT **points; /* array with pre-calculated multiples of
* generator: 'num' pointers to EC_POINT
* objects followed by a NULL */
size_t num; /* numblocks * 2^(w-1) */
int references;
} EC_PRE_COMP;
/* functions to manage EC_PRE_COMP within the EC_GROUP extra_data framework */
static void *ec_pre_comp_dup(void *);
static void ec_pre_comp_free(void *);
static void ec_pre_comp_clear_free(void *);
static EC_PRE_COMP *
ec_pre_comp_new(const EC_GROUP *group)
{
EC_PRE_COMP *ret = NULL;
if (!group)
return NULL;
ret = malloc(sizeof(EC_PRE_COMP));
if (!ret) {
ECerror(ERR_R_MALLOC_FAILURE);
return ret;
}
ret->group = group;
ret->blocksize = 8; /* default */
ret->numblocks = 0;
ret->w = 4; /* default */
ret->points = NULL;
ret->num = 0;
ret->references = 1;
return ret;
}
static void *
ec_pre_comp_dup(void *src_)
{
EC_PRE_COMP *src = src_;
/* no need to actually copy, these objects never change! */
CRYPTO_add(&src->references, 1, CRYPTO_LOCK_EC_PRE_COMP);
return src_;
}
static void
ec_pre_comp_free(void *pre_)
{
int i;
EC_PRE_COMP *pre = pre_;
if (!pre)
return;
i = CRYPTO_add(&pre->references, -1, CRYPTO_LOCK_EC_PRE_COMP);
if (i > 0)
return;
if (pre->points) {
EC_POINT **p;
for (p = pre->points; *p != NULL; p++)
EC_POINT_free(*p);
free(pre->points);
}
free(pre);
}
static void
ec_pre_comp_clear_free(void *pre_)
{
int i;
EC_PRE_COMP *pre = pre_;
if (!pre)
return;
i = CRYPTO_add(&pre->references, -1, CRYPTO_LOCK_EC_PRE_COMP);
if (i > 0)
return;
if (pre->points) {
EC_POINT **p;
for (p = pre->points; *p != NULL; p++) {
EC_POINT_free(*p);
explicit_bzero(p, sizeof *p);
}
free(pre->points);
}
freezero(pre, sizeof *pre);
}
/* Determine the modified width-(w+1) Non-Adjacent Form (wNAF) of 'scalar'.
* This is an array r[] of values that are either zero or odd with an
* absolute value less than 2^w satisfying
@ -338,8 +229,7 @@ ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
const EC_POINT *generator = NULL;
EC_POINT *tmp = NULL;
size_t totalnum;
size_t blocksize = 0, numblocks = 0; /* for wNAF splitting */
size_t pre_points_per_block = 0;
size_t numblocks = 0; /* for wNAF splitting */
size_t i, j;
int k;
int r_is_inverted = 0;
@ -354,7 +244,6 @@ ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
EC_POINT **v;
EC_POINT ***val_sub = NULL; /* pointers to sub-arrays of 'val' or
* 'pre_comp->points' */
const EC_PRE_COMP *pre_comp = NULL;
int num_scalar = 0; /* flag: will be set to 1 if 'scalar' must be
* treated like other scalars, i.e.
* precomputation is not available */
@ -380,42 +269,10 @@ ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
ECerror(EC_R_UNDEFINED_GENERATOR);
goto err;
}
/* look if we can use precomputed multiples of generator */
pre_comp = EC_EX_DATA_get_data(group->extra_data, ec_pre_comp_dup, ec_pre_comp_free, ec_pre_comp_clear_free);
if (pre_comp && pre_comp->numblocks &&
(EC_POINT_cmp(group, generator, pre_comp->points[0], ctx) == 0)) {
blocksize = pre_comp->blocksize;
/*
* determine maximum number of blocks that wNAF
* splitting may yield (NB: maximum wNAF length is
* bit length plus one)
*/
numblocks = (BN_num_bits(scalar) / blocksize) + 1;
/*
* we cannot use more blocks than we have
* precomputation for
*/
if (numblocks > pre_comp->numblocks)
numblocks = pre_comp->numblocks;
pre_points_per_block = (size_t) 1 << (pre_comp->w - 1);
/* check that pre_comp looks sane */
if (pre_comp->num != (pre_comp->numblocks * pre_points_per_block)) {
ECerror(ERR_R_INTERNAL_ERROR);
goto err;
}
} else {
/* can't use precomputation */
pre_comp = NULL;
numblocks = 1;
num_scalar = 1; /* treat 'scalar' like 'num'-th
* element of 'scalars' */
}
numblocks = 1;
num_scalar = 1; /* treat 'scalar' like 'num'-th
* element of 'scalars' */
}
totalnum = num + numblocks;
@ -457,111 +314,9 @@ ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
if (numblocks) {
/* we go here iff scalar != NULL */
if (pre_comp == NULL) {
if (num_scalar != 1) {
ECerror(ERR_R_INTERNAL_ERROR);
goto err;
}
/* we have already generated a wNAF for 'scalar' */
} else {
size_t tmp_len = 0;
if (num_scalar != 0) {
ECerror(ERR_R_INTERNAL_ERROR);
goto err;
}
/*
* use the window size for which we have
* precomputation
*/
wsize[num] = pre_comp->w;
tmp_wNAF = compute_wNAF(scalar, wsize[num], &tmp_len);
if (tmp_wNAF == NULL)
goto err;
if (tmp_len <= max_len) {
/*
* One of the other wNAFs is at least as long
* as the wNAF belonging to the generator, so
* wNAF splitting will not buy us anything.
*/
numblocks = 1;
totalnum = num + 1; /* don't use wNAF
* splitting */
wNAF[num] = tmp_wNAF;
tmp_wNAF = NULL;
wNAF[num + 1] = NULL;
wNAF_len[num] = tmp_len;
if (tmp_len > max_len)
max_len = tmp_len;
/*
* pre_comp->points starts with the points
* that we need here:
*/
val_sub[num] = pre_comp->points;
} else {
/*
* don't include tmp_wNAF directly into wNAF
* array - use wNAF splitting and include the
* blocks
*/
signed char *pp;
EC_POINT **tmp_points;
if (tmp_len < numblocks * blocksize) {
/*
* possibly we can do with fewer
* blocks than estimated
*/
numblocks = (tmp_len + blocksize - 1) / blocksize;
if (numblocks > pre_comp->numblocks) {
ECerror(ERR_R_INTERNAL_ERROR);
goto err;
}
totalnum = num + numblocks;
}
/* split wNAF in 'numblocks' parts */
pp = tmp_wNAF;
tmp_points = pre_comp->points;
for (i = num; i < totalnum; i++) {
if (i < totalnum - 1) {
wNAF_len[i] = blocksize;
if (tmp_len < blocksize) {
ECerror(ERR_R_INTERNAL_ERROR);
goto err;
}
tmp_len -= blocksize;
} else
/*
* last block gets whatever
* is left (this could be
* more or less than
* 'blocksize'!)
*/
wNAF_len[i] = tmp_len;
wNAF[i + 1] = NULL;
wNAF[i] = malloc(wNAF_len[i]);
if (wNAF[i] == NULL) {
ECerror(ERR_R_MALLOC_FAILURE);
goto err;
}
memcpy(wNAF[i], pp, wNAF_len[i]);
if (wNAF_len[i] > max_len)
max_len = wNAF_len[i];
if (*tmp_points == NULL) {
ECerror(ERR_R_INTERNAL_ERROR);
goto err;
}
val_sub[i] = tmp_points;
tmp_points += pre_points_per_block;
pp += blocksize;
}
}
if (num_scalar != 1) {
ECerror(ERR_R_INTERNAL_ERROR);
goto err;
}
}
/*
@ -693,181 +448,3 @@ ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
free(val_sub);
return ret;
}
/* ec_wNAF_precompute_mult()
* creates an EC_PRE_COMP object with preprecomputed multiples of the generator
* for use with wNAF splitting as implemented in ec_wNAF_mul().
*
* 'pre_comp->points' is an array of multiples of the generator
* of the following form:
* points[0] = generator;
* points[1] = 3 * generator;
* ...
* points[2^(w-1)-1] = (2^(w-1)-1) * generator;
* points[2^(w-1)] = 2^blocksize * generator;
* points[2^(w-1)+1] = 3 * 2^blocksize * generator;
* ...
* points[2^(w-1)*(numblocks-1)-1] = (2^(w-1)) * 2^(blocksize*(numblocks-2)) * generator
* points[2^(w-1)*(numblocks-1)] = 2^(blocksize*(numblocks-1)) * generator
* ...
* points[2^(w-1)*numblocks-1] = (2^(w-1)) * 2^(blocksize*(numblocks-1)) * generator
* points[2^(w-1)*numblocks] = NULL
*/
int
ec_wNAF_precompute_mult(EC_GROUP *group, BN_CTX *ctx)
{
const EC_POINT *generator;
EC_POINT *tmp_point = NULL, *base = NULL, **var;
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;
/* if there is an old EC_PRE_COMP object, throw it away */
EC_EX_DATA_free_data(&group->extra_data, ec_pre_comp_dup, ec_pre_comp_free, ec_pre_comp_clear_free);
if ((pre_comp = ec_pre_comp_new(group)) == NULL)
return 0;
generator = EC_GROUP_get0_generator(group);
if (generator == NULL) {
ECerror(EC_R_UNDEFINED_GENERATOR);
goto err;
}
BN_CTX_start(ctx);
if ((order = BN_CTX_get(ctx)) == NULL)
goto err;
if (!EC_GROUP_get_order(group, order, ctx))
goto err;
if (BN_is_zero(order)) {
ECerror(EC_R_UNKNOWN_ORDER);
goto err;
}
bits = BN_num_bits(order);
/*
* The following parameters mean we precompute (approximately) one
* point per bit.
*
* TBD: The combination 8, 4 is perfect for 160 bits; for other bit
* lengths, other parameter combinations might provide better
* efficiency.
*/
blocksize = 8;
w = 4;
if (EC_window_bits_for_scalar_size(bits) > w) {
/* let's not make the window too small ... */
w = EC_window_bits_for_scalar_size(bits);
}
numblocks = (bits + blocksize - 1) / blocksize; /* max. number of blocks
* to use for wNAF
* splitting */
pre_points_per_block = (size_t) 1 << (w - 1);
num = pre_points_per_block * numblocks; /* number of points to
* compute and store */
points = reallocarray(NULL, (num + 1), sizeof(EC_POINT *));
if (!points) {
ECerror(ERR_R_MALLOC_FAILURE);
goto err;
}
var = points;
var[num] = NULL; /* pivot */
for (i = 0; i < num; i++) {
if ((var[i] = EC_POINT_new(group)) == NULL) {
ECerror(ERR_R_MALLOC_FAILURE);
goto err;
}
}
if (!(tmp_point = EC_POINT_new(group)) || !(base = EC_POINT_new(group))) {
ECerror(ERR_R_MALLOC_FAILURE);
goto err;
}
if (!EC_POINT_copy(base, generator))
goto err;
/* do the precomputation */
for (i = 0; i < numblocks; i++) {
size_t j;
if (!EC_POINT_dbl(group, tmp_point, base, ctx))
goto err;
if (!EC_POINT_copy(*var++, base))
goto err;
for (j = 1; j < pre_points_per_block; j++, var++) {
/* calculate odd multiples of the current base point */
if (!EC_POINT_add(group, *var, tmp_point, *(var - 1), ctx))
goto err;
}
if (i < numblocks - 1) {
/*
* get the next base (multiply current one by
* 2^blocksize)
*/
size_t k;
if (blocksize <= 2) {
ECerror(ERR_R_INTERNAL_ERROR);
goto err;
}
if (!EC_POINT_dbl(group, base, tmp_point, ctx))
goto err;
for (k = 2; k < blocksize; k++) {
if (!EC_POINT_dbl(group, base, base, ctx))
goto err;
}
}
}
if (!EC_POINTs_make_affine(group, num, points, ctx))
goto err;
pre_comp->group = group;
pre_comp->blocksize = blocksize;
pre_comp->numblocks = numblocks;
pre_comp->w = w;
pre_comp->points = points;
points = NULL;
pre_comp->num = num;
if (!EC_EX_DATA_set_data(&group->extra_data, pre_comp,
ec_pre_comp_dup, ec_pre_comp_free, ec_pre_comp_clear_free))
goto err;
pre_comp = NULL;
ret = 1;
err:
BN_CTX_end(ctx);
ec_pre_comp_free(pre_comp);
if (points) {
EC_POINT **p;
for (p = points; *p != NULL; p++)
EC_POINT_free(*p);
free(points);
}
EC_POINT_free(tmp_point);
EC_POINT_free(base);
return ret;
}
int
ec_wNAF_have_precompute_mult(const EC_GROUP *group)
{
if (EC_EX_DATA_get_data(group->extra_data, ec_pre_comp_dup, ec_pre_comp_free, ec_pre_comp_clear_free) != NULL)
return 1;
else
return 0;
}