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 + .../implementations/digests/blake2_prov.c | 10 ++++ providers/implementations/digests/sha3_prov.c | 10 ++++ .../include/prov/digestcommon.h | 7 +++ test/evp_extra_test2.c | 43 +++++++++++++++++ 8 files changed, 116 insertions(+), 15 deletions(-) diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index 0fdd545306..6aa175fb3f 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -617,23 +617,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; + } } } @@ -1103,6 +1115,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 && fncnt != 6) diff --git a/doc/man7/provider-digest.pod b/doc/man7/provider-digest.pod index d23da59e1a..751321c84b 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 @@ -273,6 +283,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 72d9995e8f..4775c05ecd 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -285,6 +285,7 @@ struct evp_md_st { OSSL_FUNC_digest_squeeze_fn *dsqueeze; 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 03838ddd0e..9243aa9ec1 100644 --- a/include/openssl/core_dispatch.h +++ b/include/openssl/core_dispatch.h @@ -305,6 +305,7 @@ OSSL_CORE_MAKE_FUNC(int, provider_self_test, (void *provctx)) # define OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS 12 # define OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS 13 # define OSSL_FUNC_DIGEST_SQUEEZE 14 +# define OSSL_FUNC_DIGEST_COPYCTX 15 OSSL_CORE_MAKE_FUNC(void *, digest_newctx, (void *provctx)) OSSL_CORE_MAKE_FUNC(int, digest_init, (void *dctx, const OSSL_PARAM params[])) @@ -322,6 +323,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/blake2_prov.c b/providers/implementations/digests/blake2_prov.c index be1ceb5ed6..5495aab61a 100644 --- a/providers/implementations/digests/blake2_prov.c +++ b/providers/implementations/digests/blake2_prov.c @@ -133,6 +133,15 @@ static void *blake##variantsize##_dupctx(void *ctx) \ if (ret != NULL) \ *ret = *in; \ return ret; \ +} \ +\ +static void blake##variantsize##_copyctx(void *voutctx, void *vinctx) \ +{ \ + struct blake##variant##_md_data_st *inctx, *outctx; \ + \ + outctx = (struct blake##variant##_md_data_st *)voutctx; \ + inctx = (struct blake##variant##_md_data_st *)vinctx; \ + *outctx = *inctx; \ } \ \ static int blake##variantsize##_internal_final(void *ctx, unsigned char *out, \ @@ -169,6 +178,7 @@ const OSSL_DISPATCH ossl_blake##variantsize##_functions[] = { \ {OSSL_FUNC_DIGEST_FINAL, (void (*)(void))blake##variantsize##_internal_final}, \ {OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))blake##variantsize##_freectx}, \ {OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))blake##variantsize##_dupctx}, \ + {OSSL_FUNC_DIGEST_COPYCTX, (void (*)(void))blake##variantsize##_copyctx}, \ {OSSL_FUNC_DIGEST_GET_PARAMS, (void (*)(void))blake##variantsize##_get_params}, \ {OSSL_FUNC_DIGEST_GETTABLE_PARAMS, \ (void (*)(void))ossl_digest_default_gettable_params}, \ diff --git a/providers/implementations/digests/sha3_prov.c b/providers/implementations/digests/sha3_prov.c index 4a4386ff8b..d4f6d9797c 100644 --- a/providers/implementations/digests/sha3_prov.c +++ b/providers/implementations/digests/sha3_prov.c @@ -33,6 +33,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_squeeze_fn shake_squeeze; static OSSL_FUNC_digest_get_ctx_params_fn shake_get_ctx_params; @@ -534,6 +535,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) \ @@ -560,6 +562,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 abdb8bb2ad..332d473490 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 1665ac4273..27b5a07375 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; @@ -1298,6 +1300,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) { @@ -1391,6 +1433,7 @@ int setup_tests(void) ADD_TEST(test_rsa_pss_sign); ADD_TEST(test_evp_md_ctx_dup); 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);