Skip to content

Commit 7bc40a5

Browse files
authored
Improve OpenSSL digest performance
In OpenSSL 1.x, hash algorithm functions like EVP_sha256 are basically static variable lookups to a function table, making them fairly efficient. OpenSSL 3 however, has a different model for this. OpenSSL 3 instead has a fetch mechanism using EVP_fetch which is the preferred mechanism for "get me an EVP_MD". This is called an explicit fetch. For compatibility, the old OpenSSL 1.x functions were kept, however instead of EVP_sha256 being a simple "get me this variable's value" it returns a shim that does EVP_sha256 for you, ever time it is used. This is called an implicit fetch. The OpenSSL documentation recommends using explicit fetching, and memoizing the value yourself. https://docs.openssl.org/master/man7/ossl-guide-libcrypto-introduction/#performance This has worthwhile performance benefits. For "single block" inputs in to the hash algorithm, this reduces the call time by 200-300ns. That can be anywhere from a 25% to 33% reduction for empty and a couple of blocks of input data. Even though we memoize the value over on the managed side in the HashAlgorithmDispenser, this memorization only really avoids the p/invoke boundary. What is actually getting memoized is a lookup function (more or less)
1 parent 8cc92aa commit 7bc40a5

File tree

1 file changed

+57
-115
lines changed
  • src/native/libs/System.Security.Cryptography.Native

1 file changed

+57
-115
lines changed

src/native/libs/System.Security.Cryptography.Native/pal_evp.c

Lines changed: 57 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,55 @@
1010

1111
#define SUCCESS 1
1212

13-
static const EVP_MD* g_evpFetchMd5 = NULL;
14-
static pthread_once_t g_evpFetch = PTHREAD_ONCE_INIT;
15-
16-
static void EnsureFetchEvpMdAlgorithms(void)
17-
{
18-
// This is called from a pthread_once - this method should not be called directly.
19-
2013
#ifdef NEED_OPENSSL_3_0
21-
if (API_EXISTS(EVP_MD_fetch))
22-
{
23-
ERR_clear_error();
24-
25-
// Try to fetch an MD5 implementation that will work regardless if
26-
// FIPS is enforced or not.
27-
g_evpFetchMd5 = EVP_MD_fetch(NULL, "MD5", "-fips");
14+
#define BUILD_MD_FETCH(export, fn, name, query) \
15+
static const EVP_MD* g_evpFetch##export = NULL; \
16+
static pthread_once_t g_evpFetchInit##export = PTHREAD_ONCE_INIT; \
17+
static void EnsureFetchEvpMd##export(void) \
18+
{ \
19+
if (API_EXISTS(EVP_MD_fetch)) \
20+
{ \
21+
ERR_clear_error(); \
22+
g_evpFetch##export = EVP_MD_fetch(NULL, name, query); \
23+
} \
24+
\
25+
if (g_evpFetch##export == NULL) \
26+
{ \
27+
g_evpFetch##export = fn(); \
28+
} \
29+
} \
30+
\
31+
const EVP_MD* export(void) \
32+
{ \
33+
pthread_once(&g_evpFetchInit##export, EnsureFetchEvpMd##export); \
34+
return g_evpFetch##export; \
2835
}
36+
#define BUILD_MD_FETCH_LIGHTUP_SHA3(...) BUILD_MD_FETCH(__VA_ARGS__)
37+
#else
38+
#define BUILD_MD_FETCH(export, fn, name, query) \
39+
const EVP_MD* export(void) \
40+
{ \
41+
return fn(); \
42+
}
43+
#if HAVE_OPENSSL_SHA3
44+
#define BUILD_MD_FETCH_LIGHTUP_SHA3(export, fn, name, query) \
45+
const EVP_MD* export(void) \
46+
{ \
47+
if (API_EXISTS(fn)) \
48+
{ \
49+
return fn(); \
50+
} \
51+
\
52+
return NULL; \
53+
}
54+
#else
55+
const EVP_MD* export(void) \
56+
{ \
57+
return NULL; \
58+
}
59+
#endif
2960
#endif
3061

31-
// No error queue impact.
32-
// If EVP_MD_fetch is unavailable, use the implicit loader. If it failed, use the implicit loader as a last resort.
33-
if (g_evpFetchMd5 == NULL)
34-
{
35-
g_evpFetchMd5 = EVP_md5();
36-
}
37-
}
3862

3963
EVP_MD_CTX* CryptoNative_EvpMdCtxCreate(const EVP_MD* type)
4064
{
@@ -291,100 +315,18 @@ int32_t CryptoNative_EvpMdSize(const EVP_MD* md)
291315
return EVP_MD_get_size(md);
292316
}
293317

294-
const EVP_MD* CryptoNative_EvpMd5(void)
295-
{
296-
pthread_once(&g_evpFetch, EnsureFetchEvpMdAlgorithms);
297-
return g_evpFetchMd5;
298-
}
299-
300-
const EVP_MD* CryptoNative_EvpSha1(void)
301-
{
302-
// No error queue impact.
303-
return EVP_sha1();
304-
}
305-
306-
const EVP_MD* CryptoNative_EvpSha256(void)
307-
{
308-
// No error queue impact.
309-
return EVP_sha256();
310-
}
311-
312-
const EVP_MD* CryptoNative_EvpSha384(void)
313-
{
314-
// No error queue impact.
315-
return EVP_sha384();
316-
}
317-
318-
const EVP_MD* CryptoNative_EvpSha512(void)
319-
{
320-
// No error queue impact.
321-
return EVP_sha512();
322-
}
323-
324-
const EVP_MD* CryptoNative_EvpSha3_256(void)
325-
{
326-
// No error queue impact.
327-
#if HAVE_OPENSSL_SHA3
328-
if (API_EXISTS(EVP_sha3_256))
329-
{
330-
return EVP_sha3_256();
331-
}
332-
#endif
333-
334-
return NULL;
335-
}
336-
337-
const EVP_MD* CryptoNative_EvpSha3_384(void)
338-
{
339-
// No error queue impact.
340-
#if HAVE_OPENSSL_SHA3
341-
if (API_EXISTS(EVP_sha3_384))
342-
{
343-
return EVP_sha3_384();
344-
}
345-
#endif
346-
347-
return NULL;
348-
}
349-
350-
const EVP_MD* CryptoNative_EvpSha3_512(void)
351-
{
352-
// No error queue impact.
353-
#if HAVE_OPENSSL_SHA3
354-
if (API_EXISTS(EVP_sha3_512))
355-
{
356-
return EVP_sha3_512();
357-
}
358-
#endif
359-
360-
return NULL;
361-
}
362-
363-
const EVP_MD* CryptoNative_EvpShake128(void)
364-
{
365-
// No error queue impact.
366-
#if HAVE_OPENSSL_SHA3
367-
if (API_EXISTS(EVP_shake128))
368-
{
369-
return EVP_shake128();
370-
}
371-
#endif
372-
373-
return NULL;
374-
}
375-
376-
const EVP_MD* CryptoNative_EvpShake256(void)
377-
{
378-
// No error queue impact.
379-
#if HAVE_OPENSSL_SHA3
380-
if (API_EXISTS(EVP_shake256))
381-
{
382-
return EVP_shake256();
383-
}
384-
#endif
385-
386-
return NULL;
387-
}
318+
// MD5 should use a non-FIPS implementation if it is available. We should not fail
319+
// to fetch MD5 even on a FIPS enforced system.
320+
BUILD_MD_FETCH(CryptoNative_EvpMd5, EVP_md5, "MD5", "-fips")
321+
BUILD_MD_FETCH(CryptoNative_EvpSha1, EVP_sha1, "SHA1", NULL)
322+
BUILD_MD_FETCH(CryptoNative_EvpSha256, EVP_sha256, "SHA256", NULL)
323+
BUILD_MD_FETCH(CryptoNative_EvpSha384, EVP_sha384, "SHA384", NULL)
324+
BUILD_MD_FETCH(CryptoNative_EvpSha512, EVP_sha512, "SHA512", NULL)
325+
BUILD_MD_FETCH_LIGHTUP_SHA3(CryptoNative_EvpSha3_256, EVP_sha3_256, "SHA3-256", NULL)
326+
BUILD_MD_FETCH_LIGHTUP_SHA3(CryptoNative_EvpSha3_384, EVP_sha3_384, "SHA3-384", NULL)
327+
BUILD_MD_FETCH_LIGHTUP_SHA3(CryptoNative_EvpSha3_512, EVP_sha3_512, "SHA3-512", NULL)
328+
BUILD_MD_FETCH_LIGHTUP_SHA3(CryptoNative_EvpShake128, EVP_shake128, "SHAKE-128", NULL)
329+
BUILD_MD_FETCH_LIGHTUP_SHA3(CryptoNative_EvpShake256, EVP_shake256, "SHAKE-256", NULL)
388330

389331
int32_t CryptoNative_GetMaxMdSize(void)
390332
{

0 commit comments

Comments
 (0)