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 <bangwangnj@163.com>

Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25726)
This commit is contained in:
wangcheng 2024-10-26 17:10:38 +08:00 committed by Tomas Mraz
parent 9ca66fc273
commit 4c41aa4b33
8 changed files with 116 additions and 15 deletions

View File

@ -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)

View File

@ -20,6 +20,7 @@ provider-digest - The digest library E<lt>-E<gt> 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<openssl-core_dispatch.h(7)>, 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<dctx> parameter and return the duplicate copy.
OSSL_FUNC_digest_copyctx() should copy the provider side digest context in the
I<vinctx> parameter to the I<voutctx> 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<life_cycle-digest(7)>, L<EVP_DigestInit(3)>
=head1 HISTORY
The provider DIGEST interface was introduced in OpenSSL 3.0.
OSSL_FUNC_digest_copyctx() was added in 3.5 version.
=head1 COPYRIGHT

View File

@ -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;

View File

@ -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,

View File

@ -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}, \

View File

@ -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;

View File

@ -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 \

View File

@ -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);