Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache Digest constants #13730

Closed
wants to merge 2 commits into from

Conversation

mattcaswell
Copy link
Member

EVP_CIPHER already caches certain constants so that we don't have to query the provider every time. We do the same thing with EVP_MD constants. Without this we can get performance issues, e.g. running "speed" with small blocks of data to digest can spend a long time in EVP_MD_size(), which should be quick.

Partialy fixes #13578

I've also refactored the EVP_CIPHER constant caching to move it into evp_cipher_from_dispatch, i.e. to where the EVP_CIPHER actually gets constructed rather than where it is fetched in EVP_CIPHER_fetch. The problem with the latter is that we construct a cipher once, but we may fetch it many times. We shouldn't have to cache the constants every time we fetch.

Previously we cached the cipher constants in EVP_CIPHER_fetch(). However,
this means we do the caching every time we call that function, even if
the core has previusly fetched the cipher and cached it already. This
means we can end up re-caching the constants even though they are already
present. This also means we could be updating these constants from
multiple threads at the same time.
EVP_CIPHER already caches certain constants so that we don't have to
query the provider every time. We do the same thing with EVP_MD constants.
Without this we can get performance issues, e.g. running "speed" with
small blocks of data to digest can spend a long time in EVP_MD_size(),
which should be quick.

Partialy fixes openssl#13578
@mattcaswell
Copy link
Member Author

I've run some tests to see the effect on performance of this change. I ran both the "before" and "after" tests with #13721 applied. I'm seeing quite a lot of variability in the results from one run to the next, so my confidence the precise numbers is quite low. To try and reduce variability as much as possible I "warmed up" my machine before both the "before" and "after" tests by repeating the same speed command 5 times and discarding the results. I then ran the command again 5 times and recorded the results.

I compiled with just config --strict-warnings, and ran this speed command:

openssl speed -evp sha256 -bytes 16

The "before" output was:

sha256           38566.15k
sha256           36042.71k
sha256           42937.12k
sha256           42312.67k
sha256           44117.60k

Which gives an average of 40795.25k

The "after" output was:

sha256           46979.81k
sha256           49308.40k
sha256           48095.66k
sha256           46935.39k
sha256           46201.71k

Which gives an average of 47504.19k.

This represents a speed-up of approximately 16% (if you believe the numbers).

if (md == NULL) {
ERR_raise(ERR_LIB_EVP, EVP_R_MESSAGE_DIGEST_IS_NULL);
return -1;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we don't check for md here but check in EVP_MD_size?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same question is about EVP_MD_flags

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just reverting the implementation back to the way it used to be in 1.1.1. There, EVP_MD_size() has the check:

int EVP_MD_size(const EVP_MD *md)
{
if (!md) {
EVPerr(EVP_F_EVP_MD_SIZE, EVP_R_MESSAGE_DIGEST_IS_NULL);
return -1;
}
return md->md_size;
}

But EVP_MD_block_size and EVP_MD_flags do not:

int EVP_MD_block_size(const EVP_MD *md)
{
return md->block_size;
}

unsigned long EVP_MD_flags(const EVP_MD *md)
{
return md->flags;
}

Copy link
Member

@beldmit beldmit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@beldmit beldmit added approval: done This pull request has the required number of approvals branch: master Merge to master branch labels Dec 22, 2020
@kroeckx
Copy link
Member

kroeckx commented Dec 22, 2020

Deprecated functions like EVP_MD_meth_set_result_size() set md->md_size, which is now the cached value. Either they should actually change something in the provider, return an error, or be removed. This PR doesn't actually introduce the problem, but now seems to use existing variables that were not initialized before, but that we did read and then don't do anything with.

The functions are currently documented to just return 1.

@beldmit
Copy link
Member

beldmit commented Dec 23, 2020

I think it's a separate issue and these functions should not be applicable to provided digests.

@mattcaswell
Copy link
Member Author

I think it's a separate issue and these functions should not be applicable to provided digests.

Exactly. Those functions are deprecated for a reason. They are intended for use when you are setting up your own custom EVP_MD which you have created yourself.

Either they should actually change something in the provider, return an error, or be removed.

I disagree. They are deprecated and therefore their usage is strongly discouraged. Existing uses will not be broken. I don't see a problem here.

@openssl-machine
Copy link
Collaborator

24 hours has passed since 'approval: done' was set, but as this PR has been updated in that time the label 'approval: ready to merge' is not being automatically set. Please review the updates and set the label manually.

openssl-machine pushed a commit that referenced this pull request Dec 23, 2020
Previously we cached the cipher constants in EVP_CIPHER_fetch(). However,
this means we do the caching every time we call that function, even if
the core has previusly fetched the cipher and cached it already. This
means we can end up re-caching the constants even though they are already
present. This also means we could be updating these constants from
multiple threads at the same time.

Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
(Merged from #13730)
openssl-machine pushed a commit that referenced this pull request Dec 23, 2020
EVP_CIPHER already caches certain constants so that we don't have to
query the provider every time. We do the same thing with EVP_MD constants.
Without this we can get performance issues, e.g. running "speed" with
small blocks of data to digest can spend a long time in EVP_MD_size(),
which should be quick.

Partialy fixes #13578

Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
(Merged from #13730)
@beldmit
Copy link
Member

beldmit commented Dec 23, 2020

Merged. Thanks!

@beldmit beldmit closed this Dec 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approval: done This pull request has the required number of approvals branch: master Merge to master branch
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SHA performance test
4 participants