diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index 2aa59e5d180..9430e81fd32 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -202,6 +202,7 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const d->sessionCipher == other.d->sessionCipher && d->sessionProtocol == other.d->sessionProtocol && d->ciphers == other.d->ciphers && + d->ellipticCurves == other.d->ellipticCurves && d->caCertificates == other.d->caCertificates && d->protocol == other.d->protocol && d->peerVerifyMode == other.d->peerVerifyMode && @@ -242,6 +243,7 @@ bool QSslConfiguration::isNull() const d->allowRootCertOnDemandLoading == true && d->caCertificates.count() == 0 && d->ciphers.count() == 0 && + d->ellipticCurves.isEmpty() && d->localCertificateChain.isEmpty() && d->privateKey.isNull() && d->peerCertificate.isNull() && @@ -694,6 +696,50 @@ int QSslConfiguration::sessionTicketLifeTimeHint() const return d->sslSessionTicketLifeTimeHint; } +/*! + \since 5.5 + + Returns this connection's current list of elliptic curves. This + list is used during the handshake phase for choosing an + elliptic curve (when using an elliptic curve cipher). + The returned list of curves is ordered by descending preference + (i.e., the first curve in the list is the most preferred one). + + By default, the handshake phase can choose any of the curves + supported by this system's SSL libraries, which may vary from + system to system. The list of curves supported by this system's + SSL libraries is returned by QSslSocket::supportedEllipticCurves(). + + You can restrict the list of curves used for choosing the session cipher + for this socket by calling setEllipticCurves() with a subset of the + supported ciphers. You can revert to using the entire set by calling + setEllipticCurves() with the list returned by + QSslSocket::supportedEllipticCurves(). + + \sa setEllipticCurves + */ +QVector QSslConfiguration::ellipticCurves() const +{ + return d->ellipticCurves; +} + +/*! + \since 5.5 + + Sets the list of elliptic curves to be used by this socket to \a curves, + which must contain a subset of the curves in the list returned by + supportedEllipticCurves(). + + Restricting the elliptic curves must be done before the handshake + phase, where the session cipher is chosen. + + \sa ellipticCurves + */ +void QSslConfiguration::setEllipticCurves(const QVector &curves) +{ + d->ellipticCurves = curves; +} + /*! \since 5.3 diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index 0c4e1229120..7bc7a32cb7b 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -62,6 +62,7 @@ template class QList; class QSslCertificate; class QSslCipher; class QSslKey; +class QSslEllipticCurve; class QSslConfigurationPrivate; class Q_NETWORK_EXPORT QSslConfiguration @@ -122,6 +123,10 @@ public: void setSessionTicket(const QByteArray &sessionTicket); int sessionTicketLifeTimeHint() const; + // EC settings + QVector ellipticCurves() const; + void setEllipticCurves(const QVector &curves); + static QSslConfiguration defaultConfiguration(); static void setDefaultConfiguration(const QSslConfiguration &configuration); diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h index 6880f59c4b8..54a4786d9fa 100644 --- a/src/network/ssl/qsslconfiguration_p.h +++ b/src/network/ssl/qsslconfiguration_p.h @@ -66,6 +66,7 @@ #include "qsslcertificate.h" #include "qsslcipher.h" #include "qsslkey.h" +#include "qsslellipticcurve.h" QT_BEGIN_NAMESPACE @@ -107,6 +108,8 @@ public: Q_AUTOTEST_EXPORT static const QSsl::SslOptions defaultSslOptions; + QVector ellipticCurves; + QByteArray sslSession; int sslSessionTicketLifeTimeHint; diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp index a2758398f4c..e62367cef34 100644 --- a/src/network/ssl/qsslcontext_openssl.cpp +++ b/src/network/ssl/qsslcontext_openssl.cpp @@ -2,6 +2,7 @@ ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Copyright (C) 2014 BlackBerry Limited. All rights reserved. +** Copyright (C) 2014 Governikus GmbH & Co. KG. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -327,6 +328,30 @@ init_context: q_EC_KEY_free(ecdh); #endif // OPENSSL_NO_EC + const QVector qcurves = sslContext->sslConfiguration.ellipticCurves(); + if (!qcurves.isEmpty()) { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_EC) + // Set the curves to be used + if (q_SSLeay() >= 0x10002000L) { + QVarLengthArray curves; + foreach (const QSslEllipticCurve curve, qcurves) + curves.append(curve.id); + + if (!q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_CURVES, curves.size(), curves.data())) { + sslContext->errorStr = QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); + sslContext->errorCode = QSslError::UnspecifiedError; + return sslContext; + } + } else +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_EC) + { + // specific curves requested, but not possible to set -> error + sslContext->errorStr = QSslSocket::tr("Error when setting the elliptic curves (OpenSSL version too old, need at least v1.0.2)"); + sslContext->errorCode = QSslError::UnspecifiedError; + return sslContext; + } + } + return sslContext; } diff --git a/src/network/ssl/qsslellipticcurve.cpp b/src/network/ssl/qsslellipticcurve.cpp new file mode 100644 index 00000000000..7e122557df4 --- /dev/null +++ b/src/network/ssl/qsslellipticcurve.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Governikus GmbH & Co. KG. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsslellipticcurve.h" + +#ifndef QT_NO_DEBUG_STREAM +#include +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QSslEllipticCurve + \since 5.5 + + \brief Represents an elliptic curve for use by elliptic-curve cipher algorithms. + + \reentrant + \ingroup network + \ingroup ssl + \inmodule QtNetwork + + The class QSslEllipticCurve represents an elliptic curve for use by + elliptic-curve cipher algorithms. + + Elliptic curves can be constructed from a "short name" (SN) (fromShortName()), + and by a call to QSslSocket::supportedEllipticCurves(). + + QSslEllipticCurve instances can be compared for equality and can be used as keys + in QHash and QSet. They cannot be used as key in a QMap. +*/ + +/*! + \fn QSslEllipticCurve::QSslEllipticCurve() + + Constructs an invalid elliptic curve. + + \sa isValid(), QSslSocket::supportedEllipticCurves() +*/ + +/*! + \fn QSslEllipticCurve QSslEllipticCurve::fromShortName(const QString &name) + + Returns an QSslEllipticCurve instance representing the + named curve \a name. The \a name is the conventional short + name for the curve, as represented by RFC 4492 (for instance \c{secp521r1}), + or as NIST short names (for instance \c{P-256}). The actual set of + recognized names depends on the SSL implementation. + + If the given \a name is not supported, returns an invalid QSslEllipticCurve instance. + + \note The OpenSSL implementation of this function treats the name case-sensitively. + + \sa shortName() +*/ + + +/*! + \fn QString QSslEllipticCurve::shortName() const + + Returns the conventional short name for this curve. If this + curve is invalid, returns an empty string. + + \sa longName() +*/ + +/*! + \fn QString QSslEllipticCurve::longName() const + + Returns the conventional long name for this curve. If this + curve is invalid, returns an empty string. + + \sa shortName() +*/ + +/*! + \fn bool QSslEllipticCurve::isValid() const + + Returns true if this elliptic curve is a valid curve, false otherwise. +*/ + +/*! + \fn bool QSslEllipticCurve::isTlsNamedCurve() const + + Returns true if this elliptic curve is one of the named curves that can be + used in the key exchange when using an elliptic curve cipher with TLS; + false otherwise. +*/ + +/*! + \fn bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) + \since 5.5 + \relates QSslEllipticCurve + + Returns true if the curve \a lhs represents the same curve of \a rhs; + false otherwise. +*/ + +/*! + \fn bool operator!=(QSslEllipticCurve lhs, QSslEllipticCurve rhs) + \since 5.5 + \relates QSslEllipticCurve + + Returns true if the curve \a lhs represents a different curve than \a rhs; + false otherwise. +*/ + +/*! + \fn uint qHash(QSslEllipticCurve curve, uint seed) + \since 5.5 + \relates QHash + + Returns an hash value for the curve \a curve, using \a seed to seed + the calculation. +*/ + +#ifndef QT_NO_DEBUG_STREAM +/*! + \relates QSslEllipticCurve + \since 5.5 + + Writes the elliptic curve \a curve into the debug object \a debug for + debugging purposes. + + \sa {Debugging Techniques} +*/ +QDebug operator<<(QDebug debug, QSslEllipticCurve curve) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "QSslEllipticCurve(" << curve.shortName() << ")"; + return debug; +} +#endif + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslellipticcurve.h b/src/network/ssl/qsslellipticcurve.h new file mode 100644 index 00000000000..8f31b327772 --- /dev/null +++ b/src/network/ssl/qsslellipticcurve.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Governikus GmbH & Co. KG. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSSLELLIPTICCURVE_H +#define QSSLELLIPTICCURVE_H + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSslEllipticCurve; +// qHash is a friend, but we can't use default arguments for friends (ยง8.3.6.4) +Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed = 0) Q_DECL_NOTHROW; + +class QSslEllipticCurve { +public: + Q_DECL_CONSTEXPR QSslEllipticCurve() + : id(0) + { + } + + Q_NETWORK_EXPORT static QSslEllipticCurve fromShortName(const QString &name); + + Q_NETWORK_EXPORT QString shortName() const Q_REQUIRED_RESULT; + Q_NETWORK_EXPORT QString longName() const Q_REQUIRED_RESULT; + + Q_DECL_CONSTEXPR bool isValid() const + { + return id != 0; + } + + Q_NETWORK_EXPORT bool isTlsNamedCurve() const; + +private: + int id; + + friend Q_DECL_CONSTEXPR bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) Q_DECL_NOTHROW + { return lhs.id == rhs.id; } + friend Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed) Q_DECL_NOTHROW + { return qHash(curve.id, seed); } + + friend class QSslContext; + friend class QSslSocketPrivate; + friend class QSslSocketBackendPrivate; +}; + +Q_DECLARE_TYPEINFO(QSslEllipticCurve, Q_PRIMITIVE_TYPE); + +Q_DECL_CONSTEXPR inline bool operator!=(QSslEllipticCurve lhs, QSslEllipticCurve rhs) Q_DECL_NOTHROW +{ return !operator==(lhs, rhs); } + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QSslEllipticCurve curve); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QSslEllipticCurve) + +#endif // QSSLELLIPTICCURVE_H diff --git a/src/network/ssl/qsslellipticcurve_dummy.cpp b/src/network/ssl/qsslellipticcurve_dummy.cpp new file mode 100644 index 00000000000..64ac1514ab1 --- /dev/null +++ b/src/network/ssl/qsslellipticcurve_dummy.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Governikus GmbH & Co. KG. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsslellipticcurve.h" + +QT_BEGIN_NAMESPACE + +QString QSslEllipticCurve::shortName() const +{ + return QString(); +} + +QString QSslEllipticCurve::longName() const +{ + return QString(); +} + +QSslEllipticCurve QSslEllipticCurve::fromShortName(const QString &name) +{ + Q_UNUSED(name); + return QSslEllipticCurve(); +} + +bool QSslEllipticCurve::isTlsNamedCurve() const +{ + return false; +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslellipticcurve_openssl.cpp b/src/network/ssl/qsslellipticcurve_openssl.cpp new file mode 100644 index 00000000000..d8d1307b2ef --- /dev/null +++ b/src/network/ssl/qsslellipticcurve_openssl.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Governikus GmbH & Co. KG. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsslellipticcurve.h" +#include "qsslsocket_p.h" +#include "qsslsocket_openssl_symbols_p.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +QString QSslEllipticCurve::shortName() const +{ + if (id == 0) + return QString(); + + QSslSocketPrivate::ensureInitialized(); + + QString result; +#ifndef OPENSSL_NO_EC + result = QString::fromLatin1(q_OBJ_nid2sn(id)); +#endif + return result; +} + +QString QSslEllipticCurve::longName() const +{ + if (id == 0) + return QString(); + + QSslSocketPrivate::ensureInitialized(); + + QString result; +#ifndef OPENSSL_NO_EC + result = QString::fromLatin1(q_OBJ_nid2ln(id)); +#endif + return result; +} + +QSslEllipticCurve QSslEllipticCurve::fromShortName(const QString &name) +{ + if (name.isEmpty()) + return QSslEllipticCurve(); + + QSslSocketPrivate::ensureInitialized(); + + QSslEllipticCurve result; + +#ifndef OPENSSL_NO_EC + const QByteArray curveNameLatin1 = name.toLatin1(); + + int nid = q_OBJ_sn2nid(curveNameLatin1.data()); + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (nid == 0 && q_SSLeay() >= 0x10002000L) + nid = q_EC_curve_nist2nid(curveNameLatin1.data()); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + + result.id = nid; +#endif + + return result; +} + +// The brainpool curve NIDs (RFC 7027) have been introduced in OpenSSL 1.0.2, +// redefine them here to make Qt compile with previous versions of OpenSSL +// (yet correctly recognize them as TLS named curves). +// See crypto/objects/obj_mac.h +#ifndef NID_brainpoolP256r1 +#define NID_brainpoolP256r1 927 +#endif + +#ifndef NID_brainpoolP384r1 +#define NID_brainpoolP384r1 931 +#endif + +#ifndef NID_brainpoolP512r1 +#define NID_brainpoolP512r1 933 +#endif + +// NIDs 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 +static const int tlsNamedCurveNIDs[] = { + // RFC 4492 + NID_sect163k1, + NID_sect163r1, + NID_sect163r2, + NID_sect193r1, + NID_sect193r2, + NID_sect233k1, + NID_sect233r1, + NID_sect239k1, + NID_sect283k1, + NID_sect283r1, + NID_sect409k1, + NID_sect409r1, + NID_sect571k1, + NID_sect571r1, + + NID_secp160k1, + NID_secp160r1, + NID_secp160r2, + NID_secp192k1, + NID_X9_62_prime192v1, // secp192r1 + NID_secp224k1, + NID_secp224r1, + NID_secp256k1, + NID_X9_62_prime256v1, // secp256r1 + NID_secp384r1, + NID_secp521r1, + + // RFC 7027 + NID_brainpoolP256r1, + NID_brainpoolP384r1, + NID_brainpoolP512r1 +}; + +static const size_t tlsNamedCurveNIDCount = sizeof(tlsNamedCurveNIDs) / sizeof(tlsNamedCurveNIDs[0]); + +bool QSslEllipticCurve::isTlsNamedCurve() const +{ + const int * const tlsNamedCurveNIDsEnd = tlsNamedCurveNIDs + tlsNamedCurveNIDCount; + return std::find(tlsNamedCurveNIDs, tlsNamedCurveNIDsEnd, id) != tlsNamedCurveNIDsEnd; +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 06d85de91b8..e8096eca781 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -320,6 +320,7 @@ public: QMutex mutex; QList supportedCiphers; + QVector supportedEllipticCurves; QExplicitlySharedDataPointer config; }; Q_GLOBAL_STATIC(QSslSocketGlobalData, globalData) @@ -899,6 +900,7 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration) d->configuration.localCertificateChain = configuration.localCertificateChain(); d->configuration.privateKey = configuration.privateKey(); d->configuration.ciphers = configuration.ciphers(); + d->configuration.ellipticCurves = configuration.ellipticCurves(); d->configuration.caCertificates = configuration.caCertificates(); d->configuration.peerVerifyDepth = configuration.peerVerifyDepth(); d->configuration.peerVerifyMode = configuration.peerVerifyMode(); @@ -1266,6 +1268,120 @@ QList QSslSocket::supportedCiphers() return QSslSocketPrivate::supportedCiphers(); } +/*! + \since 5.5 + + Returns this socket's current list of elliptic curves. This + list is used during the socket's handshake phase for choosing an + elliptic curve (when using an elliptic curve cipher). + The returned list of curves is ordered by descending preference + (i.e., the first curve in the list is the most preferred one). + + By default, this list is empty. An empty default list means that the + handshake phase can choose any of the curves supported by this system's SSL + libraries (which may vary from system to system). The list of curves + supported by this system's SSL libraries is returned by + supportedEllipticCurves(). + + You can restrict the list of curves used for choosing the session cipher + for this socket by calling setEllipticCurves() with a subset of the + supported ciphers. You can revert to using the entire set by calling + setEllipticCurves() with the list returned by supportedEllipticCurves(). + + \sa setEllipticCurves(), defaultEllipticCurves(), setDefaultEllipticCurves(), supportedEllipticCurves() +*/ +QVector QSslSocket::ellipticCurves() const +{ + Q_D(const QSslSocket); + return d->configuration.ellipticCurves; +} + +/*! + \since 5.5 + + Sets the list of elliptic curves to be used by this socket to \a curves, + which must contain a subset of the curves in the list returned by + supportedEllipticCurves(). + + Restricting the elliptic curves must be done before the handshake + phase, where the session cipher is chosen. + + If an empty list is set, then the handshake phase can choose any of the + curves supported by this system's SSL libraries (which may vary from system + to system). The list of curves supported by this system's SSL libraries is + returned by supportedEllipticCurves(). + + Use setCipher() in order to disable the usage of elliptic curve ciphers. + + \sa ellipticCurves(), setDefaultEllipticCurves(), supportedEllipticCurves() +*/ +void QSslSocket::setEllipticCurves(const QVector &curves) +{ + Q_D(QSslSocket); + d->configuration.ellipticCurves = curves; +} + +/*! + \since 5.5 + + Sets the list of elliptic curves to be used by all sockets in this + application to \a curves, which must contain a subset of the curves in the + list returned by supportedEllipticCurves(). + + Restricting the default elliptic curves only affects SSL sockets + that perform their handshake phase after the default list has been changed. + + If an empty list is set, then the handshake phase can choose any of the + curves supported by this system's SSL libraries (which may vary from system + to system). The list of curves supported by this system's SSL libraries is + returned by supportedEllipticCurves(). + + Use setDefaultCiphers() in order to disable the usage of elliptic curve ciphers. + + \sa setEllipticCurves(), defaultEllipticCurves(), supportedEllipticCurves() +*/ +void QSslSocket::setDefaultEllipticCurves(const QVector &curves) +{ + QSslSocketPrivate::setDefaultEllipticCurves(curves); +} + + +/*! + \since 5.5 + + Returns the default elliptic curves list for all sockets in + this application. This list is used during the socket's handshake + phase when negotiating with the peer to choose a session cipher. + The list is ordered by preference (i.e., the first curve in the + list is the most preferred one). + + By default, this list is empty. An empty default list means that the + handshake phase can choose any of the curves supported by this system's SSL + libraries (which may vary from system to system). The list of curves + supported by this system's SSL libraries is returned by + supportedEllipticCurves(). + + \sa setDefaultEllipticCurves(), supportedEllipticCurves() +*/ +QVector QSslSocket::defaultEllipticCurves() +{ + return QSslSocketPrivate::defaultEllipticCurves(); +} + +/*! + \since 5.5 + + Returns the list of elliptic curves supported by this + system. This list is set by the system's SSL libraries and may + vary from system to system. + + \sa ellipticCurves(), setEllipticCurves(), defaultEllipticCurves() +*/ +QVector QSslSocket::supportedEllipticCurves() +{ + return QSslSocketPrivate::supportedEllipticCurves(); +} + /*! Searches all files in the \a path for certificates encoded in the specified \a format and adds them to this socket's CA certificate @@ -2028,6 +2144,46 @@ void QSslSocketPrivate::setDefaultSupportedCiphers(const QList &ciph globalData()->supportedCiphers = ciphers; } +/*! + \internal +*/ +QVector QSslSocketPrivate::defaultEllipticCurves() +{ + QSslSocketPrivate::ensureInitialized(); + const QMutexLocker locker(&globalData()->mutex); + return globalData()->config->ellipticCurves; +} + +/*! + \internal +*/ +QVector QSslSocketPrivate::supportedEllipticCurves() +{ + QSslSocketPrivate::ensureInitialized(); + const QMutexLocker locker(&globalData()->mutex); + return globalData()->supportedEllipticCurves; +} + +/*! + \internal +*/ +void QSslSocketPrivate::setDefaultEllipticCurves(const QVector &curves) +{ + const QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->config->ellipticCurves = curves; +} + +/*! + \internal +*/ +void QSslSocketPrivate::setDefaultSupportedEllipticCurves(const QVector &curves) +{ + const QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->supportedEllipticCurves = curves; +} + /*! \internal */ @@ -2139,6 +2295,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri ptr->peerVerifyMode = global->peerVerifyMode; ptr->peerVerifyDepth = global->peerVerifyDepth; ptr->sslOptions = global->sslOptions; + ptr->ellipticCurves = global->ellipticCurves; } /*! diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h index 1fe12e59895..149dcc99ccf 100644 --- a/src/network/ssl/qsslsocket.h +++ b/src/network/ssl/qsslsocket.h @@ -51,6 +51,7 @@ class QDir; class QSslCipher; class QSslCertificate; class QSslConfiguration; +class QSslEllipticCurve; class QSslSocketPrivate; class Q_NETWORK_EXPORT QSslSocket : public QTcpSocket @@ -149,6 +150,13 @@ public: static QList defaultCiphers(); static QList supportedCiphers(); + // EC settings. + QVector ellipticCurves() const; + void setEllipticCurves(const QVector &curves); + static void setDefaultEllipticCurves(const QVector &curves); + static QVector defaultEllipticCurves(); + static QVector supportedEllipticCurves(); + // CA settings. bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem, QRegExp::PatternSyntax syntax = QRegExp::FixedString); diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 84b0d9c75e2..516d8629367 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Governikus GmbH & Co. KG ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -55,6 +56,7 @@ #include "qsslcertificate_p.h" #include "qsslcipher_p.h" #include "qsslkey_p.h" +#include "qsslellipticcurve.h" #include #include @@ -479,6 +481,7 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded() s_loadedCiphersAndCerts = true; resetDefaultCiphers(); + resetDefaultEllipticCurves(); #ifndef QT_NO_LIBRARY //load symbols needed to receive certificates from system store @@ -627,6 +630,31 @@ void QSslSocketPrivate::resetDefaultCiphers() setDefaultCiphers(defaultCiphers); } +void QSslSocketPrivate::resetDefaultEllipticCurves() +{ + QVector curves; + +#ifndef OPENSSL_NO_EC + const size_t curveCount = q_EC_get_builtin_curves(NULL, 0); + + QVarLengthArray builtinCurves(static_cast(curveCount)); + + if (q_EC_get_builtin_curves(builtinCurves.data(), curveCount) == curveCount) { + for (size_t i = 0; i < curveCount; ++i) { + QSslEllipticCurve curve; + curve.id = builtinCurves[i].nid; + curves.append(curve); + } + } +#endif // OPENSSL_NO_EC + + // set the list of supported ECs, but not the list + // of *default* ECs. OpenSSL doesn't like forcing an EC for the wrong + // ciphersuite, so don't try it -- leave the empty list to mean + // "the implementation will choose the most suitable one". + setDefaultSupportedEllipticCurves(curves); +} + QList QSslSocketPrivate::systemCaCertificates() { ensureInitialized(); diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 71b8237e031..08dec606ae0 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -167,6 +167,7 @@ DEFINEFUNC(int, EVP_PKEY_type, int a, a, return NID_undef, return) DEFINEFUNC2(int, i2d_X509, X509 *a, a, unsigned char **b, b, return -1, return) DEFINEFUNC(const char *, OBJ_nid2sn, int a, a, return 0, return) DEFINEFUNC(const char *, OBJ_nid2ln, int a, a, return 0, return) +DEFINEFUNC(int, OBJ_sn2nid, const char *s, s, return 0, return) DEFINEFUNC3(int, i2t_ASN1_OBJECT, char *a, a, int b, b, ASN1_OBJECT *c, c, return -1, return) DEFINEFUNC4(int, OBJ_obj2txt, char *a, a, int b, b, ASN1_OBJECT *c, c, int d, d, return -1, return) @@ -371,6 +372,10 @@ DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM #ifndef OPENSSL_NO_EC DEFINEFUNC(EC_KEY *, EC_KEY_new_by_curve_name, int nid, nid, return 0, return) DEFINEFUNC(void, EC_KEY_free, EC_KEY *ecdh, ecdh, return, DUMMYARG) +DEFINEFUNC2(size_t, EC_get_builtin_curves, EC_builtin_curve * r, r, size_t nitems, nitems, return 0, return) +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +DEFINEFUNC(int, EC_curve_nist2nid, const char *name, name, return 0, return) +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L #endif // OPENSSL_NO_EC DEFINEFUNC5(int, PKCS12_parse, PKCS12 *p12, p12, const char *pass, pass, EVP_PKEY **pkey, pkey, \ @@ -728,6 +733,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(EVP_PKEY_type) RESOLVEFUNC(OBJ_nid2sn) RESOLVEFUNC(OBJ_nid2ln) + RESOLVEFUNC(OBJ_sn2nid) RESOLVEFUNC(i2t_ASN1_OBJECT) RESOLVEFUNC(OBJ_obj2txt) RESOLVEFUNC(OBJ_obj2nid) @@ -878,6 +884,11 @@ bool q_resolveOpenSslSymbols() #ifndef OPENSSL_NO_EC RESOLVEFUNC(EC_KEY_new_by_curve_name) RESOLVEFUNC(EC_KEY_free) + RESOLVEFUNC(EC_get_builtin_curves) +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (q_SSLeay() >= 0x10002000L) + RESOLVEFUNC(EC_curve_nist2nid) +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L #endif // OPENSSL_NO_EC RESOLVEFUNC(PKCS12_parse) RESOLVEFUNC(d2i_PKCS12_bio) diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index e2e09e4feb6..ee19345e4a5 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -248,6 +248,7 @@ Q_AUTOTEST_EXPORT EVP_PKEY *q_EVP_PKEY_new(); int q_i2d_X509(X509 *a, unsigned char **b); const char *q_OBJ_nid2sn(int a); const char *q_OBJ_nid2ln(int a); +int q_OBJ_sn2nid(const char *s); int q_i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *obj); int q_OBJ_obj2txt(char *buf, int buf_len, ASN1_OBJECT *obj, int no_name); int q_OBJ_obj2nid(const ASN1_OBJECT *a); @@ -435,6 +436,12 @@ BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); EC_KEY *q_EC_KEY_new_by_curve_name(int nid); void q_EC_KEY_free(EC_KEY *ecdh); #define q_SSL_CTX_set_tmp_ecdh(ctx, ecdh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_ECDH, 0, (char *)ecdh) + +// EC curves management +size_t q_EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +int q_EC_curve_nist2nid(const char *name); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L #endif // OPENSSL_NO_EC // PKCS#12 support diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h index 6e7a2c55205..53f91ee2050 100644 --- a/src/network/ssl/qsslsocket_p.h +++ b/src/network/ssl/qsslsocket_p.h @@ -135,6 +135,12 @@ public: static void setDefaultSupportedCiphers(const QList &ciphers); static void resetDefaultCiphers(); + static QVector defaultEllipticCurves(); + static QVector supportedEllipticCurves(); + static void setDefaultEllipticCurves(const QVector &curves); + static void setDefaultSupportedEllipticCurves(const QVector &curves); + static void resetDefaultEllipticCurves(); + static QList defaultCaCertificates(); static QList systemCaCertificates(); static void setDefaultCaCertificates(const QList &certs); diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri index 384e149241d..85fd6fc3f52 100644 --- a/src/network/ssl/ssl.pri +++ b/src/network/ssl/ssl.pri @@ -8,6 +8,7 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op ssl/qsslconfiguration_p.h \ ssl/qsslcipher.h \ ssl/qsslcipher_p.h \ + ssl/qsslellipticcurve.h \ ssl/qsslerror.h \ ssl/qsslkey.h \ ssl/qsslkey_p.h \ @@ -20,6 +21,7 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op ssl/qsslcertificate.cpp \ ssl/qsslconfiguration.cpp \ ssl/qsslcipher.cpp \ + ssl/qsslellipticcurve.cpp \ ssl/qsslkey_p.cpp \ ssl/qsslerror.cpp \ ssl/qsslsocket.cpp \ @@ -31,7 +33,8 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op ssl/qsslcertificate_winrt.cpp \ ssl/qsslkey_qt.cpp \ ssl/qsslkey_winrt.cpp \ - ssl/qsslsocket_winrt.cpp + ssl/qsslsocket_winrt.cpp \ + ssl/qsslellipticcurve_dummy.cpp } } @@ -41,6 +44,7 @@ contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) { ssl/qsslsocket_openssl_symbols_p.h SOURCES += ssl/qsslcertificate_openssl.cpp \ ssl/qsslcontext_openssl.cpp \ + ssl/qsslellipticcurve_openssl.cpp \ ssl/qsslkey_openssl.cpp \ ssl/qsslsocket_openssl.cpp \ ssl/qsslsocket_openssl_symbols.cpp diff --git a/tests/auto/network/ssl/qsslellipticcurve/.gitignore b/tests/auto/network/ssl/qsslellipticcurve/.gitignore new file mode 100644 index 00000000000..27f97e770a0 --- /dev/null +++ b/tests/auto/network/ssl/qsslellipticcurve/.gitignore @@ -0,0 +1 @@ +tst_qsslellipticcurves diff --git a/tests/auto/network/ssl/qsslellipticcurve/qsslellipticcurve.pro b/tests/auto/network/ssl/qsslellipticcurve/qsslellipticcurve.pro new file mode 100644 index 00000000000..d9a771a0809 --- /dev/null +++ b/tests/auto/network/ssl/qsslellipticcurve/qsslellipticcurve.pro @@ -0,0 +1,8 @@ +CONFIG += testcase +CONFIG += parallel_test + +SOURCES += tst_qsslellipticcurve.cpp +!wince*:win32:LIBS += -lws2_32 +QT = core network testlib + +TARGET = tst_qsslellipticcurve diff --git a/tests/auto/network/ssl/qsslellipticcurve/tst_qsslellipticcurve.cpp b/tests/auto/network/ssl/qsslellipticcurve/tst_qsslellipticcurve.cpp new file mode 100644 index 00000000000..a5b1d14a920 --- /dev/null +++ b/tests/auto/network/ssl/qsslellipticcurve/tst_qsslellipticcurve.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Governikus GmbH & Co. KG. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include + +class tst_QSslEllipticCurve : public QObject +{ + Q_OBJECT + +#ifndef QT_NO_SSL +private Q_SLOTS: + void constExpr(); + void construction(); + void fromShortName_data(); + void fromShortName(); +#endif +}; + +#ifndef QT_NO_SSL + +void tst_QSslEllipticCurve::constExpr() +{ +#ifdef Q_COMPILER_CONSTEXPR + // check that default ctor and op ==/!= are constexpr: + char array1[QSslEllipticCurve() == QSslEllipticCurve() ? 1 : -1]; + char array2[QSslEllipticCurve() != QSslEllipticCurve() ? -1 : 1]; + Q_UNUSED(array1); + Q_UNUSED(array2); +#else + QSKIP("This test requires C++11 generalized constant expression support enabled in the compiler."); +#endif +} + +void tst_QSslEllipticCurve::construction() +{ + QSslEllipticCurve curve; + QCOMPARE(curve.isValid(), false); + QCOMPARE(curve.shortName(), QString()); + QCOMPARE(curve.longName(), QString()); + QCOMPARE(curve.isTlsNamedCurve(), false); +} + +void tst_QSslEllipticCurve::fromShortName_data() +{ + QTest::addColumn("shortName"); + QTest::addColumn("curve"); + QTest::addColumn("valid"); + + QTest::newRow("QString()") << QString() << QSslEllipticCurve() << false; + QTest::newRow("\"\"") << QString("") << QSslEllipticCurve() << false; + QTest::newRow("does-not-exist") << QStringLiteral("does-not-exist") << QSslEllipticCurve() << false; + Q_FOREACH (QSslEllipticCurve ec, QSslSocket::supportedEllipticCurves()) { + const QString sN = ec.shortName(); + QTest::newRow(qPrintable("supported EC \"" + sN + '"')) << sN << ec << true; + // At least in the OpenSSL impl, the short name is case-sensitive. That feels odd. + //const QString SN = sN.toUpper(); + //QTest::newRow(qPrintable("supported EC \"" + SN + '"')) << SN << ec << true; + //const QString sn = sN.toLower(); + //QTest::newRow(qPrintable("supported EC \"" + sn + '"')) << sn << ec << true; + } +} + +void tst_QSslEllipticCurve::fromShortName() +{ + QFETCH(QString, shortName); + QFETCH(QSslEllipticCurve, curve); + QFETCH(bool, valid); + + const QSslEllipticCurve result = QSslEllipticCurve::fromShortName(shortName); + QCOMPARE(result, curve); + QCOMPARE(result.isValid(), valid); + QCOMPARE(result.shortName(), curve.shortName()); + QCOMPARE(result.shortName(), valid ? shortName : QString()); +} + +#endif // QT_NO_SSL + +QTEST_MAIN(tst_QSslEllipticCurve) +#include "tst_qsslellipticcurve.moc" diff --git a/tests/auto/network/ssl/ssl.pro b/tests/auto/network/ssl/ssl.pro index 4e30a9cded1..3418a3ae650 100644 --- a/tests/auto/network/ssl/ssl.pro +++ b/tests/auto/network/ssl/ssl.pro @@ -2,6 +2,7 @@ TEMPLATE=subdirs SUBDIRS=\ qsslcertificate \ qsslcipher \ + qsslellipticcurve \ qsslerror \ qsslkey \