From 1a0da3ae69964142b3a31d87ecc88a925006a4de Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Mon, 25 Jan 2021 15:37:03 +0100 Subject: QSsl::TlsKey - provide the interface and implementations which will become parts of TLS plugins in the future. Task-number: QTBUG-65922 Change-Id: I4ee3c59c435fc34a9f4dacd3ff0e3cfb44251e23 Reviewed-by: Timur Pocheptsov --- src/network/ssl/qsslkey.h | 7 + src/network/ssl/qsslkey_p.cpp | 38 ++ src/network/ssl/qsslkey_p.h | 18 +- src/network/ssl/qsslsocket.cpp | 17 +- src/network/ssl/qsslsocket_mac.cpp | 5 + src/network/ssl/qsslsocket_openssl.cpp | 6 + src/network/ssl/qsslsocket_p.h | 7 +- src/network/ssl/qsslsocket_schannel.cpp | 6 + src/network/ssl/qtlsbackend.cpp | 43 +- src/network/ssl/qtlsbackend_p.h | 53 +- src/network/ssl/qtlskey_base.cpp | 133 +++++ src/network/ssl/qtlskey_base_p.h | 104 ++++ src/network/ssl/qtlskey_generic.cpp | 827 ++++++++++++++++++++++++++++++++ src/network/ssl/qtlskey_generic_p.h | 128 +++++ src/network/ssl/qtlskey_openssl.cpp | 509 ++++++++++++++++++++ src/network/ssl/qtlskey_openssl_p.h | 124 +++++ src/network/ssl/qtlskey_schannel.cpp | 187 ++++++++ src/network/ssl/qtlskey_schannel_p.h | 82 ++++ src/network/ssl/qtlskey_st.cpp | 110 +++++ src/network/ssl/qtlskey_st_p.h | 81 ++++ 20 files changed, 2469 insertions(+), 16 deletions(-) create mode 100644 src/network/ssl/qtlskey_base.cpp create mode 100644 src/network/ssl/qtlskey_base_p.h create mode 100644 src/network/ssl/qtlskey_generic.cpp create mode 100644 src/network/ssl/qtlskey_generic_p.h create mode 100644 src/network/ssl/qtlskey_openssl.cpp create mode 100644 src/network/ssl/qtlskey_openssl_p.h create mode 100644 src/network/ssl/qtlskey_schannel.cpp create mode 100644 src/network/ssl/qtlskey_schannel_p.h create mode 100644 src/network/ssl/qtlskey_st.cpp create mode 100644 src/network/ssl/qtlskey_st_p.h (limited to 'src/network/ssl') 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 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 #include @@ -93,6 +95,34 @@ QT_BEGIN_NAMESPACE \a pem. */ +/*! + \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. @@ -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 #endif +#include + 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 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 @@ -86,6 +87,10 @@ private: { return builtinBackendNames[nameIndexSecureTransport]; } + QSsl::TlsKey *createKey() const override + { + return new QSsl::TlsKeySecureTransport; + } QList 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 supportedProtocols() const override { QList 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 #else @@ -108,6 +107,7 @@ using QHCertStorePointer = std::unique_ptr; #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 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 #include @@ -167,6 +168,11 @@ private: return builtinBackendNames[nameIndexSchannel]; } + QSsl::TlsKey *createKey() const override + { + return new QSsl::TlsKeySchannel; + } + QList supportedProtocols() const override { QList 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 +#include #include #include @@ -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 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 +#include + #include #include #include #include +#include #include #include #include @@ -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 *headers) const = 0; + virtual QByteArray pemFromDer(const QByteArray &der, const QMap &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 + static DynamicType *backend(const TLSObject &o) + { + return static_cast(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 &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::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 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 + +#include + +#include + +#include + +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 &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 + +#include +#include +#include + +#include +#include +#include +#include + +#include + +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; + +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 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 &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 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 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 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 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 &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 &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 headers; + QByteArray data = derFromPem(pem, &headers); + + if (headers.value("Proc-Type") == "4,ENCRYPTED") { + const QList 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 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(&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 *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 + +#include +#include + +#include +#include + +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 *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 + +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 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(pem.data()), pem.size()); + if (!bio) + return; + + const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);}); + + void *phrase = const_cast(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 *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(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(key.constData()), + reinterpret_cast(iv.constData()), + enc); + q_EVP_CipherUpdate(ctx, + reinterpret_cast(output.data()), &len, + reinterpret_cast(data.constData()), data.size()); + q_EVP_CipherFinal(ctx, + reinterpret_cast(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 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 + +#include "qtlskey_base_p.h" +#include "qtlsbackend_p.h" +#include "qsslkey_p.h" + +#include + +#include +#include + +#include +#include +#include + +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 *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 +#include + +#include + +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(const_cast(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(const_cast(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 output; + auto cryptFunction = encrypt ? BCryptEncrypt : BCryptDecrypt; + for (int i = 0; i < 2; i++) { + output.resize(int(sizeNeeded)); + auto input = reinterpret_cast(const_cast(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(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(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 + +#include + +#include + +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é +** 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 + +#include + +#include + +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 + +#include + +#include + +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 -- cgit v1.2.3