Merge branch '3795-speed-up-EVP_DigestInit_ex' into 'main'

Avoid implicit algorithm fetch for OpenSSL EVP_MD family

See merge request isc-projects/bind9!7375
This commit is contained in:
Ondřej Surý 2023-01-18 19:24:29 +00:00
commit 35e2842d00
11 changed files with 301 additions and 55 deletions

View file

@ -1,3 +1,9 @@
6072. [bug] Avoid the OpenSSL lock contention when initializing
Message Digest Contexts by using explicit algorithm
fetching, initializing static contexts for every
supported algorithms, and initializing the new context
by copying the static copy. [GL #3795]
6071. [func] The use of "port" when configuring query-source,
transfer-source, notify-source and parental-source
addresses has been deprecated, along with the

View file

@ -55,6 +55,9 @@ Bug Fixes
cause increased memory consumption due to delayed cleaning of view memory.
This has been fixed. :gl:`#3801`
- Improve the speed of the message digest algorithms (MD5, SHA-1,
SHA-2) and NSEC3 hashing. :gl:`#3795`
Known Issues
~~~~~~~~~~~~

View file

@ -35,4 +35,13 @@ isc_iterated_hash(unsigned char *out, const unsigned int hashalg,
const int saltlength, const unsigned char *in,
const int inlength);
/*
* Private
*/
void
isc__iterated_hash_initialize(void);
void
isc__iterated_hash_shutdown(void);
ISC_LANG_ENDDECLS

View file

@ -37,25 +37,19 @@ typedef void isc_md_t;
*/
typedef void isc_md_type_t;
#define ISC_MD_MD5 isc__md_md5()
#define ISC_MD_SHA1 isc__md_sha1()
#define ISC_MD_SHA224 isc__md_sha224()
#define ISC_MD_SHA256 isc__md_sha256()
#define ISC_MD_SHA384 isc__md_sha384()
#define ISC_MD_SHA512 isc__md_sha512()
extern const isc_md_type_t *isc__md_md5;
extern const isc_md_type_t *isc__md_sha1;
extern const isc_md_type_t *isc__md_sha224;
extern const isc_md_type_t *isc__md_sha256;
extern const isc_md_type_t *isc__md_sha384;
extern const isc_md_type_t *isc__md_sha512;
const isc_md_type_t *
isc__md_md5(void);
const isc_md_type_t *
isc__md_sha1(void);
const isc_md_type_t *
isc__md_sha224(void);
const isc_md_type_t *
isc__md_sha256(void);
const isc_md_type_t *
isc__md_sha384(void);
const isc_md_type_t *
isc__md_sha512(void);
#define ISC_MD_MD5 isc__md_md5
#define ISC_MD_SHA1 isc__md_sha1
#define ISC_MD_SHA224 isc__md_sha224
#define ISC_MD_SHA256 isc__md_sha256
#define ISC_MD_SHA384 isc__md_sha384
#define ISC_MD_SHA512 isc__md_sha512
#define ISC_MD5_DIGESTLENGTH isc_md_type_get_size(ISC_MD_MD5)
#define ISC_MD5_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_MD5)
@ -202,3 +196,13 @@ isc_md_type_get_size(const isc_md_type_t *md_type);
*/
size_t
isc_md_type_get_block_size(const isc_md_type_t *md_type);
/**
* Private
*/
void
isc__md_initialize(void);
void
isc__md_shutdown(void);

View file

@ -13,64 +13,154 @@
#include <stdio.h>
#include <openssl/opensslv.h>
#include <isc/iterated_hash.h>
#include <isc/md.h>
#include <isc/util.h>
#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
#include <openssl/sha.h>
int
isc_iterated_hash(unsigned char *out, const unsigned int hashalg,
const int iterations, const unsigned char *salt,
const int saltlength, const unsigned char *in,
const int inlength) {
isc_md_t *md;
isc_result_t result;
REQUIRE(out != NULL);
int n = 0;
unsigned int outlength = 0;
size_t len;
const unsigned char *buf;
REQUIRE(out != NULL);
SHA_CTX ctx;
if (hashalg != 1) {
return (0);
}
if ((md = isc_md_new()) == NULL) {
buf = in;
len = inlength;
do {
if (SHA1_Init(&ctx) != 1) {
return (0);
}
if (SHA1_Update(&ctx, buf, len) != 1) {
return (0);
}
if (SHA1_Update(&ctx, salt, saltlength) != 1) {
return (0);
}
if (SHA1_Final(out, &ctx) != 1) {
return (0);
}
buf = out;
len = SHA_DIGEST_LENGTH;
} while (n++ < iterations);
return (SHA_DIGEST_LENGTH);
}
void
isc__iterated_hash_initialize(void) {
/* empty */
}
void
isc__iterated_hash_shutdown(void) {
/* empty */
}
#else /* HAVE_SHA1_INIT */
#include <openssl/evp.h>
#include <isc/md.h>
static thread_local bool initialized = false;
static thread_local EVP_MD_CTX *mdctx = NULL;
static thread_local EVP_MD_CTX *basectx = NULL;
int
isc_iterated_hash(unsigned char *out, const unsigned int hashalg,
const int iterations, const unsigned char *salt,
const int saltlength, const unsigned char *in,
const int inlength) {
REQUIRE(out != NULL);
REQUIRE(mdctx != NULL);
REQUIRE(basectx != NULL);
int n = 0;
size_t len;
unsigned int outlength = 0;
const unsigned char *buf;
if (hashalg != 1) {
return (0);
}
len = inlength;
buf = in;
len = inlength;
do {
result = isc_md_init(md, ISC_MD_SHA1);
if (result != ISC_R_SUCCESS) {
goto md_fail;
if (EVP_MD_CTX_copy_ex(mdctx, basectx) != 1) {
goto fail;
}
result = isc_md_update(md, buf, len);
if (result != ISC_R_SUCCESS) {
goto md_fail;
if (EVP_DigestUpdate(mdctx, buf, len) != 1) {
goto fail;
}
result = isc_md_update(md, salt, saltlength);
if (result != ISC_R_SUCCESS) {
goto md_fail;
if (EVP_DigestUpdate(mdctx, salt, saltlength) != 1) {
goto fail;
}
result = isc_md_final(md, out, &outlength);
if (result != ISC_R_SUCCESS) {
goto md_fail;
}
result = isc_md_reset(md);
if (result != ISC_R_SUCCESS) {
goto md_fail;
if (EVP_DigestFinal_ex(mdctx, out, &outlength) != 1) {
goto fail;
}
buf = out;
len = outlength;
} while (n++ < iterations);
isc_md_free(md);
return (outlength);
md_fail:
isc_md_free(md);
fail:
return (0);
}
#undef RETERR
void
isc__iterated_hash_initialize(void) {
if (initialized) {
return;
}
basectx = EVP_MD_CTX_new();
INSIST(basectx != NULL);
mdctx = EVP_MD_CTX_new();
INSIST(mdctx != NULL);
RUNTIME_CHECK(EVP_DigestInit_ex(basectx, ISC_MD_SHA1, NULL) == 1);
initialized = true;
}
void
isc__iterated_hash_shutdown(void) {
if (!initialized) {
return;
}
REQUIRE(mdctx != NULL);
EVP_MD_CTX_free(mdctx);
mdctx = NULL;
REQUIRE(basectx != NULL);
EVP_MD_CTX_free(basectx);
basectx = NULL;
initialized = false;
}
#endif /* HAVE_SHA1_INIT */

View file

@ -14,6 +14,8 @@
/*! \file */
#include <isc/bind9.h>
#include <isc/iterated_hash.h>
#include <isc/md.h>
#include <isc/mem.h>
#include <isc/os.h>
#include <isc/tls.h>
@ -49,11 +51,15 @@ isc__initialize(void) {
isc__trampoline_initialize();
isc__uv_initialize();
isc__xml_initialize();
isc__md_initialize();
isc__iterated_hash_initialize();
(void)isc_os_ncpus();
}
void
isc__shutdown(void) {
isc__iterated_hash_shutdown();
isc__md_shutdown();
isc__xml_shutdown();
isc__uv_shutdown();
isc__trampoline_shutdown();

View file

@ -164,12 +164,56 @@ end:
return (res);
}
#define md_register_algorithm(alg) \
const isc_md_type_t *isc__md_##alg(void) { return (EVP_##alg()); }
#ifndef UNIT_TESTING
const isc_md_type_t *isc__md_md5 = NULL;
const isc_md_type_t *isc__md_sha1 = NULL;
const isc_md_type_t *isc__md_sha224 = NULL;
const isc_md_type_t *isc__md_sha256 = NULL;
const isc_md_type_t *isc__md_sha384 = NULL;
const isc_md_type_t *isc__md_sha512 = NULL;
md_register_algorithm(md5);
md_register_algorithm(sha1);
md_register_algorithm(sha224);
md_register_algorithm(sha256);
md_register_algorithm(sha384);
md_register_algorithm(sha512);
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#define md_register_algorithm(alg, algname) \
{ \
REQUIRE(isc__md_##alg == NULL); \
isc__md_##alg = EVP_MD_fetch(NULL, algname, NULL); \
RUNTIME_CHECK(isc__md_##alg != NULL); \
}
#define md_unregister_algorithm(alg) \
{ \
REQUIRE(isc__md_##alg != NULL); \
EVP_MD_free(*(isc_md_type_t **)&isc__md_##alg); \
isc__md_##alg = NULL; \
}
#else
#define md_register_algorithm(alg, algname) \
{ \
isc__md_##alg = EVP_##alg(); \
RUNTIME_CHECK(isc__md_##alg != NULL); \
}
#define md_unregister_algorithm(alg)
#endif
void
isc__md_initialize(void) {
md_register_algorithm(md5, "MD5");
md_register_algorithm(sha1, "SHA1");
md_register_algorithm(sha224, "SHA224");
md_register_algorithm(sha256, "SHA256");
md_register_algorithm(sha384, "SHA384");
md_register_algorithm(sha512, "SHA512");
}
void
isc__md_shutdown(void) {
md_unregister_algorithm(sha512);
md_unregister_algorithm(sha384);
md_unregister_algorithm(sha256);
md_unregister_algorithm(sha224);
md_unregister_algorithm(sha1);
md_unregister_algorithm(md5);
}
#endif /* UNIT_TESTING */

View file

@ -16,6 +16,8 @@
#include <inttypes.h>
#include <stdlib.h>
#include <isc/iterated_hash.h>
#include <isc/md.h>
#include <isc/mem.h>
#include <isc/once.h>
#include <isc/thread.h>
@ -194,9 +196,13 @@ isc__trampoline_run(isc_threadarg_t arg) {
isc__trampoline_attach(trampoline);
isc__iterated_hash_initialize();
/* Run the main function */
result = (trampoline->start)(trampoline->arg);
isc__iterated_hash_shutdown();
isc__trampoline_detach(trampoline);
return (result);

View file

@ -1,4 +1,5 @@
/ascii
/compress
/iterated_hash
/dns_name_fromwire
/siphash

View file

@ -12,6 +12,7 @@ LDADD += \
noinst_PROGRAMS = \
ascii \
compress \
iterated_hash \
dns_name_fromwire \
siphash

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <isc/iterated_hash.h>
#include <isc/random.h>
#include <isc/time.h>
#include <dns/name.h>
static void
time_it(const int count, const int iterations, const unsigned char *salt,
const int saltlen, const unsigned char *in, const int inlen) {
uint8_t out[NSEC3_MAX_HASH_LENGTH] = { 0 };
isc_time_t start, finish;
printf("%d iterations, %d salt length, %d input length: ", iterations,
saltlen, inlen);
fflush(stdout);
isc_time_now_hires(&start);
int i = 0;
while (i++ < count) {
isc_iterated_hash(out, 1, iterations, salt, saltlen, in, inlen);
}
isc_time_now_hires(&finish);
uint64_t microseconds = isc_time_microdiff(&finish, &start);
printf("%0.2f us per iterated_hash()\n", (double)microseconds / count);
fflush(stdout);
}
int
main(void) {
uint8_t salt[DNS_NAME_MAXWIRE];
uint8_t in[DNS_NAME_MAXWIRE];
size_t saltlen = sizeof(salt);
size_t inlen = sizeof(in);
isc_random_buf(salt, saltlen);
isc_random_buf(in, inlen);
time_it(10000, 150, salt, saltlen, in, inlen);
time_it(10000, 15, salt, saltlen, in, inlen);
time_it(10000, 0, salt, saltlen, in, inlen);
saltlen = 32;
inlen = 32;
time_it(10000, 150, salt, 32, in, inlen);
time_it(10000, 15, salt, 32, in, inlen);
time_it(10000, 0, salt, saltlen, in, inlen);
saltlen = 0;
inlen = 1;
time_it(10000, 150, salt, 32, in, inlen);
time_it(10000, 15, salt, 32, in, inlen);
time_it(10000, 0, salt, saltlen, in, inlen);
}