AusweisApp2/patches/qt-Make-server-side-signatu...

351 lines
16 KiB
Diff

From 992a338b639e4df6da16659dc238dbaae0ae802f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20L=C3=B6sch?= <Sebastian.Loesch@governikus.de>
Date: Thu, 21 Apr 2016 09:19:19 +0200
Subject: [PATCH] Make server side signature algorithms configurable
Signature algorithms are used during the TLS handshake phase to protect
transferred security parameters, e.g the message ServerKeyExchange.
This patch enables the configuration of allowed algorithms used by the
server side.
Change-Id: Ia178efd4778b91863fcc919bf50219115b300d77
---
src/network/ssl/qsslconfiguration.cpp | 42 +++++++++++++
src/network/ssl/qsslconfiguration.h | 8 ++-
src/network/ssl/qsslconfiguration_p.h | 5 ++
src/network/ssl/qsslcontext_openssl.cpp | 45 ++++++++++++++
src/network/ssl/qsslcontext_openssl_p.h | 1 +
src/network/ssl/qsslsocket.cpp | 2 +
src/network/ssl/qsslsocket_openssl_symbols_p.h | 5 ++
.../auto/network/ssl/qsslsocket/tst_qsslsocket.cpp | 70 ++++++++++++++++++++++
8 files changed, 177 insertions(+), 1 deletion(-)
diff --git x/qtbase/src/network/ssl/qsslconfiguration.cpp y/qtbase/src/network/ssl/qsslconfiguration.cpp
index 75a880f..37f99fe 100644
--- x/qtbase/src/network/ssl/qsslconfiguration.cpp
+++ y/qtbase/src/network/ssl/qsslconfiguration.cpp
@@ -221,6 +221,7 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->peerVerifyMode == other.d->peerVerifyMode &&
d->peerVerifyDepth == other.d->peerVerifyDepth &&
d->allowRootCertOnDemandLoading == other.d->allowRootCertOnDemandLoading &&
+ d->signatureAndHashAlgorithms == other.d->signatureAndHashAlgorithms &&
d->sslOptions == other.d->sslOptions &&
d->sslSession == other.d->sslSession &&
d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint &&
@@ -263,6 +264,7 @@ bool QSslConfiguration::isNull() const
d->privateKey.isNull() &&
d->peerCertificate.isNull() &&
d->peerCertificateChain.count() == 0 &&
+ d->signatureAndHashAlgorithms.isEmpty() &&
d->sslOptions == QSslConfigurationPrivate::defaultSslOptions &&
d->sslSession.isNull() &&
d->sslSessionTicketLifeTimeHint == -1 &&
@@ -870,6 +872,46 @@ void QSslConfiguration::setDiffieHellmanParameters(const QSslDiffieHellmanParame
}
/*!
+ \since 5.9
+
+ Returns the connection's current list of supported signature
+ algorithms if enabled. Enable it by calling
+ setSignatureAndHashAlgorithms().
+
+ \sa setSignatureAndHashAlgorithms()
+ */
+QVector<QPair<QSsl::KeyAlgorithm, QCryptographicHash::Algorithm> > QSslConfiguration::signatureAndHashAlgorithms() const
+{
+ return d->signatureAndHashAlgorithms;
+}
+
+/*!
+ \since 5.9
+
+ Sets the list of signature algorithms to be used for the current
+ connection. The algorithms are expected to be ordered by descending
+ preference (i.e., the first algorithm is the most preferred one).
+ Notice that this restricts the list of supported ciphers (e.g.
+ configuring the signature algorithm RSA+SHA1 will restrict the ciphers
+ to RSA ciphers).
+
+ When configuring the client side this are the algorithms set in the
+ Signature Algorithms TLS extension, see RFC 5246 for details. Although
+ this extension will be ignored for TLS protocol versions prior 1.2
+ this still restricts the supported ciphers as mentioned above.
+
+ By default, the handshake phase can choose any of the algorithms
+ supported by this system's SSL libraries, which may vary from
+ system to system.
+
+ \sa signatureAndHashAlgorithms()
+ */
+void QSslConfiguration::setSignatureAndHashAlgorithms(const QVector<QPair<QSsl::KeyAlgorithm, QCryptographicHash::Algorithm> > &algorithms)
+{
+ d->signatureAndHashAlgorithms = algorithms;
+}
+
+/*!
\since 5.3
This function returns the protocol negotiated with the server
diff --git x/qtbase/src/network/ssl/qsslconfiguration.h y/qtbase/src/network/ssl/qsslconfiguration.h
index 1c57beb..4d3e512 100644
--- x/qtbase/src/network/ssl/qsslconfiguration.h
+++ y/qtbase/src/network/ssl/qsslconfiguration.h
@@ -56,10 +56,13 @@
#ifndef QSSLCONFIGURATION_H
#define QSSLCONFIGURATION_H
-#include <QtNetwork/qtnetworkglobal.h>
+#include <QtCore/qcryptographichash.h>
+#include <QtCore/qpair.h>
#include <QtCore/qshareddata.h>
+#include <QtCore/qvector.h>
#include <QtNetwork/qsslsocket.h>
#include <QtNetwork/qssl.h>
+#include <QtNetwork/qtnetworkglobal.h>
#ifndef QT_NO_SSL
@@ -149,6 +152,9 @@ public:
QSslDiffieHellmanParameters diffieHellmanParameters() const;
void setDiffieHellmanParameters(const QSslDiffieHellmanParameters &dhparams);
+ QVector<QPair<QSsl::KeyAlgorithm, QCryptographicHash::Algorithm> > signatureAndHashAlgorithms() const;
+ void setSignatureAndHashAlgorithms(const QVector<QPair<QSsl::KeyAlgorithm, QCryptographicHash::Algorithm> > &algorithms);
+
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
diff --git x/qtbase/src/network/ssl/qsslconfiguration_p.h y/qtbase/src/network/ssl/qsslconfiguration_p.h
index 6adf2c9..7be2539 100644
--- x/qtbase/src/network/ssl/qsslconfiguration_p.h
+++ y/qtbase/src/network/ssl/qsslconfiguration_p.h
@@ -75,6 +75,9 @@
#include "qsslkey.h"
#include "qsslellipticcurve.h"
#include "qssldiffiehellmanparameters.h"
+#include <QtCore/qcryptographichash.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qvector.h>
QT_BEGIN_NAMESPACE
@@ -123,6 +126,8 @@ public:
QSslDiffieHellmanParameters dhParams;
+ QVector<QPair<QSsl::KeyAlgorithm, QCryptographicHash::Algorithm> > signatureAndHashAlgorithms;
+
QByteArray sslSession;
int sslSessionTicketLifeTimeHint;
diff --git x/qtbase/src/network/ssl/qsslcontext_openssl.cpp y/qtbase/src/network/ssl/qsslcontext_openssl.cpp
index c92d8fc..29df53a 100644
--- x/qtbase/src/network/ssl/qsslcontext_openssl.cpp
+++ y/qtbase/src/network/ssl/qsslcontext_openssl.cpp
@@ -42,6 +42,7 @@
#include <QtNetwork/qsslsocket.h>
#include <QtNetwork/qssldiffiehellmanparameters.h>
+#include <QtCore/qmetaobject.h>
#include <QtCore/qmutex.h>
#include "private/qssl_p.h"
@@ -78,6 +79,11 @@ QSslContext::~QSslContext()
q_SSL_SESSION_free(session);
}
+static inline QString msgErrorSettingSignatureAlgorithms(const QString &why)
+{
+ return QSslSocket::tr("Error when setting the signature algorithms (%1)").arg(why);
+}
+
static inline QString msgErrorSettingEllipticCurves(const QString &why)
{
return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why);
@@ -371,6 +377,45 @@ init_context:
sslContext->errorCode = QSslError::UnspecifiedError;
}
}
+
+ const auto& sigAndHashAlgorithms = sslContext->sslConfiguration.signatureAndHashAlgorithms();
+ if (!sigAndHashAlgorithms.isEmpty()) {
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ if (q_SSLeay() >= 0x10002000L) {
+ QMetaEnum hashMetaEnum = QMetaEnum::fromType<QCryptographicHash::Algorithm>();
+ QByteArrayList algorithmList;
+ for (int i=0; i < sigAndHashAlgorithms.size(); ++i) {
+ QByteArray sig;
+ switch (sigAndHashAlgorithms[i].first) {
+ case QSsl::KeyAlgorithm::Rsa:
+ sig = QByteArrayLiteral("RSA");
+ break;
+ case QSsl::KeyAlgorithm::Dsa:
+ sig = QByteArrayLiteral("DSA");
+ break;
+ case QSsl::KeyAlgorithm::Ec:
+ sig = QByteArrayLiteral("ECDSA");
+ break;
+ case QSsl::KeyAlgorithm::Opaque:
+ qCWarning(lcSsl, "Invalid value KeyAlgorithm::Opaque will be ignored");
+ continue;
+ }
+ QByteArray hash = QByteArray(hashMetaEnum.valueToKey(sigAndHashAlgorithms[i].second)).toUpper();
+ algorithmList += sig + QByteArrayLiteral("+") + hash;
+ }
+ QByteArray algorithms = algorithmList.join(':');
+ if (!q_SSL_CTX_set1_sigalgs_list(sslContext->ctx, algorithms.data())) {
+ sslContext->errorStr = msgErrorSettingSignatureAlgorithms(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ }
+ } else
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+ {
+ // specific algorithms requested, but not possible to set -> error
+ sslContext->errorStr = msgErrorSettingSignatureAlgorithms(QSslSocket::tr("OpenSSL version too old, need at least v1.0.2"));
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ }
+ }
}
QSslContext* QSslContext::fromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading)
diff --git x/qtbase/src/network/ssl/qsslcontext_openssl_p.h y/qtbase/src/network/ssl/qsslcontext_openssl_p.h
index 06a31af..c8c8e194 100644
--- x/qtbase/src/network/ssl/qsslcontext_openssl_p.h
+++ y/qtbase/src/network/ssl/qsslcontext_openssl_p.h
@@ -54,6 +54,7 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include <QtCore/qobjectdefs.h>
#include <QtCore/qvariant.h>
#include <QtNetwork/qsslcertificate.h>
#include <QtNetwork/qsslconfiguration.h>
diff --git x/qtbase/src/network/ssl/qsslsocket.cpp y/qtbase/src/network/ssl/qsslsocket.cpp
index 29e1f32..8257112 100644
--- x/qtbase/src/network/ssl/qsslsocket.cpp
+++ y/qtbase/src/network/ssl/qsslsocket.cpp
@@ -922,6 +922,7 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
d->configuration.peerVerifyDepth = configuration.peerVerifyDepth();
d->configuration.peerVerifyMode = configuration.peerVerifyMode();
d->configuration.protocol = configuration.protocol();
+ d->configuration.signatureAndHashAlgorithms = configuration.signatureAndHashAlgorithms();
d->configuration.sslOptions = configuration.d->sslOptions;
d->configuration.sslSession = configuration.sessionTicket();
d->configuration.sslSessionTicketLifeTimeHint = configuration.sessionTicketLifeTimeHint();
@@ -2230,6 +2231,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
ptr->peerVerifyDepth = global->peerVerifyDepth;
ptr->sslOptions = global->sslOptions;
ptr->ellipticCurves = global->ellipticCurves;
+ ptr->signatureAndHashAlgorithms = global->signatureAndHashAlgorithms;
}
/*!
diff --git x/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h y/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h
index b35a895..d4cd493 100644
--- x/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ y/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -517,6 +517,11 @@ int q_EC_curve_nist2nid(const char *name);
#define q_SSL_get_server_tmp_key(ssl, key) q_SSL_ctrl((ssl), SSL_CTRL_GET_SERVER_TMP_KEY, 0, (char *)key)
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+// Signature algorithm extension
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+#define q_SSL_CTX_set1_sigalgs_list(ctx, s) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_SIGALGS_LIST, 0, (char *)s)
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+
// PKCS#12 support
int q_PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca);
PKCS12 *q_d2i_PKCS12_bio(BIO *bio, PKCS12 **pkcs12);
diff --git x/qtbase/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp y/qtbase/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
index 4eb26d1..79a55cc 100644
--- x/qtbase/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
+++ y/qtbase/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
@@ -236,6 +236,8 @@ private slots:
void ephemeralServerKey();
void allowedProtocolNegotiation();
void pskServer();
+ void signatureAlgorithm_data();
+ void signatureAlgorithm();
#endif
void setEmptyDefaultConfiguration(); // this test should be last
@@ -3744,6 +3746,74 @@ void tst_QSslSocket::pskServer()
QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState);
QCOMPARE(disconnectedSpy.count(), 1);
}
+using SigAlgPair = QPair<QSsl::KeyAlgorithm, QCryptographicHash::Algorithm>;
+Q_DECLARE_METATYPE(QCryptographicHash::Algorithm);
+Q_DECLARE_METATYPE(QSsl::KeyAlgorithm);
+Q_DECLARE_METATYPE(SigAlgPair);
+
+void tst_QSslSocket::signatureAlgorithm_data()
+{
+ QTest::addColumn<SigAlgPair>("serverSigAlgPair");
+ QTest::addColumn<QSsl::SslProtocol>("serverProtocol");
+ QTest::addColumn<SigAlgPair>("clientSigAlgPair");
+ QTest::addColumn<QSsl::SslProtocol>("clientProtocol");
+ QTest::addColumn<QAbstractSocket::SocketState>("state");
+
+ auto rsaSha256 = SigAlgPair(QSsl::Rsa, QCryptographicHash::Sha256);
+ auto rsaSha512 = SigAlgPair(QSsl::Rsa, QCryptographicHash::Sha512);
+ auto ecdsaSha512= SigAlgPair(QSsl::Ec, QCryptographicHash::Sha512);
+
+ QTest::newRow("match_TlsV1_2") << rsaSha256 << QSsl::TlsV1_2 << rsaSha256 << QSsl::AnyProtocol << QAbstractSocket::ConnectedState;
+ QTest::newRow("no_hashalg_match_TlsV1_2") << rsaSha256 << QSsl::TlsV1_2 << rsaSha512 << QSsl::AnyProtocol << QAbstractSocket::UnconnectedState;
+ QTest::newRow("no_sigalg_match_TlsV1_2") << ecdsaSha512 << QSsl::TlsV1_2 << rsaSha512 << QSsl::AnyProtocol << QAbstractSocket::UnconnectedState;
+ QTest::newRow("no_cipher_match_AnyProtocol") << rsaSha512 << QSsl::TlsV1_2 << ecdsaSha512 << QSsl::AnyProtocol << QAbstractSocket::UnconnectedState;
+
+ // signature algorithms do not match, but are ignored because the tls version is not v1.2
+ QTest::newRow("client_ignore_TlsV1_1") << rsaSha256 << QSsl::TlsV1_1 << rsaSha512 << QSsl::AnyProtocol << QAbstractSocket::ConnectedState;
+ QTest::newRow("server_ignore_TlsV1_1") << rsaSha256 << QSsl::AnyProtocol << rsaSha512 << QSsl::TlsV1_1 << QAbstractSocket::ConnectedState;
+ QTest::newRow("client_ignore_TlsV1_0") << rsaSha256 << QSsl::TlsV1_0 << rsaSha512 << QSsl::AnyProtocol << QAbstractSocket::ConnectedState;
+ QTest::newRow("server_ignore_TlsV1_0") << rsaSha256 << QSsl::AnyProtocol << rsaSha512 << QSsl::TlsV1_0 << QAbstractSocket::ConnectedState;
+}
+
+
+void tst_QSslSocket::signatureAlgorithm()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (!QSslSocket::supportsSsl() || setProxy)
+ return;
+
+ QFETCH(SigAlgPair, serverSigAlgPair);
+ QFETCH(QSsl::SslProtocol, serverProtocol);
+ QFETCH(SigAlgPair, clientSigAlgPair);
+ QFETCH(QSsl::SslProtocol, clientProtocol);
+ QFETCH(QAbstractSocket::SocketState, state);
+
+
+ SslServer server;
+ server.protocol = serverProtocol;
+ server.config.setCiphers({QSslCipher("ECDHE-RSA-AES256-SHA")});
+ server.config.setSignatureAndHashAlgorithms({serverSigAlgPair});
+ QVERIFY(server.listen());
+
+ QSslConfiguration clientConfig = QSslConfiguration::defaultConfiguration();
+ clientConfig.setSignatureAndHashAlgorithms({clientSigAlgPair});
+ clientConfig.setProtocol(clientProtocol);
+ QSslSocket client;
+ client.setSslConfiguration(clientConfig);
+ socket = &client;
+
+ QEventLoop loop;
+ QTimer::singleShot(5000, &loop, SLOT(quit()));
+ connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
+ connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
+
+
+ client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
+ loop.exec();
+ QCOMPARE(client.state(), state);
+}
+
#endif // QT_NO_OPENSSL
--
2.10.2