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 <timur.pocheptsov@qt.io>
This commit is contained in:
Timur Pocheptsov 2021-01-25 15:37:03 +01:00
parent 59252a3a96
commit 1a0da3ae69
21 changed files with 2475 additions and 16 deletions

View File

@ -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
)

View File

@ -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)

View File

@ -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>
@ -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

View File

@ -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:

View File

@ -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"

View File

@ -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
{

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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)
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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