QPasswordDigestor: add OpenSSL 3.0 support to deriveKeyPbkdf2

Use OpenSSL 3.0 in deriveKeyPbkdf2() if it's available. This currently
produces same results, but in the future we will have possibilities to
force some additional compliancy, for example we can enable SP800-132
compliance checks, which are not enabled currently because it would
differ from the original implementation.

Change-Id: Ia7a7cc91ea3f20c9d76e39309daa7c97cd7ad5c6
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
bb10
Jan Grulich 2022-12-12 15:50:07 +01:00
parent 92f5836fe8
commit c2d3d0c0e2
2 changed files with 101 additions and 2 deletions

View File

@ -336,6 +336,11 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_localserver AND WIN32
socket/qlocalsocket_win.cpp
)
qt_internal_extend_target(Network CONDITION QT_FEATURE_openssl_linked AND QT_FEATURE_opensslv30
LIBRARIES
WrapOpenSSL::WrapOpenSSL
)
qt_internal_extend_target(Network CONDITION QT_FEATURE_system_proxies
DEFINES
QT_USE_SYSTEM_PROXIES

View File

@ -6,9 +6,20 @@
#include <QtCore/QDebug>
#include <QtCore/QMessageAuthenticationCode>
#include <QtCore/QtEndian>
#include <QtCore/QList>
#include "qtcore-config_p.h"
#include <limits>
#if QT_CONFIG(opensslv30) && QT_CONFIG(openssl_linked)
#define USING_OPENSSL30
#include <openssl/core_names.h>
#include <openssl/kdf.h>
#include <openssl/params.h>
#include <openssl/provider.h>
#endif
QT_BEGIN_NAMESPACE
namespace QPasswordDigestor {
@ -86,6 +97,85 @@ Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf1(QCryptographicHash::Algorithm algori
return key.left(dkLen);
}
#ifdef USING_OPENSSL30
// Copied from QCryptographicHashPrivate
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;
}
}
static QByteArray opensslDeriveKeyPbkdf2(QCryptographicHash::Algorithm algorithm,
const QByteArray &data, const QByteArray &salt,
uint64_t iterations, quint64 dkLen)
{
EVP_KDF *kdf = EVP_KDF_fetch(nullptr, "PBKDF2", nullptr);
if (!kdf)
return QByteArray();
auto cleanUpKdf = qScopeGuard([kdf] {
EVP_KDF_free(kdf);
});
EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf);
if (!ctx)
return QByteArray();
auto cleanUpCtx = qScopeGuard([ctx] {
EVP_KDF_CTX_free(ctx);
});
// Do not enable SP800-132 compliance check, otherwise we will require:
// - the iteration count is at least 1000
// - the salt length is at least 128 bits
// - the derived key length is at least 112 bits
// This would be a different behavior from the original implementation.
int checkDisabled = 1;
QList<OSSL_PARAM> params;
params.append(OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, const_cast<char*>(methodToName(algorithm)), 0));
params.append(OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, const_cast<char*>(salt.data()), salt.size()));
params.append(OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD, const_cast<char*>(data.data()), data.size()));
params.append(OSSL_PARAM_construct_uint64(OSSL_KDF_PARAM_ITER, &iterations));
params.append(OSSL_PARAM_construct_int(OSSL_KDF_PARAM_PKCS5, &checkDisabled));
params.append(OSSL_PARAM_construct_end());
if (EVP_KDF_CTX_set_params(ctx, params.data()) <= 0)
return QByteArray();
QByteArray derived(dkLen, '\0');
if (!EVP_KDF_derive(ctx, reinterpret_cast<unsigned char*>(derived.data()), derived.size(), nullptr))
return QByteArray();
return derived;
}
#endif
/*!
\since 5.12
@ -107,8 +197,6 @@ Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algori
const QByteArray &data, const QByteArray &salt,
int iterations, quint64 dkLen)
{
// https://tools.ietf.org/html/rfc8018#section-5.2
// The RFC recommends checking that 'dkLen' is not greater than '(2^32 - 1) * hLen'
int hashLen = QCryptographicHash::hashLength(algorithm);
const quint64 maxLen = quint64(std::numeric_limits<quint32>::max() - 1) * hashLen;
@ -122,6 +210,12 @@ Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algori
if (iterations < 1 || dkLen < 1)
return QByteArray();
#ifdef USING_OPENSSL30
if (methodToName(algorithm))
return opensslDeriveKeyPbkdf2(algorithm, data, salt, iterations, dkLen);
#endif
// https://tools.ietf.org/html/rfc8018#section-5.2
QByteArray key;
quint32 currentIteration = 1;
QMessageAuthenticationCode hmac(algorithm, data);