diff options
author | Jan Grulich <jgrulich@redhat.com> | 2022-12-12 15:50:07 +0100 |
---|---|---|
committer | Jan Grulich <jgrulich@redhat.com> | 2023-02-09 19:30:15 +0100 |
commit | c2d3d0c0e2b2a49288ca7b4503aea41ad214a7be (patch) | |
tree | 9d5af74fe08d01d7fbee9c638ef34c9f830f7e6c /src/network/ssl/qpassworddigestor.cpp | |
parent | 92f5836fe8611d6f7a70efcc3b0dd5db262ba175 (diff) |
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>
Diffstat (limited to 'src/network/ssl/qpassworddigestor.cpp')
-rw-r--r-- | src/network/ssl/qpassworddigestor.cpp | 98 |
1 files changed, 96 insertions, 2 deletions
diff --git a/src/network/ssl/qpassworddigestor.cpp b/src/network/ssl/qpassworddigestor.cpp index a08702e7b6..94de14abd4 100644 --- a/src/network/ssl/qpassworddigestor.cpp +++ b/src/network/ssl/qpassworddigestor.cpp @@ -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); |