summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/network/CMakeLists.txt6
-rw-r--r--src/network/ssl/qsslkey.h7
-rw-r--r--src/network/ssl/qsslkey_p.cpp38
-rw-r--r--src/network/ssl/qsslkey_p.h18
-rw-r--r--src/network/ssl/qsslsocket.cpp17
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp5
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp6
-rw-r--r--src/network/ssl/qsslsocket_p.h7
-rw-r--r--src/network/ssl/qsslsocket_schannel.cpp6
-rw-r--r--src/network/ssl/qtlsbackend.cpp43
-rw-r--r--src/network/ssl/qtlsbackend_p.h53
-rw-r--r--src/network/ssl/qtlskey_base.cpp133
-rw-r--r--src/network/ssl/qtlskey_base_p.h104
-rw-r--r--src/network/ssl/qtlskey_generic.cpp827
-rw-r--r--src/network/ssl/qtlskey_generic_p.h128
-rw-r--r--src/network/ssl/qtlskey_openssl.cpp509
-rw-r--r--src/network/ssl/qtlskey_openssl_p.h124
-rw-r--r--src/network/ssl/qtlskey_schannel.cpp187
-rw-r--r--src/network/ssl/qtlskey_schannel_p.h82
-rw-r--r--src/network/ssl/qtlskey_st.cpp110
-rw-r--r--src/network/ssl/qtlskey_st_p.h81
21 files changed, 2475 insertions, 16 deletions
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index 28d78989f8..4ea9656ffb 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -327,6 +327,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_ssl
ssl/qsslpresharedkeyauthenticator.cpp ssl/qsslpresharedkeyauthenticator.h ssl/qsslpresharedkeyauthenticator_p.h
ssl/qsslsocket.cpp ssl/qsslsocket.h ssl/qsslsocket_p.h
ssl/qtlsbackend.cpp ssl/qtlsbackend_p.h
+ ssl/qtlskey_base.cpp ssl/qtlskey_base_p.h
)
qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_ssl
@@ -338,6 +339,8 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_s
ssl/qsslkey_schannel.cpp
ssl/qsslsocket_qt.cpp
ssl/qsslsocket_schannel.cpp ssl/qsslsocket_schannel_p.h
+ ssl/qtlskey_generic.cpp ssl/qtlskey_generic_p.h
+ ssl/qtlskey_schannel.cpp ssl/qtlskey_schannel_p.h
LIBRARIES
Crypt32
Secur32
@@ -354,6 +357,8 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_securetransport AND QT_FE
ssl/qsslsocket_mac.cpp ssl/qsslsocket_mac_p.h
ssl/qsslsocket_mac_shared.cpp
ssl/qsslsocket_qt.cpp
+ ssl/qtlskey_generic.cpp ssl/qtlskey_generic_p.h
+ ssl/qtlskey_st.cpp ssl/qtlskey_st_p.h
)
qt_internal_extend_target(Network CONDITION QT_FEATURE_dtls AND QT_FEATURE_ssl
@@ -370,6 +375,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_openssl AND QT_FEATURE_ss
ssl/qsslkey_openssl.cpp
ssl/qsslsocket_openssl.cpp ssl/qsslsocket_openssl_p.h
ssl/qsslsocket_openssl_symbols.cpp ssl/qsslsocket_openssl_symbols_p.h
+ ssl/qtlskey_openssl.cpp ssl/qtlskey_openssl_p.h
DEFINES
OPENSSL_API_COMPAT=0x10100000L
)
diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h
index 9e70554716..9ed29c2561 100644
--- a/src/network/ssl/qsslkey.h
+++ b/src/network/ssl/qsslkey.h
@@ -52,6 +52,10 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_SSL
+namespace QSsl {
+class TlsKey;
+}
+
class QIODevice;
class QSslKeyPrivate;
@@ -92,9 +96,12 @@ public:
inline bool operator!=(const QSslKey &key) const { return !operator==(key); }
private:
+ QSsl::TlsKey *backendImplementation() const;
+
QExplicitlySharedDataPointer<QSslKeyPrivate> d;
friend class QSslCertificate;
friend class QSslSocketBackendPrivate;
+ friend class QTlsBackend;
};
Q_DECLARE_SHARED(QSslKey)
diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp
index ce12f49989..7ba8d16f63 100644
--- a/src/network/ssl/qsslkey_p.cpp
+++ b/src/network/ssl/qsslkey_p.cpp
@@ -54,6 +54,7 @@
\sa QSslSocket, QSslCertificate, QSslCipher
*/
+#include "qssl_p.h"
#include "qsslkey.h"
#include "qsslkey_p.h"
#ifndef QT_NO_OPENSSL
@@ -62,6 +63,7 @@
#include "qsslsocket.h"
#include "qsslsocket_p.h"
#include "qasn1element_p.h"
+#include "qtlsbackend_p.h"
#include <QtCore/qatomic.h>
#include <QtCore/qbytearray.h>
@@ -94,6 +96,34 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \internal
+*/
+QSslKeyPrivate::QSslKeyPrivate()
+ : algorithm(QSsl::Opaque)
+ , opaque(nullptr)
+{
+ clear(false); // TLSTODO: remove
+ const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse();
+ if (!tlsBackend)
+ return;
+ keyBackend.reset(tlsBackend->createKey());
+ if (keyBackend.get())
+ keyBackend->clear(false /*not deep clear*/);
+ else
+ qCWarning(lcSsl, "Active TLS backend does not support key creation");
+}
+
+/*!
+ \internal
+*/
+QSslKeyPrivate::~QSslKeyPrivate()
+{
+ clear(); // TLSTODO: remove
+ if (keyBackend.get())
+ keyBackend->clear(true /*deep clear*/);
+}
+
+/*!
Constructs a null key.
\sa isNull()
@@ -539,6 +569,14 @@ bool QSslKey::operator==(const QSslKey &other) const
return toDer() == other.toDer();
}
+/*!
+ \since 6.1
+ Returns TLS backend-specific implementation this QSslKey is using.
+*/
+QSsl::TlsKey *QSslKey::backendImplementation() const
+{
+ return d->keyBackend.get();
+}
/*! \fn bool QSslKey::operator!=(const QSslKey &other) const
Returns \c true if this key is not equal to key \a other; otherwise
diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h
index dd1a31b0e5..402d00daf1 100644
--- a/src/network/ssl/qsslkey_p.h
+++ b/src/network/ssl/qsslkey_p.h
@@ -61,20 +61,19 @@
#include <openssl/dsa.h>
#endif
+#include <memory>
+
QT_BEGIN_NAMESPACE
+namespace QSsl {
+class TlsKey;
+}
+
class QSslKeyPrivate
{
public:
- inline QSslKeyPrivate()
- : algorithm(QSsl::Opaque)
- , opaque(nullptr)
- {
- clear(false);
- }
-
- inline ~QSslKeyPrivate()
- { clear(); }
+ QSslKeyPrivate();
+ ~QSslKeyPrivate();
void clear(bool deep = true);
@@ -130,6 +129,7 @@ public:
int keyLength;
#endif
+ std::unique_ptr<QSsl::TlsKey> keyBackend;
QAtomicInt ref;
private:
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index d2b386d024..ffbb4d61cc 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -1603,7 +1603,7 @@ bool QSslSocket::setActiveBackend(const QString &backendName)
}
QMutexLocker locker(&QSslSocketPrivate::backendMutex);
- if (QSslSocketPrivate::tlsBackend.get()) {
+ if (QSslSocketPrivate::tlsBackend) {
qCWarning(lcSsl) << "Cannot set backend named" << backendName
<< "as active, another backend is already in use";
locker.unlock();
@@ -2832,6 +2832,21 @@ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hos
return true;
}
+/*!
+ \internal
+*/
+QTlsBackend *QSslSocketPrivate::tlsBackendInUse()
+{
+ const QMutexLocker locker(&backendMutex);
+ if (tlsBackend)
+ return tlsBackend;
+
+ if (!activeBackendName.size())
+ activeBackendName = QTlsBackend::defaultBackendName();
+
+ return tlsBackend = QTlsBackend::findBackend(activeBackendName);
+}
+
QT_END_NAMESPACE
#include "moc_qsslsocket.cpp"
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
index 6370a83dd3..c4d5b3ece7 100644
--- a/src/network/ssl/qsslsocket_mac.cpp
+++ b/src/network/ssl/qsslsocket_mac.cpp
@@ -46,6 +46,7 @@
#include "qsslcertificate_p.h"
#include "qtlsbackend_p.h"
#include "qsslcipher_p.h"
+#include "qtlskey_st_p.h"
#include "qsslkey_p.h"
#include <QtCore/qmessageauthenticationcode.h>
@@ -86,6 +87,10 @@ private:
{
return builtinBackendNames[nameIndexSecureTransport];
}
+ QSsl::TlsKey *createKey() const override
+ {
+ return new QSsl::TlsKeySecureTransport;
+ }
QList<QSsl::SslProtocol> supportedProtocols() const override
{
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index e6efaca2d0..c73c2fc235 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -67,6 +67,8 @@
#include "qsslpresharedkeyauthenticator_p.h"
#include "qocspresponse_p.h"
#include "qsslkey.h"
+#include "qtlsbackend_p.h"
+#include "qtlskey_openssl_p.h"
#ifdef Q_OS_WIN
#include "qwindowscarootfetcher_p.h"
@@ -110,6 +112,10 @@ private:
{
return builtinBackendNames[nameIndexOpenSSL];
}
+ QSsl::TlsKey *createKey() const override
+ {
+ return new QSsl::TlsKeyOpenSSL;
+ }
QList<QSsl::SslProtocol> supportedProtocols() const override
{
QList<QSsl::SslProtocol> protocols;
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index 0a30f02e5f..6bd6cc02a4 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -59,7 +59,6 @@
#include "qsslkey.h"
#include "qsslconfiguration_p.h"
#include "qocspresponse.h"
-#include "qtlsbackend_p.h"
#ifndef QT_NO_OPENSSL
#include <private/qsslcontext_openssl_p.h>
#else
@@ -108,6 +107,7 @@ using QHCertStorePointer = std::unique_ptr<void, QHCertStoreDeleter>;
#endif // Q_OS_WIN
+class QTlsBackend;
class QSslSocketPrivate : public QTcpSocketPrivate
{
Q_DECLARE_PUBLIC(QSslSocket)
@@ -209,7 +209,7 @@ public:
Q_AUTOTEST_EXPORT static bool rootCertOnDemandLoadingSupported();
- static bool loadBackend(const QString &backendName);
+ static QTlsBackend *tlsBackendInUse();
static void registerAdHocFactory();
private:
@@ -221,6 +221,7 @@ private:
static bool s_libraryLoaded;
static bool s_loadedCiphersAndCerts;
+
protected:
bool verifyErrorsHaveBeenIgnored();
bool paused;
@@ -233,7 +234,7 @@ protected:
static inline QMutex backendMutex;
static inline QString activeBackendName;
- static inline std::unique_ptr<QTlsBackend> tlsBackend;
+ static inline QTlsBackend *tlsBackend = nullptr;
};
#if QT_CONFIG(securetransport) || QT_CONFIG(schannel)
diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp
index d36b1790e1..91dea3e81c 100644
--- a/src/network/ssl/qsslsocket_schannel.cpp
+++ b/src/network/ssl/qsslsocket_schannel.cpp
@@ -47,6 +47,7 @@
#include "qsslcertificate_p.h"
#include "qsslcipher_p.h"
#include "qtlsbackend_p.h"
+#include "qtlskey_schannel_p.h"
#include <QtCore/qscopeguard.h>
#include <QtCore/qoperatingsystemversion.h>
@@ -167,6 +168,11 @@ private:
return builtinBackendNames[nameIndexSchannel];
}
+ QSsl::TlsKey *createKey() const override
+ {
+ return new QSsl::TlsKeySchannel;
+ }
+
QList<QSsl::SslProtocol> supportedProtocols() const override
{
QList<QSsl::SslProtocol> protocols;
diff --git a/src/network/ssl/qtlsbackend.cpp b/src/network/ssl/qtlsbackend.cpp
index 4c726a5b5d..85dc90cf61 100644
--- a/src/network/ssl/qtlsbackend.cpp
+++ b/src/network/ssl/qtlsbackend.cpp
@@ -43,6 +43,7 @@
#include <QtCore/private/qfactoryloader_p.h>
+#include <QtCore/qbytearray.h>
#include <QtCore/qmutex.h>
#include <algorithm>
@@ -137,6 +138,47 @@ private:
Q_GLOBAL_STATIC(BackendCollection, backends);
+namespace QSsl {
+
+TlsKey::~TlsKey() = default;
+
+QByteArray TlsKey::pemHeader() const
+{
+ if (type() == QSsl::PublicKey)
+ return QByteArrayLiteral("-----BEGIN PUBLIC KEY-----");
+ else if (algorithm() == QSsl::Rsa)
+ return QByteArrayLiteral("-----BEGIN RSA PRIVATE KEY-----");
+ else if (algorithm() == QSsl::Dsa)
+ return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----");
+ else if (algorithm() == QSsl::Ec)
+ return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----");
+ else if (algorithm() == QSsl::Dh)
+ return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
+
+ Q_UNREACHABLE();
+ return {};
+}
+
+QByteArray TlsKey::pemFooter() const
+{
+ if (type() == QSsl::PublicKey)
+ return QByteArrayLiteral("-----END PUBLIC KEY-----");
+ else if (algorithm() == QSsl::Rsa)
+ return QByteArrayLiteral("-----END RSA PRIVATE KEY-----");
+ else if (algorithm() == QSsl::Dsa)
+ return QByteArrayLiteral("-----END DSA PRIVATE KEY-----");
+ else if (algorithm() == QSsl::Ec)
+ return QByteArrayLiteral("-----END EC PRIVATE KEY-----");
+ else if (algorithm() == QSsl::Dh)
+ return QByteArrayLiteral("-----END PRIVATE KEY-----");
+
+ Q_UNREACHABLE();
+ return {};
+}
+
+
+} // namespace QSsl
+
const QString QTlsBackend::builtinBackendNames[] = {
QStringLiteral("schannel"),
QStringLiteral("securetransport"),
@@ -282,7 +324,6 @@ QList<QSsl::ImplementedClass> QTlsBackend::implementedClasses(const QString &bac
return fct->implementedClasses();
return {};
-
}
QT_END_NAMESPACE
diff --git a/src/network/ssl/qtlsbackend_p.h b/src/network/ssl/qtlsbackend_p.h
index 4650848a53..b0a54ecf59 100644
--- a/src/network/ssl/qtlsbackend_p.h
+++ b/src/network/ssl/qtlsbackend_p.h
@@ -53,11 +53,14 @@
#include <private/qtnetworkglobal_p.h>
+#include <private/qsslkey_p.h>
+
#include <QtNetwork/qsslcertificate.h>
#include <QtNetwork/qsslerror.h>
#include <QtNetwork/qsslkey.h>
#include <QtNetwork/qssl.h>
+#include <QtCore/qnamespace.h>
#include <QtCore/qobject.h>
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
@@ -76,9 +79,49 @@ class QIODevice;
namespace QSsl {
-// Encapsulates key's data or backend-specific
+// The class TlsKey encapsulates key's data (DER) or backend-specific
// data-structure, like RSA/DSA/DH structs in OpenSSL.
-class TlsKey;
+// TLSTODO: Interface is mostly what QSslKeyPrivate is now. Names,
+// however strange they are, for now preserved to ease the transition
+// (this may change in future - for example, 'decodeDer' is not just
+// decoding DER, it's initializing a key from DER.
+class TlsKey {
+public:
+ virtual ~TlsKey();
+
+ virtual void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der,
+ const QByteArray &passPhrase, bool deepClear) = 0;
+ virtual void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem,
+ const QByteArray &passPhrase, bool deepClear) = 0;
+
+ virtual QByteArray toPem(const QByteArray &passPhrase) const = 0;
+ virtual QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const = 0;
+ virtual QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const = 0;
+
+ virtual void fromHandle(Qt::HANDLE opaque, KeyType type) = 0;
+ virtual Qt::HANDLE handle() const = 0;
+
+ virtual bool isNull() const = 0;
+ virtual KeyType type() const = 0;
+ virtual KeyAlgorithm algorithm() const = 0;
+ virtual int length() const = 0;
+
+ virtual void clear(bool deepClear) = 0;
+
+ // Needed by QSslKeyPrivate::pemFromDer() for non-OpenSSL backends.
+ virtual bool isPkcs8() const = 0;
+
+ using Cipher = QSslKeyPrivate::Cipher;
+ virtual QByteArray decrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const = 0;
+ virtual QByteArray encrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const = 0;
+
+ // Those two are non-virtual, always the same and only depend on the key type
+ // and algorithm:
+ QByteArray pemHeader() const;
+ QByteArray pemFooter() const;
+};
// Abstraction above OpenSSL's X509, or our generic
// 'derData'-based code.
@@ -149,6 +192,12 @@ public:
static const QString builtinBackendNames[];
+ template<class DynamicType, class TLSObject>
+ static DynamicType *backend(const TLSObject &o)
+ {
+ return static_cast<DynamicType *>(o.backendImplementation());
+ }
+
Q_DISABLE_COPY_MOVE(QTlsBackend)
};
diff --git a/src/network/ssl/qtlskey_base.cpp b/src/network/ssl/qtlskey_base.cpp
new file mode 100644
index 0000000000..99bc0258bb
--- /dev/null
+++ b/src/network/ssl/qtlskey_base.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtlskey_base_p.h"
+#include "qasn1element_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QSsl {
+
+QByteArray TlsKeyBase::pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const
+{
+ QByteArray pem(der.toBase64());
+
+ const int lineWidth = 64; // RFC 1421
+ const int newLines = pem.size() / lineWidth;
+ const bool rem = pem.size() % lineWidth;
+
+ for (int i = 0; i < newLines; ++i)
+ pem.insert((i + 1) * lineWidth + i, '\n');
+ if (rem)
+ pem.append('\n');
+
+ QByteArray extra;
+ if (!headers.isEmpty()) {
+ QMap<QByteArray, QByteArray>::const_iterator it = headers.constEnd();
+ do {
+ --it;
+ extra += it.key() + ": " + it.value() + '\n';
+ } while (it != headers.constBegin());
+ extra += '\n';
+ }
+
+ if (isEncryptedPkcs8(der)) {
+ pem.prepend(pkcs8Header(true) + '\n' + extra);
+ pem.append(pkcs8Footer(true) + '\n');
+ } else if (isPkcs8()) {
+ pem.prepend(pkcs8Header(false) + '\n' + extra);
+ pem.append(pkcs8Footer(false) + '\n');
+ } else {
+ pem.prepend(pemHeader() + '\n' + extra);
+ pem.append(pemFooter() + '\n');
+ }
+
+ return pem;
+}
+
+QByteArray TlsKeyBase::pkcs8Header(bool encrypted)
+{
+ return encrypted
+ ? QByteArrayLiteral("-----BEGIN ENCRYPTED PRIVATE KEY-----")
+ : QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
+}
+
+QByteArray TlsKeyBase::pkcs8Footer(bool encrypted)
+{
+ return encrypted
+ ? QByteArrayLiteral("-----END ENCRYPTED PRIVATE KEY-----")
+ : QByteArrayLiteral("-----END PRIVATE KEY-----");
+}
+
+bool TlsKeyBase::isEncryptedPkcs8(const QByteArray &der)
+{
+ static const QList<QByteArray> pbes1OIds {
+ // PKCS5
+ { PKCS5_MD2_DES_CBC_OID }, { PKCS5_MD2_RC2_CBC_OID }, { PKCS5_MD5_DES_CBC_OID },
+ { PKCS5_MD5_RC2_CBC_OID }, { PKCS5_SHA1_DES_CBC_OID }, { PKCS5_SHA1_RC2_CBC_OID },
+ };
+ QAsn1Element elem;
+ if (!elem.read(der) || elem.type() != QAsn1Element::SequenceType)
+ return false;
+
+ const auto items = elem.toList();
+ if (items.size() != 2
+ || items[0].type() != QAsn1Element::SequenceType
+ || items[1].type() != QAsn1Element::OctetStringType) {
+ return false;
+ }
+
+ const auto encryptionSchemeContainer = items[0].toList();
+ if (encryptionSchemeContainer.size() != 2
+ || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType
+ || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) {
+ return false;
+ }
+
+ const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId();
+ return encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID
+ || pbes1OIds.contains(encryptionScheme)
+ || encryptionScheme.startsWith(PKCS12_OID);
+}
+
+} // namespace QSsl
+
+QT_END_NAMESPACE
+
+
diff --git a/src/network/ssl/qtlskey_base_p.h b/src/network/ssl/qtlskey_base_p.h
new file mode 100644
index 0000000000..48850aa781
--- /dev/null
+++ b/src/network/ssl/qtlskey_base_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTLSKEY_BASE_P_H
+#define QTLSKEY_BASE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtnetworkglobal_p.h>
+
+#include <private/qtlsbackend_p.h>
+
+#include <qssl.h>
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSsl {
+
+// TLSTODO: Note, 'base' is supposed to move to plugins together with
+// 'generic' and 'backendXXX'.
+class TlsKeyBase : public TlsKey
+{
+public:
+ bool isNull() const override
+ {
+ return keyIsNull;
+ }
+ QSsl::KeyType type() const override
+ {
+ return keyType;
+ }
+ QSsl::KeyAlgorithm algorithm() const override
+ {
+ return keyAlgorithm;
+ }
+ bool isPkcs8 () const override
+ {
+ return false;
+ }
+
+ QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const override;
+
+protected:
+ static QByteArray pkcs8Header(bool encrypted);
+ static QByteArray pkcs8Footer(bool encrypted);
+ static bool isEncryptedPkcs8(const QByteArray &der);
+
+ bool keyIsNull = true;
+ QSsl::KeyType keyType = QSsl::PublicKey;
+ QSsl::KeyAlgorithm keyAlgorithm = QSsl::Opaque;
+};
+
+} // namespace QSsl
+
+QT_END_NAMESPACE
+
+#endif // QTLSKEY_BASE_P_H
diff --git a/src/network/ssl/qtlskey_generic.cpp b/src/network/ssl/qtlskey_generic.cpp
new file mode 100644
index 0000000000..5333f28814
--- /dev/null
+++ b/src/network/ssl/qtlskey_generic.cpp
@@ -0,0 +1,827 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtlskey_generic_p.h"
+#include "qasn1element_p.h"
+#include "qsslkey_p.h"
+
+#include <QtNetwork/qpassworddigestor.h>
+
+#include <QtCore/QMessageAuthenticationCode>
+#include <QtCore/qcryptographichash.h>
+#include <QtCore/qrandom.h>
+
+#include <QtCore/qdatastream.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qmap.h>
+
+#include <cstring>
+
+QT_BEGIN_NAMESPACE
+
+// The code here is essentially what we had in qsslkey_qt.cpp before, with
+// minimal changes/restructure.
+
+namespace QSsl {
+// OIDs of named curves allowed in TLS as per RFCs 4492 and 7027,
+// see also https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
+namespace {
+
+const quint8 bits_table[256] = {
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+};
+
+using OidLengthMap = QMap<QByteArray, int>;
+
+OidLengthMap createOidMap()
+{
+ OidLengthMap oids;
+ oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.10045.3.1.1"), 192); // secp192r1 a.k.a prime192v1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.10045.3.1.7"), 256); // secp256r1 a.k.a prime256v1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.1"), 193); // sect193r2
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.10"), 256); // secp256k1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.16"), 283); // sect283k1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.17"), 283); // sect283r1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.26"), 233); // sect233k1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.27"), 233); // sect233r1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.3"), 239); // sect239k1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.30"), 160); // secp160r2
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.31"), 192); // secp192k1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.32"), 224); // secp224k1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.33"), 224); // secp224r1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.34"), 384); // secp384r1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.35"), 521); // secp521r1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.36"), 409); // sect409k1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.37"), 409); // sect409r1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.38"), 571); // sect571k1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.39"), 571); // sect571r1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.8"), 160); // secp160r1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.9"), 160); // secp160k1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.11"), 384); // brainpoolP384r1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.13"), 512); // brainpoolP512r1
+ oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.7"), 256); // brainpoolP256r1
+ return oids;
+}
+
+} // Unnamed namespace.
+
+Q_GLOBAL_STATIC_WITH_ARGS(OidLengthMap, oidLengthMap, (createOidMap()))
+
+namespace {
+
+// Maps OIDs to the encryption cipher they specify
+const QMap<QByteArray, QSslKeyPrivate::Cipher> oidCipherMap {
+ {DES_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesCbc},
+ {DES_EDE3_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesEde3Cbc},
+ // {PKCS5_MD2_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc}, // No MD2
+ {PKCS5_MD5_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc},
+ {PKCS5_SHA1_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc},
+ // {PKCS5_MD2_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc}, // No MD2
+ {PKCS5_MD5_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc},
+ {PKCS5_SHA1_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc},
+ {RC2_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc2Cbc}
+ // {RC5_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc5Cbc}, // No RC5
+ // {AES128_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes128}, // no AES
+ // {AES192_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes192},
+ // {AES256_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes256}
+};
+
+struct EncryptionData
+{
+ EncryptionData() = default;
+
+ EncryptionData(QSslKeyPrivate::Cipher cipher, QByteArray key, QByteArray iv)
+ : initialized(true), cipher(cipher), key(key), iv(iv)
+ {
+ }
+ bool initialized = false;
+ QSslKeyPrivate::Cipher cipher;
+ QByteArray key;
+ QByteArray iv;
+};
+
+EncryptionData readPbes2(const QList<QAsn1Element> &element, const QByteArray &passPhrase)
+{
+ // RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.2
+ /*** Scheme: ***
+ * Sequence (scheme-specific info..)
+ * Sequence (key derivation info)
+ * Object Identifier (Key derivation algorithm (e.g. PBKDF2))
+ * Sequence (salt)
+ * CHOICE (this entry can be either of the types it contains)
+ * Octet string (actual salt)
+ * Object identifier (Anything using this is deferred to a later version of PKCS #5)
+ * Integer (iteration count)
+ * Sequence (encryption algorithm info)
+ * Object identifier (identifier for the algorithm)
+ * Algorithm dependent, is covered in the switch further down
+ */
+
+ static const QMap<QByteArray, QCryptographicHash::Algorithm> pbes2OidHashFunctionMap {
+ // PBES2/PBKDF2
+ {HMAC_WITH_SHA1, QCryptographicHash::Sha1},
+ {HMAC_WITH_SHA224, QCryptographicHash::Sha224},
+ {HMAC_WITH_SHA256, QCryptographicHash::Sha256},
+ {HMAC_WITH_SHA512, QCryptographicHash::Sha512},
+ {HMAC_WITH_SHA512_224, QCryptographicHash::Sha512},
+ {HMAC_WITH_SHA512_256, QCryptographicHash::Sha512},
+ {HMAC_WITH_SHA384, QCryptographicHash::Sha384}
+ };
+
+ // Values from their respective sections here: https://tools.ietf.org/html/rfc8018#appendix-B.2
+ static const QMap<QSslKeyPrivate::Cipher, int> cipherKeyLengthMap {
+ {QSslKeyPrivate::Cipher::DesCbc, 8},
+ {QSslKeyPrivate::Cipher::DesEde3Cbc, 24},
+ // @note: variable key-length (https://tools.ietf.org/html/rfc8018#appendix-B.2.3)
+ {QSslKeyPrivate::Cipher::Rc2Cbc, 4}
+ // @todo: AES(, rc5?)
+ };
+
+ const QList<QAsn1Element> keyDerivationContainer = element[0].toList();
+ if (keyDerivationContainer.size() != 2
+ || keyDerivationContainer[0].type() != QAsn1Element::ObjectIdentifierType
+ || keyDerivationContainer[1].type() != QAsn1Element::SequenceType) {
+ return {};
+ }
+
+ const QByteArray keyDerivationAlgorithm = keyDerivationContainer[0].toObjectId();
+ const auto keyDerivationParams = keyDerivationContainer[1].toList();
+
+ const auto encryptionAlgorithmContainer = element[1].toList();
+ if (encryptionAlgorithmContainer.size() != 2
+ || encryptionAlgorithmContainer[0].type() != QAsn1Element::ObjectIdentifierType) {
+ return {};
+ }
+
+ auto iterator = oidCipherMap.constFind(encryptionAlgorithmContainer[0].toObjectId());
+ if (iterator == oidCipherMap.cend()) {
+ qWarning()
+ << "QSslKey: Unsupported encryption cipher OID:" << encryptionAlgorithmContainer[0].toObjectId()
+ << "\nFile a bug report to Qt (include the line above).";
+ return {};
+ }
+
+ QSslKeyPrivate::Cipher cipher = *iterator;
+ QByteArray key;
+ QByteArray iv;
+ switch (cipher) {
+ case QSslKeyPrivate::Cipher::DesCbc:
+ case QSslKeyPrivate::Cipher::DesEde3Cbc:
+ // https://tools.ietf.org/html/rfc8018#appendix-B.2.1 (DES-CBC-PAD)
+ // https://tools.ietf.org/html/rfc8018#appendix-B.2.2 (DES-EDE3-CBC-PAD)
+ // @todo https://tools.ietf.org/html/rfc8018#appendix-B.2.5 (AES-CBC-PAD)
+ /*** Scheme: ***
+ * Octet string (IV)
+ */
+ if (encryptionAlgorithmContainer[1].type() != QAsn1Element::OctetStringType)
+ return {};
+
+ // @note: All AES identifiers should be able to use this branch!!
+ iv = encryptionAlgorithmContainer[1].value();
+
+ if (iv.size() != 8) // @note: AES needs 16 bytes
+ return {};
+ break;
+ case QSslKeyPrivate::Cipher::Rc2Cbc: {
+ // https://tools.ietf.org/html/rfc8018#appendix-B.2.3
+ /*** Scheme: ***
+ * Sequence (rc2 parameters)
+ * Integer (rc2 parameter version)
+ * Octet string (IV)
+ */
+ if (encryptionAlgorithmContainer[1].type() != QAsn1Element::SequenceType)
+ return {};
+ const auto rc2ParametersContainer = encryptionAlgorithmContainer[1].toList();
+ if ((rc2ParametersContainer.size() != 1 && rc2ParametersContainer.size() != 2)
+ || rc2ParametersContainer.back().type() != QAsn1Element::OctetStringType) {
+ return {};
+ }
+ iv = rc2ParametersContainer.back().value();
+ if (iv.size() != 8)
+ return {};
+ break;
+ } // @todo(?): case (RC5 , AES)
+ case QSslKeyPrivate::Cipher::Aes128Cbc:
+ case QSslKeyPrivate::Cipher::Aes192Cbc:
+ case QSslKeyPrivate::Cipher::Aes256Cbc:
+ Q_UNREACHABLE();
+ }
+
+ if (Q_LIKELY(keyDerivationAlgorithm == PKCS5_PBKDF2_ENCRYPTION_OID)) {
+ // Definition: https://tools.ietf.org/html/rfc8018#appendix-A.2
+ QByteArray salt;
+ if (keyDerivationParams[0].type() == QAsn1Element::OctetStringType) {
+ salt = keyDerivationParams[0].value();
+ } else if (keyDerivationParams[0].type() == QAsn1Element::ObjectIdentifierType) {
+ Q_UNIMPLEMENTED();
+ /* See paragraph from https://tools.ietf.org/html/rfc8018#appendix-A.2
+ which ends with: "such facilities are deferred to a future version of PKCS #5"
+ */
+ return {};
+ } else {
+ return {};
+ }
+
+ // Iterations needed to derive the key
+ int iterationCount = keyDerivationParams[1].toInteger();
+ // Optional integer
+ int keyLength = -1;
+ int vectorPos = 2;
+ if (keyDerivationParams.size() > vectorPos
+ && keyDerivationParams[vectorPos].type() == QAsn1Element::IntegerType) {
+ keyLength = keyDerivationParams[vectorPos].toInteger(nullptr);
+ ++vectorPos;
+ } else {
+ keyLength = cipherKeyLengthMap[cipher];
+ }
+
+ // Optional algorithm identifier (default: HMAC-SHA-1)
+ QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1;
+ if (keyDerivationParams.size() > vectorPos
+ && keyDerivationParams[vectorPos].type() == QAsn1Element::SequenceType) {
+ const auto hashAlgorithmContainer = keyDerivationParams[vectorPos].toList();
+ hashAlgorithm = pbes2OidHashFunctionMap[hashAlgorithmContainer.front().toObjectId()];
+ Q_ASSERT(hashAlgorithmContainer[1].type() == QAsn1Element::NullType);
+ ++vectorPos;
+ }
+ Q_ASSERT(keyDerivationParams.size() == vectorPos);
+
+ key = QPasswordDigestor::deriveKeyPbkdf2(hashAlgorithm, passPhrase, salt, iterationCount, keyLength);
+ } else {
+ qWarning()
+ << "QSslKey: Unsupported key derivation algorithm OID:" << keyDerivationAlgorithm
+ << "\nFile a bugreport to Qt (include the line above).";
+ return {};
+ }
+ return {cipher, key, iv};
+}
+
+// Maps OIDs to the hash function it specifies
+const QMap<QByteArray, QCryptographicHash::Algorithm> pbes1OidHashFunctionMap {
+#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+ // PKCS5
+ //{PKCS5_MD2_DES_CBC_OID, QCryptographicHash::Md2}, No MD2
+ //{PKCS5_MD2_RC2_CBC_OID, QCryptographicHash::Md2},
+ {PKCS5_MD5_DES_CBC_OID, QCryptographicHash::Md5},
+ {PKCS5_MD5_RC2_CBC_OID, QCryptographicHash::Md5},
+#endif
+ {PKCS5_SHA1_DES_CBC_OID, QCryptographicHash::Sha1},
+ {PKCS5_SHA1_RC2_CBC_OID, QCryptographicHash::Sha1},
+ // PKCS12 (unimplemented)
+ // {PKCS12_SHA1_RC4_128_OID, QCryptographicHash::Sha1}, // No RC4
+ // {PKCS12_SHA1_RC4_40_OID, QCryptographicHash::Sha1},
+ // @todo: lacking support. @note: there might be code to do this inside qsslsocket_mac...
+ // further note that more work may be required for the 3DES variations listed to be available.
+ // {PKCS12_SHA1_3KEY_3DES_CBC_OID, QCryptographicHash::Sha1},
+ // {PKCS12_SHA1_2KEY_3DES_CBC_OID, QCryptographicHash::Sha1},
+ // {PKCS12_SHA1_RC2_128_CBC_OID, QCryptographicHash::Sha1},
+ // {PKCS12_SHA1_RC2_40_CBC_OID, QCryptographicHash::Sha1}
+};
+
+EncryptionData readPbes1(const QList<QAsn1Element> &element, const QByteArray &encryptionScheme,
+ const QByteArray &passPhrase)
+{
+ // RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.1
+ // Steps refer to this section: https://tools.ietf.org/html/rfc8018#section-6.1.2
+ /*** Scheme: ***
+ * Sequence (PBE Parameter)
+ * Octet string (salt)
+ * Integer (iteration counter)
+ */
+ // Step 1
+ if (element.size() != 2
+ || element[0].type() != QAsn1Element::ElementType::OctetStringType
+ || element[1].type() != QAsn1Element::ElementType::IntegerType) {
+ return {};
+ }
+ QByteArray salt = element[0].value();
+ if (salt.size() != 8)
+ return {};
+
+ int iterationCount = element[1].toInteger();
+ if (iterationCount < 0)
+ return {};
+
+ // Step 2
+ auto iterator = pbes1OidHashFunctionMap.constFind(encryptionScheme);
+ if (iterator == pbes1OidHashFunctionMap.cend()) {
+ // Qt was compiled with ONLY_SHA1 (or it's MD2)
+ return {};
+ }
+ QCryptographicHash::Algorithm hashAlgorithm = *iterator;
+ QByteArray key = QPasswordDigestor::deriveKeyPbkdf1(hashAlgorithm, passPhrase, salt, iterationCount, 16);
+ if (key.size() != 16)
+ return {};
+
+ // Step 3
+ QByteArray iv = key.right(8); // last 8 bytes are used as IV
+ key.truncate(8); // first 8 bytes are used for the key
+
+ QSslKeyPrivate::Cipher cipher = oidCipherMap[encryptionScheme];
+ // Steps 4-6 are done after returning
+ return {cipher, key, iv};
+}
+
+int curveBits(const QByteArray &oid)
+{
+ const int length = oidLengthMap->value(oid);
+ return length ? length : -1;
+}
+
+int numberOfBits(const QByteArray &modulus)
+{
+ int bits = modulus.size() * 8;
+ for (int i = 0; i < modulus.size(); ++i) {
+ quint8 b = modulus[i];
+ bits -= 8;
+ if (b != 0) {
+ bits += bits_table[b];
+ break;
+ }
+ }
+ return bits;
+}
+
+QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase,
+ const QByteArray &iv)
+{
+ // This is somewhat simplified and shortened version of what OpenSSL does.
+ // See, for example, EVP_BytesToKey for the "algorithm" itself and elsewhere
+ // in their code for what they pass as arguments to EVP_BytesToKey when
+ // deriving encryption keys (when reading/writing pems files with encrypted
+ // keys).
+
+ Q_ASSERT(iv.size() >= 8);
+
+ QCryptographicHash hash(QCryptographicHash::Md5);
+
+ QByteArray data(passPhrase);
+ data.append(iv.data(), 8); // AKA PKCS5_SALT_LEN in OpenSSL.
+
+ hash.addData(data);
+
+ if (cipher == QSslKeyPrivate::Aes128Cbc)
+ return hash.result();
+
+ QByteArray key(hash.result());
+ hash.reset();
+ hash.addData(key);
+ hash.addData(data);
+
+ if (cipher == QSslKeyPrivate::Aes192Cbc)
+ return key.append(hash.result().constData(), 8);
+
+ return key.append(hash.result());
+}
+
+QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase,
+ const QByteArray &iv)
+{
+ QByteArray key;
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ hash.addData(passPhrase);
+ hash.addData(iv);
+ switch (cipher) {
+ case QSslKeyPrivate::DesCbc:
+ key = hash.result().left(8);
+ break;
+ case QSslKeyPrivate::DesEde3Cbc:
+ key = hash.result();
+ hash.reset();
+ hash.addData(key);
+ hash.addData(passPhrase);
+ hash.addData(iv);
+ key += hash.result().left(8);
+ break;
+ case QSslKeyPrivate::Rc2Cbc:
+ key = hash.result();
+ break;
+ case QSslKeyPrivate::Aes128Cbc:
+ case QSslKeyPrivate::Aes192Cbc:
+ case QSslKeyPrivate::Aes256Cbc:
+ return deriveAesKey(cipher, passPhrase, iv);
+ }
+ return key;
+}
+
+int extractPkcs8KeyLength(const QList<QAsn1Element> &items, TlsKey *that)
+{
+ Q_ASSERT(items.size() == 3);
+ Q_ASSERT(that);
+
+ int keyLength = -1;
+
+ auto getName = [](QSsl::KeyAlgorithm algorithm) {
+ switch (algorithm){
+ case QSsl::Rsa: return "RSA";
+ case QSsl::Dsa: return "DSA";
+ case QSsl::Dh: return "DH";
+ case QSsl::Ec: return "EC";
+ case QSsl::Opaque: return "Opaque";
+ }
+ Q_UNREACHABLE();
+ };
+
+ const auto pkcs8Info = items[1].toList();
+ if (pkcs8Info.size() != 2 || pkcs8Info[0].type() != QAsn1Element::ObjectIdentifierType)
+ return -1;
+ const QByteArray value = pkcs8Info[0].toObjectId();
+ if (value == RSA_ENCRYPTION_OID) {
+ if (Q_UNLIKELY(that->algorithm() != QSsl::Rsa)) {
+ // We could change the 'algorithm' of QSslKey here and continue loading, but
+ // this is not supported in the openssl back-end, so we'll fail here and give
+ // the user some feedback.
+ qWarning() << "QSslKey: Found RSA key when asked to use" << getName(that->algorithm())
+ << "\nLoading will fail.";
+ return -1;
+ }
+ // Luckily it contains the 'normal' RSA-key format inside, so we can just recurse
+ // and read the key's info.
+ that->decodeDer(that->type(), that->algorithm(), items[2].value(), {}, true);
+ // The real info has been filled out in the call above, so return as if it was invalid
+ // to avoid overwriting the data.
+ return -1;
+ } else if (value == EC_ENCRYPTION_OID) {
+ if (Q_UNLIKELY(that->algorithm() != QSsl::Ec)) {
+ // As above for RSA.
+ qWarning() << "QSslKey: Found EC key when asked to use" << getName(that->algorithm())
+ << "\nLoading will fail.";
+ return -1;
+ }
+ // I don't know where this is documented, but the elliptic-curve identifier has been
+ // moved into the "pkcs#8 wrapper", which is what we're interested in.
+ if (pkcs8Info[1].type() != QAsn1Element::ObjectIdentifierType)
+ return -1;
+ keyLength = curveBits(pkcs8Info[1].toObjectId());
+ } else if (value == DSA_ENCRYPTION_OID) {
+ if (Q_UNLIKELY(that->algorithm() != QSsl::Dsa)) {
+ // As above for RSA.
+ qWarning() << "QSslKey: Found DSA when asked to use" << getName(that->algorithm())
+ << "\nLoading will fail.";
+ return -1;
+ }
+ // DSA's structure is documented here:
+ // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9.
+ if (pkcs8Info[1].type() != QAsn1Element::SequenceType)
+ return -1;
+ const auto dsaInfo = pkcs8Info[1].toList();
+ if (dsaInfo.size() != 3 || dsaInfo[0].type() != QAsn1Element::IntegerType)
+ return -1;
+ keyLength = numberOfBits(dsaInfo[0].value());
+ } else if (value == DH_ENCRYPTION_OID) {
+ if (Q_UNLIKELY(that->algorithm() != QSsl::Dh)) {
+ // As above for RSA.
+ qWarning() << "QSslKey: Found DH when asked to use" << getName(that->algorithm())
+ << "\nLoading will fail.";
+ return -1;
+ }
+ // DH's structure is documented here:
+ // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9.
+ if (pkcs8Info[1].type() != QAsn1Element::SequenceType)
+ return -1;
+ const auto dhInfo = pkcs8Info[1].toList();
+ if (dhInfo.size() < 2 || dhInfo.size() > 3 || dhInfo[0].type() != QAsn1Element::IntegerType)
+ return -1;
+ keyLength = numberOfBits(dhInfo[0].value());
+ } else {
+ // in case of unexpected formats:
+ qWarning() << "QSslKey: Unsupported PKCS#8 key algorithm:" << value
+ << "\nFile a bugreport to Qt (include the line above).";
+ return -1;
+ }
+
+ return keyLength;
+}
+
+} // Unnamed namespace
+
+void TlsKeyGeneric::decodeDer(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm, const QByteArray &der,
+ const QByteArray &passPhrase, bool deepClear)
+{
+ keyType = type;
+ keyAlgorithm = algorithm;
+
+ clear(deepClear);
+
+ if (der.isEmpty())
+ return;
+ // decryptPkcs8 decrypts if necessary or returns 'der' unaltered
+ QByteArray decryptedDer = decryptPkcs8(der, passPhrase);
+
+ QAsn1Element elem;
+ if (!elem.read(decryptedDer) || elem.type() != QAsn1Element::SequenceType)
+ return;
+
+ if (type == QSsl::PublicKey) {
+ // key info
+ QDataStream keyStream(elem.value());
+ if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType)
+ return;
+ const auto infoItems = elem.toList();
+ if (infoItems.size() < 2 || infoItems[0].type() != QAsn1Element::ObjectIdentifierType)
+ return;
+ if (algorithm == QSsl::Rsa) {
+ if (infoItems[0].toObjectId() != RSA_ENCRYPTION_OID)
+ return;
+ // key data
+ if (!elem.read(keyStream) || elem.type() != QAsn1Element::BitStringType || elem.value().isEmpty())
+ return;
+ if (!elem.read(elem.value().mid(1)) || elem.type() != QAsn1Element::SequenceType)
+ return;
+ if (!elem.read(elem.value()) || elem.type() != QAsn1Element::IntegerType)
+ return;
+ keyLength = numberOfBits(elem.value());
+ } else if (algorithm == QSsl::Dsa) {
+ if (infoItems[0].toObjectId() != DSA_ENCRYPTION_OID)
+ return;
+ if (infoItems[1].type() != QAsn1Element::SequenceType)
+ return;
+ // key params
+ const auto params = infoItems[1].toList();
+ if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType)
+ return;
+ keyLength = numberOfBits(params[0].value());
+ } else if (algorithm == QSsl::Dh) {
+ if (infoItems[0].toObjectId() != DH_ENCRYPTION_OID)
+ return;
+ if (infoItems[1].type() != QAsn1Element::SequenceType)
+ return;
+ // key params
+ const auto params = infoItems[1].toList();
+ if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType)
+ return;
+ keyLength = numberOfBits(params[0].value());
+ } else if (algorithm == QSsl::Ec) {
+ if (infoItems[0].toObjectId() != EC_ENCRYPTION_OID)
+ return;
+ if (infoItems[1].type() != QAsn1Element::ObjectIdentifierType)
+ return;
+ keyLength = curveBits(infoItems[1].toObjectId());
+ }
+
+ } else {
+ const auto items = elem.toList();
+ if (items.isEmpty())
+ return;
+
+ // version
+ if (items[0].type() != QAsn1Element::IntegerType)
+ return;
+ const QByteArray versionHex = items[0].value().toHex();
+
+ if (items.size() == 3 && items[1].type() == QAsn1Element::SequenceType
+ && items[2].type() == QAsn1Element::OctetStringType) {
+ if (versionHex != "00" && versionHex != "01")
+ return;
+ int pkcs8KeyLength = extractPkcs8KeyLength(items, this);
+ if (pkcs8KeyLength == -1)
+ return;
+ pkcs8 = true;
+ keyLength = pkcs8KeyLength;
+ } else if (algorithm == QSsl::Rsa) {
+ if (versionHex != "00")
+ return;
+ if (items.size() != 9 || items[1].type() != QAsn1Element::IntegerType)
+ return;
+ keyLength = numberOfBits(items[1].value());
+ } else if (algorithm == QSsl::Dsa) {
+ if (versionHex != "00")
+ return;
+ if (items.size() != 6 || items[1].type() != QAsn1Element::IntegerType)
+ return;
+ keyLength = numberOfBits(items[1].value());
+ } else if (algorithm == QSsl::Dh) {
+ if (versionHex != "00")
+ return;
+ if (items.size() < 5 || items.size() > 6 || items[1].type() != QAsn1Element::IntegerType)
+ return;
+ keyLength = numberOfBits(items[1].value());
+ } else if (algorithm == QSsl::Ec) {
+ if (versionHex != "01")
+ return;
+ if (items.size() != 4
+ || items[1].type() != QAsn1Element::OctetStringType
+ || items[2].type() != QAsn1Element::Context0Type
+ || items[3].type() != QAsn1Element::Context1Type)
+ return;
+ QAsn1Element oidElem;
+ if (!oidElem.read(items[2].value())
+ || oidElem.type() != QAsn1Element::ObjectIdentifierType)
+ return;
+ keyLength = curveBits(oidElem.toObjectId());
+ }
+ }
+
+ derData = decryptedDer;
+ keyIsNull = false;
+}
+
+void TlsKeyGeneric::decodePem(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm, const QByteArray &pem,
+ const QByteArray &passPhrase, bool deepClear)
+{
+ keyType = type;
+ keyAlgorithm = algorithm;
+
+ QMap<QByteArray, QByteArray> headers;
+ QByteArray data = derFromPem(pem, &headers);
+
+ if (headers.value("Proc-Type") == "4,ENCRYPTED") {
+ const QList<QByteArray> dekInfo = headers.value("DEK-Info").split(',');
+ if (dekInfo.size() != 2) {
+ clear(deepClear);
+ return;
+ }
+
+ QSslKeyPrivate::Cipher cipher;
+ if (dekInfo.first() == "DES-CBC") {
+ cipher = QSslKeyPrivate::DesCbc;
+ } else if (dekInfo.first() == "DES-EDE3-CBC") {
+ cipher = QSslKeyPrivate::DesEde3Cbc;
+ } else if (dekInfo.first() == "RC2-CBC") {
+ cipher = QSslKeyPrivate::Rc2Cbc;
+ } else if (dekInfo.first() == "AES-128-CBC") {
+ cipher = QSslKeyPrivate::Aes128Cbc;
+ } else if (dekInfo.first() == "AES-192-CBC") {
+ cipher = QSslKeyPrivate::Aes192Cbc;
+ } else if (dekInfo.first() == "AES-256-CBC") {
+ cipher = QSslKeyPrivate::Aes256Cbc;
+ } else {
+ clear(deepClear);
+ return;
+ }
+
+ const QByteArray iv = QByteArray::fromHex(dekInfo.last());
+ const QByteArray key = deriveKey(cipher, passPhrase, iv);
+ data = decrypt(cipher, data, key, iv);
+ }
+
+ decodeDer(keyType, keyAlgorithm, data, passPhrase, deepClear);
+}
+
+QByteArray TlsKeyGeneric::toPem(const QByteArray &passPhrase) const
+{
+ QByteArray data;
+ QMap<QByteArray, QByteArray> headers;
+
+ if (type() == QSsl::PrivateKey && !passPhrase.isEmpty()) {
+ // ### use a cryptographically secure random number generator
+ quint64 random = QRandomGenerator::system()->generate64();
+ QByteArray iv = QByteArray::fromRawData(reinterpret_cast<const char *>(&random), sizeof(random));
+
+ auto cipher = QSslKeyPrivate::DesEde3Cbc;
+ const QByteArray key = deriveKey(cipher, passPhrase, iv);
+ data = encrypt(cipher, derData, key, iv);
+
+ headers.insert("Proc-Type", "4,ENCRYPTED");
+ headers.insert("DEK-Info", "DES-EDE3-CBC," + iv.toHex());
+ } else {
+ data = derData;
+ }
+
+ return pemFromDer(data, headers);
+}
+
+QByteArray TlsKeyGeneric::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
+{
+ Q_UNUSED(pem)
+ Q_UNUSED(headers)
+
+ // This is quite an ugly hack, but so be it. Generic is using der, this 'pem' is coming from
+ // 'toPem()' for OpenSSL.
+ return derData;
+}
+
+void TlsKeyGeneric::fromHandle(Qt::HANDLE handle, KeyType expectedType)
+{
+ opaque = handle;
+ keyType = expectedType;
+}
+
+void TlsKeyGeneric::clear(bool deep)
+{
+ keyIsNull = true;
+ if (deep)
+ std::memset(derData.data(), 0, derData.size());
+ derData.clear();
+ keyLength = -1;
+}
+
+QByteArray TlsKeyGeneric::decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase)
+{
+ // RFC 5958: https://tools.ietf.org/html/rfc5958
+ /*** Scheme: ***
+ * Sequence
+ * Sequence
+ * Object Identifier (encryption scheme (currently PBES2, PBES1, @todo PKCS12))
+ * Sequence (scheme parameters)
+ * Octet String (the encrypted data)
+ */
+ QAsn1Element elem;
+ if (!elem.read(encrypted) || elem.type() != QAsn1Element::SequenceType)
+ return encrypted;
+
+ const auto items = elem.toList();
+ if (items.size() != 2
+ || items[0].type() != QAsn1Element::SequenceType
+ || items[1].type() != QAsn1Element::OctetStringType) {
+ return encrypted;
+ }
+
+ const auto encryptionSchemeContainer = items[0].toList();
+
+ if (encryptionSchemeContainer.size() != 2
+ || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType
+ || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) {
+ return encrypted;
+ }
+
+ const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId();
+ const auto schemeParameterContainer = encryptionSchemeContainer[1].toList();
+
+ if (schemeParameterContainer.size() != 2
+ && schemeParameterContainer[0].type() != QAsn1Element::SequenceType
+ && schemeParameterContainer[1].type() != QAsn1Element::SequenceType) {
+ return encrypted;
+ }
+
+ EncryptionData data;
+ if (encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID) {
+ data = readPbes2(schemeParameterContainer, passPhrase);
+ } else if (pbes1OidHashFunctionMap.contains(encryptionScheme)) {
+ data = readPbes1(schemeParameterContainer, encryptionScheme, passPhrase);
+ } else if (encryptionScheme.startsWith(PKCS12_OID)) {
+ Q_UNIMPLEMENTED(); // this isn't some 'unknown', I know these aren't implemented
+ return encrypted;
+ } else {
+ qWarning()
+ << "QSslKey: Unsupported encryption scheme OID:" << encryptionScheme
+ << "\nFile a bugreport to Qt (include the line above).";
+ return encrypted;
+ }
+
+ if (!data.initialized) {
+ // something went wrong, return
+ return encrypted;
+ }
+
+ QByteArray decryptedKey = decrypt(data.cipher, items[1].value(), data.key, data.iv);
+ // The data is still wrapped in a octet string, so let's unwrap it
+ QAsn1Element decryptedKeyElement(QAsn1Element::ElementType::OctetStringType, decryptedKey);
+ return decryptedKeyElement.value();
+}
+
+} // namespace QSsl
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qtlskey_generic_p.h b/src/network/ssl/qtlskey_generic_p.h
new file mode 100644
index 0000000000..b7dcc05be2
--- /dev/null
+++ b/src/network/ssl/qtlskey_generic_p.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTLSKEY_GENERIC_P_H
+#define QTLSKEY_GENERIC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtnetworkglobal_p.h>
+
+#include <private/qtlskey_base_p.h>
+#include <private/qtlsbackend_p.h>
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSsl {
+
+// This class is what previously was known as qsslkey_qt:
+// it implements most of functionality needed by QSslKey
+// not relying on any TLS implementation. It's used by
+// our SecureTransport and Schannel backends. This
+// class is still an abstract class, since it does not
+// provide encryption and decryption - a part done by
+// a real TLS implementation.
+class TlsKeyGeneric : public TlsKeyBase
+{
+public:
+ TlsKeyGeneric()
+ {
+ // Note, while clear is pure-virtual, the final-overrider
+ // in this class is sufficient. Same for d-tor below.
+ clear(false);
+ }
+ ~TlsKeyGeneric()
+ {
+ clear(true);
+ }
+
+ void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der,
+ const QByteArray &passPhrase, bool deepClear) override;
+ void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem,
+ const QByteArray &passPhrase, bool deepClear) override;
+
+ QByteArray toPem(const QByteArray &passPhrase) const override;
+
+ QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray,
+ QByteArray> *headers) const override;
+
+ void fromHandle(Qt::HANDLE opaque, KeyType expectedType) override;
+
+ void clear(bool deep) override;
+
+ Qt::HANDLE handle() const override
+ {
+ return Qt::HANDLE(opaque);
+ }
+
+ int length() const override
+ {
+ return keyLength;
+ }
+
+ bool isPkcs8() const override
+ {
+ return pkcs8;
+ }
+private:
+ QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase);
+
+ bool pkcs8 = false;
+ Qt::HANDLE opaque = nullptr;
+ QByteArray derData;
+ int keyLength = -1;
+};
+
+} // namespace QSsl
+
+QT_END_NAMESPACE
+
+#endif // QTLSKEY_GENERIC_P_H
diff --git a/src/network/ssl/qtlskey_openssl.cpp b/src/network/ssl/qtlskey_openssl.cpp
new file mode 100644
index 0000000000..6517be2d76
--- /dev/null
+++ b/src/network/ssl/qtlskey_openssl.cpp
@@ -0,0 +1,509 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsslsocket_openssl_symbols_p.h"
+#include "qtlskey_openssl_p.h"
+#include "qsslsocket.h"
+#include "qsslkey_p.h"
+
+#include <qscopeguard.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSsl {
+
+void TlsKeyOpenSSL::decodeDer(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm, const QByteArray &der,
+ const QByteArray &passPhrase, bool deepClear)
+{
+ if (der.isEmpty())
+ return;
+
+ keyType = type;
+ keyAlgorithm = algorithm;
+
+ QMap<QByteArray, QByteArray> headers;
+ const auto pem = pemFromDer(der, headers);
+
+ decodePem(type, algorithm, pem, passPhrase, deepClear);
+}
+
+void TlsKeyOpenSSL::decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem,
+ const QByteArray &passPhrase, bool deepClear)
+{
+ if (pem.isEmpty())
+ return;
+
+ keyType = type;
+ keyAlgorithm = algorithm;
+
+ clear(deepClear);
+
+ BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
+ if (!bio)
+ return;
+
+ const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
+
+ void *phrase = const_cast<char *>(passPhrase.data());
+
+ if (algorithm == QSsl::Rsa) {
+ RSA *result = (type == QSsl::PublicKey)
+ ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, nullptr, phrase)
+ : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, nullptr, phrase);
+ if (rsa && rsa == result)
+ keyIsNull = false;
+ } else if (algorithm == QSsl::Dsa) {
+ DSA *result = (type == QSsl::PublicKey)
+ ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, nullptr, phrase)
+ : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, nullptr, phrase);
+ if (dsa && dsa == result)
+ keyIsNull = false;
+ } else if (algorithm == QSsl::Dh) {
+ EVP_PKEY *result = (type == QSsl::PublicKey)
+ ? q_PEM_read_bio_PUBKEY(bio, nullptr, nullptr, phrase)
+ : q_PEM_read_bio_PrivateKey(bio, nullptr, nullptr, phrase);
+ if (result)
+ dh = q_EVP_PKEY_get1_DH(result);
+ if (dh)
+ keyIsNull = false;
+ q_EVP_PKEY_free(result);
+#ifndef OPENSSL_NO_EC
+ } else if (algorithm == QSsl::Ec) {
+ EC_KEY *result = (type == QSsl::PublicKey)
+ ? q_PEM_read_bio_EC_PUBKEY(bio, &ec, nullptr, phrase)
+ : q_PEM_read_bio_ECPrivateKey(bio, &ec, nullptr, phrase);
+ if (ec && ec == result)
+ keyIsNull = false;
+#endif
+ }
+}
+
+QByteArray TlsKeyOpenSSL::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
+{
+ QByteArray header = pemHeader();
+ QByteArray footer = pemFooter();
+
+ QByteArray der(pem);
+
+ int headerIndex = der.indexOf(header);
+ int footerIndex = der.indexOf(footer, headerIndex + header.length());
+ if (type() != QSsl::PublicKey) {
+ if (headerIndex == -1 || footerIndex == -1) {
+ header = pkcs8Header(true);
+ footer = pkcs8Footer(true);
+ headerIndex = der.indexOf(header);
+ footerIndex = der.indexOf(footer, headerIndex + header.length());
+ }
+ if (headerIndex == -1 || footerIndex == -1) {
+ header = pkcs8Header(false);
+ footer = pkcs8Footer(false);
+ headerIndex = der.indexOf(header);
+ footerIndex = der.indexOf(footer, headerIndex + header.length());
+ }
+ }
+ if (headerIndex == -1 || footerIndex == -1)
+ return QByteArray();
+
+ der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));
+
+ if (der.contains("Proc-Type:")) {
+ // taken from QHttpNetworkReplyPrivate::parseHeader
+ int i = 0;
+ while (i < der.count()) {
+ int j = der.indexOf(':', i); // field-name
+ if (j == -1)
+ break;
+ const QByteArray field = der.mid(i, j - i).trimmed();
+ j++;
+ // any number of LWS is allowed before and after the value
+ QByteArray value;
+ do {
+ i = der.indexOf('\n', j);
+ if (i == -1)
+ break;
+ if (!value.isEmpty())
+ value += ' ';
+ // check if we have CRLF or only LF
+ bool hasCR = (i && der[i-1] == '\r');
+ int length = i -(hasCR ? 1: 0) - j;
+ value += der.mid(j, length).trimmed();
+ j = ++i;
+ } while (i < der.count() && (der.at(i) == ' ' || der.at(i) == '\t'));
+ if (i == -1)
+ break; // something is wrong
+
+ headers->insert(field, value);
+ }
+ der = der.mid(i);
+ }
+
+ return QByteArray::fromBase64(der); // ignores newlines
+}
+
+void TlsKeyOpenSSL::clear(bool deep)
+{
+ keyIsNull = true;
+
+ if (algorithm() == QSsl::Rsa && rsa) {
+ if (deep)
+ q_RSA_free(rsa);
+ rsa = nullptr;
+ }
+ if (algorithm() == QSsl::Dsa && dsa) {
+ if (deep)
+ q_DSA_free(dsa);
+ dsa = nullptr;
+ }
+ if (algorithm() == QSsl::Dh && dh) {
+ if (deep)
+ q_DH_free(dh);
+ dh = nullptr;
+ }
+#ifndef OPENSSL_NO_EC
+ if (algorithm() == QSsl::Ec && ec) {
+ if (deep)
+ q_EC_KEY_free(ec);
+ ec = nullptr;
+ }
+#endif
+ if (algorithm() == QSsl::Opaque && opaque) {
+ if (deep)
+ q_EVP_PKEY_free(opaque);
+ opaque = nullptr;
+ }
+}
+
+Qt::HANDLE TlsKeyOpenSSL::handle() const
+{
+ switch (keyAlgorithm) {
+ case QSsl::Opaque:
+ return Qt::HANDLE(opaque);
+ case QSsl::Rsa:
+ return Qt::HANDLE(rsa);
+ case QSsl::Dsa:
+ return Qt::HANDLE(dsa);
+ case QSsl::Dh:
+ return Qt::HANDLE(dh);
+#ifndef OPENSSL_NO_EC
+ case QSsl::Ec:
+ return Qt::HANDLE(ec);
+#endif
+ default:
+ return Qt::HANDLE(nullptr);
+ }
+}
+
+int TlsKeyOpenSSL::length() const
+{
+ if (isNull() || algorithm() == QSsl::Opaque)
+ return -1;
+
+ switch (algorithm()) {
+ case QSsl::Rsa:
+ return q_RSA_bits(rsa);
+ case QSsl::Dsa:
+ return q_DSA_bits(dsa);
+ case QSsl::Dh:
+ return q_DH_bits(dh);
+#ifndef OPENSSL_NO_EC
+ case QSsl::Ec:
+ return q_EC_GROUP_get_degree(q_EC_KEY_get0_group(ec));
+#endif
+ default:
+ return -1;
+ }
+}
+
+QByteArray TlsKeyOpenSSL::toPem(const QByteArray &passPhrase) const
+{
+ if (!QSslSocket::supportsSsl() || isNull() || algorithm() == QSsl::Opaque)
+ return {};
+
+ const EVP_CIPHER *cipher = nullptr;
+ if (type() == QSsl::PrivateKey && !passPhrase.isEmpty()) {
+#ifndef OPENSSL_NO_DES
+ cipher = q_EVP_des_ede3_cbc();
+#else
+ return {};
+#endif
+ }
+
+ BIO *bio = q_BIO_new(q_BIO_s_mem());
+ if (!bio)
+ return {};
+
+ const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
+
+ bool fail = false;
+
+ if (algorithm() == QSsl::Rsa) {
+ if (type() == QSsl::PublicKey) {
+ if (!q_PEM_write_bio_RSA_PUBKEY(bio, rsa))
+ fail = true;
+ } else {
+ if (!q_PEM_write_bio_RSAPrivateKey(
+ bio, rsa, cipher, (uchar *)passPhrase.data(),
+ passPhrase.size(), nullptr, nullptr)) {
+ fail = true;
+ }
+ }
+ } else if (algorithm() == QSsl::Dsa) {
+ if (type() == QSsl::PublicKey) {
+ if (!q_PEM_write_bio_DSA_PUBKEY(bio, dsa))
+ fail = true;
+ } else {
+ if (!q_PEM_write_bio_DSAPrivateKey(
+ bio, dsa, cipher, (uchar *)passPhrase.data(),
+ passPhrase.size(), nullptr, nullptr)) {
+ fail = true;
+ }
+ }
+ } else if (algorithm() == QSsl::Dh) {
+ EVP_PKEY *result = q_EVP_PKEY_new();
+ if (!result || !q_EVP_PKEY_set1_DH(result, dh)) {
+ fail = true;
+ } else if (type() == QSsl::PublicKey) {
+ if (!q_PEM_write_bio_PUBKEY(bio, result))
+ fail = true;
+ } else if (!q_PEM_write_bio_PrivateKey(
+ bio, result, cipher, (uchar *)passPhrase.data(),
+ passPhrase.size(), nullptr, nullptr)) {
+ fail = true;
+ }
+ q_EVP_PKEY_free(result);
+#ifndef OPENSSL_NO_EC
+ } else if (algorithm() == QSsl::Ec) {
+ if (type() == QSsl::PublicKey) {
+ if (!q_PEM_write_bio_EC_PUBKEY(bio, ec))
+ fail = true;
+ } else {
+ if (!q_PEM_write_bio_ECPrivateKey(
+ bio, ec, cipher, (uchar *)passPhrase.data(),
+ passPhrase.size(), nullptr, nullptr)) {
+ fail = true;
+ }
+ }
+#endif
+ } else {
+ fail = true;
+ }
+
+ QByteArray pem;
+ if (!fail) {
+ char *data = nullptr;
+ const long size = q_BIO_get_mem_data(bio, &data);
+ if (size > 0 && data)
+ pem = QByteArray(data, size);
+ }
+
+ return pem;
+}
+
+void TlsKeyOpenSSL::fromHandle(Qt::HANDLE handle, QSsl::KeyType expectedType)
+{
+ EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle);
+ if (!evpKey || !fromEVP_PKEY(evpKey)) {
+ opaque = evpKey;
+ keyAlgorithm = QSsl::Opaque;
+ } else {
+ q_EVP_PKEY_free(evpKey);
+ }
+
+ keyType = expectedType;
+ keyIsNull = !opaque;
+}
+
+bool TlsKeyOpenSSL::fromEVP_PKEY(EVP_PKEY *pkey)
+{
+ if (!pkey)
+ return false;
+
+ switch (q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey))) {
+ case EVP_PKEY_RSA:
+ keyIsNull = false;
+ keyAlgorithm = QSsl::Rsa;
+ keyType = QSsl::PrivateKey;
+ rsa = q_EVP_PKEY_get1_RSA(pkey);
+
+ return true;
+ case EVP_PKEY_DSA:
+ keyIsNull = false;
+ keyAlgorithm = QSsl::Dsa;
+ keyType = QSsl::PrivateKey;
+ dsa = q_EVP_PKEY_get1_DSA(pkey);
+
+ return true;
+ case EVP_PKEY_DH:
+ keyIsNull = false;
+ keyAlgorithm = QSsl::Dh;
+ keyType = QSsl::PrivateKey;
+ dh = q_EVP_PKEY_get1_DH(pkey);
+ return true;
+#ifndef OPENSSL_NO_EC
+ case EVP_PKEY_EC:
+ keyIsNull = false;
+ keyAlgorithm = QSsl::Ec;
+ keyType = QSsl::PrivateKey;
+ ec = q_EVP_PKEY_get1_EC_KEY(pkey);
+
+ return true;
+#endif
+ default:;
+ // Unknown key type. This could be handled as opaque, but then
+ // we'd eventually leak memory since we wouldn't be able to free
+ // the underlying EVP_PKEY structure. For now, we won't support
+ // this.
+ }
+
+ return false;
+}
+
+QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv, bool enc)
+{
+ const EVP_CIPHER *type = nullptr;
+ int i = 0, len = 0;
+
+ switch (cipher) {
+ case QSslKeyPrivate::DesCbc:
+#ifndef OPENSSL_NO_DES
+ type = q_EVP_des_cbc();
+#endif
+ break;
+ case QSslKeyPrivate::DesEde3Cbc:
+#ifndef OPENSSL_NO_DES
+ type = q_EVP_des_ede3_cbc();
+#endif
+ break;
+ case QSslKeyPrivate::Rc2Cbc:
+#ifndef OPENSSL_NO_RC2
+ type = q_EVP_rc2_cbc();
+#endif
+ break;
+ case QSslKeyPrivate::Aes128Cbc:
+ type = q_EVP_aes_128_cbc();
+ break;
+ case QSslKeyPrivate::Aes192Cbc:
+ type = q_EVP_aes_192_cbc();
+ break;
+ case QSslKeyPrivate::Aes256Cbc:
+ type = q_EVP_aes_256_cbc();
+ break;
+ }
+
+ if (type == nullptr)
+ return {};
+
+ QByteArray output;
+ output.resize(data.size() + EVP_MAX_BLOCK_LENGTH);
+
+ EVP_CIPHER_CTX *ctx = q_EVP_CIPHER_CTX_new();
+ q_EVP_CIPHER_CTX_reset(ctx);
+ q_EVP_CipherInit(ctx, type, nullptr, nullptr, enc);
+ q_EVP_CIPHER_CTX_set_key_length(ctx, key.size());
+ if (cipher == QSslKeyPrivate::Rc2Cbc)
+ q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), nullptr);
+
+ q_EVP_CipherInit_ex(ctx, nullptr, nullptr,
+ reinterpret_cast<const unsigned char *>(key.constData()),
+ reinterpret_cast<const unsigned char *>(iv.constData()),
+ enc);
+ q_EVP_CipherUpdate(ctx,
+ reinterpret_cast<unsigned char *>(output.data()), &len,
+ reinterpret_cast<const unsigned char *>(data.constData()), data.size());
+ q_EVP_CipherFinal(ctx,
+ reinterpret_cast<unsigned char *>(output.data()) + len, &i);
+ len += i;
+
+ q_EVP_CIPHER_CTX_reset(ctx);
+ q_EVP_CIPHER_CTX_free(ctx);
+
+ return output.left(len);
+}
+
+QByteArray TlsKeyOpenSSL::decrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const
+{
+ return doCrypt(cipher, data, key, iv, false);
+}
+
+QByteArray TlsKeyOpenSSL::encrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const
+{
+ return doCrypt(cipher, data, key, iv, true);
+}
+
+TlsKeyOpenSSL *TlsKeyOpenSSL::publicKeyFromX509(X509 *x)
+{
+ TlsKeyOpenSSL *tlsKey = new TlsKeyOpenSSL;
+ std::unique_ptr<TlsKeyOpenSSL> keyRaii(tlsKey);
+
+ tlsKey->keyType = QSsl::PublicKey;
+
+ EVP_PKEY *pkey = q_X509_get_pubkey(x);
+ Q_ASSERT(pkey);
+ const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey));
+
+ if (keyType == EVP_PKEY_RSA) {
+ tlsKey->rsa = q_EVP_PKEY_get1_RSA(pkey);
+ tlsKey->keyAlgorithm = QSsl::Rsa;
+ tlsKey->keyIsNull = false;
+ } else if (keyType == EVP_PKEY_DSA) {
+ tlsKey->dsa = q_EVP_PKEY_get1_DSA(pkey);
+ tlsKey->keyAlgorithm = QSsl::Dsa;
+ tlsKey->keyIsNull = false;
+#ifndef OPENSSL_NO_EC
+ } else if (keyType == EVP_PKEY_EC) {
+ tlsKey->ec = q_EVP_PKEY_get1_EC_KEY(pkey);
+ tlsKey->keyAlgorithm = QSsl::Ec;
+ tlsKey->keyIsNull = false;
+#endif
+ } else if (keyType == EVP_PKEY_DH) {
+ // DH unsupported (key is null)
+ } else {
+ // error? (key is null)
+ }
+
+ q_EVP_PKEY_free(pkey);
+ return keyRaii.release();
+}
+
+} // namespace QSsl
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qtlskey_openssl_p.h b/src/network/ssl/qtlskey_openssl_p.h
new file mode 100644
index 0000000000..1bcad0090d
--- /dev/null
+++ b/src/network/ssl/qtlskey_openssl_p.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTLSKEY_OPENSSL_H
+#define QTLSKEY_OPENSSL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtnetworkglobal_p.h>
+
+#include "qtlskey_base_p.h"
+#include "qtlsbackend_p.h"
+#include "qsslkey_p.h"
+
+#include <QtNetwork/qssl.h>
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qglobal.h>
+
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <openssl/dh.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSsl {
+
+class TlsKeyOpenSSL final : public TlsKeyBase
+{
+public:
+ TlsKeyOpenSSL()
+ : opaque(nullptr)
+ {
+ clear(false);
+ }
+ ~TlsKeyOpenSSL()
+ {
+ clear(true);
+ }
+
+ void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der,
+ const QByteArray &passPhrase, bool deepClear) override;
+ void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem,
+ const QByteArray &passPhrase, bool deepClear) override;
+
+ QByteArray toPem(const QByteArray &passPhrase) const override;
+ QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const override;
+
+ void fromHandle(Qt::HANDLE opaque, KeyType expectedType) override;
+
+ void clear(bool deep) override;
+ Qt::HANDLE handle() const override;
+ int length() const override;
+
+ QByteArray decrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const override;
+ QByteArray encrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const override;
+
+ static TlsKeyOpenSSL *publicKeyFromX509(X509 *x);
+
+ union {
+ EVP_PKEY *opaque;
+ RSA *rsa;
+ DSA *dsa;
+ DH *dh;
+#ifndef OPENSSL_NO_EC
+ EC_KEY *ec;
+#endif
+ };
+
+ bool fromEVP_PKEY(EVP_PKEY *pkey);
+};
+
+} // namespace QCrypto
+
+QT_END_NAMESPACE
+
+#endif // QTLSKEY_OPENSSL_H
diff --git a/src/network/ssl/qtlskey_schannel.cpp b/src/network/ssl/qtlskey_schannel.cpp
new file mode 100644
index 0000000000..d739cbd117
--- /dev/null
+++ b/src/network/ssl/qtlskey_schannel.cpp
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtlskey_schannel_p.h"
+#include "qsslkey_p.h"
+#include "qsslkey.h"
+
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qbytearray.h>
+
+#include <wincrypt.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+const wchar_t *getName(QSslKeyPrivate::Cipher cipher)
+{
+ switch (cipher) {
+ case QSslKeyPrivate::Cipher::DesCbc:
+ return BCRYPT_DES_ALGORITHM;
+ case QSslKeyPrivate::Cipher::DesEde3Cbc:
+ return BCRYPT_3DES_ALGORITHM;
+ case QSslKeyPrivate::Cipher::Rc2Cbc:
+ return BCRYPT_RC2_ALGORITHM;
+ case QSslKeyPrivate::Cipher::Aes128Cbc:
+ case QSslKeyPrivate::Cipher::Aes192Cbc:
+ case QSslKeyPrivate::Cipher::Aes256Cbc:
+ return BCRYPT_AES_ALGORITHM;
+ }
+ Q_UNREACHABLE();
+}
+
+BCRYPT_ALG_HANDLE getHandle(QSslKeyPrivate::Cipher cipher)
+{
+ BCRYPT_ALG_HANDLE handle;
+ NTSTATUS status = BCryptOpenAlgorithmProvider(
+ &handle, // phAlgorithm
+ getName(cipher), // pszAlgId
+ nullptr, // pszImplementation
+ 0 // dwFlags
+ );
+ if (status < 0) {
+ // TLSTODO:
+ //qCWarning(lcSChannel, "Failed to open algorithm handle (%ld)!", status);
+ return nullptr;
+ }
+
+ return handle;
+}
+
+BCRYPT_KEY_HANDLE generateSymmetricKey(BCRYPT_ALG_HANDLE handle,
+ const QByteArray &key)
+{
+ BCRYPT_KEY_HANDLE keyHandle;
+ NTSTATUS status = BCryptGenerateSymmetricKey(
+ handle, // hAlgorithm
+ &keyHandle, // phKey
+ nullptr, // pbKeyObject (can ignore)
+ 0, // cbKeyObject (also ignoring)
+ reinterpret_cast<unsigned char *>(const_cast<char *>(key.data())), // pbSecret
+ ULONG(key.length()), // cbSecret
+ 0 // dwFlags
+ );
+ if (status < 0) {
+ // TLSTODO - category
+ //qCWarning(lcSChannel, "Failed to generate symmetric key (%ld)!", status);
+ return nullptr;
+ }
+
+ status = BCryptSetProperty(
+ keyHandle, // hObject
+ BCRYPT_CHAINING_MODE, // pszProperty
+ reinterpret_cast<UCHAR *>(const_cast<wchar_t *>(BCRYPT_CHAIN_MODE_CBC)), // pbInput
+ ARRAYSIZE(BCRYPT_CHAIN_MODE_CBC), // cbInput
+ 0 // dwFlags
+ );
+ if (status < 0) {
+ BCryptDestroyKey(keyHandle);
+ // TLSTODO: category
+ //qCWarning(lcSChannel, "Failed to change the symmetric key's chaining mode (%ld)!", status);
+ return nullptr;
+ }
+ return keyHandle;
+}
+
+QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key,
+ const QByteArray &iv, bool encrypt)
+{
+ BCRYPT_ALG_HANDLE handle = getHandle(cipher);
+ if (!handle)
+ return {};
+ auto handleDealloc = qScopeGuard([&handle]() {
+ BCryptCloseAlgorithmProvider(handle, 0);
+ });
+
+ BCRYPT_KEY_HANDLE keyHandle = generateSymmetricKey(handle, key);
+ if (!keyHandle)
+ return {};
+ auto keyHandleDealloc = qScopeGuard([&keyHandle]() {
+ BCryptDestroyKey(keyHandle);
+ });
+
+ QByteArray ivCopy = iv; // This gets modified, so we take a copy
+
+ ULONG sizeNeeded = 0;
+ QVarLengthArray<unsigned char> output;
+ auto cryptFunction = encrypt ? BCryptEncrypt : BCryptDecrypt;
+ for (int i = 0; i < 2; i++) {
+ output.resize(int(sizeNeeded));
+ auto input = reinterpret_cast<unsigned char *>(const_cast<char *>(data.data()));
+ // Need to call it twice because the first iteration lets us know the size needed.
+ NTSTATUS status = cryptFunction(
+ keyHandle, // hKey
+ input, // pbInput
+ ULONG(data.length()), // cbInput
+ nullptr, // pPaddingInfo
+ reinterpret_cast<unsigned char *>(ivCopy.data()), // pbIV
+ ULONG(ivCopy.length()), // cbIV
+ sizeNeeded ? output.data() : nullptr, // pbOutput
+ ULONG(output.length()), // cbOutput
+ &sizeNeeded, // pcbResult
+ BCRYPT_BLOCK_PADDING // dwFlags
+ );
+ if (status < 0) {
+ qCWarning(lcSsl, "%s failed (%ld)!", encrypt ? "Encrypt" : "Decrypt", status);
+ return {};
+ }
+ }
+
+ return QByteArray(reinterpret_cast<const char *>(output.constData()), int(sizeNeeded));
+}
+} // anonymous namespace
+
+namespace QSsl {
+
+QByteArray TlsKeySchannel::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
+ const QByteArray &iv) const
+{
+ return doCrypt(cipher, data, key, iv, false);
+}
+
+QByteArray TlsKeySchannel::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
+ const QByteArray &iv) const
+{
+ return doCrypt(cipher, data, key, iv, true);
+}
+
+} // namespace QSsl
+
+QT_END_NAMESPACE
+
diff --git a/src/network/ssl/qtlskey_schannel_p.h b/src/network/ssl/qtlskey_schannel_p.h
new file mode 100644
index 0000000000..274f642422
--- /dev/null
+++ b/src/network/ssl/qtlskey_schannel_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTLSKEY_SCHANNEL_P_H
+#define QTLSKEY_SCHANNEL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtnetworkglobal_p.h>
+
+#include <private/qtlskey_generic_p.h>
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSsl {
+
+class TlsKeySchannel final : public TlsKeyGeneric
+{
+public:
+ TlsKeySchannel() = default;
+
+ QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
+ const QByteArray &iv) const override;
+ QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
+ const QByteArray &iv) const override;
+
+ Q_DISABLE_COPY_MOVE(TlsKeySchannel)
+};
+
+} // namespace QSsl
+
+QT_END_NAMESPACE
+
+#endif // QTLSKEY_SCHANNEL_P_H
+
diff --git a/src/network/ssl/qtlskey_st.cpp b/src/network/ssl/qtlskey_st.cpp
new file mode 100644
index 0000000000..e4616c5167
--- /dev/null
+++ b/src/network/ssl/qtlskey_st.cpp
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtlskey_st_p.h"
+#include "qsslkey_p.h"
+
+#include <qbytearray.h>
+
+#include <CommonCrypto/CommonCrypto.h>
+
+#include <cstddef>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSsl {
+namespace {
+
+// Before this code was located in qsslkey_mac.cpp.
+QByteArray wrapCCCrypt(CCOperation ccOp, QSslKeyPrivate::Cipher cipher,
+ const QByteArray &data, const QByteArray &key,
+ const QByteArray &iv)
+{
+ int blockSize = {};
+ CCAlgorithm ccAlgorithm = {};
+ switch (cipher) {
+ case QSslKeyPrivate::DesCbc:
+ blockSize = kCCBlockSizeDES;
+ ccAlgorithm = kCCAlgorithmDES;
+ break;
+ case QSslKeyPrivate::DesEde3Cbc:
+ blockSize = kCCBlockSize3DES;
+ ccAlgorithm = kCCAlgorithm3DES;
+ break;
+ case QSslKeyPrivate::Rc2Cbc:
+ blockSize = kCCBlockSizeRC2;
+ ccAlgorithm = kCCAlgorithmRC2;
+ break;
+ case QSslKeyPrivate::Aes128Cbc:
+ case QSslKeyPrivate::Aes192Cbc:
+ case QSslKeyPrivate::Aes256Cbc:
+ blockSize = kCCBlockSizeAES128;
+ ccAlgorithm = kCCAlgorithmAES;
+ break;
+ }
+ std::size_t plainLength = 0;
+ QByteArray plain(data.size() + blockSize, 0);
+ CCCryptorStatus status = CCCrypt(ccOp, ccAlgorithm, kCCOptionPKCS7Padding,
+ key.constData(), std::size_t(key.size()),
+ iv.constData(), data.constData(), std::size_t(data.size()),
+ plain.data(), std::size_t(plain.size()), &plainLength);
+ if (status == kCCSuccess)
+ return plain.left(int(plainLength));
+
+ return {};
+}
+
+} // Unnamed namespace.
+
+QByteArray TlsKeySecureTransport::decrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const
+{
+ return wrapCCCrypt(kCCDecrypt, cipher, data, key, iv);
+}
+
+QByteArray TlsKeySecureTransport::encrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const
+{
+ return wrapCCCrypt(kCCEncrypt, cipher, data, key, iv);
+}
+
+} // namespace QSsl.
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qtlskey_st_p.h b/src/network/ssl/qtlskey_st_p.h
new file mode 100644
index 0000000000..7bc12edb45
--- /dev/null
+++ b/src/network/ssl/qtlskey_st_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTLSKEY_ST_P_H
+#define QTLSKEY_ST_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtnetworkglobal_p.h>
+
+#include <private/qtlskey_generic_p.h>
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSsl {
+
+class TlsKeySecureTransport final : public TlsKeyGeneric
+{
+public:
+ TlsKeySecureTransport() = default;
+
+ QByteArray decrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const override;
+ QByteArray encrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const override;
+
+ Q_DISABLE_COPY_MOVE(TlsKeySecureTransport)
+};
+
+} // namespace QSsl
+
+QT_END_NAMESPACE
+
+#endif // QTLSKEY_ST_P_H