diff --git a/CHANGES.md b/CHANGES.md index 49dbe58502630..9918e10c972ba 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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 ----------- diff --git a/crypto/bn/bn_mont.c b/crypto/bn/bn_mont.c index 8b4c7900ad47f..7cd16c66ee8af 100644 --- a/crypto/bn/bn_mont.c +++ b/crypto/bn/bn_mont.c @@ -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; +} diff --git a/crypto/ec/ec_curve.c b/crypto/ec/ec_curve.c index d703d16b3cae8..75feaa79d499c 100644 --- a/crypto/ec/ec_curve.c +++ b/crypto/ec/ec_curve.c @@ -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 @@ -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 } }; @@ -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 @@ -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; } diff --git a/crypto/ec/ec_local.h b/crypto/ec/ec_local.h index 2814d8739438f..a041db9c1335c 100644 --- a/crypto/ec/ec_local.h +++ b/crypto/ec/ec_local.h @@ -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); }; /* diff --git a/crypto/ec/ecp_nistz256.c b/crypto/ec/ecp_nistz256.c index 5760639a2ee24..765c344bec70a 100644 --- a/crypto/ec/ecp_nistz256.c +++ b/crypto/ec/ecp_nistz256.c @@ -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 = { @@ -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; diff --git a/include/crypto/bn.h b/include/crypto/bn.h index 9a988a467de27..128cae3bf8558 100644 --- a/include/crypto/bn.h +++ b/include/crypto/bn.h @@ -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); diff --git a/test/ec_internal_test.c b/test/ec_internal_test.c index 5076f9894d5b8..8e99f6210507d 100644 --- a/test/ec_internal_test.c +++ b/test/ec_internal_test.c @@ -16,6 +16,7 @@ #include "testutil.h" #include #include "ec_local.h" +#include #include static size_t crv_len = 0; @@ -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); @@ -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; }