Skip to content

Commit

Permalink
Allow group methods to customize initialization for speed
Browse files Browse the repository at this point in the history
This commit also adds an implementation for P256 that avoids some
expensive initialization of Montgomery arithmetic structures in favor
of precomputation. Since ECC groups are not always cached by higher
layers this brings significant savings to TLS handshakes.

Reviewed-by: Shane Lontis <[email protected]>
Reviewed-by: Tomas Mraz <[email protected]>
(Merged from openssl#22746)
  • Loading branch information
wbl authored and t8m committed Jun 5, 2024
1 parent 0e2567d commit 23b6ef4
Show file tree
Hide file tree
Showing 7 changed files with 275 additions and 7 deletions.
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ OpenSSL 3.4

*Alexander Kanavin*

* ECC groups may now customize their initialization to save CPU by using
precomputed values. This is used by the P-256 implementation.

*Watson Ladd*

OpenSSL 3.3
-----------

Expand Down
42 changes: 42 additions & 0 deletions crypto/bn/bn_mont.c
Original file line number Diff line number Diff line change
Expand Up @@ -465,3 +465,45 @@ BN_MONT_CTX *BN_MONT_CTX_set_locked(BN_MONT_CTX **pmont, CRYPTO_RWLOCK *lock,
CRYPTO_THREAD_unlock(lock);
return ret;
}

int ossl_bn_mont_ctx_set(BN_MONT_CTX *ctx, const BIGNUM *modulus, int ri, const unsigned char *rr,
size_t rrlen, uint32_t nlo, uint32_t nhi)
{
if (BN_copy(&ctx->N, modulus) == NULL)
return 0;
if (BN_bin2bn(rr, rrlen, &ctx->RR) == NULL)
return 0;
ctx->ri = ri;
#if (BN_BITS2 <= 32) && defined(OPENSSL_BN_ASM_MONT)
ctx->n0[0] = nlo;
ctx->n0[1] = nhi;
#elif BN_BITS2 <= 32
ctx->n0[0] = nlo;
ctx->n0[1] = 0;
#else
ctx->n0[0] = ((BN_ULONG)nhi << 32)| nlo;
ctx->n0[1] = 0;
#endif

return 1;
}

int ossl_bn_mont_ctx_eq(const BN_MONT_CTX *m1, const BN_MONT_CTX *m2)
{
if (m1->ri != m2->ri)
return 0;
if (BN_cmp(&m1->RR, &m2->RR) != 0)
return 0;
if (m1->flags != m2->flags)
return 0;
#ifdef MONT_WORD
if (m1->n0[0] != m2->n0[0])
return 0;
if (m1->n0[1] != m2->n0[1])
return 0;
#else
if (BN_cmp(&m1->Ni, &m2->Ni) != 0)
return 0;
#endif
return 1;
}
36 changes: 30 additions & 6 deletions crypto/ec/ec_curve.c
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ static const struct {

static const struct {
EC_CURVE_DATA h;
unsigned char data[20 + 32 * 6];
unsigned char data[20 + 32 * 8];
} _EC_X9_62_PRIME_256V1 = {
{
NID_X9_62_prime_field, 20, 32, 1
Expand Down Expand Up @@ -415,7 +415,15 @@ static const struct {
/* order */
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51
0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51,
/* RR for prime */
0x00, 0x00, 0x00, 0x04, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
/* RR for order */
0x66, 0xe1, 0x2d, 0x94, 0xf3, 0xd9, 0x56, 0x20, 0x28, 0x45, 0xb2, 0x39,
0x2b, 0x6b, 0xec, 0x59, 0x46, 0x99, 0x79, 0x9c, 0x49, 0xbd, 0x6f, 0xa6,
0x83, 0x24, 0x4c, 0x95, 0xbe, 0x79, 0xee, 0xa2
}
};

Expand Down Expand Up @@ -3168,6 +3176,24 @@ static EC_GROUP *ec_group_new_from_data(OSSL_LIB_CTX *libctx,
seed_len = data->seed_len;
param_len = data->param_len;
params = (const unsigned char *)(data + 1); /* skip header */

if (curve.meth != NULL) {
meth = curve.meth();
if ((group = ossl_ec_group_new_ex(libctx, propq, meth)) == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB);
goto err;
}
if (group->meth->group_full_init != NULL) {
if (!group->meth->group_full_init(group, params)){
ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB);
goto err;
}
EC_GROUP_set_curve_name(group, curve.nid);
BN_CTX_free(ctx);
return group;
}
}

params += seed_len; /* skip seed */

if ((p = BN_bin2bn(params + 0 * param_len, param_len, NULL)) == NULL
Expand All @@ -3177,10 +3203,8 @@ static EC_GROUP *ec_group_new_from_data(OSSL_LIB_CTX *libctx,
goto err;
}

if (curve.meth != 0) {
meth = curve.meth();
if (((group = ossl_ec_group_new_ex(libctx, propq, meth)) == NULL) ||
(!(group->meth->group_set_curve(group, p, a, b, ctx)))) {
if (group != NULL) {
if (group->meth->group_set_curve(group, p, a, b, ctx) == 0) {
ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB);
goto err;
}
Expand Down
1 change: 1 addition & 0 deletions crypto/ec/ec_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ struct ec_method_st {
int (*ladder_post)(const EC_GROUP *group,
EC_POINT *r, EC_POINT *s,
EC_POINT *p, BN_CTX *ctx);
int (*group_full_init)(EC_GROUP *group, const unsigned char *data);
};

/*
Expand Down
128 changes: 127 additions & 1 deletion crypto/ec/ecp_nistz256.c
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,131 @@ static int ecp_nistz256_inv_mod_ord(const EC_GROUP *group, BIGNUM *r,
# define ecp_nistz256_inv_mod_ord NULL
#endif

static int ecp_nistz256group_full_init(EC_GROUP *group,
const unsigned char *params) {
BN_CTX *ctx = NULL;
BN_MONT_CTX *mont = NULL, *ordmont = NULL;
const int param_len = 32;
const int seed_len = 20;
int ok = 0;
uint32_t hi_order_n = 0xccd1c8aa;
uint32_t lo_order_n = 0xee00bc4f;
BIGNUM *p = NULL, *a = NULL, *b = NULL, *x = NULL, *y = NULL, *one = NULL,
*order = NULL;
EC_POINT *P = NULL;

if ((ctx = BN_CTX_new_ex(group->libctx)) == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
return 0;
}

if (!EC_GROUP_set_seed(group, params, seed_len)) {
ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB);
goto err;
}
params += seed_len;

if ((p = BN_bin2bn(params + 0 * param_len, param_len, NULL)) == NULL
|| (a = BN_bin2bn(params + 1 * param_len, param_len, NULL)) == NULL
|| (b = BN_bin2bn(params + 2 * param_len, param_len, NULL)) == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_BN_LIB);
goto err;
}

/*
* Set up curve params and montgomery for field
* Start by setting up montgomery and one
*/
mont = BN_MONT_CTX_new();
if (mont == NULL)
goto err;

if (!ossl_bn_mont_ctx_set(mont, p, 256, params + 6 * param_len, param_len,
1, 0))
goto err;

one = BN_new();
if (one == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_BN_LIB);
goto err;
}
if (!BN_to_montgomery(one, BN_value_one(), mont, ctx)){
ERR_raise(ERR_LIB_EC, ERR_R_BN_LIB);
goto err;
}
group->field_data1 = mont;
mont = NULL;
group->field_data2 = one;
one = NULL;

if (!ossl_ec_GFp_simple_group_set_curve(group, p, a, b, ctx)) {
ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB);
goto err;
}

if ((P = EC_POINT_new(group)) == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB);
goto err;
}

if ((x = BN_bin2bn(params + 3 * param_len, param_len, NULL)) == NULL
|| (y = BN_bin2bn(params + 4 * param_len, param_len, NULL)) == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_BN_LIB);
goto err;
}
if (!EC_POINT_set_affine_coordinates(group, P, x, y, ctx)) {
ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB);
goto err;
}
if ((order = BN_bin2bn(params + 5 * param_len, param_len, NULL)) == NULL
|| !BN_set_word(x, (BN_ULONG)1)) { // cofactor is 1
ERR_raise(ERR_LIB_EC, ERR_R_BN_LIB);
goto err;
}

/*
* Set up generator and order and montgomery data
*/
group->generator = EC_POINT_new(group);
if (group->generator == NULL){
ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB);
goto err;
}
if (!EC_POINT_copy(group->generator, P))
goto err;
if (!BN_copy(group->order, order))
goto err;
if (!BN_set_word(group->cofactor, 1))
goto err;

ordmont = BN_MONT_CTX_new();
if (ordmont == NULL)
goto err;
if (!ossl_bn_mont_ctx_set(ordmont, order, 256, params + 7 * param_len,
param_len, lo_order_n, hi_order_n))
goto err;

group->mont_data = ordmont;
ordmont = NULL;

ok = 1;

err:
EC_POINT_free(P);
BN_CTX_free(ctx);
BN_MONT_CTX_free(mont);
BN_MONT_CTX_free(ordmont);
BN_free(p);
BN_free(one);
BN_free(a);
BN_free(b);
BN_free(order);
BN_free(x);
BN_free(y);

return ok;
}

const EC_METHOD *EC_GFp_nistz256_method(void)
{
static const EC_METHOD ret = {
Expand Down Expand Up @@ -1501,7 +1626,8 @@ const EC_METHOD *EC_GFp_nistz256_method(void)
0, /* blind_coordinates */
0, /* ladder_pre */
0, /* ladder_step */
0 /* ladder_post */
0, /* ladder_post */
ecp_nistz256group_full_init
};

return &ret;
Expand Down
6 changes: 6 additions & 0 deletions include/crypto/bn.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,9 @@ int s390x_crt(BIGNUM *r, const BIGNUM *i, const BIGNUM *p, const BIGNUM *q,
const BIGNUM *dmp, const BIGNUM *dmq, const BIGNUM *iqmp);

#endif

int ossl_bn_mont_ctx_set(BN_MONT_CTX *ctx, const BIGNUM *modulus, int ri,
const unsigned char *rr, size_t rrlen,
uint32_t nlo, uint32_t nhi);

int ossl_bn_mont_ctx_eq(const BN_MONT_CTX *m1, const BN_MONT_CTX *m2);
64 changes: 64 additions & 0 deletions test/ec_internal_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "testutil.h"
#include <openssl/ec.h>
#include "ec_local.h"
#include <crypto/bn.h>
#include <openssl/objects.h>

static size_t crv_len = 0;
Expand Down Expand Up @@ -433,6 +434,68 @@ int ecpkparams_i2d2i_test(int n)
return testresult;
}


static int check_bn_mont_ctx(BN_MONT_CTX *mont, BIGNUM *mod, BN_CTX *ctx)
{
int ret = 0;
BN_MONT_CTX *regenerated = BN_MONT_CTX_new();

if (!TEST_ptr(regenerated))
return ret;
if (!TEST_ptr(mont))
goto err;

if (!TEST_true(BN_MONT_CTX_set(regenerated, mod, ctx)))
goto err;

if (!TEST_true(ossl_bn_mont_ctx_eq(regenerated, mont)))
goto err;

ret = 1;

err:
BN_MONT_CTX_free(regenerated);
return ret;
}

static int montgomery_correctness_test(EC_GROUP *group)
{
int ret = 0;
BN_CTX *ctx = NULL;

ctx = BN_CTX_new();
if (!TEST_ptr(ctx))
return ret;
if (!TEST_true(check_bn_mont_ctx(group->mont_data, group->order, ctx))) {
TEST_error("group order issue");
goto err;
}
if (group->field_data1 != NULL) {
if (!TEST_true(check_bn_mont_ctx(group->field_data1, group->field, ctx)))
goto err;
}
ret = 1;
err:
BN_CTX_free(ctx);
return ret;
}

static int named_group_creation_test(void)
{
int ret = 0;
EC_GROUP *group = NULL;

if (!TEST_ptr(group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1))
|| !TEST_true(montgomery_correctness_test(group)))
goto err;

ret = 1;

err:
EC_GROUP_free(group);
return ret;
}

int setup_tests(void)
{
crv_len = EC_get_builtin_curves(NULL, 0);
Expand All @@ -452,6 +515,7 @@ int setup_tests(void)
ADD_TEST(set_private_key);
ADD_TEST(decoded_flag_test);
ADD_ALL_TESTS(ecpkparams_i2d2i_test, crv_len);
ADD_TEST(named_group_creation_test);

return 1;
}
Expand Down

0 comments on commit 23b6ef4

Please sign in to comment.