]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Allow group methods to customize initialization for speed
authorWatson Ladd <watsonbladd@gmail.com>
Tue, 21 Nov 2023 17:59:05 +0000 (12:59 -0500)
committerTomas Mraz <tomas@openssl.org>
Wed, 5 Jun 2024 09:11:52 +0000 (11:11 +0200)
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 <shane.lontis@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22746)

CHANGES.md
crypto/bn/bn_mont.c
crypto/ec/ec_curve.c
crypto/ec/ec_local.h
crypto/ec/ecp_nistz256.c
include/crypto/bn.h
test/ec_internal_test.c

index 49dbe585026305d795030de4ef4223d1496d67da..9918e10c972bae34b04dbf3e89fa83111c1b63b3 100644 (file)
@@ -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
 -----------
 
index 8b4c7900ad47fa7ac1c072faa25203b2c6c9b2fc..7cd16c66ee8aff859e4246ee493ae821f55ff004 100644 (file)
@@ -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;
+}
index d703d16b3cae81465b44477674174da66e7b4529..75feaa79d499cfb6015edcbd2bd124b3a48d2479 100644 (file)
@@ -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;
         }
index 2814d8739438f86894588290d2841ad34a9f5145..a041db9c1335cb1da54d797ca44e86d919d12766 100644 (file)
@@ -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);
 };
 
 /*
index 5760639a2ee2405b2413b672db347e898a426d4d..765c344bec70a6ff698b88a42a149c05a58ce1a8 100644 (file)
@@ -1445,6 +1445,131 @@ err:
 # 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;
index 9a988a467de27ec02a1725a8f4c193f4e44a7072..128cae3bf8558fab4ddaaf865a823953b5ffcd22 100644 (file)
@@ -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);
index 5076f9894d5b8ab76bdc8311d6bfc326394d0202..8e99f6210507d699d6bea2a2a2dacbcf68b0d151 100644 (file)
@@ -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;
@@ -433,6 +434,68 @@ end:
     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;
 }