From 4c41aa4b338ca181a394483c8bb6aeb6366c6f96 Mon Sep 17 00:00:00 2001 From: wangcheng Date: Sat, 26 Oct 2024 17:10:38 +0800 Subject: [PATCH] Add CTX copy function for EVP_MD to optimize the performance of EVP_MD_CTX_copy_ex. 1. Add OSSL_FUNC_digest_copyctx_fn function for EVP_MD, which is used to copy algctx from the old EVP_MD_CTX to the new one. 2. Add implementation of OSSL_FUNC_digest_copyctx_fn function for default providers. 3. Modify EVP_MD_CTX_copy_ex: When the fetched digest is the same in in and out contexts, use the copy function to copy the members in EVP_MD_CTX if the OSSL_FUNC_digest_copyctx_fn function exists. Otherwise, use the previous method to copy. 4. Add documentation for OSSL_FUNC_digest_copyctx function in doc/man7/provider-digest.pod. 5. Add testcase. Fixes #25703 Signed-off-by: wangcheng Reviewed-by: Dmitry Belyavskiy Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/25726) --- crypto/evp/digest.c | 47 +++++++++++++------ doc/man7/provider-digest.pod | 11 +++++ include/crypto/evp.h | 1 + include/openssl/core_dispatch.h | 2 + providers/implementations/digests/sha3_prov.c | 10 ++++ .../include/prov/digestcommon.h | 7 +++ test/evp_extra_test2.c | 43 +++++++++++++++++ 7 files changed, 106 insertions(+), 15 deletions(-) diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index eefed52..f5c44b1 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -548,23 +548,35 @@ int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) return 0; } - evp_md_ctx_reset_ex(out, 1); - digest_change = (out->fetched_digest != in->fetched_digest); - if (digest_change && out->fetched_digest != NULL) - EVP_MD_free(out->fetched_digest); - *out = *in; - /* NULL out pointers in case of error */ - out->pctx = NULL; - out->algctx = NULL; + if (out->digest == in->digest && in->digest->copyctx != NULL) { - if (digest_change && in->fetched_digest != NULL) - EVP_MD_up_ref(in->fetched_digest); + in->digest->copyctx(out->algctx, in->algctx); - if (in->algctx != NULL) { - out->algctx = in->digest->dupctx(in->algctx); - if (out->algctx == NULL) { - ERR_raise(ERR_LIB_EVP, EVP_R_NOT_ABLE_TO_COPY_CTX); - return 0; + EVP_PKEY_CTX_free(out->pctx); + out->pctx = NULL; + cleanup_old_md_data(out, 0); + + out->flags = in->flags; + out->update = in->update; + } else { + evp_md_ctx_reset_ex(out, 1); + digest_change = (out->fetched_digest != in->fetched_digest); + if (digest_change && out->fetched_digest != NULL) + EVP_MD_free(out->fetched_digest); + *out = *in; + /* NULL out pointers in case of error */ + out->pctx = NULL; + out->algctx = NULL; + + if (digest_change && in->fetched_digest != NULL) + EVP_MD_up_ref(in->fetched_digest); + + if (in->algctx != NULL) { + out->algctx = in->digest->dupctx(in->algctx); + if (out->algctx == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_NOT_ABLE_TO_COPY_CTX); + return 0; + } } } @@ -1029,6 +1041,11 @@ static void *evp_md_from_algorithm(int name_id, md->gettable_ctx_params = OSSL_FUNC_digest_gettable_ctx_params(fns); break; + case OSSL_FUNC_DIGEST_COPYCTX: + if (md->copyctx == NULL) + md->copyctx = + OSSL_FUNC_digest_copyctx(fns); + break; } } if ((fncnt != 0 && fncnt != 5) diff --git a/doc/man7/provider-digest.pod b/doc/man7/provider-digest.pod index cac53ac..0ed0d3b 100644 --- a/doc/man7/provider-digest.pod +++ b/doc/man7/provider-digest.pod @@ -20,6 +20,7 @@ provider-digest - The digest library E-E provider functions void *OSSL_FUNC_digest_newctx(void *provctx); void OSSL_FUNC_digest_freectx(void *dctx); void *OSSL_FUNC_digest_dupctx(void *dctx); + void OSSL_FUNC_digest_copyctx(void *voutctx, void *vinctx); /* Digest generation */ int OSSL_FUNC_digest_init(void *dctx, const OSSL_PARAM params[]); @@ -76,6 +77,7 @@ macros in L, as follows: OSSL_FUNC_digest_newctx OSSL_FUNC_DIGEST_NEWCTX OSSL_FUNC_digest_freectx OSSL_FUNC_DIGEST_FREECTX OSSL_FUNC_digest_dupctx OSSL_FUNC_DIGEST_DUPCTX + OSSL_FUNC_digest_copyctx OSSL_FUNC_DIGEST_COPYCTX OSSL_FUNC_digest_init OSSL_FUNC_DIGEST_INIT OSSL_FUNC_digest_update OSSL_FUNC_DIGEST_UPDATE @@ -111,6 +113,14 @@ This function should free any resources associated with that context. OSSL_FUNC_digest_dupctx() should duplicate the provider side digest context in the I parameter and return the duplicate copy. +OSSL_FUNC_digest_copyctx() should copy the provider side digest context in the +I parameter to the I parameter which is the another provider side +context. +The OSSL_FUNC_digest_copyctx function is used in the EVP_MD_CTX_copy_ex function to +speed up HMAC operations in the PBKDF2. +This function is optional, and dupctx will be used if there is no EVP_MD_CTX_copy_ex +function. + =head2 Digest Generation Functions OSSL_FUNC_digest_init() initialises a digest operation given a newly created @@ -274,6 +284,7 @@ L, L =head1 HISTORY The provider DIGEST interface was introduced in OpenSSL 3.0. +OSSL_FUNC_digest_copyctx() was added in 3.5 version. =head1 COPYRIGHT diff --git a/include/crypto/evp.h b/include/crypto/evp.h index e70d8e9..f17b569 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -277,6 +277,7 @@ struct evp_md_st { OSSL_FUNC_digest_final_fn *dfinal; OSSL_FUNC_digest_digest_fn *digest; OSSL_FUNC_digest_freectx_fn *freectx; + OSSL_FUNC_digest_copyctx_fn *copyctx; OSSL_FUNC_digest_dupctx_fn *dupctx; OSSL_FUNC_digest_get_params_fn *get_params; OSSL_FUNC_digest_set_ctx_params_fn *set_ctx_params; diff --git a/include/openssl/core_dispatch.h b/include/openssl/core_dispatch.h index 99fcda0..e6fa38d 100644 --- a/include/openssl/core_dispatch.h +++ b/include/openssl/core_dispatch.h @@ -283,6 +283,7 @@ OSSL_CORE_MAKE_FUNC(int, provider_self_test, (void *provctx)) # define OSSL_FUNC_DIGEST_GETTABLE_PARAMS 11 # define OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS 12 # define OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS 13 +# define OSSL_FUNC_DIGEST_COPYCTX 14 OSSL_CORE_MAKE_FUNC(void *, digest_newctx, (void *provctx)) OSSL_CORE_MAKE_FUNC(int, digest_init, (void *dctx, const OSSL_PARAM params[])) @@ -297,6 +298,7 @@ OSSL_CORE_MAKE_FUNC(int, digest_digest, OSSL_CORE_MAKE_FUNC(void, digest_freectx, (void *dctx)) OSSL_CORE_MAKE_FUNC(void *, digest_dupctx, (void *dctx)) +OSSL_CORE_MAKE_FUNC(void, digest_copyctx, (void *outctx, void *inctx)) OSSL_CORE_MAKE_FUNC(int, digest_get_params, (OSSL_PARAM params[])) OSSL_CORE_MAKE_FUNC(int, digest_set_ctx_params, diff --git a/providers/implementations/digests/sha3_prov.c b/providers/implementations/digests/sha3_prov.c index 168825d..3929f97 100644 --- a/providers/implementations/digests/sha3_prov.c +++ b/providers/implementations/digests/sha3_prov.c @@ -32,6 +32,7 @@ static OSSL_FUNC_digest_init_fn keccak_init_params; static OSSL_FUNC_digest_update_fn keccak_update; static OSSL_FUNC_digest_final_fn keccak_final; static OSSL_FUNC_digest_freectx_fn keccak_freectx; +static OSSL_FUNC_digest_copyctx_fn keccak_copyctx; static OSSL_FUNC_digest_dupctx_fn keccak_dupctx; static OSSL_FUNC_digest_set_ctx_params_fn shake_set_ctx_params; static OSSL_FUNC_digest_settable_ctx_params_fn shake_settable_ctx_params; @@ -236,6 +237,7 @@ const OSSL_DISPATCH ossl_##name##_functions[] = { \ { OSSL_FUNC_DIGEST_FINAL, (void (*)(void))keccak_final }, \ { OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))keccak_freectx }, \ { OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))keccak_dupctx }, \ + { OSSL_FUNC_DIGEST_COPYCTX, (void (*)(void))keccak_copyctx }, \ PROV_DISPATCH_FUNC_DIGEST_GET_PARAMS(name) #define PROV_FUNC_SHA3_DIGEST(name, bitlen, blksize, dgstsize, flags) \ @@ -258,6 +260,14 @@ static void keccak_freectx(void *vctx) OPENSSL_clear_free(ctx, sizeof(*ctx)); } +static void keccak_copyctx(void *voutctx, void *vinctx) +{ + KECCAK1600_CTX *outctx = (KECCAK1600_CTX *)voutctx; + KECCAK1600_CTX *inctx = (KECCAK1600_CTX *)vinctx; + + *outctx = *inctx; +} + static void *keccak_dupctx(void *ctx) { KECCAK1600_CTX *in = (KECCAK1600_CTX *)ctx; diff --git a/providers/implementations/include/prov/digestcommon.h b/providers/implementations/include/prov/digestcommon.h index abdb8bb..332d473 100644 --- a/providers/implementations/include/prov/digestcommon.h +++ b/providers/implementations/include/prov/digestcommon.h @@ -70,6 +70,12 @@ static void *name##_dupctx(void *ctx) \ *ret = *in; \ return ret; \ } \ +static void name##_copyctx(void *voutctx, void *vinctx) \ +{ \ + CTX *outctx = (CTX *)voutctx; \ + CTX *inctx = (CTX *)vinctx; \ + *outctx = *inctx; \ +} \ PROV_FUNC_DIGEST_FINAL(name, dgstsize, fin) \ PROV_FUNC_DIGEST_GET_PARAM(name, blksize, dgstsize, flags) \ const OSSL_DISPATCH ossl_##name##_functions[] = { \ @@ -78,6 +84,7 @@ const OSSL_DISPATCH ossl_##name##_functions[] = { \ { OSSL_FUNC_DIGEST_FINAL, (void (*)(void))name##_internal_final }, \ { OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))name##_freectx }, \ { OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))name##_dupctx }, \ + { OSSL_FUNC_DIGEST_COPYCTX, (void (*)(void))name##_copyctx }, \ PROV_DISPATCH_FUNC_DIGEST_GET_PARAMS(name) # define PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END \ diff --git a/test/evp_extra_test2.c b/test/evp_extra_test2.c index 68329b0..be7db00 100644 --- a/test/evp_extra_test2.c +++ b/test/evp_extra_test2.c @@ -27,6 +27,8 @@ #include "testutil.h" #include "internal/nelem.h" +#include "crypto/evp.h" +#include "../crypto/evp/evp_local.h" static OSSL_LIB_CTX *mainctx = NULL; static OSSL_PROVIDER *nullprov = NULL; @@ -1193,6 +1195,46 @@ static int test_evp_md_ctx_copy(void) return ret; } +static int test_evp_md_ctx_copy2(void) +{ + int ret = 0; + EVP_MD *md = NULL; + OSSL_LIB_CTX *ctx = NULL; + EVP_MD_CTX *inctx = NULL, *outctx = NULL; + void *origin_algctx = NULL; + + if (!TEST_ptr(ctx = OSSL_LIB_CTX_new()) + || !TEST_ptr(md = EVP_MD_fetch(ctx, "sha256", NULL))) + goto end; + + inctx = EVP_MD_CTX_new(); + outctx = EVP_MD_CTX_new(); + + if (!TEST_ptr(inctx) || !TEST_ptr(outctx)) + goto end; + + /* init inctx and outctx, now the contexts are from same providers */ + if (!TEST_true(EVP_DigestInit_ex2(inctx, md, NULL))) + goto end; + if (!TEST_true(EVP_DigestInit_ex2(outctx, md, NULL))) + goto end; + + /* + * Test the EVP_MD_CTX_copy_ex function. After copying, + * outctx->algctx should be the same as the original. + */ + origin_algctx = outctx->algctx; + ret = TEST_true(EVP_MD_CTX_copy_ex(outctx, inctx)) + && TEST_true(outctx->algctx == origin_algctx); + +end: + EVP_MD_free(md); + EVP_MD_CTX_free(inctx); + EVP_MD_CTX_free(outctx); + OSSL_LIB_CTX_free(ctx); + return ret; +} + #if !defined OPENSSL_NO_DES && !defined OPENSSL_NO_MD5 static int test_evp_pbe_alg_add(void) { @@ -1262,6 +1304,7 @@ int setup_tests(void) ADD_ALL_TESTS(test_PEM_read_bio_negative_wrong_password, 2); ADD_TEST(test_rsa_pss_sign); ADD_TEST(test_evp_md_ctx_copy); + ADD_TEST(test_evp_md_ctx_copy2); ADD_ALL_TESTS(test_provider_unload_effective, 2); #if !defined OPENSSL_NO_DES && !defined OPENSSL_NO_MD5 ADD_TEST(test_evp_pbe_alg_add); -- 2.33.0