Port QSslKey to the new plugin-based implementation

The idea is to have QSslKey(Private) backend-neutral and hide
all library-specific code inside plugins.

Fixes: QTBUG-90953
Task-number: QTBUG-65922
Change-Id: I2eeee3b2b72c78c2e24f2fb914abce3caa913be8
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
(cherry picked from commit 0352cf8e1bf57615b9faf3f6f383896444e762ac)
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Timur Pocheptsov 2021-02-02 14:55:00 +01:00
parent 9385e54071
commit 43d933bf50
25 changed files with 277 additions and 1969 deletions

View File

@ -336,8 +336,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_s
ssl/qsslcertificate_schannel.cpp
ssl/qssldiffiehellmanparameters_dummy.cpp
ssl/qsslellipticcurve_dummy.cpp
ssl/qsslkey_qt.cpp
ssl/qsslkey_schannel.cpp
ssl/qsslsocket_qt.cpp
ssl/qsslsocket_schannel.cpp ssl/qsslsocket_schannel_p.h
ssl/qtlsbackend_schannel_p.h
@ -356,8 +354,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_securetransport AND QT_FE
SOURCES
ssl/qssldiffiehellmanparameters_dummy.cpp
ssl/qsslellipticcurve_dummy.cpp
ssl/qsslkey_mac.cpp
ssl/qsslkey_qt.cpp
ssl/qsslsocket_mac.cpp ssl/qsslsocket_mac_p.h
ssl/qsslsocket_mac_shared.cpp
ssl/qsslsocket_qt.cpp
@ -379,7 +375,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_openssl AND QT_FEATURE_ss
ssl/qsslcontext_openssl.cpp ssl/qsslcontext_openssl_p.h
ssl/qssldiffiehellmanparameters_openssl.cpp
ssl/qsslellipticcurve_openssl.cpp
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

View File

@ -60,6 +60,19 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcSsl)
namespace QSsl {
enum class Cipher {
DesCbc,
DesEde3Cbc,
Rc2Cbc,
Aes128Cbc,
Aes192Cbc,
Aes256Cbc
};
} // namespace QSsl
QT_END_NAMESPACE
#endif // QSSL_P_H

View File

@ -43,6 +43,8 @@
#include "qsslcertificate_p.h"
#include "qsslkey_p.h"
#include "qsslcertificateextension_p.h"
#include "qtlsbackend_openssl_p.h"
#include "qtlskey_openssl_p.h"
#include <QtCore/qendian.h>
#include <QtCore/qmutex.h>
@ -295,25 +297,26 @@ QSslKey QSslCertificate::publicKey() const
QSslKey key;
key.d->type = QSsl::PublicKey;
auto *tlsKey = QTlsBackend::backend<QSsl::TlsKeyOpenSSL>(key);
tlsKey->keyType = QSsl::PublicKey;
EVP_PKEY *pkey = q_X509_get_pubkey(d->x509);
Q_ASSERT(pkey);
const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey));
if (keyType == EVP_PKEY_RSA) {
key.d->rsa = q_EVP_PKEY_get1_RSA(pkey);
key.d->algorithm = QSsl::Rsa;
key.d->isNull = false;
tlsKey->rsa = q_EVP_PKEY_get1_RSA(pkey);
tlsKey->keyAlgorithm = QSsl::Rsa;
tlsKey->keyIsNull = false;
} else if (keyType == EVP_PKEY_DSA) {
key.d->dsa = q_EVP_PKEY_get1_DSA(pkey);
key.d->algorithm = QSsl::Dsa;
key.d->isNull = false;
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) {
key.d->ec = q_EVP_PKEY_get1_EC_KEY(pkey);
key.d->algorithm = QSsl::Ec;
key.d->isNull = false;
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

View File

@ -39,6 +39,8 @@
#include "qsslcertificate.h"
#include "qsslcertificate_p.h"
#include "qtlsbackend_p.h"
#include "qtlskey_generic_p.h"
#include "qssl_p.h"
#ifndef QT_NO_SSL
@ -153,11 +155,14 @@ Qt::HANDLE QSslCertificate::handle() const
QSslKey QSslCertificate::publicKey() const
{
QSslKey key;
key.d->type = QSsl::PublicKey;
if (d->publicKeyAlgorithm != QSsl::Opaque) {
key.d->algorithm = d->publicKeyAlgorithm;
key.d->decodeDer(d->publicKeyDerData);
}
auto *tlsKey = QTlsBackend::backend<QSsl::TlsKeyGeneric>(key);
if (!tlsKey)
return key;
tlsKey->keyType = QSsl::PublicKey;
if (d->publicKeyAlgorithm != QSsl::Opaque)
tlsKey->decodeDer(QSsl::PublicKey, d->publicKeyAlgorithm, d->publicKeyDerData, {}, false);
return key;
}
#endif

View File

@ -88,6 +88,7 @@ public:
QSsl::KeyAlgorithm algorithm() const;
QByteArray toPem(const QByteArray &passPhrase = QByteArray()) const;
// ### Qt 7: drop passPhrase
QByteArray toDer(const QByteArray &passPhrase = QByteArray()) const;
Qt::HANDLE handle() const;

View File

@ -1,99 +0,0 @@
/****************************************************************************
**
** 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 "qsslkey.h"
#include "qsslkey_p.h"
#include <CommonCrypto/CommonCrypto.h>
#include <cstddef>
QT_BEGIN_NAMESPACE
static 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;
}
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 QByteArray();
}
QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
{
return wrapCCCrypt(kCCDecrypt, cipher, data, key, iv);
}
QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
{
return wrapCCCrypt(kCCEncrypt, cipher, data, key, iv);
}
QT_END_NAMESPACE

View File

@ -1,383 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Copyright (C) 2016 Richard J. Moore <rich@kde.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 "qsslkey.h"
#include "qsslkey_p.h"
#include "qsslsocket_openssl_symbols_p.h"
#include "qsslsocket.h"
#include "qsslsocket_p.h"
#include <QtCore/qatomic.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qiodevice.h>
#ifndef QT_NO_DEBUG_STREAM
#include <QtCore/qdebug.h>
#endif
QT_BEGIN_NAMESPACE
void QSslKeyPrivate::clear(bool deep)
{
isNull = true;
if (!QSslSocket::supportsSsl())
return;
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;
}
}
bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey)
{
if (pkey == nullptr)
return false;
const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey));
if (keyType == EVP_PKEY_RSA) {
isNull = false;
algorithm = QSsl::Rsa;
type = QSsl::PrivateKey;
rsa = q_EVP_PKEY_get1_RSA(pkey);
return true;
} else if (keyType == EVP_PKEY_DSA) {
isNull = false;
algorithm = QSsl::Dsa;
type = QSsl::PrivateKey;
dsa = q_EVP_PKEY_get1_DSA(pkey);
return true;
} else if (keyType == EVP_PKEY_DH) {
isNull = false;
algorithm = QSsl::Dh;
type = QSsl::PrivateKey;
dh = q_EVP_PKEY_get1_DH(pkey);
return true;
}
#ifndef OPENSSL_NO_EC
else if (keyType == EVP_PKEY_EC) {
isNull = false;
algorithm = QSsl::Ec;
type = QSsl::PrivateKey;
ec = q_EVP_PKEY_get1_EC_KEY(pkey);
return true;
}
#endif
else {
// 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;
}
void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, bool deepClear)
{
QMap<QByteArray, QByteArray> headers;
decodePem(pemFromDer(der, headers), passPhrase, deepClear);
}
void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
bool deepClear)
{
if (pem.isEmpty())
return;
clear(deepClear);
if (!QSslSocket::supportsSsl())
return;
BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
if (!bio)
return;
void *phrase = const_cast<char *>(passPhrase.constData());
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)
isNull = 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)
isNull = 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)
isNull = 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)
isNull = false;
#endif
}
q_BIO_free(bio);
}
int QSslKeyPrivate::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 QSslKeyPrivate::toPem(const QByteArray &passPhrase) const
{
if (!QSslSocket::supportsSsl() || isNull || algorithm == QSsl::Opaque)
return QByteArray();
// ### the cipher should be selectable in the API:
const EVP_CIPHER *cipher = nullptr;
if (type == QSsl::PrivateKey && !passPhrase.isEmpty()) {
#ifndef OPENSSL_NO_DES
cipher = q_EVP_des_ede3_cbc();
#else
return QByteArray();
#endif
}
BIO *bio = q_BIO_new(q_BIO_s_mem());
if (!bio)
return QByteArray();
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;
long size = q_BIO_get_mem_data(bio, &data);
pem = QByteArray(data, size);
}
q_BIO_free(bio);
return pem;
}
Qt::HANDLE QSslKeyPrivate::handle() const
{
switch (algorithm) {
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);
}
}
static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv, int 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();
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 QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
{
return doCrypt(cipher, data, key, iv, 0);
}
QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
{
return doCrypt(cipher, data, key, iv, 1);
}
QT_END_NAMESPACE

View File

@ -74,11 +74,6 @@
QT_BEGIN_NAMESPACE
/*!
\fn void QSslKeyPrivate::clear(bool deep)
\internal
*/
/*!
\fn void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
bool deepClear)
@ -99,10 +94,7 @@ QT_BEGIN_NAMESPACE
\internal
*/
QSslKeyPrivate::QSslKeyPrivate()
: algorithm(QSsl::Opaque)
, opaque(nullptr)
{
clear(false); // TLSTODO: remove
const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse();
if (!tlsBackend)
return;
@ -118,11 +110,30 @@ QSslKeyPrivate::QSslKeyPrivate()
*/
QSslKeyPrivate::~QSslKeyPrivate()
{
clear(); // TLSTODO: remove
if (keyBackend.get())
keyBackend->clear(true /*deep clear*/);
}
QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
{
if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) {
const std::unique_ptr<QSsl::TlsKey> cryptor(tlsBackend->createKey());
return cryptor->decrypt(cipher, data, key, iv);
}
return {};
}
QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
{
if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) {
const std::unique_ptr<QSsl::TlsKey> cryptor(tlsBackend->createKey());
return cryptor->encrypt(cipher, data, key, iv);
}
return {};
}
/*!
Constructs a null key.
@ -133,203 +144,6 @@ QSslKey::QSslKey()
{
}
/*!
\internal
*/
QByteArray QSslKeyPrivate::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();
}
static QByteArray pkcs8Header(bool encrypted)
{
return encrypted
? QByteArrayLiteral("-----BEGIN ENCRYPTED PRIVATE KEY-----")
: QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
}
/*!
\internal
*/
QByteArray QSslKeyPrivate::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 QByteArray();
}
static QByteArray pkcs8Footer(bool encrypted)
{
return encrypted
? QByteArrayLiteral("-----END ENCRYPTED PRIVATE KEY-----")
: QByteArrayLiteral("-----END PRIVATE KEY-----");
}
/*!
\internal
Returns a DER key formatted as PEM.
*/
QByteArray QSslKeyPrivate::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;
// ### optimize
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');
#if !QT_CONFIG(openssl)
} else if (isPkcs8) {
pem.prepend(pkcs8Header(false) + '\n' + extra);
pem.append(pkcs8Footer(false) + '\n');
#endif
} else {
pem.prepend(pemHeader() + '\n' + extra);
pem.append(pemFooter() + '\n');
}
return pem;
}
/*!
\internal
Returns a PEM key formatted as DER.
*/
QByteArray QSslKeyPrivate::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
}
bool QSslKeyPrivate::isEncryptedPkcs8(const QByteArray &der) const
{
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);
}
/*!
Constructs a QSslKey by decoding the string in the byte array
\a encoded using a specified \a algorithm and \a encoding format.
@ -344,12 +158,12 @@ QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase)
: d(new QSslKeyPrivate)
{
d->type = type;
d->algorithm = algorithm;
if (encoding == QSsl::Der)
d->decodeDer(encoded, passPhrase);
else
d->decodePem(encoded, passPhrase);
if (auto *tlsKey = d->keyBackend.get()) {
if (encoding == QSsl::Der)
tlsKey->decodeDer(type, algorithm, encoded, passPhrase, true /*deep clear*/);
else
tlsKey->decodePem(type, algorithm, encoded, passPhrase, true /*deep clear*/);
}
}
/*!
@ -369,12 +183,13 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding
QByteArray encoded;
if (device)
encoded = device->readAll();
d->type = type;
d->algorithm = algorithm;
if (encoding == QSsl::Der)
d->decodeDer(encoded, passPhrase);
else
d->decodePem(encoded, passPhrase);
if (auto *tlsKey = d->keyBackend.get()) {
if (encoding == QSsl::Der)
tlsKey->decodeDer(type, algorithm, encoded, passPhrase, true /*deep clear*/);
else
tlsKey->decodePem(type, algorithm, encoded, passPhrase, true /*deep clear*/);
}
}
/*!
@ -388,20 +203,8 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding
QSslKey::QSslKey(Qt::HANDLE handle, QSsl::KeyType type)
: d(new QSslKeyPrivate)
{
#ifndef QT_NO_OPENSSL
EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle);
if (!evpKey || !d->fromEVP_PKEY(evpKey)) {
d->opaque = evpKey;
d->algorithm = QSsl::Opaque;
} else {
q_EVP_PKEY_free(evpKey);
}
#else
d->opaque = handle;
d->algorithm = QSsl::Opaque;
#endif
d->type = type;
d->isNull = !d->opaque;
if (auto *tlsKey = d->keyBackend.get())
tlsKey->fromHandle(handle, type);
}
/*!
@ -463,7 +266,10 @@ QSslKey &QSslKey::operator=(const QSslKey &other)
*/
bool QSslKey::isNull() const
{
return d->isNull;
if (const auto *tlsKey = d->keyBackend.get())
return tlsKey->isNull();
return true;
}
/*!
@ -481,7 +287,10 @@ void QSslKey::clear()
*/
int QSslKey::length() const
{
return d->length();
if (const auto *tlsKey = d->keyBackend.get())
return tlsKey->length();
return -1;
}
/*!
@ -489,7 +298,10 @@ int QSslKey::length() const
*/
QSsl::KeyType QSslKey::type() const
{
return d->type;
if (const auto *tlsKey = d->keyBackend.get())
return tlsKey->type();
return QSsl::PublicKey;
}
/*!
@ -497,7 +309,10 @@ QSsl::KeyType QSslKey::type() const
*/
QSsl::KeyAlgorithm QSslKey::algorithm() const
{
return d->algorithm;
if (const auto *tlsKey = d->keyBackend.get())
return tlsKey->algorithm();
return QSsl::Opaque;
}
/*!
@ -508,19 +323,18 @@ QSsl::KeyAlgorithm QSslKey::algorithm() const
*/
QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
{
if (d->isNull || d->algorithm == QSsl::Opaque)
return QByteArray();
if (isNull() || algorithm() == QSsl::Opaque)
return {};
// Encrypted DER is nonsense, see QTBUG-41038.
if (d->type == QSsl::PrivateKey && !passPhrase.isEmpty())
return QByteArray();
if (type() == QSsl::PrivateKey && !passPhrase.isEmpty())
return {};
#ifndef QT_NO_OPENSSL
QMap<QByteArray, QByteArray> headers;
return d->derFromPem(toPem(passPhrase), &headers);
#else
return d->derData;
#endif
if (const auto *tlsKey = d->keyBackend.get())
return tlsKey->derFromPem(toPem(passPhrase), &headers);
return {};
}
/*!
@ -530,7 +344,10 @@ QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
*/
QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
{
return d->toPem(passPhrase);
if (const auto *tlsKey = d->keyBackend.get())
return tlsKey->toPem(passPhrase);
return {};
}
/*!
@ -546,7 +363,10 @@ QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
*/
Qt::HANDLE QSslKey::handle() const
{
return d->handle();
if (d->keyBackend.get())
return d->keyBackend->handle();
return nullptr;
}
/*!

View File

@ -53,13 +53,9 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslkey.h"
#include "qsslsocket_p.h" // includes wincrypt.h
#ifndef QT_NO_OPENSSL
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#endif
#include "qsslkey.h"
#include "qssl_p.h"
#include <memory>
@ -75,60 +71,11 @@ public:
QSslKeyPrivate();
~QSslKeyPrivate();
void clear(bool deep = true);
#ifndef QT_NO_OPENSSL
bool fromEVP_PKEY(EVP_PKEY *pkey);
#endif
void decodeDer(const QByteArray &der, const QByteArray &passPhrase = {}, bool deepClear = true);
void decodePem(const QByteArray &pem, const QByteArray &passPhrase, bool deepClear = true);
QByteArray pemHeader() const;
QByteArray pemFooter() const;
QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const;
QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const;
int length() const;
QByteArray toPem(const QByteArray &passPhrase) const;
Qt::HANDLE handle() const;
bool isEncryptedPkcs8(const QByteArray &der) const;
#if !QT_CONFIG(openssl)
QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase);
bool isPkcs8 = false;
#endif
bool isNull;
QSsl::KeyType type;
QSsl::KeyAlgorithm algorithm;
enum Cipher {
DesCbc,
DesEde3Cbc,
Rc2Cbc,
Aes128Cbc,
Aes192Cbc,
Aes256Cbc
};
using Cipher = QSsl::Cipher;
Q_AUTOTEST_EXPORT static QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv);
Q_AUTOTEST_EXPORT static QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv);
#ifndef QT_NO_OPENSSL
union {
EVP_PKEY *opaque;
RSA *rsa;
DSA *dsa;
DH *dh;
#ifndef OPENSSL_NO_EC
EC_KEY *ec;
#endif
};
#else
Qt::HANDLE opaque;
QByteArray derData;
int keyLength;
#endif
std::unique_ptr<QSsl::TlsKey> keyBackend;
QAtomicInt ref;

View File

@ -1,785 +0,0 @@
/****************************************************************************
**
** 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 "qsslkey.h"
#include "qsslkey_p.h"
#include "qasn1element_p.h"
#include <QtCore/qdatastream.h>
#include <QtCore/qcryptographichash.h>
#include <QtCore/QMessageAuthenticationCode>
#include <QtCore/qrandom.h>
#include <QtNetwork/qpassworddigestor.h>
#include <cstring>
QT_USE_NAMESPACE
static 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,
};
// 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
typedef QMap<QByteArray, int> OidLengthMap;
static 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;
}
Q_GLOBAL_STATIC_WITH_ARGS(OidLengthMap, oidLengthMap, (createOidMap()))
static int curveBits(const QByteArray &oid)
{
const int length = oidLengthMap->value(oid);
return length ? length : -1;
}
static 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;
}
static 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());
}
static 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;
}
void QSslKeyPrivate::clear(bool deep)
{
isNull = true;
if (deep)
std::memset(derData.data(), 0, derData.size());
derData.clear();
keyLength = -1;
}
static int extractPkcs8KeyLength(const QList<QAsn1Element> &items, QSslKeyPrivate *that)
{
Q_ASSERT(items.size() == 3);
int keyLength;
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(items[2].value());
// 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;
}
void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, bool deepClear)
{
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;
isPkcs8 = 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;
isNull = false;
}
void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
bool deepClear)
{
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;
}
Cipher cipher;
if (dekInfo.first() == "DES-CBC") {
cipher = DesCbc;
} else if (dekInfo.first() == "DES-EDE3-CBC") {
cipher = DesEde3Cbc;
} else if (dekInfo.first() == "RC2-CBC") {
cipher = Rc2Cbc;
} else if (dekInfo.first() == "AES-128-CBC") {
cipher = Aes128Cbc;
} else if (dekInfo.first() == "AES-192-CBC") {
cipher = Aes192Cbc;
} else if (dekInfo.first() == "AES-256-CBC") {
cipher = 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(data, passPhrase, deepClear);
}
int QSslKeyPrivate::length() const
{
return keyLength;
}
QByteArray QSslKeyPrivate::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));
Cipher cipher = 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);
}
Qt::HANDLE QSslKeyPrivate::handle() const
{
return opaque;
}
// Maps OIDs to the encryption cipher they specify
static 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() : initialized(false)
{}
EncryptionData(QSslKeyPrivate::Cipher cipher, QByteArray key, QByteArray iv)
: initialized(true), cipher(cipher), key(key), iv(iv)
{}
bool initialized;
QSslKeyPrivate::Cipher cipher;
QByteArray key;
QByteArray iv;
};
static 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 bugreport 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
static 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}
};
static 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};
}
QByteArray QSslKeyPrivate::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();
}

View File

@ -1,178 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2018 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 "qssl_p.h"
#include "qsslkey.h"
#include "qsslkey_p.h"
#include "qsslcertificate_p.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qscopeguard.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) {
qCWarning(lcSsl, "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) {
qCWarning(lcSsl, "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);
qCWarning(lcSsl, "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
QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
const QByteArray &iv)
{
return doCrypt(cipher, data, key, iv, false);
}
QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
const QByteArray &iv)
{
return doCrypt(cipher, data, key, iv, true);
}
QT_END_NAMESPACE

View File

@ -69,6 +69,7 @@
#include "qsslkey.h"
#include "qtlsbackend_openssl_p.h"
#include "qtlskey_openssl_p.h"
#include "qx509_openssl_p.h"
#ifdef Q_OS_WIN
#include "qwindowscarootfetcher_p.h"
@ -2323,123 +2324,14 @@ QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates
QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain,
const QString &hostName)
{
auto roots = QSslConfiguration::defaultConfiguration().caCertificates();
#ifndef Q_OS_WIN
// On Windows, system CA certificates are already set as default ones.
// No need to add them again (and again) and also, if the default configuration
// has its own set of CAs, this probably should not be amended by the ones
// from the 'ROOT' store, since it's not what an application chose to trust.
if (s_loadRootCertsOnDemand)
roots.append(systemCaCertificates());
#endif // Q_OS_WIN
return verify(roots, certificateChain, hostName);
return QSsl::X509CertificateOpenSSL::verify(certificateChain, hostName);
}
QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &caCertificates,
const QList<QSslCertificate> &certificateChain,
const QString &hostName)
{
if (certificateChain.count() <= 0)
return {QSslError(QSslError::UnspecifiedError)};
QList<QSslError> errors;
// Setup the store with the default CA certificates
X509_STORE *certStore = q_X509_STORE_new();
if (!certStore) {
qCWarning(lcSsl) << "Unable to create certificate store";
errors << QSslError(QSslError::UnspecifiedError);
return errors;
}
const std::unique_ptr<X509_STORE, decltype(&q_X509_STORE_free)> storeGuard(certStore, q_X509_STORE_free);
const QDateTime now = QDateTime::currentDateTimeUtc();
for (const QSslCertificate &caCertificate : caCertificates) {
// From https://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html:
//
// If several CA certificates matching the name, key identifier, and
// serial number condition are available, only the first one will be
// examined. This may lead to unexpected results if the same CA
// certificate is available with different expiration dates. If a
// ``certificate expired'' verification error occurs, no other
// certificate will be searched. Make sure to not have expired
// certificates mixed with valid ones.
//
// See also: QSslContext::fromConfiguration()
if (caCertificate.expiryDate() >= now) {
q_X509_STORE_add_cert(certStore, reinterpret_cast<X509 *>(caCertificate.handle()));
}
}
QList<QSslErrorEntry> lastErrors;
if (!q_X509_STORE_set_ex_data(certStore, 0, &lastErrors)) {
qCWarning(lcSsl) << "Unable to attach external data (error list) to a store";
errors << QSslError(QSslError::UnspecifiedError);
return errors;
}
// Register a custom callback to get all verification errors.
q_X509_STORE_set_verify_cb(certStore, q_X509Callback);
// Build the chain of intermediate certificates
STACK_OF(X509) *intermediates = nullptr;
if (certificateChain.length() > 1) {
intermediates = (STACK_OF(X509) *) q_OPENSSL_sk_new_null();
if (!intermediates) {
errors << QSslError(QSslError::UnspecifiedError);
return errors;
}
bool first = true;
for (const QSslCertificate &cert : certificateChain) {
if (first) {
first = false;
continue;
}
q_OPENSSL_sk_push((OPENSSL_STACK *)intermediates, reinterpret_cast<X509 *>(cert.handle()));
}
}
X509_STORE_CTX *storeContext = q_X509_STORE_CTX_new();
if (!storeContext) {
errors << QSslError(QSslError::UnspecifiedError);
return errors;
}
std::unique_ptr<X509_STORE_CTX, decltype(&q_X509_STORE_CTX_free)> ctxGuard(storeContext, q_X509_STORE_CTX_free);
if (!q_X509_STORE_CTX_init(storeContext, certStore, reinterpret_cast<X509 *>(certificateChain[0].handle()), intermediates)) {
errors << QSslError(QSslError::UnspecifiedError);
return errors;
}
// Now we can actually perform the verification of the chain we have built.
// We ignore the result of this function since we process errors via the
// callback.
(void) q_X509_verify_cert(storeContext);
ctxGuard.reset();
q_OPENSSL_sk_free((OPENSSL_STACK *)intermediates);
// Now process the errors
if (QSslCertificatePrivate::isBlacklisted(certificateChain[0])) {
QSslError error(QSslError::CertificateBlacklisted, certificateChain[0]);
errors << error;
}
// Check the certificate name against the hostname if one was specified
if ((!hostName.isEmpty()) && (!isMatchingHostname(certificateChain[0], hostName))) {
// No matches in common names or alternate names.
QSslError error(QSslError::HostNameMismatch, certificateChain[0]);
errors << error;
}
// Translate errors from the error list into QSslErrors.
errors.reserve(errors.size() + lastErrors.size());
for (const auto &error : qAsConst(lastErrors))
errors << _q_OpenSSL_to_QSslError(error.code, certificateChain.value(error.depth));
return errors;
return QSsl::X509CertificateOpenSSL::verify(caCertificates, certificateChain, hostName);
}
bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
@ -2485,7 +2377,8 @@ bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
}
// Convert to Qt types
if (!key->d->fromEVP_PKEY(pkey)) {
auto *tlsKey = QTlsBackend::backend<QSsl::TlsKeyOpenSSL>(*key);
if (!tlsKey || !tlsKey->fromEVP_PKEY(pkey)) {
qCWarning(lcSsl, "Unable to convert private key");
q_OPENSSL_sk_pop_free(reinterpret_cast<OPENSSL_STACK *>(ca),
reinterpret_cast<void (*)(void *)>(q_X509_free));

View File

@ -200,7 +200,7 @@ static QByteArray _q_PKCS12_shroudedKeyBag(const QSslKey &key, const QString &pa
QByteArray plain;
QDataStream plainStream(&plain, QIODevice::WriteOnly);
_q_PKCS12_key(key).write(plainStream);
QByteArray crypted = QSslKeyPrivate::encrypt(QSslKeyPrivate::DesEde3Cbc,
QByteArray crypted = QSslKeyPrivate::encrypt(QSsl::Cipher::DesEde3Cbc,
plain, cKey, cIv);
QList<QAsn1Element> items;

View File

@ -159,6 +159,8 @@
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.schannel");
namespace {
bool supportsTls13();
}

View File

@ -54,6 +54,7 @@
#include <private/qtnetworkglobal_p.h>
#include <private/qsslkey_p.h>
#include <private/qssl_p.h>
#include <QtNetwork/qsslcertificate.h>
#include <QtNetwork/qsslerror.h>
@ -71,8 +72,6 @@
#include <vector>
#include <memory>
QT_REQUIRE_CONFIG(ssl);
QT_BEGIN_NAMESPACE
class QByteArray;
@ -85,7 +84,9 @@ namespace QSsl {
// 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.
// decoding DER, it's initializing a key from DER. Note, QSslKey requires
// a real TLS library because private keys tend to be encrypted. This
// base class does not need a working TLS library.
class TlsKey {
public:
virtual ~TlsKey();
@ -112,7 +113,7 @@ public:
// Needed by QSslKeyPrivate::pemFromDer() for non-OpenSSL backends.
virtual bool isPkcs8() const = 0;
using Cipher = QSslKeyPrivate::Cipher;
using Cipher = QSsl::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,
@ -243,7 +244,6 @@ Q_DECLARE_LOGGING_CATEGORY(lcTlsBackend)
#define QTlsBackend_iid "org.qt-project.Qt.QTlsBackend"
Q_DECLARE_INTERFACE(QTlsBackend, QTlsBackend_iid);
QT_END_NAMESPACE
#endif // QTLSBACKEND_P_H

View File

@ -97,7 +97,9 @@ protected:
static QByteArray pkcs8Header(bool encrypted);
static QByteArray pkcs8Footer(bool encrypted);
static bool isEncryptedPkcs8(const QByteArray &der);
public:
// TLSTODO: this public is quick fix needed by old _openssl classes
// will become non-public as soon as those classes fixed.
bool keyIsNull = true;
KeyType keyType = PublicKey;
KeyAlgorithm keyAlgorithm = Opaque;

View File

@ -1,5 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
@ -60,6 +61,7 @@ QT_BEGIN_NAMESPACE
// 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 {
@ -412,7 +414,7 @@ QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhr
hash.addData(data);
if (cipher == QSslKeyPrivate::Aes128Cbc)
if (cipher == QSsl::Cipher::Aes128Cbc)
return hash.result();
QByteArray key(hash.result());
@ -420,7 +422,7 @@ QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhr
hash.addData(key);
hash.addData(data);
if (cipher == QSslKeyPrivate::Aes192Cbc)
if (cipher == QSsl::Cipher::Aes192Cbc)
return key.append(hash.result().constData(), 8);
return key.append(hash.result());
@ -434,10 +436,10 @@ QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase
hash.addData(passPhrase);
hash.addData(iv);
switch (cipher) {
case QSslKeyPrivate::DesCbc:
case QSsl::Cipher::DesCbc:
key = hash.result().left(8);
break;
case QSslKeyPrivate::DesEde3Cbc:
case QSsl::Cipher::DesEde3Cbc:
key = hash.result();
hash.reset();
hash.addData(key);
@ -445,12 +447,12 @@ QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase
hash.addData(iv);
key += hash.result().left(8);
break;
case QSslKeyPrivate::Rc2Cbc:
case QSsl::Cipher::Rc2Cbc:
key = hash.result();
break;
case QSslKeyPrivate::Aes128Cbc:
case QSslKeyPrivate::Aes192Cbc:
case QSslKeyPrivate::Aes256Cbc:
case QSsl::Cipher::Aes128Cbc:
case QSsl::Cipher::Aes192Cbc:
case QSsl::Cipher::Aes256Cbc:
return deriveAesKey(cipher, passPhrase, iv);
}
return key;
@ -686,17 +688,17 @@ void TlsKeyGeneric::decodePem(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm,
QSslKeyPrivate::Cipher cipher;
if (dekInfo.first() == "DES-CBC") {
cipher = QSslKeyPrivate::DesCbc;
cipher = QSsl::Cipher::DesCbc;
} else if (dekInfo.first() == "DES-EDE3-CBC") {
cipher = QSslKeyPrivate::DesEde3Cbc;
cipher = QSsl::Cipher::DesEde3Cbc;
} else if (dekInfo.first() == "RC2-CBC") {
cipher = QSslKeyPrivate::Rc2Cbc;
cipher = QSsl::Cipher::Rc2Cbc;
} else if (dekInfo.first() == "AES-128-CBC") {
cipher = QSslKeyPrivate::Aes128Cbc;
cipher = QSsl::Cipher::Aes128Cbc;
} else if (dekInfo.first() == "AES-192-CBC") {
cipher = QSslKeyPrivate::Aes192Cbc;
cipher = QSsl::Cipher::Aes192Cbc;
} else if (dekInfo.first() == "AES-256-CBC") {
cipher = QSslKeyPrivate::Aes256Cbc;
cipher = QSsl::Cipher::Aes256Cbc;
} else {
clear(deepClear);
return;
@ -720,7 +722,7 @@ QByteArray TlsKeyGeneric::toPem(const QByteArray &passPhrase) const
quint64 random = QRandomGenerator::system()->generate64();
QByteArray iv = QByteArray::fromRawData(reinterpret_cast<const char *>(&random), sizeof(random));
auto cipher = QSslKeyPrivate::DesEde3Cbc;
auto cipher = QSsl::Cipher::DesEde3Cbc;
const QByteArray key = deriveKey(cipher, passPhrase, iv);
data = encrypt(cipher, derData, key, iv);
@ -735,12 +737,67 @@ QByteArray TlsKeyGeneric::toPem(const QByteArray &passPhrase) const
QByteArray TlsKeyGeneric::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
{
Q_UNUSED(pem)
Q_UNUSED(headers)
if (derData.size())
return derData;
// This is quite an ugly hack, but so be it. Generic is using der, this 'pem' is coming from
// 'toPem()' for OpenSSL.
return derData;
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 TlsKeyGeneric::fromHandle(Qt::HANDLE handle, KeyType expectedType)

View File

@ -66,24 +66,11 @@ 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.
// our SecureTransport and Schannel backends.
class TlsKeyGeneric : public TlsKeyBase
{
public:
TlsKeyGeneric(KeyType type = PublicKey, KeyAlgorithm algorithm = Opaque)
: TlsKeyBase(type, algorithm)
{
// Note, while clear is pure-virtual in the base class,
// the final-overrider in this class is sufficient.
clear(false);
}
~TlsKeyGeneric()
{
clear(true);
}
using TlsKeyBase::TlsKeyBase;
void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der,
const QByteArray &passPhrase, bool deepClear) override;
@ -113,6 +100,29 @@ public:
{
return pkcs8;
}
QByteArray decrypt(Cipher cipher, const QByteArray &data,
const QByteArray &key, const QByteArray &iv) const override
{
// The real implementation is to be provided by Schannel or SecureTransport.
Q_UNUSED(cipher)
Q_UNUSED(data)
Q_UNUSED(key)
Q_UNUSED(iv)
return {};
}
QByteArray encrypt(Cipher cipher, const QByteArray &data,
const QByteArray &key, const QByteArray &iv) const override
{
// The real implementation is to be provided by Schannel or SecureTransport.
Q_UNUSED(cipher)
Q_UNUSED(data)
Q_UNUSED(key)
Q_UNUSED(iv)
return {};
}
private:
QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase);

View File

@ -401,28 +401,28 @@ QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
int i = 0, len = 0;
switch (cipher) {
case QSslKeyPrivate::DesCbc:
case QSsl::Cipher::DesCbc:
#ifndef OPENSSL_NO_DES
type = q_EVP_des_cbc();
#endif
break;
case QSslKeyPrivate::DesEde3Cbc:
case QSsl::Cipher::DesEde3Cbc:
#ifndef OPENSSL_NO_DES
type = q_EVP_des_ede3_cbc();
#endif
break;
case QSslKeyPrivate::Rc2Cbc:
case QSsl::Cipher::Rc2Cbc:
#ifndef OPENSSL_NO_RC2
type = q_EVP_rc2_cbc();
#endif
break;
case QSslKeyPrivate::Aes128Cbc:
case QSsl::Cipher::Aes128Cbc:
type = q_EVP_aes_128_cbc();
break;
case QSslKeyPrivate::Aes192Cbc:
case QSsl::Cipher::Aes192Cbc:
type = q_EVP_aes_192_cbc();
break;
case QSslKeyPrivate::Aes256Cbc:
case QSsl::Cipher::Aes256Cbc:
type = q_EVP_aes_256_cbc();
break;
}
@ -437,7 +437,7 @@ QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
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)
if (cipher == QSsl::Cipher::Rc2Cbc)
q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), nullptr);
q_EVP_CipherInit_ex(ctx, nullptr, nullptr,

View File

@ -68,6 +68,8 @@
QT_BEGIN_NAMESPACE
QT_REQUIRE_CONFIG(ssl);
namespace QSsl {
class TlsKeyOpenSSL final : public TlsKeyBase

View File

@ -46,6 +46,7 @@
#include <QtCore/qscopeguard.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qt_windows.h>
#include <wincrypt.h>
QT_BEGIN_NAMESPACE
@ -54,15 +55,15 @@ namespace {
const wchar_t *getName(QSslKeyPrivate::Cipher cipher)
{
switch (cipher) {
case QSslKeyPrivate::Cipher::DesCbc:
case QSsl::Cipher::DesCbc:
return BCRYPT_DES_ALGORITHM;
case QSslKeyPrivate::Cipher::DesEde3Cbc:
case QSsl::Cipher::DesEde3Cbc:
return BCRYPT_3DES_ALGORITHM;
case QSslKeyPrivate::Cipher::Rc2Cbc:
case QSsl::Cipher::Rc2Cbc:
return BCRYPT_RC2_ALGORITHM;
case QSslKeyPrivate::Cipher::Aes128Cbc:
case QSslKeyPrivate::Cipher::Aes192Cbc:
case QSslKeyPrivate::Cipher::Aes256Cbc:
case QSsl::Cipher::Aes128Cbc:
case QSsl::Cipher::Aes192Cbc:
case QSsl::Cipher::Aes256Cbc:
return BCRYPT_AES_ALGORITHM;
}
Q_UNREACHABLE();

View File

@ -57,6 +57,8 @@
#include <QtCore/qglobal.h>
QT_REQUIRE_CONFIG(ssl);
QT_BEGIN_NAMESPACE
namespace QSsl {
@ -70,8 +72,6 @@ public:
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

View File

@ -60,21 +60,21 @@ QByteArray wrapCCCrypt(CCOperation ccOp, QSslKeyPrivate::Cipher cipher,
int blockSize = {};
CCAlgorithm ccAlgorithm = {};
switch (cipher) {
case QSslKeyPrivate::DesCbc:
case Cipher::DesCbc:
blockSize = kCCBlockSizeDES;
ccAlgorithm = kCCAlgorithmDES;
break;
case QSslKeyPrivate::DesEde3Cbc:
case Cipher::DesEde3Cbc:
blockSize = kCCBlockSize3DES;
ccAlgorithm = kCCAlgorithm3DES;
break;
case QSslKeyPrivate::Rc2Cbc:
case Cipher::Rc2Cbc:
blockSize = kCCBlockSizeRC2;
ccAlgorithm = kCCAlgorithmRC2;
break;
case QSslKeyPrivate::Aes128Cbc:
case QSslKeyPrivate::Aes192Cbc:
case QSslKeyPrivate::Aes256Cbc:
case Cipher::Aes128Cbc:
case Cipher::Aes192Cbc:
case Cipher::Aes256Cbc:
blockSize = kCCBlockSizeAES128;
ccAlgorithm = kCCAlgorithmAES;
break;
@ -93,13 +93,13 @@ QByteArray wrapCCCrypt(CCOperation ccOp, QSslKeyPrivate::Cipher cipher,
} // Unnamed namespace.
QByteArray TlsKeySecureTransport::decrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
QByteArray TlsKeySecureTransport::decrypt(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,
QByteArray TlsKeySecureTransport::encrypt(Cipher cipher, const QByteArray &data,
const QByteArray &key, const QByteArray &iv) const
{
return wrapCCCrypt(kCCEncrypt, cipher, data, key, iv);

View File

@ -57,6 +57,8 @@
#include <QtCore/qglobal.h>
QT_REQUIRE_CONFIG(ssl);
QT_BEGIN_NAMESPACE
namespace QSsl {

View File

@ -614,7 +614,7 @@ Q_DECLARE_METATYPE(QSslKeyPrivate::Cipher)
void tst_QSslKey::encrypt_data()
{
QTest::addColumn<QSslKeyPrivate::Cipher>("cipher");
QTest::addColumn<QSsl::Cipher>("cipher");
QTest::addColumn<QByteArray>("key");
QTest::addColumn<QByteArray>("plainText");
QTest::addColumn<QByteArray>("cipherText");
@ -622,91 +622,91 @@ void tst_QSslKey::encrypt_data()
QByteArray iv("abcdefgh");
QTest::newRow("DES-CBC, length 0")
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
<< QByteArray()
<< QByteArray::fromHex("956585228BAF9B1F")
<< iv;
QTest::newRow("DES-CBC, length 1")
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
<< QByteArray(1, 'a')
<< QByteArray::fromHex("E6880AF202BA3C12")
<< iv;
QTest::newRow("DES-CBC, length 2")
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
<< QByteArray(2, 'a')
<< QByteArray::fromHex("A82492386EED6026")
<< iv;
QTest::newRow("DES-CBC, length 3")
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
<< QByteArray(3, 'a')
<< QByteArray::fromHex("90B76D5B79519CBA")
<< iv;
QTest::newRow("DES-CBC, length 4")
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
<< QByteArray(4, 'a')
<< QByteArray::fromHex("63E3DD6FED87052A")
<< iv;
QTest::newRow("DES-CBC, length 5")
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
<< QByteArray(5, 'a')
<< QByteArray::fromHex("03ACDB0EACBDFA94")
<< iv;
QTest::newRow("DES-CBC, length 6")
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
<< QByteArray(6, 'a')
<< QByteArray::fromHex("7D95024E42A3A88A")
<< iv;
QTest::newRow("DES-CBC, length 7")
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
<< QByteArray(7, 'a')
<< QByteArray::fromHex("5003436B8A8E42E9")
<< iv;
QTest::newRow("DES-CBC, length 8")
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
<< QByteArray(8, 'a')
<< QByteArray::fromHex("E4C1F054BF5521C0A4A0FD4A2BC6C1B1")
<< iv;
QTest::newRow("DES-EDE3-CBC, length 0")
<< QSslKeyPrivate::DesEde3Cbc << QByteArray("0123456789abcdefghijklmn")
<< QSsl::Cipher::DesEde3Cbc << QByteArray("0123456789abcdefghijklmn")
<< QByteArray()
<< QByteArray::fromHex("3B2B4CD0B0FD495F")
<< iv;
QTest::newRow("DES-EDE3-CBC, length 8")
<< QSslKeyPrivate::DesEde3Cbc << QByteArray("0123456789abcdefghijklmn")
<< QSsl::Cipher::DesEde3Cbc << QByteArray("0123456789abcdefghijklmn")
<< QByteArray(8, 'a')
<< QByteArray::fromHex("F2A5A87763C54A72A3224103D90CDB03")
<< iv;
QTest::newRow("RC2-40-CBC, length 0")
<< QSslKeyPrivate::Rc2Cbc << QByteArray("01234")
<< QSsl::Cipher::Rc2Cbc << QByteArray("01234")
<< QByteArray()
<< QByteArray::fromHex("6D05D52392FF6E7A")
<< iv;
QTest::newRow("RC2-40-CBC, length 8")
<< QSslKeyPrivate::Rc2Cbc << QByteArray("01234")
<< QSsl::Cipher::Rc2Cbc << QByteArray("01234")
<< QByteArray(8, 'a')
<< QByteArray::fromHex("75768E64C5749072A5D168F3AFEB0005")
<< iv;
QTest::newRow("RC2-64-CBC, length 0")
<< QSslKeyPrivate::Rc2Cbc << QByteArray("01234567")
<< QSsl::Cipher::Rc2Cbc << QByteArray("01234567")
<< QByteArray()
<< QByteArray::fromHex("ADAE6BF70F420130")
<< iv;
QTest::newRow("RC2-64-CBC, length 8")
<< QSslKeyPrivate::Rc2Cbc << QByteArray("01234567")
<< QSsl::Cipher::Rc2Cbc << QByteArray("01234567")
<< QByteArray(8, 'a')
<< QByteArray::fromHex("C7BF5C80AFBE9FBEFBBB9FD935F6D0DF")
<< iv;
QTest::newRow("RC2-128-CBC, length 0")
<< QSslKeyPrivate::Rc2Cbc << QByteArray("012345679abcdefg")
<< QSsl::Cipher::Rc2Cbc << QByteArray("012345679abcdefg")
<< QByteArray()
<< QByteArray::fromHex("1E965D483A13C8FB")
<< iv;
QTest::newRow("RC2-128-CBC, length 8")
<< QSslKeyPrivate::Rc2Cbc << QByteArray("012345679abcdefg")
<< QSsl::Cipher::Rc2Cbc << QByteArray("012345679abcdefg")
<< QByteArray(8, 'a')
<< QByteArray::fromHex("5AEC1A5B295660B02613454232F7DECE")
<< iv;
@ -715,34 +715,34 @@ void tst_QSslKey::encrypt_data()
// AES needs a longer IV
iv = QByteArray("abcdefghijklmnop");
QTest::newRow("AES-128-CBC, length 0")
<< QSslKeyPrivate::Aes128Cbc << QByteArray("012345679abcdefg")
<< QSsl::Cipher::Aes128Cbc << QByteArray("012345679abcdefg")
<< QByteArray()
<< QByteArray::fromHex("28DE1A9AA26601C30DD2527407121D1A")
<< iv;
QTest::newRow("AES-128-CBC, length 8")
<< QSslKeyPrivate::Aes128Cbc << QByteArray("012345679abcdefg")
<< QSsl::Cipher::Aes128Cbc << QByteArray("012345679abcdefg")
<< QByteArray(8, 'a')
<< QByteArray::fromHex("08E880B1BA916F061C1E801D7F44D0EC")
<< iv;
QTest::newRow("AES-192-CBC, length 0")
<< QSslKeyPrivate::Aes192Cbc << QByteArray("0123456789abcdefghijklmn")
<< QSsl::Cipher::Aes192Cbc << QByteArray("0123456789abcdefghijklmn")
<< QByteArray()
<< QByteArray::fromHex("E169E0E205CDC2BA895B7CF6097673B1")
<< iv;
QTest::newRow("AES-192-CBC, length 8")
<< QSslKeyPrivate::Aes192Cbc << QByteArray("0123456789abcdefghijklmn")
<< QSsl::Cipher::Aes192Cbc << QByteArray("0123456789abcdefghijklmn")
<< QByteArray(8, 'a')
<< QByteArray::fromHex("3A227D6A3A13237316D30AA17FF9B0A7")
<< iv;
QTest::newRow("AES-256-CBC, length 0")
<< QSslKeyPrivate::Aes256Cbc << QByteArray("0123456789abcdefghijklmnopqrstuv")
<< QSsl::Cipher::Aes256Cbc << QByteArray("0123456789abcdefghijklmnopqrstuv")
<< QByteArray()
<< QByteArray::fromHex("4BAACAA0D22199C97DE206C465B7B14A")
<< iv;
QTest::newRow("AES-256-CBC, length 8")
<< QSslKeyPrivate::Aes256Cbc << QByteArray("0123456789abcdefghijklmnopqrstuv")
<< QSsl::Cipher::Aes256Cbc << QByteArray("0123456789abcdefghijklmnopqrstuv")
<< QByteArray(8, 'a')
<< QByteArray::fromHex("879C8C25EC135CDF0B14490A0A7C2F67")
<< iv;