Commit 2bc9121fa16a274093f6756164a52c30ecb7496c
1 parent
b7459209
Fix major performance bug with openssl crypto (fixes #798)
Lazily load MD5 and RC4 once in the life of the program. Only load the legacy provider if RC4 is actually being used.
Showing
4 changed files
with
73 additions
and
36 deletions
ChangeLog
| 1 | 1 | 2022-10-08 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | |
| 3 | + * Fix major performance bug with the openssl crypto provider when | |
| 4 | + using OpenSSL 3. The legacy loader and rc4 algorithm was being | |
| 5 | + loaded with every call to the crypto provider instead of once in | |
| 6 | + the life of the program. Fixes #798. | |
| 7 | + | |
| 3 | 8 | * performance_check: add --test option to limit which tests are |
| 4 | 9 | run. |
| 5 | 10 | ... | ... |
libqpdf/QPDFCrypto_openssl.cc
| 1 | 1 | #include <qpdf/QPDFCrypto_openssl.hh> |
| 2 | 2 | |
| 3 | 3 | #include <cstring> |
| 4 | +#include <memory> | |
| 4 | 5 | #include <stdexcept> |
| 5 | 6 | #include <string> |
| 6 | 7 | |
| ... | ... | @@ -18,6 +19,60 @@ |
| 18 | 19 | |
| 19 | 20 | #include <qpdf/QIntC.hh> |
| 20 | 21 | |
| 22 | +#ifndef QPDF_OPENSSL_1 | |
| 23 | +namespace | |
| 24 | +{ | |
| 25 | + class RC4Loader | |
| 26 | + { | |
| 27 | + public: | |
| 28 | + static EVP_CIPHER const* getRC4(); | |
| 29 | + ~RC4Loader(); | |
| 30 | + | |
| 31 | + private: | |
| 32 | + RC4Loader(); | |
| 33 | + OSSL_PROVIDER* legacy; | |
| 34 | + OSSL_LIB_CTX* libctx; | |
| 35 | + EVP_CIPHER* rc4; | |
| 36 | + }; | |
| 37 | +} // namespace | |
| 38 | + | |
| 39 | +EVP_CIPHER const* | |
| 40 | +RC4Loader::getRC4() | |
| 41 | +{ | |
| 42 | + static auto loader = std::shared_ptr<RC4Loader>(new RC4Loader()); | |
| 43 | + return loader->rc4; | |
| 44 | +} | |
| 45 | + | |
| 46 | +RC4Loader::RC4Loader() | |
| 47 | +{ | |
| 48 | + libctx = OSSL_LIB_CTX_new(); | |
| 49 | + if (libctx == nullptr) { | |
| 50 | + throw std::runtime_error("unable to create openssl library context"); | |
| 51 | + return; | |
| 52 | + } | |
| 53 | + legacy = OSSL_PROVIDER_load(libctx, "legacy"); | |
| 54 | + if (legacy == nullptr) { | |
| 55 | + OSSL_LIB_CTX_free(libctx); | |
| 56 | + throw std::runtime_error("unable to load openssl legacy provider"); | |
| 57 | + return; | |
| 58 | + } | |
| 59 | + rc4 = EVP_CIPHER_fetch(libctx, "RC4", nullptr); | |
| 60 | + if (rc4 == nullptr) { | |
| 61 | + OSSL_PROVIDER_unload(legacy); | |
| 62 | + OSSL_LIB_CTX_free(libctx); | |
| 63 | + throw std::runtime_error("unable to load openssl rc4 algorithm"); | |
| 64 | + return; | |
| 65 | + } | |
| 66 | +} | |
| 67 | + | |
| 68 | +RC4Loader::~RC4Loader() | |
| 69 | +{ | |
| 70 | + EVP_CIPHER_free(rc4); | |
| 71 | + OSSL_PROVIDER_unload(legacy); | |
| 72 | + OSSL_LIB_CTX_free(libctx); | |
| 73 | +} | |
| 74 | +#endif // not QPDF_OPENSSL_1 | |
| 75 | + | |
| 21 | 76 | static void |
| 22 | 77 | bad_bits(int bits) |
| 23 | 78 | { |
| ... | ... | @@ -41,32 +96,9 @@ check_openssl(int status) |
| 41 | 96 | } |
| 42 | 97 | |
| 43 | 98 | QPDFCrypto_openssl::QPDFCrypto_openssl() : |
| 44 | -#ifdef QPDF_OPENSSL_1 | |
| 45 | - rc4(EVP_rc4()), | |
| 46 | -#endif | |
| 47 | 99 | md_ctx(EVP_MD_CTX_new()), |
| 48 | 100 | cipher_ctx(EVP_CIPHER_CTX_new()) |
| 49 | 101 | { |
| 50 | -#ifndef QPDF_OPENSSL_1 | |
| 51 | - libctx = OSSL_LIB_CTX_new(); | |
| 52 | - if (libctx == nullptr) { | |
| 53 | - throw std::runtime_error("unable to create openssl library context"); | |
| 54 | - return; | |
| 55 | - } | |
| 56 | - legacy = OSSL_PROVIDER_load(libctx, "legacy"); | |
| 57 | - if (legacy == nullptr) { | |
| 58 | - OSSL_LIB_CTX_free(libctx); | |
| 59 | - throw std::runtime_error("unable to load openssl legacy provider"); | |
| 60 | - return; | |
| 61 | - } | |
| 62 | - rc4 = EVP_CIPHER_fetch(libctx, "RC4", nullptr); | |
| 63 | - if (rc4 == nullptr) { | |
| 64 | - OSSL_PROVIDER_unload(legacy); | |
| 65 | - OSSL_LIB_CTX_free(libctx); | |
| 66 | - throw std::runtime_error("unable to load openssl rc4 algorithm"); | |
| 67 | - return; | |
| 68 | - } | |
| 69 | -#endif | |
| 70 | 102 | memset(md_out, 0, sizeof(md_out)); |
| 71 | 103 | EVP_MD_CTX_init(md_ctx); |
| 72 | 104 | EVP_CIPHER_CTX_init(cipher_ctx); |
| ... | ... | @@ -77,11 +109,6 @@ QPDFCrypto_openssl::~QPDFCrypto_openssl() |
| 77 | 109 | EVP_MD_CTX_reset(md_ctx); |
| 78 | 110 | EVP_CIPHER_CTX_reset(cipher_ctx); |
| 79 | 111 | EVP_CIPHER_CTX_free(cipher_ctx); |
| 80 | -#ifndef QPDF_OPENSSL_1 | |
| 81 | - EVP_CIPHER_free(rc4); | |
| 82 | - OSSL_PROVIDER_unload(legacy); | |
| 83 | - OSSL_LIB_CTX_free(libctx); | |
| 84 | -#endif | |
| 85 | 112 | EVP_MD_CTX_free(md_ctx); |
| 86 | 113 | } |
| 87 | 114 | |
| ... | ... | @@ -101,7 +128,7 @@ QPDFCrypto_openssl::MD5_init() |
| 101 | 128 | void |
| 102 | 129 | QPDFCrypto_openssl::SHA2_init(int bits) |
| 103 | 130 | { |
| 104 | - const EVP_MD* md = EVP_sha512(); | |
| 131 | + static const EVP_MD* md = EVP_sha512(); | |
| 105 | 132 | switch (bits) { |
| 106 | 133 | case 256: |
| 107 | 134 | md = EVP_sha256(); |
| ... | ... | @@ -174,6 +201,11 @@ QPDFCrypto_openssl::SHA2_digest() |
| 174 | 201 | void |
| 175 | 202 | QPDFCrypto_openssl::RC4_init(unsigned char const* key_data, int key_len) |
| 176 | 203 | { |
| 204 | +#ifdef QPDF_OPENSSL_1 | |
| 205 | + static auto const rc4 = EVP_rc4(); | |
| 206 | +#else | |
| 207 | + static auto const rc4 = RC4Loader::getRC4(); | |
| 208 | +#endif | |
| 177 | 209 | check_openssl(EVP_CIPHER_CTX_reset(cipher_ctx)); |
| 178 | 210 | if (key_len == -1) { |
| 179 | 211 | key_len = | ... | ... |
libqpdf/qpdf/QPDFCrypto_openssl.hh
| ... | ... | @@ -58,13 +58,6 @@ class QPDFCrypto_openssl: public QPDFCryptoImpl |
| 58 | 58 | void rijndael_finalize() override; |
| 59 | 59 | |
| 60 | 60 | private: |
| 61 | -#ifdef QPDF_OPENSSL_1 | |
| 62 | - EVP_CIPHER const* rc4; | |
| 63 | -#else | |
| 64 | - OSSL_LIB_CTX* libctx; | |
| 65 | - OSSL_PROVIDER* legacy; | |
| 66 | - EVP_CIPHER* rc4; | |
| 67 | -#endif | |
| 68 | 61 | EVP_MD_CTX* const md_ctx; |
| 69 | 62 | EVP_CIPHER_CTX* const cipher_ctx; |
| 70 | 63 | uint8_t md_out[EVP_MAX_MD_SIZE]; | ... | ... |
manual/release-notes.rst
| ... | ... | @@ -13,6 +13,13 @@ For a detailed list of changes, please see the file |
| 13 | 13 | |
| 14 | 14 | - A C++-17 compiler is now required. |
| 15 | 15 | |
| 16 | + - Bug fixes | |
| 17 | + | |
| 18 | + - Fix major performance bug with the OpenSSL crypto provider. This | |
| 19 | + bug was causing a 6x to 12x slowdown for encrypted files when | |
| 20 | + OpenSSL 3 was in use. This includes the default Windows builds | |
| 21 | + distributed with the qpdf release. | |
| 22 | + | |
| 16 | 23 | 11.1.1: October 1, 2022 |
| 17 | 24 | - Bug fixes |
| 18 | 25 | ... | ... |