QCryptographicHash: implement OpenSSL 3.0 support

Use OpenSSL 3.0 as a provider of all hashing algorithms, except the
BLAKE2b and BLAKE2s. BLAKE2b and BLAKE2s algorithms support a variable
length digest, but OpenSSL's implementation outputs only a digest of a
fixed length (the maximum length supported). This is 512-bits for the
BLAKE2b and 256-bits for the BLAKE2s and for that reason we still use
the original implementation.

[ChangeLog][QtCore][QCryptographicHash] Uses the OpenSSL 3.0
implementation now, where available.

Change-Id: Ia4e4139b92ea9b40a18aa480aa5c06562178f916
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Jan Grulich 2021-11-04 15:59:17 +01:00
parent 256fbdeedd
commit 633c136596
6 changed files with 296 additions and 77 deletions

View File

@ -18,6 +18,93 @@ if(TARGET ZLIB::ZLIB)
set_property(TARGET ZLIB::ZLIB PROPERTY IMPORTED_GLOBAL TRUE)
endif()
qt_find_package(WrapOpenSSLHeaders PROVIDED_TARGETS WrapOpenSSLHeaders::WrapOpenSSLHeaders MODULE_NAME core)
# openssl_headers
# OPENSSL_VERSION_MAJOR is not defined for OpenSSL 1.1.1
qt_config_compile_test(opensslv11_headers
LIBRARIES
WrapOpenSSLHeaders::WrapOpenSSLHeaders
CODE
"#include <openssl/ssl.h>
#include <openssl/opensslv.h>
#if !defined(OPENSSL_VERSION_NUMBER) || defined(OPENSSL_VERSION_MAJOR) || OPENSSL_VERSION_NUMBER-0 < 0x10101000L
# error OpenSSL >= 1.1.1 is required
#endif
#if !defined(OPENSSL_NO_EC) && !defined(SSL_CTRL_SET_CURVES)
# error OpenSSL was reported as >= 1.1.1 but is missing required features, possibly it is libressl which is unsupported
#endif
int main(void)
{
/* BEGIN TEST: */
/* END TEST: */
return 0;
}
")
qt_find_package(WrapOpenSSL PROVIDED_TARGETS WrapOpenSSL::WrapOpenSSL MODULE_NAME core QMAKE_LIB openssl)
# openssl
# OPENSSL_VERSION_MAJOR is not defined for OpenSSL 1.1.1
qt_config_compile_test(opensslv11
LIBRARIES
WrapOpenSSL::WrapOpenSSL
CODE
"#include <openssl/ssl.h>
#include <openssl/opensslv.h>
#if !defined(OPENSSL_VERSION_NUMBER) || defined(OPENSSL_VERSION_MAJOR) || OPENSSL_VERSION_NUMBER-0 < 0x10101000L
# error OpenSSL >= 1.1.1 is required
#endif
#if !defined(OPENSSL_NO_EC) && !defined(SSL_CTRL_SET_CURVES)
# error OpenSSL was reported as >= 1.1.1 but is missing required features, possibly it is libressl which is unsupported
#endif
int main(void)
{
/* BEGIN TEST: */
SSL_free(SSL_new(0));
/* END TEST: */
return 0;
}
")
# opensslv30
# openssl_headers
qt_config_compile_test(opensslv30_headers
LIBRARIES
WrapOpenSSLHeaders::WrapOpenSSLHeaders
CODE
"#include <openssl/ssl.h>
#include <openssl/opensslv.h>
#if !OPENSSL_VERSION_PREREQ(3,0)
# error OpenSSL >= 3.0 is required
#endif
int main(void)
{
/* BEGIN TEST: */
/* END TEST: */
return 0;
}
")
qt_config_compile_test(opensslv30
LIBRARIES
WrapOpenSSL::WrapOpenSSL
CODE
"#include <openssl/ssl.h>
#include <openssl/opensslv.h>
#if !OPENSSL_VERSION_PREREQ(3,0)
# error OpenSSL >= 3.0 is required
#endif
int main(void)
{
/* BEGIN TEST: */
SSL_free(SSL_new(0));
/* END TEST: */
return 0;
}
")
# special case end
qt_find_package(WrapZSTD 1.3 PROVIDED_TARGETS WrapZSTD::WrapZSTD MODULE_NAME global QMAKE_LIB zstd)
qt_find_package(WrapDBus1 1.2 PROVIDED_TARGETS dbus-1 MODULE_NAME global QMAKE_LIB dbus)
@ -962,6 +1049,34 @@ qt_feature("libudev" PRIVATE
LABEL "udev"
CONDITION Libudev_FOUND AND NOT INTEGRITY
)
qt_feature("openssl" PRIVATE
LABEL "OpenSSL"
CONDITION QT_FEATURE_openssl_runtime OR QT_FEATURE_openssl_linked
ENABLE false
)
qt_feature_definition("openssl" "QT_NO_OPENSSL" NEGATE)
qt_feature_config("openssl" QMAKE_PUBLIC_QT_CONFIG)
qt_feature("openssl-runtime"
AUTODETECT NOT WASM
CONDITION TEST_opensslv11_headers OR TEST_opensslv30_headers
ENABLE INPUT_openssl STREQUAL 'yes' OR INPUT_openssl STREQUAL 'runtime'
DISABLE INPUT_openssl STREQUAL 'no' OR INPUT_openssl STREQUAL 'linked' OR INPUT_ssl STREQUAL 'no'
)
qt_feature("openssl-linked" PRIVATE
LABEL " Qt directly linked to OpenSSL"
AUTODETECT OFF
CONDITION TEST_opensslv11 OR TEST_opensslv30
ENABLE INPUT_openssl STREQUAL 'linked'
)
qt_feature_definition("openssl-linked" "QT_LINKED_OPENSSL")
qt_feature("opensslv11" PUBLIC
LABEL "OpenSSL 1.1"
CONDITION TEST_opensslv11 OR TEST_opensslv11_headers
)
qt_feature("opensslv30" PUBLIC
LABEL "OpenSSL 3.0"
CONDITION TEST_opensslv30 OR TEST_opensslv30_headers
)
qt_feature("ccache"
LABEL "Using ccache"
AUTODETECT 1
@ -1090,6 +1205,10 @@ qt_configure_end_summary_section() # end of "Qt modules and options" section
qt_configure_add_summary_section(NAME "Support enabled for")
qt_configure_add_summary_entry(ARGS "pkg-config")
qt_configure_add_summary_entry(ARGS "libudev")
qt_configure_add_summary_entry(ARGS "openssl")
qt_configure_add_summary_entry(ARGS "openssl-linked")
qt_configure_add_summary_entry(ARGS "opensslv11")
qt_configure_add_summary_entry(ARGS "opensslv30")
qt_configure_add_summary_entry(ARGS "system-zlib")
qt_configure_add_summary_entry(ARGS "zstd")
qt_configure_add_summary_entry(ARGS "thread")

View File

@ -68,6 +68,9 @@ qt_commandline_option(gui TYPE boolean)
qt_commandline_option(headersclean TYPE boolean)
qt_commandline_option(incredibuild-xge TYPE boolean NAME incredibuild_xge)
qt_commandline_option(libudev TYPE boolean)
qt_commandline_option(openssl TYPE optionalString VALUES no yes linked runtime)
qt_commandline_option(openssl-linked TYPE void NAME openssl VALUE linked)
qt_commandline_option(openssl-runtime TYPE void NAME openssl VALUE runtime)
qt_commandline_option(linker TYPE optionalString VALUES bfd gold lld mold)
qt_commandline_option(ltcg TYPE boolean)
qt_commandline_option(intelcet TYPE boolean)

View File

@ -829,6 +829,20 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_regularexpression
WrapPCRE2::WrapPCRE2
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_openssl_linked AND QT_FEATURE_opensslv30
LIBRARIES
WrapOpenSSL::WrapOpenSSL
PRIVATE_MODULE_INTERFACE
WrapOpenSSL::WrapOpenSSL
)
qt_internal_extend_target(Core CONDITION NOT QT_FEATURE_openssl AND QT_FEATURE_opensslv30
LIBRARIES
WrapOpenSSLHeaders::WrapOpenSSLHeaders
PRIVATE_MODULE_INTERFACE
WrapOpenSSLHeaders::WrapOpenSSLHeaders
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_hijricalendar
SOURCES
time/qhijricalendar.cpp time/qhijricalendar_p.h

View File

@ -12,6 +12,7 @@
#endif
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
#if !QT_CONFIG(opensslv30)
// qdoc and qmake only need SHA-1
#include "../../3rdparty/md5/md5.h"
#include "../../3rdparty/md5/md5.cpp"
@ -91,6 +92,9 @@ static inline int SHA384_512AddLength(SHA512Context *context, unsigned int lengt
uint64_t addTemp;
return SHA384_512AddLengthM(context, length);
}
#endif // !QT_CONFIG(opensslv30)
#include "qtcore-config_p.h"
#if QT_CONFIG(system_libb2)
#include <blake2.h>
@ -100,6 +104,13 @@ static inline int SHA384_512AddLength(SHA512Context *context, unsigned int lengt
#endif
#endif // QT_CRYPTOGRAPHICHASH_ONLY_SHA1
#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(opensslv30)
#define USING_OPENSSL30
#include <openssl/evp.h>
#include <openssl/provider.h>
#include <openssl/sha.h>
#endif
QT_BEGIN_NAMESPACE
static constexpr qsizetype MaxHashLength = 64;
@ -117,10 +128,17 @@ static constexpr int hashLengthInternal(QCryptographicHash::Algorithm method) no
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
CASE(Md4, 16);
CASE(Md5, 16);
#ifdef USING_OPENSSL30
CASE(Sha224, SHA224_DIGEST_LENGTH);
CASE(Sha256, SHA256_DIGEST_LENGTH);
CASE(Sha384, SHA384_DIGEST_LENGTH);
CASE(Sha512, SHA512_DIGEST_LENGTH);
#else
CASE(Sha224, SHA224HashSize);
CASE(Sha256, SHA256HashSize);
CASE(Sha384, SHA384HashSize);
CASE(Sha512, SHA512HashSize);
#endif
CASE(Blake2s_128, 128 / 8);
case QCryptographicHash::Blake2b_160:
case QCryptographicHash::Blake2s_160:
@ -153,6 +171,37 @@ static constexpr int hashLengthInternal(QCryptographicHash::Algorithm method) no
return 0;
}
#ifdef USING_OPENSSL30
static constexpr const char * methodToName(QCryptographicHash::Algorithm method) noexcept
{
switch (method) {
#define CASE(Enum, Name) \
case QCryptographicHash:: Enum : \
return Name \
/*end*/
CASE(Sha1, "SHA1");
CASE(Md4, "MD4");
CASE(Md5, "MD5");
CASE(Sha224, "SHA224");
CASE(Sha256, "SHA256");
CASE(Sha384, "SHA384");
CASE(Sha512, "SHA512");
CASE(RealSha3_224, "SHA3-224");
CASE(RealSha3_256, "SHA3-256");
CASE(RealSha3_384, "SHA3-384");
CASE(RealSha3_512, "SHA3-512");
CASE(Keccak_224, "SHA3-224");
CASE(Keccak_256, "SHA3-256");
CASE(Keccak_384, "SHA3-384");
CASE(Keccak_512, "SHA3-512");
CASE(Blake2b_512, "BLAKE2B512");
CASE(Blake2s_256, "BLAKE2S256");
#undef CASE
default: return nullptr;
}
}
#endif
class QCryptographicHashPrivate
{
public:
@ -168,9 +217,29 @@ public:
QByteArrayView resultView() const noexcept { return result.toByteArrayView(); }
const QCryptographicHash::Algorithm method;
#ifdef USING_OPENSSL30
struct EVP_MD_CTX_deleter {
void operator()(EVP_MD_CTX *ctx) const noexcept {
EVP_MD_CTX_free(ctx);
}
};
struct EVP_MD_deleter {
void operator()(EVP_MD *md) const noexcept {
EVP_MD_free(md);
}
};
using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, EVP_MD_CTX_deleter>;
using EVP_MD_ptr = std::unique_ptr<EVP_MD, EVP_MD_deleter>;
EVP_MD_ptr algorithm;
EVP_MD_CTX_ptr context;
bool initializationFailed = false;
#endif
union {
Sha1State sha1Context;
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
#ifndef USING_OPENSSL30
MD5Context md5Context;
md4_context md4Context;
SHA224Context sha224Context;
@ -178,17 +247,20 @@ public:
SHA384Context sha384Context;
SHA512Context sha512Context;
SHA3Context sha3Context;
#endif
blake2b_state blake2bContext;
blake2s_state blake2sContext;
#endif
};
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
#ifndef USING_OPENSSL30
enum class Sha3Variant
{
Sha3,
Keccak
};
void sha3Finish(int bitCount, Sha3Variant sha3Variant);
#endif
#endif
class SmallByteArray {
std::array<char, MaxHashLength> m_data;
@ -212,6 +284,7 @@ public:
};
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
#ifndef USING_OPENSSL30
void QCryptographicHashPrivate::sha3Finish(int bitCount, Sha3Variant sha3Variant)
{
/*
@ -250,6 +323,7 @@ void QCryptographicHashPrivate::sha3Finish(int bitCount, Sha3Variant sha3Variant
sha3Final(&copy, reinterpret_cast<BitSequence *>(result.data()));
}
#endif // !QT_CONFIG(opensslv30)
#endif
/*!
@ -376,6 +450,52 @@ QCryptographicHash::Algorithm QCryptographicHash::algorithm() const noexcept
void QCryptographicHashPrivate::reset() noexcept
{
#ifdef USING_OPENSSL30
if (method == QCryptographicHash::Blake2b_160 ||
method == QCryptographicHash::Blake2b_256 ||
method == QCryptographicHash::Blake2b_384) {
new (&blake2bContext) blake2b_state;
blake2b_init(&blake2bContext, hashLengthInternal(method));
return;
} else if (method == QCryptographicHash::Blake2s_128 ||
method == QCryptographicHash::Blake2s_160 ||
method == QCryptographicHash::Blake2s_224) {
new (&blake2sContext) blake2s_state;
blake2s_init(&blake2sContext, hashLengthInternal(method));
return;
}
initializationFailed = true;
if (method == QCryptographicHash::Md4) {
/*
* We need to load the legacy provider in order to have the MD4
* algorithm available.
*/
if (!OSSL_PROVIDER_load(nullptr, "legacy"))
return;
if (!OSSL_PROVIDER_load(nullptr, "default"))
return;
}
context = EVP_MD_CTX_ptr(EVP_MD_CTX_new());
if (!context) {
return;
}
/*
* Using the "-fips" option will disable the global "fips=yes" for
* this one lookup and the algorithm can be fetched from any provider
* that implements the algorithm (including the FIPS provider).
*/
algorithm = EVP_MD_ptr(EVP_MD_fetch(nullptr, methodToName(method), "-fips"));
if (!algorithm) {
return;
}
initializationFailed = !EVP_DigestInit_ex(context.get(), algorithm.get(), nullptr);
#else
switch (method) {
case QCryptographicHash::Sha1:
new (&sha1Context) Sha1State;
@ -439,6 +559,7 @@ void QCryptographicHashPrivate::reset() noexcept
#endif
}
result.clear();
#endif // !QT_CONFIG(opensslv30)
}
#if QT_DEPRECATED_SINCE(6, 4)
@ -481,6 +602,22 @@ void QCryptographicHashPrivate::addData(QByteArrayView bytes) noexcept
#else
{
#endif
#ifdef USING_OPENSSL30
if (method == QCryptographicHash::Blake2b_160 ||
method == QCryptographicHash::Blake2b_256 ||
method == QCryptographicHash::Blake2b_384) {
blake2b_update(&blake2bContext, reinterpret_cast<const uint8_t *>(data), length);
} else if (method == QCryptographicHash::Blake2s_128 ||
method == QCryptographicHash::Blake2s_160 ||
method == QCryptographicHash::Blake2s_224) {
blake2s_update(&blake2sContext, reinterpret_cast<const uint8_t *>(data), length);
} else if (!initializationFailed) {
result.resizeForOverwrite(EVP_MD_get_size(algorithm.get()));
const int ret = EVP_DigestUpdate(context.get(), (const unsigned char *)data, length);
Q_UNUSED(ret);
}
#else
switch (method) {
case QCryptographicHash::Sha1:
sha1Update(&sha1Context, (const unsigned char *)data, length);
@ -533,6 +670,7 @@ void QCryptographicHashPrivate::addData(QByteArrayView bytes) noexcept
break;
#endif
}
#endif // !QT_CONFIG(opensslv30)
}
result.clear();
}
@ -591,6 +729,27 @@ void QCryptographicHashPrivate::finalize() noexcept
if (!result.isEmpty())
return;
#ifdef USING_OPENSSL30
if (method == QCryptographicHash::Blake2b_160 ||
method == QCryptographicHash::Blake2b_256 ||
method == QCryptographicHash::Blake2b_384) {
const auto length = hashLengthInternal(method);
blake2b_state copy = blake2bContext;
result.resizeForOverwrite(length);
blake2b_final(&copy, reinterpret_cast<uint8_t *>(result.data()), length);
} else if (method == QCryptographicHash::Blake2s_128 ||
method == QCryptographicHash::Blake2s_160 ||
method == QCryptographicHash::Blake2s_224) {
const auto length = hashLengthInternal(method);
blake2s_state copy = blake2sContext;
result.resizeForOverwrite(length);
blake2s_final(&copy, reinterpret_cast<uint8_t *>(result.data()), length);
} else if (!initializationFailed) {
result.resizeForOverwrite(EVP_MD_get_size(algorithm.get()));
const int ret = EVP_DigestFinal_ex(context.get(), (unsigned char *)result.data(), nullptr);
Q_UNUSED(ret);
}
#else
switch (method) {
case QCryptographicHash::Sha1: {
Sha1State copy = sha1Context;
@ -677,6 +836,7 @@ void QCryptographicHashPrivate::finalize() noexcept
}
#endif
}
#endif // !QT_CONFIG(opensslv30)
}
/*!

View File

@ -9,53 +9,6 @@
qt_find_package(WrapBrotli PROVIDED_TARGETS WrapBrotli::WrapBrotliDec MODULE_NAME network QMAKE_LIB brotli)
qt_find_package(Libproxy PROVIDED_TARGETS PkgConfig::Libproxy MODULE_NAME network QMAKE_LIB libproxy)
qt_find_package(WrapOpenSSLHeaders PROVIDED_TARGETS WrapOpenSSLHeaders::WrapOpenSSLHeaders MODULE_NAME network)
# openssl_headers
qt_config_compile_test(openssl_headers
LIBRARIES
WrapOpenSSLHeaders::WrapOpenSSLHeaders
CODE
"#include <openssl/ssl.h>
#include <openssl/opensslv.h>
#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER-0 < 0x10101000L
# error OpenSSL >= 1.1.1 is required
#endif
#if !defined(OPENSSL_NO_EC) && !defined(SSL_CTRL_SET_CURVES)
# error OpenSSL was reported as >= 1.1.1 but is missing required features, possibly it is libressl which is unsupported
#endif
int main(void)
{
/* BEGIN TEST: */
/* END TEST: */
return 0;
}
")
qt_find_package(WrapOpenSSL PROVIDED_TARGETS WrapOpenSSL::WrapOpenSSL MODULE_NAME network QMAKE_LIB openssl)
# openssl
qt_config_compile_test(openssl
LIBRARIES
WrapOpenSSL::WrapOpenSSL
CODE
"#include <openssl/ssl.h>
#include <openssl/opensslv.h>
#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER-0 < 0x10101000L
# error OpenSSL >= 1.1.1 is required
#endif
#if !defined(OPENSSL_NO_EC) && !defined(SSL_CTRL_SET_CURVES)
# error OpenSSL was reported as >= 1.1.1 but is missing required features, possibly it is libressl which is unsupported
#endif
int main(void)
{
/* BEGIN TEST: */
SSL_free(SSL_new(0));
/* END TEST: */
return 0;
}
")
qt_find_package(GSSAPI PROVIDED_TARGETS GSSAPI::GSSAPI MODULE_NAME network QMAKE_LIB gssapi)
qt_find_package(GLIB2 OPTIONAL_COMPONENTS GOBJECT PROVIDED_TARGETS GLIB2::GOBJECT MODULE_NAME core QMAKE_LIB gobject)
qt_find_package(GLIB2 OPTIONAL_COMPONENTS GIO PROVIDED_TARGETS GLIB2::GIO MODULE_NAME core QMAKE_LIB gio)
@ -257,26 +210,6 @@ qt_feature("linux-netlink" PRIVATE
LABEL "Linux AF_NETLINK"
CONDITION LINUX AND NOT ANDROID AND TEST_linux_netlink
)
qt_feature("openssl" PRIVATE
LABEL "OpenSSL"
CONDITION QT_FEATURE_openssl_runtime OR QT_FEATURE_openssl_linked
ENABLE false
)
qt_feature_definition("openssl" "QT_NO_OPENSSL" NEGATE)
qt_feature_config("openssl" QMAKE_PUBLIC_QT_CONFIG)
qt_feature("openssl-runtime"
AUTODETECT NOT WASM
CONDITION TEST_openssl_headers
ENABLE INPUT_openssl STREQUAL 'yes' OR INPUT_openssl STREQUAL 'runtime'
DISABLE INPUT_openssl STREQUAL 'no' OR INPUT_openssl STREQUAL 'linked' OR INPUT_ssl STREQUAL 'no'
)
qt_feature("openssl-linked" PRIVATE
LABEL " Qt directly linked to OpenSSL"
AUTODETECT OFF
CONDITION TEST_openssl
ENABLE INPUT_openssl STREQUAL 'linked'
)
qt_feature_definition("openssl-linked" "QT_LINKED_OPENSSL")
qt_feature("securetransport" PUBLIC
LABEL "SecureTransport"
CONDITION APPLE
@ -306,10 +239,6 @@ qt_feature("ocsp" PUBLIC
PURPOSE "Provides OCSP stapling support"
CONDITION QT_FEATURE_opensslv11 AND TEST_ocsp
)
qt_feature("opensslv11" PUBLIC
LABEL "OpenSSL 1.1"
CONDITION QT_FEATURE_openssl
)
qt_feature("sctp" PUBLIC
LABEL "SCTP"
AUTODETECT OFF
@ -435,9 +364,6 @@ qt_configure_add_summary_entry(
ARGS "schannel"
CONDITION WIN32
)
qt_configure_add_summary_entry(ARGS "openssl")
qt_configure_add_summary_entry(ARGS "openssl-linked")
qt_configure_add_summary_entry(ARGS "opensslv11")
qt_configure_add_summary_entry(ARGS "dtls")
qt_configure_add_summary_entry(ARGS "ocsp")
qt_configure_add_summary_entry(ARGS "sctp")

View File

@ -2,9 +2,6 @@
# SPDX-License-Identifier: BSD-3-Clause
qt_commandline_option(libproxy TYPE boolean)
qt_commandline_option(openssl TYPE optionalString VALUES no yes linked runtime)
qt_commandline_option(openssl-linked TYPE void NAME openssl VALUE linked)
qt_commandline_option(openssl-runtime TYPE void NAME openssl VALUE runtime)
qt_commandline_option(dtls TYPE boolean)
qt_commandline_option(ocsp TYPE boolean)
qt_commandline_option(sctp TYPE boolean)